diff --git a/.gitignore b/.gitignore index 092a4fa..da4445e 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,4 @@ .bender +working_dir +build +fault_sim diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 55f8cf4..9ea6457 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -3,20 +3,19 @@ # SPDX-License-Identifier: SHL-0.51 stages: - - build - sim variables: - VSIM: "questa-2022.3 vsim" + VSIM: "questa-2025.1-dz vsim" + VCS: "vcs-2024.09-zr vcs" + VLOGAN: "vcs-2024.09-zr vlogan" -build: - stage: build +sim: + stage: sim script: - - make build + - make all_vsim -sim: +sim-vcs: stage: sim - dependencies: - - build script: - - make all + - make all_vcs diff --git a/Bender.lock b/Bender.lock index 67dd6ef..fc5b373 100644 --- a/Bender.lock +++ b/Bender.lock @@ -1,4 +1,20 @@ packages: + apb: + revision: 77ddf073f194d44b9119949d2421be59789e69ae + version: 0.2.4 + source: + Git: https://github.com/pulp-platform/apb.git + dependencies: + - common_cells + axi: + revision: f07498d53ecd5518b277c7d213ec3b71ca4df93c + version: 0.39.7 + source: + Git: https://github.com/pulp-platform/axi.git + dependencies: + - common_cells + - common_verification + - tech_cells_generic common_cells: revision: 9afda9abb565971649c2aa0985639c096f351171 version: 1.38.0 @@ -8,11 +24,31 @@ packages: - common_verification - tech_cells_generic common_verification: - revision: 9c07fa860593b2caabd9b5681740c25fac04b878 - version: 0.2.3 + revision: fb1885f48ea46164a10568aeff51884389f67ae3 + version: 0.2.5 source: Git: https://github.com/pulp-platform/common_verification.git dependencies: [] + redundancy_cells: + revision: 766e376b7ddb70e744740e703a371f7fe7cc3e74 + version: null + source: + Git: https://github.com/pulp-platform/redundancy_cells.git + dependencies: + - common_cells + - common_verification + - register_interface + - tech_cells_generic + register_interface: + revision: 5daa85d164cf6b54ad061ea1e4c6f3624556e467 + version: 0.4.5 + source: + Git: https://github.com/pulp-platform/register_interface.git + dependencies: + - apb + - axi + - common_cells + - common_verification tech_cells_generic: revision: 7968dd6e6180df2c644636bc6d2908a49f2190cf version: 0.2.13 diff --git a/Bender.yml b/Bender.yml index 5d4f84d..b59fa29 100644 --- a/Bender.yml +++ b/Bender.yml @@ -10,6 +10,7 @@ package: dependencies: common_cells: { git: "https://github.com/pulp-platform/common_cells.git", version: 1.38.0 } common_verification: { git: "https://github.com/pulp-platform/common_verification.git", version: 0.2.3 } + redundancy_cells: { git: "https://github.com/pulp-platform/redundancy_cells.git", rev: 766e376b7ddb70e744740e703a371f7fe7cc3e74 } export_include_dirs: - include @@ -21,6 +22,7 @@ sources: - src/obi_intf.sv - src/obi_rready_converter.sv - src/apb_to_obi.sv + - src/obi_to_apb.sv # Level 3 - src/obi_atop_resolver.sv - src/obi_cut.sv @@ -29,8 +31,9 @@ sources: - src/obi_mux.sv - src/obi_sram_shim.sv # Level 4 + - src/obi_isolate.sv - src/obi_xbar.sv - - target: test + - target: any(test, obi_test) files: - src/test/obi_asserter.sv - src/test/obi_test.sv @@ -38,3 +41,39 @@ sources: - src/test/tb_obi_xbar.sv - src/test/atop_golden_mem_pkg.sv - src/test/tb_obi_atop_resolver.sv + + - target: relOBI + files: + # Level 1 + - src/relobi_pkg.sv + - src/relobi_tmr_r.sv + # Level 2 + - src/relobi_a_other_decoder.sv + - src/relobi_a_other_encoder.sv + - src/relobi_a_other_corrector.sv + - src/relobi_r_other_decoder.sv + - src/relobi_r_other_encoder.sv + - src/relobi_r_other_corrector.sv + # Level 3 + - src/relobi_decoder.sv + - src/relobi_encoder.sv + - src/relobi_corrector.sv + # Level 4 + - src/relobi_cut.sv + - src/relobi_demux.sv + - src/relobi_err_sbr.sv + - src/relobi_mux.sv + - src/relobi_sram_shim.sv + # Level 5 + - src/relobi_isolate.sv + - src/relobi_xbar.sv + - target: any(test, obi_test) + files: + - target: ZOIX + files: + - src/test/tb_relobi_xbar.sv + include_dirs: + - fault_sim + - target: not(ZOIX) + files: + - src/test/tb_relobi_xbar.sv diff --git a/Makefile b/Makefile index f065071..3799eb1 100644 --- a/Makefile +++ b/Makefile @@ -4,32 +4,77 @@ BENDER ?= bender VSIM ?= vsim +VCS ?= vcs +VLOGAN ?= vlogan -AVAILABLE_TESTBENCHES = tb_obi_xbar tb_obi_atop_resolver +BENDER_TARGETS := -t obi_test +BENDER_TARGETS += -t relOBI -scripts/compile.tcl: +AVAILABLE_TESTBENCHES = tb_obi_xbar tb_obi_atop_resolver tb_relobi_xbar + +TBS_VSIM = $(addsuffix _vsim, $(AVAILABLE_TESTBENCHES)) +TBS_VCS = $(addsuffix _vcs, $(AVAILABLE_TESTBENCHES)) + +# QuestaSim Flow +scripts/compile_vsim.tcl: Bender.yml Bender.lock mkdir -p scripts - $(BENDER) script vsim -t test --vlog-arg="-svinputport=compat" > $@ + $(BENDER) script vsim $(BENDER_TARGETS) --vlog-arg="-svinputport=compat" > $@ -.PHONY: build -build: scripts/compile.tcl - $(VSIM) -c -do 'exit -code [source scripts/compile.tcl]' +.PHONY: build_vsim +build_vsim: scripts/compile_vsim.tcl + $(VSIM) -c -do 'exit -code [source scripts/compile_vsim.tcl]' -.PHONY: $(AVAILABLE_TESTBENCHES) -$(AVAILABLE_TESTBENCHES): build +.PHONY: $(TBS_VSIM) +$(TBS_VSIM): build_vsim ifdef gui - $(VSIM) $@ -voptargs="+acc" + $(VSIM) $(patsubst %_vsim, %, $@) -voptargs="+acc" else - $(VSIM) -c $@ -do "run -all; quit -f" + $(VSIM) -c $(patsubst %_vsim, %, $@) -voptargs="+acc" -do "run -all; quit -f" endif -.PHONY: all -all: $(AVAILABLE_TESTBENCHES) +.PHONY: all_vsim +all_vsim: $(TBS_VSIM) + +# VCS Flow +VCS_SCRIPT_ARGS += -assert svaext +v2k -override_timescale=10ns/10ps -kdb +VCS_COMPILE_ARGS += -debug_access+all -override_timescale=10ns/10ps +VCS_COMPILE_ARGS += +lint=TFIPC-L +lint=PCWM +warn=noCWUC +warn=noUII-L +VCS_RUNTIME_ARGS = + +scripts/compile_vcs.sh: Bender.yml Bender.lock + mkdir -p scripts + $(BENDER) script vcs --vlogan-bin="$(VLOGAN)" $(BENDER_TARGETS) --vlog-arg="$(VCS_SCRIPT_ARGS)" > $@ + +.PHONY: build_vcs +build_vcs: scripts/compile_vcs.sh + mkdir -p build + chmod +x scripts/compile_vcs.sh + cd build && ../scripts/compile_vcs.sh + +build/%.sim: build_vcs + @if ! echo "$(AVAILABLE_TESTBENCHES)" | grep -wq "$*"; then \ + echo "Error: $(basename $@) is not an available testbench"; \ + echo "Available testbenches: $(AVAILABLE_TESTBENCHES)"; \ + exit 1; \ + fi + cd build && \ + $(VCS) $(VCS_COMPILE_ARGS) -o $*.sim $* + +.PHONY: $(TBS_VCS) +$(TBS_VCS): + @echo "Running VCS simulation for $@ as $(patsubst %_vcs,%,$@)" + $(MAKE) build/$(patsubst %_vcs,%,$@).sim + build/$(patsubst %_vcs,%,$@).sim $(VCS_RUNTIME_ARGS) + +.PHONY: all_vcs +all_vcs: $(TBS_VCS) .PHONY: clean clean: - rm -f scripts/compile.tcl + rm -f scripts/compile_vsim.tcl rm -rf work rm -f modelsim.ini rm -f transcript rm -f vsim.wlf + rm -f scripts/compile_vcs.sh + rm -rf build diff --git a/include/obi/assign.svh b/include/obi/assign.svh index a5395e2..e8dd4db 100644 --- a/include/obi/assign.svh +++ b/include/obi/assign.svh @@ -27,46 +27,11 @@ __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 + __opt_as __lhs.req = __rhs.req; `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 + __opt_as __lhs.rvalid = __rhs.rvalid; //////////////////////////////////////////////////////////////////////////////////////////////////// @@ -86,36 +51,10 @@ `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 + assign src.gnt = dst.gnt; `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 + assign dst.rvalid = src.rvalid; `define OBI_ASSIGN(sbr, mgr, sbrcfg, mgrcfg) \ `OBI_ASSIGN_A(sbr, mgr, sbrcfg, mgrcfg) \ `OBI_ASSIGN_R(mgr, sbr, mgrcfg, sbrcfg) diff --git a/include/obi/typedef.svh b/include/obi/typedef.svh index 9bbe65a..4d76840 100644 --- a/include/obi/typedef.svh +++ b/include/obi/typedef.svh @@ -127,4 +127,71 @@ `OBI_TYPEDEF_R_CHAN_T(obi_t``_r_chan_t, cfg.DataWidth, cfg.IdWidth, obi_t``_r_optional_t) \ `OBI_TYPEDEF_RSP_T(obi_t``_rsp_t, obi_t``_r_chan_t) +`define OBI_TYPEDEF_ALL_WITH_OPTIONAL(obi_t, cfg, a_optional_t, r_optional_t) \ + `OBI_TYPEDEF_A_CHAN_T(obi_t``_a_chan_t, cfg.AddrWidth, cfg.DataWidth, cfg.IdWidth, a_optional_t) \ + `OBI_TYPEDEF_INTEGRITY_REQ_T(obi_t``_req_t, obi_t``_a_chan_t) \ + `OBI_TYPEDEF_R_CHAN_T(obi_t``_r_chan_t, cfg.DataWidth, cfg.IdWidth, r_optional_t) \ + `OBI_TYPEDEF_INTEGRITY_RSP_T(obi_t``_rsp_t, obi_t``_r_chan_t) + +`define OBI_TYPEDEF_ALL_DEFAULT_WITH_OPTIONAL(obi_t, cfg, a_optional_t, r_optional_t) \ + `OBI_TYPEDEF_A_CHAN_T(obi_t``_a_chan_t, cfg.AddrWidth, cfg.DataWidth, cfg.IdWidth, a_optional_t) \ + `OBI_TYPEDEF_DEFAULT_REQ_T(obi_t``_req_t, obi_t``_a_chan_t) \ + `OBI_TYPEDEF_R_CHAN_T(obi_t``_r_chan_t, cfg.DataWidth, cfg.IdWidth, r_optional_t) \ + `OBI_TYPEDEF_RSP_T(obi_t``_rsp_t, obi_t``_r_chan_t) + +`define RELOBI_TYPEDEF_A_CHAN_T(a_chan_t, ADDR_WIDTH, DATA_WIDTH, ID_WIDTH, a_optional_t) \ + typedef struct packed { \ + logic [ ADDR_WIDTH+hsiao_ecc_pkg::min_ecc(ADDR_WIDTH)-1:0] addr; \ + logic we; \ + logic [ DATA_WIDTH/8-1:0] be; \ + logic [ DATA_WIDTH+hsiao_ecc_pkg::min_ecc(DATA_WIDTH)-1:0] wdata; \ + logic [ ID_WIDTH-1:0] aid; \ + a_optional_t a_optional; \ + logic [hsiao_ecc_pkg::min_ecc(1+DATA_WIDTH/8+ID_WIDTH+$bits(a_optional_t))-1:0] other_ecc; \ + } a_chan_t; + +`define RELOBI_TYPEDEF_DEFAULT_REQ_T(req_t, a_chan_t) \ + typedef struct packed { \ + a_chan_t a; \ + logic [2:0] req; \ + } req_t; + +`define RELOBI_TYPEDEF_REQ_T(req_t, a_chan_t) \ + typedef struct packed { \ + a_chan_t a; \ + logic [2:0] req; \ + logic [2:0] rready; \ + } req_t; + +`define RELOBI_TYPEDEF_R_CHAN_T(r_chan_t, RDATA_WIDTH, ID_WIDTH, r_optional_t) \ + typedef struct packed { \ + logic [ RDATA_WIDTH+hsiao_ecc_pkg::min_ecc(RDATA_WIDTH)-1:0] rdata; \ + logic [ ID_WIDTH-1:0] rid; \ + logic err; \ + r_optional_t r_optional; \ + logic [hsiao_ecc_pkg::min_ecc(ID_WIDTH+1+$bits(r_optional_t))-1:0] other_ecc; \ + } r_chan_t; + +`define RELOBI_TYPEDEF_RSP_T(rsp_t, r_chan_t) \ + typedef struct packed { \ + r_chan_t r; \ + logic [2:0] gnt; \ + logic [2:0] rvalid; \ + } rsp_t; + +`define RELOBI_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) \ + `RELOBI_TYPEDEF_A_CHAN_T(obi_t``_a_chan_t, cfg.AddrWidth, cfg.DataWidth, cfg.IdWidth, obi_t``_a_optional_t) \ + `RELOBI_TYPEDEF_DEFAULT_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) \ + `RELOBI_TYPEDEF_R_CHAN_T(obi_t``_r_chan_t, cfg.DataWidth, cfg.IdWidth, obi_t``_r_optional_t) \ + `RELOBI_TYPEDEF_RSP_T(obi_t``_rsp_t, obi_t``_r_chan_t) + +`define RELOBI_TYPEDEF_ALL_WITH_OPTIONAL(obi_t, cfg, a_optional_t, r_optional_t) \ + `RELOBI_TYPEDEF_A_CHAN_T(obi_t``_a_chan_t, cfg.AddrWidth, cfg.DataWidth, cfg.IdWidth, a_optional_t) \ + `RELOBI_TYPEDEF_DEFAULT_REQ_T(obi_t``_req_t, obi_t``_a_chan_t) \ + `RELOBI_TYPEDEF_R_CHAN_T(obi_t``_r_chan_t, cfg.DataWidth, cfg.IdWidth, r_optional_t) \ + `RELOBI_TYPEDEF_RSP_T(obi_t``_rsp_t, obi_t``_r_chan_t) + + `endif // OBI_TYPEDEF_SVH diff --git a/src/obi_atop_resolver.sv b/src/obi_atop_resolver.sv index d5c1581..ce71c58 100644 --- a/src/obi_atop_resolver.sv +++ b/src/obi_atop_resolver.sv @@ -502,8 +502,12 @@ module obi_atop_resolver_intf #( /// The configuration of the subordinate ports (input ports). parameter obi_pkg::obi_cfg_t SbrPortObiCfg = obi_pkg::ObiDefaultConfig, + parameter type sbr_port_a_optional_t = logic, + parameter type sbr_port_r_optional_t = logic, /// The configuration of the manager port (output port). parameter obi_pkg::obi_cfg_t MgrPortObiCfg = SbrPortObiCfg, + parameter type mgr_port_a_optional_t = logic, + parameter type mgr_port_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 @@ -518,8 +522,10 @@ module obi_atop_resolver_intf OBI_BUS.Manager mgr_port ); - `OBI_TYPEDEF_ALL(sbr_port_obi, SbrPortObiCfg) - `OBI_TYPEDEF_ALL(mgr_port_obi, MgrPortObiCfg) + `OBI_TYPEDEF_ALL_WITH_OPTIONAL(sbr_port_obi, SbrPortObiCfg, + sbr_port_a_optional_t, sbr_port_r_optional_t) + `OBI_TYPEDEF_ALL_WITH_OPTIONAL(mgr_port_obi, MgrPortObiCfg, + mgr_port_a_optional_t, mgr_port_r_optional_t) sbr_port_obi_req_t sbr_port_req; sbr_port_obi_rsp_t sbr_port_rsp; @@ -540,8 +546,8 @@ module obi_atop_resolver_intf .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), + .mgr_port_obi_a_optional_t(mgr_port_a_optional_t), + .mgr_port_obi_r_optional_t(mgr_port_r_optional_t), .LrScEnable (LrScEnable), .RegisterAmo (RegisterAmo) ) i_obi_atop_resolver ( diff --git a/src/obi_cut.sv b/src/obi_cut.sv index b5f42d5..6582353 100644 --- a/src/obi_cut.sv +++ b/src/obi_cut.sv @@ -46,28 +46,30 @@ module obi_cut #( .data_o ( mgr_port_req_o.a ) ); - logic ready_o; - logic ready_i; - if (ObiCfg.UseRReady) begin : gen_use_rready - assign mgr_port_req_o.rready = ready_o; - assign ready_i = sbr_port_req_i.rready; + spill_register #( + .T ( obi_r_chan_t ), + .Bypass ( BypassRsp ) + ) i_req_r ( + .clk_i, + .rst_ni, + .valid_i ( mgr_port_rsp_i.rvalid ), + .ready_o ( mgr_port_req_o.rready ), + .data_i ( mgr_port_rsp_i.r ), + .valid_o ( sbr_port_rsp_o.rvalid ), + .ready_i ( sbr_port_req_i.rready ), + .data_o ( sbr_port_rsp_o.r ) + ); end else begin : gen_no_use_rready - assign ready_i = 1'b1; + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + sbr_port_rsp_o.rvalid <= 1'b0; + sbr_port_rsp_o.r <= '0; + end else begin + sbr_port_rsp_o.rvalid <= mgr_port_rsp_i.rvalid; + sbr_port_rsp_o.r <= mgr_port_rsp_i.r; + end + end end - spill_register #( - .T ( obi_r_chan_t ), - .Bypass ( BypassRsp ) - ) i_req_r ( - .clk_i, - .rst_ni, - .valid_i ( mgr_port_rsp_i.rvalid ), - .ready_o ( ready_o ), - .data_i ( mgr_port_rsp_i.r ), - .valid_o ( sbr_port_rsp_o.rvalid ), - .ready_i ( ready_i ), - .data_o ( sbr_port_rsp_o.r ) - ); - endmodule diff --git a/src/obi_demux.sv b/src/obi_demux.sv index 3eb016e..5570327 100644 --- a/src/obi_demux.sv +++ b/src/obi_demux.sv @@ -53,7 +53,7 @@ module obi_demux #( sbr_port_gnt = 1'b0; if (!overflow) begin - if (sbr_port_select_i == select_q || in_flight == '0 || (in_flight == 1 && cnt_down)) begin + if (sbr_port_select_i == select_q || in_flight == '0) 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; sbr_port_gnt = mgr_ports_rsp_i[sbr_port_select_i].gnt; diff --git a/src/obi_isolate.sv b/src/obi_isolate.sv new file mode 100644 index 0000000..fbdc1f8 --- /dev/null +++ b/src/obi_isolate.sv @@ -0,0 +1,179 @@ +// Copyright 2025 ETH Zurich and University of Bologna. +// Solderpad Hardware License, Version 0.51, see LICENSE for details. +// SPDX-License-Identifier: SHL-0.51 + +// Michael Rogenmoser + +module obi_isolate #( + /// The OBI configuration. + 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, + /// Maximum number of pending requests per channel + parameter int unsigned MaxPending = 4, + /// Gracefully terminate all incoming transactions in case of isolation by returning proper error + /// responses. + parameter bit TerminateTransaction = 1'b0 +) ( + input logic clk_i, + input logic rst_ni, + + input obi_req_t sbr_port_req_i, + output obi_rsp_t sbr_port_rsp_o, + + output obi_req_t mgr_port_req_o, + input obi_rsp_t mgr_port_rsp_i, + + input logic isolate_i, + output logic isolated_o +); + // plus 1 in clog for accouning no open transaction + localparam int unsigned CounterWidth = $clog2(MaxPending + 32'd1); + typedef logic [CounterWidth-1:0] cnt_t; + + + obi_req_t in_req, out_req; + obi_rsp_t in_rsp, out_rsp; + + if (TerminateTransaction) begin : gen_err_rsp + obi_req_t err_req; + obi_rsp_t err_rsp; + // demux to error subordinate + obi_demux #( + .ObiCfg ( ObiCfg ), + .obi_req_t ( obi_req_t ), + .obi_rsp_t ( obi_rsp_t ), + .NumMgrPorts ( 2 ), + .NumMaxTrans ( MaxPending ) + ) i_demux ( + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), + + .sbr_port_select_i ( isolated_o ), + .sbr_port_req_i ( sbr_port_req_i ), + .sbr_port_rsp_o ( sbr_port_rsp_o ), + + .mgr_ports_req_o ( { in_req, err_req } ), + .mgr_ports_rsp_i ( { in_rsp, err_rsp } ) + ); + + obi_err_sbr #( + .ObiCfg ( ObiCfg ), + .obi_req_t ( obi_req_t ), + .obi_rsp_t ( obi_rsp_t ), + .NumMaxTrans ( MaxPending ) + ) i_err_sbr ( + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), + .testmode_i ( 1'b0 ), + + .obi_req_i ( err_req ), + .obi_rsp_o ( err_rsp ) + ); + end else begin : gen_no_err_rsp + assign in_req = sbr_port_req_i; + assign sbr_port_rsp_o = in_rsp; + end + + typedef enum logic [1:0] { + Normal, + Hold, + Drain, + Isolate + } isolate_state_e; + + isolate_state_e isolate_state_d, isolate_state_q; + cnt_t in_flight_d, in_flight_q; + logic rready, mgr_rready; + + if (ObiCfg.UseRReady) begin : gen_rready + assign rready = in_rsp.rready; + always_comb begin + mgr_port_req_o = out_req; + mgr_port_req_o.rready = mgr_rready; + end + end else begin : gen_no_rready + assign rready = 1'b1; + assign mgr_port_req_o = out_req; + end + + // Update counters + always_comb begin + in_flight_d = in_flight_q; + if (out_req.req && (isolate_state_q == Normal)) begin + in_flight_d++; + end + if (out_rsp.rvalid && rready) begin + in_flight_d--; + end + end + + // Perform isolation + always_comb begin + isolate_state_d = isolate_state_q; + // Connect channel by default + out_req = in_req; + in_rsp = out_rsp; + mgr_rready = rready; + unique case (isolate_state_q) + Normal: begin + // Block if in flight transactions overflows + if (in_flight_q >= cnt_t'(MaxPending)) begin + out_req.req = 1'b0; + in_rsp.gnt = 1'b0; + if (isolate_i) begin + isolate_state_d = Hold; + end + end else begin + if (in_req.req && !out_rsp.gnt) begin + isolate_state_d = Hold; + end else begin + if (isolate_i) begin + isolate_state_d = Drain; + end + end + end + end + Hold: begin + out_req.req = 1'b1; + if (out_rsp.gnt) begin + isolate_state_d = isolate_i ? Drain : Normal; + end + end + Drain: begin + out_req.req = 1'b0; + in_rsp.gnt = 1'b0; + if (in_flight_q == '0) begin + isolate_state_d = Isolate; + end + end + Isolate: begin + out_req.req = 1'b0; + out_req.a = '{default: '0}; + in_rsp.gnt = 1'b0; + in_rsp.rvalid = 1'b0; + in_rsp.r = '{default: '0}; + mgr_rready = 1'b0; + if (!isolate_i) begin + isolate_state_d = Normal; + end + end + default: ; + endcase + end + + assign isolated_o = (isolate_state_q == Isolate); + + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + isolate_state_q <= Isolate; + in_flight_q <= '0; + end else begin + isolate_state_q <= isolate_state_d; + in_flight_q <= in_flight_d; + end + end + +endmodule diff --git a/src/obi_mux.sv b/src/obi_mux.sv index 5e17b5c..f6b7c34 100644 --- a/src/obi_mux.sv +++ b/src/obi_mux.sv @@ -35,9 +35,11 @@ module obi_mux #( input logic rst_ni, input logic testmode_i, + /// Subordinate ports (input) 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, + /// Manager ports (output) output mgr_port_obi_req_t mgr_port_req_o, input mgr_port_obi_rsp_t mgr_port_rsp_i ); @@ -166,8 +168,12 @@ endmodule module obi_mux_intf #( /// The configuration of the subordinate ports (input ports). parameter obi_pkg::obi_cfg_t SbrPortObiCfg = obi_pkg::ObiDefaultConfig, + parameter type sbr_port_a_optional_t = logic, + parameter type sbr_port_r_optional_t = logic, /// The configuration of the manager port (output port). parameter obi_pkg::obi_cfg_t MgrPortObiCfg = SbrPortObiCfg, + parameter type mgr_port_a_optional_t = logic, + parameter type mgr_port_r_optional_t = logic, /// The number of subordinate ports (input ports). parameter int unsigned NumSbrPorts = 32'd0, /// The maximum number of outstanding transactions. @@ -184,8 +190,10 @@ module obi_mux_intf #( OBI_BUS.Manager mgr_port ); - `OBI_TYPEDEF_ALL(sbr_port_obi, SbrPortObiCfg) - `OBI_TYPEDEF_ALL(mgr_port_obi, MgrPortObiCfg) + `OBI_TYPEDEF_ALL_WITH_OPTIONAL(sbr_port_obi, SbrPortObiCfg, + sbr_port_a_optional_t, sbr_port_r_optional_t) + `OBI_TYPEDEF_ALL_WITH_OPTIONAL(mgr_port_obi, MgrPortObiCfg, + mgr_port_a_optional_t, mgr_port_r_optional_t) sbr_port_obi_req_t [NumSbrPorts-1:0] sbr_ports_req; sbr_port_obi_rsp_t [NumSbrPorts-1:0] sbr_ports_rsp; diff --git a/src/obi_pkg.sv b/src/obi_pkg.sv index 32bb1d1..8ff947d 100644 --- a/src/obi_pkg.sv +++ b/src/obi_pkg.sv @@ -15,8 +15,11 @@ package obi_pkg; /// The OBI prot type, to be expanded. typedef logic [2:0] prot_t; + /// Default ATOP has no atomic/exclusive operation, i.e., standard transaction. localparam atop_t DefaultAtop = 6'b000000; + /// Default Memory type is non-bufferable and non-cacheable. localparam memtype_t DefaultMemtype = 2'b00; + /// Default Prot is Data access in machine mode. localparam prot_t DefaultProt = 3'b111; /// The config type for OBI bus optional fields. @@ -33,6 +36,7 @@ package obi_pkg; int unsigned RChkWidth; } obi_optional_cfg_t; + /// The minimal optional configuration disables all optional features. localparam obi_optional_cfg_t ObiMinimalOptionalConfig = '{ UseAtop: 1'b0, UseMemtype: 1'b0, @@ -46,6 +50,7 @@ package obi_pkg; RChkWidth: 0 }; + /// The atop optional config only enables atomics, everything else is disabled. localparam obi_optional_cfg_t ObiAtopOptionalConfig = '{ UseAtop: 1'b1, UseMemtype: 1'b0, @@ -59,6 +64,7 @@ package obi_pkg; RChkWidth: 0 }; + /// Returns a config enabling all optional features with specified user, mid, and chk widths. 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); @@ -88,6 +94,7 @@ package obi_pkg; obi_optional_cfg_t OptionalCfg; } obi_cfg_t; + /// Returns a default configuration with specified address, data, and ID widths, and a specified optional configuration. 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 = '{ @@ -102,9 +109,10 @@ package obi_pkg; }; endfunction - /// The default OBI bus config. + /// The default OBI bus config (32-bit address and data, 1-bit ID width, no optional features. localparam obi_cfg_t ObiDefaultConfig = obi_default_cfg(32, 32, 1, ObiMinimalOptionalConfig); + /// Returns an OBI bus config increasing the ID width for a multiplexer/crossbar with NumManagers. function automatic obi_cfg_t mux_grow_cfg(obi_cfg_t ObiCfgIn, int unsigned NumManagers); mux_grow_cfg = '{ UseRReady: ObiCfgIn.UseRReady, @@ -118,6 +126,7 @@ package obi_pkg; }; endfunction + /// OBI Atomic operations. typedef enum atop_t { ATOPLR = 6'h22, ATOPSC = 6'h23, diff --git a/src/obi_to_apb.sv b/src/obi_to_apb.sv new file mode 100644 index 0000000..651dad1 --- /dev/null +++ b/src/obi_to_apb.sv @@ -0,0 +1,167 @@ +// Copyright 2025 ETH Zurich and University of Bologna. +// Solderpad Hardware License, Version 0.51, see LICENSE for details. +// SPDX-License-Identifier: SHL-0.51 + +// Michael Rogenmoser + +`include "common_cells/assertions.svh" + +/// An OBI to APB adapter. +module obi_to_apb #( + /// The configuration of the subordinate port (input port). + parameter obi_pkg::obi_cfg_t ObiCfg = obi_pkg::ObiDefaultConfig, + /// The OBI request struct for the subordinate port (input port). + parameter type obi_req_t = logic, + /// The OBI response struct for the subordinate port (input port). + parameter type obi_rsp_t = logic, // OBI response struct + /// The APB request struct for the manager port (output port). + parameter type apb_req_t = logic, // APB request struct + /// The APB response struct for the manager port (output port). + parameter type apb_rsp_t = logic, // APB response struct + /// Disable gnt/rvalid in same cycle. + parameter bit DisableSameCycleRsp = 1'b1 +) ( + input logic clk_i, + input logic rst_ni, + // Subordinate OBI port. + input obi_req_t obi_req_i, + output obi_rsp_t obi_rsp_o, + // Manager APB port. + output apb_req_t apb_req_o, + input apb_rsp_t apb_rsp_i +); + + logic [ObiCfg.AddrWidth-1:0] addr; + logic [ObiCfg.DataWidth-1:0] wdata; + logic we; + logic [ObiCfg.DataWidth/8-1:0] be; + logic [2:0] prot; + + assign apb_req_o.paddr = addr; + assign apb_req_o.pwrite = we; + // OBI expects '1 on writes, APB sets pstrb to '0 on reads. + assign apb_req_o.pstrb = we ? be : '0; + assign apb_req_o.pwdata = wdata; + if (ObiCfg.OptionalCfg.UseProt) begin : gen_pprot + // Bit 2: inverted bit 0 of OBI prot + assign apb_req_o.pprot[2] = ~prot[2]; + // Bit 1: secure access only for machine mode + assign apb_req_o.pprot[1] = ~(prot[1] & prot[0]); + // Bit 0: privileged for machine mode and supervisor mode + assign apb_req_o.pprot[0] = prot[1]; + end else begin : gen_no_pprot + // OBI does not enable prot, so we set it to priviledged, secure, instruction. + assign apb_req_o.pprot = 3'b101; + end + + assign obi_rsp_o.r.rdata = apb_rsp_i.prdata; + assign obi_rsp_o.r.err = apb_rsp_i.pslverr; + assign obi_rsp_o.r.r_optional = '0; // No optional R feature supported at the moment. + + if (DisableSameCycleRsp) begin : gen_hs_buffers + logic tsxn_in_progress; + logic [ObiCfg.IdWidth-1:0] aid_q; + logic [ObiCfg.AddrWidth-1:0] addr_q; + logic [ObiCfg.DataWidth-1:0] wdata_q; + logic we_q; + logic [ObiCfg.DataWidth/8-1:0] be_q; + logic [2:0] prot_q; + + assign apb_req_o.psel = obi_req_i.req | tsxn_in_progress; + assign apb_req_o.penable = tsxn_in_progress; + assign obi_rsp_o.gnt = ~tsxn_in_progress; + assign obi_rsp_o.rvalid = apb_rsp_i.pready & tsxn_in_progress; + // Mirror AID to RID + assign obi_rsp_o.r.rid = aid_q; + + assign addr = tsxn_in_progress ? addr_q : obi_req_i.a.addr; + assign wdata = tsxn_in_progress ? wdata_q : obi_req_i.a.wdata; + assign we = tsxn_in_progress ? we_q : obi_req_i.a.we; + assign be = tsxn_in_progress ? be_q : obi_req_i.a.be; + + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + tsxn_in_progress <= 1'b0; + aid_q <= '0; + addr_q <= '0; + wdata_q <= '0; + we_q <= 1'b0; + be_q <= '0; + end else begin + tsxn_in_progress <= apb_req_o.psel & ~apb_rsp_i.pready; + if (obi_req_i.req & obi_rsp_o.gnt) begin + aid_q <= obi_req_i.a.aid; + addr_q <= obi_req_i.a.addr; + wdata_q <= obi_req_i.a.wdata; + we_q <= obi_req_i.a.we; + be_q <= obi_req_i.a.be; + end + end + end + if (ObiCfg.OptionalCfg.UseProt) begin : gen_prot + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + prot <= 3'b101; // Privileged, secure, instruction. + end else if (obi_req_i.req & obi_rsp_o.gnt) begin + prot <= obi_req_i.a.a_optional.prot; + end + end + end else begin : gen_no_prot + assign prot = 3'b101; // Privileged, secure, instruction. + end + + end else begin : gen_no_hs_buffers + logic psel_q; + assign apb_req_o.psel = obi_req_i.req; + assign apb_req_o.penable = psel_q; + assign obi_rsp_o.gnt = apb_rsp_i.pready; + assign obi_rsp_o.rvalid = apb_rsp_i.pready; + // Mirror AID to RID + assign obi_rsp_o.r.rid = obi_req_i.a.aid; + assign addr = obi_req_i.a.addr; + assign wdata = obi_req_i.a.wdata; + assign we = obi_req_i.a.we; + assign be = obi_req_i.a.be; + if (ObiCfg.OptionalCfg.UseProt) begin : gen_prot + assign prot = obi_req_i.a.a_optional.prot; + end else begin : gen_no_prot + assign prot = 3'b101; // Privileged, secure, instruction. + end + + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + psel_q <= 1'b0; + end else begin + psel_q <= obi_req_i.req & ~apb_rsp_i.pready; + end + end + end + + + // ---------- + // Assertions + // ---------- + + `ASSERT_INIT(no_rready, ObiCfg.UseRReady == 0, + "RReady not supported in OBI to APB conversion") + `ASSERT_INIT(no_atop, ObiCfg.OptionalCfg.UseAtop == 0, + "ATOP not supported in OBI to APB conversion") + `ASSERT_INIT(no_memtype, ObiCfg.OptionalCfg.UseMemtype == 0, + "Memtype not supported in OBI to APB conversion") + `ASSERT_INIT(no_debug, ObiCfg.OptionalCfg.UseDbg == 0, + "Debug not supported in OBI to APB conversion") + `ASSERT_INIT(no_integrity, !ObiCfg.Integrity, + "Integrity not supported!") + `ASSERT_INIT(no_achk, ObiCfg.OptionalCfg.AChkWidth == 0, + "ACHK field not supported!") + `ASSERT_INIT(equal_wdata_width, $bits(apb_req_o.pwdata) == $bits(obi_req_i.a.wdata), + "WDATA width mismatch between APB and OBI ports!") + `ASSERT_INIT(equal_be_width, $bits(apb_req_o.pstrb) == $bits(obi_req_i.a.be), + "Strobe width mismatch between APB and OBI ports!") + `ASSERT_INIT(equal_rdata_width, $bits(apb_rsp_i.prdata) == $bits(obi_rsp_o.r.rdata), + "RDATA width mismatch between APB and OBI ports!") + `ASSERT_INIT(equal_addr_width, $bits(apb_req_o.paddr) == $bits(obi_req_i.a.addr), + "Address width mismatch between APB and OBI ports!") + + +endmodule diff --git a/src/obi_xbar.sv b/src/obi_xbar.sv index 2f1b3ce..2b89152 100644 --- a/src/obi_xbar.sv +++ b/src/obi_xbar.sv @@ -161,8 +161,12 @@ endmodule module obi_xbar_intf #( /// The OBI configuration for the subordinate ports (input ports). parameter obi_pkg::obi_cfg_t SbrPortObiCfg = obi_pkg::ObiDefaultConfig, + parameter type sbr_port_a_optional_t = logic, + parameter type sbr_port_r_optional_t = logic, /// The OBI configuration for the manager ports (ouput ports). parameter obi_pkg::obi_cfg_t MgrPortObiCfg = SbrPortObiCfg, + parameter type mgr_port_a_optional_t = logic, + parameter type mgr_port_r_optional_t = logic, /// The number of subordinate ports (input ports). parameter int unsigned NumSbrPorts = 32'd0, /// The number of manager ports (output ports). @@ -191,8 +195,10 @@ module obi_xbar_intf #( input logic [NumSbrPorts-1:0][cf_math_pkg::idx_width(NumMgrPorts)-1:0] default_idx_i ); - `OBI_TYPEDEF_ALL(sbr_port_obi, SbrPortObiCfg) - `OBI_TYPEDEF_ALL(mgr_port_obi, MgrPortObiCfg) + `OBI_TYPEDEF_ALL_WITH_OPTIONAL(sbr_port_obi, SbrPortObiCfg, + sbr_port_a_optional_t, sbr_port_r_optional_t) + `OBI_TYPEDEF_ALL_WITH_OPTIONAL(mgr_port_obi, MgrPortObiCfg, + mgr_port_a_optional_t, mgr_port_r_optional_t) sbr_port_obi_req_t [NumSbrPorts-1:0] sbr_ports_req; sbr_port_obi_rsp_t [NumSbrPorts-1:0] sbr_ports_rsp; diff --git a/src/relobi_a_other_corrector.sv b/src/relobi_a_other_corrector.sv new file mode 100644 index 0000000..16f839e --- /dev/null +++ b/src/relobi_a_other_corrector.sv @@ -0,0 +1,46 @@ +// 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 + +module relobi_a_other_corrector #( + /// Configuration of the bus + parameter obi_pkg::obi_cfg_t Cfg = obi_pkg::ObiDefaultConfig, + + parameter type a_optional_t = logic, + parameter int unsigned OtherEccWidth = relobi_pkg::relobi_a_other_ecc_width(Cfg) +) ( + input logic we_i, + input logic [Cfg.DataWidth/8-1:0] be_i, + input logic [Cfg.IdWidth -1:0] aid_i, + input a_optional_t a_optional_i, + input logic [ OtherEccWidth-1:0] other_ecc_i, + + output logic we_o, + output logic [Cfg.DataWidth/8-1:0] be_o, + output logic [Cfg.IdWidth -1:0] aid_o, + output a_optional_t a_optional_o, + output logic [ OtherEccWidth-1:0] other_ecc_o, + + output logic [1:0] fault_o +); + + hsiao_ecc_cor #( + .DataWidth (relobi_pkg::relobi_a_other_width(Cfg)) + ) i_a_remaining_cor ( + .in ( {other_ecc_i, + we_i, + be_i, + aid_i, + a_optional_i} ), + .out ( {other_ecc_o, + we_o, + be_o, + aid_o, + a_optional_o} ), + .syndrome_o(), + .err_o (fault_o) + ); + +endmodule diff --git a/src/relobi_a_other_decoder.sv b/src/relobi_a_other_decoder.sv new file mode 100644 index 0000000..1ce100d --- /dev/null +++ b/src/relobi_a_other_decoder.sv @@ -0,0 +1,44 @@ +// 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 + +module relobi_a_other_decoder #( + /// Configuration of the bus + parameter obi_pkg::obi_cfg_t Cfg = obi_pkg::ObiDefaultConfig, + + parameter type a_optional_t = logic, + parameter int unsigned OtherEccWidth = relobi_pkg::relobi_a_other_ecc_width(Cfg) +) ( + input logic we_i, + input logic [Cfg.DataWidth/8-1:0] be_i, + input logic [Cfg.IdWidth -1:0] aid_i, + input a_optional_t a_optional_i, + input logic [ OtherEccWidth-1:0] other_ecc_i, + + output logic we_o, + output logic [Cfg.DataWidth/8-1:0] be_o, + output logic [Cfg.IdWidth -1:0] aid_o, + output a_optional_t a_optional_o, + + output logic [1:0] fault_o +); + + hsiao_ecc_dec #( + .DataWidth (relobi_pkg::relobi_a_other_width(Cfg)) + ) i_a_remaining_dec ( + .in ( {other_ecc_i, + we_i, + be_i, + aid_i, + a_optional_i} ), + .out ( {we_o, + be_o, + aid_o, + a_optional_o} ), + .syndrome_o(), + .err_o (fault_o) + ); + +endmodule diff --git a/src/relobi_a_other_encoder.sv b/src/relobi_a_other_encoder.sv new file mode 100644 index 0000000..f34482d --- /dev/null +++ b/src/relobi_a_other_encoder.sv @@ -0,0 +1,34 @@ +// 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 + +module relobi_a_other_encoder #( + /// Configuration of the bus + parameter obi_pkg::obi_cfg_t Cfg = obi_pkg::ObiDefaultConfig, + + parameter type a_optional_t = logic, + parameter int unsigned OtherEccWidth = relobi_pkg::relobi_a_other_ecc_width(Cfg) +) ( + input logic we_i, + input logic [Cfg.DataWidth/8-1:0] be_i, + input logic [Cfg.IdWidth -1:0] aid_i, + input a_optional_t a_optional_i, + + output logic [ OtherEccWidth-1:0] other_ecc_o +); + + logic [relobi_pkg::relobi_a_other_width(Cfg)-1:0] unused; + + hsiao_ecc_enc #( + .DataWidth (relobi_pkg::relobi_a_other_width(Cfg)) + ) i_a_remaining_enc ( + .in ( {we_i, + be_i, + aid_i, + a_optional_i} ), + .out ( {other_ecc_o, unused} ) + ); + +endmodule diff --git a/src/relobi_corrector.sv b/src/relobi_corrector.sv new file mode 100644 index 0000000..798dc63 --- /dev/null +++ b/src/relobi_corrector.sv @@ -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 + +module relobi_corrector #( + /// Configuration of the bus + parameter obi_pkg::obi_cfg_t Cfg = obi_pkg::ObiDefaultConfig, + + parameter type relobi_req_t = logic, + parameter type relobi_rsp_t = logic, + parameter type a_optional_t = logic, + parameter type r_optional_t = logic +) ( + input relobi_req_t req_i, + output relobi_rsp_t rsp_o, + + output relobi_req_t req_o, + input relobi_rsp_t rsp_i, + + output logic [1:0] fault_o +); + + logic [11:0] voter_errs; + logic voter_errs_red; + logic [4:0][1:0] hsiao_errs; + logic [1:0][4:0] hsiao_errs_transpose; + logic [1:0] hsiao_errs_transpose_red; + + for (genvar i = 0; i < 2; i++) begin : gen_hsiao_errs_transpose + assign hsiao_errs_transpose_red[i] = |hsiao_errs_transpose[i]; + for (genvar j = 0; j < 5; j++) begin : gen_hsiao_errs_transpose_inner + assign hsiao_errs_transpose[i][j] = hsiao_errs[j][i]; + end + end + + assign voter_errs_red = |voter_errs; + assign fault_o[0] = voter_errs_red | hsiao_errs_transpose_red[0]; + assign fault_o[1] = hsiao_errs_transpose_red[1]; + + for (genvar i = 0; i < 3; i++) begin : gen_tmr_part + TMR_voter_fail i_req_req_vote ( + .a_i (req_i.req[0]), + .b_i (req_i.req[1]), + .c_i (req_i.req[2]), + .majority_o (req_o.req[i]), + .fault_detected_o(voter_errs[i]) + ); + TMR_voter_fail i_rsp_gnt_vote ( + .a_i (rsp_i.gnt[0]), + .b_i (rsp_i.gnt[1]), + .c_i (rsp_i.gnt[2]), + .majority_o (rsp_o.gnt[i]), + .fault_detected_o(voter_errs[3+i]) + ); + TMR_voter_fail i_rsp_rvalid_vote ( + .a_i (rsp_i.rvalid[0]), + .b_i (rsp_i.rvalid[1]), + .c_i (rsp_i.rvalid[2]), + .majority_o (rsp_o.rvalid[i]), + .fault_detected_o(voter_errs[6+i]) + ); + if (Cfg.UseRReady) begin : gen_rready_vote + TMR_voter_fail i_req_rready_vote ( + .a_i (req_i.rready[0]), + .b_i (req_i.rready[1]), + .c_i (req_i.rready[2]), + .majority_o (req_o.rready[i]), + .fault_detected_o(voter_errs[9+i]) + ); + end else begin : gen_no_rready_vote + assign voter_errs[9+i] = 1'b0; + end + end + + hsiao_ecc_cor #( + .DataWidth ( Cfg.AddrWidth ) + ) i_addr_enc ( + .in ( req_i.a.addr ), + .out( req_o.a.addr ), + .syndrome_o(), + .err_o (hsiao_errs[0]) + ); + + hsiao_ecc_cor #( + .DataWidth ( Cfg.DataWidth ) + ) i_wdata_enc ( + .in ( req_i.a.wdata ), + .out( req_o.a.wdata ), + .syndrome_o(), + .err_o (hsiao_errs[1]) + ); + + relobi_a_other_corrector #( + .Cfg (Cfg), + .a_optional_t (a_optional_t) + ) i_a_remaining_enc ( + .we_i (req_i.a.we), + .be_i (req_i.a.be), + .aid_i (req_i.a.aid), + .a_optional_i(req_i.a.a_optional), + .other_ecc_i (req_i.a.other_ecc), + .we_o (req_o.a.we), + .be_o (req_o.a.be), + .aid_o (req_o.a.aid), + .a_optional_o(req_o.a.a_optional), + .other_ecc_o (req_o.a.other_ecc), + .fault_o (hsiao_errs[2]) + ); + + hsiao_ecc_cor #( + .DataWidth ( Cfg.DataWidth ) + ) i_rdata_dec ( + .in ( rsp_i.r.rdata ), + .out ( rsp_o.r.rdata ), + .syndrome_o(), + .err_o (hsiao_errs[3]) + ); + + relobi_r_other_corrector #( + .Cfg (Cfg), + .r_optional_t (r_optional_t) + ) i_r_remaining_dec ( + .rid_i (rsp_i.r.rid), + .err_i (rsp_i.r.err), + .r_optional_i(rsp_i.r.r_optional), + .other_ecc_i (rsp_i.r.other_ecc), + .rid_o (rsp_o.r.rid), + .err_o (rsp_o.r.err), + .r_optional_o(rsp_o.r.r_optional), + .other_ecc_o (rsp_o.r.other_ecc), + .fault_o (hsiao_errs[4]) + ); + +endmodule diff --git a/src/relobi_cut.sv b/src/relobi_cut.sv new file mode 100644 index 0000000..33acd16 --- /dev/null +++ b/src/relobi_cut.sv @@ -0,0 +1,120 @@ +// Copyright 2025 ETH Zurich and University of Bologna. +// Solderpad Hardware License, Version 0.51, see LICENSE for details. +// SPDX-License-Identifier: SHL-0.51 + +// Michael Rogenmoser + +module relobi_cut #( + /// The OBI configuration. + 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, + /// The obi A channel struct. + parameter type obi_a_chan_t = logic, + /// The obi R channel struct. + parameter type obi_r_chan_t = logic, + /// Optional type for the A channel. + parameter type a_optional_t = logic, + /// Optional type for the R channel. + parameter type r_optional_t = logic, + /// Bypass enable, can be individually overridden! + parameter bit Bypass = 1'b0, + /// Bypass enable for Request side. + parameter bit BypassReq = Bypass, + /// Bypass enable for Response side. + parameter bit BypassRsp = Bypass +) ( + input logic clk_i, + input logic rst_ni, + + input obi_req_t sbr_port_req_i, + output obi_rsp_t sbr_port_rsp_o, + + output obi_req_t mgr_port_req_o, + input obi_rsp_t mgr_port_rsp_i, + output logic [1:0] fault_o +); + + logic [1:0] faults; + logic [1:0] corrector_faults; + assign fault_o[0] = |faults | corrector_faults[0]; + assign fault_o[1] = corrector_faults[1]; + + obi_req_t corrector_req, corrected_req; + obi_rsp_t corrector_rsp, corrected_rsp; + assign corrector_req.req = '0; + if (ObiCfg.UseRReady) begin : gen_corrector_req_rready + assign corrector_req.rready = '0; + end + assign corrector_rsp.rvalid = '0; + assign corrector_rsp.gnt = '0; + + relobi_corrector #( + .Cfg ( ObiCfg ), + .relobi_req_t ( obi_req_t ), + .relobi_rsp_t ( obi_rsp_t ), + .a_optional_t ( a_optional_t ), + .r_optional_t ( r_optional_t ) + ) i_relobi_corrector ( + .req_i (corrector_req), + .rsp_i (corrector_rsp), + .req_o (corrected_req), + .rsp_o (corrected_rsp), + .fault_o (corrector_faults) + ); + + rel_spill_register #( + .T ( obi_a_chan_t ), + .Bypass ( BypassReq ), + .TmrHandshake ( 1'b1 ), + .DataCorrector ( 1'b1 ) + ) i_reg_a ( + .clk_i, + .rst_ni, + .valid_i ( sbr_port_req_i.req ), + .ready_o ( sbr_port_rsp_o.gnt ), + .data_i ( sbr_port_req_i.a ), + .valid_o ( mgr_port_req_o.req ), + .ready_i ( mgr_port_rsp_i.gnt ), + .data_o ( mgr_port_req_o.a ), + .fault_o ( faults[0] ), + .data_corrector_o ( corrector_req.a ), + .data_corrected_i ( corrected_req.a ) + ); + + if (ObiCfg.UseRReady) begin : gen_use_rready + rel_spill_register #( + .T ( obi_r_chan_t ), + .Bypass ( BypassRsp ), + .TmrHandshake ( 1'b1 ), + .DataCorrector ( 1'b1 ) + ) i_reg_r ( + .clk_i, + .rst_ni, + .valid_i ( mgr_port_rsp_i.rvalid ), + .ready_o ( mgr_port_req_o.rready ), + .data_i ( mgr_port_rsp_i.r ), + .valid_o ( sbr_port_rsp_o.rvalid ), + .ready_i ( sbr_port_req_i.rready ), + .data_o ( sbr_port_rsp_o.r ), + .fault_o ( faults[1] ), + .data_corrector_o ( corrector_rsp.r ), + .data_corrected_i ( corrected_rsp.r ) + ); + end else begin : gen_no_use_rready + assign faults[1] = 1'b0; + assign corrector_rsp.r = '0; + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + sbr_port_rsp_o.rvalid <= 1'b0; + sbr_port_rsp_o.r <= '0; + end else begin + sbr_port_rsp_o.rvalid <= mgr_port_rsp_i.rvalid; + sbr_port_rsp_o.r <= mgr_port_rsp_i.r; + end + end + end + +endmodule diff --git a/src/relobi_decoder.sv b/src/relobi_decoder.sv new file mode 100644 index 0000000..66c6de6 --- /dev/null +++ b/src/relobi_decoder.sv @@ -0,0 +1,123 @@ +// 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 + +/// An encoder for reliable OBI +module relobi_decoder import hsiao_ecc_pkg::*; #( + /// Configuration of the bus + parameter obi_pkg::obi_cfg_t Cfg = obi_pkg::ObiDefaultConfig, + + parameter type relobi_req_t = logic, + parameter type relobi_rsp_t = logic, + parameter type obi_req_t = logic, + parameter type obi_rsp_t = logic, + parameter type a_optional_t = logic, + parameter type r_optional_t = logic +) ( + input relobi_req_t rel_req_i, + output relobi_rsp_t rel_rsp_o, + + output obi_req_t req_o, + input obi_rsp_t rsp_i, + + output logic [1:0] fault_o +); + + logic [1:0] voter_errs; + logic [2:0][1:0] hsiao_errs; + logic [2:0][1:0] hsiao_errs_gated; + logic [1:0][2:0] hsiao_errs_transpose; + + for (genvar i = 0; i < 2; i++) begin : gen_hsiao_errs_transpose + for (genvar j = 0; j < 3; j++) begin : gen_hsiao_errs_transpose_inner + assign hsiao_errs_transpose[i][j] = hsiao_errs_gated[j][i]; + end + end + + assign fault_o[0] = |voter_errs | |hsiao_errs_transpose[0]; + assign fault_o[1] = |hsiao_errs_transpose[1]; + + TMR_voter_fail i_req_valid_vote ( + .a_i (rel_req_i.req[0]), + .b_i (rel_req_i.req[1]), + .c_i (rel_req_i.req[2]), + .majority_o (req_o.req), + .fault_detected_o(voter_errs[0]) + ); + + assign rel_rsp_o.gnt = {3{rsp_i.gnt}}; + + if (Cfg.UseRReady) begin : gen_rready_vote + TMR_voter_fail i_rsp_ready_vote ( + .a_i (rel_req_i.rready[0]), + .b_i (rel_req_i.rready[1]), + .c_i (rel_req_i.rready[2]), + .majority_o (req_o.rready), + .fault_detected_o(voter_errs[1]) + ); + end else begin : gen_no_rready + assign voter_errs[1] = '0; + end + + assign rel_rsp_o.rvalid = {3{rsp_i.rvalid}}; + + hsiao_ecc_dec #( + .DataWidth ( Cfg.AddrWidth ) + ) i_addr_dec ( + .in ( rel_req_i.a.addr ), + .out ( req_o.a.addr ), + .syndrome_o(), + .err_o (hsiao_errs[0]) + ); + assign hsiao_errs_gated[0] = rel_req_i.req[0] ? hsiao_errs[0] : '0; + + hsiao_ecc_dec #( + .DataWidth ( Cfg.DataWidth ) + ) i_wdata_dec ( + .in ( rel_req_i.a.wdata ), + .out ( req_o.a.wdata ), + .syndrome_o(), + .err_o (hsiao_errs[1]) + ); + assign hsiao_errs_gated[1] = rel_req_i.req[0] ? hsiao_errs[1] : '0; + + relobi_a_other_decoder #( + .Cfg (Cfg), + .a_optional_t (a_optional_t) + ) i_a_remaining_dec ( + .we_i (rel_req_i.a.we), + .be_i (rel_req_i.a.be), + .aid_i (rel_req_i.a.aid), + .a_optional_i(rel_req_i.a.a_optional), + .other_ecc_i (rel_req_i.a.other_ecc), + .we_o (req_o.a.we), + .be_o (req_o.a.be), + .aid_o (req_o.a.aid), + .a_optional_o(req_o.a.a_optional), + .fault_o (hsiao_errs[2]) + ); + assign hsiao_errs_gated[2] = rel_req_i.req[0] ? hsiao_errs[2] : '0; + + hsiao_ecc_enc #( + .DataWidth ( Cfg.DataWidth ) + ) i_rdata_enc ( + .in ( rsp_i.r.rdata ), + .out( rel_rsp_o.r.rdata ) + ); + + relobi_r_other_encoder #( + .Cfg (Cfg), + .r_optional_t (r_optional_t) + ) i_r_remaining_enc ( + .rid_i (rsp_i.r.rid), + .err_i (rsp_i.r.err), + .r_optional_i(rsp_i.r.r_optional), + .other_ecc_o (rel_rsp_o.r.other_ecc) + ); + assign rel_rsp_o.r.rid = rsp_i.r.rid; + assign rel_rsp_o.r.err = rsp_i.r.err; + assign rel_rsp_o.r.r_optional = rsp_i.r.r_optional; + +endmodule diff --git a/src/relobi_demux.sv b/src/relobi_demux.sv new file mode 100644 index 0000000..2af2299 --- /dev/null +++ b/src/relobi_demux.sv @@ -0,0 +1,278 @@ +// 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 + +module relobi_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 r_chan struct for all ports. + parameter type obi_r_chan_t = logic, + /// The optional r_chan struct for all ports. + parameter type obi_r_optional_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, + /// Use TMR for select signal + parameter bit TmrSelect = 1'b1, + /// The type of the port select signal. + parameter type select_t = logic [$clog2(NumMgrPorts)-1:0], + parameter int unsigned SelWidth = TmrSelect ? 3 : 1 +) ( + input logic clk_i, + input logic rst_ni, + + input select_t [ SelWidth-1:0] 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, + + output logic [1:0] fault_o +); + + if (ObiCfg.Integrity) begin : gen_integrity_err + $fatal(1, "unimplemented"); + end + + logic [6:0] faults; + assign fault_o[0] = |faults; + assign fault_o[1] = 1'b0; // reserved for future use + + // stall requests to ensure in-order behavior (could be handled differently with rready) + localparam int unsigned CounterWidth = cf_math_pkg::idx_width(NumMaxTrans); + + // Internals TMR'd + + logic [2:0][NumMgrPorts-1:0] mgr_ports_req; + + obi_r_chan_t [NumMgrPorts-1:0] mgr_ports_rsp_r; + logic [2:0][NumMgrPorts-1:0] mgr_ports_rsp_rvalid; + logic [2:0][NumMgrPorts-1:0] mgr_ports_gnt; + obi_r_chan_t [2:0] sbr_port_rsp_r; + logic [2:0] sbr_port_rready; + + for (genvar i = 0; i < NumMgrPorts; i++) begin : gen_mgr_rsp + assign mgr_ports_rsp_r[i] = mgr_ports_rsp_i[i].r; + for (genvar j = 0; j < 3; j++) begin : gen_mgr_rsp_valid + assign mgr_ports_rsp_rvalid[j][i] = mgr_ports_rsp_i[i].rvalid[j]; + assign mgr_ports_gnt[j][i] = mgr_ports_rsp_i[i].gnt[j]; + end + end + + select_t [2:0] select_d_sync; + select_t [2:0][1:0] alt_select_d_sync; + logic [2:0][CounterWidth:0] counter_d_sync; + logic [2:0][1:0][CounterWidth:0] alt_counter_d_sync; + for (genvar i = 0; i < 3; i++) begin : gen_tmr_part + for (genvar j = 0; j < 2; j++) begin : gen_alt_sync + assign alt_select_d_sync[i][j] = select_d_sync[(i+j+1) % 3]; + assign alt_counter_d_sync[i][j] = counter_d_sync[(i+j+1) % 3]; + end + relobi_demux_tmr_part #( + .NumMgrPorts (NumMgrPorts), + .CounterWidth (CounterWidth), + .select_t (select_t), + .obi_r_chan_t (obi_r_chan_t), + .TmrBeforeReg (1'b0) + ) i_tmr_part ( + .clk_i (clk_i), + .rst_ni (rst_ni), + .select_i (TmrSelect ? sbr_port_select_i[i] : sbr_port_select_i[0]), + .sbr_port_req (sbr_port_req_i.req[i]), + .sbr_port_gnt (sbr_port_rsp_o.gnt[i]), + .mgr_ports_req (mgr_ports_req[i]), + .mgr_ports_gnt (mgr_ports_gnt[i]), + .mgr_ports_rsp_r (mgr_ports_rsp_r), + .mgr_ports_rsp_rvalid (mgr_ports_rsp_rvalid[i]), + .sbr_port_rsp_r (sbr_port_rsp_r[i]), + .sbr_port_rsp_rvalid (sbr_port_rsp_o.rvalid[i]), + .sbr_port_rready (sbr_port_rready[i]), + .select_d_sync (select_d_sync[i]), + .counter_d_sync (counter_d_sync[i]), + .alt_select_d_sync (alt_select_d_sync[i]), + .alt_counter_d_sync (alt_counter_d_sync[i]), + .fault_o (faults[2*i+1:2*i]) + ); + end + + for (genvar i = 0; i < NumMgrPorts; i++) begin : gen_mgr_req + assign mgr_ports_req_o[i].req[0] = mgr_ports_req[0][i]; + assign mgr_ports_req_o[i].req[1] = mgr_ports_req[1][i]; + assign mgr_ports_req_o[i].req[2] = mgr_ports_req[2][i]; + assign mgr_ports_req_o[i].a = sbr_port_req_i.a; + end + + relobi_tmr_r #( + .ObiCfg (ObiCfg), + .obi_r_chan_t(obi_r_chan_t), + .r_optional_t (obi_r_optional_t) + ) i_r_vote ( + .three_r_i(sbr_port_rsp_r), + .voted_r_o(sbr_port_rsp_o.r), + .fault_o (faults[6]) + ); + + 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 = 3'b111; + end + +endmodule + +(* no_ungroup *) +(* no_boundary_optimization *) +module relobi_demux_tmr_part #( + parameter int unsigned NumMgrPorts = 32'd0, + parameter int unsigned CounterWidth = 7, + parameter type select_t = logic [$clog2(NumMgrPorts)-1:0], + parameter type obi_r_chan_t = logic, + parameter bit TmrBeforeReg = 1'b0 +) ( + input logic clk_i, + input logic rst_ni, + input select_t select_i, + input logic sbr_port_req, + output logic sbr_port_gnt, + output logic [NumMgrPorts-1:0] mgr_ports_req, + input logic [NumMgrPorts-1:0] mgr_ports_gnt, + input obi_r_chan_t [NumMgrPorts-1:0] mgr_ports_rsp_r, + input logic [NumMgrPorts-1:0] mgr_ports_rsp_rvalid, + output obi_r_chan_t sbr_port_rsp_r, + output logic sbr_port_rsp_rvalid, + input logic sbr_port_rready, + output select_t select_d_sync, + output logic [CounterWidth:0] counter_d_sync, + input select_t [1:0] alt_select_d_sync, + input logic [1:0][CounterWidth:0] alt_counter_d_sync, + output logic [1:0] fault_o +); + + + logic cnt_up, cnt_down, overflow; + logic [CounterWidth-1:0] in_flight; + + select_t select_d, select_q; + logic [CounterWidth:0] counter_d, counter_q; + + always_comb begin : proc_req + select_d = select_q; + cnt_up = 1'b0; + for (int j = 0; j < NumMgrPorts; j++) begin + mgr_ports_req[j] = 1'b0; + end + sbr_port_gnt = 1'b0; + if (!overflow) begin + if (select_i == select_q || + in_flight == '0) begin + mgr_ports_req[select_i] = sbr_port_req; + sbr_port_gnt = mgr_ports_gnt[select_i]; + end + end + + if (mgr_ports_req[select_i] && mgr_ports_gnt[select_i]) begin + select_d = select_i; + cnt_up = 1'b1; + end + end + + assign sbr_port_rsp_r = mgr_ports_rsp_r[select_q]; + assign sbr_port_rsp_rvalid = mgr_ports_rsp_rvalid[select_q]; + + // Could be voted, but with only one error source (either select_q or rvalid) should suffice + assign cnt_down = mgr_ports_rsp_rvalid[select_q] && sbr_port_rready; + + assign overflow = counter_q[CounterWidth]; + assign in_flight = counter_q[CounterWidth-1:0]; + + always_comb begin + counter_d = counter_q; + + if (cnt_up & ~cnt_down) begin + counter_d = counter_q + {{CounterWidth-1{1'b0}}, 1'b1}; + end else if (cnt_down & ~cnt_up) begin + counter_d = counter_q - {{CounterWidth-1{1'b0}}, 1'b1}; + end + end + + if (TmrBeforeReg) begin : gen_tmr_before_reg + select_t select_d_voted; + logic [CounterWidth:0] counter_d_voted; + assign select_d_sync = select_d; + assign counter_d_sync = counter_d; + bitwise_TMR_voter_fail #( + .DataWidth( $clog2(NumMgrPorts) ) + ) i_select_vote ( + .a_i (select_d), + .b_i (alt_select_d_sync[0]), + .c_i (alt_select_d_sync[1]), + .majority_o (select_d_voted), + .fault_detected_o (fault_o[0]) + ); + bitwise_TMR_voter_fail #( + .DataWidth( CounterWidth+1 ) + ) i_counter_vote ( + .a_i (counter_d), + .b_i (alt_counter_d_sync[0]), + .c_i (alt_counter_d_sync[1]), + .majority_o (counter_d_voted), + .fault_detected_o (fault_o[1]) + ); + always_ff @(posedge clk_i or negedge rst_ni) begin : proc_select + if(!rst_ni) begin + counter_q <= '0; + select_q <= '0; + end else begin + counter_q <= counter_d_voted; + select_q <= select_d_voted; + end + end + end else begin : gen_tmr_after_reg + select_t select_d_next; + logic [CounterWidth:0] counter_d_next; + assign select_d_sync = select_d_next; + assign counter_d_sync = counter_d_next; + bitwise_TMR_voter_fail #( + .DataWidth( $clog2(NumMgrPorts) ) + ) i_select_vote ( + .a_i (select_d_next), + .b_i (alt_select_d_sync[0]), + .c_i (alt_select_d_sync[1]), + .majority_o (select_q), + .fault_detected_o (fault_o[0]) + ); + bitwise_TMR_voter_fail #( + .DataWidth( CounterWidth+1 ) + ) i_counter_vote ( + .a_i (counter_d_next), + .b_i (alt_counter_d_sync[0]), + .c_i (alt_counter_d_sync[1]), + .majority_o (counter_q), + .fault_detected_o (fault_o[1]) + ); + always_ff @(posedge clk_i or negedge rst_ni) begin : proc_select + if(!rst_ni) begin + counter_d_next <= '0; + select_d_next <= '0; + end else begin + counter_d_next <= counter_d; + select_d_next <= select_d; + end + end + + end + + + +endmodule diff --git a/src/relobi_encoder.sv b/src/relobi_encoder.sv new file mode 100644 index 0000000..b3ba32f --- /dev/null +++ b/src/relobi_encoder.sv @@ -0,0 +1,123 @@ +// 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 + +module relobi_encoder #( + /// Configuration of the bus + parameter obi_pkg::obi_cfg_t Cfg = obi_pkg::ObiDefaultConfig, + + parameter type relobi_req_t = logic, + parameter type relobi_rsp_t = logic, + parameter type obi_req_t = logic, + parameter type obi_rsp_t = logic, + parameter type a_optional_t = logic, + parameter type r_optional_t = logic +) ( + input obi_req_t req_i, + output obi_rsp_t rsp_o, + + output relobi_req_t rel_req_o, + input relobi_rsp_t rel_rsp_i, + + output logic [1:0] fault_o +); + + logic [1:0][2:0] voter_errs; + logic [1:0] voter_errs_red; + logic voter_errs_red_red; + logic [1:0][1:0] hsiao_errs; + logic [2:0][1:0] hsiao_errs_gated; + logic [1:0][1:0] hsiao_errs_transpose; + logic [1:0] hsiao_errs_transpose_red; + + for (genvar i = 0; i < 2; i++) begin : gen_hsiao_errs_transpose + assign voter_errs_red[i] = |voter_errs[i]; + assign hsiao_errs_transpose_red[i] = |hsiao_errs_transpose[i]; + for (genvar j = 0; j < 2; j++) begin : gen_hsiao_errs_transpose_inner + assign hsiao_errs_transpose[i][j] = hsiao_errs_gated[j][i]; + end + end + + assign voter_errs_red_red = |voter_errs_red; + assign fault_o[0] = voter_errs_red_red | hsiao_errs_transpose_red[0]; + assign fault_o[1] = hsiao_errs_transpose_red[1]; + + assign rel_req_o.req = {3{req_i.req}}; + + TMR_voter_detect i_req_gnt_vote ( + .a_i (rel_rsp_i.gnt[0]), + .b_i (rel_rsp_i.gnt[1]), + .c_i (rel_rsp_i.gnt[2]), + .majority_o (rsp_o.gnt), + .error_cba_o(voter_errs[0]) + ); + + if (Cfg.UseRReady) begin : gen_rready_multiply + assign rel_req_o.rready = {3{req_i.rready}}; + end + + TMR_voter_detect i_rsp_valid_vote ( + .a_i (rel_rsp_i.rvalid[0]), + .b_i (rel_rsp_i.rvalid[1]), + .c_i (rel_rsp_i.rvalid[2]), + .majority_o (rsp_o.rvalid), + .error_cba_o(voter_errs[1]) + ); + + hsiao_ecc_enc #( + .DataWidth ( Cfg.AddrWidth ) + ) i_addr_enc ( + .in ( req_i.a.addr ), + .out( rel_req_o.a.addr ) + ); + + hsiao_ecc_enc #( + .DataWidth ( Cfg.DataWidth ) + ) i_wdata_enc ( + .in ( req_i.a.wdata ), + .out( rel_req_o.a.wdata ) + ); + + relobi_a_other_encoder #( + .Cfg (Cfg), + .a_optional_t (a_optional_t) + ) i_a_remaining_enc ( + .we_i (req_i.a.we), + .be_i (req_i.a.be), + .aid_i (req_i.a.aid), + .a_optional_i(req_i.a.a_optional), + .other_ecc_o (rel_req_o.a.other_ecc) + ); + assign rel_req_o.a.we = req_i.a.we; + assign rel_req_o.a.be = req_i.a.be; + assign rel_req_o.a.aid = req_i.a.aid; + assign rel_req_o.a.a_optional = req_i.a.a_optional; + + hsiao_ecc_dec #( + .DataWidth ( Cfg.DataWidth ) + ) i_rdata_dec ( + .in ( rel_rsp_i.r.rdata ), + .out ( rsp_o.r.rdata ), + .syndrome_o(), + .err_o (hsiao_errs[0]) + ); + assign hsiao_errs_gated[0] = rel_rsp_i.rvalid[0] ? hsiao_errs[0] : '0; + + relobi_r_other_decoder #( + .Cfg (Cfg), + .r_optional_t (r_optional_t) + ) i_r_remaining_dec ( + .rid_i (rel_rsp_i.r.rid), + .err_i (rel_rsp_i.r.err), + .r_optional_i(rel_rsp_i.r.r_optional), + .other_ecc_i (rel_rsp_i.r.other_ecc), + .rid_o (rsp_o.r.rid), + .err_o (rsp_o.r.err), + .r_optional_o(rsp_o.r.r_optional), + .fault_o (hsiao_errs[1]) + ); + assign hsiao_errs_gated[1] = rel_rsp_i.rvalid[0] ? hsiao_errs[1] : '0; + +endmodule diff --git a/src/relobi_err_sbr.sv b/src/relobi_err_sbr.sv new file mode 100644 index 0000000..13d175c --- /dev/null +++ b/src/relobi_err_sbr.sv @@ -0,0 +1,234 @@ +// 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 + +module relobi_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, + parameter type a_optional_t = logic, + parameter type r_optional_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, + + output logic [1:0] fault_o +); + + logic [3:0][1:0] hsiao_errs; + logic [1:0][3:0] hsiao_errs_transpose; + logic [1:0] voter_errs; + for (genvar i = 0; i < 2; i++) begin : gen_hsiao_errs_transpose + for (genvar j = 0; j < 4; j++) begin : gen_hsiao_errs_transpose_inner + assign hsiao_errs_transpose[i][j] = hsiao_errs[j][i]; + end + end + assign fault_o[0] = |voter_errs | |hsiao_errs_transpose[0]; + assign fault_o[1] = |hsiao_errs_transpose[1]; + + logic [ObiCfg.IdWidth-1:0] rid_d, rid_q; + logic [2:0][ObiCfg.IdWidth-1:0] rid_tmr; + logic [2:0] fifo_full, fifo_empty, fifo_pop; + + logic [relobi_pkg::relobi_r_other_ecc_width(ObiCfg)-1:0] other_ecc_d, other_ecc_q; + logic [2:0][relobi_pkg::relobi_r_other_ecc_width(ObiCfg)-1:0] other_ecc_tmr; + logic [ObiCfg.DataWidth+hsiao_ecc_pkg::min_ecc(ObiCfg.DataWidth)-1:0] rdata_encoded; + + hsiao_ecc_enc #( + .DataWidth (ObiCfg.DataWidth) + ) i_rdata_encoder ( + .in ( RspData ), + .out ( rdata_encoded ) + ); + + for (genvar i = 0; i < 3; i++) begin : gen_tmr_part + relobi_err_sbr_tmr_part #( + .ObiCfg(ObiCfg), + .a_optional_t(a_optional_t), + .r_optional_t(r_optional_t) + ) i_tmr_part ( + .we_i(obi_req_i.a.we), + .be_i(obi_req_i.a.be), + .aid_i(obi_req_i.a.aid), + .a_optional_i(obi_req_i.a.a_optional), + .other_ecc_i(obi_req_i.a.other_ecc), + + .rid_o(rid_tmr[i]), + .other_ecc_o(other_ecc_tmr[i]), + + .fault_o(hsiao_errs[i]) + ); + end + + bitwise_TMR_voter_fail #( + .DataWidth( ObiCfg.IdWidth ) + ) i_rid_vote ( + .a_i (rid_tmr[0]), + .b_i (rid_tmr[1]), + .c_i (rid_tmr[2]), + .majority_o (rid_d), + .fault_detected_o(voter_errs[0]) + ); + bitwise_TMR_voter_fail #( + .DataWidth( relobi_pkg::relobi_r_other_ecc_width(ObiCfg) ) + ) i_other_ecc_vote ( + .a_i (other_ecc_tmr[0]), + .b_i (other_ecc_tmr[1]), + .c_i (other_ecc_tmr[2]), + .majority_o (other_ecc_d), + .fault_detected_o(voter_errs[1]) + ); + + always_comb begin + obi_rsp_o.r.rdata = '0; + obi_rsp_o.r.rdata = rdata_encoded; + obi_rsp_o.r.rid = rid_q; + obi_rsp_o.r.err = 1'b1; + obi_rsp_o.r.r_optional = '0; + obi_rsp_o.r.other_ecc = other_ecc_q; + obi_rsp_o.gnt = ~fifo_full; + obi_rsp_o.rvalid = ~fifo_empty; + end + + if (ObiCfg.UseRReady) begin : gen_rready_fifo + assign fifo_pop = obi_rsp_o.rvalid && obi_req_i.rready; + rel_fifo #( + .Depth ( ObiCfg.UseRReady ? NumMaxTrans : 1 ), + .FallThrough ( 1'b0 ), + .DataWidth ( ObiCfg.IdWidth + relobi_pkg::relobi_r_other_ecc_width(ObiCfg) ), + .TmrStatus ( 1'b1 ), + .DataHasEcc ( 1'b1 ) + ) i_id_fifo ( + .clk_i, + .rst_ni, + .testmode_i, + .flush_i ( '0 ), + .full_o ( fifo_full ), + .empty_o ( fifo_empty ), + .usage_o (), + .data_i ( {other_ecc_d, rid_d} ), + .push_i ( obi_req_i.req & obi_rsp_o.gnt ), + .data_o ( {other_ecc_q, rid_q} ), + .pop_i ( fifo_pop ), + .fault_o (hsiao_errs[3]) + ); + end else begin : gen_no_rready_fifo + assign fifo_full = '0; + assign fifo_pop = '0; + assign hsiao_errs[3] = '0; + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + rid_q <= '0; + other_ecc_q <= '0; + fifo_empty <= '1; + end else begin + rid_q <= rid_d; + other_ecc_q <= other_ecc_d; + fifo_empty <= ~(obi_req_i.req & obi_rsp_o.gnt); + end + end + end + +endmodule + +module relobi_err_sbr_tmr_part #( + parameter obi_pkg::obi_cfg_t ObiCfg = obi_pkg::ObiDefaultConfig, + parameter type a_optional_t = logic, + parameter type r_optional_t = logic + +) ( + input logic we_i, + input logic [ObiCfg.DataWidth/8-1:0] be_i, + input logic [ObiCfg.IdWidth -1:0] aid_i, + input a_optional_t a_optional_i, + input logic [relobi_pkg::relobi_a_other_ecc_width(ObiCfg)-1:0] other_ecc_i, + + output logic [ObiCfg.IdWidth -1:0] rid_o, + output logic [relobi_pkg::relobi_r_other_ecc_width(ObiCfg)-1:0] other_ecc_o, + + output logic [1:0] fault_o +); + + relobi_a_other_decoder #( + .Cfg(ObiCfg), + .a_optional_t(a_optional_t) + ) i_a_other_decoder ( + .we_i(we_i), + .be_i(be_i), + .aid_i(aid_i), + .a_optional_i(a_optional_i), + .other_ecc_i(other_ecc_i), + .we_o(), + .be_o(), + .aid_o(rid_o), + .a_optional_o(), + .fault_o(fault_o) + ); + + relobi_r_other_encoder #( + .Cfg(ObiCfg), + .r_optional_t(r_optional_t) + ) i_other_ecc_encoder ( + .rid_i(rid_o), + .err_i(1'b1), // Always error + .r_optional_i('0), // No optional fields in error response + .other_ecc_o(other_ecc_o) + ); + +endmodule + +`include "obi/typedef.svh" +`include "obi/assign.svh" + +module relobi_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 diff --git a/src/relobi_isolate.sv b/src/relobi_isolate.sv new file mode 100644 index 0000000..fc82ecc --- /dev/null +++ b/src/relobi_isolate.sv @@ -0,0 +1,329 @@ +// Copyright 2025 ETH Zurich and University of Bologna. +// Solderpad Hardware License, Version 0.51, see LICENSE for details. +// SPDX-License-Identifier: SHL-0.51 + +// Michael Rogenmoser + +module relobi_isolate #( + /// The OBI configuration. + 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, + /// The a_chan struct for all ports. + parameter type obi_a_chan_t = logic, + /// The r_chan struct for all ports. + parameter type obi_r_chan_t = logic, + /// The optional a_chan struct for all ports. + parameter type obi_a_optional_t = logic, + /// The optional r_chan struct for all ports. + parameter type obi_r_optional_t = logic, + /// Maximum number of pending requests per channel + parameter int unsigned MaxPending = 4, + /// Gracefully terminate all incoming transactions in case of isolation by returning proper error + /// responses. + parameter bit TerminateTransaction = 1'b0, + /// Use TMR for isolate signals + parameter bit TmrSelect = 1'b0, + parameter int unsigned SelWidth = TmrSelect ? 3 : 1 +) ( + input logic clk_i, + input logic rst_ni, + + input obi_req_t sbr_port_req_i, + output obi_rsp_t sbr_port_rsp_o, + + output obi_req_t mgr_port_req_o, + input obi_rsp_t mgr_port_rsp_i, + + input logic [SelWidth-1:0] isolate_i, + output logic [SelWidth-1:0] isolated_o, + + output logic fault_o +); + + // plus 1 in clog for accouning no open transaction + localparam int unsigned CounterWidth = $clog2(MaxPending + 32'd1); + typedef logic [CounterWidth-1:0] cnt_t; + + logic [3:0] faults; + assign fault_o = |faults; + + obi_req_t in_req, out_req; + obi_rsp_t in_rsp, out_rsp; + + if (TerminateTransaction) begin : gen_err_rsp + obi_req_t err_req; + obi_rsp_t err_rsp; + // demux to error subordinate + relobi_demux #( + .ObiCfg ( ObiCfg ), + .obi_req_t ( obi_req_t ), + .obi_rsp_t ( obi_rsp_t ), + .obi_r_chan_t( obi_r_chan_t), + .obi_r_optional_t( obi_r_optional_t), + .NumMgrPorts ( 2 ), + .NumMaxTrans ( MaxPending ), + .TmrSelect ( TmrSelect ) + ) i_demux ( + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), + + .sbr_port_select_i ( isolated_o ), + .sbr_port_req_i ( sbr_port_req_i ), + .sbr_port_rsp_o ( sbr_port_rsp_o ), + + .mgr_ports_req_o ( { in_req, err_req } ), + .mgr_ports_rsp_i ( { in_rsp, err_rsp } ) + ); + + relobi_err_sbr #( + .ObiCfg ( ObiCfg ), + .obi_req_t ( obi_req_t ), + .obi_rsp_t ( obi_rsp_t ), + .a_optional_t( obi_a_optional_t), + .r_optional_t( obi_r_optional_t), + .NumMaxTrans ( MaxPending ) + ) i_err_sbr ( + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), + .testmode_i ( 1'b0 ), + + .obi_req_i ( err_req ), + .obi_rsp_o ( err_rsp ) + ); + end else begin : gen_no_err_rsp + assign in_req = sbr_port_req_i; + assign sbr_port_rsp_o = in_rsp; + end + + logic [2:0] rready, mgr_rready; + logic [2:0] isolate_in, isolated_out; + + if (TmrSelect) begin : gen_tmr_isolate + assign isolate_in = isolate_i; + assign isolated_o = isolated_out; + assign faults[0] = 1'b0; + end else begin : gen_no_tmr_isolate + assign isolate_in[0] = isolate_i; + assign isolate_in[1] = isolate_i; + assign isolate_in[2] = isolate_i; + TMR_voter_fail i_isolate_vote ( + .a_i (isolated_out[0]), + .b_i (isolated_out[1]), + .c_i (isolated_out[2]), + .majority_o (isolated_o), + .fault_detected_o(faults[0]) + ); + end + + if (ObiCfg.UseRReady) begin : gen_rready + assign rready = in_rsp.rready; + always_comb begin + mgr_port_req_o = out_req; + mgr_port_req_o.rready = mgr_rready; + end + end else begin : gen_no_rready + assign rready = 1'b1; + assign mgr_port_req_o = out_req; + end + + logic [2:0][1:0] isolate_state_sync; + logic [2:0][1:0][1:0] alt_isolate_state_sync; + logic [2:0][CounterWidth-1:0] in_flight_sync; + logic [2:0][1:0][CounterWidth-1:0] alt_in_flight_sync; + for (genvar i = 0; i < 3; i++) begin : gen_tmr_part + for (genvar j = 0; j < 2; j++) begin : gen_alt_sync + assign alt_isolate_state_sync[i][j] = isolate_state_sync[(i+j+1) % 3]; + assign alt_in_flight_sync[i][j] = in_flight_sync[(i+j+1) % 3]; + end + relobi_isolate_tmr_part #( + .CounterWidth ( CounterWidth ), + .cnt_t ( cnt_t ) + ) i_tmr_part ( + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), + + .in_req_req_i ( in_req.req[i] ), + .in_rsp_gnt_o ( in_rsp.gnt[i] ), + .in_rsp_rvalid_o ( in_rsp.rvalid[i] ), + .out_req_req_o ( out_req.req[i] ), + .out_rsp_gnt_i ( mgr_port_rsp_i.gnt[i] ), + .out_rsp_rvalid_i ( mgr_port_rsp_i.rvalid[i] ), + .mgr_rready_o ( mgr_rready[i] ), + .rready_i ( rready[i] ), + + .isolate_i ( isolate_in[i] ), + .isolated_o ( isolated_out[i] ), + + .in_flight_sync_o ( in_flight_sync[i] ), + .alt_in_flight_sync_i ( alt_in_flight_sync[i] ), + .isolate_state_sync_o ( isolate_state_sync[i] ), + .alt_isolate_state_sync_i( alt_isolate_state_sync[i] ), + + .fault_o ( faults[i+1] ) + ); + end + + logic [$bits(obi_a_chan_t)-1:0] in_req_a, out_req_a; + logic [$bits(obi_r_chan_t)-1:0] in_rsp_r, out_rsp_r; + assign in_req_a = in_req.a; + assign out_req.a = obi_a_chan_t'(out_req_a); + assign in_rsp.r = obi_r_chan_t'(in_rsp_r); + assign out_rsp_r = out_rsp.r; + + for (genvar i = 0; i < $bits(obi_a_chan_t); i++) begin : gen_chan_connections + logic isolated_out_voted; + TMR_voter_fail i_isolate_vote_tmr_part ( + .a_i (isolated_out[0]), + .b_i (isolated_out[1]), + .c_i (isolated_out[2]), + .majority_o (isolated_out_voted), + .fault_detected_o(faults[4+i]) + ); + assign out_req_a[i] = isolated_out_voted ? '0 : in_req_a[i]; + if (i < $bits(obi_r_chan_t)) begin + assign in_rsp_r[i] = isolated_out_voted ? '0 : in_rsp_r[i]; + end + end + +endmodule + +module relobi_isolate_tmr_part #( + parameter int unsigned MaxPending = 4, + parameter int unsigned CounterWidth = $clog2(MaxPending + 32'd1), + parameter type cnt_t = logic[CounterWidth-1:0] +) ( + input logic clk_i, + input logic rst_ni, + + input logic in_req_req_i, + output logic in_rsp_gnt_o, + output logic in_rsp_rvalid_o, + output logic out_req_req_o, + input logic out_rsp_gnt_i, + input logic out_rsp_rvalid_i, + output logic mgr_rready_o, + input logic rready_i, + + input logic isolate_i, + output logic isolated_o, + + output cnt_t in_flight_sync_o, + input cnt_t [1:0] alt_in_flight_sync_i, + output logic [1:0] isolate_state_sync_o, + input logic [1:0][1:0] alt_isolate_state_sync_i, + output logic fault_o +); + + typedef enum logic [1:0] { + Normal, + Hold, + Drain, + Isolate + } isolate_state_e; + + isolate_state_e isolate_state_d, isolate_state_q, isolate_state_q_voted; + cnt_t in_flight_d, in_flight_q, in_flight_q_voted; + logic [1:0] faults; + assign fault_o = |faults; + + // Update counters + always_comb begin + in_flight_d = in_flight_q_voted; + if (out_req_req_o && (isolate_state_q_voted == Normal)) begin + in_flight_d++; + end + if (out_rsp_rvalid_i && rready_i) begin + in_flight_d--; + end + end + + // Perform isolation + always_comb begin + isolate_state_d = isolate_state_q_voted; + // Connect channel by default + out_req_req_o = in_req_req_i; + in_rsp_gnt_o = out_rsp_gnt_i; + in_rsp_rvalid_o = out_rsp_rvalid_i; + mgr_rready_o = rready_i; + unique case (isolate_state_q_voted) + Normal: begin + // Block if in flight transactions overflows + if (in_flight_q_voted >= cnt_t'(MaxPending)) begin + out_req_req_o = 1'b0; + in_rsp_gnt_o = 1'b0; + if (isolate_i) begin + isolate_state_d = Hold; + end + end else begin + if (in_req_req_i && !out_rsp_gnt_i) begin + isolate_state_d = Hold; + end else begin + if (isolate_i) begin + isolate_state_d = Drain; + end + end + end + end + Hold: begin + out_req_req_o = 1'b1; + if (out_rsp_gnt_i) begin + isolate_state_d = isolate_i ? Drain : Normal; + end + end + Drain: begin + out_req_req_o = 1'b0; + in_rsp_gnt_o = 1'b0; + if (in_flight_q_voted == '0) begin + isolate_state_d = Isolate; + end + end + Isolate: begin + out_req_req_o = 1'b0; + in_rsp_gnt_o = 1'b0; + in_rsp_rvalid_o = 1'b0; + mgr_rready_o = 1'b0; + if (!isolate_i) begin + isolate_state_d = Normal; + end + end + default: ; + endcase + end + + assign isolated_o = (isolate_state_q_voted == Isolate); + + bitwise_TMR_voter_fail #( + .DataWidth( $bits(isolate_state_e) ) + ) i_isolate_state_vote ( + .a_i (isolate_state_q), + .b_i (alt_isolate_state_sync_i[0]), + .c_i (alt_isolate_state_sync_i[1]), + .majority_o (isolate_state_q_voted), + .fault_detected_o(faults[0]) + ); + assign isolate_state_sync_o = isolate_state_q; + bitwise_TMR_voter_fail #( + .DataWidth( CounterWidth ) + ) i_in_flight_vote ( + .a_i (in_flight_q), + .b_i (alt_in_flight_sync_i[0]), + .c_i (alt_in_flight_sync_i[1]), + .majority_o (in_flight_q_voted), + .fault_detected_o(faults[1]) + ); + assign in_flight_sync_o = in_flight_q; + + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + isolate_state_q <= Isolate; + in_flight_q <= '0; + end else begin + isolate_state_q <= isolate_state_d; + in_flight_q <= in_flight_d; + end + end + +endmodule diff --git a/src/relobi_mux.sv b/src/relobi_mux.sv new file mode 100644 index 0000000..02d3180 --- /dev/null +++ b/src/relobi_mux.sv @@ -0,0 +1,504 @@ +// 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 + +`include "obi/assign.svh" +`include "redundancy_cells/voters.svh" + +/// An OBI multiplexer. +module relobi_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 port (output port). + parameter type mgr_port_obi_rsp_t = sbr_port_obi_rsp_t, + /// The A channel struct for the manager port (output port). + parameter type mgr_port_a_chan_t = logic, + /// The R channel struct for the manager port (output port). + parameter type mgr_port_r_chan_t = logic, + /// The A channel optionals struct for all ports. + parameter type a_optional_t = logic, + /// The R channel optionals struct for all ports. + parameter type r_optional_t = logic, + /// 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, + + output logic [1:0] fault_o +); + if (NumSbrPorts <= 1) begin : gen_NumSbrPorts_err + $fatal(1, "unimplemented"); + end + if (MgrPortObiCfg.IdWidth < SbrPortObiCfg.IdWidth) begin : gen_IdWidthDecreasing_err + $fatal(1, "relobi_mux: IdWidth not allowed to decrease"); + end + if (MgrPortObiCfg.UseRReady != SbrPortObiCfg.UseRReady || + MgrPortObiCfg.CombGnt != SbrPortObiCfg.CombGnt || + MgrPortObiCfg.AddrWidth != SbrPortObiCfg.DataWidth || + MgrPortObiCfg.DataWidth != SbrPortObiCfg.DataWidth || + MgrPortObiCfg.Integrity != SbrPortObiCfg.Integrity || + MgrPortObiCfg.BeFull != SbrPortObiCfg.BeFull || + MgrPortObiCfg.OptionalCfg != SbrPortObiCfg.OptionalCfg ) begin : gen_ConfigDiff_err + $fatal(1, "relobi_mux: Configuration needs to be identical for mgr & sbr except IdWidth"); + end + + localparam int unsigned RequiredExtraIdWidth = $clog2(NumSbrPorts); + + logic [8:0][1:0] hsiao_faults; + logic [8:0][1:0] hsiao_faults_gated; + logic [1:0][8:0] hsiao_faults_transpose; + logic [4:0] voter_faults; + for (genvar i = 0; i < 9; i++) begin : gen_hsiao_faults_transpose + for (genvar j = 0; j < 2; j++) begin : gen_hsiao_faults_transpose_inner + assign hsiao_faults_transpose[j][i] = hsiao_faults_gated[i][j]; + end + end + assign fault_o[0] = |voter_faults | |hsiao_faults_transpose[0]; + assign fault_o[1] = |hsiao_faults_transpose[1]; + + logic [NumSbrPorts-1:0][2:0] sbr_ports_req, sbr_ports_gnt; + sbr_port_a_chan_t [NumSbrPorts-1:0] sbr_ports_a; + + sbr_port_a_chan_t mgr_port_a_in_sbr; + mgr_port_a_chan_t [2:0] mgr_port_a_tmr; + logic [2:0][RequiredExtraIdWidth-1:0] selected_id; + logic [2:0] mgr_port_req, fifo_full, fifo_pop; + + logic [2:0] rr_arb_mgr_port_gnt; + + logic [2:0][RequiredExtraIdWidth + hsiao_ecc_pkg::min_ecc(RequiredExtraIdWidth)-1:0] + selected_id_tmr_three; + logic [RequiredExtraIdWidth + hsiao_ecc_pkg::min_ecc(RequiredExtraIdWidth)-1:0] + selected_id_tmr, response_id_encoded; + + logic [2:0][NumSbrPorts-1:0] sbr_rsp_rvalid; + logic [2:0][NumSbrPorts-1:0] sbr_req_rready; + logic [2:0] mgr_req_rready; + + sbr_port_r_chan_t [2:0] sbr_r_tmr; + + + 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 + + assign mgr_port_req_o.req = mgr_port_req & ~fifo_full; + assign rr_arb_mgr_port_gnt = mgr_port_rsp_i.gnt & ~fifo_full; + + for (genvar i = 0; i < 3; i++) begin : gen_tmr_part + relobi_mux_tmr_part #( + .SbrPortObiCfg ( SbrPortObiCfg ), + .MgrPortObiCfg ( MgrPortObiCfg ), + .NumSbrPorts ( NumSbrPorts ), + .mgr_port_a_chan_t ( mgr_port_a_chan_t ), + .mgr_port_r_chan_t ( mgr_port_r_chan_t ), + .sbr_port_a_chan_t ( sbr_port_a_chan_t ), + .sbr_port_r_chan_t ( sbr_port_r_chan_t ), + .a_optional_t ( a_optional_t ), + .r_optional_t ( r_optional_t ), + .RequiredExtraIdWidth( RequiredExtraIdWidth ), + .UseIdForRouting ( UseIdForRouting ) + ) i_tmr_part ( + .clk_i, + .rst_ni, + + .mgr_port_a_in_sbr ( mgr_port_a_in_sbr ), + .mgr_port_req ( mgr_port_req[i] ), + .selected_id ( selected_id[i] ), + .mgr_port_a_tmr ( mgr_port_a_tmr[i] ), + + .mgr_port_rsp_r ( UseIdForRouting || MgrPortObiCfg.IdWidth != SbrPortObiCfg.IdWidth ? + mgr_port_rsp_i.r : '0 ), + .mgr_port_rsp_rvalid( UseIdForRouting ? mgr_port_rsp_i.rvalid[i] : '0 ), + .selected_id_tmr_three ( selected_id_tmr_three[i] ), + .response_id_encoded( UseIdForRouting ? '0 : response_id_encoded ), + .fifo_pop ( UseIdForRouting ? '0 : fifo_pop[i] ), + + .mgr_rsp_rvalid ( mgr_port_rsp_i.rvalid[i] ), + .sbr_rsp_rvalid ( sbr_rsp_rvalid[i] ), + .sbr_req_rready ( MgrPortObiCfg.UseRReady ? sbr_req_rready[i] : '1 ), + .mgr_req_rready ( mgr_req_rready[i] ), + + .sbr_r_tmr ( sbr_r_tmr[i] ), + + .hsiao_faults ( hsiao_faults[3*i+2:3*i] ), + .hsiao_faults_gated( hsiao_faults_gated[2*i+1:2*i] ) + ); + end + + rel_rr_arb_tree #( + .NumIn ( NumSbrPorts ), + .DataType ( sbr_port_a_chan_t ), + .AxiVldRdy ( 1'b1 ), + .LockIn ( 1'b1 ), + .TmrStatus ( 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 ( rr_arb_mgr_port_gnt ), + .data_o ( mgr_port_a_in_sbr ), + + .idx_o ( selected_id ), + + .fault_o ( voter_faults[0] ) + ); + + if (MgrPortObiCfg.IdWidth == SbrPortObiCfg.IdWidth) begin : gen_aid_identical + assign mgr_port_req_o.a = mgr_port_a_in_sbr; + assign voter_faults[1] = '0; + end else begin : gen_aid_vote + bitwise_TMR_voter_fail #( + .DataWidth ( $bits(mgr_port_a_chan_t) ), + .VoterType ( 1 ) + ) i_a_tmr ( + .a_i ( mgr_port_a_tmr[0] ), + .b_i ( mgr_port_a_tmr[1] ), + .c_i ( mgr_port_a_tmr[2] ), + .majority_o ( mgr_port_req_o.a ), + .fault_detected_o ( voter_faults[1] ) + ); + end + + logic [2:0][SbrPortObiCfg.IdWidth-1:0] rsp_rid; + + if (UseIdForRouting) begin : gen_id_assign + if (!(MgrPortObiCfg.IdWidth > 0 && + (MgrPortObiCfg.IdWidth >= SbrPortObiCfg.IdWidth + + RequiredExtraIdWidth ))) begin : gen_IdWidth_err + $fatal(1, "UseIdForRouting requires MgrPort IdWidth to increase with log2(NumSbrPorts)"); + end + + assign fifo_full = 3'b0; + + assign voter_faults[3:2] = '0; + assign selected_id_tmr = '0; + + end else begin : gen_no_id_assign + + bitwise_TMR_voter_fail #( + .DataWidth ( RequiredExtraIdWidth + hsiao_ecc_pkg::min_ecc(RequiredExtraIdWidth) ), + .VoterType ( 1 ) + ) i_selected_id_tmr ( + .a_i ( selected_id_tmr_three[0] ), + .b_i ( selected_id_tmr_three[1] ), + .c_i ( selected_id_tmr_three[2] ), + .majority_o ( selected_id_tmr ), + .fault_detected_o ( voter_faults[2] ) + ); + + rel_fifo #( + .FallThrough( 1'b0 ), + .DataWidth ( RequiredExtraIdWidth + hsiao_ecc_pkg::min_ecc(RequiredExtraIdWidth) ), + .Depth ( NumMaxTrans ), + .TmrStatus ( 1'b1 ), + .DataHasEcc ( 1'b1 ), + .StatusFF ( 1'b0 ) + ) i_fifo ( + .clk_i, + .rst_ni, + .flush_i ('0), + .testmode_i, + + .full_o ( fifo_full ), + .empty_o (), + .usage_o (), + .data_i ( selected_id_tmr ), + .push_i ( mgr_port_req_o.req & mgr_port_rsp_i.gnt ), + .data_o ( response_id_encoded ), + .pop_i ( fifo_pop ), + + .fault_o (voter_faults[3]) + ); + + end + + if (MgrPortObiCfg.UseRReady) begin : gen_rready_connect + assign mgr_port_req_o.rready = mgr_req_rready; + for (genvar i = 0; i < NumSbrPorts; i++) begin : gen_sbr_ports_rready + for (genvar j = 0; j < 3; j++) begin : gen_sbr_req_rready + assign sbr_req_rready[j][i] = sbr_ports_req_i[i].rready[j]; + end + end + end else begin : gen_rready_tie + assign sbr_req_rready = '1; + end + sbr_port_r_chan_t [NumSbrPorts-1:0] sbr_rsp_r; + if (MgrPortObiCfg.IdWidth == SbrPortObiCfg.IdWidth) begin : gen_rid_identical + always_comb begin : proc_sbr_rsp + for (int i = 0; i < NumSbrPorts; i++) begin + // Always assign r struct to avoid triplication overhead + `OBI_SET_R_STRUCT(sbr_rsp_r[i], mgr_port_rsp_i.r); + sbr_rsp_r[i].other_ecc = mgr_port_rsp_i.r.other_ecc; + end + end + assign hsiao_faults_gated[8:6] = '0; + assign voter_faults[4] = '0; + end else begin : gen_rid_decrease + sbr_port_r_chan_t sbr_r; + + assign hsiao_faults_gated[6] = fifo_pop[0] ? hsiao_faults[2] : '0; + assign hsiao_faults_gated[7] = fifo_pop[1] ? hsiao_faults[5] : '0; + assign hsiao_faults_gated[8] = fifo_pop[2] ? hsiao_faults[8] : '0; + relobi_tmr_r #( + .ObiCfg (SbrPortObiCfg), + .obi_r_chan_t (sbr_port_r_chan_t), + .r_optional_t (r_optional_t) + ) tmr_r_vote ( + .three_r_i(sbr_r_tmr), + .voted_r_o(sbr_r), + .fault_o (voter_faults[4]) + ); + for (genvar i = 0; i < NumSbrPorts; i++) begin : gen_sbr_r_assign + assign sbr_rsp_r[i].rdata = sbr_r.rdata; + assign sbr_rsp_r[i].rid = sbr_r.rid; + assign sbr_rsp_r[i].err = sbr_r.err; + assign sbr_rsp_r[i].r_optional = sbr_r.r_optional; + assign sbr_rsp_r[i].other_ecc = sbr_r.other_ecc; + end + 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[2][i], + sbr_rsp_rvalid[1][i], + sbr_rsp_rvalid[0][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 + +(* no_ungroup *) +(* no_boundary_optimization *) +module relobi_mux_tmr_part #( + parameter obi_pkg::obi_cfg_t SbrPortObiCfg = obi_pkg::ObiDefaultConfig, + parameter obi_pkg::obi_cfg_t MgrPortObiCfg = SbrPortObiCfg, + parameter int unsigned NumSbrPorts = 32'd0, + parameter type mgr_port_a_chan_t = logic, + parameter type mgr_port_r_chan_t = logic, + parameter type sbr_port_a_chan_t = logic, + parameter type sbr_port_r_chan_t = logic, + parameter type a_optional_t = logic, + parameter type r_optional_t = logic, + parameter int unsigned RequiredExtraIdWidth = 0, + parameter bit UseIdForRouting = 1'b0 + +) ( + input logic clk_i, + input logic rst_ni, + input sbr_port_a_chan_t mgr_port_a_in_sbr, + input logic mgr_port_req, + input logic [RequiredExtraIdWidth-1:0] selected_id, + output mgr_port_a_chan_t mgr_port_a_tmr, + + // Only if UseIdForRouting is true or Id width increases + input mgr_port_r_chan_t mgr_port_rsp_r, + // Only if UseIdForRouting is true + input logic mgr_port_rsp_rvalid, + // Only if UseIdForRouting is false + output logic [RequiredExtraIdWidth+ + hsiao_ecc_pkg::min_ecc(RequiredExtraIdWidth)-1:0] selected_id_tmr_three, + input logic [RequiredExtraIdWidth+ + hsiao_ecc_pkg::min_ecc(RequiredExtraIdWidth)-1:0] response_id_encoded, + input logic fifo_pop, + + output sbr_port_r_chan_t sbr_r_tmr, + + input logic mgr_rsp_rvalid, + output logic [NumSbrPorts-1:0] sbr_rsp_rvalid, + input logic [NumSbrPorts-1:0] sbr_req_rready, + output logic mgr_req_rready, + + output logic [2:0][1:0] hsiao_faults, + output logic [1:0][1:0] hsiao_faults_gated +); + + logic [RequiredExtraIdWidth-1:0] response_id; + + if (MgrPortObiCfg.IdWidth == SbrPortObiCfg.IdWidth) begin : gen_aid_identical + assign mgr_port_a_tmr = mgr_port_a_in_sbr; + assign hsiao_faults[0] = '0; + assign hsiao_faults_gated[0] = '0; + end else begin : gen_aid_extend + logic we; + logic [MgrPortObiCfg.DataWidth/8-1:0] be; + logic [SbrPortObiCfg.IdWidth-1:0] sbr_aid; + logic [MgrPortObiCfg.IdWidth-1:0] mgr_aid; + a_optional_t a_optional; + logic [relobi_pkg::relobi_a_other_ecc_width(MgrPortObiCfg)-1:0] other_ecc; + relobi_a_other_decoder #( + .Cfg (SbrPortObiCfg), + .a_optional_t (a_optional_t) + ) i_other_decode ( + .we_i (mgr_port_a_in_sbr.we), + .be_i (mgr_port_a_in_sbr.be), + .aid_i (mgr_port_a_in_sbr.aid), + .a_optional_i(mgr_port_a_in_sbr.a_optional), + .other_ecc_i (mgr_port_a_in_sbr.other_ecc), + .we_o (we), + .be_o (be), + .aid_o (sbr_aid), + .a_optional_o(a_optional), + + .fault_o (hsiao_faults[0]) + ); + assign hsiao_faults_gated[0] = mgr_port_req ? hsiao_faults[0] : '0; + if (MgrPortObiCfg.IdWidth >= SbrPortObiCfg.IdWidth + + RequiredExtraIdWidth ) begin : gen_aid_extend + always_comb begin + mgr_aid = '0; + mgr_aid[SbrPortObiCfg.IdWidth + RequiredExtraIdWidth-1:0] = {selected_id, sbr_aid}; + end + end else begin : gen_aid_noextend + always_comb begin + mgr_aid = '0; + mgr_aid[SbrPortObiCfg.IdWidth-1:0] = sbr_aid; + end + end + relobi_a_other_encoder #( + .Cfg (MgrPortObiCfg), + .a_optional_t (a_optional_t) + ) i_other_encode ( + .we_i (we), + .be_i (be), + .aid_i (mgr_aid), + .a_optional_i(a_optional), + .other_ecc_o (other_ecc) + ); + + assign mgr_port_a_tmr.addr = mgr_port_a_in_sbr.addr; + assign mgr_port_a_tmr.wdata = mgr_port_a_in_sbr.wdata; + assign mgr_port_a_tmr.we = we; + assign mgr_port_a_tmr.be = be; + assign mgr_port_a_tmr.aid = mgr_aid; + assign mgr_port_a_tmr.a_optional = a_optional; + assign mgr_port_a_tmr.other_ecc = other_ecc; + + end + + if (UseIdForRouting) begin : gen_id_assign + logic [MgrPortObiCfg.IdWidth-1:0] corr_rsp_rid; + logic [SbrPortObiCfg.IdWidth-1:0] rsp_rid; + relobi_r_other_decoder #( + .Cfg (MgrPortObiCfg), + .r_optional_t (r_optional_t) + ) i_r_other_decode ( + .rid_i (mgr_port_rsp_r.rid), + .err_i (mgr_port_rsp_r.err), + .r_optional_i(mgr_port_rsp_r.r_optional), + .other_ecc_i (mgr_port_rsp_r.other_ecc), + .rid_o (corr_rsp_rid), + .err_o (), + .r_optional_o(), + .fault_o (hsiao_faults[1]) + ); + assign hsiao_faults_gated[1] = mgr_port_rsp_rvalid ? hsiao_faults[1] : '0; + + assign {response_id, rsp_rid} = + corr_rsp_rid[SbrPortObiCfg.IdWidth + RequiredExtraIdWidth-1:0]; + + // TODO encode rsp_rid packet for sbrs? + end else begin : gen_no_id_assign + hsiao_ecc_enc #( + .DataWidth (RequiredExtraIdWidth) + ) i_ecc ( + .in (selected_id), + .out (selected_id_tmr_three) + ); + + hsiao_ecc_dec #( + .DataWidth (RequiredExtraIdWidth) + ) i_ecc_dec ( + .in (response_id_encoded), + .out (response_id), + .syndrome_o(), + .err_o (hsiao_faults[1]) + ); + assign hsiao_faults_gated[1] = fifo_pop ? hsiao_faults[1] : '0; + end + + always_comb begin + sbr_rsp_rvalid = '0; + sbr_rsp_rvalid[response_id] = mgr_rsp_rvalid; + end + if (MgrPortObiCfg.UseRReady) begin : gen_rready_connect + assign mgr_req_rready = sbr_req_rready[response_id]; + end else begin : gen_rready_tie + assign mgr_req_rready = '1; + end + + + if (MgrPortObiCfg.IdWidth == SbrPortObiCfg.IdWidth) begin : gen_rid_identical + assign hsiao_faults[2] = '0; + assign sbr_r_tmr = '0; + end else begin : gen_rid_decrease + logic [MgrPortObiCfg.IdWidth-1:0] mgr_rid_tmr; + relobi_r_other_decoder #( + .Cfg (MgrPortObiCfg), + .r_optional_t (r_optional_t) + ) i_r_other_decode ( + .rid_i (mgr_port_rsp_r.rid), + .err_i (mgr_port_rsp_r.err), + .r_optional_i(mgr_port_rsp_r.r_optional), + .other_ecc_i (mgr_port_rsp_r.other_ecc), + .rid_o (mgr_rid_tmr), + .err_o (sbr_r_tmr.err), + .r_optional_o(sbr_r_tmr.r_optional), + .fault_o (hsiao_faults[2]) + ); + assign sbr_r_tmr.rid = mgr_rid_tmr[SbrPortObiCfg.IdWidth-1:0]; + relobi_r_other_encoder #( + .Cfg (SbrPortObiCfg), + .r_optional_t (r_optional_t) + ) i_r_other_encode ( + .rid_i (sbr_r_tmr.rid), + .err_i (sbr_r_tmr.err), + .r_optional_i(sbr_r_tmr.r_optional), + .other_ecc_o (sbr_r_tmr.other_ecc) + ); + assign sbr_r_tmr.rdata = mgr_port_rsp_r.rdata; + + end + +endmodule diff --git a/src/relobi_pkg.sv b/src/relobi_pkg.sv new file mode 100644 index 0000000..77657bb --- /dev/null +++ b/src/relobi_pkg.sv @@ -0,0 +1,52 @@ +// Copyright 2024 ETH Zurich and University of Bologna. +// Solderpad Hardware License, Version 0.51, see LICENSE for details. +// SPDX-License-Identifier: SHL-0.51 + +// Michael Rogenmoser + +package relobi_pkg; + + // `let` is not supported by VCS, change it to a function + function automatic integer unsigned sigwidth (input integer unsigned width); + return (width != 32'd0) ? unsigned'(width) : 32'd1; + endfunction + + function automatic int unsigned relobi_a_other_width(input obi_pkg::obi_cfg_t Cfg); + + relobi_a_other_width = 1 /* we */ + + Cfg.DataWidth/8 /* be */ + + Cfg.IdWidth /* aid */ + +/* a_optional_t */ sigwidth((Cfg.OptionalCfg.UseAtop ? 6 : 0) + + (Cfg.OptionalCfg.UseMemtype ? 2 : 0) + + (Cfg.OptionalCfg.UseProt ? 3 : 0) + + (Cfg.OptionalCfg.UseDbg ? 1 : 0) + + Cfg.OptionalCfg.AUserWidth + + Cfg.OptionalCfg.WUserWidth + + Cfg.OptionalCfg.MidWidth + + Cfg.OptionalCfg.AChkWidth ); + + endfunction + + function automatic int unsigned relobi_a_other_ecc_width(input obi_pkg::obi_cfg_t Cfg); + + relobi_a_other_ecc_width = hsiao_ecc_pkg::min_ecc(relobi_a_other_width(Cfg)); + + endfunction + + function automatic int unsigned relobi_r_other_width(input obi_pkg::obi_cfg_t Cfg); + + relobi_r_other_width = 1 /* err */ + + Cfg.IdWidth /* rid */ + +/* r_optional_t */ sigwidth((Cfg.OptionalCfg.UseAtop ? 1 : 0) + + Cfg.OptionalCfg.RUserWidth + + Cfg.OptionalCfg.RChkWidth ); + + endfunction + + function automatic int unsigned relobi_r_other_ecc_width(input obi_pkg::obi_cfg_t Cfg); + + relobi_r_other_ecc_width = hsiao_ecc_pkg::min_ecc(relobi_r_other_width(Cfg)); + + endfunction + +endpackage diff --git a/src/relobi_r_other_corrector.sv b/src/relobi_r_other_corrector.sv new file mode 100644 index 0000000..b801a01 --- /dev/null +++ b/src/relobi_r_other_corrector.sv @@ -0,0 +1,43 @@ +// Copyright 2025 ETH Zurich and University of Bologna. +// Solderpad Hardware License, Version 0.51, see LICENSE for details. +// SPDX-License-Identifier: SHL-0.51 + +// Michael Rogenmoser + +module relobi_r_other_corrector #( + /// Configuration of the bus + parameter obi_pkg::obi_cfg_t Cfg = obi_pkg::ObiDefaultConfig, + + parameter type r_optional_t = logic, + parameter int unsigned OtherEccWidth = relobi_pkg::relobi_r_other_ecc_width(Cfg) +) ( + input logic [Cfg.IdWidth -1:0] rid_i, + input logic err_i, + input r_optional_t r_optional_i, + input logic [OtherEccWidth-1:0] other_ecc_i, + + output logic [Cfg.IdWidth -1:0] rid_o, + output logic err_o, + output r_optional_t r_optional_o, + output logic [ OtherEccWidth-1:0] other_ecc_o, + + output logic [1:0] fault_o +); + + + hsiao_ecc_cor #( + .DataWidth ( relobi_pkg::relobi_r_other_width(Cfg) ) + ) i_r_remaining_cor ( + .in ( {other_ecc_i, + rid_i, + err_i, + r_optional_i} ), + .out( {other_ecc_o, + rid_o, + err_o, + r_optional_o} ), + .syndrome_o(), + .err_o (fault_o) + ); + +endmodule diff --git a/src/relobi_r_other_decoder.sv b/src/relobi_r_other_decoder.sv new file mode 100644 index 0000000..a3d2a4b --- /dev/null +++ b/src/relobi_r_other_decoder.sv @@ -0,0 +1,41 @@ +// Copyright 2025 ETH Zurich and University of Bologna. +// Solderpad Hardware License, Version 0.51, see LICENSE for details. +// SPDX-License-Identifier: SHL-0.51 + +// Michael Rogenmoser + +module relobi_r_other_decoder #( + /// Configuration of the bus + parameter obi_pkg::obi_cfg_t Cfg = obi_pkg::ObiDefaultConfig, + + parameter type r_optional_t = logic, + parameter int unsigned OtherEccWidth = relobi_pkg::relobi_r_other_ecc_width(Cfg) +) ( + input logic [Cfg.IdWidth -1:0] rid_i, + input logic err_i, + input r_optional_t r_optional_i, + input logic [OtherEccWidth-1:0] other_ecc_i, + + output logic [Cfg.IdWidth -1:0] rid_o, + output logic err_o, + output r_optional_t r_optional_o, + + output logic [1:0] fault_o +); + + + hsiao_ecc_dec #( + .DataWidth ( relobi_pkg::relobi_r_other_width(Cfg) ) + ) i_r_remaining_dec ( + .in ( {other_ecc_i, + rid_i, + err_i, + r_optional_i} ), + .out( {rid_o, + err_o, + r_optional_o} ), + .syndrome_o(), + .err_o (fault_o) + ); + +endmodule diff --git a/src/relobi_r_other_encoder.sv b/src/relobi_r_other_encoder.sv new file mode 100644 index 0000000..07ea2bb --- /dev/null +++ b/src/relobi_r_other_encoder.sv @@ -0,0 +1,32 @@ +// Copyright 2025 ETH Zurich and University of Bologna. +// Solderpad Hardware License, Version 0.51, see LICENSE for details. +// SPDX-License-Identifier: SHL-0.51 + +// Michael Rogenmoser + +module relobi_r_other_encoder #( + /// Configuration of the bus + parameter obi_pkg::obi_cfg_t Cfg = obi_pkg::ObiDefaultConfig, + + parameter type r_optional_t = logic, + parameter int unsigned OtherEccWidth = relobi_pkg::relobi_r_other_ecc_width(Cfg) +) ( + input logic [Cfg.IdWidth -1:0] rid_i, + input logic err_i, + input r_optional_t r_optional_i, + + output logic [ OtherEccWidth-1:0] other_ecc_o +); + + logic [relobi_pkg::relobi_r_other_width(Cfg)-1:0] unused; + + hsiao_ecc_enc #( + .DataWidth (relobi_pkg::relobi_r_other_width(Cfg)) + ) i_a_remaining_enc ( + .in ( {rid_i, + err_i, + r_optional_i} ), + .out ( {other_ecc_o, unused} ) + ); + +endmodule diff --git a/src/relobi_sram_shim.sv b/src/relobi_sram_shim.sv new file mode 100644 index 0000000..551dff3 --- /dev/null +++ b/src/relobi_sram_shim.sv @@ -0,0 +1,403 @@ +// 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 + +// For single-cycle SRAMs, supports RMW for byte enable +module relobi_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 relobi_req_t = logic, + /// The response struct for all ports. + parameter type relobi_rsp_t = logic, + parameter type a_optional_t = logic, + parameter type r_optional_t = logic, + parameter bit EnableScrubber = 1'b0, + parameter int unsigned ScrubberMemWords = 256, + parameter bit ScrubberCorrectRead = 1'b1, + parameter int unsigned AddrWidth = EnableScrubber ? $clog2(ScrubberMemWords) : ObiCfg.AddrWidth +) ( + input logic clk_i, + input logic rst_ni, + + input relobi_req_t obi_req_i, + output relobi_rsp_t obi_rsp_o, + + output logic req_o, + output logic we_o, + output logic [AddrWidth-1:0] addr_o, + output logic [ObiCfg.DataWidth+hsiao_ecc_pkg::min_ecc(ObiCfg.DataWidth)-1:0] wdata_o, + + input logic gnt_i, // Should generally be 1'b1 + input logic [ObiCfg.DataWidth+hsiao_ecc_pkg::min_ecc(ObiCfg.DataWidth)-1:0] rdata_i, + + input logic scrub_trigger_i, // Set to 1'b0 to disable + output logic scrub_bit_corrected_o, + output logic scrub_uncorrectable_o, + output logic [1:0] fault_o +); + + 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 [7:0] voter_errs; + logic [11:0][1:0] hsiao_errs; + logic [1:0][11:0] hsiao_errs_transpose; + + for (genvar i = 0; i < 2; i++) begin : gen_hsiao_errs_transpose + for (genvar j = 0; j < 12; j++) begin : gen_hsiao_errs_transpose_inner + assign hsiao_errs_transpose[i][j] = hsiao_errs[j][i]; + end + end + + assign fault_o[0] = |voter_errs | |hsiao_errs_transpose[0]; + assign fault_o[1] = |hsiao_errs_transpose[1]; + + + logic [2:0] rvalid_d, rvalid_q; + logic [ObiCfg.IdWidth-1:0] id_d, id_q; + logic [relobi_pkg::relobi_r_other_ecc_width(ObiCfg)-1:0] other_ecc_d, other_ecc_q; + + logic [ObiCfg.DataWidth+hsiao_ecc_pkg::min_ecc(ObiCfg.DataWidth)-1:0] use_buffered; + logic [2:0][ObiCfg.DataWidth+hsiao_ecc_pkg::min_ecc(ObiCfg.DataWidth)-1:0] use_buffered_tmr; + logic [2:0] req_o_tmr, req_o_scrubbed_tmr; + logic [2:0] we, we_scrubbed; + logic [2:0][ObiCfg.DataWidth/8-1:0] be; + logic [2:0][ObiCfg.IdWidth-1:0] aid; + logic [2:0][relobi_pkg::relobi_r_other_ecc_width(ObiCfg)-1:0] other_ecc; + logic [2:0][ObiCfg.DataWidth+hsiao_ecc_pkg::min_ecc(ObiCfg.DataWidth)-1:0] rmw_wdata_tmr; + logic [ObiCfg.DataWidth+hsiao_ecc_pkg::min_ecc(ObiCfg.DataWidth)-1:0] wdata_buffer, rmw_wdata, scrub_wdata, scrub_rdata; + logic [ObiCfg.AddrWidth+hsiao_ecc_pkg::min_ecc(ObiCfg.AddrWidth)-1:0] addr_buffer; + logic [2:0] r_gnt; + logic [2:0] scrub_gnt; + logic [2:0][ObiCfg.AddrWidth-1:0] addr_decoded; + logic [2:0][AddrWidth-1:0] addr_decoded_trimmed; + logic [2:0][AddrWidth-1:0] addr_scrubbed; + + TMR_voter_fail i_req_valid_vote ( + .a_i (req_o_scrubbed_tmr[0]), + .b_i (req_o_scrubbed_tmr[1]), + .c_i (req_o_scrubbed_tmr[2]), + .majority_o (req_o), + .fault_detected_o(voter_errs[0]) + ); + + bitwise_TMR_voter_fail #( + .DataWidth( ObiCfg.DataWidth+hsiao_ecc_pkg::min_ecc(ObiCfg.DataWidth) ) + ) i_use_buffered_vote ( + .a_i (use_buffered_tmr[0]), + .b_i (use_buffered_tmr[1]), + .c_i (use_buffered_tmr[2]), + .majority_o (use_buffered), + .fault_detected_o(voter_errs[1]) + ); + + for (genvar i = 0; i < ObiCfg.DataWidth+hsiao_ecc_pkg::min_ecc(ObiCfg.DataWidth); i++) begin : gen_scrub_wdata + assign scrub_wdata[i] = use_buffered[i] ? rmw_wdata[i] : obi_req_i.a.wdata[i]; + end + + always_comb begin + obi_rsp_o = '0; + obi_rsp_o.gnt = r_gnt; + obi_rsp_o.rvalid = rvalid_q; + obi_rsp_o.r.rdata = scrub_rdata; + obi_rsp_o.r.rid = id_q; + obi_rsp_o.r.err = 1'b0; + obi_rsp_o.r.other_ecc = other_ecc_q; + end + + assign rvalid_d = obi_req_i.req & obi_rsp_o.gnt; + + if (EnableScrubber) begin : gen_scrubber + ecc_scrubber #( + .BankSize (ScrubberMemWords), + .UseExternalECC (1'b0), + .DataWidth (ObiCfg.DataWidth+hsiao_ecc_pkg::min_ecc(ObiCfg.DataWidth)), + .ProtWidth (hsiao_ecc_pkg::min_ecc(ObiCfg.DataWidth)), + .CorrectRead (ScrubberCorrectRead), + .TmrHs (1'b1) + ) i_scrubber ( + .clk_i (clk_i), + .rst_ni (rst_ni), + + .scrub_trigger_i (scrub_trigger_i), + .bit_corrected_o (scrub_bit_corrected_o), + .uncorrectable_o (scrub_uncorrectable_o), + + // Input signals from others accessing memory bank + .intc_req_i (req_o_tmr), + .intc_gnt_o (scrub_gnt), + .intc_we_i (we), + .intc_add_i (addr_decoded_trimmed), + .intc_wdata_i (scrub_wdata), + .intc_rdata_o (scrub_rdata), + + // Output directly to bank + .bank_req_o (req_o_scrubbed_tmr), + .bank_gnt_i ({3{gnt_i}}), + .bank_we_o (we_scrubbed), + .bank_add_o (addr_scrubbed), + .bank_wdata_o (wdata_o), + .bank_rdata_i (rdata_i), + + // If using external ECC + .ecc_out_o (), + .ecc_in_i ('0), + .ecc_err_i ('0), + + .fault_o (voter_errs[7]) + ); + end else begin : gen_no_scrubber + assign req_o_scrubbed_tmr = req_o_tmr; + assign scrub_gnt = {3{gnt_i}}; + assign we_scrubbed = we; + assign addr_scrubbed = addr_decoded; + assign wdata_o = scrub_wdata; + assign scrub_bit_corrected_o = 1'b0; + assign scrub_uncorrectable_o = 1'b0; + assign voter_errs[7] = 1'b0; + end + + for (genvar i = 0; i < 3; i++) begin : gen_tmr_part + relobi_sram_shim_tmr_part #( + .ObiCfg(ObiCfg), + .relobi_req_t(relobi_req_t), + .relobi_rsp_t(relobi_rsp_t), + .a_optional_t(a_optional_t), + .r_optional_t(r_optional_t) + ) i_tmr_part ( + .clk_i(clk_i), + .rst_ni(rst_ni), + .req_i(obi_req_i.req[i]), + .gnt_i(scrub_gnt[i]), + .gnt_o(r_gnt[i]), + .a_we_i(obi_req_i.a.we), + .a_be_i(obi_req_i.a.be), + .a_aid_i(obi_req_i.a.aid), + .a_optional_i(obi_req_i.a.a_optional), + .a_other_ecc_i(obi_req_i.a.other_ecc), + .wdata_rmw(wdata_buffer), + .ldata_rmw(scrub_rdata), + .wdata_modified(rmw_wdata_tmr[i]), + .use_buffered(use_buffered_tmr[i]), + .req_o(req_o_tmr[i]), + .a_we_o(we[i]), + .a_aid_o(aid[i]), + .other_ecc_d(other_ecc[i]), + .a_addr_i(obi_req_i.a.addr), + .addr_buffer(addr_buffer), + .addr_o(addr_decoded[i]), + .hsiao_errs(hsiao_errs[4*i+:4]) + ); + assign addr_decoded_trimmed[i] = addr_decoded[i][AddrWidth-1+$clog2(ObiCfg.AddrWidth/8):$clog2(ObiCfg.AddrWidth/8)]; + end + + TMR_voter_fail i_we_vote ( + .a_i (we_scrubbed[0]), + .b_i (we_scrubbed[1]), + .c_i (we_scrubbed[2]), + .majority_o (we_o), + .fault_detected_o(voter_errs[2]) + ); + bitwise_TMR_voter_fail #( + .DataWidth(AddrWidth) + ) i_addr_vote ( + .a_i (addr_scrubbed[0]), + .b_i (addr_scrubbed[1]), + .c_i (addr_scrubbed[2]), + .majority_o (addr_o), + .fault_detected_o(voter_errs[3]) + ); + bitwise_TMR_voter_fail #( + .DataWidth( ObiCfg.IdWidth ) + ) i_aid_vote ( + .a_i (aid[0]), + .b_i (aid[1]), + .c_i (aid[2]), + .majority_o (id_d), + .fault_detected_o(voter_errs[4]) + ); + bitwise_TMR_voter_fail #( + .DataWidth( relobi_pkg::relobi_r_other_ecc_width(ObiCfg) ) + ) i_other_ecc_vote ( + .a_i (other_ecc[0]), + .b_i (other_ecc[1]), + .c_i (other_ecc[2]), + .majority_o (other_ecc_d), + .fault_detected_o(voter_errs[5]) + ); + bitwise_TMR_voter_fail #( + .DataWidth( ObiCfg.DataWidth+hsiao_ecc_pkg::min_ecc(ObiCfg.DataWidth) ) + ) i_wdata_vote ( + .a_i (rmw_wdata_tmr[0]), + .b_i (rmw_wdata_tmr[1]), + .c_i (rmw_wdata_tmr[2]), + .majority_o (rmw_wdata), + .fault_detected_o(voter_errs[6]) + ); + + always_ff @(posedge clk_i or negedge rst_ni) begin + if(!rst_ni) begin + rvalid_q <= 1'b0; + id_q <= '0; + other_ecc_q <= '0; + end else begin + rvalid_q <= rvalid_d; + id_q <= id_d; + other_ecc_q <= other_ecc_d; + end + end + + always_ff @(posedge clk_i or negedge rst_ni) begin + if(!rst_ni) begin + wdata_buffer <= '0; + addr_buffer <= '0; + end else if (req_o && gnt_i) begin + wdata_buffer <= obi_req_i.a.wdata; + addr_buffer <= obi_req_i.a.addr; + end + end + + +endmodule + +module relobi_sram_shim_tmr_part #( + parameter obi_pkg::obi_cfg_t ObiCfg = obi_pkg::ObiDefaultConfig, + parameter type relobi_req_t = logic, + parameter type relobi_rsp_t = logic, + parameter type a_optional_t = logic, + parameter type r_optional_t = logic +) ( + input logic clk_i, + input logic rst_ni, + input logic req_i, + input logic gnt_i, + output logic gnt_o, + input logic a_we_i, + input logic [ObiCfg.DataWidth/8-1:0] a_be_i, + input logic [ObiCfg.IdWidth-1:0] a_aid_i, + input a_optional_t a_optional_i, + input logic [relobi_pkg::relobi_a_other_ecc_width(ObiCfg)-1:0] a_other_ecc_i, + input logic [ObiCfg.DataWidth+hsiao_ecc_pkg::min_ecc(ObiCfg.DataWidth)-1:0] wdata_rmw, + input logic [ObiCfg.DataWidth+hsiao_ecc_pkg::min_ecc(ObiCfg.DataWidth)-1:0] ldata_rmw, + output logic [ObiCfg.DataWidth+hsiao_ecc_pkg::min_ecc(ObiCfg.DataWidth)-1:0] wdata_modified, + output logic [ObiCfg.DataWidth+hsiao_ecc_pkg::min_ecc(ObiCfg.DataWidth)-1:0] use_buffered, + output logic req_o, + output logic a_we_o, + output logic [ObiCfg.IdWidth-1:0] a_aid_o, + output logic [relobi_pkg::relobi_r_other_ecc_width(ObiCfg)-1:0] other_ecc_d, + input logic [ObiCfg.AddrWidth+hsiao_ecc_pkg::min_ecc(ObiCfg.AddrWidth)-1:0] a_addr_i, + input logic [ObiCfg.AddrWidth+hsiao_ecc_pkg::min_ecc(ObiCfg.AddrWidth)-1:0] addr_buffer, + output logic [ObiCfg.AddrWidth-1:0] addr_o, + output logic [3:0][1:0] hsiao_errs +); + + typedef enum logic { NORMAL, READ_MODIFY_WRITE } store_state_e; + store_state_e store_state_d, store_state_q; + + logic [ObiCfg.DataWidth/8-1:0] a_be_int, be_buffer; + + logic [ObiCfg.DataWidth-1:0] wdata_rmw_dec, ldata_rmw_dec; + logic [ObiCfg.DataWidth-1:0] be_selector; + logic a_we_int; + + relobi_a_other_decoder #( + .Cfg (ObiCfg), + .a_optional_t (a_optional_t) + ) i_a_other_decoder ( + .we_i (a_we_i), + .be_i (a_be_i), + .aid_i (a_aid_i), + .a_optional_i (a_optional_i), + .other_ecc_i (a_other_ecc_i), + .we_o (a_we_int), + .be_o (a_be_int), + .aid_o (a_aid_o), + .a_optional_o (), + .fault_o (hsiao_errs[0]) + ); + + relobi_r_other_encoder #( + .Cfg(ObiCfg), + .r_optional_t(r_optional_t) + ) i_r_other_enc ( + .rid_i(a_aid_o), + .err_i(1'b0), + .r_optional_i('0), + .other_ecc_o (other_ecc_d) + ); + + for (genvar i = 0; i < ObiCfg.DataWidth/8; i++) begin : gen_be_selector + assign be_selector[i*8 +: 8] = {8{be_buffer[i]}}; + end + + hsiao_ecc_dec #( + .DataWidth ( ObiCfg.DataWidth ) + ) i_wdata_dec ( + .in ( wdata_rmw ), + .out ( wdata_rmw_dec ), + .syndrome_o(), + .err_o (hsiao_errs[1]) + ); + hsiao_ecc_dec #( + .DataWidth ( ObiCfg.DataWidth ) + ) i_rdata_dec ( + .in ( ldata_rmw ), + .out ( ldata_rmw_dec ), + .syndrome_o(), + .err_o (hsiao_errs[2]) + ); + hsiao_ecc_enc #( + .DataWidth ( ObiCfg.DataWidth ) + ) i_wdata_enc ( + .in ( be_selector & wdata_rmw_dec | + (~be_selector & ldata_rmw_dec) ), + .out ( wdata_modified ) + ); + + hsiao_ecc_dec #( + .DataWidth ( ObiCfg.AddrWidth ) + ) i_addr_dec ( + .in ( use_buffered[0] ? addr_buffer : a_addr_i ), + .out ( addr_o ), + .syndrome_o(), + .err_o (hsiao_errs[3]) + ); + + + always_comb begin + req_o = req_i; + gnt_o = gnt_i; + store_state_d = NORMAL; + use_buffered = {ObiCfg.DataWidth+hsiao_ecc_pkg::min_ecc(ObiCfg.DataWidth){1'b0}}; + a_we_o = a_we_int; + if (store_state_q == NORMAL) begin + if (req_i & (a_be_int != {ObiCfg.DataWidth/8{1'b1}}) & a_we_int) begin + store_state_d = READ_MODIFY_WRITE; + a_we_o = 1'b0; + end + end else begin + req_o = 1'b1; + gnt_o = 1'b0; + a_we_o = 1'b1; + use_buffered = {ObiCfg.DataWidth+hsiao_ecc_pkg::min_ecc(ObiCfg.DataWidth){1'b1}}; + end + end + + always_ff @(posedge clk_i or negedge rst_ni) begin + if(!rst_ni) begin + store_state_q <= NORMAL; + be_buffer <= '0; + end else if (req_o && gnt_i) begin + store_state_q <= store_state_d; // Quick to reload and dependents always voted + be_buffer <= a_be_i; // Quick to reload and always voted with wdata + end + end + + +endmodule diff --git a/src/relobi_tmr_a.sv b/src/relobi_tmr_a.sv new file mode 100644 index 0000000..9b7159a --- /dev/null +++ b/src/relobi_tmr_a.sv @@ -0,0 +1,194 @@ +// Copyright 2024 ETH Zurich and University of Bologna. +// Solderpad Hardware License, Version 0.51, see LICENSE for details. +// SPDX-License-Identifier: SHL-0.51 + +// Michael Rogenmoser + +module relobi_tmr_a #( + parameter obi_pkg::obi_cfg_t ObiCfg = obi_pkg::ObiDefaultConfig, + parameter type obi_a_chan_t = logic, + parameter type a_optional_t = logic +) ( + input obi_a_chan_t [2:0] three_a_i, + output obi_a_chan_t voted_a_o, + output logic fault_o +); + + logic [14:0] faults; + + assign fault_o = |faults; + + bitwise_TMR_voter_fail #( + .DataWidth(ObiCfg.AddrWidth+hsiao_ecc_pkg::min_ecc(ObiCfg.AddrWidth)) + ) i_r_data ( + .a_i (three_a_i[0].addr), + .b_i (three_a_i[1].addr), + .c_i (three_a_i[2].addr), + .majority_o (voted_a_o.addr), + .fault_detected_o (faults[0]) + ); + TMR_voter_fail i_r_we ( + .a_i (three_a_i[0].we), + .b_i (three_a_i[1].we), + .c_i (three_a_i[2].we), + .majority_o (voted_a_o.we), + .fault_detected_o (faults[1]) + ); + bitwise_TMR_voter_fail #( + .DataWidth(ObiCfg.DataWidth/8) + ) i_r_data ( + .a_i (three_a_i[0].be), + .b_i (three_a_i[1].be), + .c_i (three_a_i[2].be), + .majority_o (voted_a_o.be), + .fault_detected_o (faults[2]) + ); + bitwise_TMR_voter_fail #( + .DataWidth(ObiCfg.DataWidth+hsiao_ecc_pkg::min_ecc(ObiCfg.DataWidth)) + ) i_r_data ( + .a_i (three_a_i[0].wdata), + .b_i (three_a_i[1].wdata), + .c_i (three_a_i[2].wdata), + .majority_o (voted_a_o.wdata), + .fault_detected_o (faults[3]) + ); + bitwise_TMR_voter_fail #( + .DataWidth(ObiCfg.IdWidth) + ) i_r_id ( + .a_i (three_a_i[0].aid), + .b_i (three_a_i[1].aid), + .c_i (three_a_i[2].aid), + .majority_o (voted_a_o.aid), + .fault_detected_o (faults[4]) + ); + TMR_voter_fail i_r_err ( + .a_i (three_r_i[0].err), + .b_i (three_r_i[1].err), + .c_i (three_r_i[2].err), + .majority_o (voted_r_o.err), + .fault_detected_o (faults[5]) + ); + + if (ObiCfg.OptionalCfg.AUserWidth) begin : gen_auser + bitwise_TMR_voter_fail #( + .DataWidth(ObiCfg.OptionalCfg.RUserWidth) + ) i_r_id ( + .a_i (three_a_i[0].a_optional.auser), + .b_i (three_a_i[1].a_optional.auser), + .c_i (three_a_i[2].a_optional.auser), + .majority_o (voted_a_o.a_optional.auser), + .fault_detected_o (faults[6]) + ); + end else begin : gen_no_auser + assign faults[6] = 1'b0; + end + if (ObiCfg.OptionalCfg.WUserWidth) begin : gen_wuser + bitwise_TMR_voter_fail #( + .DataWidth(ObiCfg.OptionalCfg.RUserWidth) + ) i_r_id ( + .a_i (three_a_i[0].a_optional.wuser), + .b_i (three_a_i[1].a_optional.wuser), + .c_i (three_a_i[2].a_optional.wuser), + .majority_o (voted_a_o.a_optional.wuser), + .fault_detected_o (faults[7]) + ); + end else begin : gen_no_wuser + assign faults[7] = 1'b0; + end + if (ObiCfg.OptionalCfg.UseAtop) begin : gen_atop + bitwise_TMR_voter #( + .DataWidth(6) + ) i_r_err ( + .a_i (three_a_i[0].a_optional.atop), + .b_i (three_a_i[1].a_optional.atop), + .c_i (three_a_i[2].a_optional.atop), + .majority_o (voted_a_o.a_optional.atop), + .fault_detected_o (faults[8]) + ); + end else begin : gen_no_atop + assign faults[8] = 1'b0; + end + if (ObiCfg.OptionalCfg.UseMemtype) begin : gen_memtype + bitwise_TMR_voter_fail #( + .DataWidth(2) + ) i_r_err ( + .a_i (three_a_i[0].a_optional.memtype), + .b_i (three_a_i[1].a_optional.memtype), + .c_i (three_a_i[2].a_optional.memtype), + .majority_o (voted_a_o.a_optional.memtype), + .fault_detected_o (faults[9]) + ); + end else begin : gen_no_memtype + assign faults[9] = 1'b0; + end + if (ObiCfg.OptionalCfg.MidWidth) begin : gen_mid + bitwise_TMR_voter_fail #( + .DataWidth(ObiCfg.OptionalCfg.MidWidth) + ) i_r_err ( + .a_i (three_a_i[0].a_optional.mid), + .b_i (three_a_i[1].a_optional.mid), + .c_i (three_a_i[2].a_optional.mid), + .majority_o (voted_a_o.a_optional.mid), + .fault_detected_o (faults[10]) + ); + end else begin : gen_no_mid + assign faults[10] = 1'b0; + end + if (ObiCfg.OptionalCfg.UseProt) begin : gen_prot + bitwise_TMR_voter_fail #( + .DataWidth(3) + ) i_r_err ( + .a_i (three_a_i[0].a_optional.prot), + .b_i (three_a_i[1].a_optional.prot), + .c_i (three_a_i[2].a_optional.prot), + .majority_o (voted_a_o.a_optional.prot), + .fault_detected_o (faults[11]) + ); + end else begin : gen_no_prot + assign faults[11] = 1'b0; + end + if (ObiCfg.OptionalCfg.UseDbg) begin : gen_dbg + TMR_voter_fail i_r_err ( + .a_i (three_a_i[0].a_optional.dbg), + .b_i (three_a_i[1].a_optional.dbg), + .c_i (three_a_i[2].a_optional.dbg), + .majority_o (voted_a_o.a_optional.dbg), + .fault_detected_o (faults[12]) + ); + end else begin : gen_no_dbg + assign faults[12] = 1'b0; + end + if (ObiCfg.OptionalCfg.AChkWidth) begin : gen_achk + bitwise_TMR_voter_fail #( + .DataWidth(ObiCfg.OptionalCfg.AChkWidth) + ) i_r_id ( + .a_i (three_a_i[0].a_optional.achk), + .b_i (three_a_i[1].a_optional.achk), + .c_i (three_a_i[2].a_optional.achk), + .majority_o (voted_a_o.a_optional.achk), + .fault_detected_o (faults[13]) + ); + end else begin : gen_no_achk + assign faults[13] = 1'b0; + end + if (!ObiCfg.OptionalCfg.AUserWidth && + !ObiCfg.OptionalCfg.WUserWidth && + !ObiCfg.OptionalCfg.UseAtop && + !ObiCfg.OptionalCfg.UseMemtype && + !ObiCfg.OptionalCfg.MidWidth && + !ObiCfg.OptionalCfg.UseProt && + !ObiCfg.OptionalCfg.UseDbg && + !ObiCfg.OptionalCfg.AChkWidth ) begin : gen_optional_tie + assign voted_a_o.a_optional = '0; + end + bitwise_TMR_voter_fail #( + .DataWidth(hsiao_ecc_pkg::min_ecc(1+ObiCfg.DataWidth/8+ObiCfg.IdWidth+$bits(a_optional_t))) + ) i_r_id ( + .a_i (three_a_i[0].other_ecc), + .b_i (three_a_i[1].other_ecc), + .c_i (three_a_i[2].other_ecc), + .majority_o (voted_a_o.other_ecc), + .fault_detected_o (faults[14]) + ); + +endmodule diff --git a/src/relobi_tmr_r.sv b/src/relobi_tmr_r.sv new file mode 100644 index 0000000..7c0c9b2 --- /dev/null +++ b/src/relobi_tmr_r.sv @@ -0,0 +1,101 @@ +// Copyright 2024 ETH Zurich and University of Bologna. +// Solderpad Hardware License, Version 0.51, see LICENSE for details. +// SPDX-License-Identifier: SHL-0.51 + +// Michael Rogenmoser + +module relobi_tmr_r #( + parameter obi_pkg::obi_cfg_t ObiCfg = obi_pkg::ObiDefaultConfig, + parameter type obi_r_chan_t = logic, + parameter type r_optional_t = logic +) ( + input obi_r_chan_t [2:0] three_r_i, + output obi_r_chan_t voted_r_o, + + output logic fault_o +); + + logic [6:0] faults; + assign fault_o = |faults; + + bitwise_TMR_voter_fail #( + .DataWidth(ObiCfg.DataWidth+hsiao_ecc_pkg::min_ecc(ObiCfg.DataWidth)) + ) i_r_data ( + .a_i (three_r_i[0].rdata), + .b_i (three_r_i[1].rdata), + .c_i (three_r_i[2].rdata), + .majority_o (voted_r_o.rdata), + .fault_detected_o (faults[0]) + ); + bitwise_TMR_voter_fail #( + .DataWidth(ObiCfg.IdWidth) + ) i_r_id ( + .a_i (three_r_i[0].rid), + .b_i (three_r_i[1].rid), + .c_i (three_r_i[2].rid), + .majority_o (voted_r_o.rid), + .fault_detected_o (faults[1]) + ); + TMR_voter_fail i_r_err ( + .a_i (three_r_i[0].err), + .b_i (three_r_i[1].err), + .c_i (three_r_i[2].err), + .majority_o (voted_r_o.err), + .fault_detected_o (faults[2]) + ); + + if (ObiCfg.OptionalCfg.RUserWidth) begin : gen_ruser + bitwise_TMR_voter_fail #( + .DataWidth(ObiCfg.OptionalCfg.RUserWidth) + ) i_r_user ( + .a_i (three_r_i[0].r_optional.ruser), + .b_i (three_r_i[1].r_optional.ruser), + .c_i (three_r_i[2].r_optional.ruser), + .majority_o (voted_r_o.r_optional.ruser), + .fault_detected_o (faults[3]) + ); + end else begin : gen_no_ruser + assign faults[3] = '0; + end + if (ObiCfg.OptionalCfg.RChkWidth) begin : gen_rchk + bitwise_TMR_voter_fail #( + .DataWidth(ObiCfg.OptionalCfg.RChkWidth) + ) i_r_rchk ( + .a_i (three_r_i[0].r_optional.rchk), + .b_i (three_r_i[1].r_optional.rchk), + .c_i (three_r_i[2].r_optional.rchk), + .majority_o (voted_r_o.r_optional.rchk), + .fault_detected_o (faults[4]) + ); + end else begin : gen_no_rchk + assign faults[4] = '0; + end + if (ObiCfg.OptionalCfg.UseAtop) begin : gen_exokay + TMR_voter_fail i_r_err ( + .a_i (three_r_i[0].r_optional.exokay), + .b_i (three_r_i[1].r_optional.exokay), + .c_i (three_r_i[2].r_optional.exokay), + .majority_o (voted_r_o.r_optional.exokay), + .fault_detected_o (faults[5]) + ); + end else begin : gen_no_exokay + assign faults[5] = '0; + end + if (!ObiCfg.OptionalCfg.RUserWidth && + !ObiCfg.OptionalCfg.RChkWidth && + !ObiCfg.OptionalCfg.UseAtop ) begin : gen_optional_tie + assign voted_r_o.r_optional = '0; + end + + bitwise_TMR_voter_fail #( + .DataWidth(hsiao_ecc_pkg::min_ecc(ObiCfg.IdWidth+1+$bits(r_optional_t))) + ) i_r_other_ecc ( + .a_i (three_r_i[0].other_ecc), + .b_i (three_r_i[1].other_ecc), + .c_i (three_r_i[2].other_ecc), + .majority_o (voted_r_o.other_ecc), + .fault_detected_o (faults[6]) + ); + + +endmodule diff --git a/src/relobi_xbar.sv b/src/relobi_xbar.sv new file mode 100644 index 0000000..3bcf38f --- /dev/null +++ b/src/relobi_xbar.sv @@ -0,0 +1,281 @@ +// Copyright 2025 ETH Zurich and University of Bologna. +// Solderpad Hardware License, Version 0.51, see LICENSE for details. +// SPDX-License-Identifier: SHL-0.51 + +// Michael Rogenmoser + +/// An OBI crossbar interconnect. +module relobi_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 A channel struct for the manager port (output port). + parameter type mgr_port_a_chan_t = logic, + /// The R channel struct for the manager port (output port). + parameter type mgr_port_r_chan_t = logic, + /// The A channel optionals struct for all ports. + parameter type a_optional_t = logic, + /// The R channel optionals struct for all ports. + parameter type r_optional_t = logic, + /// 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, + /// Use TMR for addr map signal + parameter bit TmrMap = 1'b1, + parameter int unsigned MapWidth = TmrMap ? 3 : 1, + parameter bit DecodeAbort = 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 [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 [MapWidth-1:0][NumAddrRules-1:0] addr_map_i, + input logic [MapWidth-1:0][NumSbrPorts-1:0] en_default_idx_i, + input logic [MapWidth-1:0][NumSbrPorts-1:0][$clog2(NumMgrPorts)-1:0] default_idx_i, + + output logic [1:0] fault_o +); + + logic [4*NumSbrPorts+NumMgrPorts-1:0][1:0] faults; + logic [1:0][4*NumSbrPorts+NumMgrPorts-1:0] faults_transpose; + for (genvar i = 0; i < 4*NumSbrPorts+NumMgrPorts; i++) begin : gen_faults_transpose + for (genvar j = 0; j < 2; j++) begin : gen_faults_transpose_inner + assign faults_transpose[j][i] = faults[i][j]; + end + end + assign fault_o[0] = |faults_transpose[0]; + assign fault_o[1] = |faults_transpose[1]; + + logic [2:0][NumSbrPorts-1:0][$clog2(NumMgrPorts)-1:0] sbr_port_select; + logic [NumSbrPorts-1:0][MgrPortObiCfg.AddrWidth+ + hsiao_ecc_pkg::min_ecc(MgrPortObiCfg.AddrWidth)-1:0] addr_input; + logic [2:0][NumSbrPorts-1:0] decode_abort; + + + // Signals from the demuxes + sbr_port_obi_req_t [NumSbrPorts-1:0][NumMgrPorts-1:0] sbr_reqs, sbr_reqs_aborted; + sbr_port_obi_rsp_t [NumSbrPorts-1:0][NumMgrPorts-1:0] sbr_rsps, sbr_rsps_aborted; + + // 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 < 3; i++) begin : gen_tmr_part + relobi_xbar_tmr_part #( + .NumSbrPorts ( NumSbrPorts ), + .NumMgrPorts ( NumMgrPorts ), + .AddrWidth ( MgrPortObiCfg.AddrWidth ), + .EccAddrWidth( MgrPortObiCfg.AddrWidth + hsiao_ecc_pkg::min_ecc(MgrPortObiCfg.AddrWidth) ), + .NumAddrRules( NumAddrRules ), + .addr_map_rule_t( addr_map_rule_t ), + .DecodeAbort( DecodeAbort ) + ) i_tmr_part ( + .addr_i ( addr_input ), + .addr_map_i ( TmrMap ? addr_map_i[i] : addr_map_i[0] ), + .en_default_idx_i ( TmrMap ? en_default_idx_i[i] : en_default_idx_i[0] ), + .default_idx_i ( TmrMap ? default_idx_i[i] : default_idx_i[0] ), + .sbr_port_select ( sbr_port_select[i] ), + .faults ( faults[i*NumSbrPorts +: NumSbrPorts] ), + .decode_abort_o ( decode_abort[i] ) + ); + end + + for (genvar i = 0; i < NumSbrPorts; i++) begin : gen_demux + + assign addr_input[i] = sbr_ports_req_i[i].a.addr; + + relobi_demux #( + .ObiCfg ( SbrPortObiCfg ), + .obi_req_t ( sbr_port_obi_req_t ), + .obi_rsp_t ( sbr_port_obi_rsp_t ), + .obi_r_chan_t( sbr_port_r_chan_t ), + .obi_r_optional_t ( r_optional_t ), + .NumMgrPorts ( NumMgrPorts ), + .NumMaxTrans ( NumMaxTrans ), + .TmrSelect ( 1'b1 ) + ) i_demux ( + .clk_i, + .rst_ni, + .sbr_port_select_i ( {sbr_port_select[2][i], sbr_port_select[1][i], sbr_port_select[0][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] ), + .fault_o ( faults[3*NumSbrPorts+i] ) + ); + + for (genvar j = 0; j < NumMgrPorts; j++) begin : gen_sbr_reqs_abort_inner + assign sbr_reqs_aborted[i][j].a = sbr_reqs[i][j].a; + assign sbr_rsps_aborted[i][j].r = sbr_rsps[i][j].r; + assign sbr_rsps_aborted[i][j].rvalid = sbr_rsps[i][j].rvalid; + if (SbrPortObiCfg.UseRReady) begin : gen_rready + assign sbr_reqs_aborted[i][j].rready = sbr_reqs[i][j].rready; + end + assign sbr_reqs_aborted[i][j].req[0] = sbr_reqs[i][j].req[0] & ~decode_abort[0][i]; + assign sbr_reqs_aborted[i][j].req[1] = sbr_reqs[i][j].req[1] & ~decode_abort[1][i]; + assign sbr_reqs_aborted[i][j].req[2] = sbr_reqs[i][j].req[2] & ~decode_abort[2][i]; + assign sbr_rsps_aborted[i][j].gnt[0] = sbr_rsps[i][j].gnt[0] & ~decode_abort[0][i]; + assign sbr_rsps_aborted[i][j].gnt[1] = sbr_rsps[i][j].gnt[1] & ~decode_abort[1][i]; + assign sbr_rsps_aborted[i][j].gnt[2] = sbr_rsps[i][j].gnt[2] & ~decode_abort[2][i]; + end + + 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_aborted[i][j]; + assign sbr_rsps[i][j] = mgr_rsps[j][i]; + end else begin : gen_err_sbr + assign mgr_reqs[j][i].req = '0; + if (MgrPortObiCfg.UseRReady) begin : gen_rready + assign mgr_reqs[j][i].rready = '0; + end + assign mgr_reqs[j][i].a = '0; + if (MgrPortObiCfg.Integrity) begin : gen_integrity + assign mgr_reqs[j][i].reqpar = '1; + if (MgrPortObiCfg.UseRReady) begin : gen_int_rready + assign mgr_reqs[j][i].rreadypar = '1; + end + end + relobi_err_sbr #( + .ObiCfg ( SbrPortObiCfg ), + .obi_req_t ( sbr_port_obi_req_t ), + .obi_rsp_t ( sbr_port_obi_rsp_t ), + .a_optional_t ( a_optional_t ), + .r_optional_t ( r_optional_t ), + .NumMaxTrans ( NumMaxTrans ), + .RspData ( 32'hBADCAB1E ) + ) i_err_sbr ( + .clk_i, + .rst_ni, + .testmode_i, + .obi_req_i (sbr_reqs_aborted[i][j]), + .obi_rsp_o (sbr_rsps[i][j]), + .fault_o () // TODO + ); + end + end + end + + for (genvar i = 0; i < NumMgrPorts; i++) begin : gen_mux + relobi_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 ), + .mgr_port_a_chan_t ( mgr_port_a_chan_t ), + .mgr_port_r_chan_t ( mgr_port_r_chan_t ), + .a_optional_t ( a_optional_t ), + .r_optional_t ( r_optional_t ), + .NumSbrPorts ( NumSbrPorts ), + .NumMaxTrans ( NumMaxTrans ), + .UseIdForRouting ( UseIdForRouting ) + ) i_mux ( + .clk_i, + .rst_ni, + .testmode_i ( 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] ), + .fault_o ( faults[4*NumSbrPorts+i] ) + ); + end + +endmodule + +(* no_ungroup *) +(* no_boundary_optimization *) +module relobi_xbar_tmr_part #( + parameter int unsigned NumSbrPorts = 32'd0, + parameter int unsigned NumMgrPorts = 32'd0, + parameter int unsigned AddrWidth = 32'd0, + parameter int unsigned EccAddrWidth = AddrWidth + hsiao_ecc_pkg::min_ecc(AddrWidth), + parameter int unsigned NumAddrRules = 32'd0, + parameter type addr_map_rule_t = logic, + parameter bit DecodeAbort = 1'b1 +) ( + input logic [NumSbrPorts-1:0][EccAddrWidth-1:0] addr_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, + output logic [NumSbrPorts-1:0][$clog2(NumMgrPorts)-1:0] sbr_port_select, + output logic [NumSbrPorts-1:0][1:0] faults, + output logic [NumSbrPorts-1:0] decode_abort_o +); + logic [NumSbrPorts-1:0][AddrWidth-1:0] addr, addr_dec; + + for (genvar i = 0; i < NumSbrPorts; i++) begin : gen_ecc_dec + hsiao_ecc_dec #( + .DataWidth ( AddrWidth ) + ) i_addr_dec ( + .in ( addr_i[i] ), + .out ( addr_dec [i] ), + .syndrome_o(), + .err_o (faults[i]) + ); + end + if (DecodeAbort) begin : gen_decode_abort + for (genvar i = 0; i < NumSbrPorts; i++) begin : gen_addr + assign addr[i] = addr_i[i][AddrWidth-1:0]; + assign decode_abort_o[i] = faults[i][0];// | faults[i][1]; + end + end else begin : gen_no_decode_abort + assign addr = addr_dec; + assign decode_abort_o = '0; + end + + + for (genvar i = 0; i < NumSbrPorts; i++) begin : gen_sel + addr_decode #( + .NoIndices ( NumMgrPorts ), + .NoRules ( NumAddrRules ), + .addr_t ( logic [AddrWidth-1:0] ), + .rule_t ( addr_map_rule_t ) + ) i_addr_decode ( + .addr_i ( addr [i] ), + .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] ) + ); + end + +endmodule diff --git a/src/test/obi_sim_mem.sv b/src/test/obi_sim_mem.sv index aec1528..e031347 100644 --- a/src/test/obi_sim_mem.sv +++ b/src/test/obi_sim_mem.sv @@ -150,6 +150,8 @@ endmodule module obi_sim_mem_intf import obi_pkg::*; #( parameter obi_pkg::obi_cfg_t ObiCfg = obi_pkg::ObiDefaultConfig, + parameter type a_optional_t = logic, + parameter type r_optional_t = logic, parameter bit WarnUninitialized = 1'b0, parameter bit ClearErrOnAccess = 1'b0, parameter time ApplDelay = 0ps, @@ -167,7 +169,11 @@ module obi_sim_mem_intf import obi_pkg::*; #( output logic [ ObiCfg.IdWidth-1:0] mon_id_o ); + `ifdef TARGET_VSIM `OBI_TYPEDEF_ALL(obi, ObiCfg) + `else + `OBI_TYPEDEF_ALL_WITH_OPTIONAL(obi, ObiCfg, a_optional_t, r_optional_t) + `endif obi_req_t obi_req; obi_rsp_t obi_rsp; diff --git a/src/test/obi_test.sv b/src/test/obi_test.sv index 90a49ef..ebe2585 100644 --- a/src/test/obi_test.sv +++ b/src/test/obi_test.sv @@ -50,6 +50,7 @@ package obi_test; obi.rvalidpar <= '1; obi.rdata <= '0; obi.rid <= '0; + obi.err <= '0; obi.r_optional <= '0; endfunction @@ -93,12 +94,15 @@ package obi_test; task send_r ( input logic [ObiCfg.DataWidth-1:0] rdata, input logic [ ObiCfg.IdWidth-1:0] rid, + input logic err, 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.err <= #TA err; obi.r_optional <= #TA r_optional; cycle_start(); if (ObiCfg.UseRReady) begin @@ -109,6 +113,7 @@ package obi_test; obi.rvalidpar <= #TA 1'b1; obi.rdata <= #TA '0; obi.rid <= #TA '0; + obi.rid <= #TA '0; obi.r_optional <= #TA '0; endtask @@ -171,9 +176,9 @@ package obi_test; parameter int unsigned MaxAddr = 32'hffff_ffff, // Wait Parameters parameter int unsigned AMinWaitCycles = 0, - parameter int unsigned AMaxWaitCycles = 100, + parameter int unsigned AMaxWaitCycles = 20, parameter int unsigned RMinWaitCycles = 0, - parameter int unsigned RMaxWaitCycles = 100 + parameter int unsigned RMaxWaitCycles = 20 ); typedef obi_test::obi_driver #( .ObiCfg ( ObiCfg ), @@ -232,7 +237,7 @@ package obi_test; 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_wdata = $urandom() % (1 << ObiCfg.DataWidth-1); a_aid = $urandom() % (1 << ObiCfg.IdWidth); a_optional = obi_a_optional_t'($urandom()); @@ -250,7 +255,9 @@ package obi_test; repeat (n_rsps) begin wait (a_queue.size() > 0); a_addr = this.a_queue.pop_front(); - rand_wait(RMinWaitCycles, RMaxWaitCycles); + if (ObiCfg.UseRReady) begin + rand_wait(RMinWaitCycles, RMaxWaitCycles); + end drv.recv_r(r_rdata, r_rid, r_err, r_optional); end endtask @@ -373,6 +380,7 @@ package obi_test; 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; wait (a_queue.size() > 0); @@ -380,9 +388,10 @@ package obi_test; a_addr = this.a_queue.pop_front(); r_rid = this.id_queue.pop_front(); + r_err = '0; 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); + this.drv.send_r(r_rdata, r_rid, r_err, r_optional); end endtask diff --git a/src/test/tb_obi_atop_resolver.sv b/src/test/tb_obi_atop_resolver.sv index 0e4b33d..5a10e5f 100644 --- a/src/test/tb_obi_atop_resolver.sv +++ b/src/test/tb_obi_atop_resolver.sv @@ -169,7 +169,11 @@ module tb_obi_atop_resolver; obi_mux_intf #( .SbrPortObiCfg ( MgrConfig ), + .sbr_port_a_optional_t ( mgr_a_optional_t ), + .sbr_port_r_optional_t ( mgr_r_optional_t ), .MgrPortObiCfg ( MgrMuxedConfig ), + .mgr_port_a_optional_t ( mgr_a_optional_t ), + .mgr_port_r_optional_t ( mgr_r_optional_t ), .NumSbrPorts ( NumManagers ), .NumMaxTrans ( 2 ), .UseIdForRouting ( 1'b0 ) @@ -183,7 +187,11 @@ module tb_obi_atop_resolver; obi_atop_resolver_intf #( .SbrPortObiCfg ( MgrMuxedConfig ), + .sbr_port_a_optional_t ( mgr_a_optional_t ), + .sbr_port_r_optional_t ( mgr_r_optional_t ), .MgrPortObiCfg ( SbrConfig ), + .mgr_port_a_optional_t ( sbr_a_optional_t ), + .mgr_port_r_optional_t ( sbr_r_optional_t ), .LrScEnable ( 1 ), .RegisterAmo ( 1'b0 ) ) i_atop_resolver ( @@ -196,6 +204,8 @@ module tb_obi_atop_resolver; obi_sim_mem_intf #( .ObiCfg ( SbrConfig ), + .a_optional_t ( sbr_a_optional_t ), + .r_optional_t ( sbr_r_optional_t ), .ClearErrOnAccess ( 1'b0 ), .WarnUninitialized ( 1'b0 ), .ApplDelay ( ApplTime ), diff --git a/src/test/tb_obi_xbar.sv b/src/test/tb_obi_xbar.sv index 9586050..95e4df0 100644 --- a/src/test/tb_obi_xbar.sv +++ b/src/test/tb_obi_xbar.sv @@ -7,6 +7,315 @@ `include "obi/typedef.svh" `include "obi/assign.svh" +module obi_xbar_dut_wrapper #( + parameter int unsigned NumManagers = 32'd6, + parameter int unsigned NumSubordinates = 32'd8, + parameter obi_pkg::obi_cfg_t MgrConfig = obi_pkg::ObiDefaultConfig, + parameter obi_pkg::obi_cfg_t SbrConfig = obi_pkg::ObiDefaultConfig, + parameter type mgr_bus_req_t = logic, + parameter type mgr_bus_rsp_t = logic, + parameter type sbr_bus_req_t = logic, + parameter type sbr_bus_rsp_t = logic, + parameter type mgr_bus_a_chan_t = logic, + parameter type mgr_bus_r_chan_t = logic, + parameter type sbr_bus_a_chan_t = logic, + parameter type sbr_bus_r_chan_t = logic, + parameter type mgr_a_optional_t = logic, + parameter type mgr_r_optional_t = logic, + parameter type sbr_a_optional_t = logic, + parameter type sbr_r_optional_t = logic, + parameter int unsigned NumMaxTrans = 32'd8, + parameter int unsigned NumRules = 8, + parameter type rule_t = logic, + parameter rule_t [NumRules-1:0] AddrMap = '0, + parameter bit UseIdForRouting = 1'b0, + parameter time TestTime = 8ns, + parameter int unsigned NumRequests = 1 + +) ( + input logic clk, + input logic rst_n, + + // Manager ports + input mgr_bus_req_t [NumManagers-1:0] mgr_bus_req, + output mgr_bus_rsp_t [NumManagers-1:0] mgr_bus_rsp, + + // Subordinate ports + output sbr_bus_req_t [NumSubordinates-1:0] sbr_bus_req, + input sbr_bus_rsp_t [NumSubordinates-1:0] sbr_bus_rsp, + + input logic [NumManagers-1:0] end_of_sim +); + + localparam int unsigned MgrBusReqBits = $bits(mgr_bus_req_t); + localparam int unsigned MgrBusRspBits = $bits(mgr_bus_rsp_t); + localparam int unsigned SbrBusReqBits = $bits(sbr_bus_req_t); + localparam int unsigned SbrBusRspBits = $bits(sbr_bus_rsp_t); + + mgr_bus_req_t [NumManagers-1:0] mgr_bus_req_cut; + mgr_bus_rsp_t [NumManagers-1:0] mgr_bus_rsp_cut; + sbr_bus_req_t [NumSubordinates-1:0] sbr_bus_req_cut; + sbr_bus_rsp_t [NumSubordinates-1:0] sbr_bus_rsp_cut; + + logic [NumManagers-1:0][MgrBusRspBits-1:0] mgr_bus_rsp_flat; + for (genvar i = 0; i < NumManagers; i++) begin : gen_mgr_bus_rsp + assign mgr_bus_rsp_flat[i] = mgr_bus_rsp[i]; + end + + logic [NumSubordinates-1:0][SbrBusReqBits-1:0] sbr_bus_req_flat; + for (genvar i = 0; i < NumSubordinates; i++) begin : gen_sbr_bus_req + assign sbr_bus_req_flat[i] = sbr_bus_req[i]; + end + + for (genvar i = 0; i < NumManagers; i++) begin : gen_mgr_cut + obi_cut #( + .ObiCfg ( MgrConfig ), + .obi_req_t ( mgr_bus_req_t ), + .obi_rsp_t ( mgr_bus_rsp_t ), + .obi_a_chan_t ( mgr_bus_a_chan_t ), + .obi_r_chan_t ( mgr_bus_r_chan_t ), + .Bypass ( 1'b0 ) + ) i_cut_mgr ( + .clk_i ( clk ), + .rst_ni ( rst_n ), + + .sbr_port_req_i ( mgr_bus_req[i] ), + .sbr_port_rsp_o ( mgr_bus_rsp[i] ), + + .mgr_port_req_o ( mgr_bus_req_cut[i] ), + .mgr_port_rsp_i ( mgr_bus_rsp_cut[i] ) + ); + end + + // DUT + obi_xbar #( + .SbrPortObiCfg ( MgrConfig ), + .sbr_port_obi_req_t ( mgr_bus_req_t ), + .sbr_port_obi_rsp_t ( mgr_bus_rsp_t ), + .sbr_port_a_chan_t ( mgr_bus_a_chan_t ), + .sbr_port_r_chan_t ( mgr_bus_r_chan_t ), + .MgrPortObiCfg ( SbrConfig ), + .mgr_port_obi_req_t ( sbr_bus_req_t ), + .mgr_port_obi_rsp_t ( sbr_bus_rsp_t ), + .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_req_i ( mgr_bus_req_cut ), + .sbr_ports_rsp_o ( mgr_bus_rsp_cut ), + .mgr_ports_req_o ( sbr_bus_req_cut ), + .mgr_ports_rsp_i ( sbr_bus_rsp_cut ), + .addr_map_i ( AddrMap ), + .en_default_idx_i ( '0 ), + .default_idx_i ( '0 ) + ); + + + for (genvar i = 0; i < NumSubordinates; i++) begin : gen_sbr_cut + obi_cut #( + .ObiCfg ( SbrConfig ), + .obi_req_t ( sbr_bus_req_t ), + .obi_rsp_t ( sbr_bus_rsp_t ), + .obi_a_chan_t ( sbr_bus_a_chan_t ), + .obi_r_chan_t ( sbr_bus_r_chan_t ), + .Bypass ( 1'b0 ) + ) i_cut_sbr ( + .clk_i ( clk ), + .rst_ni ( rst_n ), + + .sbr_port_req_i ( sbr_bus_req_cut[i] ), + .sbr_port_rsp_o ( sbr_bus_rsp_cut[i] ), + + .mgr_port_req_o ( sbr_bus_req[i] ), + .mgr_port_rsp_i ( sbr_bus_rsp[i] ) + ); + end + + + logic interface_error; + logic [NumSubordinates-1:0] sbr_error; + logic [NumManagers-1:0] mgr_error; + assign interface_error = |sbr_error | |mgr_error; + + // Collect sent requests and responses + localparam int unsigned NumMABits = $bits(mgr_bus_a_chan_t); + localparam int unsigned NumMRBits = $bits(mgr_bus_r_chan_t); + localparam int unsigned NumSABits = $bits(sbr_bus_a_chan_t); + localparam int unsigned NumSRBits = $bits(sbr_bus_r_chan_t); + // logic [NumMABits-1:0] mgr_bus_a_chan_flat [NumManagers][NumSubordinates][$]; + logic mgr_bus_a_chan_flat_empty [NumManagers][NumSubordinates]; + logic [NumManagers-1:0][NumSubordinates-1:0] mgr_bus_a_chan_flat_pop; + logic [NumMABits-1:0] mgr_bus_a_chan_flat_front [NumManagers][NumSubordinates]; + // logic [$clog2(NumSubordinates)-1:0] mgr_bus_rsp_idx [NumManagers][$]; + logic [$clog2(NumSubordinates)-1:0] mgr_bus_rsp_idx_front [NumManagers]; + logic [$clog2(NumSubordinates)-1:0] mgr_bus_rsp_idx_next [NumManagers]; + logic [NumManagers-1:0] mgr_bus_rsp_idx_push; + // logic [NumSRBits-1:0] sbr_bus_r_chan_flat [NumSubordinates][$]; + logic [NumSRBits-1:0] sbr_bus_r_chan_flat_front [NumSubordinates]; + logic [NumSubordinates-1:0] sbr_bus_r_chan_flat_pop; + logic [NumSubordinates-1:0][NumManagers-1:0] sbr_mgr_match; + logic [NumManagers-1:0][NumSubordinates-1:0] mgr_sbr_match; + + logic [NumSubordinates-1:0] sbr_data_valid; + logic [NumManagers-1:0] mgr_data_valid; + for (genvar i = 0; i < NumSubordinates; i++) begin + assign sbr_data_valid[i] = sbr_bus_req[i].req & sbr_bus_rsp[i].gnt; + assign sbr_error[i] = sbr_bus_req[i].req & sbr_bus_rsp[i].gnt & ~(|sbr_mgr_match[i]); + end + for (genvar i = 0; i < NumManagers; i++) begin + assign mgr_data_valid[i] = mgr_bus_rsp[i].rvalid; + assign mgr_error[i] = mgr_bus_rsp[i].rvalid & ~(|mgr_sbr_match[i]); + end + + for (genvar i = 0; i < NumManagers; i++) begin + for (genvar j = 0; j < NumSubordinates; j++) begin + fifo_v3 #( + .FALL_THROUGH (1'b0), + .DATA_WIDTH (NumMABits), + .DEPTH (NumMaxTrans) + ) i_fifo_mgr_a_chan ( + .clk_i (clk), + .rst_ni (rst_n), + .flush_i (1'b0), + .testmode_i (1'b0), + .full_o (), + .empty_o (mgr_bus_a_chan_flat_empty[i][j]), + .usage_o (), + .data_i (mgr_bus_req[i].a), + .push_i (mgr_bus_req[i].req && mgr_bus_rsp[i].gnt && + mgr_bus_req[i].a.addr >= AddrMap[j].start_addr && + mgr_bus_req[i].a.addr < AddrMap[j].end_addr), + .data_o (mgr_bus_a_chan_flat_front[i][j]), + .pop_i (mgr_bus_a_chan_flat_pop[i][j]) + ); + end + fifo_v3 #( + .FALL_THROUGH (1'b0), + .DATA_WIDTH ($clog2(NumSubordinates)), + .DEPTH (NumMaxTrans) + ) i_fifo_mgr_idx ( + .clk_i (clk), + .rst_ni (rst_n), + .flush_i (1'b0), + .testmode_i (1'b0), + .full_o (), + .empty_o (), + .usage_o (), + .data_i (mgr_bus_rsp_idx_next[i]), + .push_i (mgr_bus_rsp_idx_push[i]), + .data_o (mgr_bus_rsp_idx_front[i]), + .pop_i (mgr_bus_rsp[i].rvalid) + ); + end + for (genvar i = 0; i < NumSubordinates; i++) begin + fifo_v3 #( + .FALL_THROUGH (1'b0), + .DATA_WIDTH (NumSRBits), + .DEPTH (NumMaxTrans*NumManagers) + ) i_fifo_sbr_r_chan ( + .clk_i (clk), + .rst_ni (rst_n), + .flush_i (1'b0), + .testmode_i (1'b0), + .full_o (), + .empty_o (), + .usage_o (), + .data_i (sbr_bus_rsp[i].r), + .push_i (sbr_bus_rsp[i].rvalid), + .data_o (sbr_bus_r_chan_flat_front[i]), + .pop_i (sbr_bus_r_chan_flat_pop[i]) + ); + end + + always_comb begin + for (int unsigned i = 0; i < NumSubordinates; i++) begin + for (int unsigned j = 0; j < NumManagers; j++) begin + if (sbr_bus_req[i].req && sbr_bus_rsp[i].gnt) begin + if (mgr_bus_a_chan_flat_empty[j][i]) begin + sbr_mgr_match[i][j] = 1'b0; + end else begin + sbr_mgr_match[i][j] = mgr_bus_a_chan_flat_front[j][i] == sbr_bus_req[i].a; + end + end else begin + sbr_mgr_match[i][j] = 1'b0; + end + end + end + for (int unsigned i = 0; i < NumManagers; i++) begin + if (mgr_bus_rsp[i].rvalid) begin + mgr_sbr_match[i] = sbr_bus_r_chan_flat_front[mgr_bus_rsp_idx_front[i]] == mgr_bus_rsp[i].r; + end else begin + mgr_sbr_match[i] = '0; + end + end + end + + always_comb begin + mgr_bus_a_chan_flat_pop = '0; + mgr_bus_rsp_idx_push = '0; + sbr_bus_r_chan_flat_pop = '0; + for (int unsigned i = 0; i < NumManagers; i++) begin + for (int unsigned j = 0; j < NumSubordinates; j++) begin + if (mgr_bus_req[i].req && mgr_bus_rsp[i].gnt && + mgr_bus_req[i].a.addr >= AddrMap[j].start_addr && + mgr_bus_req[i].a.addr < AddrMap[j].end_addr) begin + // mgr_bus_a_chan_flat[i][j].push_back(mgr_bus_req[i].a); + mgr_bus_rsp_idx_next[i] = j; + mgr_bus_rsp_idx_push[i] = 1'b1; + // mgr_bus_rsp_idx[i].push_back(j); + end + end + if (mgr_bus_rsp[i].rvalid) begin + sbr_bus_r_chan_flat_pop[mgr_bus_rsp_idx_front[i]] = 1'b1; + // sbr_bus_r_chan_flat[mgr_bus_rsp_idx[i][0]].pop_front(); + // mgr_bus_rsp_idx[i].pop_front(); + end + end + for (int unsigned i = 0; i < NumSubordinates; i++) begin + if (sbr_bus_req[i].req && sbr_bus_rsp[i].gnt) begin + for (int unsigned j = 0; j < NumManagers; j++) begin + if (mgr_bus_a_chan_flat_front[j][i] == sbr_bus_req[i].a) begin + // mgr_bus_a_chan_flat[j][i].pop_front(); + mgr_bus_a_chan_flat_pop[j][i] = 1'b1; + break; + end + end + end + // if (sbr_bus_rsp[i].rvalid) begin + // sbr_bus_r_chan_flat[i].push_back(sbr_bus_rsp[i].r); + // end + end + + // for (int unsigned i = 0; i < NumManagers; i++) begin + // mgr_bus_rsp_idx_front[i] = mgr_bus_rsp_idx[i][0]; + // for (int unsigned j = 0; j < NumSubordinates; j++) begin + // mgr_bus_a_chan_flat_empty[i][j] = (mgr_bus_a_chan_flat[i][j].size() == 0); + // mgr_bus_a_chan_flat_front[i][j] = mgr_bus_a_chan_flat[i][j][0]; + // end + // end + // for (int unsigned i = 0; i < NumSubordinates; i++) begin + // sbr_bus_r_chan_flat_front[i] = sbr_bus_r_chan_flat[i][0]; + // end + end + + // Fault indication + logic corrected_fault; + logic uncorrectable_fault; + assign corrected_fault = '0; + assign uncorrectable_fault = 1'b1; + + `ifdef TARGET_ZOIX + `include "strobe.sv" + `endif + +endmodule + module tb_obi_xbar; localparam int unsigned NumManagers = 32'd6; @@ -17,21 +326,21 @@ module tb_obi_xbar; localparam int unsigned AddrWidth = 32; localparam int unsigned DataWidth = 32; localparam int unsigned MgrIdWidth = 5; - localparam int unsigned SbrIdWidth = MgrIdWidth+cf_math_pkg::idx_width(NumManagers); + localparam int unsigned SbrIdWidth = MgrIdWidth; localparam int unsigned AUserWidth = 4; localparam int unsigned WUserWidth = 2; localparam int unsigned RUserWidth = 3; // TODO CHK! - localparam int unsigned NumRequests = 32'd10000; + localparam int unsigned NumRequests = 32'd1000; localparam time CyclTime = 10ns; localparam time ApplTime = 2ns; localparam time TestTime = 8ns; localparam obi_pkg::obi_cfg_t MgrConfig = '{ - UseRReady: 1'b1, + UseRReady: 1'b0, CombGnt: 1'b0, AddrWidth: AddrWidth, DataWidth: DataWidth, @@ -60,11 +369,11 @@ module tb_obi_xbar; .TA ( ApplTime ), .TT ( TestTime ), .MinAddr (32'h0000_0000), - .MaxAddr (32'h0001_3000) + .MaxAddr (32'h0001_1000) ) rand_manager_t; localparam obi_pkg::obi_cfg_t SbrConfig = '{ - UseRReady: 1'b1, + UseRReady: 1'b0, CombGnt: 1'b0, AddrWidth: AddrWidth, DataWidth: DataWidth, @@ -199,31 +508,67 @@ module tb_obi_xbar; ); // 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 ) + `OBI_TYPEDEF_ALL_DEFAULT_WITH_OPTIONAL(mgr_bus, MgrConfig, mgr_a_optional_t, mgr_r_optional_t) + `OBI_TYPEDEF_ALL_DEFAULT_WITH_OPTIONAL(sbr_bus, SbrConfig, sbr_a_optional_t, sbr_r_optional_t) + mgr_bus_req_t [NumManagers-1:0] mgr_bus_req; + mgr_bus_rsp_t [NumManagers-1:0] mgr_bus_rsp; + sbr_bus_req_t [NumSubordinates-1:0] sbr_bus_req; + sbr_bus_rsp_t [NumSubordinates-1:0] sbr_bus_rsp; + + for (genvar i = 0; i < NumManagers; i++) begin : gen_mgr_encode + `OBI_ASSIGN_TO_REQ(mgr_bus_req[i], mgr_bus[i], MgrConfig) + `OBI_ASSIGN_FROM_RSP(mgr_bus[i], mgr_bus_rsp[i], MgrConfig) + end + + obi_xbar_dut_wrapper #( + .NumManagers(NumManagers), + .NumSubordinates(NumSubordinates), + .MgrConfig(MgrConfig), + .SbrConfig(SbrConfig), + .mgr_bus_req_t(mgr_bus_req_t), + .mgr_bus_rsp_t(mgr_bus_rsp_t), + .sbr_bus_req_t(sbr_bus_req_t), + .sbr_bus_rsp_t(sbr_bus_rsp_t), + .mgr_bus_a_chan_t(mgr_bus_a_chan_t), + .mgr_bus_r_chan_t(mgr_bus_r_chan_t), + .sbr_bus_a_chan_t(sbr_bus_a_chan_t), + .sbr_bus_r_chan_t(sbr_bus_r_chan_t), + .mgr_a_optional_t(mgr_a_optional_t), + .mgr_r_optional_t(mgr_r_optional_t), + .sbr_a_optional_t(sbr_a_optional_t), + .sbr_r_optional_t(sbr_r_optional_t), + .NumMaxTrans(NumMaxTrans), + .NumRules(NumRules), + .rule_t(rule_t), + .AddrMap(AddrMap), + .UseIdForRouting(UseIdForRouting), + .TestTime(TestTime), + .NumRequests(NumRequests) + ) i_dut_wrapper ( + .clk, + .rst_n, + + // Manager ports + .mgr_bus_req, + .mgr_bus_rsp, + + // Subordinate ports + .sbr_bus_req, + .sbr_bus_rsp, + + .end_of_sim(end_of_sim) ); + for (genvar i = 0; i < NumSubordinates; i++) begin : gen_sbr_decode + `OBI_ASSIGN_FROM_REQ(sbr_bus[i], sbr_bus_req[i], SbrConfig) + `OBI_ASSIGN_TO_RSP(sbr_bus_rsp[i], sbr_bus[i], SbrConfig) + end + initial begin wait(&end_of_sim); repeat (1000) @(posedge clk); $display("Simulation stopped as all Masters transferred their data, Success.",); - $stop(); + $finish(); end endmodule diff --git a/src/test/tb_relobi_xbar.sv b/src/test/tb_relobi_xbar.sv new file mode 100644 index 0000000..9e2e723 --- /dev/null +++ b/src/test/tb_relobi_xbar.sv @@ -0,0 +1,685 @@ +// Copyright 2025 ETH Zurich and University of Bologna. +// Solderpad Hardware License, Version 0.51, see LICENSE for details. +// SPDX-License-Identifier: SHL-0.51 + +// Michael Rogenmoser + +`include "obi/typedef.svh" +`include "obi/assign.svh" + +module relobi_xbar_dut_wrapper #( + parameter int unsigned NumManagers = 32'd6, + parameter int unsigned NumSubordinates = 32'd8, + parameter obi_pkg::obi_cfg_t MgrConfig = obi_pkg::ObiDefaultConfig, + parameter obi_pkg::obi_cfg_t SbrConfig = obi_pkg::ObiDefaultConfig, + parameter type rel_mgr_bus_req_t = logic, + parameter type rel_mgr_bus_rsp_t = logic, + parameter type rel_mgr_bus_a_chan_t = logic, + parameter type rel_mgr_bus_r_chan_t = logic, + parameter type rel_sbr_bus_req_t = logic, + parameter type rel_sbr_bus_rsp_t = logic, + parameter type rel_sbr_bus_a_chan_t = logic, + parameter type rel_sbr_bus_r_chan_t = logic, + parameter type mgr_bus_req_t = logic, + parameter type mgr_bus_rsp_t = logic, + parameter type sbr_bus_req_t = logic, + parameter type sbr_bus_rsp_t = logic, + parameter type mgr_bus_a_chan_t = logic, + parameter type mgr_bus_r_chan_t = logic, + parameter type sbr_bus_a_chan_t = logic, + parameter type sbr_bus_r_chan_t = logic, + parameter type mgr_a_optional_t = logic, + parameter type mgr_r_optional_t = logic, + parameter type sbr_a_optional_t = logic, + parameter type sbr_r_optional_t = logic, + parameter int unsigned NumMaxTrans = 32'd8, + parameter int unsigned NumRules = 8, + parameter type rule_t = logic, + parameter rule_t [NumRules-1:0] AddrMap = '0, + parameter bit UseIdForRouting = 1'b0, + parameter time TestTime = 8ns, + parameter int unsigned NumRequests = 1 +) ( + input logic clk, + input logic rst_n, + + // Manager ports + input mgr_bus_req_t [NumManagers-1:0] mgr_bus_req, + output mgr_bus_rsp_t [NumManagers-1:0] mgr_bus_rsp, + + // Subordinate ports + output sbr_bus_req_t [NumSubordinates-1:0] sbr_bus_req, + input sbr_bus_rsp_t [NumSubordinates-1:0] sbr_bus_rsp, + + input logic [NumManagers-1:0] end_of_sim +); + + localparam int unsigned MgrBusReqBits = $bits(mgr_bus_req_t); + localparam int unsigned MgrBusRspBits = $bits(mgr_bus_rsp_t); + localparam int unsigned SbrBusReqBits = $bits(sbr_bus_req_t); + localparam int unsigned SbrBusRspBits = $bits(sbr_bus_rsp_t); + + rel_mgr_bus_req_t [NumManagers-1:0] rel_mgr_bus_req, rel_mgr_bus_req_cut; + rel_mgr_bus_rsp_t [NumManagers-1:0] rel_mgr_bus_rsp, rel_mgr_bus_rsp_cut; + rel_sbr_bus_req_t [NumSubordinates-1:0] rel_sbr_bus_req, rel_sbr_bus_req_cut; + rel_sbr_bus_rsp_t [NumSubordinates-1:0] rel_sbr_bus_rsp, rel_sbr_bus_rsp_cut; + + logic [1:0] xbar_fault; + logic [NumManagers-1:0][1:0] encoder_faults; + logic [NumManagers-1:0][1:0] enc_cut_faults; + logic [NumSubordinates-1:0][1:0] decoder_faults; + logic [NumSubordinates-1:0][1:0] dec_cut_faults; + + logic [NumManagers-1:0][MgrBusRspBits-1:0] mgr_bus_rsp_flat; + for (genvar i = 0; i < NumManagers; i++) begin : gen_mgr_bus_rsp + assign mgr_bus_rsp_flat[i] = mgr_bus_rsp[i]; + end + + logic [NumSubordinates-1:0][SbrBusReqBits-1:0] sbr_bus_req_flat; + for (genvar i = 0; i < NumSubordinates; i++) begin : gen_sbr_bus_req + assign sbr_bus_req_flat[i] = sbr_bus_req[i]; + end + + + for (genvar i = 0; i < NumManagers; i++) begin : gen_mgr_encode + relobi_encoder #( + .Cfg ( MgrConfig ), + .relobi_req_t ( rel_mgr_bus_req_t ), + .relobi_rsp_t ( rel_mgr_bus_rsp_t ), + .obi_req_t ( mgr_bus_req_t ), + .obi_rsp_t ( mgr_bus_rsp_t ), + .a_optional_t ( mgr_a_optional_t ), + .r_optional_t ( mgr_r_optional_t ) + ) i_enc_mgr ( + .req_i (mgr_bus_req[i]), + .rsp_o (mgr_bus_rsp[i]), + + .rel_req_o (rel_mgr_bus_req[i]), + .rel_rsp_i (rel_mgr_bus_rsp[i]), + + .fault_o (encoder_faults[i] ) + ); + end + + for (genvar i = 0; i < NumManagers; i++) begin : gen_mgr_cut + relobi_cut #( + .ObiCfg ( MgrConfig ), + .obi_req_t ( rel_mgr_bus_req_t ), + .obi_rsp_t ( rel_mgr_bus_rsp_t ), + .obi_a_chan_t ( rel_mgr_bus_a_chan_t ), + .obi_r_chan_t ( rel_mgr_bus_r_chan_t ), + .a_optional_t ( mgr_a_optional_t ), + .r_optional_t ( mgr_r_optional_t ), + .Bypass ( 1'b0 ) + ) i_cut_mgr ( + .clk_i ( clk ), + .rst_ni ( rst_n ), + + .sbr_port_req_i ( rel_mgr_bus_req[i] ), + .sbr_port_rsp_o ( rel_mgr_bus_rsp[i] ), + + .mgr_port_req_o ( rel_mgr_bus_req_cut[i] ), + .mgr_port_rsp_i ( rel_mgr_bus_rsp_cut[i] ), + + .fault_o ( enc_cut_faults[i] ) + ); + end + + relobi_xbar #( + .SbrPortObiCfg ( MgrConfig ), + .MgrPortObiCfg ( SbrConfig ), + .sbr_port_obi_req_t ( rel_mgr_bus_req_t ), + .sbr_port_a_chan_t ( rel_mgr_bus_a_chan_t ), + .sbr_port_obi_rsp_t ( rel_mgr_bus_rsp_t ), + .sbr_port_r_chan_t ( rel_mgr_bus_r_chan_t ), + .mgr_port_obi_req_t ( rel_sbr_bus_req_t ), + .mgr_port_obi_rsp_t ( rel_sbr_bus_rsp_t ), + .mgr_port_a_chan_t ( rel_sbr_bus_a_chan_t ), + .mgr_port_r_chan_t ( rel_sbr_bus_r_chan_t ), + .a_optional_t ( mgr_a_optional_t ), + .r_optional_t ( mgr_r_optional_t ), + .NumSbrPorts ( NumManagers ), + .NumMgrPorts ( NumSubordinates ), + .NumMaxTrans ( NumMaxTrans ), + .NumAddrRules ( NumRules ), + .addr_map_rule_t ( rule_t ), + .UseIdForRouting ( UseIdForRouting ), + .TmrMap (1'b1), + .DecodeAbort (1'b1) + ) i_dut ( + .clk_i ( clk ), + .rst_ni ( rst_n ), + .testmode_i ( 1'b0 ), + + .sbr_ports_req_i ( rel_mgr_bus_req_cut ), + .sbr_ports_rsp_o ( rel_mgr_bus_rsp_cut ), + + .mgr_ports_req_o ( rel_sbr_bus_req_cut ), + .mgr_ports_rsp_i ( rel_sbr_bus_rsp_cut ), + + .addr_map_i ( {3{AddrMap}} ), + .en_default_idx_i ( '0 ), + .default_idx_i ( '0 ), + + .fault_o ( xbar_fault ) + ); + + for (genvar i = 0; i < NumSubordinates; i++) begin : gen_sbr_cut + relobi_cut #( + .ObiCfg ( SbrConfig ), + .obi_req_t ( rel_sbr_bus_req_t ), + .obi_rsp_t ( rel_sbr_bus_rsp_t ), + .obi_a_chan_t ( rel_sbr_bus_a_chan_t ), + .obi_r_chan_t ( rel_sbr_bus_r_chan_t ), + .a_optional_t ( sbr_a_optional_t ), + .r_optional_t ( sbr_r_optional_t ), + .Bypass ( 1'b0 ) + ) i_cut_sbr ( + .clk_i ( clk ), + .rst_ni ( rst_n ), + + .sbr_port_req_i ( rel_sbr_bus_req_cut[i] ), + .sbr_port_rsp_o ( rel_sbr_bus_rsp_cut[i] ), + + .mgr_port_req_o ( rel_sbr_bus_req[i] ), + .mgr_port_rsp_i ( rel_sbr_bus_rsp[i] ), + + .fault_o ( dec_cut_faults[i] ) + ); + end + + for (genvar i = 0; i < NumSubordinates; i++) begin : gen_sbr_decode + relobi_decoder #( + .Cfg ( SbrConfig ), + .relobi_req_t ( rel_sbr_bus_req_t ), + .relobi_rsp_t ( rel_sbr_bus_rsp_t ), + .obi_req_t ( sbr_bus_req_t ), + .obi_rsp_t ( sbr_bus_rsp_t ), + .a_optional_t ( sbr_a_optional_t ), + .r_optional_t ( sbr_r_optional_t ) + ) i_dec_sbr ( + .req_o (sbr_bus_req[i]), + .rsp_i (sbr_bus_rsp[i]), + + .rel_req_i (rel_sbr_bus_req[i]), + .rel_rsp_o (rel_sbr_bus_rsp[i]), + + .fault_o ( decoder_faults[i] ) + ); + end + + logic interface_error; + logic [NumSubordinates-1:0] sbr_error; + logic [NumManagers-1:0] mgr_error; + assign interface_error = |sbr_error | |mgr_error; + + // Collect sent requests and responses + localparam int unsigned NumMABits = $bits(mgr_bus_a_chan_t); + localparam int unsigned NumMRBits = $bits(mgr_bus_r_chan_t); + localparam int unsigned NumSABits = $bits(sbr_bus_a_chan_t); + localparam int unsigned NumSRBits = $bits(sbr_bus_r_chan_t); + // logic [NumMABits-1:0] mgr_bus_a_chan_flat [NumManagers][NumSubordinates][$]; + logic mgr_bus_a_chan_flat_empty [NumManagers][NumSubordinates]; + logic [NumManagers-1:0][NumSubordinates-1:0] mgr_bus_a_chan_flat_pop; + logic [NumMABits-1:0] mgr_bus_a_chan_flat_front [NumManagers][NumSubordinates]; + // logic [$clog2(NumSubordinates)-1:0] mgr_bus_rsp_idx [NumManagers][$]; + logic [$clog2(NumSubordinates)-1:0] mgr_bus_rsp_idx_front [NumManagers]; + logic [$clog2(NumSubordinates)-1:0] mgr_bus_rsp_idx_next [NumManagers]; + logic [NumManagers-1:0] mgr_bus_rsp_idx_push; + // logic [NumSRBits-1:0] sbr_bus_r_chan_flat [NumSubordinates][$]; + logic [NumSRBits-1:0] sbr_bus_r_chan_flat_front [NumSubordinates]; + logic [NumSubordinates-1:0] sbr_bus_r_chan_flat_pop; + logic [NumSubordinates-1:0][NumManagers-1:0] sbr_mgr_match; + logic [NumManagers-1:0][NumSubordinates-1:0] mgr_sbr_match; + + logic [NumSubordinates-1:0] sbr_data_valid; + logic [NumManagers-1:0] mgr_data_valid; + for (genvar i = 0; i < NumSubordinates; i++) begin + assign sbr_data_valid[i] = sbr_bus_req[i].req & sbr_bus_rsp[i].gnt; + assign sbr_error[i] = sbr_bus_req[i].req & sbr_bus_rsp[i].gnt & ~(|sbr_mgr_match[i]); + end + for (genvar i = 0; i < NumManagers; i++) begin + assign mgr_data_valid[i] = mgr_bus_rsp[i].rvalid; + assign mgr_error[i] = mgr_bus_rsp[i].rvalid & ~(|mgr_sbr_match[i]); + end + + for (genvar i = 0; i < NumManagers; i++) begin + for (genvar j = 0; j < NumSubordinates; j++) begin + fifo_v3 #( + .FALL_THROUGH (1'b0), + .DATA_WIDTH (NumMABits), + .DEPTH (NumMaxTrans) + ) i_fifo_mgr_a_chan ( + .clk_i (clk), + .rst_ni (rst_n), + .flush_i (1'b0), + .testmode_i (1'b0), + .full_o (), + .empty_o (mgr_bus_a_chan_flat_empty[i][j]), + .usage_o (), + .data_i (mgr_bus_req[i].a), + .push_i (mgr_bus_req[i].req && mgr_bus_rsp[i].gnt && + mgr_bus_req[i].a.addr >= AddrMap[j].start_addr && + mgr_bus_req[i].a.addr < AddrMap[j].end_addr), + .data_o (mgr_bus_a_chan_flat_front[i][j]), + .pop_i (mgr_bus_a_chan_flat_pop[i][j]) + ); + end + fifo_v3 #( + .FALL_THROUGH (1'b0), + .DATA_WIDTH ($clog2(NumSubordinates)), + .DEPTH (NumMaxTrans) + ) i_fifo_mgr_idx ( + .clk_i (clk), + .rst_ni (rst_n), + .flush_i (1'b0), + .testmode_i (1'b0), + .full_o (), + .empty_o (), + .usage_o (), + .data_i (mgr_bus_rsp_idx_next[i]), + .push_i (mgr_bus_rsp_idx_push[i]), + .data_o (mgr_bus_rsp_idx_front[i]), + .pop_i (mgr_bus_rsp[i].rvalid) + ); + end + for (genvar i = 0; i < NumSubordinates; i++) begin + fifo_v3 #( + .FALL_THROUGH (1'b0), + .DATA_WIDTH (NumSRBits), + .DEPTH (NumMaxTrans*NumManagers) + ) i_fifo_sbr_r_chan ( + .clk_i (clk), + .rst_ni (rst_n), + .flush_i (1'b0), + .testmode_i (1'b0), + .full_o (), + .empty_o (), + .usage_o (), + .data_i (sbr_bus_rsp[i].r), + .push_i (sbr_bus_rsp[i].rvalid), + .data_o (sbr_bus_r_chan_flat_front[i]), + .pop_i (sbr_bus_r_chan_flat_pop[i]) + ); + end + + always_comb begin + for (int unsigned i = 0; i < NumSubordinates; i++) begin + for (int unsigned j = 0; j < NumManagers; j++) begin + if (sbr_bus_req[i].req && sbr_bus_rsp[i].gnt) begin + if (mgr_bus_a_chan_flat_empty[j][i]) begin + sbr_mgr_match[i][j] = 1'b0; + end else begin + sbr_mgr_match[i][j] = mgr_bus_a_chan_flat_front[j][i] == sbr_bus_req[i].a; + end + end else begin + sbr_mgr_match[i][j] = 1'b0; + end + end + end + for (int unsigned i = 0; i < NumManagers; i++) begin + if (mgr_bus_rsp[i].rvalid) begin + mgr_sbr_match[i] = sbr_bus_r_chan_flat_front[mgr_bus_rsp_idx_front[i]] == mgr_bus_rsp[i].r; + end else begin + mgr_sbr_match[i] = '0; + end + end + end + + always_comb begin + mgr_bus_a_chan_flat_pop = '0; + mgr_bus_rsp_idx_push = '0; + sbr_bus_r_chan_flat_pop = '0; + for (int unsigned i = 0; i < NumManagers; i++) begin + for (int unsigned j = 0; j < NumSubordinates; j++) begin + if (mgr_bus_req[i].req && mgr_bus_rsp[i].gnt && + mgr_bus_req[i].a.addr >= AddrMap[j].start_addr && + mgr_bus_req[i].a.addr < AddrMap[j].end_addr) begin + // mgr_bus_a_chan_flat[i][j].push_back(mgr_bus_req[i].a); + mgr_bus_rsp_idx_next[i] = j; + mgr_bus_rsp_idx_push[i] = 1'b1; + // mgr_bus_rsp_idx[i].push_back(j); + end + end + if (mgr_bus_rsp[i].rvalid) begin + sbr_bus_r_chan_flat_pop[mgr_bus_rsp_idx_front[i]] = 1'b1; + // sbr_bus_r_chan_flat[mgr_bus_rsp_idx[i][0]].pop_front(); + // mgr_bus_rsp_idx[i].pop_front(); + end + end + for (int unsigned i = 0; i < NumSubordinates; i++) begin + if (sbr_bus_req[i].req && sbr_bus_rsp[i].gnt) begin + for (int unsigned j = 0; j < NumManagers; j++) begin + if (mgr_bus_a_chan_flat_front[j][i] == sbr_bus_req[i].a) begin + // mgr_bus_a_chan_flat[j][i].pop_front(); + mgr_bus_a_chan_flat_pop[j][i] = 1'b1; + break; + end + end + end + // if (sbr_bus_rsp[i].rvalid) begin + // sbr_bus_r_chan_flat[i].push_back(sbr_bus_rsp[i].r); + // end + end + + // for (int unsigned i = 0; i < NumManagers; i++) begin + // mgr_bus_rsp_idx_front[i] = mgr_bus_rsp_idx[i][0]; + // for (int unsigned j = 0; j < NumSubordinates; j++) begin + // mgr_bus_a_chan_flat_empty[i][j] = (mgr_bus_a_chan_flat[i][j].size() == 0); + // mgr_bus_a_chan_flat_front[i][j] = mgr_bus_a_chan_flat[i][j][0]; + // end + // end + // for (int unsigned i = 0; i < NumSubordinates; i++) begin + // sbr_bus_r_chan_flat_front[i] = sbr_bus_r_chan_flat[i][0]; + // end + end + + // Fault indication + logic corrected_fault; + logic uncorrectable_fault; + always_comb begin + corrected_fault = xbar_fault[0]; + uncorrectable_fault = xbar_fault[1]; + + for (int unsigned i = 0; i < NumManagers; i++) begin + corrected_fault |= encoder_faults[i][0] | enc_cut_faults[i][0]; + uncorrectable_fault |= encoder_faults[i][1] | enc_cut_faults[i][1]; + end + for (int unsigned i = 0; i < NumSubordinates; i++) begin + corrected_fault |= decoder_faults[i][0] | dec_cut_faults[i][0]; + uncorrectable_fault |= decoder_faults[i][1] | dec_cut_faults[i][1]; + end + end + + always @(posedge clk) begin + #(TestTime); + if (rst_n == 1'b1) begin + if (uncorrectable_fault) begin + $display("Uncorrectable fault detected in the crossbar at %t.", $time); + end else if (corrected_fault) begin + $display("Corrected fault detected in the crossbar at %t.", $time); + end + end + end + + `ifdef TARGET_ZOIX + `include "strobe.sv" + `endif + +endmodule + +module tb_relobi_xbar; + import obi_pkg::*; + + 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; + localparam int unsigned AUserWidth = 4; + localparam int unsigned WUserWidth = 2; + localparam int unsigned RUserWidth = 3; + + // TODO CHK! + + localparam int unsigned NumRequests = 32'd1000; + + 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'b1, + UseProt: 1'b1, + UseDbg: 1'b1, + AUserWidth: AUserWidth, + WUserWidth: WUserWidth, + RUserWidth: RUserWidth, + MidWidth: 0, + AChkWidth: 0, + RChkWidth: 0 + } + }; + + typedef struct packed { + logic [ AUserWidth-1:0] auser; + logic [ WUserWidth-1:0] wuser; + obi_pkg::atop_t atop; + obi_pkg::memtype_t memtype; + obi_pkg::prot_t prot; + logic dbg; + } mgr_a_optional_t; + typedef struct packed { + logic [RUserWidth-1:0] ruser; + logic exokay; + } mgr_r_optional_t; + 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_1000) + ) rand_manager_t; + + 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'b1, + UseMemtype: 1'b1, + UseProt: 1'b1, + UseDbg: 1'b1, + AUserWidth: AUserWidth, + WUserWidth: WUserWidth, + RUserWidth: RUserWidth, + MidWidth: 0, + AChkWidth: 0, + RChkWidth: 0 + } + }; + + typedef mgr_a_optional_t sbr_a_optional_t; + typedef mgr_r_optional_t sbr_r_optional_t; + + 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), + prot: obi_pkg::prot_t'('0), + dbg: '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), + prot: obi_pkg::prot_t'('0), + dbg: '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_TYPEDEF_ALL_DEFAULT_WITH_OPTIONAL(mgr_bus, MgrConfig, mgr_a_optional_t, mgr_r_optional_t) + `OBI_TYPEDEF_ALL_DEFAULT_WITH_OPTIONAL(sbr_bus, SbrConfig, sbr_a_optional_t, sbr_r_optional_t) + `RELOBI_TYPEDEF_ALL_WITH_OPTIONAL(rel_mgr_bus, MgrConfig, mgr_a_optional_t, mgr_r_optional_t) + `RELOBI_TYPEDEF_ALL_WITH_OPTIONAL(rel_sbr_bus, SbrConfig, sbr_a_optional_t, sbr_r_optional_t) + mgr_bus_req_t [NumManagers-1:0] mgr_bus_req; + mgr_bus_rsp_t [NumManagers-1:0] mgr_bus_rsp; + sbr_bus_req_t [NumSubordinates-1:0] sbr_bus_req; + sbr_bus_rsp_t [NumSubordinates-1:0] sbr_bus_rsp; + + for (genvar i = 0; i < NumManagers; i++) begin : gen_mgr_encode + `OBI_ASSIGN_TO_REQ(mgr_bus_req[i], mgr_bus[i], MgrConfig) + `OBI_ASSIGN_FROM_RSP(mgr_bus[i], mgr_bus_rsp[i], MgrConfig) + end + + relobi_xbar_dut_wrapper #( + .NumManagers(NumManagers), + .NumSubordinates(NumSubordinates), + .MgrConfig(MgrConfig), + .SbrConfig(SbrConfig), + .rel_mgr_bus_req_t(rel_mgr_bus_req_t), + .rel_mgr_bus_rsp_t(rel_mgr_bus_rsp_t), + .rel_mgr_bus_a_chan_t(rel_mgr_bus_a_chan_t), + .rel_mgr_bus_r_chan_t(rel_mgr_bus_r_chan_t), + .rel_sbr_bus_req_t(rel_sbr_bus_req_t), + .rel_sbr_bus_rsp_t(rel_sbr_bus_rsp_t), + .rel_sbr_bus_a_chan_t(rel_sbr_bus_a_chan_t), + .rel_sbr_bus_r_chan_t(rel_sbr_bus_r_chan_t), + .mgr_bus_req_t(mgr_bus_req_t), + .mgr_bus_rsp_t(mgr_bus_rsp_t), + .sbr_bus_req_t(sbr_bus_req_t), + .sbr_bus_rsp_t(sbr_bus_rsp_t), + .mgr_bus_a_chan_t(mgr_bus_a_chan_t), + .mgr_bus_r_chan_t(mgr_bus_r_chan_t), + .sbr_bus_a_chan_t(sbr_bus_a_chan_t), + .sbr_bus_r_chan_t(sbr_bus_r_chan_t), + .mgr_a_optional_t(mgr_a_optional_t), + .mgr_r_optional_t(mgr_r_optional_t), + .sbr_a_optional_t(sbr_a_optional_t), + .sbr_r_optional_t(sbr_r_optional_t), + .NumMaxTrans(NumMaxTrans), + .NumRules(NumRules), + .rule_t(rule_t), + .AddrMap(AddrMap), + .UseIdForRouting(UseIdForRouting), + .TestTime(TestTime), + .NumRequests(NumRequests) + ) i_dut_wrapper ( + .clk, + .rst_n, + + // Manager ports + .mgr_bus_req, + .mgr_bus_rsp, + + // Subordinate ports + .sbr_bus_req, + .sbr_bus_rsp, + + .end_of_sim(end_of_sim) + ); + + for (genvar i = 0; i < NumSubordinates; i++) begin : gen_sbr_decode + `OBI_ASSIGN_FROM_REQ(sbr_bus[i], sbr_bus_req[i], SbrConfig) + `OBI_ASSIGN_TO_RSP(sbr_bus_rsp[i], sbr_bus[i], SbrConfig) + end + + initial begin + wait(&end_of_sim); + repeat (1000) @(posedge clk); + $display("Simulation stopped as all Masters transferred their data, Success.",); + $finish(); + end + +endmodule