From eb5b8d86a97c5ee9ca237ec9c2f1b707b6f6bbe3 Mon Sep 17 00:00:00 2001 From: Marno van der Maas Date: Fri, 1 May 2026 15:19:09 +0100 Subject: [PATCH 01/10] [hw] CVA6 config adjusted in top_chip_system This moves the Mocha specific changes to the top instead of a commit in the vendored in repository. --- hw/top_chip/rtl/top_chip_system.sv | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/hw/top_chip/rtl/top_chip_system.sv b/hw/top_chip/rtl/top_chip_system.sv index 95477bfa..d8bd1c8f 100644 --- a/hw/top_chip/rtl/top_chip_system.sv +++ b/hw/top_chip/rtl/top_chip_system.sv @@ -76,8 +76,12 @@ module top_chip_system #( // CVA6 configuration function automatic config_pkg::cva6_cfg_t build_cva6_config(config_pkg::cva6_user_cfg_t CVA6UserCfg); config_pkg::cva6_user_cfg_t cfg = CVA6UserCfg; + // Extensions cfg.RVZiCond = bit'(0); + cfg.RVF = bit'(0); + cfg.RVD = bit'(0); cfg.CvxifEn = bit'(0); + // Memory map cfg.DmBaseAddress = top_pkg::DebugMemBase; cfg.NrExecuteRegionRules = unsigned'(4); cfg.ExecuteRegionAddrBase = 1024'({top_pkg::DRAMBase, From 5d4f5dc11401d5d92062fb42f8c2fb74b2f53f11 Mon Sep 17 00:00:00 2001 From: Marno van der Maas Date: Fri, 1 May 2026 16:17:43 +0100 Subject: [PATCH 02/10] [vendor] HPDCache vendored in --- hw/vendor/hpdcache.vendor.hjson | 17 +++++++++++++++++ util/artefacts.py | 1 + 2 files changed, 18 insertions(+) create mode 100644 hw/vendor/hpdcache.vendor.hjson diff --git a/hw/vendor/hpdcache.vendor.hjson b/hw/vendor/hpdcache.vendor.hjson new file mode 100644 index 00000000..9d1e4b18 --- /dev/null +++ b/hw/vendor/hpdcache.vendor.hjson @@ -0,0 +1,17 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +{ + name: "hpdcache", + target_dir: "hpdcache", + + upstream: { + url: "https://github.com/Capabilities-Limited/cv-hpdcache.git", + rev: "cva6-zcheri-support", + }, + + exclude_from_upstream: [ + ".github", + "docs/old", + ], +} diff --git a/util/artefacts.py b/util/artefacts.py index 758e04c7..39c5903a 100755 --- a/util/artefacts.py +++ b/util/artefacts.py @@ -102,6 +102,7 @@ # vendored hardware dependencies ["util/vendor.py", "hw/vendor/axi_riscv_atomics.vendor.hjson"], ["util/vendor.py", "hw/vendor/cva6_cheri.vendor.hjson"], + ["util/vendor.py", "hw/vendor/hpdcache.vendor.hjson"], ["util/vendor.py", "hw/vendor/lowrisc_ip.vendor.hjson"], ["util/vendor.py", "hw/vendor/pulp_axi.vendor.hjson"], ["util/vendor.py", "hw/vendor/pulp_axi_llc.vendor.hjson"], From 17b34ae715ded5cd925872d8ac1e8509ab94b901 Mon Sep 17 00:00:00 2001 From: Marno van der Maas Date: Fri, 1 May 2026 16:06:06 +0100 Subject: [PATCH 03/10] [core] HPDCache core file created This commit also switches CVA6's core file to use the HPDCache configuration and to depend on the HPDCache. It also adds the License information for the HPDCache in REUSE.toml --- hw/vendor/REUSE.toml | 8 +++++ hw/vendor/cva6.core | 25 +++++++------- hw/vendor/hpdcache.core | 75 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 95 insertions(+), 13 deletions(-) create mode 100644 hw/vendor/hpdcache.core diff --git a/hw/vendor/REUSE.toml b/hw/vendor/REUSE.toml index 8581fc39..7c007fbd 100644 --- a/hw/vendor/REUSE.toml +++ b/hw/vendor/REUSE.toml @@ -41,6 +41,14 @@ SPDX-FileCopyrightText = [ "2025 Capabilities Limited." ] +[[annotations]] +path = ["hpdcache/**"] +SPDX-License-Identifier = "Apache-2.0 WITH SHL-2.1" +SPDX-FileCopyrightText = [ + "Copyright 2023,2024 Commissariat a l'Energie Atomique et aux Energies Alternatives (CEA)", + "Copyright 2025 Univ. Grenoble Alpes, Inria, TIMA Laboratory" +] + [[annotations]] path = ["axi_riscv_atomics/**"] SPDX-License-Identifier = "SHL-0.51" diff --git a/hw/vendor/cva6.core b/hw/vendor/cva6.core index 0248a3d1..3a5fdd17 100644 --- a/hw/vendor/cva6.core +++ b/hw/vendor/cva6.core @@ -11,6 +11,7 @@ filesets: depend: - pulp-platform.org::axi - lowrisc:common_cells:all + - lowrisc:cosmic:hpdcache - lowrisc:prim:lfsr files: # Includes. @@ -18,7 +19,7 @@ filesets: - cva6_cheri/core/include/cvxif_types.svh: {is_include_file: true, include_path: cva6_cheri/core/include} # Packages. - cva6_cheri/core/include/config_pkg.sv - - cva6_cheri/core/include/cv64a6_imafdchzcheri_sv39_config_pkg.sv + - cva6_cheri/core/include/cv64a6_imafdczcheri_sv39_hpdcache_wb_config_pkg.sv - cva6_cheri/core/include/riscv_pkg.sv - cva6_cheri/core/include/cva6_cheri_pkg.sv - cva6_cheri/core/include/wt_cache_pkg.sv @@ -30,6 +31,7 @@ filesets: - cva6_cheri/core/cva6.sv - cva6_cheri/core/cva6_rvfi_probes.sv - cva6_cheri/core/alu.sv + - cva6_cheri/core/alu_wrapper.sv - cva6_cheri/core/branch_unit.sv - cva6_cheri/core/compressed_decoder.sv - cva6_cheri/core/macro_decoder.sv @@ -70,18 +72,6 @@ filesets: - cva6_cheri/core/frontend/instr_scan.sv - cva6_cheri/core/frontend/instr_queue.sv - cva6_cheri/core/frontend/frontend.sv - # Cache. - - cva6_cheri/core/cache_subsystem/wt_dcache_ctrl.sv - - cva6_cheri/core/cache_subsystem/wt_dcache_mem.sv - - cva6_cheri/core/cache_subsystem/wt_dcache_missunit.sv - - cva6_cheri/core/cache_subsystem/wt_dcache_wbuffer.sv - - cva6_cheri/core/cache_subsystem/wt_dcache.sv - - cva6_cheri/core/cache_subsystem/cva6_icache.sv - - cva6_cheri/core/cache_subsystem/wt_cache_subsystem.sv - - cva6_cheri/core/cache_subsystem/wt_axi_adapter.sv - - cva6_cheri/common/local/util/tc_sram_wrapper.sv - - cva6_cheri/common/local/util/sram.sv - - cva6_cheri/common/local/util/sram_cache.sv # Physical memory protection. - cva6_cheri/core/pmp/src/pmp.sv - cva6_cheri/core/pmp/src/pmp_entry.sv @@ -91,6 +81,15 @@ filesets: - cva6_cheri/core/cva6_mmu/cva6_ptw.sv - cva6_cheri/core/cva6_mmu/cva6_tlb.sv - cva6_cheri/core/cva6_mmu/cva6_shared_tlb.sv + # HPDCache + - cva6_cheri/common/local/util/sram.sv + - cva6_cheri/common/local/util/sram_cache.sv + - cva6_cheri/common/local/util/tc_sram_wrapper.sv + - cva6_cheri/core/cache_subsystem/cva6_hpdcache_subsystem.sv + - cva6_cheri/core/cache_subsystem/cva6_icache.sv + - cva6_cheri/core/cache_subsystem/cva6_hpdcache_subsystem_axi_arbiter.sv + - cva6_cheri/core/cache_subsystem/cva6_hpdcache_if_adapter.sv + - cva6_cheri/core/cache_subsystem/cva6_hpdcache_wrapper.sv file_type: systemVerilogSource files_model: diff --git a/hw/vendor/hpdcache.core b/hw/vendor/hpdcache.core new file mode 100644 index 00000000..83182ed2 --- /dev/null +++ b/hw/vendor/hpdcache.core @@ -0,0 +1,75 @@ +CAPI=2: +# Copyright lowRISC contributors (COSMIC project). +# Licensed under the Apache License, Version 2.0, see LICENSE for details. +# SPDX-License-Identifier: Apache-2.0 + +name: "lowrisc:cosmic:hpdcache" +description: "HPDCache core file for COSMIC project." + +filesets: + files_rtl: + depend: + - pulp-platform.org::axi + - lowrisc:common_cells:all + files: + # Includes. + - hpdcache/rtl/include/hpdcache_typedef.svh: {is_include_file: true, include_path: hpdcache/rtl/include} + # RTL. + - hpdcache/rtl/src/hpdcache_pkg.sv + - hpdcache/rtl/src/utils/hpdcache_mem_resp_demux.sv + - hpdcache/rtl/src/utils/hpdcache_mem_req_read_arbiter.sv + - hpdcache/rtl/src/utils/hpdcache_mem_req_write_arbiter.sv + - hpdcache/rtl/src/utils/hpdcache_mem_to_axi_read.sv + - hpdcache/rtl/src/utils/hpdcache_mem_to_axi_write.sv + - hpdcache/rtl/src/common/hpdcache_data_downsize.sv + - hpdcache/rtl/src/common/hpdcache_data_resize.sv + - hpdcache/rtl/src/common/hpdcache_data_upsize.sv + - hpdcache/rtl/src/common/hpdcache_demux.sv + - hpdcache/rtl/src/common/hpdcache_lfsr.sv + - hpdcache/rtl/src/common/hpdcache_sync_buffer.sv + - hpdcache/rtl/src/common/hpdcache_fifo_reg.sv + - hpdcache/rtl/src/common/hpdcache_fifo_reg_initialized.sv + - hpdcache/rtl/src/common/hpdcache_fxarb.sv + - hpdcache/rtl/src/common/hpdcache_rrarb.sv + - hpdcache/rtl/src/common/hpdcache_mux.sv + - hpdcache/rtl/src/common/hpdcache_decoder.sv + - hpdcache/rtl/src/common/hpdcache_1hot_to_binary.sv + - hpdcache/rtl/src/common/hpdcache_prio_1hot_encoder.sv + - hpdcache/rtl/src/common/hpdcache_prio_bin_encoder.sv + - hpdcache/rtl/src/common/hpdcache_sram.sv + - hpdcache/rtl/src/common/hpdcache_sram_wbyteenable.sv + - hpdcache/rtl/src/common/hpdcache_sram_watomenable.sv + - hpdcache/rtl/src/common/hpdcache_sram_wmask.sv + - hpdcache/rtl/src/common/hpdcache_regbank_wbyteenable_1rw.sv + - hpdcache/rtl/src/common/hpdcache_regbank_wmask_1rw.sv + - hpdcache/rtl/src/common/macros/behav/hpdcache_sram_1rw.sv + - hpdcache/rtl/src/common/macros/behav/hpdcache_sram_wbyteenable_1rw.sv + - hpdcache/rtl/src/common/macros/behav/hpdcache_sram_watomenable_1rw.sv + - hpdcache/rtl/src/common/macros/behav/hpdcache_sram_wmask_1rw.sv + - hpdcache/rtl/src/hwpf_stride/hwpf_stride_pkg.sv + - hpdcache/rtl/src/hwpf_stride/hwpf_stride.sv + - hpdcache/rtl/src/hwpf_stride/hwpf_stride_arb.sv + - hpdcache/rtl/src/hwpf_stride/hwpf_stride_wrapper.sv + - hpdcache/rtl/src/hpdcache.sv + - hpdcache/rtl/src/hpdcache_amo.sv + - hpdcache/rtl/src/hpdcache_cbuf.sv + - hpdcache/rtl/src/hpdcache_cmo.sv + - hpdcache/rtl/src/hpdcache_core_arbiter.sv + - hpdcache/rtl/src/hpdcache_ctrl.sv + - hpdcache/rtl/src/hpdcache_ctrl_pe.sv + - hpdcache/rtl/src/hpdcache_flush.sv + - hpdcache/rtl/src/hpdcache_memctrl.sv + - hpdcache/rtl/src/hpdcache_miss_handler.sv + - hpdcache/rtl/src/hpdcache_mshr.sv + - hpdcache/rtl/src/hpdcache_rtab.sv + - hpdcache/rtl/src/hpdcache_uncached.sv + - hpdcache/rtl/src/hpdcache_victim_plru.sv + - hpdcache/rtl/src/hpdcache_victim_random.sv + - hpdcache/rtl/src/hpdcache_victim_sel.sv + - hpdcache/rtl/src/hpdcache_wbuf.sv + file_type: systemVerilogSource + +targets: + default: + filesets: + - files_rtl From a186794d1f05827acb3f6b1c94e3d2dbcca8781b Mon Sep 17 00:00:00 2001 From: Marno van der Maas Date: Mon, 4 May 2026 10:56:39 +0100 Subject: [PATCH 04/10] Update hpdcache to Capabilities-Limited/cv-hpdcache@07f4ebe Update code from upstream repository https://github.com/Capabilities-Limited/cv-hpdcache.git to revision 07f4ebe7bd074e2f82f6b10c2386026bc12455d8 Signed-off-by: Marno van der Maas --- hw/vendor/hpdcache.lock.hjson | 14 + hw/vendor/hpdcache/.gitignore | 7 + hw/vendor/hpdcache/.readthedocs.yaml | 24 + hw/vendor/hpdcache/Bender.yml | 51 + hw/vendor/hpdcache/CHANGELOG.md | 176 + hw/vendor/hpdcache/CODEOWNERS | 2 + hw/vendor/hpdcache/LICENSE | 98 + hw/vendor/hpdcache/README.md | 95 + hw/vendor/hpdcache/docs/.gitignore | 1 + hw/vendor/hpdcache/docs/LICENSE | 97 + hw/vendor/hpdcache/docs/Makefile | 20 + hw/vendor/hpdcache/docs/README.md | 55 + hw/vendor/hpdcache/docs/requirements.txt | 8 + .../docs/source/_static/theme_overrides.css | 9 + hw/vendor/hpdcache/docs/source/amo.rst | 202 + .../hpdcache/docs/source/architecture.rst | 1392 +++ hw/vendor/hpdcache/docs/source/cmo.rst | 343 + hw/vendor/hpdcache/docs/source/conf.py | 71 + hw/vendor/hpdcache/docs/source/csrs.rst | 349 + .../docs/source/images/hpdcache_core.pdf | Bin 0 -> 49948 bytes .../docs/source/images/hpdcache_core.svg | 3446 ++++++ .../source/images/hpdcache_csr_addr_space.pdf | Bin 0 -> 8270 bytes .../source/images/hpdcache_csr_addr_space.svg | 300 + .../images/hpdcache_data_ram_organization.pdf | Bin 0 -> 24114 bytes .../images/hpdcache_data_ram_organization.svg | 2344 ++++ .../images/hpdcache_highlevel_integration.pdf | Bin 0 -> 28916 bytes .../images/hpdcache_highlevel_integration.svg | 501 + ...pdcache_request_address_data_alignment.pdf | Bin 0 -> 15429 bytes ...pdcache_request_address_data_alignment.svg | 2016 ++++ .../images/hpdcache_request_arbiter.pdf | Bin 0 -> 16576 bytes .../images/hpdcache_request_arbiter.svg | 428 + .../docs/source/images/hpdcache_vipt.pdf | Bin 0 -> 10182 bytes .../docs/source/images/hpdcache_vipt.svg | 911 ++ .../docs/source/images/wave_back_to_back.json | 10 + .../docs/source/images/wave_back_to_back.pdf | Bin 0 -> 6118 bytes .../docs/source/images/wave_back_to_back.svg | 4 + .../images/wave_ready_before_valid.json | 10 + .../source/images/wave_ready_before_valid.pdf | Bin 0 -> 5869 bytes .../source/images/wave_ready_before_valid.svg | 4 + .../source/images/wave_ready_when_valid.json | 10 + .../source/images/wave_ready_when_valid.pdf | Bin 0 -> 5866 bytes .../source/images/wave_ready_when_valid.svg | 4 + .../images/wave_valid_before_ready.json | 10 + .../source/images/wave_valid_before_ready.pdf | Bin 0 -> 5798 bytes .../source/images/wave_valid_before_ready.svg | 4 + hw/vendor/hpdcache/docs/source/index.rst | 37 + hw/vendor/hpdcache/docs/source/interface.rst | 1318 +++ hw/vendor/hpdcache/docs/source/overview.rst | 88 + hw/vendor/hpdcache/docs/source/references.rst | 37 + hw/vendor/hpdcache/rtl/fv/lnt/README.md | 61 + hw/vendor/hpdcache/rtl/fv/lnt/cachedata.lnt | 138 + hw/vendor/hpdcache/rtl/fv/lnt/channels.lnt | 81 + hw/vendor/hpdcache/rtl/fv/lnt/hpdcache.lnt | 248 + hw/vendor/hpdcache/rtl/fv/lnt/main.lnt | 135 + hw/vendor/hpdcache/rtl/fv/lnt/misshandler.lnt | 157 + hw/vendor/hpdcache/rtl/fv/lnt/replaytable.lnt | 348 + hw/vendor/hpdcache/rtl/fv/lnt/types.lnt | 234 + hw/vendor/hpdcache/rtl/fv/lnt/writebuffer.lnt | 170 + hw/vendor/hpdcache/rtl/hpdcache.Flist | 71 + .../hpdcache/rtl/include/hpdcache_typedef.svh | 156 + hw/vendor/hpdcache/rtl/lint/.gitignore | 1 + hw/vendor/hpdcache/rtl/lint/Makefile | 33 + hw/vendor/hpdcache/rtl/lint/hpdcache_lint.sv | 231 + hw/vendor/hpdcache/rtl/lint/verible.waiver | 1 + hw/vendor/hpdcache/rtl/lint/verible_rules.cfg | 16 + .../rtl/src/common/hpdcache_1hot_to_binary.sv | 60 + .../rtl/src/common/hpdcache_data_downsize.sv | 191 + .../rtl/src/common/hpdcache_data_resize.sv | 129 + .../rtl/src/common/hpdcache_data_upsize.sv | 181 + .../rtl/src/common/hpdcache_decoder.sv | 53 + .../hpdcache/rtl/src/common/hpdcache_demux.sv | 72 + .../rtl/src/common/hpdcache_fifo_reg.sv | 176 + .../common/hpdcache_fifo_reg_initialized.sv | 147 + .../hpdcache/rtl/src/common/hpdcache_fxarb.sv | 87 + .../hpdcache/rtl/src/common/hpdcache_lfsr.sv | 95 + .../hpdcache/rtl/src/common/hpdcache_mux.sv | 84 + .../src/common/hpdcache_prio_1hot_encoder.sv | 42 + .../src/common/hpdcache_prio_bin_encoder.sv | 36 + .../hpdcache_regbank_wbyteenable_1rw.sv | 63 + .../src/common/hpdcache_regbank_wmask_1rw.sv | 61 + .../hpdcache/rtl/src/common/hpdcache_rrarb.sv | 121 + .../hpdcache/rtl/src/common/hpdcache_sram.sv | 56 + .../src/common/hpdcache_sram_watomenable.sv | 39 + .../src/common/hpdcache_sram_wbyteenable.sv | 58 + .../rtl/src/common/hpdcache_sram_wmask.sv | 58 + .../rtl/src/common/hpdcache_sync_buffer.sv | 89 + .../common/macros/behav/hpdcache_sram_1rw.sv | 60 + .../behav/hpdcache_sram_watomenable_1rw.sv | 49 + .../behav/hpdcache_sram_wbyteenable_1rw.sv | 68 + .../macros/behav/hpdcache_sram_wmask_1rw.sv | 61 + .../macros/blackbox/hpdcache_sram_1rw.sv | 42 + .../blackbox/hpdcache_sram_wbyteenable_1rw.sv | 43 + .../blackbox/hpdcache_sram_wmask_1rw.sv | 43 + hw/vendor/hpdcache/rtl/src/hpdcache.sv | 1374 +++ hw/vendor/hpdcache/rtl/src/hpdcache.vlt | 29 + hw/vendor/hpdcache/rtl/src/hpdcache_amo.sv | 114 + hw/vendor/hpdcache/rtl/src/hpdcache_cbuf.sv | 99 + hw/vendor/hpdcache/rtl/src/hpdcache_cmo.sv | 671 ++ .../hpdcache/rtl/src/hpdcache_core_arbiter.sv | 175 + hw/vendor/hpdcache/rtl/src/hpdcache_ctrl.sv | 1373 +++ .../hpdcache/rtl/src/hpdcache_ctrl_pe.sv | 1097 ++ hw/vendor/hpdcache/rtl/src/hpdcache_flush.sv | 417 + .../hpdcache/rtl/src/hpdcache_memctrl.sv | 1161 ++ .../hpdcache/rtl/src/hpdcache_miss_handler.sv | 953 ++ hw/vendor/hpdcache/rtl/src/hpdcache_mshr.sv | 423 + hw/vendor/hpdcache/rtl/src/hpdcache_pkg.sv | 540 + hw/vendor/hpdcache/rtl/src/hpdcache_rtab.sv | 690 ++ .../hpdcache/rtl/src/hpdcache_uncached.sv | 1206 ++ .../hpdcache/rtl/src/hpdcache_victim_plru.sv | 146 + .../rtl/src/hpdcache_victim_random.sv | 126 + .../hpdcache/rtl/src/hpdcache_victim_sel.sv | 122 + hw/vendor/hpdcache/rtl/src/hpdcache_wbuf.sv | 763 ++ .../rtl/src/hwpf_stride/hwpf_stride.sv | 379 + .../rtl/src/hwpf_stride/hwpf_stride_arb.sv | 121 + .../rtl/src/hwpf_stride/hwpf_stride_pkg.sv | 68 + .../src/hwpf_stride/hwpf_stride_wrapper.sv | 289 + .../cva6/cva6_hpdcache_cmo_if_adapter.sv | 186 + .../cva6/cva6_op_hpdcache_params_pkg.sv | 261 + .../rtl/src/utils/hpdcache_l15_req_arbiter.sv | 227 + .../rtl/src/utils/hpdcache_l15_resp_demux.sv | 106 + .../utils/hpdcache_mem_req_read_arbiter.sv | 89 + .../utils/hpdcache_mem_req_write_arbiter.sv | 189 + .../rtl/src/utils/hpdcache_mem_resp_demux.sv | 108 + .../rtl/src/utils/hpdcache_mem_to_axi_read.sv | 97 + .../src/utils/hpdcache_mem_to_axi_write.sv | 149 + .../hpdcache/rtl/src/utils/hpdcache_to_l15.sv | 457 + hw/vendor/hpdcache/rtl/tb/.gitignore | 2 + hw/vendor/hpdcache/rtl/tb/Makefile | 356 + hw/vendor/hpdcache/rtl/tb/README.md | 103 + .../hpdcache/rtl/tb/configs/default_config.mk | 64 + .../rtl/tb/configs/embedded_config.mk | 64 + .../hpdcache/rtl/tb/configs/hpc_config.mk | 64 + hw/vendor/hpdcache/rtl/tb/hpdcache.vlt.Flist | 34 + hw/vendor/hpdcache/rtl/tb/hpdcache_tb.cpp | 520 + .../hpdcache/rtl/tb/hpdcache_test_agent.h | 103 + hw/vendor/hpdcache/rtl/tb/hpdcache_test_amo.h | 90 + .../hpdcache/rtl/tb/hpdcache_test_defs.h | 192 + .../hpdcache/rtl/tb/hpdcache_test_driver.h | 213 + .../rtl/tb/hpdcache_test_mem_resp_model.h | 471 + .../tb/hpdcache_test_mem_resp_model_base.h | 298 + .../rtl/tb/hpdcache_test_scoreboard.h | 1099 ++ .../hpdcache/rtl/tb/hpdcache_test_sequence.h | 268 + .../rtl/tb/hpdcache_test_transaction.h | 581 + hw/vendor/hpdcache/rtl/tb/hpdcache_wrapper.sv | 383 + hw/vendor/hpdcache/rtl/tb/rtl_conf.mk | 142 + .../rtl/tb/sc_verif_lib/include/agent.h | 78 + .../rtl/tb/sc_verif_lib/include/driver.h | 55 + .../rtl/tb/sc_verif_lib/include/logger.h | 95 + .../rtl/tb/sc_verif_lib/include/sequence.h | 82 + .../rtl/tb/sc_verif_lib/include/transaction.h | 51 + .../sc_verif_lib/include/transaction_pool.h | 108 + .../include/generic_cache_data.h | 123 + .../include/generic_cache_directory_base.h | 239 + .../include/generic_cache_directory_plru.h | 150 + .../modules/mem_model/include/mem_model.h | 140 + .../modules/mem_model/include/ram_model.h | 138 + .../rtl/tb/scripts/perl5/Text/Aligner.pm | 486 + .../hpdcache/rtl/tb/scripts/perl5/Text/CSV.pm | 2705 +++++ .../rtl/tb/scripts/perl5/Text/CSV_PP.pm | 5354 +++++++++ .../rtl/tb/scripts/perl5/Text/Table.pm | 1629 +++ .../rtl/tb/scripts/random_numbers.dat | 9947 +++++++++++++++++ .../hpdcache/rtl/tb/scripts/scan_logs.pl | 1363 +++ .../scripts/scan_patterns/build_patterns.pat | 7 + .../scripts/scan_patterns/run_attributes.pat | 6 + .../tb/scripts/scan_patterns/run_patterns.pat | 13 + .../scan_patterns/verilate_patterns.pat | 7 + hw/vendor/hpdcache/rtl/tb/scripts/vcd2fst.sh | 49 + .../rtl/tb/scripts/verilate_waivers.vlt | 12 + .../sequence_lib/hpdcache_test_random_seq.h | 336 + .../tb/sequence_lib/hpdcache_test_read_seq.h | 169 + .../hpdcache_test_unique_set_seq.h | 315 + .../tb/sequence_lib/hpdcache_test_write_seq.h | 171 + 172 files changed, 61921 insertions(+) create mode 100644 hw/vendor/hpdcache.lock.hjson create mode 100644 hw/vendor/hpdcache/.gitignore create mode 100644 hw/vendor/hpdcache/.readthedocs.yaml create mode 100644 hw/vendor/hpdcache/Bender.yml create mode 100644 hw/vendor/hpdcache/CHANGELOG.md create mode 100644 hw/vendor/hpdcache/CODEOWNERS create mode 100644 hw/vendor/hpdcache/LICENSE create mode 100644 hw/vendor/hpdcache/README.md create mode 100644 hw/vendor/hpdcache/docs/.gitignore create mode 100644 hw/vendor/hpdcache/docs/LICENSE create mode 100644 hw/vendor/hpdcache/docs/Makefile create mode 100644 hw/vendor/hpdcache/docs/README.md create mode 100644 hw/vendor/hpdcache/docs/requirements.txt create mode 100644 hw/vendor/hpdcache/docs/source/_static/theme_overrides.css create mode 100644 hw/vendor/hpdcache/docs/source/amo.rst create mode 100644 hw/vendor/hpdcache/docs/source/architecture.rst create mode 100644 hw/vendor/hpdcache/docs/source/cmo.rst create mode 100644 hw/vendor/hpdcache/docs/source/conf.py create mode 100644 hw/vendor/hpdcache/docs/source/csrs.rst create mode 100644 hw/vendor/hpdcache/docs/source/images/hpdcache_core.pdf create mode 100644 hw/vendor/hpdcache/docs/source/images/hpdcache_core.svg create mode 100644 hw/vendor/hpdcache/docs/source/images/hpdcache_csr_addr_space.pdf create mode 100644 hw/vendor/hpdcache/docs/source/images/hpdcache_csr_addr_space.svg create mode 100644 hw/vendor/hpdcache/docs/source/images/hpdcache_data_ram_organization.pdf create mode 100755 hw/vendor/hpdcache/docs/source/images/hpdcache_data_ram_organization.svg create mode 100644 hw/vendor/hpdcache/docs/source/images/hpdcache_highlevel_integration.pdf create mode 100644 hw/vendor/hpdcache/docs/source/images/hpdcache_highlevel_integration.svg create mode 100644 hw/vendor/hpdcache/docs/source/images/hpdcache_request_address_data_alignment.pdf create mode 100755 hw/vendor/hpdcache/docs/source/images/hpdcache_request_address_data_alignment.svg create mode 100644 hw/vendor/hpdcache/docs/source/images/hpdcache_request_arbiter.pdf create mode 100644 hw/vendor/hpdcache/docs/source/images/hpdcache_request_arbiter.svg create mode 100644 hw/vendor/hpdcache/docs/source/images/hpdcache_vipt.pdf create mode 100755 hw/vendor/hpdcache/docs/source/images/hpdcache_vipt.svg create mode 100755 hw/vendor/hpdcache/docs/source/images/wave_back_to_back.json create mode 100644 hw/vendor/hpdcache/docs/source/images/wave_back_to_back.pdf create mode 100755 hw/vendor/hpdcache/docs/source/images/wave_back_to_back.svg create mode 100755 hw/vendor/hpdcache/docs/source/images/wave_ready_before_valid.json create mode 100644 hw/vendor/hpdcache/docs/source/images/wave_ready_before_valid.pdf create mode 100755 hw/vendor/hpdcache/docs/source/images/wave_ready_before_valid.svg create mode 100755 hw/vendor/hpdcache/docs/source/images/wave_ready_when_valid.json create mode 100644 hw/vendor/hpdcache/docs/source/images/wave_ready_when_valid.pdf create mode 100755 hw/vendor/hpdcache/docs/source/images/wave_ready_when_valid.svg create mode 100755 hw/vendor/hpdcache/docs/source/images/wave_valid_before_ready.json create mode 100644 hw/vendor/hpdcache/docs/source/images/wave_valid_before_ready.pdf create mode 100755 hw/vendor/hpdcache/docs/source/images/wave_valid_before_ready.svg create mode 100644 hw/vendor/hpdcache/docs/source/index.rst create mode 100644 hw/vendor/hpdcache/docs/source/interface.rst create mode 100644 hw/vendor/hpdcache/docs/source/overview.rst create mode 100644 hw/vendor/hpdcache/docs/source/references.rst create mode 100644 hw/vendor/hpdcache/rtl/fv/lnt/README.md create mode 100644 hw/vendor/hpdcache/rtl/fv/lnt/cachedata.lnt create mode 100644 hw/vendor/hpdcache/rtl/fv/lnt/channels.lnt create mode 100644 hw/vendor/hpdcache/rtl/fv/lnt/hpdcache.lnt create mode 100644 hw/vendor/hpdcache/rtl/fv/lnt/main.lnt create mode 100644 hw/vendor/hpdcache/rtl/fv/lnt/misshandler.lnt create mode 100644 hw/vendor/hpdcache/rtl/fv/lnt/replaytable.lnt create mode 100644 hw/vendor/hpdcache/rtl/fv/lnt/types.lnt create mode 100644 hw/vendor/hpdcache/rtl/fv/lnt/writebuffer.lnt create mode 100644 hw/vendor/hpdcache/rtl/hpdcache.Flist create mode 100644 hw/vendor/hpdcache/rtl/include/hpdcache_typedef.svh create mode 100644 hw/vendor/hpdcache/rtl/lint/.gitignore create mode 100644 hw/vendor/hpdcache/rtl/lint/Makefile create mode 100644 hw/vendor/hpdcache/rtl/lint/hpdcache_lint.sv create mode 100644 hw/vendor/hpdcache/rtl/lint/verible.waiver create mode 100644 hw/vendor/hpdcache/rtl/lint/verible_rules.cfg create mode 100644 hw/vendor/hpdcache/rtl/src/common/hpdcache_1hot_to_binary.sv create mode 100644 hw/vendor/hpdcache/rtl/src/common/hpdcache_data_downsize.sv create mode 100644 hw/vendor/hpdcache/rtl/src/common/hpdcache_data_resize.sv create mode 100644 hw/vendor/hpdcache/rtl/src/common/hpdcache_data_upsize.sv create mode 100644 hw/vendor/hpdcache/rtl/src/common/hpdcache_decoder.sv create mode 100644 hw/vendor/hpdcache/rtl/src/common/hpdcache_demux.sv create mode 100644 hw/vendor/hpdcache/rtl/src/common/hpdcache_fifo_reg.sv create mode 100644 hw/vendor/hpdcache/rtl/src/common/hpdcache_fifo_reg_initialized.sv create mode 100644 hw/vendor/hpdcache/rtl/src/common/hpdcache_fxarb.sv create mode 100644 hw/vendor/hpdcache/rtl/src/common/hpdcache_lfsr.sv create mode 100644 hw/vendor/hpdcache/rtl/src/common/hpdcache_mux.sv create mode 100644 hw/vendor/hpdcache/rtl/src/common/hpdcache_prio_1hot_encoder.sv create mode 100644 hw/vendor/hpdcache/rtl/src/common/hpdcache_prio_bin_encoder.sv create mode 100644 hw/vendor/hpdcache/rtl/src/common/hpdcache_regbank_wbyteenable_1rw.sv create mode 100644 hw/vendor/hpdcache/rtl/src/common/hpdcache_regbank_wmask_1rw.sv create mode 100644 hw/vendor/hpdcache/rtl/src/common/hpdcache_rrarb.sv create mode 100644 hw/vendor/hpdcache/rtl/src/common/hpdcache_sram.sv create mode 100644 hw/vendor/hpdcache/rtl/src/common/hpdcache_sram_watomenable.sv create mode 100644 hw/vendor/hpdcache/rtl/src/common/hpdcache_sram_wbyteenable.sv create mode 100644 hw/vendor/hpdcache/rtl/src/common/hpdcache_sram_wmask.sv create mode 100644 hw/vendor/hpdcache/rtl/src/common/hpdcache_sync_buffer.sv create mode 100644 hw/vendor/hpdcache/rtl/src/common/macros/behav/hpdcache_sram_1rw.sv create mode 100644 hw/vendor/hpdcache/rtl/src/common/macros/behav/hpdcache_sram_watomenable_1rw.sv create mode 100644 hw/vendor/hpdcache/rtl/src/common/macros/behav/hpdcache_sram_wbyteenable_1rw.sv create mode 100644 hw/vendor/hpdcache/rtl/src/common/macros/behav/hpdcache_sram_wmask_1rw.sv create mode 100644 hw/vendor/hpdcache/rtl/src/common/macros/blackbox/hpdcache_sram_1rw.sv create mode 100644 hw/vendor/hpdcache/rtl/src/common/macros/blackbox/hpdcache_sram_wbyteenable_1rw.sv create mode 100644 hw/vendor/hpdcache/rtl/src/common/macros/blackbox/hpdcache_sram_wmask_1rw.sv create mode 100644 hw/vendor/hpdcache/rtl/src/hpdcache.sv create mode 100644 hw/vendor/hpdcache/rtl/src/hpdcache.vlt create mode 100644 hw/vendor/hpdcache/rtl/src/hpdcache_amo.sv create mode 100644 hw/vendor/hpdcache/rtl/src/hpdcache_cbuf.sv create mode 100644 hw/vendor/hpdcache/rtl/src/hpdcache_cmo.sv create mode 100644 hw/vendor/hpdcache/rtl/src/hpdcache_core_arbiter.sv create mode 100644 hw/vendor/hpdcache/rtl/src/hpdcache_ctrl.sv create mode 100644 hw/vendor/hpdcache/rtl/src/hpdcache_ctrl_pe.sv create mode 100644 hw/vendor/hpdcache/rtl/src/hpdcache_flush.sv create mode 100644 hw/vendor/hpdcache/rtl/src/hpdcache_memctrl.sv create mode 100644 hw/vendor/hpdcache/rtl/src/hpdcache_miss_handler.sv create mode 100644 hw/vendor/hpdcache/rtl/src/hpdcache_mshr.sv create mode 100644 hw/vendor/hpdcache/rtl/src/hpdcache_pkg.sv create mode 100644 hw/vendor/hpdcache/rtl/src/hpdcache_rtab.sv create mode 100644 hw/vendor/hpdcache/rtl/src/hpdcache_uncached.sv create mode 100644 hw/vendor/hpdcache/rtl/src/hpdcache_victim_plru.sv create mode 100644 hw/vendor/hpdcache/rtl/src/hpdcache_victim_random.sv create mode 100644 hw/vendor/hpdcache/rtl/src/hpdcache_victim_sel.sv create mode 100644 hw/vendor/hpdcache/rtl/src/hpdcache_wbuf.sv create mode 100644 hw/vendor/hpdcache/rtl/src/hwpf_stride/hwpf_stride.sv create mode 100644 hw/vendor/hpdcache/rtl/src/hwpf_stride/hwpf_stride_arb.sv create mode 100644 hw/vendor/hpdcache/rtl/src/hwpf_stride/hwpf_stride_pkg.sv create mode 100644 hw/vendor/hpdcache/rtl/src/hwpf_stride/hwpf_stride_wrapper.sv create mode 100644 hw/vendor/hpdcache/rtl/src/target/cva6/cva6_hpdcache_cmo_if_adapter.sv create mode 100644 hw/vendor/hpdcache/rtl/src/target/cva6/cva6_op_hpdcache_params_pkg.sv create mode 100644 hw/vendor/hpdcache/rtl/src/utils/hpdcache_l15_req_arbiter.sv create mode 100644 hw/vendor/hpdcache/rtl/src/utils/hpdcache_l15_resp_demux.sv create mode 100644 hw/vendor/hpdcache/rtl/src/utils/hpdcache_mem_req_read_arbiter.sv create mode 100644 hw/vendor/hpdcache/rtl/src/utils/hpdcache_mem_req_write_arbiter.sv create mode 100644 hw/vendor/hpdcache/rtl/src/utils/hpdcache_mem_resp_demux.sv create mode 100644 hw/vendor/hpdcache/rtl/src/utils/hpdcache_mem_to_axi_read.sv create mode 100644 hw/vendor/hpdcache/rtl/src/utils/hpdcache_mem_to_axi_write.sv create mode 100644 hw/vendor/hpdcache/rtl/src/utils/hpdcache_to_l15.sv create mode 100644 hw/vendor/hpdcache/rtl/tb/.gitignore create mode 100644 hw/vendor/hpdcache/rtl/tb/Makefile create mode 100644 hw/vendor/hpdcache/rtl/tb/README.md create mode 100644 hw/vendor/hpdcache/rtl/tb/configs/default_config.mk create mode 100644 hw/vendor/hpdcache/rtl/tb/configs/embedded_config.mk create mode 100644 hw/vendor/hpdcache/rtl/tb/configs/hpc_config.mk create mode 100644 hw/vendor/hpdcache/rtl/tb/hpdcache.vlt.Flist create mode 100644 hw/vendor/hpdcache/rtl/tb/hpdcache_tb.cpp create mode 100644 hw/vendor/hpdcache/rtl/tb/hpdcache_test_agent.h create mode 100644 hw/vendor/hpdcache/rtl/tb/hpdcache_test_amo.h create mode 100644 hw/vendor/hpdcache/rtl/tb/hpdcache_test_defs.h create mode 100644 hw/vendor/hpdcache/rtl/tb/hpdcache_test_driver.h create mode 100644 hw/vendor/hpdcache/rtl/tb/hpdcache_test_mem_resp_model.h create mode 100644 hw/vendor/hpdcache/rtl/tb/hpdcache_test_mem_resp_model_base.h create mode 100644 hw/vendor/hpdcache/rtl/tb/hpdcache_test_scoreboard.h create mode 100644 hw/vendor/hpdcache/rtl/tb/hpdcache_test_sequence.h create mode 100644 hw/vendor/hpdcache/rtl/tb/hpdcache_test_transaction.h create mode 100644 hw/vendor/hpdcache/rtl/tb/hpdcache_wrapper.sv create mode 100644 hw/vendor/hpdcache/rtl/tb/rtl_conf.mk create mode 100644 hw/vendor/hpdcache/rtl/tb/sc_verif_lib/include/agent.h create mode 100644 hw/vendor/hpdcache/rtl/tb/sc_verif_lib/include/driver.h create mode 100644 hw/vendor/hpdcache/rtl/tb/sc_verif_lib/include/logger.h create mode 100644 hw/vendor/hpdcache/rtl/tb/sc_verif_lib/include/sequence.h create mode 100644 hw/vendor/hpdcache/rtl/tb/sc_verif_lib/include/transaction.h create mode 100644 hw/vendor/hpdcache/rtl/tb/sc_verif_lib/include/transaction_pool.h create mode 100644 hw/vendor/hpdcache/rtl/tb/sc_verif_lib/modules/generic_cache/include/generic_cache_data.h create mode 100644 hw/vendor/hpdcache/rtl/tb/sc_verif_lib/modules/generic_cache/include/generic_cache_directory_base.h create mode 100644 hw/vendor/hpdcache/rtl/tb/sc_verif_lib/modules/generic_cache/include/generic_cache_directory_plru.h create mode 100644 hw/vendor/hpdcache/rtl/tb/sc_verif_lib/modules/mem_model/include/mem_model.h create mode 100644 hw/vendor/hpdcache/rtl/tb/sc_verif_lib/modules/mem_model/include/ram_model.h create mode 100644 hw/vendor/hpdcache/rtl/tb/scripts/perl5/Text/Aligner.pm create mode 100644 hw/vendor/hpdcache/rtl/tb/scripts/perl5/Text/CSV.pm create mode 100644 hw/vendor/hpdcache/rtl/tb/scripts/perl5/Text/CSV_PP.pm create mode 100644 hw/vendor/hpdcache/rtl/tb/scripts/perl5/Text/Table.pm create mode 100644 hw/vendor/hpdcache/rtl/tb/scripts/random_numbers.dat create mode 100755 hw/vendor/hpdcache/rtl/tb/scripts/scan_logs.pl create mode 100644 hw/vendor/hpdcache/rtl/tb/scripts/scan_patterns/build_patterns.pat create mode 100644 hw/vendor/hpdcache/rtl/tb/scripts/scan_patterns/run_attributes.pat create mode 100644 hw/vendor/hpdcache/rtl/tb/scripts/scan_patterns/run_patterns.pat create mode 100644 hw/vendor/hpdcache/rtl/tb/scripts/scan_patterns/verilate_patterns.pat create mode 100755 hw/vendor/hpdcache/rtl/tb/scripts/vcd2fst.sh create mode 100644 hw/vendor/hpdcache/rtl/tb/scripts/verilate_waivers.vlt create mode 100644 hw/vendor/hpdcache/rtl/tb/sequence_lib/hpdcache_test_random_seq.h create mode 100644 hw/vendor/hpdcache/rtl/tb/sequence_lib/hpdcache_test_read_seq.h create mode 100644 hw/vendor/hpdcache/rtl/tb/sequence_lib/hpdcache_test_unique_set_seq.h create mode 100644 hw/vendor/hpdcache/rtl/tb/sequence_lib/hpdcache_test_write_seq.h diff --git a/hw/vendor/hpdcache.lock.hjson b/hw/vendor/hpdcache.lock.hjson new file mode 100644 index 00000000..7532fd4a --- /dev/null +++ b/hw/vendor/hpdcache.lock.hjson @@ -0,0 +1,14 @@ +// Copyright lowRISC contributors (OpenTitan project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +// This file is generated by the util/vendor script. Please do not modify it +// manually. + +{ + upstream: + { + url: https://github.com/Capabilities-Limited/cv-hpdcache.git + rev: 07f4ebe7bd074e2f82f6b10c2386026bc12455d8 + } +} diff --git a/hw/vendor/hpdcache/.gitignore b/hw/vendor/hpdcache/.gitignore new file mode 100644 index 00000000..6ceb24a0 --- /dev/null +++ b/hw/vendor/hpdcache/.gitignore @@ -0,0 +1,7 @@ +vnc_logs/ +veloce.map +VRMDATA/ +*.log +*.wlf +*.ucdb +*.swp diff --git a/hw/vendor/hpdcache/.readthedocs.yaml b/hw/vendor/hpdcache/.readthedocs.yaml new file mode 100644 index 00000000..a752930a --- /dev/null +++ b/hw/vendor/hpdcache/.readthedocs.yaml @@ -0,0 +1,24 @@ +# Read the Docs configuration file +# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details +# Copyright 2025 OpenHW Foundation +# SPDX-License-Identifier:Apache-2.0 WITH SHL-2.1 + +# Required +version: 2 + +# Set the OS, Python version, and other tools you might need +build: + os: ubuntu-24.04 + tools: + python: "3.13" + +# Build documentation in the "docs/" directory with Sphinx +sphinx: + configuration: docs/source/conf.py + +# Optionally, but recommended, +# declare the Python requirements required to build your documentation +# See https://docs.readthedocs.io/en/stable/guides/reproducible-builds.html +python: + install: + - requirements: docs/requirements.txt diff --git a/hw/vendor/hpdcache/Bender.yml b/hw/vendor/hpdcache/Bender.yml new file mode 100644 index 00000000..4924893d --- /dev/null +++ b/hw/vendor/hpdcache/Bender.yml @@ -0,0 +1,51 @@ +package: + name: hpdcache + authors: + - "César Fuguet " + +export_include_dirs: + - rtl/include + +sources: + - rtl/src/hpdcache_pkg.sv + - rtl/src/utils/hpdcache_mem_req_read_arbiter.sv + - rtl/src/utils/hpdcache_mem_req_write_arbiter.sv + - rtl/src/common/hpdcache_demux.sv + - rtl/src/common/hpdcache_lfsr.sv + - rtl/src/common/hpdcache_sync_buffer.sv + - rtl/src/common/hpdcache_fifo_reg.sv + - rtl/src/common/hpdcache_fifo_reg_initialized.sv + - rtl/src/common/hpdcache_fxarb.sv + - rtl/src/common/hpdcache_rrarb.sv + - rtl/src/common/hpdcache_mux.sv + - rtl/src/common/hpdcache_decoder.sv + - rtl/src/common/hpdcache_1hot_to_binary.sv + - rtl/src/common/hpdcache_prio_1hot_encoder.sv + - rtl/src/common/hpdcache_prio_bin_encoder.sv + - rtl/src/common/hpdcache_sram.sv + - rtl/src/common/hpdcache_sram_wbyteenable.sv + - rtl/src/common/hpdcache_sram_wmask.sv + - rtl/src/common/hpdcache_regbank_wbyteenable_1rw.sv + - rtl/src/common/hpdcache_regbank_wmask_1rw.sv + - rtl/src/common/hpdcache_data_downsize.sv + - rtl/src/common/hpdcache_data_upsize.sv + - rtl/src/hwpf_stride/hwpf_stride_pkg.sv + - rtl/src/hwpf_stride/hwpf_stride.sv + - rtl/src/hwpf_stride/hwpf_stride_arb.sv + - rtl/src/hwpf_stride/hwpf_stride_wrapper.sv + - rtl/src/hpdcache.sv + - rtl/src/hpdcache_amo.sv + - rtl/src/hpdcache_cmo.sv + - rtl/src/hpdcache_core_arbiter.sv + - rtl/src/hpdcache_ctrl.sv + - rtl/src/hpdcache_ctrl_pe.sv + - rtl/src/hpdcache_memctrl.sv + - rtl/src/hpdcache_cbuf.sv + - rtl/src/hpdcache_miss_handler.sv + - rtl/src/hpdcache_mshr.sv + - rtl/src/hpdcache_rtab.sv + - rtl/src/hpdcache_uncached.sv + - rtl/src/hpdcache_victim_plru.sv + - rtl/src/hpdcache_victim_random.sv + - rtl/src/hpdcache_victim_sel.sv + - rtl/src/hpdcache_wbuf.sv diff --git a/hw/vendor/hpdcache/CHANGELOG.md b/hw/vendor/hpdcache/CHANGELOG.md new file mode 100644 index 00000000..3ee489dd --- /dev/null +++ b/hw/vendor/hpdcache/CHANGELOG.md @@ -0,0 +1,176 @@ +# Changelog +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) +and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). + +## [Unreleased] + +### Added + +### Removed + +### Changed + +### Fixed + +## [5.0.1] 2025-01-30 + +This releases include some bugfixes and optimizations. + +### Added + +- Support responses for CMO operations when ``need_rsp`` in the request is set +- Support not-power-of-two number of entries in the Flush controller + +### Removed + +### Changed + +### Fixed + +- Fix implementation of the data merge logic in the write buffer to improve the area +- Fix assertions syntax +- Fix CMO flushes shall unset the dirty bit in the cache directory +- Fix handling of bus errors on write misses +- Fix prefetch requests shall update PLRU bits +- Fix initialization of the CMO handler flush request valid register + +## [5.0.0] 2024-10-10 + +The major modification in this release is the support of the write-back (WB) policy (in +addition to the write-through (WT) policy). The cache can either implement one of these +policies or both in a per-cacheline basis. + +### Added + +- Support of the WB policy. +- Configuration parameters to choose between WT or WB, or both, at synthesis time. +- Validation testbench compatible to Verilator. +- Add a write-policy hint field in the request to select between WT and WB, dinamically. + +### Removed + +- WbufSendFeedThrough parameter removed + +### Changed + +- Arbitration between cacheable and uncacheable memory requests is done inside the + HPDcache. The memory interface implements 5 channels, instead of 10. +- The CMO type is into the operation field of the request (instead of the size field). +- Select the victim cacheline at cache miss time (before was done on refill time). The + slot is pre-allocated and written by the miss handler when the refill response arrives. + +### Fixed + +## [4.0.0] 2024-08-20 + +The major modification in this release is the new scheme to set parameters of the HPDcache + +### Added + +- Add utility macros to define the types in the interface of the HPDcache. +- Add scripts for code static check (lint) +- Add new User Guide document in reStructuredText format + +### Removed + +### Changed + +- Pass configuration parameters through the top module of the HPDcache instead of + defining them in a package. This allows different instances of the HPDcache to have + different parameters. +- Add round-robin priority arbiter in the write-buffer to select an entry to send. +- Add fix-priority arbiter in the write-buffer to select a free entry. +- Make AXI transactions modifiable by default + +### Fixed + +- Fix some synthesis warnings + +## [3.1.0] 2024-04-20 + +### Added + +- Add support for pseudo-random (using a LFSR) algorithm for the victim selection +- Add support for the OpenPiton Cache-Coherent Network-on-Chip +- Add support for full-associative, direct-mapped or single-entry MSHR + +### Removed + +### Changed + +- Cache directory valid bits are relocated in the Tag RAM +- Modify the arbitration between the different sources of requests (refill is now prioritary) +- Add feedthrough (optional) fifo in both wbuf and refill handler +- Disable assertions during reset +- Modify write buffer microarchitecture to improve both area and performance + +### Fixed + +- Fix support for AMOs when using a 32-bit request interface + +## [3.0.0] 2023-10-08 + +### Added + +- Add support for virtually-indexed addressing + +### Fixed + +- Fix forwarding logic of uncacheable Icache response in the cva6 cache subsystem. +- Fix wrong mask signal when implementing the MSHR in registers + +## [2.1.0] - 2023-09-25 + +### Added + +- Add additional configuration to implement MSHR in registers (when the number + of entries is low) + +### Fixed + +- Fix cache data SRAM chip-select generation when word width is different than + 64 bits (e.g. 32 bits) + +## [2.0.0] - 2023-09-18 + +### Added + +- Add parameters in the HPDcache module to define the types of interfaces to + the memory +- Add helper verilog header file with macros to ease the type definition of + interfaces to the memory +- Add new event signals in the HPDCache top module +- Add generic single-port RAM macros with byte-enable signals +- Add parameters in the package to choose between RAM macros implementing + byte-enable or bitmask for the different RAMs instances +- Add additional assertions to verify parameters +- Add additional configuration signal to inhibit write coalescing in the write + buffer + +### Removed + +- Remove base_id ports in the HPDCache top module +- Remove nettype (wire,var) in ports as it looks like is badly supported in + some cases by some simulation tools + +### Changed + +- Split the hpdcache_pkg into: (1) the hpdcache_pkg contains internally defined + parameters; (2) a new hpdcache_params_pkg that defines user parameters +- New selection policy of ready requests in the replay table. It gives priority + to requests in the same linked list. +- The write buffer now accepts writes from requesters in a pending slot when it + is waiting for the internal arbiter to forward the data to the NoC. + +### Fixed + +- Correctly support HPDCACHE_ACCESS_WORDS=1 +- Correctly support HPDCACHE_ACCESS_WORDS=HPDCACHE_CL_WORDS +- Fix width of the nlines count register in the HW memory prefetcher. + +## [1.0.0] - 2023-02-22 + +### Added +- Initial release to the OpenHW Group diff --git a/hw/vendor/hpdcache/CODEOWNERS b/hw/vendor/hpdcache/CODEOWNERS new file mode 100644 index 00000000..ce692f92 --- /dev/null +++ b/hw/vendor/hpdcache/CODEOWNERS @@ -0,0 +1,2 @@ +# Global Owners +* @cfuguet diff --git a/hw/vendor/hpdcache/LICENSE b/hw/vendor/hpdcache/LICENSE new file mode 100644 index 00000000..06b1c616 --- /dev/null +++ b/hw/vendor/hpdcache/LICENSE @@ -0,0 +1,98 @@ +Solderpad Hardware License v2.1 + +This license operates as a wraparound license to the Apache License +Version 2.0 (the “Apache License”) and incorporates the terms and +conditions of the Apache License (which can be found here: +http://apache.org/licenses/LICENSE-2.0), with the following additions and +modifications. It must be read in conjunction with the Apache License. +Section 1 below modifies definitions and terminology in the Apache +License and Section 2 below replaces Section 2 of the Apache License. +The Appendix replaces the Appendix in the Apache License. You may, at +your option, choose to treat any Work released under this license as +released under the Apache License (thus ignoring all sections written +below entirely). + +1. Terminology in the Apache License is supplemented or modified as +follows: + +“Authorship”: any reference to ‘authorship’ shall be taken to read +“authorship or design”. + +“Copyright owner”: any reference to ‘copyright owner’ shall be taken to +read “Rights owner”. + +“Copyright statement”: the reference to ‘copyright statement’ shall be +taken to read ‘copyright or other statement pertaining to Rights’. + +The following new definition shall be added to the Definitions section of +the Apache License: + +“Rights” means copyright and any similar right including design right +(whether registered or unregistered), rights in semiconductor +topographies (mask works) and database rights (but excluding Patents and +Trademarks). + +The following definitions shall replace the corresponding definitions in +the Apache License: + +“License” shall mean this Solderpad Hardware License version 2.1, being +the terms and conditions for use, manufacture, instantiation, adaptation, +reproduction, and distribution as defined by Sections 1 through 9 of this +document. + +“Licensor” shall mean the owner of the Rights or entity authorized by the +owner of the Rights that is granting the License. + +“Derivative Works” shall mean any work, whether in Source or Object form, +that is based on (or derived from) the Work and for which the editorial +revisions, annotations, elaborations, or other modifications represent, +as a whole, an original work of authorship or design. For the purposes of +this License, Derivative Works shall not include works that remain +reversibly separable from, or merely link (or bind by name) or physically +connect to or interoperate with the Work and Derivative Works thereof. + +“Object” form shall mean any form resulting from mechanical +transformation or translation of a Source form or the application of a +Source form to physical material, including but not limited to compiled +object code, generated documentation, the instantiation of a hardware +design or physical object or material and conversions to other media +types, including intermediate forms such as bytecodes, FPGA bitstreams, +moulds, artwork and semiconductor topographies (mask works). + +“Source” form shall mean the preferred form for making modifications, +including but not limited to source code, net lists, board layouts, CAD +files, documentation source, and configuration files. + +“Work” shall mean the work of authorship or design, whether in Source or +Object form, made available under the License, as indicated by a notice +relating to Rights that is included in or attached to the work (an +example is provided in the Appendix below). + +2. Grant of License. Subject to the terms and conditions of this License, +each Contributor hereby grants to You a perpetual, worldwide, +non-exclusive, no-charge, royalty-free, irrevocable license under the +Rights to reproduce, prepare Derivative Works of, make, adapt, repair, +publicly display, publicly perform, sublicense, and distribute the Work +and such Derivative Works in Source or Object form and do anything in +relation to the Work as if the Rights did not exist. + +APPENDIX + +Copyright 2023,2024 CEA* +*Commissariat a l'Energie Atomique et aux Energies Alternatives (CEA) +Copyright 2025 Inria, Universite Grenoble-Alpes, TIMA + +SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 + +Licensed under the Solderpad Hardware License v 2.1 (the “License”); you +may not use this file except in compliance with the License, or, at your +option, the Apache License version 2.0. You may obtain a copy of the +License at + +https://solderpad.org/licenses/SHL-2.1/ + +Unless required by applicable law or agreed to in writing, any work +distributed under the License is distributed on an “AS IS” BASIS, WITHOUT +WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +License for the specific language governing permissions and limitations +under the License. diff --git a/hw/vendor/hpdcache/README.md b/hw/vendor/hpdcache/README.md new file mode 100644 index 00000000..5b4971db --- /dev/null +++ b/hw/vendor/hpdcache/README.md @@ -0,0 +1,95 @@ +# OpenHW Core-V High-Performance L1 Dcache (CV-HPDcache) + +![HPDcache CI](https://github.com/openhwgroup/cv-hpdcache/actions/workflows/test.yml/badge.svg) + +The HPDcache is an open-source High-Performance, Multi-requester, Out-of-Order L1 Dcache for RISC-V cores and accelerators. + + +## Directory Structure + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
DirectoryDescription
rtlContains the file lists to be used for the compiling of the HPDcache
rtl/srcContains the SystemVerilog RTL sources of the HPDcache
rtl/lintContains a linter wrapper and a Makefile to run a lint tool on the RTL
rtl/tbContains a HPDcache standalone testbench for validation of the RTL
rtl/fv/lntContains a formal HPDcache specification written in LNT
docsContains documentation of the HPDcache
+ + +## Documentation + +The HPDcache User Guide document can be found in the *docs* folder. +It is written in reStructuredText format. + +If you need to compile the User Guide document, a dedicated *Makefile* is in the *docs* folder. + +You can find some pre-compiled User Guide documents (in both HTML or PDF) in [Releases](https://github.com/openhwgroup/cv-hpdcache/releases) + + +## Licensing + +The HPDcache is released under the Solderpad Hardware License (version 2.1). +Please refer to the [LICENSE](LICENSE) file for further information. + + +## Integration Examples of the HPDcache + +### CVA6 + +The HPDcache is integrated with the CVA6 core. +The HPDcache repository (this repository) is included as a submodule of the CVA6 Git. +After you clone the [CVA6](https://github.com/openhwgroup/cva6) repository, be sure to pass the ``config_pkg::HPDCACHE`` value to the ``DCacheType`` parameter. +This selects the HPDcache as the L1 Data Cache of the core. +For example, the CVA6 configuration package [cv64a6_imafdc_sv39_hpdcache_config_pkg.sv](https://github.com/openhwgroup/cva6/blob/master/core/include/cv64a6_imafdc_sv39_hpdcache_config_pkg.sv) does this. + +The HPDcache is instantiated in the [cva6_hpdcache_subsystem.sv](https://github.com/openhwgroup/cva6/blob/master/core/cache_subsystem/cva6_hpdcache_subsystem.sv) file. +You may take a look if you want to integrate the HPDcache with another core. + +### Integration Template + +You may look into the docs/lint subdirectory of this repository to see an integration example of the HPDcache ([hpdcache_lint.sv](docs/lint/hpdcache_lint.sv)). + +This example uses the macros defined in the [hpdcache_typedef.svh](rtl/include/hpdcache_typedef.svh) file. +These macros ease the definition of types required by the interface of the HPDcache module. + +## HPDcache Validation and Verification + +For a complete UVM testbench of the HPDcache, please see the [HPDcache Verif](https://github.com/openhwgroup/cv-hpdcache-verif) repository. + +There is another testbench (not as complete as the one above) written in SystemC into the `rtl/tb` subdirectory of this repository. +This testbench is compatible with the [Verilator](https://www.veripool.org/verilator/) simulation tool. Thus, it accepts a fully open-source simulation flow. +For more information about the SystemC testbench, read its dedicated [README](rtl/tb/README.md). + + +## HPDcache Publications & Tutorials + +- Technical Paper: César Fuguet. 2023. HPDcache: Open-Source High-Performance L1 Data Cache for RISC-V Cores. In Proceedings of the 20th ACM International Conference on Computing Frontiers (CF '23). Association for Computing Machinery, New York, NY, USA, 377–378. + +- Technical Paper: D. Million, N. Oliete-Escuín and C. Fuguet, "Breaking the Memory Wall with a Flexible Open-Source L1 Data-Cache," 2024 Design, Automation & Test in Europe Conference & Exhibition (DATE), Valencia, Spain, 2024, pp. 1-2, + +- Video: César Fuguet. 2023. High Performance L1 Dcache for RISC-V Cores. TRISTAN Workshop. RISC-V Summit Europe 2023. + +- Video: Christian Fabre, César Fuguet. 2023. One Year of Improvements on OpenHW Group's HPDCache. RISC-V Summit US 2023. diff --git a/hw/vendor/hpdcache/docs/.gitignore b/hw/vendor/hpdcache/docs/.gitignore new file mode 100644 index 00000000..567609b1 --- /dev/null +++ b/hw/vendor/hpdcache/docs/.gitignore @@ -0,0 +1 @@ +build/ diff --git a/hw/vendor/hpdcache/docs/LICENSE b/hw/vendor/hpdcache/docs/LICENSE new file mode 100644 index 00000000..279d97ad --- /dev/null +++ b/hw/vendor/hpdcache/docs/LICENSE @@ -0,0 +1,97 @@ +Solderpad Hardware License v2.1 + +This license operates as a wraparound license to the Apache License +Version 2.0 (the “Apache License”) and incorporates the terms and +conditions of the Apache License (which can be found here: +http://apache.org/licenses/LICENSE-2.0), with the following additions and +modifications. It must be read in conjunction with the Apache License. +Section 1 below modifies definitions and terminology in the Apache +License and Section 2 below replaces Section 2 of the Apache License. +The Appendix replaces the Appendix in the Apache License. You may, at +your option, choose to treat any Work released under this license as +released under the Apache License (thus ignoring all sections written +below entirely). + +1. Terminology in the Apache License is supplemented or modified as +follows: + +“Authorship”: any reference to ‘authorship’ shall be taken to read +“authorship or design”. + +“Copyright owner”: any reference to ‘copyright owner’ shall be taken to +read “Rights owner”. + +“Copyright statement”: the reference to ‘copyright statement’ shall be +taken to read ‘copyright or other statement pertaining to Rights’. + +The following new definition shall be added to the Definitions section of +the Apache License: + +“Rights” means copyright and any similar right including design right +(whether registered or unregistered), rights in semiconductor +topographies (mask works) and database rights (but excluding Patents and +Trademarks). + +The following definitions shall replace the corresponding definitions in +the Apache License: + +“License” shall mean this Solderpad Hardware License version 2.1, being +the terms and conditions for use, manufacture, instantiation, adaptation, +reproduction, and distribution as defined by Sections 1 through 9 of this +document. + +“Licensor” shall mean the owner of the Rights or entity authorized by the +owner of the Rights that is granting the License. + +“Derivative Works” shall mean any work, whether in Source or Object form, +that is based on (or derived from) the Work and for which the editorial +revisions, annotations, elaborations, or other modifications represent, +as a whole, an original work of authorship or design. For the purposes of +this License, Derivative Works shall not include works that remain +reversibly separable from, or merely link (or bind by name) or physically +connect to or interoperate with the Work and Derivative Works thereof. + +“Object” form shall mean any form resulting from mechanical +transformation or translation of a Source form or the application of a +Source form to physical material, including but not limited to compiled +object code, generated documentation, the instantiation of a hardware +design or physical object or material and conversions to other media +types, including intermediate forms such as bytecodes, FPGA bitstreams, +moulds, artwork and semiconductor topographies (mask works). + +“Source” form shall mean the preferred form for making modifications, +including but not limited to source code, net lists, board layouts, CAD +files, documentation source, and configuration files. + +“Work” shall mean the work of authorship or design, whether in Source or +Object form, made available under the License, as indicated by a notice +relating to Rights that is included in or attached to the work (an +example is provided in the Appendix below). + +2. Grant of License. Subject to the terms and conditions of this License, +each Contributor hereby grants to You a perpetual, worldwide, +non-exclusive, no-charge, royalty-free, irrevocable license under the +Rights to reproduce, prepare Derivative Works of, make, adapt, repair, +publicly display, publicly perform, sublicense, and distribute the Work +and such Derivative Works in Source or Object form and do anything in +relation to the Work as if the Rights did not exist. + +APPENDIX + +Copyright 2023 CEA* +*Commissariat a l'Energie Atomique et aux Energies Alternatives (CEA) + +SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 + +Licensed under the Solderpad Hardware License v 2.1 (the “License”); you +may not use this file except in compliance with the License, or, at your +option, the Apache License version 2.0. You may obtain a copy of the +License at + +https://solderpad.org/licenses/SHL-2.1/ + +Unless required by applicable law or agreed to in writing, any work +distributed under the License is distributed on an “AS IS” BASIS, WITHOUT +WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +License for the specific language governing permissions and limitations +under the License. diff --git a/hw/vendor/hpdcache/docs/Makefile b/hw/vendor/hpdcache/docs/Makefile new file mode 100644 index 00000000..d0c3cbf1 --- /dev/null +++ b/hw/vendor/hpdcache/docs/Makefile @@ -0,0 +1,20 @@ +# Minimal makefile for Sphinx documentation +# + +# You can set these variables from the command line, and also +# from the environment for the first two. +SPHINXOPTS ?= +SPHINXBUILD ?= sphinx-build +SOURCEDIR = source +BUILDDIR = build + +# Put it first so that "make" without argument is like "make help". +help: + @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) + +.PHONY: help Makefile + +# Catch-all target: route all unknown targets to Sphinx using the new +# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). +%: Makefile + @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) diff --git a/hw/vendor/hpdcache/docs/README.md b/hw/vendor/hpdcache/docs/README.md new file mode 100644 index 00000000..dcf1e7ce --- /dev/null +++ b/hw/vendor/hpdcache/docs/README.md @@ -0,0 +1,55 @@ +# Build instructions + +The documents in this directory are written in reStructuredText and compiled to HTML using Sphinx. For more information, check https://www.sphinx-doc.org/en/master/usage/restructuredtext/index.html. + +## Prerequisites + +This section outlines the necessary steps to build the document on Linux (tested on Debian-based distributions). + +Sphinx is based on Python and requires at least version 3.8. Additionally, `make` is required and can be installed through build-essential. + +```bash +sudo apt update +sudo apt install python3 +sudo apt install build-essential +``` + +Please verify your Python version using + +```bash +python3 --version +``` + +Sphinx requires certain packages to build these documents. These are listed in `requirements.txt`. They can be installed using + +```bash +pip install -r requirements.txt +``` + +## Building the documents + +Build is invoked via the `make` command. Typically, an HTML should be build. + +```bash +make html +``` + +A secondary build target is pdf. To build the pdf, additional prerequisites need to be met. To install `pdflatex`, run + +```bash +sudo apt-get install texlive-latex-base +``` + +Note: you may also need to install the `latexmk` package seperately. + +```bash +sudo apt install latexmk +``` + +A pdf document can be built using the command + +```bash +make latexpdf +``` + +Simply type `make` to view other available targets. diff --git a/hw/vendor/hpdcache/docs/requirements.txt b/hw/vendor/hpdcache/docs/requirements.txt new file mode 100644 index 00000000..49880193 --- /dev/null +++ b/hw/vendor/hpdcache/docs/requirements.txt @@ -0,0 +1,8 @@ +# Used by ReadTheDocs +# Copyright 2025 OpenHW Foundation +# SPDX-License-Identifier:Apache-2.0 WITH SHL-2.1 +sphinx +sphinx_rtd_theme +recommonmark +#sphinxcontrib-svg2pdfconverter +#sphinx_github_changelog diff --git a/hw/vendor/hpdcache/docs/source/_static/theme_overrides.css b/hw/vendor/hpdcache/docs/source/_static/theme_overrides.css new file mode 100644 index 00000000..7c514afd --- /dev/null +++ b/hw/vendor/hpdcache/docs/source/_static/theme_overrides.css @@ -0,0 +1,9 @@ +/* override table width restrictions */ +.wy-table-responsive table td, .wy-table-responsive table th { + white-space: normal; +} +.wy-table-responsive { + margin-bottom: 24px; + max-width: 100%; + overflow: visible; +} diff --git a/hw/vendor/hpdcache/docs/source/amo.rst b/hw/vendor/hpdcache/docs/source/amo.rst new file mode 100644 index 00000000..21b3ec38 --- /dev/null +++ b/hw/vendor/hpdcache/docs/source/amo.rst @@ -0,0 +1,202 @@ +.. + Copyright 2024 CEA* + *Commissariat a l'Energie Atomique et aux Energies Alternatives (CEA) + + SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 + + Licensed under the Solderpad Hardware License v 2.1 (the “License”); you + may not use this file except in compliance with the License, or, at your + option, the Apache License version 2.0. You may obtain a copy of the + License at + + https://solderpad.org/licenses/SHL-2.1/ + + Unless required by applicable law or agreed to in writing, any work + distributed under the License is distributed on an “AS IS” BASIS, WITHOUT + WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + License for the specific language governing permissions and limitations + under the License. + + Authors : Cesar Fuguet + Description : HPDcache Atomic Memory Operations (AMOs) + +.. _sec_amo: + +Atomic Memory Operations (AMOs) +=============================== + +Background +---------- + +The AMOs are special load/store accesses that implements a read-modify-write +semantic. A single instruction is able to read a data from the memory, perform +an arithmetical/logical operation on that data, and store the result. All this +is performed as a single operation (no other operation can come in between the +read-modify-write operations). + +These operations are meant for synchronization in multi-core environments. To +enable this synchronization, AMOs need to be performed on the PoS +(Point-of-Serialization), point where all accesses from the different cores +converge. This is usually a shared cache memory (when multiple levels of cache +are implemented) or the external RAM controllers. Thus, the HPDcache needs to +forward these operations to the PoS through the NoC interface. + +Supported AMOs +-------------- + +On the interface from requesters, the supported AMOs are the ones listed in +:numref:`Table %s `. The supported AMOs are the ones defined +in the atomic (A) extension of the RISC-V ISA specification [RISCVUP2019]_. + +Implementation +-------------- + +If :math:`\scriptsize\mathsf{CONF\_HPDCACHE\_SUPPORT\_AMO}` is set to 0, the +HPDcache does not support AMOs from requesters. +See :ref:`sec_uncacheable_handler` for more details. + +When a requester performs an AMO operation, the HPDcache needs to forward it to +the PoS. It is only at the PoS that AMOs from different caches converge and thus +where the correct result may be computed in case of multiple, simultaneous, +address-colliding AMOs. In the case a cache has a replica of the target address, +as the modification is performed at the PoS, that replica becomes obsolete +(cache obsolescence problem). This issue shall be solved through a hardware (or +software) cache coherency protocol. + +To provide a consistent view of the data, the HPDcache updates the local replica +based on the result from the memory. The procedure is as follows: Forward the +AMO to the PoS, and wait for the response with the old data. If the target +address is replicated in the HPDcache, the HPDcache computes the new value +locally based on the data from the AMO request and the old data from the memory +(not the data of the local replica). Then it updates the replica. This allows +the core to have a consistent view with regards to its operations (single-thread +consistency). However, a cache coherency protocol (hardware or software) is +still required to ensure coherency in multi-core systems. + +The HPDcache handle AMOs as non-allocating operations. This is, AMOs never fetch +a replica of the target cacheline from the memory to the cache. If the target +cacheline IS NOT replicated in the cache, the AMO modifies ONLY the memory. If +the target cacheline IS replicated in the cache, the AMO modifies BOTH the +memory and the cache. + + +AMO ordering +------------ + +As specified in the RISC-V ISA specification [RISCVUP2019]_, the base RISC-V ISA +has a relaxed memory model. To provide additional ordering constraints, AMOs +(including LR/SC) specify two bits, *aq* and *rl*, for *acquire* and *release* +semantics. + +The HPDcache ignores *aq* and *rl* bits. It considers that they are always set. +Hence, HPDcache handles AMOs as sequentially consistent memory operations. The +HPDcache waits for all pending read and write operations to complete before +serving the AMO request. + +This behavior implies that when the HPDcache forwards an AMO to the NoC, it will +be the only pending request from the HPDcache. In addition, no new requests from +the requesters are served until the AMO is completed. + +LR/SC support +------------- + +LR and SC are part of the Atomic (A) extension of the RISC-V ISA specification +[RISCVUP2019]_. These instructions allow *"complex atomic operations on a single +memory word or double-word"*. + +The HPDcache fully supports all the instructions of the A extension of the +RISC-V ISA, including LR and SC operations. + +In the specification of these instructions in the RISC-V ISA document, some +details are dependent to the implementation. Namely, the size of the reservation +set and the return code of a SC failure. + +LR/SC reservation set +~~~~~~~~~~~~~~~~~~~~~ + +When a requester executes a LR operation, it "reserves" a set of bytes in +memory. This set contains at least the bytes solicited in the request but may +contain more. RISC-V ISA defines two sizes for LR operations: 4 bytes or 8 +bytes. **The HPDcache reserves 8-bytes (double-word) containing the addressed +memory location regardless of whether the LR size is 4 or 8 bytes**. The start +address of the reservation set is a 8-bytes aligned address. + +When the LR size is 8 bytes, the address is also aligned to 8 bytes. In this +case, the reservation set matches exactly the address interval defined in the +request. When the LR size is 4 bytes, there are two possibilities: + +-# the target address is not aligned to 8 bytes. The start address of the +reservation set contains additional 4 bytes before the target address + +-# the target address is aligned to 8 bytes. The reservation set starts at the +target address but contains additional 4 bytes after the requested ones. + +In summary, in case of LR operation, the reservation set address range is +computed as follows: + +.. math:: + + \small\mathbf{reservation\_set =} + \begin{cases} + \mathsf{(\lfloor{}HPDCACHE\_REQ\_ADDR / 8\rfloor{} \times 8)} & + (\text{start address}) \\ + \mathsf{(\lfloor{}HPDCACHE\_REQ\_ADDR / 8\rfloor{} \times 8) + 8} & + (\text{end address}) \\ + \end{cases} + +**When a requester executes a SC operation, the HPDcache forwards the operation +to the memory ONLY IF the bytes addressed by the SC are part of an active +reservation set**. If the SC accesses a smaller number of bytes that those in +the active reservation set but within that reservation set, the SC is still +forwarded to the memory. + +After a SC operation, the active reservation set, if any, is invalidated. This +is regardless whether the SC operation succeeds or not. + +.. admonition:: Caution + :class: caution + + The HPDcache keeps a unique active reservation set. If multiple requesters + perform LR operations, the unique active reservation set is the one specified + by the last LR operation. + + +The HPDcache also invalidates an active reservation set when there is an +address-colliding STORE operation. If a STORE access from any requester writes +one or more bytes within the active reservation set, the latter is invalidated. + + +SC failure response code +~~~~~~~~~~~~~~~~~~~~~~~~ + +The RISC-V ISA [RISCVUP2019]_ specifies that when a SC operation succeeds, the +core shall write zero into the destination register of the operation. Otherwise, +in case of SC failure, the core shall write a non-zero value into the +destination register. + +The HPDcache returns the status of an SC operation into the ``core_rsp_o.rdata`` +signal of the response interface to requesters. The following table specifies +the values returned by the HPDcache into the ``core_rsp_o.rdata`` signal in case +of SC operation. + +.. list-table:: + :widths: 30 30 + :header-rows: 1 + :align: center + + * - **Case** + - **Return value** + * - SC Success + - :math:`\small\mathsf{0x0000\_0000}` + * - SC Failure + - :math:`\small\mathsf{0x0000\_0001}` + +Depending on the specified size in the request (``core_req_i.size``), the +returned value is extended with zeros on the most significant bits. This is, if +the SC request size is 8 bytes, and the SC is a failure, then the returned value +is :math:`\small\mathsf{0x0000\_0000\_0000\_0001}`. + +In addition, if the :math:`\small\mathsf{CONF\_HPDCACHE\_REQ\_DATA\_WIDTH}` +width is wider than the size of the SC request, the return value is replicated +:math:`\small\mathsf{CONF\_HPDCACHE\_REQ\_WORDS}` times. + diff --git a/hw/vendor/hpdcache/docs/source/architecture.rst b/hw/vendor/hpdcache/docs/source/architecture.rst new file mode 100644 index 00000000..64cf7d16 --- /dev/null +++ b/hw/vendor/hpdcache/docs/source/architecture.rst @@ -0,0 +1,1392 @@ +.. + Copyright 2024 CEA* + *Commissariat a l'Energie Atomique et aux Energies Alternatives (CEA) + + SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 + + Licensed under the Solderpad Hardware License v 2.1 (the “License”); you + may not use this file except in compliance with the License, or, at your + option, the Apache License version 2.0. You may obtain a copy of the + License at + + https://solderpad.org/licenses/SHL-2.1/ + + Unless required by applicable law or agreed to in writing, any work + distributed under the License is distributed on an “AS IS” BASIS, WITHOUT + WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + License for the specific language governing permissions and limitations + under the License. + + Authors : Cesar Fuguet + Description : HPDcache Architecture + +Architecture +============ + +:numref:`Figure %s ` depicts a global view of +the HPDcache (core partition that does not include the request arbitration). On +the upper part of the cache there is the interface from/to requesters. On the +bottom part there is the interface from/to the memory. + +.. _fig_architecture_hpdcache_core: + +.. figure:: images/hpdcache_core.* + :alt: HPDcache core + :align: center + :width: 100% + + HPDcache core + +Cache Controller +---------------- + +The cache controller is responsible for decoding and issuing the requests to the +appropriate handler. The cache controller implements a 3-stage pipeline. This +pipeline is capable of accepting one request per cycle. However, there are some +scenarios where the pipeline may either stall or put a request on hold in a side +buffer called Replay Table (RTAB). + +The first stage (stage 0) of the pipeline arbitrates between requests from the +miss handler (refill), RTAB, and requesters; the second stage (stage 1) responds +to loads (in case of hit) and to stores; the third stage (stage 2) is only used +by loads in case of miss. In this last stage, the cache triggers a read miss +transaction to the memory and allocates a new entry in the Miss Status Holding +Register (MSHR) to track the progress of the miss transaction. + +A request on stage 0 can either be consumed on that cycle (forwarded to the +stage 1) or stalled. A request on stage 1 or stage 2 always progresses. In stage +1 the request is either acknowledged (load hit or write acknowledgement), +forwarded to stage 2 (load miss), or put into the RTAB. In stage 2, the request +quits the pipeline and it is written in the MSHR. + +The arbiter in stage 0 uses a fixed-priority policy: refills have the higher +priority, followed by the RTAB, and finally core request (lower priority). + +Pipeline Stalls in Stage 0 +'''''''''''''''''''''''''' + +Stalls in stage 0 are necessary in some specific scenarios, that are listed +below. When there is a stall in stage 0, a new request from a requester cannot +be accepted, this is, the corresponding :math:`\mathsf{ready}` signal is kept +low (set to 0). Requests in the other stages (1 and 2) are processed normally +(even in case of a stall in stage 0). + +.. list-table:: Events that Stall the Pipeline + :widths: 5 50 40 + :header-rows: 1 + :align: center + + * - Event + - Description + - Stall Latency (Clock Cycles) + * - **1** + - The RTAB is full + - It depends on when an entry of the RTAB is freed + * - **2** + - A CMO invalidation or fence operation is being processed by the + corresponding handler + - It depends on the latency of the operation + * - **3** + - An uncacheable or atomic operation is being processed by the + corresponding handler + - It depends on the latency of the operation + * - **4** + - There is a load miss in stage 1 + - One cycle + * - **5** + - There is a store in stage 1 and the request in stage 0 is a load + (structural hazard on access to the internal cache data memory) + - One cycle + +.. _sec_onhold: + +On-Hold Requests +'''''''''''''''' + +In some scenarios, a request that has been accepted in the pipeline can be +later put on-hold by the cache controller. The cache controller puts a request +on-hold by removing it from the cache pipeline and writing it into the Replay +Table (RTAB). When a request is put on-hold, it is re-executed when all the +blocking conditions have been removed. The blocking conditions putting a +request on-hold are the following: + +.. _tab_onhold: + +.. list-table:: Conditions Putting a Request On-hold + :widths: 3 37 60 + :header-rows: 1 + + * - # + - Condition + - Description + * - **1** + - **Cacheable LOAD or PREFETCH, and there is a hit on a pending miss (hit + on the MSHR)** + - When there is a read miss on a given cacheline for which there is a + pending read miss, then the more recent one needs to wait for the + previous one to be served. This allows the latest one to read the data + from the cache after the refill operation completes. More importantly, + this frees the pipeline to accept the corresponding refill and prevent a + deadlock. + * - **2** + - **Cacheable LOAD or PREFETCH, there is a miss on the cache, and there is + a hit (cacheline granularity) on an opened, pending or sent entry of the + WBUF** + - When there is a read miss on an address, the cache controller needs to + read from the memory the missing cacheline. As the NoC implements + different physical channels for read and write requests, there is a race + condition between the read miss and a pending write operation. If the + read miss arrives first to the memory, it would read the old data (which + violates :ref:`sec_mcrs`). This blocking condition causes that the LOAD + or PREFETCH will have a delay penalty of up to two transaction delays: + one for the write to complete, then one for the read. + * - **3** + - **Cacheable STORE, there is a miss on the cache, and there is a hit on a + pending miss (hit on the MSHR)** + - When writing, as the NoC implements different physical channels for read + and write requests, there is a race condition between the STORE and the + pending read miss. If the STORE arrives first to the memory, the earlier + read miss would read the new data (which violates :ref:`sec_mcrs`). + * - **4** + - **Cacheable LOAD/PREFETCH/STORE, and there is a hit on an entry of the + RTAB** + - Accesses to the same cacheline SHALL be processed in order (to respect + :ref:`sec_mcrs`). In case of a hit with a valid entry in the RTAB, the + new request shall wait for previous requests on the same cacheline to + finish. + * - **5** + - **Cacheable LOAD or PREFETCH, there is a miss on the cache, and the MSHR + has no available slots** + - When there is a read miss on an address, the cache controller needs to + allocate a new entry in the MSHR. If there is no available entry, the + read request needs to wait for an entry in the MSHR to be freed. This + frees the pipeline to accept the corresponding refill and prevent a + deadlock. + * - **6** + - **Cacheable LOAD or PREFETCH, there is a miss on the cache, and the miss + handler FSM cannot send the read miss request** + - When there is a read miss on an address, the cache controller needs to + read from memory the missing cacheline. The read miss request is sent by + the miss handler FSM, but if there is congestion in the NoC, this read + request cannot be issued. This frees the pipeline to prevent a potential + deadlock. + +The cache controller checks all these conditions in the second stage (stage 1) +of the pipeline. If one of the conditions is met, the cache controller puts the +request into the RTAB and holds it there until its blocking condition is +resolved. At that moment, the cache can replay the request from the RTAB. + +The RTAB can store multiple on-hold requests. The idea is to improve the +throughput of the cache by reducing the number of cases where there is a head +of line blocking at the client interface. As mentioned in +:numref:`Table %s `, this also prevents deadlocks. To always allow +the cache controller to retire a request from the pipeline, the cache controller +does not accept new requests if the RTAB is full. + +Requests from the RTAB may be executed in an order that is different from +the order in which they were accepted (if they target different cachelines). +The requests, that target the same cacheline, are replayed by the RTAB in the +order they were accepted. + +Requests within the RTAB that have their dependencies resolved may be replayed. +These have higher priority than the new requests from requesters. + + +.. _sec_mcrs: + +Memory Consistency Rules (MCRs) +''''''''''''''''''''''''''''''' + +The cache controller processes requests following a set of Memory Consistency +Rules (MCRs). These rules allow the requesters to have a predictable behavior. + +The set MCRs respected by the cache controller are those defined by the RISC-V +Weak Memory Ordering (RVWMO) memory consistency model. [RISCVUP2019]_ specifies +this model. The following statement summarizes these rules: **if one memory +access (read or write), A, precedes another memory access (read or write), B, +and they access overlapping addresses, then they MUST be executed in program +order (A then B)**. It can be deduced from this statement, that non-overlapping +accesses can be executed in any order. + +The cache controller also needs to respect the progress axiom: **no memory +operation may be preceded by an infinite number of memory operations**. That +is, all memory operations need to be processed at some point in time. They +cannot wait indefinitely. + +Hybrid Write-Policy +''''''''''''''''''' + +The HPDcache can handle writes in both write-back and write-through policies. +The HPDcache handles the write policy at cacheline granularity. This means that +at any given time, the HPDcache has two logical subsets of cachelines: one +subset containing cachelines in write-back policy, and another subset +containing cachelines in write-through policy. Any of these two subsets may be +empty, meaning that all cachelines may be either write-back or write-through. + +If only one write-policy is required, the system designer can: + + - Disable the write-back policy setting to 0 the ``CONF_HPDCACHE_WB_ENABLE`` + parameter. + + - Disable the write-through policy setting to 0 the ``CONF_HPDCACHE_WT_ENABLE`` + parameter. + +When both write-policies are enabled, the request interface may set dynamically +the write-policy (write-back or write-through) for the target cacheline. In the +request interface, there are specific flags (hints) to indicate the desired +policy for a given request. + +The HPDcache keeps the policy for subsequent accesses until a request/response +force it to a specific policy. It accepts policy changes for any cacheline. +This is, a request/response may ask for write-back policy (respectively +write-through) for a given cacheline, and another request/response may ask for +write-through policy (respectively write-back) for that same cacheline later. + + +Cache Directory and Data +------------------------ + +State Bits in the Cache Directory +''''''''''''''''''''''''''''''''' + +Each cacheline in the HPDcache has the following state bits: + + - **valid**: when this bit is set, it means that the cacheline is valid. + Otherwise, the slot is available. + - **wback**: when this bit is set, it means that the cacheline is in + write-back mode. It is only valid when the valid bit is set. + - **dirty**: when this bit is set, it means that the cacheline is in + write-back mode and it is dirty (it has locally modified data which has not + been transmitted to the next memory level). It is only valid when the valid + bit is set. + - **fetch**: when this bit is set, it means that the cacheline has been + pre-selected to be replaced by a new one. While this bit is set, the refill + response for the miss has not yet arrived. + +Replacement Policy +'''''''''''''''''' + +The HPDcache supports the following two replacement policies: Pseudo Random or +Pseudo Least Recently Used (PLRU) replacement policy. The user selects the +actual policy at synthesis-time through the +:math:`\scriptsize\mathsf{CONF\_HPDCACHE\_VICTIM\_SEL}` +configuration parameter (:numref:`Table %s `). + +The cache uses the selected policy to select the victim way where a new +cacheline is written. In case of a read miss, CMO prefetch miss or a write miss +with the write-back policy the cache controller applies the replacement policy +to select the way of the corresponding set where the miss handler will write +the new cacheline. + +The cache selects the victim way at the moment of a cache miss. At that moment, +it also asks for the missing cacheline to the memory. While waiting for the +refill response, the victim cacheline is still accessible but in **fetch** mode +(fetch bit set). This means that it has been pre-selected for replacement, and +cannot be a candidate for victim selection for a future miss (until the refill +response does not arrive). + + +Pseudo Least Recently Used (PLRU) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +This replacement policy requires one state bit per cacheline in the cache. This +bit is named Least Recently Used (LRU) state. All LRU bits are set to 0 on +reset. They are then updated at each read, CMO prefetch, store and atomic +operation from the requesters. + +The following code snippet shows the declaration of the array containing the +LRU bits. As explained before, there are as many bits as cachelines in the +cache. Therefore the LRU bits are organized as a 2D (two-dimensions) array of +:math:`\mathsf{CONF\_HPDCACHE\_SETS}` and +:math:`\mathsf{CONF\_HPDCACHE\_WAYS}` bits. + +.. code:: c + + // 2D array containing LRU state bits + bool lru[CONF_HPDCACHE_SETS][CONF_HPDCACHE_WAYS]; + + +The following code snippet illustrates the algorithm (``update_plru`` function) +that the cache controller uses to update PLRU bits. This function is used by +read, CMO prefetch, write and atomic requests from requesters. The cache +controller first checks for a hit in any way of the set designated by the +request address. If there is a hit, the cache controller applies the +``update_plru`` algorithm on the corresponding set and way. In the case of a +miss, the cache controller first selects a victim way, then during the refill, +the miss handler applies the ``update_plru`` algorithm. + +.. code:: c + + void update_plru(int set, int way) + { + // set the LRU bit of the target set and way + lru[set][way] = true; + + // check if all LRU bits of the target "set" contain 1 + for (int w = 0; w < HPDCACHE_WAYS; w++) { + // If there is at least one 0, the update is done + if (!lru[set][w]) return; + } + + // If all LRU bits are set to 1, reset to 0 the LRU bits of all the ways + // except the one being accessed + for (int w = 0; w < HPDCACHE_WAYS; w++) { + if (w != way) lru[set][w] = false; + } + } + + +The following code snippet illustrates the algorithm (``select_victim_way`` +function) that the cache controller uses to select a victim way on a cache +miss. In summary, the victim way is either the first way (starting from way +0) where the valid bit is 0, or the first way where the LRU bit is unset. +In the case where the way with the LRU bit unset is being fetched (fetch bit +set), the controller selects one of the ways which is not being fetched, giving +the highest priority to clean ways (no local modification in case of write-back +policy). If all ways are pre-selected (being fetched by a previous miss), the +cache controller cannot select a victim, and puts the new miss request into the +replay table. + +.. code:: c + + int select_victim_way(int set) + { + // Return the first way (of the target set) whose valid bit is unset + for (int w = 0; w < HPDCACHE_WAYS; w++) { + if (!valid[set][w]) { + return w; + } + } + + // If all ways are valid, return the first way (of the target set) whose + // PLRU bit is unset and which is not pre-selected as victim + for (int w = 0; w < HPDCACHE_WAYS; w++) { + if (!fetch[set][w] && !lru[set][w]) { + return w; + } + } + + // If the PLRU designated way cannot be selected (already pre-selected + // as victim), return the first clean way (no locally modified in case + // of write-back policy) + for (int w = 0; w < HPDCACHE_WAYS; w++) { + if (!fetch[set][w] && !dirty[set][w]) { + return w; + } + } + + // If there is no clean way selectable, return the first dirty way + for (int w = 0; w < HPDCACHE_WAYS; w++) { + if (!fetch[set][w]) { + return w; + } + } + + // If there is no selectable way (all ways are being already pre-selected + // and are waiting for a refill) returns -1. In this case the cache + // controller puts the miss request on hold in the replay table. + return -1; + } + + +Pseudo Random +~~~~~~~~~~~~~ + +This replacement policy requires only one 8-bit Linear Feedback Shift Register +(LFSR). + +Each time there is a miss (read, CMO prefetch, write in write-back mode), the +cache controller selects either a free way (valid bit is set to 0), or a way +designated by the value in the LFSR. If the random way is in **fetch** mode, +then the cache controller selects the first way which is not in **fetch** mode, +giving the highest priority to clean ways. If all ways are in **fetch** mode, +then the request is put on-hold in the replay table. Each time the cache +controller uses the pseudo random value, it performs a shift of the LFSR. + +This pseudo random policy has a lower area footprint than the PLRU policy +because it only uses a 8-bit LFSR (independently of the number of cachelines in +the cache). The PLRU policy requires one bit per cacheline in the cache. +However, some applications may exhibit lower performance with the pseudo random +replacement policy as locality is not considered while selecting the victim. + + +RAM Organization +'''''''''''''''' + +The HPDcache cache uses SRAM macros for the directory and data parts of the +cache. These RAM macros are synchronous, read/write, single-port RAMs. + +The organization of the RAMs, for the directory and the data, targets the +following: + +#. **High memory bandwidth to/from the requesters** + + The organization allows to read 1, 2, 4, 8, 16, 32 or 64 bytes per cycle. The + maximum number of bytes per cycle is a configuration parameter of the cache. + Read latency is one cycle. + +#. **Low energy-consumption** + + To limit the energy-consumption, the RAMs are organized in a way that the + cache enables only a limited number of RAM macros. This number depends on the + number of requested bytes, and it also depends on the target technology. + Depending on the target technology, the RAM macros have different trade-offs + between width, depth and timing (performance). + +#. **Small RAM footprint** + + To limit the footprint of RAMs, the selected organization looks to implement + a small number of RAMs macros. The macros are selected in a way that they are + as deep and as wide as possible. The selected ratios (depth and width) depend + on the target technology node. + +.. _sec_cache_ram_organization: + +RAM Organization Parameters +''''''''''''''''''''''''''' + +The HPDcache provides a set of parameters to tune the organization of the SRAM +macros. These parameters allow to adapt the HPDcache to the Performance, Power +and Area (PPA) requirements of the system. + +The cache directory and data are both implemented using SRAM macros. + +Cache Directory Parameters +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The cache directory contains the metadata that allows to identify the cachelines +which are present in the cache. + +Each entry contains the following information: + +.. list-table:: + :widths: 5 15 80 + :header-rows: 1 + + * - Field + - Description + - Width (in bits) + * - V + - Valid + - :math:`\mathsf{1}` + * - W + - Write-back (wback) + - :math:`\mathsf{1}` + * - D + - Dirty + - :math:`\mathsf{1}` + * - F + - Fetch + - :math:`\mathsf{1}` + * - T + - Cache Tag + - :math:`\mathsf{HPDCACHE\_NLINE\_WIDTH - HPDCACHE\_SET\_WIDTH}` + +The depth of the macros is: + + :math:`\mathsf{CONF\_HPDCACHE\_SETS}`. + +The width (in bits) of the macros is: + + :math:`\mathsf{4 + T}` bits. + +Finally, the total number of SRAM macros for the cache directory is: + + :math:`\text{SRAM macros} = \mathsf{CONF\_HPDCACHE\_WAYS}` + + +.. admonition:: Possible Improvement + :class: note + + Allow to split sets in different RAMs as for the cache data + (:math:`\mathsf{CONF\_HPDCACHE\_DATA\_SETS\_PER\_RAM}`). + +.. admonition:: Possible Improvement + :class: note + + Allow to put ways side-by-side as for the cache data + (:math:`\mathsf{CONF\_HPDCACHE\_DATA\_WAYS\_PER\_RAM\_WORD}`). + +Cache Data Parameters +~~~~~~~~~~~~~~~~~~~~~ + +The depth of the macros is: + + :math:`\mathsf{\lceil\frac{CONF\_HPDCACHE\_CL\_WORDS}{CONF\_HPDCACHE\_ACCESS\_WORDS}\rceil{}\times{}CONF\_HPDCACHE\_DATA\_SETS\_PER\_RAM}` + +Multiple ways, for the same set, can be put side-by-side in the same SRAM word: + + :math:`\mathsf{CONF\_HPDCACHE\_DATA\_WAYS\_PER\_RAM\_WORD}` + +The width (in bits) of the macros is: + +.. math:: + + \mathsf{CONF\_HPDCACHE\_DATA\_WAYS\_PER\_RAM\_WORD}\\ + \mathsf{\times{}CONF\_HPDCACHE\_WORD\_WIDTH} + +Finally, the total number of SRAM macros is: + +.. math:: + + \mathsf{W} &= \mathsf{CONF\_HPDCACHE\_DATA\_WAYS}\\ + \mathsf{WR} &= \mathsf{CONF\_HPDCACHE\_DATA\_WAYS\_PER\_RAM\_WORD}\\ + \mathsf{S} &= \mathsf{CONF\_HPDCACHE\_DATA\_SETS}\\ + \mathsf{SR} &= \mathsf{CONF\_HPDCACHE\_DATA\_SETS\_PER\_RAM}\\ + \mathsf{A} &= \mathsf{CONF\_HPDCACHE\_ACCESS\_WORDS}\\ + \text{SRAM macros} &= \mathsf{A{}\times{}\lceil\frac{W}{WR}\rceil{}\times{}\lceil\frac{S}{SR}\rceil} + +The :math:`\mathsf{CONF\_HPDCACHE\_ACCESS\_WORDS}` defines the maximum number of +words that can be read or written in the same clock cycle. This parameter +affects both the refill latency and the maximum throughput (bytes/cycle) between +the HPDcache and the requesters. + +Here the refill latency is defined as the number of clock cycles that the miss +handler takes to write into the cache the cacheline data once the response of a +read miss request arrives. It does not consider the latency to receive the data +from the memory because this latency is variable and depends on the system. The +following formula determines the refill latency (in clock cycles) in the +HPDcache: + + :math:`\mathsf{max(2, \frac{CONF\_HPDCACHE\_CL\_WORDS}{CONF\_HPDCACHE\_ACCESS\_WORDS})}` + +The following formula determines the maximum throughput (bytes/cycle) between +the HPDcache and the requesters: + + :math:`\mathsf{\frac{CONF\_HPDCACHE\_ACCESS\_WORDS{}\times{}CONF\_HPDCACHE\_WORD\_WIDTH}{8}}` + +.. admonition:: Caution + :class: caution + + The choice of the :math:`\mathsf{CONF\_HPDCACHE\_ACCESS\_WORDS}` parameter is + important. It has an impact on performance because it determines the refill + latency and the request throughput. It also has an impact on the area because + the depth of SRAM macros depends on this parameter. Finally, it has an impact + on the timing (thus performance) because the number of inputs of the data + word selection multiplexor (and therefore the number of logic levels) also + depends on this parameter. + + As a rule of thumb, timing and area improves with smaller values of this + parameter. Latency and throughput improves with bigger values of this + parameter. The designer needs to choose the good tradeoff depending on the + target system. + + +Example cache data/directory RAM organization +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +:numref:`Figure %s ` illustrates a possible +organization of the RAMs. The illustrated organization allows to implement 32 +KB of data cache (128 sets, 4 ways and 64-byte cachelines). The corresponding +parameters of this organization are the following: + +.. list-table:: + :widths: 40 60 + :header-rows: 1 + + * - **Parameter** + - **Value** + * - :math:`\mathsf{CONF\_HPDCACHE\_SETS}` + - 128 + * - :math:`\mathsf{CONF\_HPDCACHE\_WAYS}` + - 4 + * - :math:`\mathsf{CONF\_HPDCACHE\_WORD\_WIDTH}` + - 64 + * - :math:`\mathsf{CONF\_HPDCACHE\_CL\_WORDS}` + - 8 + * - :math:`\mathsf{CONF\_HPDCACHE\_ACCESS\_WORDS}` + - 4 + * - :math:`\mathsf{CONF\_HPDCACHE\_DATA\_SETS\_PER\_RAM}` + - 128 + * - :math:`\mathsf{CONF\_HPDCACHE\_DATA\_WAYS\_PER\_RAM\_WORD}` + - 2 + + +.. _fig_data_ram_organization_example: + +.. figure:: images/hpdcache_data_ram_organization.* + :alt: HPDcache RAM Organization Example + :align: center + + HPDcache RAM Organization Example + +This example organization has the following characteristics: + +.. list-table:: + :widths: 25 75 + :header-rows: 0 + + * - **Refilling Latency** + - Two clock cycles. The cache needs to write two different entries on a + given memory cut + * - **Maximum Request Throughput (bytes/cycle)** + - 32 bytes/cycle. Requesters can read 1, 2, 4, 8, 16 or 32 bytes of a given + cacheline per cycle. + * - **Energy Consumption** + - It is proportional to the number of requested bytes. Accesses requesting + 1 to 8 bytes need to read two memory cuts (one containing ways 0 and 1, + and the other containing ways 2 and 3); accesses requesting 8 to 16 bytes + need to read 4 memory cuts; accesses requesting 24 to 32 bytes need to + access all the cuts at the same time (8 cuts). + + +Miss Handler +------------ + +This block is in charge of processing read miss requests to the memory. It has +three parts: + +#. One in charge of forwarding read miss requests to the memory + +#. One in charge of tracking the status of in-flight read misses + +#. One in charge of selecting a victim cacheline and updating the cache with + the response data from the memory + +Multiple-entry MSHR +''''''''''''''''''' + +The miss handler contains an essential component of the HPDcache: the +set-associative multi-entry MSHR. This components is the one responsible of +tracking the status of in-flight read miss requests to the memory. Each entry +of contains the status for each in-flight read miss request. Therefore, the +number of entries in the MSHR defines the maximum number of in-flight read miss +requests. + +The number of entries in the MSHR depends on two configuration values: +:math:`\mathsf{CONF\_HPDCACHE\_MSHR\_WAYS}` +and +:math:`\mathsf{CONF\_HPDCACHE\_MSHR\_SETS}`. +The number of entries is computed as follows: + +.. math:: \mathsf{CONF\_HPDCACHE\_MSHR\_SETS~\times~CONF\_HPDCACHE\_MSHR\_WAYS} + +The MSHR accepts the following configurations: + +.. list-table:: MSHR Configurations + :widths: 20 20 60 + :header-rows: 1 + + * - MSHR Ways + - MSHR Sets + - Configuration + * - :math:`\mathsf{= 1}` + - :math:`\mathsf{= 1}` + - Single-Entry + * - :math:`\mathsf{> 1}` + - :math:`\mathsf{= 1}` + - Fully-Associative Array + * - :math:`\mathsf{= 1}` + - :math:`\mathsf{> 1}` + - Direct Access Array + * - :math:`\mathsf{> 1}` + - :math:`\mathsf{> 1}` + - Set-Associative Access Array + + +A high number of entries in the MSHR allows to overlap multiple accesses to the +memory and hide its latency. Of course, the more entries there are, the more +silicon area the MSHR takes. Therefore, the system architect must choose the +MSHR parameters depending on a combination of memory latency, memory +throughput, area and performance. The system architecture must also consider +the capability of requesters to issue multiple read transactions. + +An entry in the MSHR contains the following information: + +.. list-table:: + :widths: 5 15 80 + :header-rows: 1 + + * - Field + - Description + - Width (in bits) + * - T + - MSHR Tag + - :math:`\mathsf{HPDCACHE\_NLINE\_WIDTH - log_2(CONF\_HPDCACHE\_MSHR\_SETS)}` + * - R + - Request ID + - :math:`\mathsf{CONF\_HPDCACHE\_REQ\_TRANS\_ID\_WIDTH}` + * - S + - Source ID + - :math:`\mathsf{CONF\_HPDCACHE\_REQ\_SRC\_ID\_WIDTH}` + * - W + - Word Index + - :math:`\mathsf{log_2(CONF\_HPDCACHE\_CL\_WORDS})` + * - N + - Need Response + - 1 + +MSHR Implementation +''''''''''''''''''' + +In order to limit the area cost, the MSHR can be implemented using SRAM +macros. SRAM macros have higher bit density than flip-flops. + +The depth of the macros is: + + :math:`\mathsf{CONF\_HPDCACHE\_MSHR\_SETS\_PER\_RAM}` + +Multiple ways, for the same set, can be put side-by-side in the same SRAM word: + + :math:`\mathsf{CONF\_HPDCACHE\_MSHR\_WAYS\_PER\_RAM\_WORD}` + +The width (in bits) of the macros is: + + :math:`\mathsf{CONF\_HPDCACHE\_MSHR\_WAYS\_PER\_RAM\_WORD{}\times{}(T + R + S + W + 1)}` + +Finally, the total number of SRAM macros is: + +.. math:: + + \mathsf{W} &= \mathsf{CONF\_HPDCACHE\_MSHR\_WAYS}\\ + \mathsf{WR} &= \mathsf{CONF\_HPDCACHE\_MSHR\_WAYS\_PER\_RAM\_WORD}\\ + \mathsf{S} &= \mathsf{CONF\_HPDCACHE\_MSHR\_SETS}\\ + \mathsf{SR} &= \mathsf{CONF\_HPDCACHE\_MSHR\_SETS\_PER\_RAM}\\ + \text{SRAM macros} &= \mathsf{\lceil\frac{W}{WR}\rceil{}\times{}\lceil\frac{S}{SR}\rceil} + +SRAM macros shall be selected depending on the required number of entries, and +the target technology node. + +When the number of entries is low +(e.g. :math:`\mathsf{MSHR\_SETS \times MSHR\_WAYS \le 16}`), +it is generally better to implement the MSHR using flip-flops. In such +configurations, the designer may use a fully-associative configuration to +remove associativity conflicts. + + +MSHR Associativity Conflicts +'''''''''''''''''''''''''''' +The MSHR implements a set-associative organization. In such organization, the +target "set" is designated by some bits of the cacheline address. + +If there are multiple in-flight read miss requests addressing different +cachelines with the same "set", this is named an associativity conflict. When +this happens, the cache will place each read miss request in a different "way" +of the MSHR. However, if there is no available way, the request is put on hold +(case 5 in :numref:`Table %s `). + + +.. _sec_uncacheable_handler: + +Uncacheable Handler +------------------- + +This block is responsible for processing uncacheable load and store requests +(see :ref:`sec_req_cacheability`), as well as atomic requests (regardless of +whether they are cacheable or not). For more information about atomic requests +see :ref:`sec_amo`. + +If :math:`\scriptsize\mathsf{CONF\_HPDCACHE\_SUPPORT\_AMO}` is set to 0, the +HPDcache does not support AMOs from requesters. In this case, the AMO has no +effect and the Uncacheable Handler responds with an error to the corresponding +requester (when ``core_req_i.need_rsp`` is set to 1). If ``core_req_i.need_rsp`` +is set to 0, the Uncacheable Handler ignores the AMO. + +All requests handled by this block produce a request to the memory. These +requests to the memory are issued through the CMI interfaces. Uncacheable read +requests are forwarded to the memory through the CMI read. Uncacheable write +requests or atomic requests are forwarded through the CMI write. + + +.. _sec_cmo_handler: + +Cache Management Operation (CMO) Handler +---------------------------------------- + +This block is responsible for handling CMOs. CMOs are special requests from +requesters that address the cache itself, and not the memory or a peripheral. +CMOs allow to invalidate or prefetch designated cachelines, or produce explicit +memory read and write fences. + +The complete list of supported CMOs is detailed in :ref:`sec_cmo`. + +If :math:`\scriptsize\mathsf{CONF\_HPDCACHE\_SUPPORT\_CMO}` is set to 0, the +HPDcache does not support CMOs from requesters. In this case, the CMO has no +effect and the CMO Handler responds with an error to the corresponding requester +(when ``core_req_i.need_rsp`` is set to 1). If ``core_req_i.need_rsp`` is set to +0, the CMO Handler ignores the CMO. + + +.. _sec_rtab: + +Replay Table (RTAB) +------------------- + +The RTAB is implemented as an array of linked lists. It is a fully-associative +multi-entry buffer where each valid entry belongs to a linked list. It is +implemented in flip-flops. Each linked lists contain requests that target the +same cacheline. There can be multiple linked lists but each shall target a +different cacheline. The head of each linked list contains the oldest request +while the tail contains the newest request. The requests are processed from the +head to the tail to respect the rules explained in section :ref:`sec_mcrs`. + +Regarding the pop operation (extracting a ready request from the replay table), +it is possible that once the request is replayed, some of the resources it needs +are again busy. Therefore, the request needs to be put on-hold again. In this +case, the request needs to keep its position as head of the linked list. This is +to preserve the program order. This is the reason why the pop operation is +implemented as a two-step operation: pop then commit, or pop then rollback. The +commit operation allows to actually retire the request, while the rollback +allows to undo the pop. + +An entry of the RTAB has the following structure (LL means Linked List): + +.. list-table:: + :widths: 10 40 50 + :header-rows: 1 + + * - **Field** + - **Description** + - **Width (in bits)** + * - Request + - Contains the on-hold request from the core (including data) + - :math:`\mathsf{\approx{}200}` + * - LL tail + - Indicates if the entry is the tail of a linked list + - :math:`\mathsf{1}` + * - LL head + - Indicates if the entry is the head of a linked list + - :math:`\mathsf{1}` + * - LL next + - Designates the next (older) request in the linked list + - :math:`\mathsf{log_2(CONF\_HPDCACHE\_RTAB\_ENTRIES)}` + * - Deps + - Indicates the kind of dependency that keeps the request on-hold. + - :math:`\mathsf{5}` + * - Valid + - Indicates if the entry is valid (if unset the entry is free). + - :math:`\mathsf{1}` + + +The following table briefly describes the possible dependencies between +memory requests. For each kind of dependency, there is a corresponding +bit in the "deps bits" field of RTAB entries. + +.. list-table:: + :widths: 25 75 + :header-rows: 1 + + * - **Dependency** + - **Description** + * - MSHR Hit + - Read miss and there is an outstanding miss request on the target address + * - MSHR Full + - Read miss and the MSHR has no available way for the requested address + * - MSHR Not Ready + - Read miss and the MSHR is busy and cannot send the miss request + * - WBUF Hit + - Read miss and there is a match with an open, pending, or sent entry in + the write buffer + * - WBUF Not Ready + - Write miss and there is a match with a sent entry in the write buffer or + the write-buffer is full + + +RTAB operations +''''''''''''''' + +The RTAB implements the following operations: + ++--------------------------+----------------------------------+ +| **Operation** | **Description** | ++==========================+==================================+ +| ``rtab_alloc`` | Allocate a new linked list | ++--------------------------+----------------------------------+ +| ``rtab_alloc_and_link`` | Allocate a new entry and link it | +| | to an existing linked list | ++--------------------------+----------------------------------+ +| ``rtab_pop_try`` | Get a ready request from one of | +| | the linked list (wihout actually | +| | removing it from the list) | ++--------------------------+----------------------------------+ +| ``rtab_pop_commit`` | Actually remove a popped request | +| | from the list | ++--------------------------+----------------------------------+ +| ``rtab_pop_rollback`` | Rollback a previously popped | +| | request (with a possible update | +| | of its dependencies) | ++--------------------------+----------------------------------+ +| ``rtab_find_ready`` | Find a ready request among the | +| | heads of valid linked lists | ++--------------------------+----------------------------------+ +| ``rtab_update_deps`` | Update the dependency bits of | +| | valid requests | ++--------------------------+----------------------------------+ +| ``rtab_find_empty`` | Find an empty request | ++--------------------------+----------------------------------+ +| ``rtab_is_full`` | Is the RTAB full ? | ++--------------------------+----------------------------------+ +| ``rtab_is_empty`` | Is the RTAB empty ? | ++--------------------------+----------------------------------+ +| ``rtab_match_tail`` | Find a 'tail' entry matching a | +| | given nline | ++--------------------------+----------------------------------+ +| ``rtab_match`` | Find an entry matching a given | +| | nline | ++--------------------------+----------------------------------+ + +The C-like functions below briefly describe the algorithms implemented in the +RTL code of the RTAB. + +The following function is called by the cache controller when it detects that +one or more of the conditions in :ref:`tab_onhold` is met, and the request shall +be put on hold. + +.. code:: c + + int rtab_alloc(req_t r, deps_t d) + { + int index = rtab_find_empty(); + rtab[index] = { + valid : 1, + deps : d, + ll_head : 1, + ll_tail : 1, + ll_next : 0, + request : r + }; + return index; + } + +The following function is called by the cache controller when it detects that +the request in the pipeline targets the same cacheline that one or more on-hold +requests. In this case, the request is linked in the tail of the corresponding list in the RTAB. + +.. code:: c + + int rtab_alloc_and_link(req_t r) + { + int index = rtab_find_empty(); + int match = rtab_match_tail(get_nline(r)); + + // replace the tail of the linked list + rtab[match].ll_tail = 0; + + // make the next pointer of the old tail to point to the new entry + rtab[match].ll_next = index; + + // add the new request as the tail of the linked list + rtab[index] = { + valid : 1, + deps : 0, + ll_head : 0, + ll_tail : 1, + ll_next : 0, + request : r + }; + + return index; + } + +The following function is called by the cache controller to select a ready +request (dependencies have been resolved) from the RTAB. + +.. code:: c + + req_t rtab_pop_try() + { + // These are global states (preserved between function calls) + static int pop_state = HEAD; + static int last = 0; + static int next = 0; + + int index; + + // Brief description of the following code: + // The rtab_pop_try function tries to retire all the requests of a given + // linked list. Then it passes to another one. + switch (pop_state) { + case HEAD: + // Find a list whose head request is ready + // (using a round-robin policy) + index = rtab_find_ready(last); + + // If there is no ready entry, or there is an ongoing rollback, + // return an invalid index + if ((index == -1) || rtab_pop_rollback) + return -1; + + // Update the pointer to the last linked list served + last = index; + + // If the list have more than one request, the next time this function + // is called, serve the next request of the list + if (!rtab[index].ll_tail) { + next = rtab[index].ll_next; + pop_state = NEXT; + } + + // Temporarily unset the head bit. This is to prevent the + // request to be rescheduled. + rtab[index].ll_head = 0; + break; + + case NEXT: + index = next; + + // If there is an ongoing rollback, abandon the current pop + if (rtab_pop_rollback) { + pop_state = HEAD; + return -1; + } + + // If the list have more than one request, the next time this function + // is called, serve the next request of the list + if (!rtab[next].ll_tail) { + next = rtab[index].ll_next; + pop_state = NEXT; + } + // It it is the last element of the list, return to the HEAD state + else { + pop_state = HEAD; + } + + // Temporarily unset the head bit. This is to prevent the + // request to be rescheduled. + rtab[index].ll_head = 0; + } + + // Pop the selected request + return rtab[index].req; + } + +The following function is called by the cache controller when the replayed +request is retired (processed). + +.. code:: c + + void rtab_pop_commit(int index) + { + rtab[index].valid = 0; + } + +The following function is called by the cache controller when the replayed +request cannot be retired because, again, one or more of the conditions in +:ref:`tab_onhold` is met. In this case, the request is restored into the RTAB +with updated dependency bits. The restored request keeps the same position it +its corresponding linked list to respect the program execution order. + +.. code:: c + + void rtab_pop_rollback(int index, bitvector deps) + { + rtab[index].ll_head = 1; + rtab[index].deps = deps; + } + + +The following function is used to find a linked list whose head request can be +replayed (dependencies have been resolved). + +.. code:: c + + int rtab_find_ready(int last) + { + // choose a ready entry using a round-robin policy + int i = (last + 1) % RTAB_NENTRIES; + for (;;) { + // ready entry found + if (rtab[i].valid && rtab[i].ll_head && (rtab[i].deps == 0)) + return i; + + // there is no ready entry + if (i == last) + return -1; + + i = (i + 1) % RTAB_NENTRIES; + } + } + + +The following function is called by the miss hander and the write buffer on the +completion of any pending transaction. It allows to update the dependency bits +of any matching request (with the same cacheline address) in the RTAB. + +.. code:: c + + void rtab_update_deps(nline_t nline, bitvector deps) + { + int index = rtab_match(nline); + if (index != -1) { + rtab[index].deps = deps; + } + } + + +The following utility functions are used by functions above. + +.. code:: c + + int rtab_find_empty() + { + for (int i = 0; i < RTAB_NENTRIES; i++) + if (!rtab[i].valid) + return i; + + return -1; + } + +.. code:: c + + bool rtab_is_full() + { + return (rtab_find_empty() == -1); + } + + +.. code:: c + + int rtab_is_empty() + { + for (int i = 0; i < RTAB_NENTRIES; i++) + if (rtab[i].valid) + return 0; + + return 1; + } + + +.. code:: c + + int rtab_match_tail(nline_t nline) + { + for (int i = 0; i < RTAB_NENTRIES; i++) + if (rtab[i].valid && get_nline(rtab[i].req) == nline && rtab[i].ll_tail) + return i; + + return -1; + } + + +.. code:: c + + int rtab_match(nline_t nline) + { + for (int i = 0; i < RTAB_NENTRIES; i++) + if (rtab[i].valid && get_nline(rtab[i].req) == nline) + return i; + + return -1; + } + + + +Policy for taking new requests in the data cache +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The cache has three possible sources of requests: + +- The core (new request from requesters) + +- The RTAB (on-hold requests) + +- The miss handler (refill requests) + +The cache controller implements a fixed priority policy between these sources. +It accepts requests in the following order of priority: + +#. Refill request (highest priority) + +#. On-hold request + +#. New request (lowest priority) + + +Write-buffer +------------ + +This cache implements a write-through policy. In this policy, the write accesses +from requesters are systematically transferred to the memory, regardless of +whether the write access hits or misses in the HPDcache. + +To decouple write acknowledgements from the memory to the HPDcache, and the +write acknowledgements from the HPDcache to the requester, the HPDcache +implements a write-buffer. The goal is to increase the performance: the +requester does not wait the acknowledgement from the memory, which may suffer +from a very high latency. In addition, the write-buffer implements coalescing of +write data to improve the bandwidth utilization of data channels in the NoC. + +The write-buffer implements two different parts: directory and data. The +directory enables tracking of active writes. The data buffers are used to +coalesce writes from the requesters. + +Write-Buffer Organization +''''''''''''''''''''''''' + +The write-buffer implements two flip-flop arrays: one for the directory and +another for the data. + + +Write-Buffer Directory +~~~~~~~~~~~~~~~~~~~~~~ + +The write-buffer directory allows to track pending write transactions. + +A given entry in the directory of the write-buffer may be in four +different states: + +.. list-table:: + :widths: 15 85 + :header-rows: 1 + + * - **State** + - **Description** + * - **FREE** + - The entry is available + * - **OPEN** + - The entry is valid and contains pending write data. The entry accepts + new writes (coalescing) + * - **PEND** + - The entry is waiting to be sent to the memory. In this state, the entry + continues to accept new writes (coalescing). + * - **SENT** + - The entry was forwarded to the memory, and is waiting for the + acknowledgement + + +Each entry contains the following additional information: + +.. list-table:: + :widths: 5 15 80 + :header-rows: 1 + + * - Field + - Description + - Width (in bits) + * - S + - State + - 2 + * - T + - Write-Buffer Tag + - :math:`\mathsf{HPDCACHE\_PA\_WIDTH - HPDCACHE\_WBUF\_OFFSET\_WIDTH}` + * - C + - Live counter + - :math:`\mathsf{HPDCACHE\_WBUF\_TIMECNT\_WIDTH}` + * - P + - Pointer to the associated data buffer + - :math:`\mathsf{log_2(HPDCACHE\_WBUF\_DATA\_ENTRIES)}` + + +The number of entries in the directory array is: + + :math:`\mathsf{CONF\_HPDCACHE\_WBUF\_DIR\_ENTRIES}` + +The width (in bits) of data entries is: + +Write-Buffer Data +~~~~~~~~~~~~~~~~~~~~~~ + +The number of entries (depth) of the data array is: + + :math:`\mathsf{CONF\_HPDCACHE\_WBUF\_DATA\_ENTRIES}` + +The width (in bits) of data entries is: + + :math:`\mathsf{CONF\_HPDCACHE\_WBUF\_WORDS{}\times{}HPDCACHE\_WORD\_WIDTH}` + +Data buffers may be as wide or wider than the data interface of requesters: + + :math:`\mathsf{CONF\_HPDCACHE\_WBUF\_WORDS \ge CONF\_HPDCACHE\_REQ\_WORDS}` + +Designer may choose data buffers to be wider than requesters' data interface to +improve the NoC bandwidth utilization. There is however a constraint: data +buffers' width cannot be wider than the NoC interface. Therefore: + +.. math:: + + \mathsf{CONF\_HPDCACHE\_WBUF\_WORDS{}\times{}HPDCACHE\_WORD\_WIDTH} \le\\ + \mathsf{CONF\_HPDCACHE\_MEM\_DATA\_WIDTH} + + + +Memory Write Consistency Model +'''''''''''''''''''''''''''''' + +The HPDcache complies with the RVWMO memory consistency model. Regarding writes, +in this consistency model, there are two important properties: + +#. The order in which write accesses on different addresses are forwarded to + memory MAY differ from the order they arrived from the requester (program + order); + +#. Writes onto the same address, MUST be visible in order. If there is a data + written by a write A on address @x followed by an another write B on the same + address, the data of A cannot be visible after the processing of B. + +The second property allows write coalescing if the hardware ensures that the +last write persists. + +The write-buffer exploits the first property. Multiple “in-flight” writes are +supported due to the multiple directory and data entries. These write +transactions can be forwarded to the memory in an order different from the +program order. + +To comply with the second property, the write-buffer does not accept a write +access when there is an address conflict with a **SENT** entry. In that case, +the write access is put on-hold as described in :ref:`sec_onhold`. + +The system may choose to relax the constraint of putting a write on-hold in case +of an address conflict with a **SENT** entry. This can be relaxed when the NoC +guaranties in-order delivery. The runtime configuration bit +``cfig_wbuf.S`` (see :ref:`sec_csr`) shall be set to 0 to relax this +dependency. + + +Functional Description +'''''''''''''''''''''' + +When an entry of the write-buffer directory is in the **OPEN** or **PEND** +states, there is an allocated data buffer, and it contains data that has not yet +been sent to the memory. When an entry of the write-buffer directory is in the +**SENT** state, the corresponding data was transferred to the memory, and the +corresponding data buffer was freed (and can be reused for another write). A +given entry in the write-buffer directory goes from **FREE** to **OPEN** state +when a new write is accepted, and cannot be coalesced with another **OPEN** or +**PEND** entry (i.e. not in the same address range). + +A directory entry passes from **OPEN** to **PEND** after a given number of clock +cycles. This number of clock cycles depends on different runtime configuration +values. Each directory entry contains a life-time counter. This counter starts +at 0 when a new write is accepted (**FREE** :math:`\rightarrow` **OPEN**), and +incremented each cycle while in **OPEN**. When the counter reaches +``cfig_wbuf.T`` (see :ref:`sec_csr`), the write-buffer directory +entry goes to **PEND**. Another runtime configurable bit, +``cfig_wbuf.R`` (see :ref:`sec_csr`), defines the behavior of an entry when a +new write is coalesced into an **OPEN** entry. If this last configuration bit is +set, the life-time counter is reset to 0 when a new write is coalesced. +Otherwise, the counter continues to increment normally. + +The life-time of a given write-buffer directory entry is longer than the +life-time of a data entry. A given directory entry is freed +(**SENT** :math:`\rightarrow` **FREE**) when the write acknowledgement is +received from the memory. The number of cycles to get an acknowledgement from +the memory may be significant and it is system-dependent. Thus, to improve +utilization of data buffers, the number of entries in the directory is +generally greater than the number of data buffers. There is a trade-off +between area and performance when choosing the depth of the data buffer. The +area cost of data buffers is the most critical cost in the write-buffer. The +synthesis-time parameters +:math:`\mathsf{CONF\_HPDCACHE\_WBUF\_DIR\_ENTRIES}` and +:math:`\mathsf{CONF\_HPDCACHE\_WBUF\_DATA\_ENTRIES}` define the number of +entries in the write-buffer directory and write-buffer data, respectively. + +When the ``cfig_wbuf.I`` (see :ref:`sec_csr`) is set, the write buffer does not +perform any write coalescing. This means that the entry passes from **FREE** to +**PEND** (bypassing the **OPEN** state). While an entry is in the **PEND** +state, and ``cfig_wbuf.I`` is set, that entry does not accept any new writes. It +only waits for the data to be sent. The ``cfig_wbuf.T`` is ignored by the write +buffer when ``cfig_wbuf.I`` is set. + +Memory Fences +''''''''''''' + +In multi-core systems, or more generally, in systems with multiple DMA-capable +devices, when synchronization is needed, it is necessary to implement memory +fences from the software. In the case of RISC-V, there is specific instructions +for this (i.e. fence). + +Fence instructions shall be forwarded to the cache to ensure ordering of writes. +The fence will force the write-buffer to send all pending writes before +accepting new ones. This cache implements two ways of signalling a fence: +sending a specific CMO instruction from the core (described later on +:ref:`sec_cmo`), or by asserting ``wbuf_flush_i`` pin (during one cycle). diff --git a/hw/vendor/hpdcache/docs/source/cmo.rst b/hw/vendor/hpdcache/docs/source/cmo.rst new file mode 100644 index 00000000..abaed9b1 --- /dev/null +++ b/hw/vendor/hpdcache/docs/source/cmo.rst @@ -0,0 +1,343 @@ +.. + Copyright 2024 CEA* + *Commissariat a l'Energie Atomique et aux Energies Alternatives (CEA) + + SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 + + Licensed under the Solderpad Hardware License v 2.1 (the “License”); you + may not use this file except in compliance with the License, or, at your + option, the Apache License version 2.0. You may obtain a copy of the + License at + + https://solderpad.org/licenses/SHL-2.1/ + + Unless required by applicable law or agreed to in writing, any work + distributed under the License is distributed on an “AS IS” BASIS, WITHOUT + WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + License for the specific language governing permissions and limitations + under the License. + + Authors : Cesar Fuguet + Description : HPDcache Cache Management Operations (CMOs) + +.. _sec_cmo: + +Cache Management Operations (CMOs) +================================== + +The HPDcache is able of performing the following Cache Management Operations +(CMOs): + +- Memory write fence + +- Invalidate a cacheline given its address + +- Invalidate all cachelines + +- Prefetch the cacheline indicated given its physical address + +Any of the clients of the HPDCACHE can trigger one of this operation anytime by +using specific opcodes in their request (see +:numref:`Table %s `). + +.. _tab_cmo_optypes: + +.. list-table:: CMO Operation Types + :widths: 25 15 60 + :align: center + :header-rows: 1 + + * - **Mnemonic** + - **Encoding** + - **Description** + * - :math:`\small\mathsf{HPDCACHE\_CMO\_FENCE}` + - :math:`\small\mathsf{0b000}` + - Memory Write Fence + * - :math:`\small\mathsf{HPDCACHE\_CMO\_INVAL\_NLINE}` + - :math:`\small\mathsf{0b010}` + - Invalidate a Cacheline given its Address + * - :math:`\small\mathsf{HPDCACHE\_CMO\_INVAL\_ALL}` + - :math:`\small\mathsf{0b100}` + - Invalidate All Cachelines + * - :math:`\small\mathsf{HPDCACHE\_CMO\_PREFETCH}` + - :math:`\small\mathsf{0b101}` + - Prefetch a Cacheline given its Address + + +The ``core_req_i.op`` must be set to ``HPDCACHE_REQ_CMO`` +(see :numref:`Table %s `). The CMO subtype +(:numref:`Table %s `) is transferred into the +``core_req_i.size`` signal of the request. + +If :math:`\scriptsize\mathsf{CONF\_HPDCACHE\_SUPPORT\_CMO}` is set to 0, the +HPDcache does not support CMOs from requesters. +See :ref:`sec_cmo_handler` for more details. + +The following sections describe in detail each of the CMO operations, +and how the requests shall be encoded to trigger each of them. + + +Memory Write Fence +------------------ + +To make sure that the HPDcache accepts new requests only when all previous +writes are sent and acknowledged from the memory, a requester can issue a fence +operation. This is useful to ensure memory consistency in multi-core systems. It +may also be necessary to ensure such consistency between the core and other +peripherals with DMA capability. + +The default consistency model of RISC-V is a Weak Memory Ordering (WMO) model +(RVWMO). In this model, the system is able to reorder memory transactions with +respect to the program order. There are however some constraints detailed in +[RISCVUP2019]_ and briefly described in :ref:`sec_mcrs`. + +To issue a memory fence, the requester shall build the request as follows: + +.. list-table:: CMO Memory Write Fence Request Formatting + :widths: 30 50 + :align: center + :header-rows: 1 + + * - **Signal** + - **Value** + * - ``core_req_o.addr_offset`` + - *Don't Care* + * - ``core_req_o.op`` + - :math:`\small\mathsf{HPDCACHE\_REQ\_CMO}` + * - ``core_req_o.wdata`` + - *Don't Care* + * - ``core_req_o.be`` + - *Don't Care* + * - ``core_req_o.size`` + - :math:`\small\mathsf{HPDCACHE\_CMO\_FENCE}` + * - ``core_req_o.sid`` + - Corresponding source ID of the requester + * - ``core_req_o.tid`` + - Transaction ID of the request + * - ``core_req_o.need_rsp`` + - Indicates if the requester needs an acknowledgement when the operation is + completed. + * - ``core_req_o.phys_indexed`` + - *Don't Care* + * - ``core_req_o.addr_tag`` + - *Don't Care* + * - ``core_req_o.pma.uncacheable`` + - *Don't Care* + * - ``core_req_o.pma.io`` + - *Don't Care* + * - ``core_req_tag_i`` + - *Don't Care* + * - ``core_req_pma_i.uncacheable`` + - *Don't Care* + * - ``core_req_pma_i.io`` + - *Don't Care* + + +As for any regular request, the request shall follow the **VALID**/**READY** +handshake protocol described in :ref:`sec_ready_valid_handshake`. + +This memory fence operation has the following effects: + +- All open entries in the write buffer (write requests waiting to be sent to the + memory) are immediately closed; + +- No new requests from any requester are acknowledged until all pending write + requests in the cache have been acknowledged on the NoC interface. + +When the memory fence transaction is completed, and the ``core_req_o.need_rsp`` +signal was set to 1, an acknowledgement is sent to the corresponding requester. + + +Invalidate a Cacheline Given its Address +---------------------------------------- + +The program may need to invalidate cachelines to ensure cache coherency by +software. This may be needed in both multi-core systems or systems with +DMA-capable peripherals. + +To invalidate a cacheline given its address, the requester shall build the +request as follows: + +.. list-table:: CMO Cacheline Invalidation given its Address Request Formatting + :widths: 30 50 + :align: center + :header-rows: 1 + + * - **Signal** + - **Value** + * - ``core_req_o.addr_offset`` + - Least significant bits of the address + * - ``core_req_o.op`` + - :math:`\small\mathsf{HPDCACHE\_REQ\_CMO}` + * - ``core_req_o.wdata`` + - *Don't Care* + * - ``core_req_o.be`` + - *Don't Care* + * - ``core_req_o.size`` + - :math:`\small\mathsf{HPDCACHE\_CMO\_INVAL\_NLINE}` + * - ``core_req_o.sid`` + - Corresponding source ID of the requester + * - ``core_req_o.tid`` + - Transaction ID of the request + * - ``core_req_o.need_rsp`` + - Indicates if the requester needs an acknowledgement when the operation is + completed. + * - ``core_req_o.phys_indexed`` + - 1 if physical indexing, 0 if virtual indexing + * - ``core_req_o.addr_tag`` + - Most significant bits of the address if ``core_req_o.phys_indexed = 1``, + *Don't Care* otherwise + * - ``core_req_o.pma.uncacheable`` + - *Don't Care* + * - ``core_req_o.pma.io`` + - *Don't Care* + * - ``core_req_tag_i`` + - Most significant bits of the address if ``core_req_o.phys_indexed = 0``, + *Don't Care* otherwise + * - ``core_req_pma_i.uncacheable`` + - *Don't Care* + * - ``core_req_pma_i.io`` + - *Don't Care* + + +As for any regular request, the request shall follow the **VALID**/**READY** +handshake protocol (see :ref:`sec_ready_valid_handshake`). +This CMO request supports both virtual or physical indexed requests (see +:ref:`sec_vipt`). + +Regarding the latency of this operation, only one cycle is needed to invalidate +the corresponding cacheline. However, if there is a pending read miss on the +target address, the HPDcache waits for the response of the read miss then +invalidates the corresponding cacheline. + +If the target address is not cached, the operation does nothing. + +When the invalidation transaction is completed, and the ``core_req_o.need_rsp`` +signal was set to 1, an acknowledgement is sent to the corresponding requester. + + +Invalidate All Cachelines +------------------------- + +With this operation, all the cachelines in the HPDcache are invalidated. + +The requester shall build the request as follows to perform a complete +invalidation of the HPDcache: + +.. list-table:: CMO All Cachelines Invalidation + :widths: 30 50 + :align: center + :header-rows: 1 + + * - **Signal** + - **Value** + * - ``core_req_o.addr_offset`` + - *Don't Care* + * - ``core_req_o.op`` + - :math:`\small\mathsf{HPDCACHE\_REQ\_CMO}` + * - ``core_req_o.wdata`` + - *Don't Care* + * - ``core_req_o.be`` + - *Don't Care* + * - ``core_req_o.size`` + - :math:`\small\mathsf{HPDCACHE\_CMO\_INVAL\_ALL}` + * - ``core_req_o.sid`` + - Corresponding source ID of the requester + * - ``core_req_o.tid`` + - Transaction ID of the request + * - ``core_req_o.need_rsp`` + - Indicates if the requester needs an acknowledgement when the operation is + completed. + * - ``core_req_o.phys_indexed`` + - *Don't Care* + * - ``core_req_o.addr_tag`` + - *Don't Care* + * - ``core_req_o.pma.uncacheable`` + - *Don't Care* + * - ``core_req_o.pma.io`` + - *Don't Care* + * - ``core_req_tag_i`` + - *Don't Care* + * - ``core_req_pma_i.uncacheable`` + - *Don't Care* + * - ``core_req_pma_i.io`` + - *Don't Care* + + +As for any regular request, the request shall follow the **VALID**/**READY** +handshake protocol (see :ref:`sec_ready_valid_handshake`). + +This operation works as a memory read fence. This is, before handling the +operation, the HPDcache waits for all pending read misses to complete. + +Regarding the latency of this operation, it has two aggregated components: + +- The time to serve all pending reads. + +- One cycle per set implemented in the HPDcache (all ways of a given set are + invalidated simultaneously). + +When the invalidation transaction is completed, and the ``core_req_o.need_rsp`` +signal was set to 1, an acknowledgement is sent to the corresponding requester. + + +Prefetch a Cacheline given its Address +-------------------------------------- + +With this operation, the cacheline corresponding to the indicated address is +prefetched into the HPDcache. + +The requester shall build the request as follows to perform a prefetch: + +.. list-table:: CMO All Cachelines Invalidation + :widths: 30 50 + :align: center + :header-rows: 1 + + * - **Signal** + - **Value** + * - ``core_req_o.addr_offset`` + - Least significant bits of the address + * - ``core_req_o.op`` + - :math:`\small\mathsf{HPDCACHE\_REQ\_CMO}` + * - ``core_req_o.wdata`` + - *Don't Care* + * - ``core_req_o.be`` + - *Don't Care* + * - ``core_req_o.size`` + - :math:`\small\mathsf{HPDCACHE\_CMO\_PREFETCH}` + * - ``core_req_o.sid`` + - Corresponding source ID of the requester + * - ``core_req_o.tid`` + - Transaction ID of the request + * - ``core_req_o.need_rsp`` + - Indicates if the requester needs an acknowledgement when the operation is + completed. + * - ``core_req_o.phys_indexed`` + - 1 if physical indexing, 0 if virtual indexing + * - ``core_req_o.addr_tag`` + - Most significant bits of the address if ``core_req_o.phys_indexed = 1``, + *Don't Care* otherwise + * - ``core_req_o.pma.uncacheable`` + - *Don't Care* + * - ``core_req_o.pma.io`` + - *Don't Care* + * - ``core_req_tag_i`` + - Most significant bits of the address if ``core_req_o.phys_indexed = 0``, + *Don't Care* otherwise + * - ``core_req_pma_i.uncacheable`` + - *Don't Care* + * - ``core_req_pma_i.io`` + - *Don't Care* + + +As for any regular request, the request shall follow the **VALID**/**READY** +handshake protocol (see :ref:`sec_ready_valid_handshake`). This CMO request +supports both virtual or physical indexed requests (see :ref:`sec_vipt`). + +If the requested cacheline is already in the cache this request has no effect. +If the requested cacheline is not present in the cache, the cacheline is fetched +from the memory and replicated into the cache. + +When the prefetch transaction is completed, and the ``core_req_o.need_rsp`` +signal was set to 1, an acknowledgement is sent to the corresponding requester. diff --git a/hw/vendor/hpdcache/docs/source/conf.py b/hw/vendor/hpdcache/docs/source/conf.py new file mode 100644 index 00000000..b6ba5f92 --- /dev/null +++ b/hw/vendor/hpdcache/docs/source/conf.py @@ -0,0 +1,71 @@ +# +# Copyright 2024 CEA* +# *Commissariat a l'Energie Atomique et aux Energies Alternatives (CEA) +# +# SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 +# +# Licensed under the Solderpad Hardware License v 2.1 (the “License”); you +# may not use this file except in compliance with the License, or, at your +# option, the Apache License version 2.0. You may obtain a copy of the +# License at +# +# https://solderpad.org/licenses/SHL-2.1/ +# +# Unless required by applicable law or agreed to in writing, any work +# distributed under the License is distributed on an “AS IS” BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +# Authors : Cesar Fuguet +# Description : Configuration file for the Sphinx documentation builder +# + +# -- Project information ----------------------------------------------------- + +project = 'HPDcache User Guide' +copyright = '2023-present Commissariat a l\'Energie Atomique et aux Energies Alternatives (CEA)' +author = 'César Fuguet' + +# The full version, including alpha/beta/rc tags +release = 'v1.0.1' + + +# -- General configuration --------------------------------------------------- + +# Add any Sphinx extension module names here, as strings. They can be +# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom +# ones. +extensions = [ + 'sphinx.ext.todo', + 'sphinx.ext.ifconfig', + 'sphinx.ext.graphviz', + 'sphinx.ext.autosectionlabel' +] + +# Add any paths that contain templates here, relative to this directory. +templates_path = ['_templates'] + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +# This pattern also affects html_static_path and html_extra_path. +exclude_patterns = [] + +# Number figures, tables and code-blocks automatically +numfig = True + + +# -- Options for HTML output ------------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +# +html_theme = 'sphinx_rtd_theme' + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +html_static_path = ['_static'] + +def setup(app): + app.add_css_file("theme_overrides.css") diff --git a/hw/vendor/hpdcache/docs/source/csrs.rst b/hw/vendor/hpdcache/docs/source/csrs.rst new file mode 100644 index 00000000..12bd4f97 --- /dev/null +++ b/hw/vendor/hpdcache/docs/source/csrs.rst @@ -0,0 +1,349 @@ +.. + Copyright 2024 CEA* + *Commissariat a l'Energie Atomique et aux Energies Alternatives (CEA) + + SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 + + Licensed under the Solderpad Hardware License v 2.1 (the “License”); you + may not use this file except in compliance with the License, or, at your + option, the Apache License version 2.0. You may obtain a copy of the + License at + + https://solderpad.org/licenses/SHL-2.1/ + + Unless required by applicable law or agreed to in writing, any work + distributed under the License is distributed on an “AS IS” BASIS, WITHOUT + WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + License for the specific language governing permissions and limitations + under the License. + + Authors : Cesar Fuguet + Description : HPDcache Control-Status Registers (CSRs) + +.. _sec_csr: + +Control-Status Registers (CSRs) +=============================== + +Dedicated CSR address space +--------------------------- + +The HPDcache defines a dedicated memory address space for configuring and +checking the internal status. This memory space is shared among all the +requesters connected to the same HPDcache. However, this space is private to +those requesters in a system-wide point of view. This is, this dedicated CSR +address space is not visible to other requesters integrated in the system. + +The dedicated CSR address space is aligned to 4 Kibytes and has this same size. +Current version of the HPDcache uses a very small subset of this address space, +but the aligning to 4 Kibytes, allows easier mapping in the virtual address +space by the OS. The smallest virtual/physical page size defined in the +[RISCVP2019]_ is 4 Kibytes. :numref:`Figure %s ` displays +the layout of the dedicated CSR address space of the HPDcache. + +The :math:`\mathsf{CFIG\_BASE}` address is specified through an input port of +the HPDcache. The name of this input pin is ``cfig_base_i``. It is a multi-bit +signal. The number of bits is :math:`\mathsf{CONF\_HPDCACHE\_PA\_WIDTH}`. As +mentioned above, as this segment must be aligned to 4 KiB, the 12 least +significant bits must be all 0. + +.. _fig_csr_addr_space: + +.. figure:: images/hpdcache_csr_addr_space.* + :alt: HPDcache CSR Dedicated Address Space + :align: center + :figwidth: 100% + :width: 100% + + HPDcache CSR Dedicated Address Space + + +Configuration registers +----------------------- + +:numref:`Table %s ` lists the configuration registers implemented +in the HPDcache. + +These are mapped on the :math:`\mathsf{CFIG}` memory address segment +(:numref:`Figure %s `). + + +.. _tab_csr_cfg: + +.. list-table:: Configuration Registers in the HPDcache + :widths: 5 60 35 + :header-rows: 2 + + * - **CFIG Segment** + - + - + * - **Register** + - **Description** + - **Base address** + * - ``cfig_version`` + - 64-bits register with cache version + - ``cfig_base_i`` + 0x00 + * - ``cfig_info`` + - 64-bits register with cache information (part I) + - ``cfig_base_i`` + 0x08 + * - ``cfig_info2`` + - 64-bits register with cache information (part II) + - ``cfig_base_i`` + 0x10 + * - ``cfig_cachectrl`` + - 64-bits register to configure the cache controller + - ``cfig_base_i`` + 0x18 + * - ``cfig_wbuf`` + - 64-bits register to configure the write buffer + - ``cfig_base_i`` + 0x20 + + +cfig_version +'''''''''''' + +.. list-table:: Configuration - Version Register + :widths: 10 15 35 5 30 + :header-rows: 1 + + * - Bits + - Field + - Description + - Mode + - Reset Value + * - [15:0] + - MinorID + - Minor Version ID of the HPDcache + - RO + - :math:`\small\mathsf{0x0001}` + * - [31:16] + - MajorID + - Major Version ID of the HPDcache + - RO + - :math:`\small\mathsf{0x0001}` + * - [47:32] + - IpID + - IP ID of the HPDcache + - RO + - :math:`\small\mathsf{0x0001}` + * - [63:48] + - VendorID + - Vendor ID + - RO + - :math:`\small\mathsf{0x0001}` + + +cfig_info +''''''''' + +.. list-table:: Configuration - Info Register + :widths: 10 15 35 5 30 + :header-rows: 1 + + * - Bits + - Field + - Description + - Mode + - Reset Value + * - [15:0] + - Sets + - Number of sets in the cache (one-based) + - RO + - :math:`\scriptsize\mathsf{CONF\_HPDCACHE\_SETS - 1}` + * - [23:16] + - Ways + - Number of ways in the cache (one-based) + - RO + - :math:`\scriptsize\mathsf{CONF\_HPDCACHE\_WAYS - 1}` + * - [27:24] + - ClBytes + - Number of bytes per cacheline (power of 2) + - RO + - :math:`\scriptsize\mathsf{log_2(CONF\_HPDCACHE\_CL\_WIDTH/8)}` + * - [39:32] + - MSHRSets + - Number of sets in the MSHR (one-based) + - RO + - :math:`\scriptsize\mathsf{CONF\_HPDCACHE\_MSHR\_SETS - 1}` + * - [47:40] + - MSHRWays + - Number of ways in the MSHR (one-based) + - RO + - :math:`\scriptsize\mathsf{CONF\_HPDCACHE\_MSHR\_WAYS - 1}` + +cfig_info2 +'''''''''' + +.. list-table:: Configuration - Info 2 Register + :widths: 10 15 35 5 30 + :header-rows: 1 + + * - Bits + - Field + - Description + - Mode + - Reset Value + * - [7:0] + - RTAB + - Number of entries in the RTAB (one-based) + - RO + - :math:`\scriptsize\mathsf{CONF\_HPDCACHE\_RTAB\_ENTRIES - 1}` + * - [23:16] + - WBufDir + - Number of entries in the directory of the Write Buffer (one-based) + - RO + - :math:`\scriptsize\mathsf{CONF\_HPDCACHE\_WBUF\_DIR\_ENTRIES - 1}` + * - [31:24] + - WBufData + - Number of entries in the data buffer of the Write Buffer (one-based) + - RO + - :math:`\scriptsize\mathsf{CONF\_HPDCACHE\_WBUF\_DATA\_ENTRIES - 1}` + * - [35:32] + - WBufBytes + - Number of bytes per Write-Buffer Data Entry (power of 2) + - RO + - :math:`\scriptsize\mathsf{log_2(CONF\_HPDCACHE\_WBUF\_WIDTH/8)}` + + +.. _sec_cfig_cachectrl: + +cfig_cachectrl +'''''''''''''' + +.. list-table:: Configuration - Cache Controller Register + :widths: 10 15 35 5 30 + :header-rows: 1 + + * - Bits + - Field + - Description + - Mode + - Reset Value + * - [0:0] + - E + - Cache Enable - When set to 0, all memory accesses are considered + uncacheable + - RW + - :math:`\small\mathsf{0}` + * - [8:8] + - P + - Performance Counters Enable - When set to 1, performance counter count + events + - RW + - :math:`\small\mathsf{1}` + * - [56:56] + - R + - Single-Entry RTAB (fallback mode) - When set to 1, the cache controller + only uses one entry of the Replay Table. + - RW + - :math:`\small\mathsf{0}` + + +cfig_wbuf +''''''''' + +.. list-table:: Configuration - Write Buffer Register + :widths: 10 15 35 5 30 + :header-rows: 1 + + * - Bits + - Field + - Description + - Mode + - Reset Value + * - [0:0] + - R + - Reset time-counter on write - When set to 1, write accesses restart the + time-counter to 0 of the used write-buffer entry + - RW + - :math:`\small\mathsf{1}` + * - [1:1] + - S + - Sequential Write after Write - When set to 1, the write buffer stalls + write accesses that collide with an in-flight write transaction (SENT). + - RW + - :math:`\small\mathsf{0}` + * - [2:2] + - I + - Inhibit Write Coalescing - When set to 1, entries in the write-buffer go + from the FREE state to the PEND state directly (bypassing the OPEN + state). Moreover, no coalescing is accepted while the entry is in the + PEND state. + - RW + - :math:`\small\mathsf{0}` + * - [15:8] + - T + - Time-counter Threshold - This field defines the time-counter threshold on + which open write-buffer entries (OPEN) go to the pending state (PEND) + - RW + - :math:`\small\mathsf{3}` + +.. _sec_perf_counters: + +Performance counters +-------------------- + +The HPDcache provides a set of performance counters. These counters provide +information that can be used by software developers, at OS level or +user application level, to, for example, debug performance issues. + +These counters are implemented in the HPDcache only if +:math:`\scriptsize\mathsf{CONF\_HPDCACHE\_SUPPORT\_PERF}` is set to 1. If this +configuration parameter is set to 0, any read or write to performance counters +is ignored and a response with an error is sent to the corresponding requester +when ``core_req_i.need_rsp`` is set to 1. + +Performance counters are incremented automatically by the hardware when the +corresponding event is triggered and the ``cfig_cachectrl.P`` +(see :ref:`sec_cfig_cachectrl`) bit is set to 1. + +:numref:`Table %s ` lists the performance counters provided +by the HPDcache. These are mapped on the :math:`\mathsf{PERF}` memory address +segment (:numref:`Figure %s `). + +.. _tab_perf_counters: + +.. list-table:: Performance Counters in the HPDcache + :widths: 20 50 30 + :header-rows: 1 + + * - **Counter** + - **Description** + - **Base Address** + * - ``perf_write_cnt`` + - 64-bit counter for processed write requests + - ``cfg_base_i`` + 0x400 + * - ``perf_read_cnt`` + - 64-bit counter for processed read requests + - ``cfg_base_i`` + 0x408 + * - ``perf_prefetch_cnt`` + - 64-bit counter for processed prefetch requests + - ``cfg_base_i`` + 0x410 + * - ``perf_uncached_cnt`` + - 64-bit counter for processed uncached requests + - ``cfg_base_i`` + 0x418 + * - ``perf_cmo_cnt`` + - 64-bit counter for processed CMO requests + - ``cfg_base_i`` + 0x420 + * - ``perf_accepted_cnt`` + - 64-bit counter for processed requests + - ``cfg_base_i`` + 0x428 + * - ``perf_write_miss_cnt`` + - 64-bit counter for write cache misses + - ``cfg_base_i`` + 0x430 + * - ``perf_read_miss_cnt`` + - 64-bit counter for read cache misses + - ``cfg_base_i`` + 0x438 + * - ``perf_onhold_cnt`` + - 64-bit counter for requests put on-hold + - ``cfg_base_i`` + 0x440 + * - ``perf_onhold_mshr_cnt`` + - 64-bit counter for requests put on-hold because of MSHR conflicts + - ``cfg_base_i`` + 0x448 + * - ``perf_onhold_wbuf_cnt`` + - 64-bit counter for requests put on-hold because of WBUF conflicts + - ``cfg_base_i`` + 0x450 + * - ``perf_onhold_rollback_cnt`` + - 64-bit counter for requests put on-hold (again) after a rollback + - ``cfg_base_i`` + 0x458 + * - ``perf_stall_cnt`` + - 64-bit counter for stall cycles (cache does not accept a request) + - ``cfg_base_i`` + 0x460 diff --git a/hw/vendor/hpdcache/docs/source/images/hpdcache_core.pdf b/hw/vendor/hpdcache/docs/source/images/hpdcache_core.pdf new file mode 100644 index 0000000000000000000000000000000000000000..e09292cd61a3e9a03f9bbef744e0b6500715c406 GIT binary patch literal 49948 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 + + + + HPDcache Core + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + HPDcache Core + February, 2023 + + + Cesar Fuguet + + + + + Commissaria a l'Energie Atomique et aux Energies Alternatives (CEA) + + + + + + + + English + + + + + + + + + + + + Miss Handler + + + + + + + + + +  Cache Directory and Data + + + + + + + + + + + + + + + + + + ways + + + sets + + v tag + + + + + = + + + + + + data to processor + + + + + + + + + tags + + @tag + hit + + + + + + + valids + @set + + way1 + way0 + + + + sets xwords/x_cuts + + {@set,@word[2]} + + + + + + + @word[1:0] + + + + + en[3] + en[2] + en[1] + en[0] + + 64 bits + + + + + @word[1:0] + + + + + + + + + way1 + way0 + + + way1 + way0 + + + way1 + way0 + + set0:word3 + set0:word2 + set0:word1 + set0:word0 + + + set0:word7 + set0:word6 + set0:word5 + set0:word4 + + + + set1:word3 + set1:word2 + set1:word1 + set1:word0 + + + set1:word7 + set1:word6 + set1:word5 + set1:word4 + + + set127:word3 + set127:word2 + set127:word1 + set127:word0 + + + set127:word7 + set127:word6 + set127:word5 + set127:word4 + + way3 + way2 + way3 + way2 + way3 + way2 + way3 + way2 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + way selection + word selection + + + + + MSHR + + + + Uncacheable & AMOHandler (UC) + + + + + + + + + + + CacheManagementOperationHandler (CMO) + + + + + + + + + + + + Cache Controller + + ARBITER + + REFILL + + + + Write Buffer(WBUF) + + + Data + + + + Dir + + + + CONFIGURATION + + READMISSREQ/RSP + HPDcache Core + CORE REQ + + CORE RSP + + + + + + ProtocolEngineStage 0 + + + + + + + + + + + + READUNCACHEDREQ/RSP + + + WRITE/AMOUNCACHEDREQUEST + + + WRITEREQUEST + + ProtocolEngineStage 1 + + + + + + + + ProtocolEngineStage 2 + + + + + + + + + + + + + + ReplayTable(RTAB) +   + + + + + + + + Arbiter + Arbiter + MEMORYREAD REQ/RSP + MEMORYWRITE REQ/RSP + + diff --git a/hw/vendor/hpdcache/docs/source/images/hpdcache_csr_addr_space.pdf b/hw/vendor/hpdcache/docs/source/images/hpdcache_csr_addr_space.pdf new file mode 100644 index 0000000000000000000000000000000000000000..b8a26854c24f26e377c764d72521a05ce5b11422 GIT binary patch literal 8270 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 literal 0 HcmV?d00001 diff --git a/hw/vendor/hpdcache/docs/source/images/hpdcache_csr_addr_space.svg b/hw/vendor/hpdcache/docs/source/images/hpdcache_csr_addr_space.svg new file mode 100644 index 00000000..b1caa0f0 --- /dev/null +++ b/hw/vendor/hpdcache/docs/source/images/hpdcache_csr_addr_space.svg @@ -0,0 +1,300 @@ + + + + + + + + + + + + + + + + + + CONFIG + + PERF + + + + + + + Reserved + Reserved + Reserved + Reserved + Reserved + Reserved + CFIG_BASE+ 0x0 + CFIG_BASE+ 0x400 + CFIG_BASE+ 0x1200 + CFIG_BASE+ 0x800 + + diff --git a/hw/vendor/hpdcache/docs/source/images/hpdcache_data_ram_organization.pdf b/hw/vendor/hpdcache/docs/source/images/hpdcache_data_ram_organization.pdf new file mode 100644 index 0000000000000000000000000000000000000000..c37f24dc28e91fa57f5892385aa852c3cc7f8f70 GIT binary patch literal 24114 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 O;nGaP!s>b&O#cHKUI)1V literal 0 HcmV?d00001 diff --git a/hw/vendor/hpdcache/docs/source/images/hpdcache_data_ram_organization.svg b/hw/vendor/hpdcache/docs/source/images/hpdcache_data_ram_organization.svg new file mode 100755 index 00000000..915dd28a --- /dev/null +++ b/hw/vendor/hpdcache/docs/source/images/hpdcache_data_ram_organization.svg @@ -0,0 +1,2344 @@ + + + + + HPDcache Data RAM Organization + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + HPDcache Data RAM Organization + February, 2023 + + + Cesar Fuguet + + + + + Commissariat a l'Energie Atomique et aux Energies Alternatives (CEA) + + + English + + + + + + + + + + + + + + + + + + + + + + ways + + + sets + + + tag + + + + set + word + + tag + @ + 64-bits address from processor + 63 + 0 + + + + + = + + + + + + data to processor + + + + + + + + + tags + + @tag + hit + + + + + + + valids + @set + + + 55 + 56 + + 2 + 5 + 6 + 12 + 13 + + + way1 + way0 + + + + + + sets xwords/x_cuts + + {@set,@word[2]} + + + + + + + @word[1:0] + + + + + en[3] + en[2] + en[1] + en[0] + + 64 bits + + + + + @word[1:0] + + + + + + + + + way1 + way0 + + + way1 + way0 + + + way1 + way0 + + set0:word3 + set0:word2 + set0:word1 + set0:word0 + + + set0:word7 + set0:word6 + set0:word5 + set0:word4 + + + + set1:word3 + set1:word2 + set1:word1 + set1:word0 + + + set1:word7 + set1:word6 + set1:word5 + set1:word4 + + + set127:word3 + set127:word2 + set127:word1 + set127:word0 + + + set127:word7 + set127:word6 + set127:word5 + set127:word4 + + way3 + way2 + way3 + way2 + way3 + way2 + way3 + way2 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + way selection + word selection + 3 + + + diff --git a/hw/vendor/hpdcache/docs/source/images/hpdcache_highlevel_integration.pdf b/hw/vendor/hpdcache/docs/source/images/hpdcache_highlevel_integration.pdf new file mode 100644 index 0000000000000000000000000000000000000000..3f6f2a9ff69db335e985a5f784957b6a805ca9bf GIT binary patch literal 28916 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 OaPMQ>xg)JAgYjRtYIlVI literal 0 HcmV?d00001 diff --git a/hw/vendor/hpdcache/docs/source/images/hpdcache_highlevel_integration.svg b/hw/vendor/hpdcache/docs/source/images/hpdcache_highlevel_integration.svg new file mode 100644 index 00000000..df808bab --- /dev/null +++ b/hw/vendor/hpdcache/docs/source/images/hpdcache_highlevel_integration.svg @@ -0,0 +1,501 @@ + + + +HPDcacheCSRsHPDcacheCore1 request/cycleMemory InterfaceWrite BuerMSHRData/DirectoryRTABHardwareMemoryPrefetcherAcceleratorLoad/StoreUnitCoreLoad/StoreUnitArbiterRISC-V Coreup to 64 bytes/cycleup to 64 bytes/channel/cycle012N-1ReadRequestWrite/AMORequest diff --git a/hw/vendor/hpdcache/docs/source/images/hpdcache_request_address_data_alignment.pdf b/hw/vendor/hpdcache/docs/source/images/hpdcache_request_address_data_alignment.pdf new file mode 100644 index 0000000000000000000000000000000000000000..a30c92fb2c0893a1c0cefc11423417d49cf00285 GIT binary patch literal 15429 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 literal 0 HcmV?d00001 diff --git a/hw/vendor/hpdcache/docs/source/images/hpdcache_request_address_data_alignment.svg b/hw/vendor/hpdcache/docs/source/images/hpdcache_request_address_data_alignment.svg new file mode 100755 index 00000000..5d9119ec --- /dev/null +++ b/hw/vendor/hpdcache/docs/source/images/hpdcache_request_address_data_alignment.svg @@ -0,0 +1,2016 @@ + + + HPDcache Request Address Data Alignment + + + + image/svg+xml + + HPDcache Request Address Data Alignment + February, 2023 + + + Cesar Fuguet + + + + + Commissariat a l'Energie Atomique et aux Energies Alternatives + + + English + + + + + + + + + @8004 + + + + + + WDATA + + BE + + + + 1 + + + + + + + + + 0 + + + + + + + + + 0xAA + + 0xBB + + 0xCC + + 0xDD + + + + + + + + + + @8008 + + 0 + + 0 + + + + + + 0 + + 0 + + + + + + 0xBB + + 0xAA + + 0x99 + + 0x88 + + 1 + + 1 + + 1 + + 1 + + 0xCC + + 0xDD + + 0xEE + + 0xFF + + 1 + + 1 + + 1 + + 1 + + XX + + XX + + + + 0 + + 0 + + 0 + + 0 + + @8009 + + + + 0 + + 56 + + 127 + + 64 + + + 8 + + 0 + + 1 + + 15 + + 8 + + 7 + + + + + + + + + + + + + + + + + + 0xAA + + + + + 1 + + + + + + + + + + + WDATA + + WDATA + + BE + + BE + + + XX + XX + XX + XX + XX + XX + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 1 + 1 + 1 + 0 + 0 + 0 + 0 + XX + XX + XX + XX + XX + XX + XX + XX + XX + XX + XX + XX + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + XX + XX + XX + XX + XX + XX + XX + XX + XX + XX + XX + XX + XX + XX + XX + 0 + + 56 + 127 + + 64 + + + 8 + 0 + + 1 + 15 + + 8 + + 7 + 0 + 56 + 127 + 64 + 8 + 0 + + 1 + 15 + + 8 + + 7 + SIZE=3 (8 bytes) + SIZE=2 (4 bytes) + SIZE=0 (1 byte) + diff --git a/hw/vendor/hpdcache/docs/source/images/hpdcache_request_arbiter.pdf b/hw/vendor/hpdcache/docs/source/images/hpdcache_request_arbiter.pdf new file mode 100644 index 0000000000000000000000000000000000000000..8c71d80d02504732641802f79d2cc5cd9bbbe15f GIT binary patch literal 16576 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 literal 0 HcmV?d00001 diff --git a/hw/vendor/hpdcache/docs/source/images/hpdcache_request_arbiter.svg b/hw/vendor/hpdcache/docs/source/images/hpdcache_request_arbiter.svg new file mode 100644 index 00000000..9fe73bba --- /dev/null +++ b/hw/vendor/hpdcache/docs/source/images/hpdcache_request_arbiter.svg @@ -0,0 +1,428 @@ + + + HPDcache Request Arbiter + + + + image/svg+xml + + HPDcache Request Arbiter + February, 2023 + + + Cesar Fuguet + + + + + Commissariat a l'Energie Atomique et aux Energies Alternatives + + + English + + + + + + + + + + + + + + + + + + + + + + + + + 0 + 1 + 2 + N-1 + + + N + HPDcache + + CSRs + + + HPDcacheCore + MemoryInterface + Requester0 + Requester1 + Requester2 + RequesterN-1 + ... + HardwareMemoryPrefetcher + 1 request/cycle + Fixed-Priority Arbiter + diff --git a/hw/vendor/hpdcache/docs/source/images/hpdcache_vipt.pdf b/hw/vendor/hpdcache/docs/source/images/hpdcache_vipt.pdf new file mode 100644 index 0000000000000000000000000000000000000000..8788175c723d6c0bd7a5dcfcfadadd18cc193fbb GIT binary patch literal 10182 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 + + + + HPDcache: VIPT support + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + HPDcache: VIPT support + October, 2023 + + + Cesar Fuguet + + + + + Commissariat a l'Energie Atomique et aux Energies Alternatives + + + + + + + + TLB + + + + + + + + + + + + + + + + CacheDir + CacheData + + LoadStoreUnit + + Arbiter + + + + + virtualindex + HPDcache + + + wayselection + + + + + + + + RSPdata + physicaltag + + + + Mux + + + + + + + + Cycle 0 + Cycle 1 + + + + + TranslationLookasideBuffer + + + = + + + + = + + + + = + + Tags + Data + + + + + + + + + + Sel + + diff --git a/hw/vendor/hpdcache/docs/source/images/wave_back_to_back.json b/hw/vendor/hpdcache/docs/source/images/wave_back_to_back.json new file mode 100755 index 00000000..c4fecb26 --- /dev/null +++ b/hw/vendor/hpdcache/docs/source/images/wave_back_to_back.json @@ -0,0 +1,10 @@ +{signal: [ + {name: 'CLK', wave: 'p....'}, + {name: 'PAYLOAD', wave: 'xx22x', data: ['dat0', 'dat1']}, + {name: 'VALID', wave: '0.1.0'}, + {name: 'READY', wave: '01..0'} +], + head:{ + tick:0 + } +} diff --git a/hw/vendor/hpdcache/docs/source/images/wave_back_to_back.pdf b/hw/vendor/hpdcache/docs/source/images/wave_back_to_back.pdf new file mode 100644 index 0000000000000000000000000000000000000000..7c3a2c3cf71cafc0447ef4364d511b1aff81eef4 GIT binary patch literal 6118 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 literal 0 HcmV?d00001 diff --git a/hw/vendor/hpdcache/docs/source/images/wave_back_to_back.svg b/hw/vendor/hpdcache/docs/source/images/wave_back_to_back.svg new file mode 100755 index 00000000..fea7140c --- /dev/null +++ b/hw/vendor/hpdcache/docs/source/images/wave_back_to_back.svg @@ -0,0 +1,4 @@ + + + +012345CLKPAYLOADdat0dat1VALIDREADY \ No newline at end of file diff --git a/hw/vendor/hpdcache/docs/source/images/wave_ready_before_valid.json b/hw/vendor/hpdcache/docs/source/images/wave_ready_before_valid.json new file mode 100755 index 00000000..fe8a4462 --- /dev/null +++ b/hw/vendor/hpdcache/docs/source/images/wave_ready_before_valid.json @@ -0,0 +1,10 @@ +{signal: [ + {name: 'CLK', wave: 'p...'}, + {name: 'PAYLOAD', wave: 'x.2x', data: ['data']}, + {name: 'VALID', wave: '0.10'}, + {name: 'READY', wave: '01.0'} +], + head:{ + tick:0 + } +} diff --git a/hw/vendor/hpdcache/docs/source/images/wave_ready_before_valid.pdf b/hw/vendor/hpdcache/docs/source/images/wave_ready_before_valid.pdf new file mode 100644 index 0000000000000000000000000000000000000000..cb1f95e40013675b473c129d78e48acbfdf3bb75 GIT binary patch literal 5869 zcma)A2|QHo_pgMEBKyv@jD4AX3?h57WGmTXn8ApdF*6uTL?ufhSxUCNAtjL|6p<7q zm3oC_-$EpcDE($g^}hXnpa1{P%)R%VIp;j*Ip?|OKHmejHZoCzsv|()s`sUx*#fzg2(!U=vmo^ z+Y~mxaD-rSTkgZ( zEAaWI8jPhUQDce%awc4V^*0r>?A`H1@ZeUDCz z@laHrK_m!vk1PJGRRdpyT}3$ikdvK)_z?Qk=>E=c9$RaZmquMxuJMc<7ha-zXtGm2 zo|pjHS|_iV=?j8xIY$d47jnVxTl5B6pHM%j7H06NT}Ae8FDW=;PHEuM$g!G19NnY0 zD@pc}RkXA2$2T{Hjqhg6J6Ps+kWQl~lDz~~9dhD&xJS-(TPy7R)D9{fzuq%!GH`u$ z;1F-KLh_Vgh7=1oE_CPR%a@wsaOch_*UKk0ymgDkfBGg~Y_hL@GIOuZB-lo2fCLI? z-`Ok{s`C6vOx{P4&ZeK9*#84}C6q2g@B_ zs9+^BViclWt+_IR`$R{FU?T43UXl8K;fmBbN5H| zkd_*iNyRz7oQ-$#_#I`7Pl^WXo40RkwT=0EI}2rtQ}}V9s-H#7dhznsK#brqpS=l5 zN{esK-K1U$j*2Kf`W?O_V_J)=$>~&Upv37dlvtP70Xw`vMy0{qC|hg;OS4TzwN{=V zud7YeMF1A15_ti=*H+tt^2!$2k{(yHq`ilvz2A!uv%qUx_ek3Hgh^xeMs-?UjcC|& z_5GPC`Mwjw9f}qY^<&H(-WQ1*U2Y^}*oQ9oKYXPb`)FsgJ!VI%pK4)8upxCjOF@qN zw>||2r^At$zwBdH;|GMfYj#g--i*l|wF^yN$OW#m`&H8v)4w4Rl-$hX;NU#x zi?dw~E_qcMUA=2pe4++rism^&-xg`Ghjo>>bDdg5?_m!TSosJx+n?utvB7*>tgWko z|0(oiC0#);#hSa)28IHxQ(Mo|I1=_>{w&6^Bz86INQkiESEkH zu*I9SVs>G?Ay2EGnjgiK@GfMnT{XrjFHX;^+#heJWWZ$*g} z`w10Z7rYnUR(h^ZRG}37pj0bolD!hwp0C(n9fXFuj-$R zH@TZvT}}7hIAhJLoMjjNLlz!BHSSKWrR|?<2#m}gT@D!z|2h{mpXI4QEQYjG4yX?3 z2CYCuwEN$F?`?Q6_X62a=R~&0K_|>bMiJXaj~61`KiRk+yh!G97xzst+#6FM-8f23 z@5Wht?<+cTF!agRk|PH{&*$qBVS+hQ5}Lm5;WH-TBmUGS?D+dj@8l*t zWttx)O;*ToCmz!om(V_uWpZ5n`DpqU?_{^(Y0Ai%cFI);33i1Umc#Ye+U?jO992rG zDAp?DAdDuMt_fcB1RW9xI#?Fm{C)Tvπ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 literal 0 HcmV?d00001 diff --git a/hw/vendor/hpdcache/docs/source/images/wave_ready_before_valid.svg b/hw/vendor/hpdcache/docs/source/images/wave_ready_before_valid.svg new file mode 100755 index 00000000..a7dd0fa1 --- /dev/null +++ b/hw/vendor/hpdcache/docs/source/images/wave_ready_before_valid.svg @@ -0,0 +1,4 @@ + + + +01234CLKPAYLOADdataVALIDREADY \ No newline at end of file diff --git a/hw/vendor/hpdcache/docs/source/images/wave_ready_when_valid.json b/hw/vendor/hpdcache/docs/source/images/wave_ready_when_valid.json new file mode 100755 index 00000000..35075278 --- /dev/null +++ b/hw/vendor/hpdcache/docs/source/images/wave_ready_when_valid.json @@ -0,0 +1,10 @@ +{signal: [ + {name: 'CLK', wave: 'p...'}, + {name: 'PAYLOAD', wave: 'x.2x', data: ['data']}, + {name: 'VALID', wave: '0.10'}, + {name: 'READY', wave: '0.10'} +], + head:{ + tick:0 + } +} diff --git a/hw/vendor/hpdcache/docs/source/images/wave_ready_when_valid.pdf b/hw/vendor/hpdcache/docs/source/images/wave_ready_when_valid.pdf new file mode 100644 index 0000000000000000000000000000000000000000..0597a44bb6f6a9355294c20abdea3ba6d87a42aa GIT binary patch literal 5866 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 literal 0 HcmV?d00001 diff --git a/hw/vendor/hpdcache/docs/source/images/wave_ready_when_valid.svg b/hw/vendor/hpdcache/docs/source/images/wave_ready_when_valid.svg new file mode 100755 index 00000000..587f606e --- /dev/null +++ b/hw/vendor/hpdcache/docs/source/images/wave_ready_when_valid.svg @@ -0,0 +1,4 @@ + + + +01234CLKPAYLOADdataVALIDREADY \ No newline at end of file diff --git a/hw/vendor/hpdcache/docs/source/images/wave_valid_before_ready.json b/hw/vendor/hpdcache/docs/source/images/wave_valid_before_ready.json new file mode 100755 index 00000000..16c313eb --- /dev/null +++ b/hw/vendor/hpdcache/docs/source/images/wave_valid_before_ready.json @@ -0,0 +1,10 @@ +{signal: [ + {name: 'CLK', wave: 'p...'}, + {name: 'PAYLOAD', wave: 'x2.x', data: ['data']}, + {name: 'VALID', wave: '01.0'}, + {name: 'READY', wave: '0.10'} +], + head:{ + tick:0 + } +} diff --git a/hw/vendor/hpdcache/docs/source/images/wave_valid_before_ready.pdf b/hw/vendor/hpdcache/docs/source/images/wave_valid_before_ready.pdf new file mode 100644 index 0000000000000000000000000000000000000000..48530cd2128bd982b017712ebd1e2daadae570c4 GIT binary patch literal 5798 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 f2p(W=X^G!@mB + + +01234CLKPAYLOADdataVALIDREADY \ No newline at end of file diff --git a/hw/vendor/hpdcache/docs/source/index.rst b/hw/vendor/hpdcache/docs/source/index.rst new file mode 100644 index 00000000..21794e7a --- /dev/null +++ b/hw/vendor/hpdcache/docs/source/index.rst @@ -0,0 +1,37 @@ +.. + Copyright 2024 CEA* + *Commissariat a l'Energie Atomique et aux Energies Alternatives (CEA) + + SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 + + Licensed under the Solderpad Hardware License v 2.1 (the “License”); you + may not use this file except in compliance with the License, or, at your + option, the Apache License version 2.0. You may obtain a copy of the + License at + + https://solderpad.org/licenses/SHL-2.1/ + + Unless required by applicable law or agreed to in writing, any work + distributed under the License is distributed on an “AS IS” BASIS, WITHOUT + WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + License for the specific language governing permissions and limitations + under the License. + + Authors : Cesar Fuguet + Description : HPDcache User Guide Master File + + +Welcome to the HPDcache User Guide +================================== + +.. toctree:: + :maxdepth: 2 + :caption: Contents: + + overview + interface + architecture + csrs + amo + cmo + references diff --git a/hw/vendor/hpdcache/docs/source/interface.rst b/hw/vendor/hpdcache/docs/source/interface.rst new file mode 100644 index 00000000..110c12af --- /dev/null +++ b/hw/vendor/hpdcache/docs/source/interface.rst @@ -0,0 +1,1318 @@ +.. + Copyright 2024 CEA* + *Commissariat a l'Energie Atomique et aux Energies Alternatives (CEA) + + SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 + + Licensed under the Solderpad Hardware License v 2.1 (the “License”); you + may not use this file except in compliance with the License, or, at your + option, the Apache License version 2.0. You may obtain a copy of the + License at + + https://solderpad.org/licenses/SHL-2.1/ + + Unless required by applicable law or agreed to in writing, any work + distributed under the License is distributed on an “AS IS” BASIS, WITHOUT + WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + License for the specific language governing permissions and limitations + under the License. + + Authors : Cesar Fuguet + Description : HPDcache Interface + +Parameters, Interfaces and Communication Protocols +================================================== + +Synthesis-time (Static) Configuration Parameters +------------------------------------------------ + +The HPDcache has several static configuration parameters. These parameters must +be defined at compilation/synthesis. + +:numref:`tab_synthesis_parameters` summarizes the list of parameters +that can be set when integrating the HPDcache. + +.. _tab_synthesis_parameters: + +.. list-table:: Static Synthesis-Time Parameters + :widths: 45 55 + :header-rows: 1 + + * - Parameter + - Description + * - :math:`\scriptsize\mathsf{NREQUESTERS}` + - Number of requesters to the HPDcache + * - :math:`\scriptsize\mathsf{CONF\_HPDCACHE\_PA\_WIDTH}` + - Physical address width (in bits) + * - :math:`\scriptsize\mathsf{CONF\_HPDCACHE\_WORD\_WIDTH}` + - Width (in bits) of a data word + * - :math:`\scriptsize\mathsf{CONF\_HPDCACHE\_SETS}` + - Number of sets + * - :math:`\scriptsize\mathsf{CONF\_HPDCACHE\_WAYS}` + - Number of ways (associativity) + * - :math:`\scriptsize\mathsf{CONF\_HPDCACHE\_CL\_WORDS}` + - Number of words in a cacheline + * - :math:`\scriptsize\mathsf{CONF\_HPDCACHE\_REQ\_WORDS}` + - Number of words in the data channels from/to requesters + * - :math:`\scriptsize\mathsf{CONF\_HPDCACHE\_REQ\_TRANS\_ID\_WIDTH}` + - Width (in bits) of the transaction ID from requesters + * - :math:`\scriptsize\mathsf{CONF\_HPDCACHE\_REQ\_SRC\_ID\_WIDTH}` + - Width (in bits) of the source ID from requesters + * - :math:`\scriptsize\mathsf{CONF\_HPDCACHE\_VICTIM\_SEL}` + - It allows to choose the replacement selection policy + * - :math:`\scriptsize\mathsf{CONF\_HPDCACHE\_MSHR\_SETS}` + - Number of sets in the MSHR + * - :math:`\scriptsize\mathsf{CONF\_HPDCACHE\_MSHR\_WAYS}` + - Number of ways (associativity) in the MSHR + * - :math:`\scriptsize\mathsf{CONF\_HPDCACHE\_WBUF\_DIR\_ENTRIES}` + - Number of entries in the directory of the write buffer + * - :math:`\scriptsize\mathsf{CONF\_HPDCACHE\_WBUF\_DATA\_ENTRIES}` + - Number of entries in the data part of the write buffer + * - :math:`\scriptsize\mathsf{CONF\_HPDCACHE\_WBUF\_WORDS}` + - Number of data words per entry in the write buffer + * - :math:`\scriptsize\mathsf{CONF\_HPDCACHE\_WBUF\_TIMECNT\_WIDTH}` + - Width (in bits) of the time counter in write buffer entries + * - :math:`\scriptsize\mathsf{CONF\_HPDCACHE\_RTAB\_ENTRIES}` + - Number of entries in the replay table + * - :math:`\scriptsize\mathsf{CONF\_HPDCACHE\_FLUSH\_ENTRIES}` + - Number of entries in the flush directory + * - :math:`\scriptsize\mathsf{CONF\_HPDCACHE\_FLUSH\_FIFO\_DEPTH}` + - Number of entries in the flush FIFO + * - :math:`\scriptsize\mathsf{CONF\_HPDCACHE\_REFILL\_FIFO\_DEPTH}` + - Number of entries in the refill FIFO + * - :math:`\scriptsize\mathsf{CONF\_HPDCACHE\_REFILL\_CORE\_RSP\_FEEDTHROUGH}` + - Use feedthrough FIFO for responses from the refill handler to the core + * - :math:`\scriptsize\mathsf{CONF\_HPDCACHE\_MEM\_DATA\_WIDTH}` + - Width (in bits) of the data channels from/to the memory interface + * - :math:`\scriptsize\mathsf{CONF\_HPDCACHE\_MEM\_ID\_WIDTH}` + - Width (in bits) of the transaction ID from the memory interface + * - :math:`\scriptsize\mathsf{CONF\_HPDCACHE\_WT\_ENABLE}` + - Enable the write-through policy in the cache + * - :math:`\scriptsize\mathsf{CONF\_HPDCACHE\_WB\_ENABLE}` + - Enable the write-back policy in the cache + * - :math:`\scriptsize\mathsf{CONF\_HPDCACHE\_SUPPORT\_AMO}` + - When set to 1, the HPDCache supports Atomic Memory Operations (AMOs) + * - :math:`\scriptsize\mathsf{CONF\_HPDCACHE\_SUPPORT\_CMO}` + - When set to 1, the HPDCache supports Cache Management Operations (CMOs) + * - :math:`\scriptsize\mathsf{CONF\_HPDCACHE\_SUPPORT\_PERF}` + - When set to 1, the HPDcache integrates performance counters + +Some parameters are not directly related with functionality. Instead, they +allow adapting the HPDcache to physical constraints in the target technology +node. Typically, these control the geometry of SRAM macros. Depending on the +technology, some dimensions are more efficient than others (in terms of +performance, power and area). These also need to be provided by the user at +synthesis-time. :numref:`tab_synthesis_physical_parameters` lists the static +synthesis-time physical parameters of the HPDcache. The +:math:`\scriptsize\mathsf{CONF\_HPDCACHE\_ACCESS\_WORDS}` has an impact on the refill +latency (see section :ref:`sec_cache_ram_organization`). + +.. _tab_synthesis_physical_parameters: + +.. list-table:: Static Synthesis-Time Physical Parameters + :widths: 50 50 + :header-rows: 1 + + * - Parameter + - Description + * - :math:`\scriptsize\mathsf{CONF\_HPDCACHE\_ACCESS\_WORDS}` + - Number of words that can be accessed simultaneously from the CACHE data + array + * - :math:`\scriptsize\mathsf{CONF\_HPDCACHE\_DATA\_WAYS\_PER\_RAM\_WORD}` + - Number of ways in the same CACHE data SRAM word + * - :math:`\scriptsize\mathsf{CONF\_HPDCACHE\_DATA\_SETS\_PER\_RAM}` + - Number of sets per RAM macro in the DATA array of the cache + * - :math:`\scriptsize\mathsf{CONF\_HPDCACHE\_DATA\_RAM\_BYTE\_ENABLE}` + - Use RAM macros with byte-enable instead of bit-mask for the CACHE data + array + * - :math:`\scriptsize\mathsf{CONF\_HPDCACHE\_MSHR\_USE\_REG\_BANK}` + - Use FFs instead of SRAM for the MSHR + * - :math:`\scriptsize\mathsf{CONF\_HPDCACHE\_MSHR\_WAYS\_PER\_RAM\_WORD}` + - Number of ways in the same MSHR SRAM word + * - :math:`\scriptsize\mathsf{CONF\_HPDCACHE\_MSHR\_SETS\_PER\_RAM}` + - Number of sets per RAM macro in the MSHR array of the cache + * - :math:`\scriptsize\mathsf{CONF\_HPDCACHE\_MSHR\_RAM\_BYTE\_ENABLE}` + - Use RAM macros with byte-enable instead of bit-mask for the MSHR + +Several internal configuration values are computed from the above ones. +:numref:`tab_internal_parameters` has a non-complete list of these +internal configuration values that may be mentioned in the remainder of this +document. + +.. _tab_internal_parameters: + +.. list-table:: Internal Parameters + :widths: 35 25 40 + :header-rows: 1 + + * - Parameter + - Description + - Value + * - :math:`\scriptsize\mathsf{HPDCACHE\_CL\_WIDTH}` + - Width (in bits) of a cacheline + - | :math:`\scriptsize\mathsf{CONF\_HPDCACHE\_CL\_WORDS \times}` + | :math:`\scriptsize\mathsf{CONF\_HPDCACHE\_WORD\_WIDTH}` + * - :math:`\scriptsize\mathsf{HPDCACHE\_REQ\_DATA\_WIDTH}` + - Width (in bits) of request data interfaces + - | :math:`\scriptsize\mathsf{CONF\_HPDCACHE\_REQ\_WORDS \times}` + | :math:`\scriptsize\mathsf{CONF\_HPDCACHE\_WORD\_WIDTH}` + * - :math:`\scriptsize\mathsf{HPDCACHE\_NLINE\_WIDTH}` + - Width (in bits) of the cacheline index part of the address + - | :math:`\scriptsize\mathsf{CONF\_HPDCACHE\_PA\_WIDTH -}` + | :math:`\scriptsize\mathsf{log_2(\frac{HPDCACHE\_CL\_WIDTH}{8})}` + * - :math:`\scriptsize\mathsf{HPDCACHE\_SET\_WIDTH}` + - Width (in bits) of the SET part of the address + - :math:`\scriptsize\mathsf{log_2(CONF\_HPDCACHE\_SETS)}` + * - :math:`\scriptsize\mathsf{HPDCACHE\_TAG\_WIDTH}` + - Width (in bits) of the TAG part of the address + - | :math:`\scriptsize\mathsf{HPDCACHE\_NLINE\_WIDTH -}` + | :math:`\scriptsize\mathsf{HPDCACHE\_SET\_WIDTH}` + * - :math:`\scriptsize\mathsf{HPDCACHE\_WBUF\_WIDTH}` + - Width (in bits) of an entry in the write-buffer + - | :math:`\scriptsize\mathsf{CONF\_HPDCACHE\_WBUF\_WORDS \times}` + | :math:`\scriptsize\mathsf{CONF\_HPDCACHE\_WORD\_WIDTH}` + + +Conventions +----------- + +The HPDcache uses the following conventions in the naming of its signals: + + - The ``_i`` suffix for input ports + - The ``_o`` suffix for output ports + - The ``_n`` suffix for active low ports + - The ``clk_`` suffix for clock ports + - The ``rst_`` suffix for reset ports + - There may be a mix of suffixes. For example ``_ni`` indicates an active-low + input port + + +Global Signals +-------------- + +.. _tab_global_signals: + +.. list-table:: Global Signals + :widths: 25 15 60 + :header-rows: 1 + + * - Signal + - Source + - Description + * - ``clk_i`` + - Clock source + - Global clock signal. + The HPDcache is synchronous to the rising-edge of the clock. + * - ``rst_ni`` + - Reset source + - Global reset signal. Asynchronous, active LOW, reset signal. + * - ``wbuf_flush_i`` + - System + - Force the write-buffer to send all pending writes. Active HIGH, + one-cycle, pulse signal. Synchronous to ``clk_i``. + * - ``wbuf_empty_o`` + - Cache + - Indicates if the write-buffer is empty (there is no pending write + transactions). When this signal is set to 1, the write-buffer is empty. + * - ``cfig_base_i`` + - System + - Base address of the CSR segment in the HPDcache (:ref:`sec_csr`) + + +Cache-Requesters Interface +-------------------------------------- + +This section describes the Cache-Requesters Interface (CRI) between requesters +and the HPDcache. It contains two channels: one for requests and one for +responses. There are as many CRIs as requesters from the core/accelerator to +the HPDcache. + +This interface is synchronous to the rising edge of the global +clock ``clk_i``. + +The address (``core_req_i.addr_offset``), size (``core_req_i.size``), +byte-enable (``core_req_i.be``), write data (``core_req_i.wdata``) and +read data (``core_rsp_o.rdata``) signals shall comply with the alignment +constraints defined in section +:ref:`Address, data, and byte enable alignment `. + +CRI Signal Description +~~~~~~~~~~~~~~~~~~~~~~ + +.. _tab_req_channel_signals: + +.. list-table:: CRI Request Channel Signals + :widths: 31 13 52 + :align: center + :header-rows: 1 + + * - Signal + - Source + - Description + * - ``core_req_valid_i`` + - Requester + - Indicates that the corresponding requester has a valid request + * - ``core_req_ready_o`` + - Cache + - Indicates that the cache is ready to accept a request from the + corresponding requester + * - ``core_req_i.addr_offset`` + - Requester + - Least significant bits of the target address of the request + * - ``core_req_i.wdata`` + - Requester + - Write data (little-endian) + * - ``core_req_i.op`` + - Requester + - Indicates the type of operation to be performed + * - ``core_req_i.be`` + - Requester + - Byte-enable for write data (little-endian) + * - ``core_req_i.size`` + - Requester + - Indicate the size of the access. The size is encoded as the power-of-two + of the number of bytes (e.g. + 0 is :math:`\scriptsize\mathsf{2^0~=~1}`, + 5 is :math:`\scriptsize\mathsf{2^5~=~32}`) + * - ``core_req_i.sid`` + - Requester + - The identification tag for the requester. It shall be identical to the + index of the request port binded to that requester + * - ``core_req_i.tid`` + - Requester + - The identification tag for the request. A requester can issue multiple + requests. The corresponding response from the cache will return this tid + * - ``core_req_i.need_rsp`` + - Requester + - Indicates if the request needs a response from the cache. When unset, + the cache will not issue a response for the corresponding request + * - ``core_req_i.phys_indexed`` + - Requester + - Indicates wheter the access uses virtual (unset) or physical indexing + (set) + * - ``core_req_i.addr_tag`` + - Requester + - Most significant bits of the target address of the request. It is only + valid when using physical indexing (``core_req_i.phys_indexed = 1``) + * - ``core_req_i.pma.uncacheable`` + - Requester + - Indicates whether the access needs to be cached (unset) or not (set). + Uncacheable accesses are directly forwarded to the memory. It is only + valid when using physical indexing (``core_req_i.phys_indexed = 1``) + * - ``core_req_i.pma.io`` + - Requester + - Indicates whether the request targets input/output (IO) peripherals + (set) or not (unset). IO accesses are directly forwarded to the memory. + It is only valid when using physical indexing + (``core_req_i.phys_indexed = 1``) + * - ``core_req_i.pma.wr_policy_hint`` + - Requester + - Indicates whether the target cacheline shall be managed as write-back + (write allocate) or write-through (write non-allocate). + It is only valid when using physical indexing + (``core_req_i.phys_indexed = 1``) + * - ``core_req_tag_i`` + - Requester + - Most significant bits of the target address of the request. This signal + must be delayed of 1 cycle after + ``(core_req_valid_i & core_req_ready_o) = 1``. + It is valid when using virtual indexing + (``core_req_i.phys_indexed = 0``) + * - ``core_req_pma_i.uncacheable`` + - Requester + - Indicates whether the access needs to be cached (unset) or not (set). + Uncacheable accesses are directly forwarded to the memory. This signal + must be delayed of 1 cycle after + ``(core_req_valid_i & core_req_ready_o) = 1``. + It is only valid when using virtual indexing + (``core_req_i.phys_indexed = 0``) + * - ``core_req_pma_i.io`` + - Requester + - Indicates whether the access targets input/output (IO) peripherals (set) + or not (unset). IO accesses are directly forwarded to the memory. This + signal must be delayed of 1 cycle after + ``(core_req_valid_i & core_req_ready_o) = 1``. + It is only valid when using virtual indexing + (``core_req_i.phys_indexed = 0``) + * - ``core_req_pma_i.wr_policy_hint`` + - Requester + - Indicates whether the target cacheline shall be managed as write-back + (write allocate) or write-through (write non-allocate). + It is only valid when using virtual indexing + (``core_req_i.phys_indexed = 0``) + +.. _tab_resp_channel_signals: + +.. list-table:: CRI Response Channel Signals + :widths: 31 13 52 + :header-rows: 1 + + * - Signal + - Source + - Description + * - ``core_rsp_valid_o`` + - Cache + - Indicates that the HPDcache has a valid response for the corresponding + requester + * - ``core_rsp_o.rdata`` + - Cache + - Response read data + * - ``core_rsp_o.sid`` + - Cache + - The identification tag for the requester. It corresponds to the **sid** + transferred with the request + * - ``core_rsp_o.tid`` + - Cache + - The identification tag for the request. It corresponds to the **tid** + transferred with the request + * - ``core_rsp_o.error`` + - Cache + - Indicates whether there was an error condition while processing the + request + * - ``core_rsp_o.aborted`` + - Cache + - Indicates if the request issued in the previous cycle shall be aborted. + It is only considered if the previous request used virtual indexing + +Cache Memory Interfaces +---------------------------------- + +This section describes the Cache-Memory Interface (CMI) between the HPDcache +and the NoC/memory. It implements 5 different channels. + +This interface is synchronous to the rising edge of the global clock +``clk_i``. + +All CMI interfaces implements the ready-valid protocol described in section +:ref:`sec_ready_valid_handshake` for the handshake +between the HPDcache and the NoC/Memory. + +The address (``mem_req_addr``), size (``mem_req_size``), +write data (``mem_req_w_data``) and write byte-enable (``mem_req_w_be``) +signals shall comply with the alignment constraints defined in section +:ref:`Address, data, and byte enable alignment `. + + +.. _sec_mi_signal_descriptions: + +CMI Signal Descriptions +~~~~~~~~~~~~~~~~~~~~~~~ + +- **Memory Read Interfaces** + +.. _tab_read_req_channel_signals: + +.. list-table:: CMI Read Request Channel Signals + :widths: 31 13 52 + :header-rows: 1 + + * - Signal + - Source + - Description + * - ``mem_req_read_valid_o`` + - Cache + - Indicates that the channel is signaling a valid request + * - ``mem_req_read_ready_i`` + - NoC + - Indicates that the NoC is ready to accept a request + * - ``mem_req_read_o.mem_req_addr`` + - Cache + - Target physical address of the request. The address shall be aligned to + the ``mem_req_read_o.mem_req_size`` field. + * - ``mem_req_read_o.mem_req_len`` + - Cache + - Indicates the number of transfers in a burst minus one + * - ``mem_req_read_o.mem_req_size`` + - Cache + - Indicate the size of the access. The size is encoded as the power-of-two + of the number of bytes + * - ``mem_req_read_o.mem_req_id`` + - Cache + - The identification tag for the request. The HPDcache always use unique + IDs on the memory interface (i.e. two or more in-flight requests cannot + share the same ID). + * - ``mem_req_read_o.mem_req_command`` + - Cache + - Indicates the type of operation to be performed + * - ``mem_req_read_o.mem_req_atomic`` + - Cache + - In case of atomic operations, it indicates its type + * - ``mem_req_read_o.mem_req_cacheable`` + - Cache + - This is a hint for the cache hierarchy in the system. It indicates if + the request can be allocated by the cache hierarchy. That is, data can + be prefetched from memory or can be reused for multiple read + transactions + + +.. _tab_read_miss_resp_channel_signals: + +.. list-table:: CMI Read Response Channel Signals + :widths: 31 13 52 + :header-rows: 1 + + * - Signal + - Source + - Description + * - ``mem_resp_read_valid_i`` + - NoC + - Indicates that the channel is signaling a valid response + * - ``mem_resp_read_ready_o`` + - Cache + - Indicates that the cache is ready to accept a response + * - ``mem_resp_read_i.mem_resp_r_error`` + - NoC + - Indicates whether there was an error condition while processing the + request + * - ``mem_resp_read_i.mem_resp_r_id`` + - NoC + - The identification tag for the request. It corresponds to the ID + transferred with the request + * - ``mem_resp_read_i.mem_resp_r_data`` + - NoC + - Response read data. It shall be naturally aligned to the request address + * - ``mem_resp_read_i.mem_resp_r_last`` + - NoC + - Indicates the last transfer in a read response burst + + +- **Memory Write Interfaces** + +.. _tab_write_req_channel_signals: + +.. list-table:: CMI Write Request Channel Signals + :widths: 31 13 52 + :header-rows: 1 + + * - Signal + - Source + - Description + * - ``mem_req_write_valid_o`` + - Cache + - Indicates that the channel is signaling a valid request + * - ``mem_req_write_ready_i`` + - NoC + - Indicates that the cache is ready to accept a response + * - ``mem_req_write_o.mem_req_addr`` + - Cache + - Target physical address of the request + * - ``mem_req_write_o.mem_req_len`` + - Cache + - Indicates the number of transfers in a burst minus one + * - ``mem_req_write_o.mem_req_size`` + - Cache + - Indicate the size of the access. The size is encoded as the + power-of-two of the number of bytes + * - ``mem_req_write_o.mem_req_id`` + - Cache + - The identification tag for the request. The HPDcache always use unique + IDs on the memory interface (i.e. two or more in-flight requests cannot + share the same ID). + * - ``mem_req_write_o.mem_req_command`` + - Cache + - Indicates the type of operation to be performed + * - ``mem_req_write_o.mem_req_atomic`` + - Cache + - In case of atomic operations, it indicates its type + * - ``mem_req_write_o.mem_req_cacheable`` + - Cache + - This is a hint for the cache hierarchy in the system. It indicates if + the write is bufferable by the cache hierarchy. This means that the + write must be visible in a timely manner at the final destination. + However, write responses can be obtained from an intermediate point + + +.. _tab_write_data_channel_signals: + +.. list-table:: CMI Write Data Channel Signals + :widths: 31 13 52 + :header-rows: 1 + + * - Signal + - Source + - Description + * - ``mem_req_write_data_valid_o`` + - Cache + - Indicates that the channel is transferring a valid data + * - ``mem_req_write_data_ready_i`` + - NoC + - Indicates that the target is ready to accept the data + * - ``mem_req_write_data_o.mem_req_w_data`` + - Cache + - Request write data. It shall be naturally aligned to the request + address + * - ``mem_req_write_data_o.mem_req_w_be`` + - Cache + - Request write byte-enable. It shall be naturally aligned to the request + address + * - ``mem_req_write_data_o.mem_req_w_last`` + - Cache + - Indicates the last transfer in a write request burst + + +.. _tab_write_resp_channel_signals: + +.. list-table:: CMI Write Response Channel Signals + :widths: 31 13 52 + :header-rows: 1 + + * - Signal + - Source + - Description + * - ``mem_resp_write_valid_i`` + - NoC + - Indicates that the channel is transferring a valid write acknowledgement + * - ``mem_resp_write_ready_o`` + - Cache + - Indicates that the cache is ready to accept the acknowledgement + * - ``mem_resp_write_i.mem_resp_w_is_atomic`` + - NoC + - Indicates whether the atomic operation was successfully processed + (atomically) + * - ``mem_resp_write_i.mem_resp_w_error`` + - NoC + - Indicates whether there was an error condition while processing the + request + * - ``mem_resp_write_i.mem_resp_w_id`` + - NoC + - The identification tag for the request. It corresponds to the ID + transferred with the request + + +Interfaces’ requirements +------------------------ + +This section describes the basic protocol transaction requirements for the +different interfaces in the HPDcache. + + +.. _sec_ready_valid_handshake: + +Valid/Ready handshake process +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +All interfaces in the HPDcache use a **valid**/**ready** handshake process to +transfer a payload between the source and the destination. The payload contains +the address, data and control information. + +As a reminder, the 7 interfaces in the HPDcache are the following: + +#. CRI request interface +#. CRI response interface +#. CMI read request interface +#. CMI read response interface +#. CMI write request interface +#. CMI write data request interface +#. CMI write response interface + +The source sets to 1 the **valid** signal to indicate when the payload is +available. The destination sets to 1 the **ready** signal to indicate that it +can accept that payload. Transfer occurs only when both the **valid** and +**ready** signals are set to 1 on the next rising edge of the clock. + +A source is not permitted to wait until **ready** is set to 1 before setting +**valid** to 1. + +A destination may or not wait for **valid** to set the **ready** to 1 +(:numref:`cases (a) and (d) in Table %s `). +In other words, a destination may set **ready** to 1 before an actual transfer +is available. + +When **valid** is set to 1, the source must keep it that way until the +handshake occurs. This is, at the next rising edge when both **valid** and +**ready** (from the destination) are set to 1. In other words, a source cannot +retire a pending **valid** transfer +(:numref:`Case (b) in Table %s `). + +After an effective transfer (**valid** and **ready** set to 1), the source may +keep **valid** set to 1 in the next cycle to signal a new transfer (with a new +payload). In the same manner, the destination may keep **ready** set to 1 if it +can accept a new transfer. This allows back-to-back transfers, with no idle +cycles, between a source and a destination +(:numref:`Case (d) in Table %s `). + +All interfaces are synchronous to the rising edge of the same global +clock (``clk_i``). + +.. _tab_ready_valid_scenarios: + +.. list-table:: valid/ready scenarios + :class: borderless + :align: center + + * - **(a)** + - **(b)** + * - .. image:: images/wave_ready_before_valid.* + - .. image:: images/wave_valid_before_ready.* + * - **(c)** + - **(d)** + * - .. image:: images/wave_ready_when_valid.* + - .. image:: images/wave_back_to_back.* + + +CRI Response Interface +''''''''''''''''''''''''''''' + +In the case of the CRI response interfaces, there is a particularity. +For these interfaces, it is assumed that the **ready** signal is always set to +1. That is why the **ready** signal is not actually implemented on those +interfaces. In other words, the requester unconditionally accepts any incoming +response. + +.. _sec_req_alignment: + +Address, data and byte enable alignment +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Address alignment +''''''''''''''''' + +The address transferred (**addr**) in all request interfaces (CRI and CMI) +shall be byte-aligned to the value of the corresponding **size** signal in that +interface. + +Some examples are illustrated in +:numref:`Figure %s `. In the first case, the +**size** value is 2 (which corresponds to :math:`\scriptsize\mathsf{2^2=4}` bytes). +Thus, the address must be a multiple of 4; In the second case, **size** value +is 3. Thus, the address must be a multiple of 8. Finally, in the third case, +**size** value is 0. Thus, there is no constraint on the address alignment. + +Data alignment +'''''''''''''' + +The data must be naturally aligned to the address (**addr**) and the maximum +valid bytes of the transfer must be equal to :math:`\scriptsize\mathsf{2^{size}}`. +This means that the first valid byte in the **data** signal must be at the +indicated offset of the address. Here, the offset corresponds to the least +significant bits of the address, that allow to indicate a byte within the +**data** word. For example, if the **data** signal is 128 bits wide (16 +bytes), then the offset corresponds to the first 4 bits of the **addr** signal. + +Some examples are illustrated in +:numref:`Figure %s `. As illustrated, within the +data word, only bytes in the range from the indicated offset in the address, to +that offset plus :math:`\scriptsize\mathsf{2^{size}}` can contain valid data. Other bytes must +be ignored by the destination. + +Additionally, within the range described above, the **be** signal indicates +which bytes within that range are actually valid. Bytes in the **data** +signal where the **be** signals are set to 0, must be ignored by the +destination. + +Byte Enable (BE) alignment +'''''''''''''''''''''''''' + +The **be** signal must be naturally aligned to the address (**addr**) and the +number of bits set in this signal must be less or equal to +:math:`\scriptsize\mathsf{2^\text{size}}`. This means that the first valid bit in the +**be** signal must be at the indicated offset of the address. The offset is +the same as the one explained above in the "Data alignment" paragraph. + +Some examples are illustrated in +:numref:`Figure %s `. As illustrated, within the +**be** word, only bits in the range from the indicated offset in the address, +to that offset plus :math:`\scriptsize\mathsf{2^{size}}` can be set. Other bits +outside that range must be set to 0. + +.. _fig_request_data_alignment: + +.. figure:: images/hpdcache_request_address_data_alignment.* + :align: center + :alt: Address, Data and Byte Enable Alignment in Requests + + Address, Data and Byte Enable Alignment in Requests + + +Cache-Requesters Interface (CRI) Attributes +------------------------------------------- + +.. _sec_vipt: + +Physical or Virtual Indexing +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The HPDcache allows the address and physical memory attributes (PMA) to be sent +by the requesters in two different (but consecutive) cycles. + +This is useful to allow the pipelining of the address translation mechanism +(when the core has one). This is illustrated in +:numref:`Figure %s `. +Doing the translation and directly forwarding to the cache is usually too +costly in terms of timing. Instead, the requesters can: + +.. list-table:: + :widths: 15 85 + :header-rows: 0 + + * - Cycle 0 + - During the first cycle, forward the least significant bits of the + address (``addr_offset``), which usually do not need to be translated, + along with the other fields of the request (operation, identifiers, + etc). In the meanwhile the core can perform the translation of the + address to compute the most significant bits (``addr_tag``) + + * - Cycle 1 + - During the second cycle, forward the previously translated most + significant bits of the address (``addr_tag``), and the corresponding + PMAs. PMAs are sent during this second cycle because usually they depend + on the target physical address. The requester can abort the request + during this cycle as explained in the next section (:ref:`sec_req_abort`). + +.. _fig_vipt: + +.. figure:: images/hpdcache_vipt.* + :align: center + :width: 80% + :alt: Pipelining of the Virtual and Physical Part of the Address + + Pipelining of the Virtual and Physical Part of the Address + +This kind of indexing is named **Virtually-Indexed Physically-Tagged (VIPT)**. + +The requester shall send the tag and PMAs the next cycle after the +``core_req_valid_i`` and ``core_req_ready_o`` signals were set to 1 and the +``core_req_i.phys_indexed`` signal was set to 0.The number of bits of the +address offset (``addr_offset``) depends on the number of cache sets +(:math:`\scriptsize\mathsf{CONF\_HPDCACHE\_SETS}`) and the size of the cachelines +(:math:`\scriptsize\mathsf{CONF\_HPDCACHE\_CL\_WIDTH/8}`). +The address offset represents the concatenation of these two fields of the +address: the byte offset in the cacheline and the set index. Requests can be +sent back-to-back with no idle cycle in-between. + +If requesters do not need virtual indexing, they can send the full address in +the first cycle by setting the ``core_req_i.phys_indexed`` bit to 1. The address +offset and the tag shall be sent through the ``core_req_i.addr_offset`` and +``core_req_i.addr_tag``, respectively. A given requester is free to alternate +between virtual and physical indexing on different clock cycles. Different +requesters can use different indexing schemes (virtual or physical). + +.. _sec_req_abort: + +Request Abortion +~~~~~~~~~~~~~~~~~~~~~ + +When using the virtual indexing, the requester can abort the request during the +second cycle of the addressing pipeline. In that case, the requester needs to +set the req_abort signal to 1. + +When a request is aborted, and the ``core_req_i.need_rsp`` field was set to 1, the +HPDcache respond to the corresponding requester with the bit +``core_rsp_o.aborted`` set to 1. + +CRI Type of Operation +~~~~~~~~~~~~~~~~~~~~~ + +A requester indicates the required operation on the 5-bit, ``HPDCACHE_REQ_OP`` +signal. The supported operation are detailed in :numref:`tab_req_op_types`. + +.. _tab_req_op_types: + +.. list-table:: Requesters Operation Types + :widths: 30 15 55 + :header-rows: 1 + + * - Mnemonic + - Encoding + - Type + * - ``HPDCACHE_REQ_LOAD`` + - 0b00000 + - Read operation + * - ``HPDCACHE_REQ_STORE`` + - 0b00001 + - Write operation + * - ``HPDCACHE_REQ_AMO_LR`` + - 0b00100 + - Atomic Load-reserved operation + * - ``HPDCACHE_REQ_AMO_SC`` + - 0b00101 + - Atomic Store-conditional operation + * - ``HPDCACHE_REQ_AMO_SWAP`` + - 0b00110 + - Atomic SWAP operation + * - ``HPDCACHE_REQ_AMO_ADD`` + - 0b00111 + - Atomic integer ADD operation + * - ``HPDCACHE_REQ_AMO_AND`` + - 0b01000 + - Atomic bitwise AND operation + * - ``HPDCACHE_REQ_AMO_OR`` + - 0b01001 + - Atomic bitwise OR operation + * - ``HPDCACHE_REQ_AMO_XOR`` + - 0b01010 + - Atomic bitwise XOR operation + * - ``HPDCACHE_REQ_AMO_MAX`` + - 0b01011 + - Atomic integer signed MAX operation + * - ``HPDCACHE_REQ_AMO_MAXU`` + - 0b01100 + - Atomic integer unsigned MAX operation + * - ``HPDCACHE_REQ_AMO_MIN`` + - 0b01101 + - Atomic integer signed MIN operation + * - ``HPDCACHE_REQ_AMO_MINU`` + - 0b01110 + - Atomic integer unsigned MIN operation + * - ``HPDCACHE_CMO_FENCE`` + - 0b10000 + - Memory write fence + * - ``HPDCACHE_CMO_PREFETCH`` + - 0b10001 + - Prefetch a cacheline given its address + * - ``HPDCACHE_CMO_INVAL_NLINE`` + - 0b10010 + - Invalidate a cacheline given its address + * - ``HPDCACHE_CMO_INVAL_ALL`` + - 0b10011 + - Invalidate all Cachelines + * - ``HPDCACHE_CMO_FLUSH_NLINE`` + - 0b10100 + - Flush a cacheline given its Address + * - ``HPDCACHE_CMO_FLUSH_ALL`` + - 0b10101 + - Flush All Cachelines + * - ``HPDCACHE_CMO_FLUSH_INVAL_NLINE`` + - 0b10110 + - Flush and invalidate a cacheline given its address + * - ``HPDCACHE_CMO_FLUSH_INVAL_ALL`` + - 0b10111 + - Flush and invalidate all cachelines + +Load and store operations are normal read and write operations from/to the +specified address. + +Atomic operations are the ones specified in the Atomic (A) extension of the +[RISCVUP2019]_. More details on how the HPDcache implements AMOs are found in +section :ref:`sec_amo`. + +CMOs are explained in :ref:`sec_cmo`. + +Source identifier +~~~~~~~~~~~~~~~~~ + +Each request identifies its source through the ``core_req_i.sid`` signal. The +``core_req_i.sid`` signal shall be decoded when the ``core_req_valid_i`` signal +is set to 1. The width of this signal is +:math:`\scriptsize\mathsf{CONF\_HPDCACHE\_REQ\_SRC\_ID\_WIDTH}` bits. +The HPDcache reflects the value of the **sid** of the request into the +corresponding **sid** of the response. + +Each port must have a unique ID that corresponds to its number. Each port is +numbered from 0 to N-1. This number shall be constant for a given port +(requester). The HPDcache uses this information to route responses to the +correct requester. + +Transaction identifier +~~~~~~~~~~~~~~~~~~~~~~ + +Each request identifies transactions through the +``core_req_i.tid`` signal. The +``core_req_i.tid`` signal shall be decoded when the +``core_req_valid_i`` signal is set to 1. The width of this signal is +:math:`\scriptsize\mathsf{CONF\_HPDCACHE\_REQ\_TRANS\_ID\_WIDTH}` bits. + +This signal can contain any value from 0 to +:math:`\scriptsize\mathsf{2^{CONF\_HPDCACHE\_REQ\_TRANS\_ID\_WIDTH} - 1}`. +The HPDcache forwards the value of the **tid** of the request into the **tid** +of the corresponding response. + +A requester can issue multiple transactions without waiting for earlier +transactions to complete. Because the HPDcache can respond to these transactions +in a different order than the one of requests, the requester can use the **tid** +to match the responses with respect to requests. + +The ID of transactions is not necessarily unique. A requester may reuse a given +transaction ID for different transactions. That is, even when some of these +transactions are not yet completed. However, when the requester starts multiple +transactions with the same **tid**, it cannot match responses and requests +because responses can be in a different order that the one of requests. + + +.. _sec_req_cacheability: + +Cacheability +~~~~~~~~~~~~ + +This cache considers that the memory space is segmented. A segment corresponds +to an address range: a **base address** and an **end address**. Some segments +are cacheable and others not. The HPDcache needs to know which segments are +cacheable to determine if for a given read request, it needs to copy the read +data into the cache. + +The request interface implements an uncacheable bit +(``core_req_i.pma.uncacheable`` or ``core_req_pma_i.uncacheable``). When this +bit is set, the access is considered uncacheable. The +``core_req_i.pma.uncacheable`` signal shall be decoded when the +``core_req_valid_i`` signal is set to 1. The ``core_req_pma_i.uncacheable`` +shall be decoded when the ``core_req_valid_i``, ``core_req_ready_o`` and the +``core_req_i.phys_indexed`` signals were set to 1 the previous cycle. + +.. admonition:: Caution + :class: caution + + For a given address, the uncacheable attribute must be consistent between + accesses. The granularity is the cacheline. **In the event that the same + address is accessed with different values in the uncacheable attribute, the + behavior of the cache for that address is unpredictable**. + +Need response +~~~~~~~~~~~~~ + +For any given request, a requester can set the bit ``core_req_i.need_rsp`` to 0 +to indicate that it does not want a response for that request. The +``core_req_i.need_rsp`` signal shall be decoded when the ``core_req_valid_i`` +signal is set to 1. + +When ``core_req_i.need_rsp`` is set to 0, the HPDcache processes the request +but it does not send an acknowledgement to the corresponding requester when the +transaction is completed. + +Write-Policy Hint +~~~~~~~~~~~~~~~~~ + +The CRI may set dynamically the write-policy (write-back or write-through) for +the target cacheline. In the request interface, there are specific flags (hint) +to indicate the desired policy for a given request. + +The request interface drives the hint through the +``core_req_i.pma.wr_policy_hint`` or ``core_req_pma_i.wr_policy_hint`` signals. +The ``core_req_i.pma.wr_policy_hint`` signal shall be decoded when the +``core_req_valid_i`` signal is set to 1. The ``core_req_pma_i.wr_policy_hint`` +shall be decoded when the ``core_req_valid_i``, ``core_req_ready_o`` and the +``core_req_i.phys_indexed`` signals were set to 1 the previous cycle. + +The supported hints are detailed in :numref:`tab_req_wr_policy_hints`. + +.. _tab_req_wr_policy_hints: + +.. list-table:: Requesters Write-Policy Hint + :widths: 30 15 55 + :header-rows: 1 + + * - Mnemonic + - Encoding + - Type + * - ``HPDCACHE_WR_POLICY_AUTO`` + - 0b001 + - Request to to keep the current write-policy for the target cacheline if + there is a copy in the cache, or use the default policy otherwise. + * - ``HPDCACHE_WR_POLICY_WB`` + - 0b010 + - Request a write-back (write allocate) policy for the target cacheline + * - ``HPDCACHE_WR_POLICY_WT`` + - 0b100 + - Request a write-through (write non-allocate) policy for the target + cacheline + +Error response +~~~~~~~~~~~~~~ + +The response interface contains a single-bit ``core_rsp_o.error`` signal. This +signal is set to 1 by the HPDcache when some error condition occurred during the +processing of the corresponding request. The ``core_rsp_o.error`` signal shall +be decoded when the ``core_rsp_valid_o`` signal is set to 1. + +When the ``core_rsp_o.error`` signal is set to 1 in the response, the effect of +the corresponding request is undefined. If this **error** signal is set in the +case of **LOAD** or **AMOs** operations, the **rdata** signal does not contain +any valid data. + +Cache-Memory Interface (CMI) Attributes +--------------------------------------- + +.. _CMI_type-of-operation: + +CMI Type of operation +~~~~~~~~~~~~~~~~~~~~~ + +.. list-table:: Memory request operation types + :widths: 35 15 50 + :header-rows: 1 + + * - Mnemonic + - Encoding + - Type + * - ``HPDCACHE_MEM_READ`` + - 0b00 + - Read operation + * - ``HPDCACHE_MEM_WRITE`` + - 0b01 + - Write operation + * - ``HPDCACHE_MEM_ATOMIC`` + - 0b10 + - Atomic operation + +``HPDCACHE_MEM_READ`` and ``HPDCACHE_MEM_WRITE`` are respectively normal read +and write operations from/to the specified address. + +In case of an atomic operation request (``HPDCACHE_MEM_ATOMIC``), the specific +operation is specified in the ``MEM_REQ_ATOMIC`` signal. These operations are +listed in :numref:`tab_mem_req_atomics_types`. Note that these +operations are compatible with the ones defined in the AMBA AXI prototol. + +.. _tab_mem_req_atomics_types: + +.. list-table:: Memory request atomic operation types + :widths: 35 15 50 + :header-rows: 1 + + * - Mnemonic + - Encoding + - Type + * - ``HPDCACHE_MEM_ATOMIC_ADD`` + - 0b0000 + - Atomic fetch-and-add operation + * - ``HPDCACHE_MEM_ATOMIC_CLR`` + - 0b0001 + - Atomic fetch-and-clear operation + * - ``HPDCACHE_MEM_ATOMIC_SET`` + - 0b0010 + - Atomic fetch-and-set operation + * - ``HPDCACHE_MEM_ATOMIC_EOR`` + - 0b0011 + - Atomic fetch-and-exclusive-or operation + * - ``HPDCACHE_MEM_ATOMIC_SMAX`` + - 0b0100 + - Atomic fetch-and-maximum (signed) operation + * - ``HPDCACHE_MEM_ATOMIC_SMIN`` + - 0b0101 + - Atomic fetch-and-minimum (signed) operation + * - ``HPDCACHE_MEM_ATOMIC_UMAX`` + - 0b0110 + - Atomic fetch-and-maximum (unsigned) operation + * - ``HPDCACHE_MEM_ATOMIC_UMIN`` + - 0b0111 + - Atomic fetch-and-minimum (unsigned) operation + * - ``HPDCACHE_MEM_ATOMIC_SWAP`` + - 0b1000 + - Atomic swap operation + * - ``HPDCACHE_MEM_ATOMIC_LDEX`` + - 0b1100 + - Load-exclusive operation + * - ``HPDCACHE_MEM_ATOMIC_STEX`` + - 0b1101 + - Store-exclusive operation + + +Type of operation per CMI request channel +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +As a reminder, the HPDcache implements two request channels to the memory: + +#. Memory read request channel +#. Memory write request channel + +:numref:`tab_optypes_by_cmi_req_channel` indicates the type of +operations that each of these two request channels can issue. + +.. _tab_optypes_by_cmi_req_channel: + +.. list-table:: Operation Types Supported by CMI Request Channels + :widths: 30 50 + :header-rows: 1 + + * - Type + - Channels + * - ``HPDCACHE_MEM_READ`` + - - CMI read request + * - ``HPDCACHE_MEM_WRITE`` + - - CMI write request + * - ``HPDCACHE_MEM_ATOMIC`` + - - CMI write request + + +Read-Modify-Write Atomic Operations +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The following atomic operations behave as read-modify-write operations: + +- ``HPDCACHE_MEM_ATOMIC_ADD`` +- ``HPDCACHE_MEM_ATOMIC_CLR`` +- ``HPDCACHE_MEM_ATOMIC_SET`` +- ``HPDCACHE_MEM_ATOMIC_EOR`` +- ``HPDCACHE_MEM_ATOMIC_SMAX`` +- ``HPDCACHE_MEM_ATOMIC_SMIN`` +- ``HPDCACHE_MEM_ATOMIC_UMAX`` +- ``HPDCACHE_MEM_ATOMIC_UMIN`` +- ``HPDCACHE_MEM_ATOMIC_SWAP`` + +These requests are forwarded to the memory through the CMI write request +interface. A particularity of these requests is that they generate two responses +from the memory: + +#. Old data value from memory is returned through the CMI read response + interface. + +#. Write acknowledgement is returned through the CMI write response interface. + +Both responses may arrive in any given order to the initiating HPDcache. + +Regarding errors, if any response has its **error** signal set to 1 +(``mem_resp_*_i.mem_resp_r_error`` or ``mem_resp_*_i.mem_resp_w_error``), the +HPDcache considers that the operation was not completed. It waits for both +responses and it forwards an error response (``core_rsp_o.error = 1``) to the +corresponding requester on the HPDcache requesters’ side. + + +Exclusive Load/Store Atomic Operations +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Exclusive load and store operations are issued as normal load and store +operations on the CMI read request interface and CMI write request interface, +respectively. + +Specific operation types are however used on these exclusive requests: +``HPDCACHE_MEM_ATOMIC_LDEX`` for loads; and +``HPDCACHE_MEM_ATOMIC_STEX`` for stores. + +These requests behave similarly to normal load and store to the memory but +provide some additional properties described in :ref:`sec_amo`. + +In the case of the ``HPDCACHE_MEM_ATOMIC_STEX`` request, the write +acknowledgement contains an additional information in the +``mem_resp_w_is_atomic`` signal. +If this signal is set to 1, the exclusive store was "atomic", hence the data was +written in memory. +If this signal is set to 0, the exclusive store was "non-atomic". Hence the +write operation was abandoned. + +The HPDcache uses exclusive stores in case of SC operations from requesters. +Depending on the ``mem_resp_w_is_atomic`` value, the HPDcache responds to the +requester according to the rules explained in :ref:`sec_amo`. A "non-atomic" +response is considered a **SC Failure**, and a "atomic" response is considered a +**SC Success**. + +CMI Transaction identifier +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Each request identifies transactions through the ``mem_req_*_o.mem_req_id`` +signals. The ``mem_req_*_o.mem_req_id`` signal shall be decoded when the +``mem_req_*_valid_o`` signal is set to 1. The width of these ID signals is +:math:`\scriptsize\mathsf{CONF\_HPDCACHE\_MEM\_ID\_WIDTH}` bits. + +The target (memory or peripheral) shall respond to a request by setting the +``mem_resp*_i.mem_resp_*_id`` signal to the corresponding +``mem_req*_i.mem_req_id``. + +``mem_req_*_o.mem_req_id`` signals can contain any value from 0 to +:math:`\scriptsize\mathsf{2^CONF\_HPDCACHE\_MEM\_ID\_WIDTH - 1}`. + +The HPDcache can issue multiple memory transactions without waiting for earlier +transactions to complete. The HPDcache uses unique IDs for each request. Unique +IDs means that two or more in-flight requests never share the same ID. In-flight +requests are those that have been issued by the HPDcache but have not yet +received their respective response. + +The target (memory or peripheral) of the in-flight request may respond to CMI +in-flight requests in any order. + + +- **Transaction IDs in the CMI read request channel** + +The HPDcache can have the following number of in-flight read miss transactions: + + :math:`\scriptsize\mathsf{CONF\_HPDCACHE\_MSHR\_SETS{}\times{}CONF\_HPDCACHE\_MSHR\_WAYS}` + +Each in-flight transaction has a unique transaction ID. This ID is formatted as +follows: + + - For cacheable requests: + + ``(mshr_way << log2(HPDCACHE_MSHR_SETS)) | mshr_set`` + + The ID is the concatenation of two indexes: the MSHR set and the MSHR way + occupied by the corresponding request. + + - For uncacheable requests + + The HPDcache can issue up to 1 in-flight, uncached, read transaction. + Uncached transactions have a unique transaction ID with all bits set to 1. + + +- **Transaction IDs in the CMI wbuf write request channel** + +The HPDcache can have the following number of in-flight write transactions: + + :math:`\scriptsize\mathsf{CONF\_HPDCACHE\_WBUF\_DIR\_ENTRIES}` + +Each in-flight transaction has a unique transaction ID. This ID is formatted as +follows: + + - For cacheable requests: + + The ID corresponds to the index of the entry in the write-buffer directory. + + ``wbuf_dir_index`` + + + - For uncacheable requests + + The HPDcache can issue up to 1 in-flight, uncached, write transaction. + Uncached transactions have a unique transaction ID with all bits set to 1. + + +Event signals +------------- + +In addition to the performance registers explained in :ref:`sec_perf_counters`, +the HPDcache provides a set of one-shot signals that indicate when a given event +is detected. These signals are set to 1 for one cycle each time the +corresponding event is detected. If the same event is detected N cycles in a +row, the corresponding event signal will remain set to 1 for N cycles. +:numref:`Table %s ` lists these event signals. + +These event signals are output-only. They can be either left unconnected, if +they are not used, or connected with the remainder of the system. The system can +use those signals, for example, for counting those events externally or for +triggering some specific actions. + +.. _tab_events: + +.. list-table:: Event Signals in the HPDcache + :widths: 31 13 52 + :header-rows: 1 + + * - **Signal** + - **Source** + - **Description** + * - ``evt_o.write_req`` + - Cache + - Write request accepted + * - ``evt_o.read_req`` + - Cache + - Read request accepted + * - ``evt_o.prefetch_req`` + - Cache + - Prefetch request accepted + * - ``evt_o.uncached_req`` + - Cache + - Uncached request accepted + * - ``evt_o.cmo_req`` + - Cache + - CMO request accepted + * - ``evt_o.accepted_req`` + - Cache + - One request accepted (any type) + * - ``evt_o.cache_write_miss`` + - Cache + - Write miss event + * - ``evt_o.cache_read_miss`` + - Cache + - Read miss event + * - ``evt_o.req_onhold`` + - Cache + - Request put on-hold in the RTAB + * - ``evt_o.req_onhold_mshr`` + - Cache + - Request put on-hold because of a MSHR conflict + * - ``evt_o.req_onhold_wbuf`` + - Cache + - Request put on-hold because of a WBUF conflict + * - ``evt_o.req_onhold_rollback`` + - Cache + - Request put on-hold (again) after a rollback + * - ``evt_o.stall`` + - Cache + - Cache stalls request event + diff --git a/hw/vendor/hpdcache/docs/source/overview.rst b/hw/vendor/hpdcache/docs/source/overview.rst new file mode 100644 index 00000000..bac3fe14 --- /dev/null +++ b/hw/vendor/hpdcache/docs/source/overview.rst @@ -0,0 +1,88 @@ +.. + Copyright 2024 CEA* + *Commissariat a l'Energie Atomique et aux Energies Alternatives (CEA) + + SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 + + Licensed under the Solderpad Hardware License v 2.1 (the “License”); you + may not use this file except in compliance with the License, or, at your + option, the Apache License version 2.0. You may obtain a copy of the + License at + + https://solderpad.org/licenses/SHL-2.1/ + + Unless required by applicable law or agreed to in writing, any work + distributed under the License is distributed on an “AS IS” BASIS, WITHOUT + WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + License for the specific language governing permissions and limitations + under the License. + + Authors : Cesar Fuguet + Description : HPDcache Overview + +Overview +======== + +This HPDcache is the responsible for serving data accesses issued by a RISC-V core, tightly-coupled accelerators and hardware memory prefetchers. +All these "clients" are called requesters. + +The HPDcache implements a hardware pipeline capable of serving one request per cycle. +An arbiter in the requesters’ interface of the HPDcache guarantees the correct behavior when there are multiple requesters. +This is illustrated in :numref:`Figure %s `. + +.. _fig_request_arbiter: + +.. figure:: images/hpdcache_highlevel_integration.* + :alt: High-Level View of the HPDcache Sub-System + :align: center + :width: 90% + + High-Level View of the HPDcache Sub-System + +List of features +---------------- + +- Support for multiple outstanding requests per requester. + +- Support for multiple outstanding read misses and writes to memory. + +- Processes one request per cycle. + +- Any given requester can access 1 to 32 bytes of a cacheline per cycle. + +- Reduced energy consumption by limiting the number of RAMs consulted per + request. + +- Fixed priority arbiter between requesters: the requester port with the lowest + index has the highest priority. + +- Non-allocate, write-through policy or allocate, write-back policy. Either one + or both are supported simultaneously at cacheline granularity. + +- Hardware write-buffer to mask the latency of write acknowledgements from + the memory system. + +- Compliance with RVWMO. + +- For address-overlapping transactions, the cache guarantees that these are + committed in the order in which they are consumed from the requesters. + +- For non-address-overlapping transactions, the cache may execute them in an + out-of-order fashion to improve performance. + +- Support for CMOs: cache invalidation operations, and memory fences for + multi-core synchronisation. Cache invalidation operations support the ones + defined in the RISC-V CMO Standard. + +- Memory-mapped CSRs for runtime configuration of the cache, status and + performance monitoring. + +- Ready-Valid, 5 channels (3 request/2 response), interface to the memory. This + interface, cache memory interface (CMI), can be easily adapted to mainstream + NoC interfaces like AMBA AXI [AXI2020]_. + +- An adapter for interfacing with AXI5 is provided. + +- External (optional), configurable, hardware, memory-prefetcher that supports + up to 4 simultaneous prefetching streams. + diff --git a/hw/vendor/hpdcache/docs/source/references.rst b/hw/vendor/hpdcache/docs/source/references.rst new file mode 100644 index 00000000..cb24eceb --- /dev/null +++ b/hw/vendor/hpdcache/docs/source/references.rst @@ -0,0 +1,37 @@ +.. + Copyright 2024 CEA* + *Commissariat a l'Energie Atomique et aux Energies Alternatives (CEA) + + SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 + + Licensed under the Solderpad Hardware License v 2.1 (the “License”); you + may not use this file except in compliance with the License, or, at your + option, the Apache License version 2.0. You may obtain a copy of the + License at + + https://solderpad.org/licenses/SHL-2.1/ + + Unless required by applicable law or agreed to in writing, any work + distributed under the License is distributed on an “AS IS” BASIS, WITHOUT + WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + License for the specific language governing permissions and limitations + under the License. + + Authors : Cesar Fuguet + Description : HPDcache References + + +References +========== + +.. [RISCVUP2019] The RISC-V Instruction Set Manual, Volume I: Unpriviledged ISA, + A. Waterman and K. Asanovic, 2019, + https://github.com/riscv/riscv-isa-manual/releases/download/Ratified-IMAFDQC/riscv-spec-20191213.pdf + +.. [RISCVP2019] The RISC-V Instruction Set Manual, Volume II: Priviledged + Architecture, + A. Waterman, K. Asanovic, and J. Hauser, 2021, + https://github.com/riscv/riscv-isa-manual/releases/download/Priv-v1.12/riscv-privileged-20211203.pdf + +.. [AXI2020] AMBA AXI and ACE Protocol Specification, ARM, 2020, + https://developer.arm.com/documentation/ihi0022/hc/?lang=en diff --git a/hw/vendor/hpdcache/rtl/fv/lnt/README.md b/hw/vendor/hpdcache/rtl/fv/lnt/README.md new file mode 100644 index 00000000..f04efab5 --- /dev/null +++ b/hw/vendor/hpdcache/rtl/fv/lnt/README.md @@ -0,0 +1,61 @@ +# Motivation +This directory contains a formal model of the HPDcache, describing its high-level architecture. +The model uses the LNT language [1], which is supported by the CADP toolbox [2]. + +# Overall architecture of the model +The model follows the architecture of the HPDCache [HPDcache User Guide from Oct 11 2024, fig 3.1], i.e. the parallel composition of one process per component. +These processes transmit the requests to each other using gates of channel type 'Wire'. +In addition, all component synchronize on a dedicated 'STATUS' gate enabling the controller to atomically access the local states of all components. + +# Files +- `main.lnt` : parallel composition of the HPDcache, along with a requester and a lower-level memory +- `hpdcache.lnt` : parallel compositions of the components & specification of the controller component +- `cachedata.lnt` : cache memory component with local data types and functions +- `misshandler.lnt` : miss handler component with local data types and functions +- `replaytable.lnt` : replay table buffer component with local data types and functions +- `writebuffer.lnt` : write buffer component with local data types and functions +- `types.lnt` : common data types +- `channels.lnt` : common channels (gate types) + +# Features +## Implemented features +- Write-through Policy with write-buffer supporting write-coalescing and multiple outstanding write requests +- Non-blocking pipeline with read under multiple misses +- Out-of-order execution of requests (replay) to prevent head-of-line blocking +- Ready-valid memory interface +- Multiple ports for requests + +## Yet to be implemented features +- Set-associative cache & miss handler allowing several sets +- Support for Atomic Memory Operations (AMOs) +- Support for Cache Management Operations (CMOs) +- Stride-based memory prefetcher + +# Configurability +The model parameters are configurable, by altering type or function definitions as indicated below. + +- number of data values (in module types.lnt): `type Data is NoData, D1, D2,` ... `DN` +- number of array locations (in module types.lnt): X ∈ {Memory, Cache, MSHR, RTAB, WBUF} + - type X is array [1..N] ... + - function X_NENTRIES is return N ... + - for X = Memory in addtion: `type Addr is NoAddr, A1, A2,` ... `AN` + - for X = Cache in addition: `type LRU_t is array [1..N] of Bool` +- number of requesters : + - *types.lnt* : `type SId is range 1..N of nat`... + - *main.lnt* : add new `|| CRI_REQ, CRI_RSP_R, CRI_RSP_W -> CORE [...] (K of SId)`s in the MAIN process parallel composition, with distinct SIds (denoted "K" above) from 1 to N. +- enable/disable the documentation fix from v5.0.1 (module replaytable.lnt): modify (constant) function fixed + +# Simulation and Verification +This formal LNT model can be simulated and verified using the [CADP toolbox](https://cadp.inria.fr/). + +# References +- [1] H. Garavel, F. Lang and W. Serwe. + From LOTOS to LNT. + In: ModelEd, TestEd, TrustEd-Essays Dedicated to Ed Brinksma on the Occasion of His 60th Birthday. + Lecture Notes in Computer Science, volume 10500, pp. 3-26, Springer Verlag, 2017. + +- [2] H. Garavel, F. Lang, R. Mateescu and W. Serwe. + CADP 2011: A Toolbox for the Construction and Analysis of Distributed Processes. + Springer International Journal on Software Tools for Technology Transfer 15(2):89-107, 2013. + https://cadp.inria.fr + diff --git a/hw/vendor/hpdcache/rtl/fv/lnt/cachedata.lnt b/hw/vendor/hpdcache/rtl/fv/lnt/cachedata.lnt new file mode 100644 index 00000000..d73dab08 --- /dev/null +++ b/hw/vendor/hpdcache/rtl/fv/lnt/cachedata.lnt @@ -0,0 +1,138 @@ +(* + * Copyright 2025 INRIA + * + * SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 + * + * Licensed under the Solderpad Hardware License v 2.1 (the “License”); you + * may not use this file except in compliance with the License, or, at your + * option, the Apache License version 2.0. You may obtain a copy of the + * License at + * + * https://solderpad.org/licenses/SHL-2.1/ + * + * Unless required by applicable law or agreed to in writing, any work + * distributed under the License is distributed on an “AS IS” BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * + * Authors : Zachary Assoumani, Wendelin Serwe + * Creation Date : March, 2025 + * Description : HPDcache LNT formal model cachedata + * History : +*) + + +module cachedata (types, channels) is + +------------------------------------------------------------------------------- +-- process managing the data stored in the cache. +------------------------------------------------------------------------------- + +process CACHEDATA [CONTROLLER_CACHE, CRI_RSP_R: Wire, STATUS: Statusgate, + DEBUG: any] is + access DEBUG; + var C: Cache, lru: LRU_t, a: Addr, d: Data, sid: SId, tid: TId, ind: nat in + -- initialize the cache + lru := LRU_t (false); + C := Cache (Invalid); + loop + STATUS (C, ?any WBUF_Buffer, ?any MSHR_Buffer, ?any RTAB_Array); + alt + -- read an address that is present in the cache + CONTROLLER_CACHE (Load, NoData, ?sid, ?tid, ?ind); + -- the read datum is sent as a response to the core + CRI_RSP_R (C[ind].D, sid, tid) + [] + -- write to a cache location + CONTROLLER_CACHE (Store, ?d, ?any SId, ?any TId, ?ind); + C[ind] := C[ind].{d -> d} + [] + -- refill + -- compute the index to be written to, and writes to it + CONTROLLER_CACHE (Refill, ?d, ?sid, ?tid, ?a); + ind := select_victim_way (C, lru); + eval update_plru (!?lru, ind); + C[ind] := Elt (a, d); + -- DEBUG ("lru", lru); + -- respond to the core + CRI_RSP_R (d, sid, tid) + [] + null + end alt + end loop + end var +end process + +------------------------------------------------------------------------------- + +function cache_match (C: Cache, a: Addr) : nat is + -- return the location of address a in cache C (or 0 if a is not in C) + var i: nat in + for i:=1 while i<=CACHE_NENTRIES by i:=i+1 loop + if (C[i] != Invalid) then + if (a==C[i].A) then + return i + end if + end if + end loop + end var; + return 0 +end function + +------------------------------------------------------------------------------- + +function select_victim_way (C: Cache, lru: LRU_t) : nat is + -- compute location to use to write new cacheline using LRU algorithm + var w: nat in + -- Return the first way (of the target set) whose valid bit is unset + for w:=1 while w<=CACHE_NENTRIES by w:=w+1 loop + if (C[w]==Invalid) then + return w -- the first empty location + end if + end loop; + + -- If all ways are valid, return the first way (of the target set) whose + -- LRU bit is unset + for w:=1 while w<=CACHE_NENTRIES by w:=w+1 loop + if (lru[w]==false) then + return w + end if + end loop + end var; + + -- This return statement should not be reached as there is always, at + -- least, one LRU bit unset (refer to update_plru) + -- /!\ except when there is only one way + return 1 +end function + +------------------------------------------------------------------------------- + +function update_plru (in out lru: LRU_t, way: nat) is + -- updates the bit table for the Least Recently Used algorithm + var w: nat in + -- set the LRU bit of the target set and way + lru[way] := true; + -- check if all LRU bits of the target "set" contain true + for w:=1 while w<=CACHE_NENTRIES by w:=w+1 loop + -- if there is at least one false, the update is done + if (lru[w] == false) then + return + end if + end loop; + -- other reset to false the LRU bits of all the ways except the one + -- being accessed + for w:=1 while w<=CACHE_NENTRIES by w:=w+1 loop + if (w != way) then + lru[way] := false + end if + end loop; + return + end var +end function + +------------------------------------------------------------------------------- + +end module \ No newline at end of file diff --git a/hw/vendor/hpdcache/rtl/fv/lnt/channels.lnt b/hw/vendor/hpdcache/rtl/fv/lnt/channels.lnt new file mode 100644 index 00000000..b8ea6436 --- /dev/null +++ b/hw/vendor/hpdcache/rtl/fv/lnt/channels.lnt @@ -0,0 +1,81 @@ +(* + * Copyright 2025 INRIA + * + * SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 + * + * Licensed under the Solderpad Hardware License v 2.1 (the “License”); you + * may not use this file except in compliance with the License, or, at your + * option, the Apache License version 2.0. You may obtain a copy of the + * License at + * + * https://solderpad.org/licenses/SHL-2.1/ + * + * Unless required by applicable law or agreed to in writing, any work + * distributed under the License is distributed on an “AS IS” BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * + * Authors : Zachary Assoumani, Wendelin Serwe + * Creation Date : March, 2025 + * Description : HPDcache LNT formal model channels + * History : +*) + + +module channels (types) is + +------------------------------------------------------------------------------- +-- Gates through which the components communicate with each other. +------------------------------------------------------------------------------- + +channel Wire is + -- CRI_REQ_R_W Requester => Controller, priority 3/3 + -- RTAB_REQ RTAB => Controller, priority 2/3 + -- CONTROLLER_CACHE with Refill + (o: Operation, d: Data, sid: SId, tid: TId, a: Addr), + + -- CONTROLLER_RTAB Controller => RTAB + (o: Operation, d: Data, sid: SId, tid: TId, a: Addr, deps: RTAB_deps), + + -- CONTROLLER_CACHE Controller => Cachedata, if hit + (o: Operation, d: Data, sid: SId, tid: TId, ind: nat), + + -- REFILL_REQ MSHR => Controller, priority 1/3 + -- CONTROLLER_WRITEBUFFER Controller => Write buffer, if write + -- CMI_RSP_R Memory => Cache, read + (d: Data, sid: SId, tid: TId, a: Addr), + + -- CRI_RSP_R Cache => Requester + -- CRI_RSP_W Cache => Requester + (d: Data, sid: SId, tid: TId), + + -- CONTROLLER_MSHR Controller => MSHR, if read miss + -- CMI_REQ_R Cache => Memory, read + (sid: SId, tid: TId, a: Addr), + + -- CMI_REQ_W Cache => Memory, write + -- CMI_RSP_W Memory => Cache, write + (a: Addr, wd: Data) +end channel + +------------------------------------------------------------------------------- +-- Special gate : used by the write buffer and the miss handler, to notify +-- the replay table about possible dependency solving. +------------------------------------------------------------------------------- +channel Notifgate is + (a: Addr) -- the conflicted address, now resolved +end channel + +------------------------------------------------------------------------------- +-- Special gate : used by the controller to consult the other components : +-- cache data, write buffer, miss handler, replay table. +-- The {cache, wbuf, mshr, rtab}_match() functions take an address as an input, and +-- return the index where it is located if it is, else zero. +------------------------------------------------------------------------------- +channel Statusgate is + (C: Cache, W: WBUF_Buffer, M: MSHR_Buffer, R: RTAB_Array) +end channel + +end module diff --git a/hw/vendor/hpdcache/rtl/fv/lnt/hpdcache.lnt b/hw/vendor/hpdcache/rtl/fv/lnt/hpdcache.lnt new file mode 100644 index 00000000..f57b069a --- /dev/null +++ b/hw/vendor/hpdcache/rtl/fv/lnt/hpdcache.lnt @@ -0,0 +1,248 @@ +(* + * Copyright 2025 INRIA + * + * SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 + * + * Licensed under the Solderpad Hardware License v 2.1 (the “License”); you + * may not use this file except in compliance with the License, or, at your + * option, the Apache License version 2.0. You may obtain a copy of the + * License at + * + * https://solderpad.org/licenses/SHL-2.1/ + * + * Unless required by applicable law or agreed to in writing, any work + * distributed under the License is distributed on an “AS IS” BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * + * Authors : Zachary Assoumani, Wendelin Serwe + * Creation Date : March, 2025 + * Description : HPDcache LNT formal model cache + * History : +*) + +module hpdcache (cachedata, misshandler, replaytable, writebuffer) is + +------------------------------------------------------------------------------- +------------------------------------------------------------------------------- +-- The full HPDcache, with its internal components. +------------------------------------------------------------------------------- +------------------------------------------------------------------------------- +process HPDCACHE [CRI_REQ, CRI_RSP_R, CRI_RSP_W, + CMI_REQ_R, CMI_REQ_W, CMI_RSP_R, CMI_RSP_W, + RTAB_REQ, REFILL_REQ, + CONTROLLER_CACHE, CONTROLLER_RTAB, CONTROLLER_WRITEBUFFER, + CONTROLLER_MSHR: Wire, + STATUS: Statusgate, WBUF_NOTIF, MSHR_NOTIF: Notifgate, + DEBUG: any] is + -- hide STATUS, RTAB_REQ, REFILL_REQ, CONTROLLER_CACHE, CONTROLLER_RTAB, + -- CONTROLLER_WRITEBUFFER, CONTROLLER_MSHR, WBUF_NOTIF, MSHR_NOTIF + par STATUS in + CRI_REQ, CMI_REQ_R, CMI_REQ_W, CMI_RSP_W, CMI_RSP_R, + RTAB_REQ, REFILL_REQ, CONTROLLER_CACHE, CONTROLLER_RTAB, + CONTROLLER_WRITEBUFFER, CONTROLLER_MSHR -> + CONTROLLER [...] + || + CONTROLLER_CACHE -> + CACHEDATA [CONTROLLER_CACHE, CRI_RSP_R, STATUS, DEBUG] + || + CONTROLLER_WRITEBUFFER, CMI_REQ_W, CMI_RSP_W, WBUF_NOTIF -> + WRITEBUFFER [CONTROLLER_WRITEBUFFER, CMI_REQ_W, CMI_RSP_W, WBUF_NOTIF, + STATUS, DEBUG] + || + CONTROLLER_MSHR, REFILL_REQ, CMI_REQ_R, CMI_RSP_R, MSHR_NOTIF -> + MSHR [CONTROLLER_MSHR, REFILL_REQ, CMI_REQ_R, CMI_RSP_R, MSHR_NOTIF, + STATUS, DEBUG] + || + CONTROLLER_RTAB, RTAB_REQ, CRI_REQ, WBUF_NOTIF, MSHR_NOTIF -> + RTAB [CONTROLLER_RTAB, RTAB_REQ, CRI_REQ, WBUF_NOTIF, MSHR_NOTIF, + STATUS, DEBUG] + end par +end process + + +------------------------------------------------------------------------------- +------------------------------------------------------------------------------- +-- Receives requests from requesters (Read Write); +-- from MSHR (Refill) +-- from RTAB (OnHold). +-- The pipeline sorts and sends them to the cache : +-- to the write-buffer, or MSHR, or RTAB. + +-- The conditions for sending to RTAB are as follows (cf. user guide 25 & 35): +-- (1) miss (or hit ?) & RTAB hit +-- (2) read-miss & (MSHR full/hit ou WBUF hit), +-- (3) write & WBUF full +-- (4) write-miss & (MSHR hit or WBUF hit sent) + +-- LOOP INVARIANT : an addr cannot be simultaneously in the WBUF and the MSHR. +-- A in MSHR => A not in cache. Writing on A will result in a write miss, +-- thus the request will be sent to RTAB (4) instead of the write buffer. +-- A in WBUF => a read-miss, because of (2), will be sent to RTAB instead +-- of MSHR. + +-- REMARK : about WBUF, the specification in unclear in case of sent hit. +-- page 35 : "Dependency [...] Write miss and there is a match with a sent +-- entry in the write buffer" +-- page 41 : "when there is an address conflict with a SENT entry, +-- the write access is put on-hold" [...] "The system may choose to +-- relax the constraint of putting a write on-hold in case of an +-- address conflict with a SENT entry" +-- but what if there is a match with a sent entry and a write miss ? +------------------------------------------------------------------------------- +------------------------------------------------------------------------------- +process CONTROLLER [CRI_REQ, RTAB_REQ, REFILL_REQ, CONTROLLER_CACHE, + CONTROLLER_RTAB, CONTROLLER_WRITEBUFFER, CONTROLLER_MSHR, + CRI_RSP_W: Wire, STATUS: Statusgate, DEBUG: any, + CMI_REQ_R, CMI_REQ_W, CMI_RSP_R, CMI_RSP_W: Wire] is + access DEBUG; + var C: Cache, W: WBUF_Buffer, M: MSHR_Buffer, R: RTAB_Array, + d: Data, sid: SId, tid: TId, a: Addr, ind: nat, + pending_refill: bool, refill_d: Data, refill_sid: SId, refill_tid: TId, + refill_a: Addr in + -- initializing variables to prevent TRAIAN warnings + -- written during refill phase 2, read during refill phase 3 + pending_refill := false; + refill_d := NoData; + refill_sid := 1 of SId; + refill_tid := 1 of TId; + refill_a := NoAddr; + loop + STATUS (?C, ?W, ?M, ?R); + alt + if (not (rtab_is_full (R))) then + CRI_REQ (Load, NoData, ?sid, ?tid, ?a); -- read + ind := cache_match (C, a); + if (rtab_match (R, a) != -1) then -- rtab hit + CONTROLLER_RTAB (Load, NoData, sid, tid, a, RtabHit) + elsif (ind != 0) then -- if cache-hit + CONTROLLER_CACHE (Load, NoData, sid, tid, ind) + else -- if cache-miss + if (mshr_match (M, a) != -1) then -- read-miss & mshr hit + CONTROLLER_RTAB (Load, NoData, sid, tid, a, MshrCollide) + elsif (wbuf_match (W, a) != -1) then -- read-miss & wbuf hit + CONTROLLER_RTAB (Load, NoData, sid, tid, a, WbufCollide) + else + CONTROLLER_MSHR (sid, tid, a) + end if + end if + end if + [] + if (not (rtab_is_full (R))) then + CRI_REQ (Store, ?d, ?sid, ?tid, ?a); -- write + if (rtab_match (R, a) != -1) then -- rtab hit + CONTROLLER_RTAB (Store, d, sid, tid, a, RtabHit) + elsif wbuf_is_full (W) then -- write & wbuf full + CONTROLLER_RTAB (Store, d, sid, tid, a, WbufFull) + elsif ((wbuf_match (W, a) != -1) and + (W.arr[Nat (wbuf_match (W, a))].s == Sent)) then + -- write & wbuf hit sent + CONTROLLER_RTAB (Store, d, sid, tid, a, WbufCollide) + else -- else the request is processed + ind := cache_match (C, a); + if (ind != 0) then -- if cache-hit + par + CONTROLLER_CACHE (Store, d, sid, tid, ind) + || + CONTROLLER_WRITEBUFFER (d, sid, tid, a) + end par; + CRI_RSP_W (d, sid, tid) + else + if (mshr_match (M, a) != -1) then -- write-miss & mshr hit + CONTROLLER_RTAB (Store, d, sid, tid, a, MshrCollide) + else + CONTROLLER_WRITEBUFFER (d, sid, tid, a); + CRI_RSP_W (d, sid, tid) + end if + end if + end if + end if + [] + -- same code as CRI_REQ for RTAB_REQ, except : + -- 1. with an additional transmission to CONTROLLER_RTAB (NoDeps) + -- if the request is successfully processed + -- 2. without stalling if RTAB is full + -- 3. without sending to RTAB in case of RTAB hit + RTAB_REQ (Load, NoData, ?sid, ?tid, ?a); -- lecture + ind := cache_match (C, a); + if (ind != 0) then -- if cache-hit + par + CONTROLLER_CACHE (Load, NoData, sid, tid, ind) + || + CONTROLLER_RTAB (Load, NoData, sid, tid, a, NoDeps) + end par + else -- if cache-miss + if (mshr_match (M, a) != -1) then -- read-miss & mshr hit + CONTROLLER_RTAB (Load, NoData, sid, tid, a, MshrCollide) + elsif (wbuf_match (W, a) != -1) then -- read-miss & wbuf hit + CONTROLLER_RTAB (Load, NoData, sid, tid, a, WbufCollide) + else + par + CONTROLLER_MSHR (sid, tid, a) + || + CONTROLLER_RTAB (Load, NoData, sid, tid, a, NoDeps) + end par + end if + end if + [] + RTAB_REQ (Store, ?d, ?sid, ?tid, ?a); -- write + if wbuf_is_full (W) then -- write & wbuf full + CONTROLLER_RTAB (Store, d, sid, tid, a, WbufFull) + elsif ((wbuf_match (W, a) != -1) and + (W.arr[Nat (wbuf_match (W, a))].s == Sent)) then + -- write & wbuf hit sent + CONTROLLER_RTAB (Store, d, sid, tid, a, WbufCollide) + else -- else the request is processed + ind := cache_match (C, a); + if (ind != 0) then -- if cache-hit + par + CONTROLLER_CACHE (Store, d, sid, tid, ind) + || + CONTROLLER_WRITEBUFFER (d, sid, tid, a) + || + CONTROLLER_RTAB (Store, d, sid, tid, a, NoDeps) + end par; + CRI_RSP_W (d, sid, tid) + else + if (mshr_match (M, a) != -1) then -- write-miss & mshr hit + CONTROLLER_RTAB (Store, d, sid, tid, a, MshrCollide) + else + par + CONTROLLER_WRITEBUFFER (d, sid, tid, a) + || + CONTROLLER_RTAB (Store, d, sid, tid, a, NoDeps) + end par; + CRI_RSP_W (d, sid, tid) + end if + end if + end if + [] + -- refill + CMI_RSP_R (?refill_d, ?refill_sid, ?refill_tid, ?refill_a); + pending_refill := true + [] + -- refill: phase 2 (with misshandler) + if (pending_refill) then + REFILL_REQ (refill_d, refill_sid, refill_tid, refill_a); + CONTROLLER_CACHE (Refill, refill_d, refill_sid, refill_tid, + refill_a); + pending_refill := false + end if + [] + -- if the cache is processing a memory response, it can't process + -- a core request at the same time + CMI_RSP_W (?any Addr, ?any Data) + [] + -- if the cache is sending a memory response, it can't process + -- a core request at the same time + CMI_REQ_R (?any SId, ?any TId, ?any Addr) + [] + CMI_REQ_W (?any Addr, ?any Data) + end alt + end loop + end var +end process + +end module diff --git a/hw/vendor/hpdcache/rtl/fv/lnt/main.lnt b/hw/vendor/hpdcache/rtl/fv/lnt/main.lnt new file mode 100644 index 00000000..36105988 --- /dev/null +++ b/hw/vendor/hpdcache/rtl/fv/lnt/main.lnt @@ -0,0 +1,135 @@ +(* + * Copyright 2025 INRIA + * + * SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 + * + * Licensed under the Solderpad Hardware License v 2.1 (the “License”); you + * may not use this file except in compliance with the License, or, at your + * option, the Apache License version 2.0. You may obtain a copy of the + * License at + * + * https://solderpad.org/licenses/SHL-2.1/ + * + * Unless required by applicable law or agreed to in writing, any work + * distributed under the License is distributed on an “AS IS” BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * + * Authors : Zachary Assoumani, Wendelin Serwe + * Creation Date : March, 2025 + * Description : HPDcache LNT formal model main module + * History : +*) + +module main (hpdcache) is + +-- Gate naming pattern + +-- "STATUS" : synchronisation between controller and other components + +-- "CONTROLLER_" : le controller sends data to composant +-- with in {CACHE, MSHR, RTAB, WRITEBUFFER} + +-- "{CMI,CRI}_{REQ,RESP}_{R,W}" : +-- CMI : Cache Memory Interface +-- CRI : Cache Requesters Interface +-- REQ : Request +-- RSP : Response +-- R : Read (= load) +-- W : Write (= store) + +-- "REFILL_REQ" : send back a request from the miss handler +-- "RTAB_REQ" : send back a request from the replay table + +-- {WBUF, MSHR}_NOTIF : notify from the {write buffer, miss handler} to the +-- replay table that a dependency has been resolved + +-- "DEBUG" : displaying + +------------------------------------------------------------------------------- +-- MAIN process +-- parallel composition of the requester, HPDcache, and the main memory. +------------------------------------------------------------------------------- + +process MAIN [CRI_REQ, CRI_RSP_R, CRI_RSP_W, -- interface CRI + CMI_REQ_R, CMI_REQ_W, CMI_RSP_R, CMI_RSP_W, -- interface CMI + RTAB_REQ, REFILL_REQ, + CONTROLLER_CACHE, CONTROLLER_RTAB, CONTROLLER_WRITEBUFFER, + CONTROLLER_MSHR: Wire, + STATUS: Statusgate, + WBUF_NOTIF, MSHR_NOTIF: Notifgate, + DEBUG: any] is + par + CRI_REQ, CRI_RSP_R, CRI_RSP_W -> + CORE [...] (1 of SId) + || + CRI_REQ, CRI_RSP_R, CRI_RSP_W, + CMI_REQ_R, CMI_REQ_W, CMI_RSP_R, CMI_RSP_W -> + HPDCACHE [CRI_REQ, CRI_RSP_R, CRI_RSP_W, + CMI_REQ_R, CMI_REQ_W, CMI_RSP_R, CMI_RSP_W, + RTAB_REQ, REFILL_REQ, + CONTROLLER_CACHE, CONTROLLER_RTAB, CONTROLLER_WRITEBUFFER, + CONTROLLER_MSHR, + STATUS, WBUF_NOTIF, MSHR_NOTIF, + DEBUG] + || + CMI_REQ_R, CMI_REQ_W, CMI_RSP_R, CMI_RSP_W -> + MEMORY [...] + end par +end process + +------------------------------------------------------------------------------- +-- requester (CPU/CORE) +------------------------------------------------------------------------------- + +process CORE [CRI_REQ, CRI_RSP_R, CRI_RSP_W: Wire] (sid: SId) is + -- sends requests, receives responses + var tid: TId, a: Addr, d: Data in + tid := 1 of TId; + loop + alt + -- requests + CRI_REQ (Load, NoData, sid, tid, ?a) where (a != NoAddr); + tid := incr(tid) + [] + CRI_REQ (Store, ?d, sid, tid, ?a) + where ((d != NoData) and (a != NoAddr)); + tid := incr(tid) + [] + -- responses from a request, along with its data and corresponding ID + CRI_RSP_R (?any Data, sid, ?any TId) + [] + CRI_RSP_W (?any Data, sid, ?any TId) + end alt + end loop + end var +end process + +------------------------------------------------------------------------------- +-- main memory +------------------------------------------------------------------------------- + +process MEMORY [CMI_REQ_R, CMI_REQ_W, CMI_RSP_R, CMI_RSP_W: Wire, + DEBUG: any] is + access DEBUG; + var M: Memory, a: Addr, sid: SId, tid: TId, wd: Data in + -- initialize memory : only D1 values + M := Memory (D1); + loop + alt + CMI_REQ_R (?sid, ?tid, ?a); + CMI_RSP_R (M[ord (a)], sid, tid, a) + [] + CMI_REQ_W (?a, ?wd); + M[ord (a)] := wd; + CMI_RSP_W (a, wd) + end alt + end loop + end var +end process + +------------------------------------------------------------------------------- + +end module \ No newline at end of file diff --git a/hw/vendor/hpdcache/rtl/fv/lnt/misshandler.lnt b/hw/vendor/hpdcache/rtl/fv/lnt/misshandler.lnt new file mode 100644 index 00000000..fcf93b14 --- /dev/null +++ b/hw/vendor/hpdcache/rtl/fv/lnt/misshandler.lnt @@ -0,0 +1,157 @@ +(* + * Copyright 2025 INRIA + * + * SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 + * + * Licensed under the Solderpad Hardware License v 2.1 (the “License”); you + * may not use this file except in compliance with the License, or, at your + * option, the Apache License version 2.0. You may obtain a copy of the + * License at + * + * https://solderpad.org/licenses/SHL-2.1/ + * + * Unless required by applicable law or agreed to in writing, any work + * distributed under the License is distributed on an “AS IS” BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * + * Authors : Zachary Assoumani, Wendelin Serwe + * Creation Date : March, 2025 + * Description : HPDcache LNT formal model miss handler + * History : +*) + +module misshandler (types, channels) is + +------------------------------------------------------------------------------- +-- queue keeping track of read-misses +-- fetches requests from the memory and then sends a refill to the cache +------------------------------------------------------------------------------- + +-- circular queue of (addr, rid, sid, bit d'attente) elements, cf. types.lnt + +-- INVARIANT 1: the locations between Front and Rear are those whose +-- addresses are not NoAddr +-- in mshr_enqueue() : rear++ <--> the address takes a value +-- in mshr_dequeue() : front++ <--> the address value is set to NoAddr +-- therefore mshr_match() only searches through valid locations in the queue + +-- INVARIANT 2: all the addresses in the queue are distinct +-- if the address is in the queue, the request is sent to the replay table + +process MSHR [CONTROLLER_MSHR, REFILL_REQ, CMI_REQ_R, CMI_RSP_R: Wire, + MSHR_NOTIF: Notifgate, STATUS: Statusgate, DEBUG: any] is + access DEBUG; + var buf: MSHR_Buffer, pending_refill: bool, refill_d: Data, refill_sid: SId, + refill_tid: TId, refill_a: Addr in + buf := MBuf (MSHR_Array (MEntry (NoAddr, 1 of SId, 1 of TId, false)), + -1, -1); + -- initialize the refill information + pending_refill := false; + refill_d := NoData; + refill_sid := 1 of SId; + refill_tid := 1 of TId; + refill_a := NoAddr; + loop + STATUS (?any Cache, ?any WBUF_Buffer, buf, ?any RTAB_Array); + alt + -- receives a read-miss and adds it to the buffer + var sid: SId, tid: TId, a: Addr in + CONTROLLER_MSHR (?sid, ?tid, ?a); + -- the following is ensured by a controller condition + assert (mshr_match (buf, a) == -1); + eval mshr_enqueue (!?buf, a, sid, tid) + end var + [] + -- sends a request to the memory from the buffer head + if ((buf.front!=-1) and (buf.arr[Nat (buf.front)].b == false)) then + var arr: MSHR_Array in + arr := buf.arr; + CMI_REQ_R (arr[Nat(buf.front)].sid, arr[Nat (buf.front)].tid, + arr[Nat (buf.front)].a); + arr[Nat (buf.front)] := arr[Nat (buf.front)].{b -> true}; + buf := buf.{arr -> arr} + end var + end if + [] + -- receives the memory responses, dequeues, + -- and sends a refill to the cache + CMI_RSP_R (?refill_d, ?refill_sid, ?refill_tid, ?refill_a); + pending_refill := true + [] + if (pending_refill) then + assert ((buf.arr[Nat (buf.front)].a == refill_a) and + (buf.arr[Nat (buf.front)].b == true)); + REFILL_REQ (refill_d, refill_sid, refill_tid, refill_a); + MSHR_NOTIF (refill_a); + eval mshr_dequeue (!?buf); + pending_refill := false + end if + [] + null + end alt + end loop + end var +end process + +------------------------------------------------------------------------------- + +function mshr_match (buf: MSHR_Buffer, a: Addr) : int is + -- returns the index to address a in the buffer buf, or -1 is a is not in buf + var i: int in + for i:=0 while i 0} + end if; + buf := buf.{rear -> (buf.rear + 1) mod MSHR_NENTRIES}; + var arr: MSHR_Array in + arr := buf.arr; + arr[Nat (buf.rear)] := MEntry (a, sid, tid, false); + buf := buf.{arr -> arr} + end var +end function + +------------------------------------------------------------------------------- + +function mshr_dequeue (in out buf: MSHR_Buffer) is + -- removes an element from the buffer buf + require (buf.front != -1); -- buffer not empty + var arr: MSHR_Array in + arr := buf.arr; + arr[Nat (buf.front)] := arr[Nat (buf.front)].{a -> NoAddr}; + buf := buf.{arr -> arr}; + -- updates the queue pointers + if (buf.front == buf.rear) then -- only one element + buf := buf.{front -> -1, rear -> -1} + else + buf := buf.{front -> (buf.front + 1) mod MSHR_NENTRIES} + end if + end var +end function + +------------------------------------------------------------------------------- + +function mshr_is_full (buf: MSHR_Buffer): Bool is + return ((buf.rear + 1) mod MSHR_NENTRIES == buf.front) +end function + +------------------------------------------------------------------------------- + +end module \ No newline at end of file diff --git a/hw/vendor/hpdcache/rtl/fv/lnt/replaytable.lnt b/hw/vendor/hpdcache/rtl/fv/lnt/replaytable.lnt new file mode 100644 index 00000000..764a21d7 --- /dev/null +++ b/hw/vendor/hpdcache/rtl/fv/lnt/replaytable.lnt @@ -0,0 +1,348 @@ +(* + * Copyright 2025 INRIA + * + * SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 + * + * Licensed under the Solderpad Hardware License v 2.1 (the “License”); you + * may not use this file except in compliance with the License, or, at your + * option, the Apache License version 2.0. You may obtain a copy of the + * License at + * + * https://solderpad.org/licenses/SHL-2.1/ + * + * Unless required by applicable law or agreed to in writing, any work + * distributed under the License is distributed on an “AS IS” BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * + * Authors : Zachary Assoumani, Wendelin Serwe + * Creation Date : March, 2025 + * Description : HPDcache LNT formal model replay table + * History : +*) + +module replaytable (types, channels) is + +------------------------------------------------------------------------------- +------------------------------------------------------------------------------- +-- Table keeping track of on-hold requests. +-- Only replays a request if the cause of the blocking is resolved. +------------------------------------------------------------------------------- +------------------------------------------------------------------------------- +-- REMARK : possible invariant, a rollback can only happen for full buffers ? +process RTAB [CONTROLLER_RTAB, RTAB_REQ, CRI_REQ: Wire, + WBUF_NOTIF, MSHR_NOTIF: Notifgate, + STATUS: Statusgate, DEBUG: any] is + access DEBUG; + var table: RTAB_Array, pop_state: pop_state_t, last: int, next: int, + o: Operation, d: Data, sid: SId, tid: TId, a: Addr, deps: RTAB_Deps, + req: RTAB_Request, ind: int in + table := RTAB_Array (REntry (false, NoDeps, false, false, 0, NoReq)); + pop_state := PS_HEAD; last := 0; next := 0; + loop + STATUS (?any Cache, ?any WBUF_Buffer, ?any MSHR_Buffer, table); + alt + -- receives a notification from the Write Buffer + WBUF_NOTIF (?a); + eval rtab_update_deps (!?table, a, NoDeps); + -- the address is in the replay table because of a WbufCollide + eval rtab_update_deps_all (!?table, WbufFull) + [] + -- receives a notification from the Miss Handler + MSHR_NOTIF (?a); + eval rtab_update_deps (!?table, a, NoDeps); + -- the address is in the replay table because of a WbufCollide + eval rtab_update_deps_all (!?table, MshrFull) + [] + -- sends a replayable request (heading, no dependency) to the controller + ind := rtab_pop_try (!?table, !?pop_state, !?last, !?next); + if (ind != -1) then + RTAB_REQ (table[Nat (ind)].request.o, + table[Nat (ind)].request.d, + table[Nat (ind)].request.sid, + table[Nat (ind)].request.tid, + table[Nat (ind)].request.a); + -- DEBUG (pop_state, last, next); + -- blocking until the controller response + CONTROLLER_RTAB (?o, ?d, ?sid, ?tid, ?a, ?deps); + assert (table[Nat (ind)].request == Req (o, d, sid, tid, a)); + if deps == NoDeps then + eval rtab_pop_commit (!?table, ind) + else + eval rtab_pop_rollback (!?table, ind, deps); + -- DEBUG ("rollback", pop_state, last, next); + -- "fixing" the bug, to allow state space generation + if (fixed) then + next := ind; + pop_state := PS_NEXT + end if + end if + else + -- replay table requests have higher priority than core requests + CRI_REQ (?any Operation, ?any Data, ?any SId, ?any TId, + ?any Addr); + alt + -- receives an on-hold request and inserts it in the buffer + -- the controller has already checked the on-hold conditions + CONTROLLER_RTAB (?o, ?d, ?sid, ?tid, ?a, ?deps); + req := Req (o, d, sid, tid, a); + if (deps == RtabHit) then + eval rtab_alloc_and_link (!?table, req) + else + eval rtab_alloc (!?table, req, deps) + end if + [] + null + end alt + end if + [] + null + end alt + end loop + end var +end process + +function fixed : bool is + return true +end function + +-- cf. guide page 36 to 39 +------------------------------------------------------------------------------- +-- In case of an on-hold condition : creates a new linked list. +------------------------------------------------------------------------------- +function rtab_alloc (in out rtab: RTAB_Array, r: RTAB_Request, d: RTAB_Deps) is + var index: int in + index := rtab_find_empty (rtab); + rtab[Nat (index)] := Rentry (true, d, true, true, 0, r) + end var +end function + +------------------------------------------------------------------------------- +-- If RTAB hit on an address : link the request at the tail. +------------------------------------------------------------------------------- +function rtab_alloc_and_link (in out rtab: RTAB_Array, r: RTAB_Request) is + var index: int, match: int in + index := rtab_find_empty (rtab); + match := rtab_match_tail (rtab, r.a); + rtab[Nat (match)] := rtab[Nat (match)].{ll_tail -> false}; + rtab[Nat (match)] := rtab[Nat (match)].{ll_next -> index}; + rtab[Nat (index)] := Rentry (true, NoDeps, false, true, 0, r) + end var +end function + +------------------------------------------------------------------------------- +-- Selects a request ready to be removed (dependencies have been resolved). +-- Global variables : pop_state = HEAD or NEXT, +-- last = head-of-list index, next = request index +-- HEAD : rtab_find_ready (last) finds the next ready head to be returned. +-- If it is not a tail, pop_state is set to NEXT. +-- NEXT : selects the next-th request to the returned. +-- If it is a tail then next is updated, +-- else pop_state is set to HEAD. +------------------------------------------------------------------------------- +function rtab_pop_try (in out rtab: RTAB_Array, in out pop_state: pop_state_t, + in out last: int, in out next: int): int is + var index: int in + -- Brief description of the following code: + -- The rtab_pop_try function tries to retire all the requests of a given + -- linked list. Then it passes to another one. + case pop_state in + PS_HEAD -> + -- Find a list whose head request is ready + -- (using a round-robin policy) + index := rtab_find_ready (rtab, last); + if (index == -1) then + return -1 + end if; + + -- Update the pointer to the last linked list served + last := index; + + -- If the list have more than one request, the next time this function + -- is called, serve the next request of the list + if (not (rtab[Nat (index)].ll_tail)) then + next := rtab[Nat (index)].ll_next; + pop_state := PS_NEXT + end if; + + -- Temporarily unset the head bit. This is to prevent the + -- request to be rescheduled. + rtab[Nat (index)] := rtab[Nat (index)].{ll_head -> false} + + | PS_NEXT -> + index := next; + + -- If the list have more than one request, the next time this + -- function is called, serve the next request of the list + if (not (rtab[Nat (index)].ll_tail)) then + next := rtab[Nat (index)].ll_next; + pop_state := PS_NEXT + -- It it is the last element of the list, return to the PS_HEAD state + else + pop_state := PS_HEAD + end if; + + -- Temporarily unset the head bit. This is to prevent the + -- request to be rescheduled. + rtab[Nat (index)] := rtab[Nat (index)].{ll_head -> false} + end case; + + -- Pop the selected request + return index + end var +end function + +type pop_state_t is + -- pop_state variable, used in rtab_pop_try() + PS_HEAD, PS_NEXT +end type + +------------------------------------------------------------------------------- +-- Called to confirm the removal of a request. +------------------------------------------------------------------------------- +function rtab_pop_commit (in out rtab: RTAB_Array, index: int) is + rtab[Nat (index)] := rtab[Nat (index)].{valid -> false} +end function + +------------------------------------------------------------------------------- +-- Called when the replayed request cannot be removed because a on-hold +-- condition is met. Restores the request into the RTAB with updated +-- dependency, at the same position in its corresponding linked list to respect +-- the program execution order. +------------------------------------------------------------------------------- +function rtab_pop_rollback (in out rtab: RTAB_Array, index: int, + deps: RTAB_Deps) is + rtab[Nat (index)] := rtab[Nat (index)].{ll_head -> true}; + rtab[Nat (index)] := rtab[Nat (index)].{deps -> deps} + -- missing update of the index to search for the next pop +end function + +------------------------------------------------------------------------------- +-- Finds a linked list whose head request can be replayed +-- (dependencies have been resolved). +------------------------------------------------------------------------------- +function rtab_find_ready (rtab: RTAB_Array, last: int): int is + var i: int in + -- choose a ready entry using a round-robin policy + i := (last + 1) mod RTAB_NENTRIES; + loop + -- ready entry found + if (rtab[Nat (i)].valid and rtab[Nat (i)].ll_head and + (rtab[Nat (i)].deps == NoDeps)) then + return i + + -- there is no ready entry + elsif i == last then + return -1 + end if; + + i := (i + 1) mod RTAB_NENTRIES + end loop + end var +end function + +------------------------------------------------------------------------------- +-- Cancels the dependency for address a. Called by MSHR or WBUF. +-- Uses the following rtab_match() function. +------------------------------------------------------------------------------- +function rtab_update_deps (in out rtab: RTAB_Array, a: Addr, + deps_new: RTAB_Deps) is + var index: int in + for index:=0 while index < RTAB_NENTRIES by index:=index+1 loop L in + if (rtab[Nat (index)].valid and + (rtab[Nat (index)].deps != deps_new) and + (rtab[Nat (index)].request.a == a)) then + rtab[Nat (index)] := rtab[Nat (index)].{deps -> deps_new}; + break L + end if + end loop + end var +end function + +------------------------------------------------------------------------------- +-- Altered version of the previous one : every occurrence of a given +-- dependency is removed. +------------------------------------------------------------------------------- +function rtab_update_deps_all (in out rtab: RTAB_Array, deps_old: RTAB_Deps) is + var index: int in + for index:=0 while index < RTAB_NENTRIES by index:=index+1 loop + if (rtab[Nat (index)].valid and (rtab[Nat (index)].deps == deps_old)) then + rtab[Nat (index)] := rtab[Nat (index)].{deps -> NoDeps} + end if + end loop + end var +end function + +------------------------------------------------------------------------------- +-- Utility function, used by rtab_alloc() and rtab_alloc_and_link(). +-- Returns an invalid (i.e. empty) location. +------------------------------------------------------------------------------- +function rtab_find_empty (rtab: RTAB_Array): int is + var i: int in + for i:=0 while i return first + | any -> return succ(tid) + end case +end function + +------------------------------------------------------------------------------- +-- types specific to the data cache +------------------------------------------------------------------------------- + +type Cache_Elt is + Invalid, -- initial value : empty cache location + Elt (A: Addr, D: Data) + with ==, !=, get, set +end type + +------------------------------------------------------------------------------- + +type Cache is + array [1..1] of Cache_Elt + -- array size must be equal to CACHE_NENTRIES +end type + +function CACHE_NENTRIES : nat is + return 1 +end function + +------------------------------------------------------------------------------- + +type LRU_t is + -- "Pseudo Least Recently Used", sect. 3.2.1 + -- array size must be equal to CACHE_NENTRIES + array [1..1] of Bool +end type + +------------------------------------------------------------------------------- +-- types specific to the replay table +------------------------------------------------------------------------------- + +type RTAB_Deps is + -- 5 possible dependencies for putting a request on hold + NoDeps, MshrFull, MshrCollide, WbufFull, WbufCollide, RtabHit + with ==, != +end type + +------------------------------------------------------------------------------- + +type RTAB_Request is + -- a request, as received by the controller and sent to the RTAB + NoReq, + Req (o: Operation, d: Data, sid: SId, tid: TId, a: Addr) + with ==, get +end type + +------------------------------------------------------------------------------- + +type RTAB_Entry is + NoEntry, + REntry (valid: Bool, -- indicates is the array location is occupied + deps: RTAB_Deps, + -- linked list structure + ll_head: Bool, ll_tail: Bool, ll_next: int, + request: RTAB_Request) + with get, set +end type + +------------------------------------------------------------------------------- + +type RTAB_Array is + array [0..1] of RTAB_Entry + -- array size must be equal to RTAB_NENTRIES +end type + +function RTAB_NENTRIES : int is + return 2 +end function + +------------------------------------------------------------------------------- +-- types specific to the miss handler +------------------------------------------------------------------------------- + +type MSHR_Entry is + MEntry (a: Addr, sid: SId, tid: TId, b: Bool) + -- b: waiting for a response from the memory + with get, set +end type + +------------------------------------------------------------------------------- + +type MSHR_Array is + array [0..1] of MSHR_Entry + -- array size must be equal to MSHR_NENTRIES + with set +end type + +function MSHR_NENTRIES : int is + return 2 +end function + +------------------------------------------------------------------------------- + +type MSHR_Buffer is + MBuf (arr: MSHR_Array, front: int, rear: int) + with get, set +end type + +------------------------------------------------------------------------------- +-- types specific to the write buffer +------------------------------------------------------------------------------- + +type WBUF_State is -- sect. 3.7.3 + Free, -- free location + Open, -- it has been written into + Pend, -- the counter is over, ready to send + -- /!\ here the counter is always set to 0 + -- so the Open phase is skipped directly to Pend + -- (increment the counter at every STATUS ?) + Sent -- sent entry, waiting for a confirmation + with ==, != +end type + +------------------------------------------------------------------------------- + +type WBUF_Entry is + WEntry (a: Addr, d: Data, cpt: nat, s: WBUF_State) + with ==, !=, get, set +end type + +------------------------------------------------------------------------------- + +type WBUF_Array is + array [0..1] of WBUF_Entry + -- array size must be equal to WBUF_NENTRIES +end type + +function WBUF_NENTRIES : int is + return 2 +end function + +------------------------------------------------------------------------------- + +type WBUF_Buffer is + WBuf (arr: WBUF_Array, front: int, rear: int) + with get, set +end type + +------------------------------------------------------------------------------- + +end module diff --git a/hw/vendor/hpdcache/rtl/fv/lnt/writebuffer.lnt b/hw/vendor/hpdcache/rtl/fv/lnt/writebuffer.lnt new file mode 100644 index 00000000..71ec0fb6 --- /dev/null +++ b/hw/vendor/hpdcache/rtl/fv/lnt/writebuffer.lnt @@ -0,0 +1,170 @@ +(* + * Copyright 2025 INRIA + * + * SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 + * + * Licensed under the Solderpad Hardware License v 2.1 (the “License”); you + * may not use this file except in compliance with the License, or, at your + * option, the Apache License version 2.0. You may obtain a copy of the + * License at + * + * https://solderpad.org/licenses/SHL-2.1/ + * + * Unless required by applicable law or agreed to in writing, any work + * distributed under the License is distributed on an “AS IS” BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * + * Authors : Zachary Assoumani, Wendelin Serwe + * Creation Date : March, 2025 + * Description : HPDcache LNT formal model write buffer + * History : +*) + +module writebuffer (types, channels) is + +------------------------------------------------------------------------------- +------------------------------------------------------------------------------- +-- Buffer keeping track of the writes, and sending them to the memory. +------------------------------------------------------------------------------- +------------------------------------------------------------------------------- +-- Circular queue of (addr, data, counter, state), cf. types.lnt + +-- INVARIANT 1 : the locations between Front and Rear are those whose +-- states are not Free. +-- in wbuf_enqueue() : rear++ <--> rear-th location goes from Free to Pend +-- in wbuf_dequeue() : front++ <--> front-th location goes from Sent to Free +-- therefore wbuf_match() only searches through valid locations in the queue. + +-- INVARIANT 2 : all the non-Free locations track distinct addresses. +-- in wbuf_enqueue(), address A added -> there is no non-Free A elsewhere + +process WRITEBUFFER [CONTROLLER_WRITEBUFFER, CMI_REQ_W, CMI_RSP_W: Wire, + WBUF_NOTIF: Notifgate, STATUS: Statusgate, DEBUG: any] is + access DEBUG; + var buf: WBUF_Buffer in + buf := WBuf (WBUF_Array (WEntry (NoAddr, NoData, 0, Free)), -1, -1); + loop + STATUS (?any Cache, buf, ?any MSHR_Buffer, ?any RTAB_Array); + alt + -- receives a request from the controller and adds it to the buffer : + -- Free => Pend + var d: Data, a: Addr, state: WBUF_State, ind: int in + CONTROLLER_WRITEBUFFER (?d, ?any SId, ?any TId, ?a); + ind := wbuf_match (buf, a); + if (ind == -1) then + state := Free + else + state := buf.arr[Nat (ind)].s -- Open or Pend + end if; + case state in + Free -> + eval wbuf_enqueue (!?buf, a, d) -- adds to the buffer + | Open | Pend -> + eval coalesce (!?buf, ind, d) -- coalesces + | Sent -> + -- impossible : ensured by a condition in the controller + raise unexpected + end case + end var + [] + -- send a request to the memory : Pend => Sent + if ((buf.front != -1) and (buf.arr[Nat(buf.front)].s == Pend)) then + var arr: WBUF_Array in + arr := buf.arr; + CMI_REQ_W (arr[Nat (buf.front)].a, arr[Nat (buf.front)].d); + arr[Nat (buf.front)] := arr[Nat (buf.front)].{s -> Sent}; + buf := buf.{arr -> arr} + end var + end if + [] + -- receives a confirmation from the memory : Dequeue, Sent => Free + var a: Addr, d: Data in + CMI_RSP_W (?a, ?d); + assert (buf.arr[Nat (buf.front)] == WEntry (a, d, 0, Sent)); + -- /!\ if counters are implemented, replace the previous '0' + eval wbuf_dequeue (!?buf); + WBUF_NOTIF (a) + end var + [] + null + end alt + end loop + end var +end process + +------------------------------------------------------------------------------- +-- Is an address in a non-Free location of the Write Buffer ? +------------------------------------------------------------------------------- +function wbuf_match (buf: WBUF_Buffer, a: Addr) : int is + var i: int in + for i:=0 while i d}; + buf := buf.{arr -> arr} + -- buf.arr[ind].d = d + end var +end function + +------------------------------------------------------------------------------- +-- Adds (a, d) to the Write Buffer. +------------------------------------------------------------------------------- +function wbuf_enqueue (in out buf: WBUF_Buffer, a: Addr, d: Data) is + if (wbuf_is_full (buf)) then -- full queue ! + return + end if; + if (buf.front == -1) then -- empty queue + buf := buf.{front -> 0} + end if; + buf := buf.{rear -> (buf.rear + 1) mod WBUF_NENTRIES}; + var arr: WBUF_Array in + arr := buf.arr; + arr[Nat (buf.rear)] := WEntry (a, d, 0, Pend); + -- /!\ if counters are implemented, replace the previous 'Pend' by 'Open' + buf := buf.{arr -> arr} + end var +end function + +------------------------------------------------------------------------------- +-- Removes an element from the Write Buffer. +------------------------------------------------------------------------------- +function wbuf_dequeue (in out buf: WBUF_Buffer) is + require (buf.front != -1); -- empty queue ! + var arr: WBUF_Array in + -- status of the emptied location is updated to Free + arr := buf.arr; + arr[Nat (buf.front)] := arr[Nat (buf.front)].{s -> Free}; + buf := buf.{arr -> arr}; + -- updates the queue pointers + if (buf.front == buf.rear) then -- un seul élément + buf := buf.{front -> -1, rear -> -1} + else + buf := buf.{front -> (buf.front + 1) mod WBUF_NENTRIES} + end if + end var +end function + +------------------------------------------------------------------------------- +-- Is the Write Buffer full ? +------------------------------------------------------------------------------- +function wbuf_is_full (buf: WBUF_Buffer): Bool is + return ((buf.rear + 1) mod WBUF_NENTRIES == buf.front) +end function + + +end module \ No newline at end of file diff --git a/hw/vendor/hpdcache/rtl/hpdcache.Flist b/hw/vendor/hpdcache/rtl/hpdcache.Flist new file mode 100644 index 00000000..a20fff2a --- /dev/null +++ b/hw/vendor/hpdcache/rtl/hpdcache.Flist @@ -0,0 +1,71 @@ +// +// Copyright 2023 CEA* +// *Commissariat a l'Energie Atomique et aux Energies Alternatives (CEA) +// +// SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 +// +// Licensed under the Solderpad Hardware License v 2.1 (the “License”); you +// may not use this file except in compliance with the License, or, at your +// option, the Apache License version 2.0. You may obtain a copy of the +// License at +// +// https://solderpad.org/licenses/SHL-2.1/ +// +// Unless required by applicable law or agreed to in writing, any work +// distributed under the License is distributed on an “AS IS” BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations +// under the License. +// +// +// Authors : Cesar Fuguet +// Creation Date : January, 2023 +// Description : File list for the HPDcache +// History : +// ++incdir+${HPDCACHE_DIR}/rtl/include +${HPDCACHE_DIR}/rtl/src/hpdcache_pkg.sv +${HPDCACHE_DIR}/rtl/src/utils/hpdcache_mem_req_read_arbiter.sv +${HPDCACHE_DIR}/rtl/src/utils/hpdcache_mem_req_write_arbiter.sv +${HPDCACHE_DIR}/rtl/src/common/hpdcache_demux.sv +${HPDCACHE_DIR}/rtl/src/common/hpdcache_lfsr.sv +${HPDCACHE_DIR}/rtl/src/common/hpdcache_sync_buffer.sv +${HPDCACHE_DIR}/rtl/src/common/hpdcache_fifo_reg.sv +${HPDCACHE_DIR}/rtl/src/common/hpdcache_fifo_reg_initialized.sv +${HPDCACHE_DIR}/rtl/src/common/hpdcache_fxarb.sv +${HPDCACHE_DIR}/rtl/src/common/hpdcache_rrarb.sv +${HPDCACHE_DIR}/rtl/src/common/hpdcache_mux.sv +${HPDCACHE_DIR}/rtl/src/common/hpdcache_decoder.sv +${HPDCACHE_DIR}/rtl/src/common/hpdcache_1hot_to_binary.sv +${HPDCACHE_DIR}/rtl/src/common/hpdcache_prio_1hot_encoder.sv +${HPDCACHE_DIR}/rtl/src/common/hpdcache_prio_bin_encoder.sv +${HPDCACHE_DIR}/rtl/src/common/hpdcache_sram.sv +${HPDCACHE_DIR}/rtl/src/common/hpdcache_sram_wbyteenable.sv +${HPDCACHE_DIR}/rtl/src/common/hpdcache_sram_watomenable.sv +${HPDCACHE_DIR}/rtl/src/common/hpdcache_sram_wmask.sv +${HPDCACHE_DIR}/rtl/src/common/hpdcache_regbank_wbyteenable_1rw.sv +${HPDCACHE_DIR}/rtl/src/common/hpdcache_regbank_wmask_1rw.sv +${HPDCACHE_DIR}/rtl/src/common/hpdcache_data_downsize.sv +${HPDCACHE_DIR}/rtl/src/common/hpdcache_data_upsize.sv +${HPDCACHE_DIR}/rtl/src/common/hpdcache_data_resize.sv +${HPDCACHE_DIR}/rtl/src/hwpf_stride/hwpf_stride_pkg.sv +${HPDCACHE_DIR}/rtl/src/hwpf_stride/hwpf_stride.sv +${HPDCACHE_DIR}/rtl/src/hwpf_stride/hwpf_stride_arb.sv +${HPDCACHE_DIR}/rtl/src/hwpf_stride/hwpf_stride_wrapper.sv +${HPDCACHE_DIR}/rtl/src/hpdcache.sv +${HPDCACHE_DIR}/rtl/src/hpdcache_amo.sv +${HPDCACHE_DIR}/rtl/src/hpdcache_cmo.sv +${HPDCACHE_DIR}/rtl/src/hpdcache_core_arbiter.sv +${HPDCACHE_DIR}/rtl/src/hpdcache_ctrl.sv +${HPDCACHE_DIR}/rtl/src/hpdcache_ctrl_pe.sv +${HPDCACHE_DIR}/rtl/src/hpdcache_memctrl.sv +${HPDCACHE_DIR}/rtl/src/hpdcache_cbuf.sv +${HPDCACHE_DIR}/rtl/src/hpdcache_miss_handler.sv +${HPDCACHE_DIR}/rtl/src/hpdcache_mshr.sv +${HPDCACHE_DIR}/rtl/src/hpdcache_rtab.sv +${HPDCACHE_DIR}/rtl/src/hpdcache_uncached.sv +${HPDCACHE_DIR}/rtl/src/hpdcache_victim_plru.sv +${HPDCACHE_DIR}/rtl/src/hpdcache_victim_random.sv +${HPDCACHE_DIR}/rtl/src/hpdcache_victim_sel.sv +${HPDCACHE_DIR}/rtl/src/hpdcache_wbuf.sv +${HPDCACHE_DIR}/rtl/src/hpdcache_flush.sv diff --git a/hw/vendor/hpdcache/rtl/include/hpdcache_typedef.svh b/hw/vendor/hpdcache/rtl/include/hpdcache_typedef.svh new file mode 100644 index 00000000..a56919d4 --- /dev/null +++ b/hw/vendor/hpdcache/rtl/include/hpdcache_typedef.svh @@ -0,0 +1,156 @@ +/* + * Copyright 2023 CEA* + * *Commissariat a l'Energie Atomique et aux Energies Alternatives (CEA) + * + * SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 + * + * Licensed under the Solderpad Hardware License v 2.1 (the “License”); you + * may not use this file except in compliance with the License, or, at your + * option, the Apache License version 2.0. You may obtain a copy of the + * License at + * + * https://solderpad.org/licenses/SHL-2.1/ + * + * Unless required by applicable law or agreed to in writing, any work + * distributed under the License is distributed on an “AS IS” BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +/* + * Authors : Cesar Fuguet + * Creation Date : February, 2023 + * Description : HPDcache Types' Definition + * History : + */ +`ifndef __HPDCACHE_TYPEDEF_SVH__ +`define __HPDCACHE_TYPEDEF_SVH__ + +`define HPDCACHE_DECL_MEM_REQ_T(__addr_t, __id_t) \ + struct packed { \ + __addr_t mem_req_addr; \ + hpdcache_pkg::hpdcache_mem_len_t mem_req_len; \ + hpdcache_pkg::hpdcache_mem_size_t mem_req_size; \ + __id_t mem_req_id; \ + hpdcache_pkg::hpdcache_mem_command_e mem_req_command; \ + hpdcache_pkg::hpdcache_mem_atomic_e mem_req_atomic; \ + logic mem_req_cacheable; \ + } + +`define HPDCACHE_USEREN_DECL_MEM_RESP_R_T(__id_t, __data_t, __user_t) \ + struct packed { \ + hpdcache_pkg::hpdcache_mem_error_e mem_resp_r_error; \ + __id_t mem_resp_r_id; \ + __data_t mem_resp_r_data; \ + logic mem_resp_r_last; \ + __user_t mem_resp_r_user; \ + } +`define HPDCACHE_DECL_MEM_RESP_R_T(__id_t, __data_t) \ + `HPDCACHE_USEREN_DECL_MEM_RESP_R_T(__id_t, __data_t, logic[0:0]) + +`define HPDCACHE_USEREN_DECL_MEM_REQ_W_T(__data_t, __be_t, __user_t) \ + struct packed { \ + __data_t mem_req_w_data; \ + __be_t mem_req_w_be; \ + logic mem_req_w_last; \ + __user_t mem_req_w_user; \ + } +`define HPDCACHE_DECL_MEM_REQ_W_T(__data_t, __be_t) \ + `HPDCACHE_USEREN_DECL_MEM_REQ_W_T(__data_t, __be_t, logic[0:0]) + +`define HPDCACHE_DECL_MEM_RESP_W_T(__id_t) \ + struct packed { \ + logic mem_resp_w_is_atomic; \ + hpdcache_pkg::hpdcache_mem_error_e mem_resp_w_error; \ + __id_t mem_resp_w_id; \ + } + +`define HPDCACHE_USEREN_TYPEDEF_MEM_ATTR_T(__addr_t, __id_t, __data_t, __be_t, __user_t, __params) \ + typedef logic [ __params.u.memAddrWidth-1:0] __addr_t; \ + typedef logic [ __params.u.memIdWidth-1:0] __id_t; \ + typedef logic [ __params.u.memDataWidth-1:0] __data_t; \ + typedef logic [__params.u.memDataWidth/8-1:0] __be_t; \ + typedef logic [ __params.u.wordUserWidth-1:0] __word_user_mem_t; \ + typedef __word_user_mem_t [ __params.wordsPerMemFlit-1:0] __user_t +`define HPDCACHE_TYPEDEF_MEM_ATTR_T(__addr_t, __id_t, __data_t, __be_t, __params) \ + `HPDCACHE_USEREN_TYPEDEF_MEM_ATTR_T(__addr_t, __id_t, __data_t, __be_t, unused_user_mem_attr_t, __params) + +`define HPDCACHE_TYPEDEF_MEM_REQ_T(__name__, __addr_t, __id_t) \ + typedef `HPDCACHE_DECL_MEM_REQ_T(__addr_t, __id_t) __name__ + +`define HPDCACHE_USEREN_TYPEDEF_MEM_RESP_R_T(__name__, __id_t, __data_t, __user_t) \ + typedef `HPDCACHE_USEREN_DECL_MEM_RESP_R_T(__id_t, __data_t, __user_t) __name__ + +`define HPDCACHE_TYPEDEF_MEM_RESP_R_T(__name__, __id_t, __data_t) \ + `HPDCACHE_USEREN_TYPEDEF_MEM_RESP_R_T(__name__, __id_t, __data_t, logic[0:0]) + +`define HPDCACHE_USEREN_TYPEDEF_MEM_REQ_W_T(__name__, __data_t, __be_t, __user_t) \ + typedef `HPDCACHE_USEREN_DECL_MEM_REQ_W_T(__data_t, __be_t, __user_t) __name__ + +`define HPDCACHE_TYPEDEF_MEM_REQ_W_T(__name__, __data_t, __be_t) \ + `HPDCACHE_USEREN_TYPEDEF_MEM_REQ_W_T(__name__, __data_t, __be_t, logic[0:0]) + +`define HPDCACHE_TYPEDEF_MEM_RESP_W_T(__name__, __id_t) \ + typedef `HPDCACHE_DECL_MEM_RESP_W_T(__id_t) __name__ + +`define HPDCACHE_USEREN_DECL_REQ_T(__offset_t, __data_t, __be_t, __sid_t, __tid_t, __tag_t, __user_t) \ + struct packed { \ + __offset_t addr_offset; \ + __data_t wdata; \ + hpdcache_pkg::hpdcache_req_op_t op; \ + __be_t be; \ + hpdcache_pkg::hpdcache_req_size_t size; \ + __sid_t sid; \ + __tid_t tid; \ + logic need_rsp; \ + logic phys_indexed; \ + __tag_t addr_tag; \ + hpdcache_pkg::hpdcache_pma_t pma; \ + __user_t wuser; \ + } + +`define HPDCACHE_DECL_REQ_T(__offset_t, __data_t, __be_t, __sid_t, __tid_t, __tag_t) \ + `HPDCACHE_USEREN_DECL_REQ_T(__offset_t, __data_t, __be_t, __sid_t, __tid_t, __tag_t, logic[0:0]) + +`define HPDCACHE_USEREN_TYPEDEF_REQ_ATTR_T(__offset_t, __word_t, __word_be_t, __data_t, __be_t, __sid_t, __tid_t, __tag_t, __user_t, __params) \ + typedef logic [ __params.tagWidth-1:0] __tag_t; \ + typedef logic [ __params.u.wordWidth-1:0] __word_t; \ + typedef logic [ __params.u.wordWidth/8-1:0] __word_be_t; \ + typedef logic [ __params.reqOffsetWidth-1:0] __offset_t; \ + typedef __word_t [ __params.u.reqWords-1:0] __data_t; \ + typedef __word_be_t [ __params.u.reqWords-1:0] __be_t; \ + typedef logic [ __params.u.reqSrcIdWidth-1:0] __sid_t; \ + typedef logic [__params.u.reqTransIdWidth-1:0] __tid_t; \ + typedef logic [ __params.u.wordUserWidth-1:0] __word_user_req_t; \ + typedef __word_user_req_t [ __params.u.reqWords-1:0] __user_t + +`define HPDCACHE_TYPEDEF_REQ_ATTR_T(__offset_t, __word_t, __word_be_t, __data_t, __be_t, __sid_t, __tid_t, __tag_t, __params) \ + `HPDCACHE_USEREN_TYPEDEF_REQ_ATTR_T(__offset_t, __word_t, __word_be_t, __data_t, __be_t, __sid_t, __tid_t, __tag_t, unused_user_req_attr_t, __params) + +`define HPDCACHE_USEREN_TYPEDEF_REQ_T(__name__, __offset_t, __data_t, __be_t, __sid_t, __tid_t, __tag_t, __user_t) \ + typedef `HPDCACHE_USEREN_DECL_REQ_T(__offset_t, __data_t, __be_t, __sid_t, __tid_t, __tag_t, __user_t) __name__ + +`define HPDCACHE_TYPEDEF_REQ_T(__name__, __offset_t, __data_t, __be_t, __sid_t, __tid_t, __tag_t) \ + `HPDCACHE_USEREN_TYPEDEF_REQ_T(__name__, __offset_t, __data_t, __be_t, __sid_t, __tid_t, __tag_t, logic[0:0]) + +`define HPDCACHE_USEREN_DECL_RSP_T(__data_t, __sid_t, __tid_t, __user_t) \ + struct packed { \ + __data_t rdata; \ + __sid_t sid; \ + __tid_t tid; \ + logic error; \ + logic aborted; \ + __user_t ruser; \ + } + +`define HPDCACHE_DECL_RSP_T(__data_t, __sid_t, __tid_t) \ + `HPDCACHE_USEREN_DECL_RSP_T(__data_t, __sid_t, __tid_t, logic[0:0]) + + +`define HPDCACHE_USEREN_TYPEDEF_RSP_T(__name__, __data_t, __sid_t, __tid_t, __user_t) \ + typedef `HPDCACHE_USEREN_DECL_RSP_T(__data_t, __sid_t, __tid_t, __user_t) __name__ + +`define HPDCACHE_TYPEDEF_RSP_T(__name__, __data_t, __sid_t, __tid_t) \ + `HPDCACHE_USEREN_TYPEDEF_RSP_T(__name__, __data_t, __sid_t, __tid_t, logic[0:0]) + +`endif // __HPDCACHE_TYPEDEF_SVH__ diff --git a/hw/vendor/hpdcache/rtl/lint/.gitignore b/hw/vendor/hpdcache/rtl/lint/.gitignore new file mode 100644 index 00000000..697cf4f4 --- /dev/null +++ b/hw/vendor/hpdcache/rtl/lint/.gitignore @@ -0,0 +1 @@ +obj_dir/ diff --git a/hw/vendor/hpdcache/rtl/lint/Makefile b/hw/vendor/hpdcache/rtl/lint/Makefile new file mode 100644 index 00000000..f5c0ce0c --- /dev/null +++ b/hw/vendor/hpdcache/rtl/lint/Makefile @@ -0,0 +1,33 @@ +HPDCACHE_DIR = ../.. +RM = rm -f + +export HPDCACHE_DIR + +HPDCACHE_FILES=$(shell grep '.*.sv' $(HPDCACHE_DIR)/rtl/hpdcache.Flist) + +.PHONY: verilator-lint verible-lint all + +verilator-lint: + verilator --cc \ + -Wall \ + -Wno-pinconnectempty \ + -Wno-fatal \ + -error-limit 100 \ + --top hpdcache_lint \ + -f $(HPDCACHE_DIR)/rtl/hpdcache.Flist \ + $(HPDCACHE_DIR)/rtl/src/common/macros/behav/*.sv \ + hpdcache_lint.sv + +verible-lint: + verible-verilog-lint \ + --rules_config verible_rules.cfg \ + --waiver_files verible.waiver \ + $(HPDCACHE_FILES) \ + $(HPDCACHE_DIR)/rtl/src/common/macros/behav/*.sv \ + hpdcache_lint.sv + +all: verilator-lint verible-lint + +.PHONY: clean +clean: + $(RM) -r obj_dir diff --git a/hw/vendor/hpdcache/rtl/lint/hpdcache_lint.sv b/hw/vendor/hpdcache/rtl/lint/hpdcache_lint.sv new file mode 100644 index 00000000..82460627 --- /dev/null +++ b/hw/vendor/hpdcache/rtl/lint/hpdcache_lint.sv @@ -0,0 +1,231 @@ +/* + * Copyright 2023 CEA* + * *Commissariat a l'Energie Atomique et aux Energies Alternatives (CEA) + * + * SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 + * + * Licensed under the Solderpad Hardware License v 2.1 (the “License”); you + * may not use this file except in compliance with the License, or, at your + * option, the Apache License version 2.0. You may obtain a copy of the + * License at + * + * https://solderpad.org/licenses/SHL-2.1/ + * + * Unless required by applicable law or agreed to in writing, any work + * distributed under the License is distributed on an “AS IS” BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +/* + * Authors : Cesar Fuguet + * Creation Date : April, 2024 + * Description : HPDcache Wrapper for Code Linting Check + * History : + */ +`include "hpdcache_typedef.svh" + +module hpdcache_lint + import hpdcache_pkg::*; +#( + localparam int unsigned HPDCACHE_NREQUESTERS = 1, + + localparam hpdcache_pkg::hpdcache_user_cfg_t HPDcacheUserCfg = '{ + nRequesters: HPDCACHE_NREQUESTERS, + paWidth: 56, + wordWidth: 64, + sets: 64, + ways: 8, + clWords: 8, + reqWords: 1, + reqTransIdWidth: 6, + reqSrcIdWidth: 3, + victimSel: hpdcache_pkg::HPDCACHE_VICTIM_RANDOM, + dataWaysPerRamWord: 2, + dataSetsPerRam: 64, + dataRamByteEnable: 1'b1, + accessWords: 8, + mshrSets: 32, + mshrWays: 2, + mshrWaysPerRamWord: 2, + mshrSetsPerRam: 32, + mshrRamByteEnable: 1'b1, + mshrUseRegbank: 1, + cbufEntries: 4, + refillCoreRspFeedthrough: 1'b1, + refillFifoDepth: 2, + wbufDirEntries: 16, + wbufDataEntries: 8, + wbufWords: 4, + wbufTimecntWidth: 3, + rtabEntries: 4, + flushEntries: 4, + flushFifoDepth: 2, + memAddrWidth: 56, + memIdWidth: 7, + memDataWidth: 512, + wtEn: 1'b1, + wbEn: 1'b1, + lowLatency: 1'b1 + }, + + localparam hpdcache_pkg::hpdcache_cfg_t HPDcacheCfg = hpdcache_pkg::hpdcacheBuildConfig( + HPDcacheUserCfg + ), + + localparam type hpdcache_mem_addr_t = logic [HPDcacheCfg.u.memAddrWidth-1:0], + localparam type hpdcache_mem_id_t = logic [HPDcacheCfg.u.memIdWidth-1:0], + localparam type hpdcache_mem_data_t = logic [HPDcacheCfg.u.memDataWidth-1:0], + localparam type hpdcache_mem_be_t = logic [HPDcacheCfg.u.memDataWidth/8-1:0], + localparam type hpdcache_mem_req_t = + `HPDCACHE_DECL_MEM_REQ_T(hpdcache_mem_addr_t, hpdcache_mem_id_t), + localparam type hpdcache_mem_resp_r_t = + `HPDCACHE_DECL_MEM_RESP_R_T(hpdcache_mem_id_t, hpdcache_mem_data_t), + localparam type hpdcache_mem_req_w_t = + `HPDCACHE_DECL_MEM_REQ_W_T(hpdcache_mem_data_t, hpdcache_mem_be_t), + localparam type hpdcache_mem_resp_w_t = + `HPDCACHE_DECL_MEM_RESP_W_T(hpdcache_mem_id_t), + + localparam type hpdcache_tag_t = logic [HPDcacheCfg.tagWidth-1:0], + localparam type hpdcache_data_word_t = logic [HPDcacheCfg.u.wordWidth-1:0], + localparam type hpdcache_data_be_t = logic [HPDcacheCfg.u.wordWidth/8-1:0], + localparam type hpdcache_req_offset_t = logic [HPDcacheCfg.reqOffsetWidth-1:0], + localparam type hpdcache_req_data_t = hpdcache_data_word_t [HPDcacheCfg.u.reqWords-1:0], + localparam type hpdcache_req_be_t = hpdcache_data_be_t [HPDcacheCfg.u.reqWords-1:0], + localparam type hpdcache_req_sid_t = logic [HPDcacheCfg.u.reqSrcIdWidth-1:0], + localparam type hpdcache_req_tid_t = logic [HPDcacheCfg.u.reqTransIdWidth-1:0], + localparam type hpdcache_req_t = + `HPDCACHE_DECL_REQ_T(hpdcache_req_offset_t, + hpdcache_req_data_t, + hpdcache_req_be_t, + hpdcache_req_sid_t, + hpdcache_req_tid_t, + hpdcache_tag_t), + localparam type hpdcache_rsp_t = + `HPDCACHE_DECL_RSP_T(hpdcache_req_data_t, + hpdcache_req_sid_t, + hpdcache_req_tid_t), + + localparam type hpdcache_wbuf_timecnt_t = logic [HPDcacheCfg.u.wbufTimecntWidth-1:0] +) + +( + input logic clk_i, + input logic rst_ni, + + input logic wbuf_flush_i, + output logic wbuf_empty_o, + + input logic core_req_valid_i [HPDCACHE_NREQUESTERS], + output logic core_req_ready_o [HPDCACHE_NREQUESTERS], + input hpdcache_req_t core_req_i [HPDCACHE_NREQUESTERS], + input logic core_req_abort_i [HPDCACHE_NREQUESTERS], + input hpdcache_tag_t core_req_tag_i [HPDCACHE_NREQUESTERS], + input hpdcache_pkg::hpdcache_pma_t core_req_pma_i [HPDCACHE_NREQUESTERS], + output logic core_rsp_valid_o [HPDCACHE_NREQUESTERS], + output hpdcache_rsp_t core_rsp_o [HPDCACHE_NREQUESTERS], + + input logic mem_req_read_ready_i, + output logic mem_req_read_valid_o, + output hpdcache_mem_req_t mem_req_read_o, + + output logic mem_resp_read_ready_o, + input logic mem_resp_read_valid_i, + input hpdcache_mem_resp_r_t mem_resp_read_i, + + input logic mem_req_write_ready_i, + output logic mem_req_write_valid_o, + output hpdcache_mem_req_t mem_req_write_o, + + input logic mem_req_write_data_ready_i, + output logic mem_req_write_data_valid_o, + output hpdcache_mem_req_w_t mem_req_write_data_o, + + output logic mem_resp_write_ready_o, + input logic mem_resp_write_valid_i, + input hpdcache_mem_resp_w_t mem_resp_write_i +); + + hpdcache #( + .HPDcacheCfg (HPDcacheCfg), + .wbuf_timecnt_t (hpdcache_wbuf_timecnt_t), + .hpdcache_tag_t (hpdcache_tag_t), + .hpdcache_data_word_t (hpdcache_data_word_t), + .hpdcache_data_be_t (hpdcache_data_be_t), + .hpdcache_req_offset_t(hpdcache_req_offset_t), + .hpdcache_req_data_t (hpdcache_req_data_t), + .hpdcache_req_be_t (hpdcache_req_be_t), + .hpdcache_req_sid_t (hpdcache_req_sid_t), + .hpdcache_req_tid_t (hpdcache_req_tid_t), + .hpdcache_req_t (hpdcache_req_t), + .hpdcache_rsp_t (hpdcache_rsp_t), + .hpdcache_mem_addr_t (hpdcache_mem_addr_t), + .hpdcache_mem_id_t (hpdcache_mem_id_t), + .hpdcache_mem_data_t (hpdcache_mem_data_t), + .hpdcache_mem_be_t (hpdcache_mem_be_t), + .hpdcache_mem_req_t (hpdcache_mem_req_t), + .hpdcache_mem_req_w_t (hpdcache_mem_req_w_t), + .hpdcache_mem_resp_r_t(hpdcache_mem_resp_r_t), + .hpdcache_mem_resp_w_t(hpdcache_mem_resp_w_t) + ) i_hpdcache ( + .clk_i, + .rst_ni, + + .wbuf_flush_i, + + .core_req_valid_i, + .core_req_ready_o, + .core_req_i, + .core_req_abort_i, + .core_req_tag_i, + .core_req_pma_i, + + .core_rsp_valid_o, + .core_rsp_o, + + .mem_req_read_ready_i, + .mem_req_read_valid_o, + .mem_req_read_o, + + .mem_resp_read_ready_o, + .mem_resp_read_valid_i, + .mem_resp_read_i, + + .mem_req_write_ready_i, + .mem_req_write_valid_o, + .mem_req_write_o, + + .mem_req_write_data_ready_i, + .mem_req_write_data_valid_o, + .mem_req_write_data_o, + + .mem_resp_write_ready_o, + .mem_resp_write_valid_i, + .mem_resp_write_i, + + .evt_cache_write_miss_o( /* unused */), + .evt_cache_read_miss_o ( /* unused */), + .evt_uncached_req_o ( /* unused */), + .evt_cmo_req_o ( /* unused */), + .evt_write_req_o ( /* unused */), + .evt_read_req_o ( /* unused */), + .evt_prefetch_req_o ( /* unused */), + .evt_req_on_hold_o ( /* unused */), + .evt_rtab_rollback_o ( /* unused */), + .evt_stall_refill_o ( /* unused */), + .evt_stall_o ( /* unused */), + + .wbuf_empty_o, + + .cfg_enable_i (1'b1), + .cfg_wbuf_threshold_i (3'd2), + .cfg_wbuf_reset_timecnt_on_write_i (1'b1), + .cfg_wbuf_sequential_waw_i (1'b0), + .cfg_wbuf_inhibit_write_coalescing_i(1'b0), + .cfg_prefetch_updt_plru_i (1'b1), + .cfg_error_on_cacheable_amo_i (1'b0), + .cfg_rtab_single_entry_i (1'b0), + .cfg_default_wb_i (1'b0) + ); + +endmodule /* hpdcache_lint */ diff --git a/hw/vendor/hpdcache/rtl/lint/verible.waiver b/hw/vendor/hpdcache/rtl/lint/verible.waiver new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/hw/vendor/hpdcache/rtl/lint/verible.waiver @@ -0,0 +1 @@ + diff --git a/hw/vendor/hpdcache/rtl/lint/verible_rules.cfg b/hw/vendor/hpdcache/rtl/lint/verible_rules.cfg new file mode 100644 index 00000000..7f9fc314 --- /dev/null +++ b/hw/vendor/hpdcache/rtl/lint/verible_rules.cfg @@ -0,0 +1,16 @@ +# checks that all lines do not exceed the maximum allowed length +line-length=length:100 + +# checks that non-type parameter and localparam names follow at least one of +# the naming conventions from a choice of CamelCase and ALL_CAPS, ORed +# together with the pipe-symbol(|) +parameter-name-style=localparam_style:CamelCase|ALL_CAPS;parameter_style:CamelCase|ALL_CAPS + +# checks that no tabs are used. Spaces should be used instead of tabs +no-tabs + +# checks that there are no trailing spaces on any lines +no-trailing-spaces + +# checks that signal names use lower_snake_case naming convention +signal-name-style diff --git a/hw/vendor/hpdcache/rtl/src/common/hpdcache_1hot_to_binary.sv b/hw/vendor/hpdcache/rtl/src/common/hpdcache_1hot_to_binary.sv new file mode 100644 index 00000000..df0d6309 --- /dev/null +++ b/hw/vendor/hpdcache/rtl/src/common/hpdcache_1hot_to_binary.sv @@ -0,0 +1,60 @@ +/* + * Copyright 2023 CEA* + * *Commissariat a l'Energie Atomique et aux Energies Alternatives (CEA) + * + * SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 + * + * Licensed under the Solderpad Hardware License v 2.1 (the “License”); you + * may not use this file except in compliance with the License, or, at your + * option, the Apache License version 2.0. You may obtain a copy of the + * License at + * + * https://solderpad.org/licenses/SHL-2.1/ + * + * Unless required by applicable law or agreed to in writing, any work + * distributed under the License is distributed on an “AS IS” BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +/* + * Authors : Cesar Fuguet + * Creation Date : April, 2021 + * Description : One-hot to binary decoder + * History : + */ +module hpdcache_1hot_to_binary + // Parameters + // {{{ +#( + parameter int unsigned N = 0, + localparam int unsigned Log2N = N > 1 ? $clog2(N) : 1, + localparam type in_t = logic unsigned [N-1:0], + localparam type out_t = logic unsigned [Log2N-1:0] +) + // }}} + + // Ports + // {{{ +( + input in_t val_i, + output out_t val_o +); + // }}} + + always_comb + begin : decode_comb + val_o = 0; + for (int unsigned i = 0; i < N; i++) begin + if (val_i[i]) val_o = out_t'(i); + end + + // Assertions + // {{{ +`ifndef HPDCACHE_ASSERT_OFF + // FIXME: The final keyword is not supported by the Spyglass linter + // assert final ($onehot0(val_i)) else $error("val_i shall be onehot or zero"); +`endif + // }}} + end +endmodule diff --git a/hw/vendor/hpdcache/rtl/src/common/hpdcache_data_downsize.sv b/hw/vendor/hpdcache/rtl/src/common/hpdcache_data_downsize.sv new file mode 100644 index 00000000..30aed4cc --- /dev/null +++ b/hw/vendor/hpdcache/rtl/src/common/hpdcache_data_downsize.sv @@ -0,0 +1,191 @@ +/* + * Copyright 2023 CEA* + * *Commissariat a l'Energie Atomique et aux Energies Alternatives (CEA) + * + * SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 + * + * Licensed under the Solderpad Hardware License v 2.1 (the “License”); you + * may not use this file except in compliance with the License, or, at your + * option, the Apache License version 2.0. You may obtain a copy of the + * License at + * + * https://solderpad.org/licenses/SHL-2.1/ + * + * Unless required by applicable law or agreed to in writing, any work + * distributed under the License is distributed on an “AS IS” BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +/* + * Authors : Cesar Fuguet + * Creation Date : November 22, 2022 + * Description : Refill data downsize + * History : + */ +module hpdcache_data_downsize +// {{{ +import hpdcache_pkg::*; +// Parameters +// {{{ +#( + parameter int WR_WIDTH = 0, + parameter int RD_WIDTH = 0, + parameter int DEPTH = 0, + + localparam type wdata_t = logic [WR_WIDTH-1:0], + localparam type rdata_t = logic [RD_WIDTH-1:0] +) +// }}} +// Ports +// {{{ +( + input logic clk_i, + input logic rst_ni, + + input logic w_i, + output logic wok_o, + input wdata_t wdata_i, + + input logic r_i, + output logic rok_o, + output rdata_t rdata_o, + output logic rlast_o +); +// }}} +// Architecture +// {{{ + // Local definitions + // {{{ + localparam int RD_WORDS = WR_WIDTH/RD_WIDTH; + localparam int PTR_WIDTH = $clog2(DEPTH); + localparam int WORDCNT_WIDTH = $clog2(RD_WORDS); + typedef logic [PTR_WIDTH-1:0] bufptr_t; + typedef logic [WORDCNT_WIDTH-1:0] wordptr_t; + typedef logic [PTR_WIDTH:0] occupancy_t; + // }}} + + // Internal registers and signals + // {{{ + rdata_t [DEPTH-1:0][RD_WORDS-1:0] buf_q; + bufptr_t wrptr_q, wrptr_d; + bufptr_t rdptr_q, rdptr_d; + occupancy_t used_q, used_d; + wordptr_t [DEPTH-1:0] words_q, words_d; + logic words_set; + logic full, empty; + // }}} + + // Control-Path + // {{{ + assign full = (hpdcache_uint'(used_q) == DEPTH); + assign empty = (used_q == 0); + assign wok_o = ~full; + assign rok_o = ~empty; + + always_comb + begin : ctrl_comb + automatic logic used_inc, used_dec; + automatic logic words_dec; + + rdptr_d = rdptr_q; + wrptr_d = wrptr_q; + used_dec = 1'b0; + used_inc = 1'b0; + words_dec = 1'b0; + words_set = 1'b0; + + if (w_i && wok_o) begin + used_inc = 1'b1; + words_set = 1'b1; + if (hpdcache_uint'(wrptr_q) == (DEPTH-1)) begin + wrptr_d = 0; + end else begin + wrptr_d = wrptr_q + 1; + end + end + + if (r_i && rok_o) begin + words_dec = (words_q[rdptr_q] > 0); + if (words_q[rdptr_q] == 0) begin + used_dec = 1'b1; + if (hpdcache_uint'(rdptr_q) == (DEPTH-1)) begin + rdptr_d = 0; + end else begin + rdptr_d = rdptr_q + 1; + end + end + end + + case ({used_inc, used_dec}) + 2'b10 : used_d = used_q + 1; + 2'b01 : used_d = used_q - 1; + default: used_d = used_q; + endcase + + words_d = words_q; + if (words_set) begin + words_d[wrptr_q] = wordptr_t'(RD_WORDS - 1); + end + if (words_dec) begin + words_d[rdptr_q] = words_q[rdptr_q] - 1; + end + end + + assign rlast_o = rok_o & (words_q[rdptr_q] == 0); + + always_ff @(posedge clk_i or negedge rst_ni) + begin : ctrl_ff + if (!rst_ni) begin + rdptr_q <= 0; + wrptr_q <= 0; + used_q <= 0; + words_q <= 0; + end else begin + rdptr_q <= rdptr_d; + wrptr_q <= wrptr_d; + used_q <= used_d; + words_q <= words_d; + end + end + // }}} + + // Data-Path + // {{{ + always_ff @(posedge clk_i or negedge rst_ni) + begin : buf_ff + if (!rst_ni) begin + buf_q <= '0; + end else begin + if (words_set) begin + buf_q[wrptr_q] <= wdata_i; + end + end + end + + assign rdata_o = buf_q[rdptr_q][RD_WORDS - hpdcache_uint'(words_q[rdptr_q]) - 1]; + // }}} + + // Assertions + // {{{ +`ifndef HPDCACHE_ASSERT_OFF + if (DEPTH <= 0) begin : gen_depth_assertion + $fatal(1, "DEPTH must be greater than 0"); + end + if (WR_WIDTH <= 0) begin : gen_wr_width_assertion + $fatal(1, "WR_WIDTH must be greater than 0"); + end + if (RD_WIDTH <= 0) begin : gen_rd_width_assertion + $fatal(1, "RD_WIDTH must be greater than 0"); + end + if (RD_WIDTH >= WR_WIDTH) begin : gen_rd_wr_ratio_assertion + $fatal(1, "RD_WIDTH must be less to WR_WIDTH"); + end + if ((WR_WIDTH % RD_WIDTH) != 0) begin : gen_wr_multiple_of_rd_assertion + $fatal(1, "WR_WIDTH must be a multiple of RD_WIDTH"); + end +`endif + // }}} +// }}} +endmodule +// }}} diff --git a/hw/vendor/hpdcache/rtl/src/common/hpdcache_data_resize.sv b/hw/vendor/hpdcache/rtl/src/common/hpdcache_data_resize.sv new file mode 100644 index 00000000..769a2d19 --- /dev/null +++ b/hw/vendor/hpdcache/rtl/src/common/hpdcache_data_resize.sv @@ -0,0 +1,129 @@ +/* + * Copyright 2023 CEA* + * *Commissariat a l'Energie Atomique et aux Energies Alternatives (CEA) + * + * SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 + * + * Licensed under the Solderpad Hardware License v 2.1 (the “License”); you + * may not use this file except in compliance with the License, or, at your + * option, the Apache License version 2.0. You may obtain a copy of the + * License at + * + * https://solderpad.org/licenses/SHL-2.1/ + * + * Unless required by applicable law or agreed to in writing, any work + * distributed under the License is distributed on an “AS IS” BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +/* + * Authors : Cesar Fuguet + * Creation Date : September, 2024 + * Description : Data Resizer + * History : +m*/ +module hpdcache_data_resize +// {{{ +import hpdcache_pkg::*; +// Parameters +// {{{ +#( + parameter int WR_WIDTH = 0, + parameter int RD_WIDTH = 0, + parameter int DEPTH = 0, + + localparam type wdata_t = logic [WR_WIDTH-1:0], + localparam type rdata_t = logic [RD_WIDTH-1:0] +) +// }}} +// Ports +// {{{ +( + input logic clk_i, + input logic rst_ni, + + input logic w_i, + output logic wok_o, + input wdata_t wdata_i, + input logic wlast_i, + + input logic r_i, + output logic rok_o, + output rdata_t rdata_o, + output logic rlast_o +); +// }}} + +// Upsizer +// {{{ +if (WR_WIDTH < RD_WIDTH) begin : gen_upsize + hpdcache_data_upsize #( + .WR_WIDTH (WR_WIDTH), + .RD_WIDTH (RD_WIDTH), + .DEPTH (DEPTH) + ) upsizer_i( + .clk_i, + .rst_ni, + + .w_i, + .wlast_i, + .wok_o, + .wdata_i, + + .r_i, + .rok_o, + .rdata_o + ); + + assign rlast_o = 1'b1; +end +// }}} +// Downsizer +// {{{ +else if (WR_WIDTH > RD_WIDTH) begin : gen_downsize + hpdcache_data_downsize #( + .WR_WIDTH (WR_WIDTH), + .RD_WIDTH (RD_WIDTH), + .DEPTH (DEPTH) + ) downsize_i( + .clk_i, + .rst_ni, + + .w_i, + .wok_o, + .wdata_i, + + .r_i, + .rok_o, + .rdata_o, + .rlast_o + ); +end +// }}} +// FIFO +// {{{ +else begin : gen_noresize + hpdcache_fifo_reg #( + .FIFO_DEPTH (DEPTH), + .FEEDTHROUGH (1'b0), + .fifo_data_t (wdata_t) + ) fifo_i( + .clk_i, + .rst_ni, + + .w_i, + .wok_o, + .wdata_i, + + .r_i, + .rok_o, + .rdata_o + ); + + assign rlast_o = 1'b1; +end +// }}} + +endmodule +// }}} diff --git a/hw/vendor/hpdcache/rtl/src/common/hpdcache_data_upsize.sv b/hw/vendor/hpdcache/rtl/src/common/hpdcache_data_upsize.sv new file mode 100644 index 00000000..357f03d2 --- /dev/null +++ b/hw/vendor/hpdcache/rtl/src/common/hpdcache_data_upsize.sv @@ -0,0 +1,181 @@ +/* + * Copyright 2023 CEA* + * *Commissariat a l'Energie Atomique et aux Energies Alternatives (CEA) + * + * SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 + * + * Licensed under the Solderpad Hardware License v 2.1 (the “License”); you + * may not use this file except in compliance with the License, or, at your + * option, the Apache License version 2.0. You may obtain a copy of the + * License at + * + * https://solderpad.org/licenses/SHL-2.1/ + * + * Unless required by applicable law or agreed to in writing, any work + * distributed under the License is distributed on an “AS IS” BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +/* + * Authors : Cesar Fuguet + * Creation Date : November 22, 2022 + * Description : Refill data upsize + * History : + */ +module hpdcache_data_upsize +// {{{ +import hpdcache_pkg::*; +// Parameters +// {{{ +#( + parameter int WR_WIDTH = 0, + parameter int RD_WIDTH = 0, + parameter int DEPTH = 0, + + localparam type wdata_t = logic [WR_WIDTH-1:0], + localparam type rdata_t = logic [RD_WIDTH-1:0] +) +// }}} +// Ports +// {{{ +( + input logic clk_i, + input logic rst_ni, + + input logic w_i, + output logic wok_o, + input wdata_t wdata_i, + input logic wlast_i, + + input logic r_i, + output logic rok_o, + output rdata_t rdata_o +); +// }}} +// Architecture +// {{{ + // Local definitions + // {{{ + localparam int WR_WORDS = RD_WIDTH/WR_WIDTH; + localparam int PTR_WIDTH = $clog2(DEPTH); + localparam int WORDCNT_WIDTH = $clog2(WR_WORDS); + typedef logic [PTR_WIDTH-1:0] bufptr_t; + typedef logic [WORDCNT_WIDTH-1:0] wordptr_t; + typedef logic [PTR_WIDTH:0] occupancy_t; + // }}} + + // Internal registers and signals + // {{{ + wdata_t [DEPTH-1:0][WR_WORDS-1:0] buf_q; + bufptr_t wrptr_q, wrptr_d; + bufptr_t rdptr_q, rdptr_d; + occupancy_t used_q, used_d; + wordptr_t [DEPTH-1:0] words_q, words_d; + logic full, empty; + logic shift; + // }}} + + // Control-Path + // {{{ + assign full = (hpdcache_uint'(used_q) == DEPTH), + empty = (used_q == 0), + wok_o = ~full, + rok_o = ~empty; + + always_comb + begin : ctrl_comb + automatic logic used_inc, used_dec; + automatic logic words_inc, words_reset; + + wrptr_d = wrptr_q; + rdptr_d = rdptr_q; + words_d = words_q; + used_dec = 1'b0; + used_inc = 1'b0; + words_reset = 1'b0; + words_inc = 1'b0; + shift = 1'b0; + + if (w_i && wok_o) begin + shift = 1'b1; + words_inc = (hpdcache_uint'(words_q[wrptr_q]) < (WR_WORDS-1)); + if (hpdcache_uint'(words_q[wrptr_q]) == (WR_WORDS-1) || wlast_i) begin + used_inc = 1'b1; + if (hpdcache_uint'(wrptr_q) == (DEPTH-1)) begin + wrptr_d = 0; + end else begin + wrptr_d = wrptr_q + 1; + end + end + end + + if (r_i && rok_o) begin + used_dec = 1'b1; + words_reset = 1'b1; + if (hpdcache_uint'(rdptr_q) == (DEPTH-1)) begin + rdptr_d = 0; + end else begin + rdptr_d = rdptr_q + 1; + end + end + + case ({used_inc, used_dec}) + 2'b10 : used_d = used_q + 1; + 2'b01 : used_d = used_q - 1; + default: used_d = used_q; + endcase + + if (words_inc) words_d[wrptr_q] = words_q[wrptr_q] + 1; + if (words_reset) words_d[rdptr_q] = 0; + end + + + always_ff @(posedge clk_i or negedge rst_ni) + begin : ctrl_ff + if (!rst_ni) begin + rdptr_q <= 0; + wrptr_q <= 0; + used_q <= 0; + words_q <= '0; + end else begin + rdptr_q <= rdptr_d; + wrptr_q <= wrptr_d; + used_q <= used_d; + words_q <= words_d; + end + end + // }}} + + // Data-Path + // {{{ + always_ff @(posedge clk_i or negedge rst_ni) + begin : buf_ff + if (!rst_ni) begin + buf_q <= '0; + end else begin + if (shift) begin + buf_q[wrptr_q][words_q[wrptr_q]] <= wdata_i; + end + end + end + + assign rdata_o = buf_q[rdptr_q]; + // }}} + + // Assertions + // {{{ +`ifndef HPDCACHE_ASSERT_OFF + initial + begin : initial_assertions + assert (DEPTH > 0) else $error("DEPTH must be greater than 0"); + assert (WR_WIDTH > 0) else $error("WR_WIDTH must be greater than 0"); + assert (RD_WIDTH > 0) else $error("RD_WIDTH must be greater than 0"); + assert (WR_WIDTH < RD_WIDTH) else $error("WR_WIDTH must be less to RD_WIDTH"); + assert ((RD_WIDTH % WR_WIDTH) == 0) else $error("RD_WIDTH must be a multiple WR_WIDTH"); + end +`endif + // }}} +// }}} +endmodule +// }}} diff --git a/hw/vendor/hpdcache/rtl/src/common/hpdcache_decoder.sv b/hw/vendor/hpdcache/rtl/src/common/hpdcache_decoder.sv new file mode 100644 index 00000000..0f7e7322 --- /dev/null +++ b/hw/vendor/hpdcache/rtl/src/common/hpdcache_decoder.sv @@ -0,0 +1,53 @@ +/* + * Copyright 2023 CEA* + * *Commissariat a l'Energie Atomique et aux Energies Alternatives (CEA) + * + * SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 + * + * Licensed under the Solderpad Hardware License v 2.1 (the “License”); you + * may not use this file except in compliance with the License, or, at your + * option, the Apache License version 2.0. You may obtain a copy of the + * License at + * + * https://solderpad.org/licenses/SHL-2.1/ + * + * Unless required by applicable law or agreed to in writing, any work + * distributed under the License is distributed on an “AS IS” BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +/* + * Authors : Cesar Fuguet + * Creation Date : April, 2021 + * Description : Decoder + * History : + */ +module hpdcache_decoder + // Parameters + // {{{ +#( + parameter int unsigned N = 0, + localparam int unsigned Pow2N = 2**N, + localparam type in_t = logic unsigned [N-1:0], + localparam type out_t = logic unsigned [Pow2N-1:0] +) + // }}} + + // Ports + // {{{ +( + input logic en_i, + input in_t val_i, + output out_t val_o +); + // }}} + + always_comb + begin : decoder_comb + val_o = 0; + for (int unsigned i = 0; i < Pow2N; i++) begin + if (val_i == in_t'(i)) val_o[i] = en_i; + end + end +endmodule diff --git a/hw/vendor/hpdcache/rtl/src/common/hpdcache_demux.sv b/hw/vendor/hpdcache/rtl/src/common/hpdcache_demux.sv new file mode 100644 index 00000000..f5495c9e --- /dev/null +++ b/hw/vendor/hpdcache/rtl/src/common/hpdcache_demux.sv @@ -0,0 +1,72 @@ +/* + * Copyright 2023 CEA* + * *Commissariat a l'Energie Atomique et aux Energies Alternatives (CEA) + * + * SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 + * + * Licensed under the Solderpad Hardware License v 2.1 (the “License”); you + * may not use this file except in compliance with the License, or, at your + * option, the Apache License version 2.0. You may obtain a copy of the + * License at + * + * https://solderpad.org/licenses/SHL-2.1/ + * + * Unless required by applicable law or agreed to in writing, any work + * distributed under the License is distributed on an “AS IS” BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +/* + * Authors : Cesar Fuguet + * Creation Date : April, 2021 + * Description : Simple multiplexor + * History : + */ +module hpdcache_demux +// Parameters +// {{{ +#( + // Number of outputs + parameter int unsigned NOUTPUT = 0, + + // Width in bits of each input + parameter int unsigned DATA_WIDTH = 0, + + // Selector signal is one-hot encoded + parameter bit ONE_HOT_SEL = 0, + + // Compute the width of the selection signal + localparam int unsigned NOUTPUT_LOG2 = $clog2(NOUTPUT), + localparam int unsigned SEL_WIDTH = ONE_HOT_SEL ? NOUTPUT : NOUTPUT_LOG2, + + localparam type data_t = logic [DATA_WIDTH-1:0], + localparam type sel_t = logic [SEL_WIDTH-1:0] +) +// }}} + +// Ports +// {{{ +( + input data_t data_i, + input sel_t sel_i, + output data_t [NOUTPUT-1:0] data_o +); +// }}} + + if (ONE_HOT_SEL) begin : gen_onehot_sel + always_comb + begin : demux_comb + for (int unsigned i = 0; i < NOUTPUT; i++) begin + data_o[i] = sel_i[i] ? data_i : '0; + end + end + end else begin : gen_bin_sel + always_comb + begin : demux_comb + for (int unsigned i = 0; i < NOUTPUT; i++) begin + data_o[i] = (sel_t'(i) == sel_i) ? data_i : '0; + end + end + end +endmodule diff --git a/hw/vendor/hpdcache/rtl/src/common/hpdcache_fifo_reg.sv b/hw/vendor/hpdcache/rtl/src/common/hpdcache_fifo_reg.sv new file mode 100644 index 00000000..4085e335 --- /dev/null +++ b/hw/vendor/hpdcache/rtl/src/common/hpdcache_fifo_reg.sv @@ -0,0 +1,176 @@ +/* + * Copyright 2023 CEA* + * *Commissariat a l'Energie Atomique et aux Energies Alternatives (CEA) + * + * SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 + * + * Licensed under the Solderpad Hardware License v 2.1 (the “License”); you + * may not use this file except in compliance with the License, or, at your + * option, the Apache License version 2.0. You may obtain a copy of the + * License at + * + * https://solderpad.org/licenses/SHL-2.1/ + * + * Unless required by applicable law or agreed to in writing, any work + * distributed under the License is distributed on an “AS IS” BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +/* + * Authors : Cesar Fuguet + * Creation Date : April, 2021 + * Description : FIFO buffer (using registers) + * History : + */ +module hpdcache_fifo_reg + // Parameters + // {{{ +#( + parameter int unsigned FIFO_DEPTH = 0, + parameter bit FEEDTHROUGH = 1'b0, + parameter type fifo_data_t = logic +) + // }}} + // Ports + // {{{ +( + input logic clk_i, + input logic rst_ni, + input logic w_i, + output logic wok_o, + input fifo_data_t wdata_i, + input logic r_i, + output logic rok_o, + output fifo_data_t rdata_o +); + // }}} + + // Declaration of constants, types and functions + // {{{ + localparam int __FIFO_DEPTH = FIFO_DEPTH > 1 ? FIFO_DEPTH : 2; + typedef logic unsigned [$clog2(__FIFO_DEPTH)-1:0] fifo_addr_t; + // }}} + + /* + * Bypass: no buffering + */ + if (FIFO_DEPTH == 0) begin : gen_bypass + assign wok_o = r_i; + assign rok_o = w_i; + assign rdata_o = wdata_i; + + /* + * Single-entry buffer -> synchronization buffer + */ + end else if (FIFO_DEPTH == 1) begin : gen_buffer + hpdcache_sync_buffer #( + .FEEDTHROUGH (FEEDTHROUGH), + .data_t (fifo_data_t) + ) i_sync_buffer ( + .clk_i, + .rst_ni, + .w_i, + .wok_o, + .wdata_i, + .r_i, + .rok_o, + .rdata_o + ); + + /* + * Multi-entry FIFO buffer + */ + end else if (FIFO_DEPTH > 1) begin : gen_fifo + // Declaration of internal wires and registers + // {{{ + fifo_data_t [FIFO_DEPTH-1:0] fifo_mem_q; + fifo_addr_t rptr_q, rptr_d; // read pointer + fifo_addr_t wptr_q, wptr_d; // write pointer + logic crossover_q, crossover_d; // write pointer has wrap + logic rexec, wexec; + logic rptr_max, wptr_max; + logic match_ptr; + logic empty, full; + // }}} + + // Global control signals + // {{{ + assign match_ptr = (wptr_q == rptr_q); + + assign empty = match_ptr & ~crossover_q, + full = match_ptr & crossover_q; + + assign rok_o = ~empty | (FEEDTHROUGH & w_i), + wok_o = ~full | (FEEDTHROUGH & r_i); + + assign rexec = r_i & ~empty, + wexec = w_i & (( FEEDTHROUGH & ((empty & ~r_i) | (full & r_i) | (~full & ~empty))) | + (~FEEDTHROUGH & ~full)); + + // }}} + + // Control of read and write pointers + // {{{ + assign rptr_max = (rptr_q == fifo_addr_t'(FIFO_DEPTH-1)); + assign wptr_max = (wptr_q == fifo_addr_t'(FIFO_DEPTH-1)); + + always_comb + begin : fifo_ctrl_comb + rptr_d = rptr_q; + wptr_d = wptr_q; + crossover_d = crossover_q; + + if (rexec) begin + rptr_d = rptr_max ? 0 : rptr_q + 1; + end + + if (wexec) begin + wptr_d = wptr_max ? 0 : wptr_q + 1; + end + + if (wexec && wptr_max) begin + crossover_d = 1'b1; + end else if (rexec && rptr_max) begin + crossover_d = 1'b0; + end + end + // }}} + + // FIFO buffer memory management + // {{{ + always_ff @(posedge clk_i) + begin + if (wexec) fifo_mem_q[wptr_q] <= wdata_i; + end + + assign rdata_o = FEEDTHROUGH && empty ? wdata_i : fifo_mem_q[rptr_q]; + // }}} + + // Setting of internal state + // {{{ + always_ff @(posedge clk_i or negedge rst_ni) + begin + if (!rst_ni) begin + rptr_q <= 0; + wptr_q <= 0; + crossover_q <= 1'b0; + end else begin + rptr_q <= rptr_d; + wptr_q <= wptr_d; + crossover_q <= crossover_d; + end + end + // }}} + + // Assertions + // {{{ +`ifndef HPDCACHE_ASSERT_OFF + rptr_ahead_wptr_assert: assert property (@(posedge clk_i) disable iff (rst_ni !== 1'b1) + ((rptr_q <= wptr_q) && !crossover_q) || + ((rptr_q >= wptr_q) && crossover_q)) else + $error("fifo: read pointer is ahead of the write pointer"); +`endif + // }}} + end +endmodule diff --git a/hw/vendor/hpdcache/rtl/src/common/hpdcache_fifo_reg_initialized.sv b/hw/vendor/hpdcache/rtl/src/common/hpdcache_fifo_reg_initialized.sv new file mode 100644 index 00000000..b4d9575a --- /dev/null +++ b/hw/vendor/hpdcache/rtl/src/common/hpdcache_fifo_reg_initialized.sv @@ -0,0 +1,147 @@ +/* + * Copyright 2023 CEA* + * *Commissariat a l'Energie Atomique et aux Energies Alternatives (CEA) + * + * SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 + * + * Licensed under the Solderpad Hardware License v 2.1 (the “License”); you + * may not use this file except in compliance with the License, or, at your + * option, the Apache License version 2.0. You may obtain a copy of the + * License at + * + * https://solderpad.org/licenses/SHL-2.1/ + * + * Unless required by applicable law or agreed to in writing, any work + * distributed under the License is distributed on an “AS IS” BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +/* + * Authors : Noelia Oliete, Cesar Fuguet + * Creation Date : September, 2023 + * Description : FIFO buffer (using registers) + * Based on design of Ivan Miro-Panades + * History : + */ +module hpdcache_fifo_reg_initialized + // Parameters + // {{{ +#( + parameter int unsigned FIFO_DEPTH = 0, + parameter type fifo_data_t = logic +) + // }}} + // Ports + // {{{ +( + input logic clk_i, + input logic rst_ni, + input logic w_i, + output logic wok_o, + input fifo_data_t wdata_i, + input logic r_i, + output logic rok_o, + output fifo_data_t rdata_o, + input fifo_data_t [FIFO_DEPTH-1:0] initial_value_i +); + // }}} + + // Declaration of constants, types and functions + // {{{ + typedef logic unsigned [$clog2(FIFO_DEPTH)-1:0] fifo_addr_t; + // }}} + + // Declaration of internal wires and registers + // {{{ + fifo_data_t [FIFO_DEPTH-1:0] fifo_mem_q; + fifo_addr_t rptr_q, rptr_d; // read pointer + fifo_addr_t wptr_q, wptr_d; // write pointer + logic crossover_q, crossover_d; // write pointer has wrap + logic rexec, wexec; + logic rptr_max, wptr_max; + logic match_ptr; + // }}} + + // Global control signals + // {{{ + assign match_ptr = (wptr_q == rptr_q); + assign rok_o = match_ptr ? crossover_q : 1'b1; + assign wok_o = match_ptr ? ~crossover_q : 1'b1; + assign rexec = rok_o & r_i; + assign wexec = wok_o & w_i; + // }}} + + // Control of read and write pointers + // {{{ + assign rptr_max = (rptr_q == fifo_addr_t'(FIFO_DEPTH-1)); + assign wptr_max = (wptr_q == fifo_addr_t'(FIFO_DEPTH-1)); + + always_comb + begin : rptr_comb + if (rexec) begin + rptr_d = rptr_max ? 0 : rptr_q + 1; + end else begin + rptr_d = rptr_q; + end + end + + always_comb + begin : wptr_comb + if (wexec) begin + wptr_d = wptr_max ? 0 : wptr_q + 1; + end else begin + wptr_d = wptr_q; + end + end + + always_comb + begin : crossover_comb + if (rexec && rptr_max) begin + crossover_d = 1'b0; + end else if (wexec && wptr_max) begin + crossover_d = 1'b1; + end else begin + crossover_d = crossover_q; + end + end + // }}} + + // FIFO buffer memory management + // {{{ + always_ff @(posedge clk_i) + begin + if (!rst_ni) begin + fifo_mem_q <= initial_value_i; + end else if (wexec) fifo_mem_q[wptr_q] <= wdata_i; + end + + assign rdata_o = fifo_mem_q[rptr_q]; + // }}} + + // Setting of internal state + // {{{ + always_ff @(posedge clk_i or negedge rst_ni) + begin + if (!rst_ni) begin + rptr_q <= 0; + wptr_q <= 0; + crossover_q <= 1'b1; + end else begin + rptr_q <= rptr_d; + wptr_q <= wptr_d; + crossover_q <= crossover_d; + end + end + // }}} + + // Assertions + // {{{ +`ifndef HPDCACHE_ASSERT_OFF + rptr_ahead_wptr_assert: assert property (@(posedge clk_i) disable iff (rst_ni !== 1'b1) + ((rptr_q <= wptr_q) && !crossover_q) || ((rptr_q >= wptr_q) && crossover_q)) else + $error("fifo: read pointer is ahead of the write pointer"); +`endif + // }}} + +endmodule diff --git a/hw/vendor/hpdcache/rtl/src/common/hpdcache_fxarb.sv b/hw/vendor/hpdcache/rtl/src/common/hpdcache_fxarb.sv new file mode 100644 index 00000000..4e3d571c --- /dev/null +++ b/hw/vendor/hpdcache/rtl/src/common/hpdcache_fxarb.sv @@ -0,0 +1,87 @@ +/* + * Copyright 2023 CEA* + * *Commissariat a l'Energie Atomique et aux Energies Alternatives (CEA) + * + * SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 + * + * Licensed under the Solderpad Hardware License v 2.1 (the “License”); you + * may not use this file except in compliance with the License, or, at your + * option, the Apache License version 2.0. You may obtain a copy of the + * License at + * + * https://solderpad.org/licenses/SHL-2.1/ + * + * Unless required by applicable law or agreed to in writing, any work + * distributed under the License is distributed on an “AS IS” BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +/* + * Authors : Cesar Fuguet + * Creation Date : April, 2021 + * Description : Fixed-Priority Arbiter + * History : + */ +module hpdcache_fxarb + // Parameters + // {{{ +#( + // Number of requesters + parameter int unsigned N = 0 +) + // }}} + // Ports + // {{{ +( + input logic clk_i, + input logic rst_ni, + input logic [N-1:0] req_i, + output logic [N-1:0] gnt_o, + input logic ready_i +); + // }}} + + // Declaration of internal wires and registers + // {{{ + logic [N-1:0] gnt_q, gnt; + logic wait_q; + // }}} + + // Compute the grant vector + // {{{ + hpdcache_prio_1hot_encoder #(.N(N)) prio_msk_i (.val_i(req_i), .val_o(gnt)); + // }}} + + // Compute the output grant vector + // {{{ + assign gnt_o = req_i & (wait_q ? gnt_q : gnt); + // }}} + + // Setting of internal state + // {{{ + always_ff @(posedge clk_i or negedge rst_ni) + begin + if (!rst_ni) begin + wait_q <= 1'b0; + gnt_q <= '0; + end else begin + wait_q <= ~ready_i & (wait_q | (|req_i)) & ((gnt_q & req_i) != 0); + if (!ready_i && !wait_q && (|req_i)) begin + gnt_q <= gnt; + end + end + end + // }}} + + // Assertions + // {{{ +`ifndef HPDCACHE_ASSERT_OFF + gnt_at_most_one_requester: assert property (@(posedge clk_i) disable iff (rst_ni !== 1'b1) + $onehot0(gnt_o)) else $error("arbiter: granting more than one requester"); + gnt_only_if_req: assert property (@(posedge clk_i) disable iff (rst_ni !== 1'b1) + (gnt_o & req_i) == gnt_o) else $error("arbiter: gnt without req"); +`endif + // }}} + +endmodule diff --git a/hw/vendor/hpdcache/rtl/src/common/hpdcache_lfsr.sv b/hw/vendor/hpdcache/rtl/src/common/hpdcache_lfsr.sv new file mode 100644 index 00000000..6daacfb8 --- /dev/null +++ b/hw/vendor/hpdcache/rtl/src/common/hpdcache_lfsr.sv @@ -0,0 +1,95 @@ +/* + * Copyright 2023 CEA* + * *Commissariat a l'Energie Atomique et aux Energies Alternatives (CEA) + * + * SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 + * + * Licensed under the Solderpad Hardware License v 2.1 (the “License”); you + * may not use this file except in compliance with the License, or, at your + * option, the Apache License version 2.0. You may obtain a copy of the + * License at + * + * https://solderpad.org/licenses/SHL-2.1/ + * + * Unless required by applicable law or agreed to in writing, any work + * distributed under the License is distributed on an “AS IS” BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +/* + * Author(s) : Cesar Fuguet + * Creation Date : Mars, 2024 + * Description : Galois Linear Feedback Shift Register + * History : + */ +module hpdcache_lfsr +// Parameters +// {{{ +#( + parameter int WIDTH = 8, + + localparam type data_t = logic [WIDTH-1:0] +) +// }}} + +// Ports +// {{{ +( + input logic clk_i, + input logic rst_ni, + input logic shift_i, + output data_t val_o +); +// }}} + +// Feedback Polynomial +// {{{ +logic [15:0] polynomial; + +assign polynomial = (WIDTH == 8) ? 16'h00E1 : + (WIDTH == 9) ? 16'h01EA : + (WIDTH == 10) ? 16'h02E3 : + (WIDTH == 11) ? 16'h04E3 : + (WIDTH == 12) ? 16'h0AE2 : + (WIDTH == 13) ? 16'h10E3 : + (WIDTH == 14) ? 16'h20EA : + (WIDTH == 15) ? 16'h41E2 : + (WIDTH == 16) ? 16'h81EE : 16'h0BAD; +// }}} + +// Linear Feedback Shift Register +// {{{ +data_t lfsr_q, lfsr_d; +data_t lfsr_shifted; + +assign lfsr_shifted = {1'b0, lfsr_q[WIDTH-1:1]}; + +always_comb +begin : lfsr_comb + if (lfsr_q[0]) lfsr_d = lfsr_shifted ^ polynomial[WIDTH-1:0]; + else lfsr_d = lfsr_shifted; +end + +assign val_o = lfsr_q; + +always_ff @(posedge clk_i or negedge rst_ni) +begin : lfsr_ff + if (!rst_ni) begin + lfsr_q <= '1; + end else begin + if (shift_i) lfsr_q <= lfsr_d; + end +end +// }}} + +// Assertions +// {{{ +`ifndef HPDCACHE_ASSERT_OFF +if ((WIDTH < 8) || (WIDTH > 16)) begin : gen_lfsr_width_assertion + $fatal(1, "illegal LFSR width"); +end +`endif +// }}} + +endmodule diff --git a/hw/vendor/hpdcache/rtl/src/common/hpdcache_mux.sv b/hw/vendor/hpdcache/rtl/src/common/hpdcache_mux.sv new file mode 100644 index 00000000..969ef8fc --- /dev/null +++ b/hw/vendor/hpdcache/rtl/src/common/hpdcache_mux.sv @@ -0,0 +1,84 @@ +/* + * Copyright 2023 CEA* + * *Commissariat a l'Energie Atomique et aux Energies Alternatives (CEA) + * + * SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 + * + * Licensed under the Solderpad Hardware License v 2.1 (the “License”); you + * may not use this file except in compliance with the License, or, at your + * option, the Apache License version 2.0. You may obtain a copy of the + * License at + * + * https://solderpad.org/licenses/SHL-2.1/ + * + * Unless required by applicable law or agreed to in writing, any work + * distributed under the License is distributed on an “AS IS” BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +/* + * Author(s) : Cesar Fuguet + * Creation Date : April, 2021 + * Description : Simple multiplexor + * History : + */ +module hpdcache_mux + // Parameters + // {{{ +#( + // Number of inputs + parameter int unsigned NINPUT = 0, + + // Width in bits of each input + parameter int unsigned DATA_WIDTH = 0, + + // Selector signal is one-hot encoded + parameter bit ONE_HOT_SEL = 0, + + // Compute the width of the selection signal + localparam int unsigned NINPUT_LOG2 = $clog2(NINPUT), + localparam int unsigned SEL_WIDTH = ONE_HOT_SEL ? NINPUT : NINPUT_LOG2, + + localparam type data_t = logic [DATA_WIDTH-1:0], + localparam type sel_t = logic [SEL_WIDTH-1:0] +) + // }}} + + // Ports + // {{{ +( + input data_t [NINPUT-1:0] data_i, + input sel_t sel_i, + output data_t data_o +); + // }}} + + typedef int unsigned uint32; + + if (NINPUT == 1) begin : gen_single_input + assign data_o = data_i[0]; + + end else begin : gen_multi_input + // Selector is one-hot encoded + if (ONE_HOT_SEL == 1) begin : gen_onehot_sel + always_comb + begin : data_out_mux_comb + data_o = '0; + for (int unsigned i = 0; i < NINPUT; i++) begin + data_o |= sel_i[i] ? data_i[i] : '0; + end + end + + // Selector is binary encoded + end else begin : gen_binary_sel + always_comb + begin : data_out_mux_comb + data_o = '0; + for (int unsigned i = 0; i < NINPUT; i++) begin + data_o |= (i == uint32'(sel_i)) ? data_i[i] : '0; + end + end + end + end +endmodule diff --git a/hw/vendor/hpdcache/rtl/src/common/hpdcache_prio_1hot_encoder.sv b/hw/vendor/hpdcache/rtl/src/common/hpdcache_prio_1hot_encoder.sv new file mode 100644 index 00000000..e9a107e2 --- /dev/null +++ b/hw/vendor/hpdcache/rtl/src/common/hpdcache_prio_1hot_encoder.sv @@ -0,0 +1,42 @@ +/* + * Copyright 2023 CEA* + * *Commissariat a l'Energie Atomique et aux Energies Alternatives (CEA) + * + * SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 + * + * Licensed under the Solderpad Hardware License v 2.1 (the “License”); you + * may not use this file except in compliance with the License, or, at your + * option, the Apache License version 2.0. You may obtain a copy of the + * License at + * + * https://solderpad.org/licenses/SHL-2.1/ + * + * Unless required by applicable law or agreed to in writing, any work + * distributed under the License is distributed on an “AS IS” BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +/* + * Authors : Cesar Fuguet + * Creation Date : April, 2021 + * Description : Priority One-hot Encoder + * History : + */ +module hpdcache_prio_1hot_encoder + // Parameters +#( + parameter int unsigned N = 0 +) + // Ports +( + input logic [N-1:0] val_i, + output logic [N-1:0] val_o +); + + assign val_o[0] = val_i[0]; + + for (genvar i = 1; i < int'(N); i++) begin : gen_prio + assign val_o[i] = val_i[i] & ~(|val_i[i-1:0]); + end +endmodule diff --git a/hw/vendor/hpdcache/rtl/src/common/hpdcache_prio_bin_encoder.sv b/hw/vendor/hpdcache/rtl/src/common/hpdcache_prio_bin_encoder.sv new file mode 100644 index 00000000..906a9f9c --- /dev/null +++ b/hw/vendor/hpdcache/rtl/src/common/hpdcache_prio_bin_encoder.sv @@ -0,0 +1,36 @@ +// Copyright (c) 2025 ETH Zurich, University of Bologna +// +// Copyright and related rights are licensed under the Solderpad Hardware +// License, Version 2.1 (the "License"); you may not use this file except in +// compliance with the License. You may obtain a copy of the License at +// http://solderpad.org/licenses/SHL-2.1. Unless required by applicable law +// or agreed to in writing, software, hardware and materials distributed under +// this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +// +// Authors : Riccardo Tedeschi +// Creation Date : April, 2025 +// Description : Priority Binary Encoder +// History : +// + +module hpdcache_prio_bin_encoder + // Parameters +#( + parameter int unsigned N = 0, + localparam int unsigned N_LOG2 = $clog2(N) +) + // Ports +( + input logic [N-1:0] val_i, + output logic [N_LOG2-1:0] val_o +); + + always_comb begin + val_o = '0; + for (int i = N-1; i >= 0; i--) + if (val_i[i]) val_o = N_LOG2'(i); + end +endmodule diff --git a/hw/vendor/hpdcache/rtl/src/common/hpdcache_regbank_wbyteenable_1rw.sv b/hw/vendor/hpdcache/rtl/src/common/hpdcache_regbank_wbyteenable_1rw.sv new file mode 100644 index 00000000..184e6fbf --- /dev/null +++ b/hw/vendor/hpdcache/rtl/src/common/hpdcache_regbank_wbyteenable_1rw.sv @@ -0,0 +1,63 @@ +/* + * Copyright 2023 CEA* + * *Commissariat a l'Energie Atomique et aux Energies Alternatives (CEA) + * + * SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 + * + * Licensed under the Solderpad Hardware License v 2.1 (the “License”); you + * may not use this file except in compliance with the License, or, at your + * option, the Apache License version 2.0. You may obtain a copy of the + * License at + * + * https://solderpad.org/licenses/SHL-2.1/ + * + * Unless required by applicable law or agreed to in writing, any work + * distributed under the License is distributed on an “AS IS” BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +/* + * Authors : Cesar Fuguet + * Creation Date : March, 2020 + * Description : 1RW register bank with write byte enable + * History : + */ +module hpdcache_regbank_wbyteenable_1rw +#( + parameter int unsigned ADDR_SIZE = 0, + parameter int unsigned DATA_SIZE = 0, + parameter int unsigned DEPTH = 2**ADDR_SIZE +) +( + input logic clk, + input logic rst_n, + input logic cs, + input logic we, + input logic [ADDR_SIZE-1:0] addr, + input logic [DATA_SIZE-1:0] wdata, + input logic [DATA_SIZE/8-1:0] wbyteenable, + output logic [DATA_SIZE-1:0] rdata +); + + /* + * Internal memory array declaration + */ + typedef logic [DATA_SIZE-1:0] mem_t [DEPTH]; + mem_t mem; + + /* + * Process to update or read the memory array + */ + always_ff @(posedge clk) + begin : mem_update_ff + if (cs == 1'b1) begin + if (we == 1'b1) begin + for (int i = 0; i < DATA_SIZE/8; i++) begin + if (wbyteenable[i]) mem[addr][i*8 +: 8] <= wdata[i*8 +: 8]; + end + end + rdata <= mem[addr]; + end + end : mem_update_ff +endmodule : hpdcache_regbank_wbyteenable_1rw diff --git a/hw/vendor/hpdcache/rtl/src/common/hpdcache_regbank_wmask_1rw.sv b/hw/vendor/hpdcache/rtl/src/common/hpdcache_regbank_wmask_1rw.sv new file mode 100644 index 00000000..e185bc40 --- /dev/null +++ b/hw/vendor/hpdcache/rtl/src/common/hpdcache_regbank_wmask_1rw.sv @@ -0,0 +1,61 @@ +/* + * Copyright 2023 CEA* + * *Commissariat a l'Energie Atomique et aux Energies Alternatives (CEA) + * + * SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 + * + * Licensed under the Solderpad Hardware License v 2.1 (the “License”); you + * may not use this file except in compliance with the License, or, at your + * option, the Apache License version 2.0. You may obtain a copy of the + * License at + * + * https://solderpad.org/licenses/SHL-2.1/ + * + * Unless required by applicable law or agreed to in writing, any work + * distributed under the License is distributed on an “AS IS” BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +/* + * Authors : Cesar Fuguet + * Creation Date : March, 2020 + * Description : 1RW register bank with write bit mask + * History : + */ +module hpdcache_regbank_wmask_1rw +#( + parameter int unsigned ADDR_SIZE = 0, + parameter int unsigned DATA_SIZE = 0, + parameter int unsigned DEPTH = 2**ADDR_SIZE +) +( + input logic clk, + input logic rst_n, + input logic cs, + input logic we, + input logic [ADDR_SIZE-1:0] addr, + input logic [DATA_SIZE-1:0] wdata, + input logic [DATA_SIZE-1:0] wmask, + output logic [DATA_SIZE-1:0] rdata +); + + /* + * Internal memory array declaration + */ + typedef logic [DATA_SIZE-1:0] mem_t [DEPTH]; + mem_t mem; + + /* + * Process to update or read the memory array + */ + always_ff @(posedge clk) + begin : mem_update_ff + if (cs == 1'b1) begin + if (we == 1'b1) begin + mem[addr] <= (mem[addr] & ~wmask) | (wdata & wmask); + end + rdata <= mem[addr]; + end + end : mem_update_ff +endmodule : hpdcache_regbank_wmask_1rw diff --git a/hw/vendor/hpdcache/rtl/src/common/hpdcache_rrarb.sv b/hw/vendor/hpdcache/rtl/src/common/hpdcache_rrarb.sv new file mode 100644 index 00000000..7d30e3dd --- /dev/null +++ b/hw/vendor/hpdcache/rtl/src/common/hpdcache_rrarb.sv @@ -0,0 +1,121 @@ +/* + * Copyright 2023 CEA* + * *Commissariat a l'Energie Atomique et aux Energies Alternatives (CEA) + * + * SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 + * + * Licensed under the Solderpad Hardware License v 2.1 (the “License”); you + * may not use this file except in compliance with the License, or, at your + * option, the Apache License version 2.0. You may obtain a copy of the + * License at + * + * https://solderpad.org/licenses/SHL-2.1/ + * + * Unless required by applicable law or agreed to in writing, any work + * distributed under the License is distributed on an “AS IS” BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +/** + * Author(s) : Cesar Fuguet + * Creation Date : April, 2021 + * Description : Round-Robin Arbiter + * Based on design from + * http://www.rtlery.com/articles/how-design-round-robin-arbiter + * History : + */ +module hpdcache_rrarb + // Parameters + // {{{ +#( + // Number of requesters + parameter int unsigned N = 0 +) + // }}} + // Ports + // {{{ +( + input logic clk_i, + input logic rst_ni, + input logic [N-1:0] req_i, + output logic [N-1:0] gnt_o, + input logic ready_i +); + // }}} + + // Declaration of internal wires and registers + // {{{ + logic [N-1:0] gnt_q, gnt; + logic [N-1:0] nxt; + logic wait_q; + logic [N-1:0] mask, gnt_msk, gnt_nomsk; + logic pending; + genvar gen_i; + // }}} + + // Elaboration-time assertions + // {{{ +`ifndef HPDCACHE_ASSERT_OFF + if (N <= 0) begin : gen_n_assertion + $fatal(1, "N must be greater than 0"); + end +`endif + // }}} + + // Compute the thermometer mask vector + // {{{ + generate + if (N > 1) begin : gen_nxt_gt_1 + assign nxt = {gnt_q[N-2:0], gnt_q[N-1]}; + end else begin : gen_nxt_1 + assign nxt = gnt_q[0]; + end + + for (gen_i = 0; gen_i < int'(N); gen_i++) begin : gen_mask + assign mask[gen_i] = |nxt[gen_i:0]; + end + endgenerate + // }}} + + // Compute the grant vector + // {{{ + hpdcache_prio_1hot_encoder #(.N(N)) prio_msk_i (.val_i(req_i & mask), .val_o(gnt_msk)); + hpdcache_prio_1hot_encoder #(.N(N)) prio_nomsk_i (.val_i(req_i) , .val_o(gnt_nomsk)); + assign gnt = |gnt_msk ? gnt_msk : gnt_nomsk; + // }}} + + // Compute the output grant vector + // {{{ + assign gnt_o = wait_q ? gnt_q : gnt; + // }}} + + // Setting of internal state + // {{{ + assign pending = |req_i; + + always_ff @(posedge clk_i or negedge rst_ni) + begin + if (!rst_ni) begin + wait_q <= 1'b0; + gnt_q <= {1'b1, {N-1{1'b0}}}; + end else begin + wait_q <= ~ready_i & (wait_q | pending); + if (!wait_q && pending) begin + gnt_q <= gnt; + end + end + end + // }}} + + // Assertions + // {{{ +`ifndef HPDCACHE_ASSERT_OFF + gnt_at_most_one_requester: assert property (@(posedge clk_i) disable iff (rst_ni !== 1'b1) + $onehot0(gnt)) else $error("arbiter: granting more than one requester"); + gnt_q_exactly_one_requester: assert property (@(posedge clk_i) disable iff (rst_ni !== 1'b1) + $onehot(gnt_q)) else $error("arbiter: grant state is not one-hot"); +`endif + // }}} + +endmodule diff --git a/hw/vendor/hpdcache/rtl/src/common/hpdcache_sram.sv b/hw/vendor/hpdcache/rtl/src/common/hpdcache_sram.sv new file mode 100644 index 00000000..d4cab7de --- /dev/null +++ b/hw/vendor/hpdcache/rtl/src/common/hpdcache_sram.sv @@ -0,0 +1,56 @@ +/* + * Copyright 2023 CEA* + * *Commissariat a l'Energie Atomique et aux Energies Alternatives (CEA) + * + * SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 + * + * Licensed under the Solderpad Hardware License v 2.1 (the “License”); you + * may not use this file except in compliance with the License, or, at your + * option, the Apache License version 2.0. You may obtain a copy of the + * License at + * + * https://solderpad.org/licenses/SHL-2.1/ + * + * Unless required by applicable law or agreed to in writing, any work + * distributed under the License is distributed on an “AS IS” BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +/* + * Authors : Cesar Fuguet + * Creation Date : March, 2020 + * Description : Wrapper for Behavioral SRAM macros + * History : + */ +module hpdcache_sram +#( + parameter int unsigned ADDR_SIZE = 0, + parameter int unsigned DATA_SIZE = 0, + parameter int unsigned DEPTH = 2**ADDR_SIZE +) +( + input logic clk, + input logic rst_n, + input logic cs, + input logic we, + input logic [ADDR_SIZE-1:0] addr, + input logic [DATA_SIZE-1:0] wdata, + output logic [DATA_SIZE-1:0] rdata +); + + hpdcache_sram_1rw #( + .ADDR_SIZE(ADDR_SIZE), + .DATA_SIZE(DATA_SIZE), + .DEPTH(DEPTH) + ) ram_i ( + .clk, + .rst_n, + .cs, + .we, + .addr, + .wdata, + .rdata + ); + +endmodule : hpdcache_sram diff --git a/hw/vendor/hpdcache/rtl/src/common/hpdcache_sram_watomenable.sv b/hw/vendor/hpdcache/rtl/src/common/hpdcache_sram_watomenable.sv new file mode 100644 index 00000000..4adc06b4 --- /dev/null +++ b/hw/vendor/hpdcache/rtl/src/common/hpdcache_sram_watomenable.sv @@ -0,0 +1,39 @@ +/* + * Creation Date : December, 2025 + * Description : Wrapper for 1RW SRAM macros implementing a write atom enable + */ +module hpdcache_sram_watomenable +#( + parameter int unsigned ADDR_SIZE = 0, + parameter int unsigned DATA_SIZE = 0, + parameter int unsigned DEPTH = 2**ADDR_SIZE, + parameter int unsigned ATOM_SIZE = DATA_SIZE >= 8 ? 8 : DATA_SIZE +) +( + input logic clk, + input logic rst_n, + input logic cs, + input logic we, + input logic [ADDR_SIZE-1:0] addr, + input logic [DATA_SIZE-1:0] wdata, + input logic [(DATA_SIZE+ATOM_SIZE-1)/ATOM_SIZE-1:0] watomenable, + output logic [DATA_SIZE-1:0] rdata +); + + hpdcache_sram_watomenable_1rw #( + .ADDR_SIZE(ADDR_SIZE), + .DATA_SIZE(DATA_SIZE), + .DEPTH(DEPTH), + .ATOM_SIZE(ATOM_SIZE) + ) ram_i ( + .clk, + .rst_n, + .cs, + .we, + .addr, + .wdata, + .watomenable, + .rdata + ); + +endmodule : hpdcache_sram_watomenable diff --git a/hw/vendor/hpdcache/rtl/src/common/hpdcache_sram_wbyteenable.sv b/hw/vendor/hpdcache/rtl/src/common/hpdcache_sram_wbyteenable.sv new file mode 100644 index 00000000..43bdb450 --- /dev/null +++ b/hw/vendor/hpdcache/rtl/src/common/hpdcache_sram_wbyteenable.sv @@ -0,0 +1,58 @@ +/* + * Copyright 2023 CEA* + * *Commissariat a l'Energie Atomique et aux Energies Alternatives (CEA) + * + * SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 + * + * Licensed under the Solderpad Hardware License v 2.1 (the “License”); you + * may not use this file except in compliance with the License, or, at your + * option, the Apache License version 2.0. You may obtain a copy of the + * License at + * + * https://solderpad.org/licenses/SHL-2.1/ + * + * Unless required by applicable law or agreed to in writing, any work + * distributed under the License is distributed on an “AS IS” BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +/* + * Authors : Cesar Fuguet + * Creation Date : March, 2020 + * Description : Wrapper for 1RW SRAM macros implementing a write byte enable + * History : + */ +module hpdcache_sram_wbyteenable +#( + parameter int unsigned ADDR_SIZE = 0, + parameter int unsigned DATA_SIZE = 0, + parameter int unsigned DEPTH = 2**ADDR_SIZE +) +( + input logic clk, + input logic rst_n, + input logic cs, + input logic we, + input logic [ADDR_SIZE-1:0] addr, + input logic [DATA_SIZE-1:0] wdata, + input logic [DATA_SIZE/8-1:0] wbyteenable, + output logic [DATA_SIZE-1:0] rdata +); + + hpdcache_sram_wbyteenable_1rw #( + .ADDR_SIZE(ADDR_SIZE), + .DATA_SIZE(DATA_SIZE), + .DEPTH(DEPTH) + ) ram_i ( + .clk, + .rst_n, + .cs, + .we, + .addr, + .wdata, + .wbyteenable, + .rdata + ); + +endmodule : hpdcache_sram_wbyteenable diff --git a/hw/vendor/hpdcache/rtl/src/common/hpdcache_sram_wmask.sv b/hw/vendor/hpdcache/rtl/src/common/hpdcache_sram_wmask.sv new file mode 100644 index 00000000..a4771e3b --- /dev/null +++ b/hw/vendor/hpdcache/rtl/src/common/hpdcache_sram_wmask.sv @@ -0,0 +1,58 @@ +/* + * Copyright 2023 CEA* + * *Commissariat a l'Energie Atomique et aux Energies Alternatives (CEA) + * + * SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 + * + * Licensed under the Solderpad Hardware License v 2.1 (the “License”); you + * may not use this file except in compliance with the License, or, at your + * option, the Apache License version 2.0. You may obtain a copy of the + * License at + * + * https://solderpad.org/licenses/SHL-2.1/ + * + * Unless required by applicable law or agreed to in writing, any work + * distributed under the License is distributed on an “AS IS” BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +/* + * Authors : Cesar Fuguet + * Creation Date : March, 2020 + * Description : Wrapper for 1RW SRAM macros implementing write bit mask + * History : + */ +module hpdcache_sram_wmask +#( + parameter int unsigned ADDR_SIZE = 0, + parameter int unsigned DATA_SIZE = 0, + parameter int unsigned DEPTH = 2**ADDR_SIZE +) +( + input logic clk, + input logic rst_n, + input logic cs, + input logic we, + input logic [ADDR_SIZE-1:0] addr, + input logic [DATA_SIZE-1:0] wdata, + input logic [DATA_SIZE-1:0] wmask, + output logic [DATA_SIZE-1:0] rdata +); + + hpdcache_sram_wmask_1rw #( + .ADDR_SIZE(ADDR_SIZE), + .DATA_SIZE(DATA_SIZE), + .DEPTH(DEPTH) + ) ram_i ( + .clk, + .rst_n, + .cs, + .we, + .addr, + .wdata, + .wmask, + .rdata + ); + +endmodule : hpdcache_sram_wmask diff --git a/hw/vendor/hpdcache/rtl/src/common/hpdcache_sync_buffer.sv b/hw/vendor/hpdcache/rtl/src/common/hpdcache_sync_buffer.sv new file mode 100644 index 00000000..863c5885 --- /dev/null +++ b/hw/vendor/hpdcache/rtl/src/common/hpdcache_sync_buffer.sv @@ -0,0 +1,89 @@ +/* + * Copyright 2023 CEA* + * *Commissariat a l'Energie Atomique et aux Energies Alternatives (CEA) + * + * SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 + * + * Licensed under the Solderpad Hardware License v 2.1 (the “License”); you + * may not use this file except in compliance with the License, or, at your + * option, the Apache License version 2.0. You may obtain a copy of the + * License at + * + * https://solderpad.org/licenses/SHL-2.1/ + * + * Unless required by applicable law or agreed to in writing, any work + * distributed under the License is distributed on an “AS IS” BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +/* + * Authors : Cesar Fuguet + * Creation Date : October, 2023 + * Description : Synchronization buffer + * History : + */ +module hpdcache_sync_buffer + // Parameters + // {{{ +#( + parameter bit FEEDTHROUGH = 1'b0, + parameter type data_t = logic +) + // }}} + // Ports + // {{{ +( + input logic clk_i, + input logic rst_ni, + input logic w_i, + output logic wok_o, + input data_t wdata_i, + input logic r_i, + output logic rok_o, + output data_t rdata_o +); + // }}} + + // Declaration of internal wires and registers + // {{{ + data_t buf_q; + logic buf_we; + logic valid_q, valid_d; + // }}} + + // Global control signals + // {{{ + assign rok_o = valid_q | (FEEDTHROUGH & w_i), + wok_o = ~valid_q | (FEEDTHROUGH & r_i); + + assign buf_we = w_i & ((FEEDTHROUGH & ~(valid_q ^ r_i)) | (~FEEDTHROUGH & ~valid_q)); + // }}} + + // Control of buffer + // {{{ + assign valid_d = buf_we | (valid_q & ~r_i); + // }}} + + // FIFO buffer memory management + // {{{ + always_ff @(posedge clk_i) + begin + if (buf_we) buf_q <= wdata_i; + end + + assign rdata_o = FEEDTHROUGH && !valid_q ? wdata_i : buf_q; + // }}} + + // Setting of internal state + // {{{ + always_ff @(posedge clk_i or negedge rst_ni) + begin + if (!rst_ni) begin + valid_q <= 1'b0; + end else begin + valid_q <= valid_d; + end + end + // }}} +endmodule diff --git a/hw/vendor/hpdcache/rtl/src/common/macros/behav/hpdcache_sram_1rw.sv b/hw/vendor/hpdcache/rtl/src/common/macros/behav/hpdcache_sram_1rw.sv new file mode 100644 index 00000000..7288c731 --- /dev/null +++ b/hw/vendor/hpdcache/rtl/src/common/macros/behav/hpdcache_sram_1rw.sv @@ -0,0 +1,60 @@ +/* + * Copyright 2023 CEA* + * *Commissariat a l'Energie Atomique et aux Energies Alternatives (CEA) + * + * SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 + * + * Licensed under the Solderpad Hardware License v 2.1 (the “License”); you + * may not use this file except in compliance with the License, or, at your + * option, the Apache License version 2.0. You may obtain a copy of the + * License at + * + * https://solderpad.org/licenses/SHL-2.1/ + * + * Unless required by applicable law or agreed to in writing, any work + * distributed under the License is distributed on an “AS IS” BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +/* + * Authors : Cesar Fuguet + * Creation Date : March, 2020 + * Description : SRAM behavioral model + * History : + */ +module hpdcache_sram_1rw +#( + parameter int unsigned ADDR_SIZE = 0, + parameter int unsigned DATA_SIZE = 0, + parameter int unsigned DEPTH = 2**ADDR_SIZE +) +( + input logic clk, + input logic rst_n, + input logic cs, + input logic we, + input logic [ADDR_SIZE-1:0] addr, + input logic [DATA_SIZE-1:0] wdata, + output logic [DATA_SIZE-1:0] rdata +); + + /* + * Internal memory array declaration + */ + typedef logic [DATA_SIZE-1:0] mem_t [DEPTH]; + mem_t mem; + + /* + * Process to update or read the memory array + */ + always_ff @(posedge clk) + begin : mem_update_ff + if (cs == 1'b1) begin + if (we == 1'b1) begin + mem[addr] <= wdata; + end + rdata <= mem[addr]; + end + end : mem_update_ff +endmodule : hpdcache_sram_1rw diff --git a/hw/vendor/hpdcache/rtl/src/common/macros/behav/hpdcache_sram_watomenable_1rw.sv b/hw/vendor/hpdcache/rtl/src/common/macros/behav/hpdcache_sram_watomenable_1rw.sv new file mode 100644 index 00000000..4f776e42 --- /dev/null +++ b/hw/vendor/hpdcache/rtl/src/common/macros/behav/hpdcache_sram_watomenable_1rw.sv @@ -0,0 +1,49 @@ +/* + * Creation Date : December, 2025 + * Description : Behavioral model of a 1RW SRAM with write atom enable + */ +module hpdcache_sram_watomenable_1rw +#( + parameter int unsigned ADDR_SIZE = 0, + parameter int unsigned DATA_SIZE = 0, + parameter int unsigned DEPTH = 2**ADDR_SIZE, + parameter int unsigned ATOM_SIZE = DATA_SIZE >= 8 ? 8 : DATA_SIZE +) +( + input logic clk, + input logic rst_n, + input logic cs, + input logic we, + input logic [ADDR_SIZE-1:0] addr, + input logic [DATA_SIZE-1:0] wdata, + input logic [(DATA_SIZE+ATOM_SIZE-1)/ATOM_SIZE-1:0] watomenable, + output logic [DATA_SIZE-1:0] rdata +); + + /* + * Internal memory array declaration + */ + typedef logic [DATA_SIZE-1:0] mem_t [DEPTH]; + mem_t mem; + logic [ADDR_SIZE-1:0] addr_reg; + + assign rdata = mem[addr_reg]; + + + /* + * Process to update or read the memory array + */ + always_ff @(posedge clk) + begin : mem_update_ff + if (cs == 1'b1) begin + if (we == 1'b1) begin + for (int i = 0; i < (DATA_SIZE+ATOM_SIZE-1)/ATOM_SIZE; i++) begin + if (watomenable[i]) + mem[addr][i*ATOM_SIZE +: ATOM_SIZE] <= wdata[i*ATOM_SIZE +: ATOM_SIZE]; + end + end + addr_reg <= addr; + end + //rdata <= mem[addr]; + end : mem_update_ff +endmodule : hpdcache_sram_watomenable_1rw diff --git a/hw/vendor/hpdcache/rtl/src/common/macros/behav/hpdcache_sram_wbyteenable_1rw.sv b/hw/vendor/hpdcache/rtl/src/common/macros/behav/hpdcache_sram_wbyteenable_1rw.sv new file mode 100644 index 00000000..eebc0534 --- /dev/null +++ b/hw/vendor/hpdcache/rtl/src/common/macros/behav/hpdcache_sram_wbyteenable_1rw.sv @@ -0,0 +1,68 @@ +/* + * Copyright 2023 CEA* + * *Commissariat a l'Energie Atomique et aux Energies Alternatives (CEA) + * + * SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 + * + * Licensed under the Solderpad Hardware License v 2.1 (the “License”); you + * may not use this file except in compliance with the License, or, at your + * option, the Apache License version 2.0. You may obtain a copy of the + * License at + * + * https://solderpad.org/licenses/SHL-2.1/ + * + * Unless required by applicable law or agreed to in writing, any work + * distributed under the License is distributed on an “AS IS” BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +/* + * Authors : Cesar Fuguet + * Creation Date : March, 2020 + * Description : Behavioral model of a 1RW SRAM with write byte enable + * History : + */ +module hpdcache_sram_wbyteenable_1rw +#( + parameter int unsigned ADDR_SIZE = 0, + parameter int unsigned DATA_SIZE = 0, + parameter int unsigned DEPTH = 2**ADDR_SIZE +) +( + input logic clk, + input logic rst_n, + input logic cs, + input logic we, + input logic [ADDR_SIZE-1:0] addr, + input logic [DATA_SIZE-1:0] wdata, + input logic [DATA_SIZE/8-1:0] wbyteenable, + output logic [DATA_SIZE-1:0] rdata +); + + /* + * Internal memory array declaration + */ + typedef logic [DATA_SIZE-1:0] mem_t [DEPTH]; + mem_t mem; + logic [ADDR_SIZE-1:0] addr_reg; + + assign rdata = mem[addr_reg]; + + /* + * Process to update or read the memory array + */ + always_ff @(posedge clk) + begin : mem_update_ff + if (cs == 1'b1) begin + if (we == 1'b1) begin + for (int i = 0; i < DATA_SIZE/8; i++) begin + if (wbyteenable[i]) mem[addr][i*8 +: 8] <= wdata[i*8 +: 8]; + //rdata[i*8 +: 8] <= (wbyteenable[i]) ? mem[addr][i*8 +: 8] : wdata[i*8 +: 8]; + end + end + addr_reg <= addr; + end + //rdata <= mem[addr_reg]; + end : mem_update_ff +endmodule : hpdcache_sram_wbyteenable_1rw diff --git a/hw/vendor/hpdcache/rtl/src/common/macros/behav/hpdcache_sram_wmask_1rw.sv b/hw/vendor/hpdcache/rtl/src/common/macros/behav/hpdcache_sram_wmask_1rw.sv new file mode 100644 index 00000000..5058ba28 --- /dev/null +++ b/hw/vendor/hpdcache/rtl/src/common/macros/behav/hpdcache_sram_wmask_1rw.sv @@ -0,0 +1,61 @@ +/* + * Copyright 2023 CEA* + * *Commissariat a l'Energie Atomique et aux Energies Alternatives (CEA) + * + * SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 + * + * Licensed under the Solderpad Hardware License v 2.1 (the “License”); you + * may not use this file except in compliance with the License, or, at your + * option, the Apache License version 2.0. You may obtain a copy of the + * License at + * + * https://solderpad.org/licenses/SHL-2.1/ + * + * Unless required by applicable law or agreed to in writing, any work + * distributed under the License is distributed on an “AS IS” BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +/* + * Authors : Cesar Fuguet + * Creation Date : March, 2020 + * Description : Behavioral model of a 1RW SRAM with write bit mask + * History : + */ +module hpdcache_sram_wmask_1rw +#( + parameter int unsigned ADDR_SIZE = 0, + parameter int unsigned DATA_SIZE = 0, + parameter int unsigned DEPTH = 2**ADDR_SIZE +) +( + input logic clk, + input logic rst_n, + input logic cs, + input logic we, + input logic [ADDR_SIZE-1:0] addr, + input logic [DATA_SIZE-1:0] wdata, + input logic [DATA_SIZE-1:0] wmask, + output logic [DATA_SIZE-1:0] rdata +); + + /* + * Internal memory array declaration + */ + typedef logic [DATA_SIZE-1:0] mem_t [DEPTH]; + mem_t mem; + + /* + * Process to update or read the memory array + */ + always_ff @(posedge clk) + begin : mem_update_ff + if (cs == 1'b1) begin + if (we == 1'b1) begin + mem[addr] <= (mem[addr] & ~wmask) | (wdata & wmask); + end + rdata <= mem[addr]; + end + end : mem_update_ff +endmodule : hpdcache_sram_wmask_1rw diff --git a/hw/vendor/hpdcache/rtl/src/common/macros/blackbox/hpdcache_sram_1rw.sv b/hw/vendor/hpdcache/rtl/src/common/macros/blackbox/hpdcache_sram_1rw.sv new file mode 100644 index 00000000..c291005d --- /dev/null +++ b/hw/vendor/hpdcache/rtl/src/common/macros/blackbox/hpdcache_sram_1rw.sv @@ -0,0 +1,42 @@ +/* + * Copyright 2023 CEA* + * *Commissariat a l'Energie Atomique et aux Energies Alternatives (CEA) + * + * SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 + * + * Licensed under the Solderpad Hardware License v 2.1 (the “License”); you + * may not use this file except in compliance with the License, or, at your + * option, the Apache License version 2.0. You may obtain a copy of the + * License at + * + * https://solderpad.org/licenses/SHL-2.1/ + * + * Unless required by applicable law or agreed to in writing, any work + * distributed under the License is distributed on an “AS IS” BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +/* + * Authors : Cesar Fuguet + * Creation Date : March, 2020 + * Description : SRAM blackbox model + * History : + */ +(* black_box *) module hpdcache_sram_1rw +#( + parameter int unsigned ADDR_SIZE = 0, + parameter int unsigned DATA_SIZE = 0, + parameter int unsigned DEPTH = 2**ADDR_SIZE +) +( + input logic clk, + input logic rst_n, + input logic cs, + input logic we, + input logic [ADDR_SIZE-1:0] addr, + input logic [DATA_SIZE-1:0] wdata, + output logic [DATA_SIZE-1:0] rdata +); + +endmodule diff --git a/hw/vendor/hpdcache/rtl/src/common/macros/blackbox/hpdcache_sram_wbyteenable_1rw.sv b/hw/vendor/hpdcache/rtl/src/common/macros/blackbox/hpdcache_sram_wbyteenable_1rw.sv new file mode 100644 index 00000000..0082e875 --- /dev/null +++ b/hw/vendor/hpdcache/rtl/src/common/macros/blackbox/hpdcache_sram_wbyteenable_1rw.sv @@ -0,0 +1,43 @@ +/* + * Copyright 2023 CEA* + * *Commissariat a l'Energie Atomique et aux Energies Alternatives (CEA) + * + * SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 + * + * Licensed under the Solderpad Hardware License v 2.1 (the “License”); you + * may not use this file except in compliance with the License, or, at your + * option, the Apache License version 2.0. You may obtain a copy of the + * License at + * + * https://solderpad.org/licenses/SHL-2.1/ + * + * Unless required by applicable law or agreed to in writing, any work + * distributed under the License is distributed on an “AS IS” BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +/* + * Authors : Cesar Fuguet + * Creation Date : March, 2020 + * Description : Blackbox model of a 1RW SRAM with write byte enable + * History : + */ +(* black_box *) module hpdcache_sram_wbyteenable_1rw +#( + parameter int unsigned ADDR_SIZE = 0, + parameter int unsigned DATA_SIZE = 0, + parameter int unsigned DEPTH = 2**ADDR_SIZE +) +( + input logic clk, + input logic rst_n, + input logic cs, + input logic we, + input logic [ADDR_SIZE-1:0] addr, + input logic [DATA_SIZE-1:0] wdata, + input logic [DATA_SIZE/8-1:0] wbyteenable, + output logic [DATA_SIZE-1:0] rdata +); + +endmodule diff --git a/hw/vendor/hpdcache/rtl/src/common/macros/blackbox/hpdcache_sram_wmask_1rw.sv b/hw/vendor/hpdcache/rtl/src/common/macros/blackbox/hpdcache_sram_wmask_1rw.sv new file mode 100644 index 00000000..aa8ec311 --- /dev/null +++ b/hw/vendor/hpdcache/rtl/src/common/macros/blackbox/hpdcache_sram_wmask_1rw.sv @@ -0,0 +1,43 @@ +/* + * Copyright 2023 CEA* + * *Commissariat a l'Energie Atomique et aux Energies Alternatives (CEA) + * + * SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 + * + * Licensed under the Solderpad Hardware License v 2.1 (the “License”); you + * may not use this file except in compliance with the License, or, at your + * option, the Apache License version 2.0. You may obtain a copy of the + * License at + * + * https://solderpad.org/licenses/SHL-2.1/ + * + * Unless required by applicable law or agreed to in writing, any work + * distributed under the License is distributed on an “AS IS” BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +/* + * Authors : Cesar Fuguet + * Creation Date : March, 2020 + * Description : Blackbox model of a 1RW SRAM with write bit mask + * History : + */ +(* black_box *) module hpdcache_sram_wmask_1rw +#( + parameter int unsigned ADDR_SIZE = 0, + parameter int unsigned DATA_SIZE = 0, + parameter int unsigned DEPTH = 2**ADDR_SIZE +) +( + input logic clk, + input logic rst_n, + input logic cs, + input logic we, + input logic [ADDR_SIZE-1:0] addr, + input logic [DATA_SIZE-1:0] wdata, + input logic [DATA_SIZE-1:0] wmask, + output logic [DATA_SIZE-1:0] rdata +); + +endmodule diff --git a/hw/vendor/hpdcache/rtl/src/hpdcache.sv b/hw/vendor/hpdcache/rtl/src/hpdcache.sv new file mode 100644 index 00000000..e7b2985b --- /dev/null +++ b/hw/vendor/hpdcache/rtl/src/hpdcache.sv @@ -0,0 +1,1374 @@ +/* + * Copyright 2023 CEA* + * *Commissariat a l'Energie Atomique et aux Energies Alternatives (CEA) + * + * SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 + * + * Licensed under the Solderpad Hardware License v 2.1 (the “License”); you + * may not use this file except in compliance with the License, or, at your + * option, the Apache License version 2.0. You may obtain a copy of the + * License at + * + * https://solderpad.org/licenses/SHL-2.1/ + * + * Unless required by applicable law or agreed to in writing, any work + * distributed under the License is distributed on an “AS IS” BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +/* + * Authors : Cesar Fuguet + * Creation Date : April, 2021 + * Description : HPDcache top + * History : + */ +`include "hpdcache_typedef.svh" + +module hpdcache +import hpdcache_pkg::*; + // Parameters + // {{{ +#( + parameter hpdcache_cfg_t HPDcacheCfg = '0, + + parameter type wbuf_timecnt_t = logic, + + // Request Interface Definitions + // {{{ + parameter type hpdcache_tag_t = logic, + parameter type hpdcache_data_word_t = logic, + parameter type hpdcache_data_be_t = logic, + parameter type hpdcache_req_offset_t = logic, + parameter type hpdcache_req_data_t = logic, + parameter type hpdcache_req_be_t = logic, + parameter type hpdcache_req_sid_t = logic, + parameter type hpdcache_req_tid_t = logic, + parameter type hpdcache_req_t = logic, + parameter type hpdcache_rsp_t = logic, + parameter type hpdcache_req_user_t = logic[0:0], + // }}} + + // Memory Interface Definitions + // {{{ + parameter type hpdcache_mem_addr_t = logic, + parameter type hpdcache_mem_id_t = logic, + parameter type hpdcache_mem_data_t = logic, + parameter type hpdcache_mem_be_t = logic, + parameter type hpdcache_mem_req_t = logic, + parameter type hpdcache_mem_req_w_t = logic, + parameter type hpdcache_mem_resp_r_t = logic, + parameter type hpdcache_mem_resp_w_t = logic, + parameter type hpdcache_mem_user_t = logic[0:0] + // }}} +) + // }}} + + // Ports + // {{{ +( + // Clock and reset signals + input logic clk_i, + input logic rst_ni, + + // Force the write buffer to send all pending writes + input logic wbuf_flush_i, + + // Core request interface + // 1st cycle + input logic core_req_valid_i [HPDcacheCfg.u.nRequesters], + output logic core_req_ready_o [HPDcacheCfg.u.nRequesters], + input hpdcache_req_t core_req_i [HPDcacheCfg.u.nRequesters], + // 2nd cycle + input logic core_req_abort_i [HPDcacheCfg.u.nRequesters], + input hpdcache_tag_t core_req_tag_i [HPDcacheCfg.u.nRequesters], + input hpdcache_pma_t core_req_pma_i [HPDcacheCfg.u.nRequesters], + + // Core response interface + output logic core_rsp_valid_o [HPDcacheCfg.u.nRequesters], + output hpdcache_rsp_t core_rsp_o [HPDcacheCfg.u.nRequesters], + + // Read / Invalidation memory interface + input logic mem_req_read_ready_i, + output logic mem_req_read_valid_o, + output hpdcache_mem_req_t mem_req_read_o, + + output logic mem_resp_read_ready_o, + input logic mem_resp_read_valid_i, + input hpdcache_mem_resp_r_t mem_resp_read_i, +`ifdef HPDCACHE_OPENPITON + input logic mem_resp_read_inval_i, + input hpdcache_nline_t mem_resp_read_inval_nline_i, +`endif + + // Write memory interface + input logic mem_req_write_ready_i, + output logic mem_req_write_valid_o, + output hpdcache_mem_req_t mem_req_write_o, + + input logic mem_req_write_data_ready_i, + output logic mem_req_write_data_valid_o, + output hpdcache_mem_req_w_t mem_req_write_data_o, + + output logic mem_resp_write_ready_o, + input logic mem_resp_write_valid_i, + input hpdcache_mem_resp_w_t mem_resp_write_i, + + // Performance events + output logic evt_cache_write_miss_o, + output logic evt_cache_read_miss_o, + output logic evt_uncached_req_o, + output logic evt_cmo_req_o, + output logic evt_write_req_o, + output logic evt_read_req_o, + output logic evt_prefetch_req_o, + output logic evt_req_on_hold_o, + output logic evt_rtab_rollback_o, + output logic evt_stall_refill_o, + output logic evt_stall_o, + + // Status interface + output logic wbuf_empty_o, + + // Configuration interface + input logic cfg_enable_i, + input wbuf_timecnt_t cfg_wbuf_threshold_i, + input logic cfg_wbuf_reset_timecnt_on_write_i, + input logic cfg_wbuf_sequential_waw_i, + input logic cfg_wbuf_inhibit_write_coalescing_i, + input logic cfg_prefetch_updt_plru_i, + input logic cfg_error_on_cacheable_amo_i, + input logic cfg_rtab_single_entry_i, + input logic cfg_default_wb_i +); + // }}} + + // Declaration of internal types + // {{{ + typedef logic [HPDcacheCfg.u.paWidth-1:0] hpdcache_req_addr_t; + typedef logic [HPDcacheCfg.nlineWidth-1:0] hpdcache_nline_t; + typedef logic [HPDcacheCfg.setWidth-1:0] hpdcache_set_t; + typedef logic [HPDcacheCfg.clOffsetWidth-1:0] hpdcache_offset_t; + typedef logic unsigned [HPDcacheCfg.clWordIdxWidth-1:0] hpdcache_word_t; + typedef logic unsigned [HPDcacheCfg.u.ways-1:0] hpdcache_way_vector_t; + typedef logic unsigned [HPDcacheCfg.wayIndexWidth-1:0] hpdcache_way_t; + typedef logic [HPDcacheCfg.u.clWords-1:0][HPDcacheCfg.u.wordUserWidth-1:0] hpdcache_cl_user_t; + + // Cache Directory entry definition + // {{{ + typedef struct packed { + // Cacheline state + // Encoding: {valid, wb, dirty, fetch} + // {0,X,X,0}: Invalid + // {0,X,X,1}: Invalid and Fetching + // {1,X,X,1}: Valid and Fetching (cacheline being replaced is accessible) + // {1,0,0,0}: Write-through + // {1,1,0,0}: Write-back (clean) + // {1,1,1,0}: Write-back (dirty) + // {{{ + logic valid; // valid cacheline + logic wback; // cacheline in write-back mode + logic dirty; // cacheline is locally modified (memory is obsolete) + logic fetch; // cacheline is reserved for a new cacheline being fetched + // }}} + + // Cacheline address tag + // {{{ + hpdcache_tag_t tag; + // }}} + } hpdcache_dir_entry_t; + // }}} + + typedef hpdcache_data_word_t [HPDcacheCfg.u.accessWords-1:0] hpdcache_access_data_t; + typedef hpdcache_data_be_t [HPDcacheCfg.u.accessWords-1:0] hpdcache_access_be_t; + typedef logic [HPDcacheCfg.u.accessWords-1:0][HPDcacheCfg.u.wordUserWidth-1:0] hpdcache_access_user_t; + + typedef hpdcache_req_addr_t wbuf_addr_t; + typedef hpdcache_req_user_t wbuf_user_t; + typedef hpdcache_req_data_t wbuf_data_t; + typedef hpdcache_req_be_t wbuf_be_t; + // }}} + + // Declaration of internal signals + // {{{ + logic refill_req_valid; + logic refill_req_ready; + logic refill_is_error; + logic refill_busy; + logic refill_updt_sel_victim; + hpdcache_set_t refill_set; + hpdcache_way_vector_t refill_way; + hpdcache_dir_entry_t refill_dir_entry; + logic refill_write_dir; + logic refill_write_data; + hpdcache_word_t refill_word; + hpdcache_access_user_t refill_user; + hpdcache_access_data_t refill_data; + logic refill_core_rsp_valid; + hpdcache_rsp_t refill_core_rsp; + hpdcache_nline_t refill_nline; + logic refill_updt_rtab; + + logic inval_check_dir; + logic inval_write_dir; + hpdcache_nline_t inval_nline; + logic inval_hit; + + logic miss_mshr_empty; + logic miss_mshr_check; + hpdcache_req_offset_t miss_mshr_check_offset; + hpdcache_nline_t miss_mshr_check_nline; + logic miss_mshr_hit; + logic miss_mshr_alloc_cs; + logic miss_mshr_alloc; + logic miss_mshr_alloc_ready; + logic miss_mshr_alloc_full; + logic miss_mshr_alloc_cbuf_full; + hpdcache_nline_t miss_mshr_alloc_nline; + hpdcache_req_tid_t miss_mshr_alloc_tid; + hpdcache_req_sid_t miss_mshr_alloc_sid; + hpdcache_word_t miss_mshr_alloc_word; + hpdcache_req_data_t miss_mshr_alloc_wdata; + hpdcache_req_user_t miss_mshr_alloc_wuser; + hpdcache_req_be_t miss_mshr_alloc_be; + hpdcache_way_vector_t miss_mshr_alloc_victim_way; + logic miss_mshr_alloc_need_rsp; + logic miss_mshr_alloc_is_prefetch; + logic miss_mshr_alloc_wback; + logic miss_mshr_alloc_dirty; + + logic wbuf_flush_all; + logic wbuf_write; + logic wbuf_write_ready; + wbuf_addr_t wbuf_write_addr; + wbuf_user_t wbuf_write_user; + wbuf_data_t wbuf_write_data; + wbuf_be_t wbuf_write_be; + logic wbuf_write_uncacheable; + logic wbuf_read_hit; + logic wbuf_read_flush_hit; + hpdcache_req_addr_t wbuf_rtab_addr; + logic wbuf_rtab_is_read; + logic wbuf_rtab_hit_open; + logic wbuf_rtab_hit_pend; + logic wbuf_rtab_hit_sent; + logic wbuf_rtab_not_ready; + + logic uc_ready; + logic uc_req_valid; + hpdcache_uc_op_t uc_req_op; + hpdcache_req_addr_t uc_req_addr; + hpdcache_req_size_t uc_req_size; + hpdcache_req_data_t uc_req_data; + hpdcache_req_user_t uc_req_user; + hpdcache_req_be_t uc_req_be; + logic uc_req_uncacheable; + hpdcache_req_sid_t uc_req_sid; + hpdcache_req_tid_t uc_req_tid; + logic uc_req_need_rsp; + logic uc_wbuf_flush_all; + logic uc_dir_amo_match; + hpdcache_set_t uc_dir_amo_match_set; + hpdcache_tag_t uc_dir_amo_match_tag; + logic uc_dir_amo_updt_sel_victim; + hpdcache_way_vector_t uc_dir_amo_hit_way; + logic uc_data_amo_write; + logic uc_data_amo_write_enable; + hpdcache_set_t uc_data_amo_write_set; + hpdcache_req_size_t uc_data_amo_write_size; + hpdcache_word_t uc_data_amo_write_word; + hpdcache_req_user_t uc_data_amo_write_user; + hpdcache_req_data_t uc_data_amo_write_data; + hpdcache_req_be_t uc_data_amo_write_be; + logic uc_lrsc_snoop; + hpdcache_req_addr_t uc_lrsc_snoop_addr; + hpdcache_req_size_t uc_lrsc_snoop_size; + logic uc_core_rsp_ready; + logic uc_core_rsp_valid; + hpdcache_rsp_t uc_core_rsp; + + logic cmo_req_valid; + logic cmo_ready; + hpdcache_cmoh_op_t cmo_req_op; + hpdcache_req_addr_t cmo_req_addr; + hpdcache_req_sid_t cmo_req_sid; + hpdcache_req_tid_t cmo_req_tid; + hpdcache_req_data_t cmo_req_wdata; + logic cmo_req_need_rsp; + logic cmo_dirty_set_en; + hpdcache_set_t cmo_dirty_min_set; + hpdcache_set_t cmo_dirty_max_set; + logic cmo_valid_set_en; + hpdcache_set_t cmo_valid_min_set; + hpdcache_set_t cmo_valid_max_set; + logic cmo_wbuf_flush_all; + logic cmo_flush_all; + logic cmo_inval_all; + logic cmo_dir_check_nline; + hpdcache_set_t cmo_dir_check_nline_set; + hpdcache_tag_t cmo_dir_check_nline_tag; + hpdcache_way_vector_t cmo_dir_check_nline_hit_way; + logic cmo_dir_check_nline_wback; + logic cmo_dir_check_nline_dirty; + logic cmo_dir_check_entry; + hpdcache_set_t cmo_dir_check_entry_set; + hpdcache_way_vector_t cmo_dir_check_entry_way; + logic cmo_dir_check_entry_valid; + logic cmo_dir_check_entry_wback; + logic cmo_dir_check_entry_dirty; + hpdcache_tag_t cmo_dir_check_entry_tag; + logic cmo_dir_updt; + hpdcache_set_t cmo_dir_updt_set; + hpdcache_way_vector_t cmo_dir_updt_way; + logic cmo_dir_updt_valid; + logic cmo_dir_updt_wback; + logic cmo_dir_updt_dirty; + logic cmo_dir_updt_fetch; + hpdcache_tag_t cmo_dir_updt_tag; + logic cmo_wait; + logic cmo_flush_alloc; + hpdcache_nline_t cmo_flush_alloc_nline; + hpdcache_way_vector_t cmo_flush_alloc_way; + logic cmo_core_rsp_ready; + logic cmo_core_rsp_valid; + hpdcache_rsp_t cmo_core_rsp; + + logic flush_empty; + logic flush_busy; + hpdcache_nline_t flush_check_nline; + logic flush_check_hit; + logic flush_alloc; + logic flush_alloc_ready; + hpdcache_nline_t flush_alloc_nline; + hpdcache_way_vector_t flush_alloc_way; + logic flush_data_read; + hpdcache_set_t flush_data_read_set; + hpdcache_word_t flush_data_read_word; + hpdcache_way_vector_t flush_data_read_way; + hpdcache_access_data_t flush_data_read_data; + hpdcache_access_user_t flush_data_read_user; + logic flush_ack; + hpdcache_nline_t flush_ack_nline; + + logic ctrl_flush_alloc; + hpdcache_nline_t ctrl_flush_alloc_nline; + hpdcache_way_vector_t ctrl_flush_alloc_way; + + logic rtab_empty; + logic ctrl_empty; + + logic core_rsp_valid; + hpdcache_rsp_t core_rsp; + + logic arb_req_valid; + logic arb_req_ready; + hpdcache_req_t arb_req; + logic arb_abort; + hpdcache_tag_t arb_tag; + hpdcache_pma_t arb_pma; + + logic mem_req_read_miss_ready; + logic mem_req_read_miss_valid; + hpdcache_mem_req_t mem_req_read_miss; + + logic mem_resp_read_miss_ready; + logic mem_resp_read_miss_valid; + hpdcache_mem_resp_r_t mem_resp_read_miss; + logic mem_resp_read_miss_inval; + hpdcache_nline_t mem_resp_read_miss_inval_nline; + + logic mem_req_read_uc_ready; + logic mem_req_read_uc_valid; + hpdcache_mem_req_t mem_req_read_uc; + + logic mem_resp_read_uc_ready; + logic mem_resp_read_uc_valid; + hpdcache_mem_resp_r_t mem_resp_read_uc; + + logic mem_req_write_wbuf_ready; + logic mem_req_write_wbuf_valid; + hpdcache_mem_req_t mem_req_write_wbuf; + + logic mem_req_write_wbuf_data_ready; + logic mem_req_write_wbuf_data_valid; + hpdcache_mem_req_w_t mem_req_write_wbuf_data; + + logic mem_resp_write_wbuf_ready; + logic mem_resp_write_wbuf_valid; + hpdcache_mem_resp_w_t mem_resp_write_wbuf; + + logic mem_req_write_flush_ready; + logic mem_req_write_flush_valid; + hpdcache_mem_req_t mem_req_write_flush; + + logic mem_req_write_flush_data_ready; + logic mem_req_write_flush_data_valid; + hpdcache_mem_req_w_t mem_req_write_flush_data; + + logic mem_resp_write_flush_ready; + logic mem_resp_write_flush_valid; + hpdcache_mem_resp_w_t mem_resp_write_flush; + + logic mem_req_write_uc_ready; + logic mem_req_write_uc_valid; + hpdcache_mem_req_t mem_req_write_uc; + + logic mem_req_write_uc_data_ready; + logic mem_req_write_uc_data_valid; + hpdcache_mem_req_w_t mem_req_write_uc_data; + + logic mem_resp_write_uc_ready; + logic mem_resp_write_uc_valid; + hpdcache_mem_resp_w_t mem_resp_write_uc; + + logic cfg_default_wb; + + localparam logic [HPDcacheCfg.u.memIdWidth-1:0] HPDCACHE_UC_READ_ID = + {HPDcacheCfg.u.memIdWidth{1'b1}}; + localparam logic [HPDcacheCfg.u.memIdWidth-1:0] HPDCACHE_UC_WRITE_ID = + {HPDcacheCfg.u.memIdWidth{1'b1}}; + // }}} + + // Requesters arbiter + // {{{ + hpdcache_core_arbiter #( + .HPDcacheCfg (HPDcacheCfg), + .hpdcache_tag_t (hpdcache_tag_t), + .hpdcache_req_t (hpdcache_req_t), + .hpdcache_rsp_t (hpdcache_rsp_t) + ) core_req_arbiter_i ( + .clk_i, + .rst_ni, + + .core_req_valid_i, + .core_req_ready_o, + .core_req_i, + .core_req_abort_i, + .core_req_tag_i, + .core_req_pma_i, + + .core_rsp_valid_i (core_rsp_valid), + .core_rsp_i (core_rsp), + .core_rsp_valid_o, + .core_rsp_o, + + .arb_req_valid_o (arb_req_valid), + .arb_req_ready_i (arb_req_ready), + .arb_req_o (arb_req), + .arb_abort_o (arb_abort), + .arb_tag_o (arb_tag), + .arb_pma_o (arb_pma) + ); + // }}} + + // HPDcache controller + // {{{ + if (HPDcacheCfg.u.wtEn && HPDcacheCfg.u.wbEn) begin : gen_cfg_default_wt_wb + assign cfg_default_wb = cfg_default_wb_i; + end else if (HPDcacheCfg.u.wtEn) begin : gen_cfg_default_wt + assign cfg_default_wb = 1'b0; + end else if (HPDcacheCfg.u.wbEn) begin : gen_cfg_default_wb + assign cfg_default_wb = 1'b1; + end + + hpdcache_ctrl #( + .HPDcacheCfg (HPDcacheCfg), + .hpdcache_nline_t (hpdcache_nline_t), + .hpdcache_tag_t (hpdcache_tag_t), + .hpdcache_set_t (hpdcache_set_t), + .hpdcache_word_t (hpdcache_word_t), + .hpdcache_data_word_t (hpdcache_data_word_t), + .hpdcache_data_be_t (hpdcache_data_be_t), + .hpdcache_dir_entry_t (hpdcache_dir_entry_t), + .hpdcache_way_vector_t (hpdcache_way_vector_t), + .hpdcache_way_t (hpdcache_way_t), + .hpdcache_cl_user_t (hpdcache_cl_user_t), + .wbuf_addr_t (wbuf_addr_t), + .wbuf_user_t (wbuf_user_t), + .wbuf_data_t (wbuf_data_t), + .wbuf_be_t (wbuf_be_t), + .hpdcache_access_data_t (hpdcache_access_data_t), + .hpdcache_access_user_t (hpdcache_access_user_t), + .hpdcache_access_be_t (hpdcache_access_be_t), + .hpdcache_req_addr_t (hpdcache_req_addr_t), + .hpdcache_req_offset_t (hpdcache_req_offset_t), + .hpdcache_req_tid_t (hpdcache_req_tid_t), + .hpdcache_req_sid_t (hpdcache_req_sid_t), + .hpdcache_req_data_t (hpdcache_req_data_t), + .hpdcache_req_user_t (hpdcache_req_user_t), + .hpdcache_req_be_t (hpdcache_req_be_t), + .hpdcache_req_t (hpdcache_req_t), + .hpdcache_rsp_t (hpdcache_rsp_t) + ) hpdcache_ctrl_i( + .clk_i, + .rst_ni, + + .core_req_valid_i (arb_req_valid), + .core_req_ready_o (arb_req_ready), + .core_req_i (arb_req), + .core_req_abort_i (arb_abort), + .core_req_tag_i (arb_tag), + .core_req_pma_i (arb_pma), + + .core_rsp_valid_o (core_rsp_valid), + .core_rsp_o (core_rsp), + + .wbuf_flush_i, + + .cachedir_hit_o (/* unused */), + + .st0_mshr_check_o (miss_mshr_check), + .st0_mshr_check_offset_o (miss_mshr_check_offset), + .st1_mshr_check_nline_o (miss_mshr_check_nline), + .st1_mshr_hit_i (miss_mshr_hit), + .st1_mshr_alloc_ready_i (miss_mshr_alloc_ready), + .st1_mshr_alloc_full_i (miss_mshr_alloc_full), + .st1_mshr_alloc_cbuf_full_i (miss_mshr_alloc_cbuf_full), + .st2_mshr_alloc_o (miss_mshr_alloc), + .st2_mshr_alloc_cs_o (miss_mshr_alloc_cs), + .st2_mshr_alloc_nline_o (miss_mshr_alloc_nline), + .st2_mshr_alloc_tid_o (miss_mshr_alloc_tid), + .st2_mshr_alloc_sid_o (miss_mshr_alloc_sid), + .st2_mshr_alloc_word_o (miss_mshr_alloc_word), + .st2_mshr_alloc_wdata_o (miss_mshr_alloc_wdata), + .st2_mshr_alloc_wuser_o (miss_mshr_alloc_wuser), + .st2_mshr_alloc_be_o (miss_mshr_alloc_be), + .st2_mshr_alloc_victim_way_o (miss_mshr_alloc_victim_way), + .st2_mshr_alloc_need_rsp_o (miss_mshr_alloc_need_rsp), + .st2_mshr_alloc_is_prefetch_o (miss_mshr_alloc_is_prefetch), + .st2_mshr_alloc_wback_o (miss_mshr_alloc_wback), + .st2_mshr_alloc_dirty_o (miss_mshr_alloc_dirty), + + .refill_req_valid_i (refill_req_valid), + .refill_req_ready_o (refill_req_ready), + .refill_is_error_i (refill_is_error), + .refill_busy_i (refill_busy), + .refill_updt_sel_victim_i (refill_updt_sel_victim), + .refill_set_i (refill_set), + .refill_way_i (refill_way), + .refill_dir_entry_i (refill_dir_entry), + .refill_write_dir_i (refill_write_dir), + .refill_write_data_i (refill_write_data), + .refill_word_i (refill_word), + .refill_user_i (refill_user), + .refill_data_i (refill_data), + .refill_core_rsp_valid_i (refill_core_rsp_valid), + .refill_core_rsp_i (refill_core_rsp), + .refill_nline_i (refill_nline), + .refill_updt_rtab_i (refill_updt_rtab), + + .flush_busy_i (flush_busy), + .flush_check_nline_o (flush_check_nline), + .flush_check_hit_i (flush_check_hit), + .flush_alloc_o (ctrl_flush_alloc), + .flush_alloc_ready_i (flush_alloc_ready), + .flush_alloc_nline_o (ctrl_flush_alloc_nline), + .flush_alloc_way_o (ctrl_flush_alloc_way), + .flush_data_read_i (flush_data_read), + .flush_data_read_set_i (flush_data_read_set), + .flush_data_read_word_i (flush_data_read_word), + .flush_data_read_way_i (flush_data_read_way), + .flush_data_read_data_o (flush_data_read_data), + .flush_data_read_user_o (flush_data_read_user), + .flush_ack_i (flush_ack), + .flush_ack_nline_i (flush_ack_nline), + + .inval_check_dir_i (inval_check_dir), + .inval_write_dir_i (inval_write_dir), + .inval_nline_i (inval_nline), + .inval_hit_o (inval_hit), + + .wbuf_empty_i (wbuf_empty_o), + .wbuf_flush_all_o (wbuf_flush_all), + .wbuf_write_o (wbuf_write), + .wbuf_write_ready_i (wbuf_write_ready), + .wbuf_write_addr_o (wbuf_write_addr), + .wbuf_write_user_o (wbuf_write_user), + .wbuf_write_data_o (wbuf_write_data), + .wbuf_write_be_o (wbuf_write_be), + .wbuf_write_uncacheable_o (wbuf_write_uncacheable), + .wbuf_read_hit_i (wbuf_read_hit), + .wbuf_read_flush_hit_o (wbuf_read_flush_hit), + .wbuf_rtab_addr_o (wbuf_rtab_addr), + .wbuf_rtab_is_read_o (wbuf_rtab_is_read), + .wbuf_rtab_hit_open_i (wbuf_rtab_hit_open), + .wbuf_rtab_hit_pend_i (wbuf_rtab_hit_pend), + .wbuf_rtab_hit_sent_i (wbuf_rtab_hit_sent), + .wbuf_rtab_not_ready_i (wbuf_rtab_not_ready), + + .uc_busy_i (~uc_ready), + .uc_lrsc_snoop_o (uc_lrsc_snoop), + .uc_lrsc_snoop_addr_o (uc_lrsc_snoop_addr), + .uc_lrsc_snoop_size_o (uc_lrsc_snoop_size), + .uc_req_valid_o (uc_req_valid), + .uc_req_op_o (uc_req_op), + .uc_req_addr_o (uc_req_addr), + .uc_req_size_o (uc_req_size), + .uc_req_data_o (uc_req_data), + .uc_req_user_o (uc_req_user), + .uc_req_be_o (uc_req_be), + .uc_req_uc_o (uc_req_uncacheable), + .uc_req_sid_o (uc_req_sid), + .uc_req_tid_o (uc_req_tid), + .uc_req_need_rsp_o (uc_req_need_rsp), + .uc_wbuf_flush_all_i (uc_wbuf_flush_all), + .uc_dir_amo_match_i (uc_dir_amo_match), + .uc_dir_amo_match_set_i (uc_dir_amo_match_set), + .uc_dir_amo_match_tag_i (uc_dir_amo_match_tag), + .uc_dir_amo_updt_sel_victim_i (uc_dir_amo_updt_sel_victim), + .uc_dir_amo_hit_way_o (uc_dir_amo_hit_way), + .uc_data_amo_write_i (uc_data_amo_write), + .uc_data_amo_write_enable_i (uc_data_amo_write_enable), + .uc_data_amo_write_set_i (uc_data_amo_write_set), + .uc_data_amo_write_size_i (uc_data_amo_write_size), + .uc_data_amo_write_word_i (uc_data_amo_write_word), + .uc_data_amo_write_user_i (uc_data_amo_write_user), + .uc_data_amo_write_data_i (uc_data_amo_write_data), + .uc_data_amo_write_be_i (uc_data_amo_write_be), + .uc_core_rsp_ready_o (uc_core_rsp_ready), + .uc_core_rsp_valid_i (uc_core_rsp_valid), + .uc_core_rsp_i (uc_core_rsp), + + .cmo_busy_i (~cmo_ready), + .cmo_wait_i (cmo_wait), + .cmo_req_valid_o (cmo_req_valid), + .cmo_req_op_o (cmo_req_op), + .cmo_req_addr_o (cmo_req_addr), + .cmo_req_wdata_o (cmo_req_wdata), + .cmo_req_sid_o (cmo_req_sid), + .cmo_req_tid_o (cmo_req_tid), + .cmo_req_need_rsp_o (cmo_req_need_rsp), + .cmo_dirty_set_en_o (cmo_dirty_set_en), + .cmo_dirty_min_set_o (cmo_dirty_min_set), + .cmo_dirty_max_set_o (cmo_dirty_max_set), + .cmo_valid_set_en_o (cmo_valid_set_en), + .cmo_valid_min_set_o (cmo_valid_min_set), + .cmo_valid_max_set_o (cmo_valid_max_set), + .cmo_wbuf_flush_all_i (cmo_wbuf_flush_all), + .cmo_flush_all_i (cmo_flush_all), + .cmo_inval_all_i (cmo_inval_all), + .cmo_dir_check_nline_i (cmo_dir_check_nline), + .cmo_dir_check_nline_set_i (cmo_dir_check_nline_set), + .cmo_dir_check_nline_tag_i (cmo_dir_check_nline_tag), + .cmo_dir_check_nline_hit_way_o (cmo_dir_check_nline_hit_way), + .cmo_dir_check_nline_wback_o (cmo_dir_check_nline_wback), + .cmo_dir_check_nline_dirty_o (cmo_dir_check_nline_dirty), + .cmo_dir_check_entry_i (cmo_dir_check_entry), + .cmo_dir_check_entry_set_i (cmo_dir_check_entry_set), + .cmo_dir_check_entry_way_i (cmo_dir_check_entry_way), + .cmo_dir_check_entry_valid_o (cmo_dir_check_entry_valid), + .cmo_dir_check_entry_wback_o (cmo_dir_check_entry_wback), + .cmo_dir_check_entry_dirty_o (cmo_dir_check_entry_dirty), + .cmo_dir_check_entry_tag_o (cmo_dir_check_entry_tag), + .cmo_dir_updt_i (cmo_dir_updt), + .cmo_dir_updt_set_i (cmo_dir_updt_set), + .cmo_dir_updt_way_i (cmo_dir_updt_way), + .cmo_dir_updt_valid_i (cmo_dir_updt_valid), + .cmo_dir_updt_wback_i (cmo_dir_updt_wback), + .cmo_dir_updt_dirty_i (cmo_dir_updt_dirty), + .cmo_dir_updt_fetch_i (cmo_dir_updt_fetch), + .cmo_dir_updt_tag_i (cmo_dir_updt_tag), + .cmo_core_rsp_ready_o (cmo_core_rsp_ready), + .cmo_core_rsp_valid_i (cmo_core_rsp_valid), + .cmo_core_rsp_i (cmo_core_rsp), + + .rtab_empty_o (rtab_empty), + .ctrl_empty_o (ctrl_empty), + + .cfg_enable_i, + .cfg_prefetch_updt_plru_i, + .cfg_rtab_single_entry_i, + .cfg_default_wb_i (cfg_default_wb), + + .evt_cache_write_miss_o, + .evt_cache_read_miss_o, + .evt_uncached_req_o, + .evt_cmo_req_o, + .evt_write_req_o, + .evt_read_req_o, + .evt_prefetch_req_o, + .evt_req_on_hold_o, + .evt_rtab_rollback_o, + .evt_stall_refill_o, + .evt_stall_o + ); + // }}} + + // HPDcache write-buffer + // {{{ + if (HPDcacheCfg.u.wtEn) begin : gen_wbuf + hpdcache_wbuf #( + .HPDcacheCfg (HPDcacheCfg), + .wbuf_addr_t (wbuf_addr_t), + .wbuf_timecnt_t (wbuf_timecnt_t), + .hpdcache_mem_id_t (hpdcache_mem_id_t), + .hpdcache_mem_req_t (hpdcache_mem_req_t), + .hpdcache_mem_req_w_t (hpdcache_mem_req_w_t), + .hpdcache_mem_resp_w_t (hpdcache_mem_resp_w_t) + ) hpdcache_wbuf_i( + .clk_i, + .rst_ni, + + .empty_o (wbuf_empty_o), + .full_o (/* unused */), + .flush_all_i (wbuf_flush_all), + + .cfg_threshold_i (cfg_wbuf_threshold_i), + .cfg_reset_timecnt_on_write_i (cfg_wbuf_reset_timecnt_on_write_i), + .cfg_sequential_waw_i (cfg_wbuf_sequential_waw_i), + .cfg_inhibit_write_coalescing_i (cfg_wbuf_inhibit_write_coalescing_i), + + .write_i (wbuf_write), + .write_ready_o (wbuf_write_ready), + .write_addr_i (wbuf_write_addr), + .write_user_i (wbuf_write_user), + .write_data_i (wbuf_write_data), + .write_be_i (wbuf_write_be), + .write_uc_i (wbuf_write_uncacheable), + + .read_addr_i (wbuf_write_addr), + .read_hit_o (wbuf_read_hit), + .read_flush_hit_i (wbuf_read_flush_hit), + + .replay_addr_i (wbuf_rtab_addr), + .replay_is_read_i (wbuf_rtab_is_read), + .replay_open_hit_o (wbuf_rtab_hit_open), + .replay_pend_hit_o (wbuf_rtab_hit_pend), + .replay_sent_hit_o (wbuf_rtab_hit_sent), + .replay_not_ready_o (wbuf_rtab_not_ready), + + .mem_req_write_ready_i (mem_req_write_wbuf_ready), + .mem_req_write_valid_o (mem_req_write_wbuf_valid), + .mem_req_write_o (mem_req_write_wbuf), + + .mem_req_write_data_ready_i (mem_req_write_wbuf_data_ready), + .mem_req_write_data_valid_o (mem_req_write_wbuf_data_valid), + .mem_req_write_data_o (mem_req_write_wbuf_data), + + .mem_resp_write_ready_o (mem_resp_write_wbuf_ready), + .mem_resp_write_valid_i (mem_resp_write_wbuf_valid), + .mem_resp_write_i (mem_resp_write_wbuf) + ); + end else begin : gen_no_wbuf + // The write-buffer behaves as a black-hole: consumes but do not produce data + assign wbuf_empty_o = 1'b1; + assign wbuf_write_ready = 1'b1; + assign wbuf_read_hit = 1'b0; + assign wbuf_rtab_hit_open = 1'b0; + assign wbuf_rtab_hit_pend = 1'b0; + assign wbuf_rtab_hit_sent = 1'b0; + assign wbuf_rtab_not_ready = 1'b0; + assign mem_req_write_wbuf_valid = 1'b0; + assign mem_req_write_wbuf = '{ + mem_req_command: HPDCACHE_MEM_READ, + mem_req_atomic : HPDCACHE_MEM_ATOMIC_ADD, + default : '0 + }; + assign mem_req_write_wbuf_data_valid = 1'b0; + assign mem_req_write_wbuf_data = '0; + assign mem_resp_write_wbuf_ready = 1'b1; + end + // }}} + + // Miss handler + // {{{ + hpdcache_miss_handler #( + .HPDcacheCfg (HPDcacheCfg), + .hpdcache_nline_t (hpdcache_nline_t), + .hpdcache_set_t (hpdcache_set_t), + .hpdcache_tag_t (hpdcache_tag_t), + .hpdcache_word_t (hpdcache_word_t), + .hpdcache_cl_user_t (hpdcache_cl_user_t), + .hpdcache_way_vector_t (hpdcache_way_vector_t), + .hpdcache_way_t (hpdcache_way_t), + .hpdcache_dir_entry_t (hpdcache_dir_entry_t), + .hpdcache_refill_data_t (hpdcache_access_data_t), + .hpdcache_refill_user_t (hpdcache_access_user_t), + .hpdcache_req_data_t (hpdcache_req_data_t), + .hpdcache_req_user_t (hpdcache_req_user_t), + .hpdcache_req_be_t (hpdcache_req_be_t), + .hpdcache_req_offset_t (hpdcache_req_offset_t), + .hpdcache_req_sid_t (hpdcache_req_sid_t), + .hpdcache_req_tid_t (hpdcache_req_tid_t), + .hpdcache_req_t (hpdcache_req_t), + .hpdcache_rsp_t (hpdcache_rsp_t), + .hpdcache_mem_id_t (hpdcache_mem_id_t), + .hpdcache_mem_req_t (hpdcache_mem_req_t), + .hpdcache_mem_resp_r_t (hpdcache_mem_resp_r_t) + ) hpdcache_miss_handler_i( + .clk_i, + .rst_ni, + + .mshr_empty_o (miss_mshr_empty), + .mshr_full_o (/* unused */), + + .cfg_prefetch_updt_sel_victim_i (cfg_prefetch_updt_plru_i), + + .mshr_check_i (miss_mshr_check), + .mshr_check_offset_i (miss_mshr_check_offset), + .mshr_check_nline_i (miss_mshr_check_nline), + .mshr_check_hit_o (miss_mshr_hit), + + .mshr_alloc_ready_o (miss_mshr_alloc_ready), + .mshr_alloc_i (miss_mshr_alloc), + .mshr_alloc_cs_i (miss_mshr_alloc_cs), + .mshr_alloc_nline_i (miss_mshr_alloc_nline), + .mshr_alloc_full_o (miss_mshr_alloc_full), + .mshr_alloc_cbuf_full_o (miss_mshr_alloc_cbuf_full), + .mshr_alloc_tid_i (miss_mshr_alloc_tid), + .mshr_alloc_sid_i (miss_mshr_alloc_sid), + .mshr_alloc_word_i (miss_mshr_alloc_word), + .mshr_alloc_victim_way_i (miss_mshr_alloc_victim_way), + .mshr_alloc_need_rsp_i (miss_mshr_alloc_need_rsp), + .mshr_alloc_is_prefetch_i (miss_mshr_alloc_is_prefetch), + .mshr_alloc_wback_i (miss_mshr_alloc_wback), + .mshr_alloc_dirty_i (miss_mshr_alloc_dirty), + .mshr_alloc_wdata_i (miss_mshr_alloc_wdata), + .mshr_alloc_wuser_i (miss_mshr_alloc_wuser), + .mshr_alloc_be_i (miss_mshr_alloc_be), + + .refill_req_ready_i (refill_req_ready), + .refill_req_valid_o (refill_req_valid), + .refill_is_error_o (refill_is_error), + .refill_busy_o (refill_busy), + .refill_updt_sel_victim_o (refill_updt_sel_victim), + .refill_set_o (refill_set), + .refill_way_o (refill_way), + .refill_dir_entry_o (refill_dir_entry), + .refill_write_dir_o (refill_write_dir), + .refill_write_data_o (refill_write_data), + .refill_user_o (refill_user), + .refill_data_o (refill_data), + .refill_word_o (refill_word), + .refill_nline_o (refill_nline), + .refill_updt_rtab_o (refill_updt_rtab), + + .inval_check_dir_o (inval_check_dir), + .inval_write_dir_o (inval_write_dir), + .inval_nline_o (inval_nline), + .inval_hit_i (inval_hit), + + .refill_core_rsp_valid_o (refill_core_rsp_valid), + .refill_core_rsp_o (refill_core_rsp), + + .mem_req_ready_i (mem_req_read_miss_ready), + .mem_req_valid_o (mem_req_read_miss_valid), + .mem_req_o (mem_req_read_miss), + + .mem_resp_ready_o (mem_resp_read_miss_ready), + .mem_resp_valid_i (mem_resp_read_miss_valid), + .mem_resp_i (mem_resp_read_miss), + .mem_resp_inval_i (mem_resp_read_miss_inval), + .mem_resp_inval_nline_i (mem_resp_read_miss_inval_nline) + ); + // }}} + + // Uncacheable request handler + // {{{ + hpdcache_uncached #( + .HPDcacheCfg (HPDcacheCfg), + .hpdcache_nline_t (hpdcache_nline_t), + .hpdcache_tag_t (hpdcache_tag_t), + .hpdcache_set_t (hpdcache_set_t), + .hpdcache_offset_t (hpdcache_offset_t), + .hpdcache_word_t (hpdcache_word_t), + .hpdcache_req_addr_t (hpdcache_req_addr_t), + .hpdcache_req_tid_t (hpdcache_req_tid_t), + .hpdcache_req_sid_t (hpdcache_req_sid_t), + .hpdcache_req_user_t (hpdcache_req_user_t), + .hpdcache_req_data_t (hpdcache_req_data_t), + .hpdcache_req_be_t (hpdcache_req_be_t), + .hpdcache_way_vector_t (hpdcache_way_vector_t), + .hpdcache_req_t (hpdcache_req_t), + .hpdcache_rsp_t (hpdcache_rsp_t), + .hpdcache_mem_id_t (hpdcache_mem_id_t), + .hpdcache_mem_req_t (hpdcache_mem_req_t), + .hpdcache_mem_req_w_t (hpdcache_mem_req_w_t), + .hpdcache_mem_resp_r_t (hpdcache_mem_resp_r_t), + .hpdcache_mem_resp_w_t (hpdcache_mem_resp_w_t) + ) hpdcache_uc_i( + .clk_i, + .rst_ni, + + .wbuf_empty_i (wbuf_empty_o), + .mshr_empty_i (miss_mshr_empty), + .rtab_empty_i (rtab_empty), + .ctrl_empty_i (ctrl_empty), + .flush_empty_i (flush_empty), + + .req_valid_i (uc_req_valid), + .req_ready_o (uc_ready), + .req_op_i (uc_req_op), + .req_addr_i (uc_req_addr), + .req_size_i (uc_req_size), + .req_data_i (uc_req_data), + .req_user_i (uc_req_user), + .req_be_i (uc_req_be), + .req_uc_i (uc_req_uncacheable), + .req_sid_i (uc_req_sid), + .req_tid_i (uc_req_tid), + .req_need_rsp_i (uc_req_need_rsp), + + .wbuf_flush_all_o (uc_wbuf_flush_all), + + .dir_amo_match_o (uc_dir_amo_match), + .dir_amo_match_set_o (uc_dir_amo_match_set), + .dir_amo_match_tag_o (uc_dir_amo_match_tag), + .dir_amo_updt_sel_victim_o (uc_dir_amo_updt_sel_victim), + .dir_amo_hit_way_i (uc_dir_amo_hit_way), + + .data_amo_write_o (uc_data_amo_write), + .data_amo_write_enable_o (uc_data_amo_write_enable), + .data_amo_write_set_o (uc_data_amo_write_set), + .data_amo_write_size_o (uc_data_amo_write_size), + .data_amo_write_word_o (uc_data_amo_write_word), + .data_amo_write_user_o (uc_data_amo_write_user), + .data_amo_write_data_o (uc_data_amo_write_data), + .data_amo_write_be_o (uc_data_amo_write_be), + + .lrsc_snoop_i (uc_lrsc_snoop), + .lrsc_snoop_addr_i (uc_lrsc_snoop_addr), + .lrsc_snoop_size_i (uc_lrsc_snoop_size), + + .core_rsp_ready_i (uc_core_rsp_ready), + .core_rsp_valid_o (uc_core_rsp_valid), + .core_rsp_o (uc_core_rsp), + + .mem_read_id_i (HPDCACHE_UC_READ_ID), + .mem_write_id_i (HPDCACHE_UC_WRITE_ID), + + .mem_req_read_ready_i (mem_req_read_uc_ready), + .mem_req_read_valid_o (mem_req_read_uc_valid), + .mem_req_read_o (mem_req_read_uc), + + .mem_resp_read_ready_o (mem_resp_read_uc_ready), + .mem_resp_read_valid_i (mem_resp_read_uc_valid), + .mem_resp_read_i (mem_resp_read_uc), + + .mem_req_write_ready_i (mem_req_write_uc_ready), + .mem_req_write_valid_o (mem_req_write_uc_valid), + .mem_req_write_o (mem_req_write_uc), + + .mem_req_write_data_ready_i (mem_req_write_uc_data_ready), + .mem_req_write_data_valid_o (mem_req_write_uc_data_valid), + .mem_req_write_data_o (mem_req_write_uc_data), + + .mem_resp_write_ready_o (mem_resp_write_uc_ready), + .mem_resp_write_valid_i (mem_resp_write_uc_valid), + .mem_resp_write_i (mem_resp_write_uc), + + .cfg_error_on_cacheable_amo_i + ); + // }}} + + // CMO Request Handler + // {{{ + hpdcache_cmo #( + .HPDcacheCfg (HPDcacheCfg), + + .hpdcache_nline_t (hpdcache_nline_t), + .hpdcache_tag_t (hpdcache_tag_t), + .hpdcache_set_t (hpdcache_set_t), + .hpdcache_data_word_t (hpdcache_data_word_t), + .hpdcache_way_vector_t (hpdcache_way_vector_t), + + .hpdcache_rsp_t (hpdcache_rsp_t), + .hpdcache_req_addr_t (hpdcache_req_addr_t), + .hpdcache_req_tid_t (hpdcache_req_tid_t), + .hpdcache_req_sid_t (hpdcache_req_sid_t), + .hpdcache_req_data_t (hpdcache_req_data_t) + ) hpdcache_cmo_i( + .clk_i, + .rst_ni, + + .wbuf_empty_i (wbuf_empty_o), + .mshr_empty_i (miss_mshr_empty), + .rtab_empty_i (rtab_empty), + .ctrl_empty_i (ctrl_empty), + + .req_valid_i (cmo_req_valid), + .req_ready_o (cmo_ready), + .req_op_i (cmo_req_op), + .req_addr_i (cmo_req_addr), + .req_wdata_i (cmo_req_wdata), + .req_sid_i (cmo_req_sid), + .req_tid_i (cmo_req_tid), + .req_need_rsp_i (cmo_req_need_rsp), + .req_wait_o (cmo_wait), + + .dirty_set_en_i (cmo_dirty_set_en), + .dirty_min_set_i (cmo_dirty_min_set), + .dirty_max_set_i (cmo_dirty_max_set), + .valid_set_en_i (cmo_valid_set_en), + .valid_min_set_i (cmo_valid_min_set), + .valid_max_set_i (cmo_valid_max_set), + .flush_all_o (cmo_flush_all), + .inval_all_o (cmo_inval_all), + + .core_rsp_ready_i (cmo_core_rsp_ready), + .core_rsp_valid_o (cmo_core_rsp_valid), + .core_rsp_o (cmo_core_rsp), + + .wbuf_flush_all_o (cmo_wbuf_flush_all), + + .dir_check_nline_o (cmo_dir_check_nline), + .dir_check_nline_set_o (cmo_dir_check_nline_set), + .dir_check_nline_tag_o (cmo_dir_check_nline_tag), + .dir_check_nline_hit_way_i (cmo_dir_check_nline_hit_way), + .dir_check_nline_wback_i (cmo_dir_check_nline_wback), + .dir_check_nline_dirty_i (cmo_dir_check_nline_dirty), + + .dir_check_entry_o (cmo_dir_check_entry), + .dir_check_entry_set_o (cmo_dir_check_entry_set), + .dir_check_entry_way_o (cmo_dir_check_entry_way), + .dir_check_entry_valid_i (cmo_dir_check_entry_valid), + .dir_check_entry_wback_i (cmo_dir_check_entry_wback), + .dir_check_entry_dirty_i (cmo_dir_check_entry_dirty), + .dir_check_entry_tag_i (cmo_dir_check_entry_tag), + + .dir_updt_o (cmo_dir_updt), + .dir_updt_set_o (cmo_dir_updt_set), + .dir_updt_way_o (cmo_dir_updt_way), + .dir_updt_valid_o (cmo_dir_updt_valid), + .dir_updt_wback_o (cmo_dir_updt_wback), + .dir_updt_dirty_o (cmo_dir_updt_dirty), + .dir_updt_fetch_o (cmo_dir_updt_fetch), + .dir_updt_tag_o (cmo_dir_updt_tag), + + .flush_empty_i (flush_empty), + .flush_alloc_o (cmo_flush_alloc), + .flush_alloc_ready_i (flush_alloc_ready), + .flush_alloc_nline_o (cmo_flush_alloc_nline), + .flush_alloc_way_o (cmo_flush_alloc_way) + ); + // }}} + + // Flush controller + // {{{ + if (HPDcacheCfg.u.wbEn) begin : gen_flush + assign flush_alloc = ctrl_flush_alloc | cmo_flush_alloc; + assign flush_alloc_nline = + ctrl_flush_alloc ? ctrl_flush_alloc_nline : cmo_flush_alloc_nline; + assign flush_alloc_way = + ctrl_flush_alloc ? ctrl_flush_alloc_way : cmo_flush_alloc_way; + + hpdcache_flush #( + .HPDcacheCfg (HPDcacheCfg), + + .hpdcache_nline_t (hpdcache_nline_t), + .hpdcache_set_t (hpdcache_set_t), + .hpdcache_word_t (hpdcache_word_t), + .hpdcache_way_vector_t (hpdcache_way_vector_t), + .hpdcache_access_data_t (hpdcache_access_data_t), + .hpdcache_access_user_t (hpdcache_access_user_t), + + .hpdcache_mem_id_t (hpdcache_mem_id_t), + .hpdcache_mem_data_t (hpdcache_mem_data_t), + .hpdcache_mem_user_t (hpdcache_mem_user_t), + .hpdcache_mem_req_t (hpdcache_mem_req_t), + .hpdcache_mem_req_w_t (hpdcache_mem_req_w_t), + .hpdcache_mem_resp_w_t (hpdcache_mem_resp_w_t) + ) flush_i( + .clk_i, + .rst_ni, + + .flush_empty_o (flush_empty), + .flush_full_o (/* open */), + .flush_busy_o (flush_busy), + + .flush_check_nline_i (flush_check_nline), + .flush_check_hit_o (flush_check_hit), + + .flush_alloc_i (flush_alloc), + .flush_alloc_ready_o (flush_alloc_ready), + .flush_alloc_nline_i (flush_alloc_nline), + .flush_alloc_way_i (flush_alloc_way), + + .flush_data_read_o (flush_data_read), + .flush_data_read_set_o (flush_data_read_set), + .flush_data_read_word_o (flush_data_read_word), + .flush_data_read_way_o (flush_data_read_way), + .flush_data_read_data_i (flush_data_read_data), + .flush_data_read_user_i (flush_data_read_user), + + .flush_ack_o (flush_ack), + .flush_ack_nline_o (flush_ack_nline), + + .mem_req_write_ready_i (mem_req_write_flush_ready), + .mem_req_write_valid_o (mem_req_write_flush_valid), + .mem_req_write_o (mem_req_write_flush), + + .mem_req_write_data_ready_i (mem_req_write_flush_data_ready), + .mem_req_write_data_valid_o (mem_req_write_flush_data_valid), + .mem_req_write_data_o (mem_req_write_flush_data), + + .mem_resp_write_ready_o (mem_resp_write_flush_ready), + .mem_resp_write_valid_i (mem_resp_write_flush_valid), + .mem_resp_write_i (mem_resp_write_flush) + ); + end else begin : gen_no_flush + // The flush controller behaves as a black-hole: consumes but do not produce data + assign flush_empty = 1'b1; + assign flush_busy = 1'b0; + assign flush_check_hit = 1'b0; + assign flush_alloc_ready = 1'b1; + assign flush_data_read = 1'b0; + assign flush_data_read_set = '0; + assign flush_data_read_word = '0; + assign flush_data_read_way = '0; + assign flush_ack = 1'b0; + assign flush_ack_nline = '0; + assign mem_req_write_flush_valid = 1'b0; + assign mem_req_write_flush = '{ + mem_req_command: HPDCACHE_MEM_READ, + mem_req_atomic : HPDCACHE_MEM_ATOMIC_ADD, + default : '0 + }; + assign mem_req_write_flush_data_valid = 1'b0; + assign mem_req_write_flush_data = '0; + assign mem_resp_write_flush_ready = 1'b1; + end + // }}} + + // Read and Write Arbiters for Memory interfaces + // {{{ + + // Read request interface + // + // There is a fixed-priority arbiter between: + // - the miss_handler (higher priority); + // - the uncacheable request handler (lower priority) + logic [1:0] arb_mem_req_read_ready; + logic [1:0] arb_mem_req_read_valid; + hpdcache_mem_req_t [1:0] arb_mem_req_read; + + assign mem_req_read_miss_ready = arb_mem_req_read_ready[0]; + assign arb_mem_req_read_valid[0] = mem_req_read_miss_valid; + assign arb_mem_req_read[0] = mem_req_read_miss; + + assign mem_req_read_uc_ready = arb_mem_req_read_ready[1]; + assign arb_mem_req_read_valid[1] = mem_req_read_uc_valid; + assign arb_mem_req_read[1] = mem_req_read_uc; + + hpdcache_mem_req_read_arbiter #( + .N (2), + .hpdcache_mem_req_t (hpdcache_mem_req_t) + ) hpdcache_mem_req_read_arbiter_i( + .clk_i, + .rst_ni, + + .mem_req_read_ready_o (arb_mem_req_read_ready), + .mem_req_read_valid_i (arb_mem_req_read_valid), + .mem_req_read_i (arb_mem_req_read), + + .mem_req_read_ready_i, + .mem_req_read_valid_o, + .mem_req_read_o (mem_req_read_o) + ); + + // Read response interface + always_comb + begin : mem_resp_read_demux_comb + mem_resp_read_uc_valid = 1'b0; + mem_resp_read_miss_valid = 1'b0; + mem_resp_read_ready_o = 1'b0; + if (mem_resp_read_valid_i) begin + if (mem_resp_read_i.mem_resp_r_id == {HPDcacheCfg.u.memIdWidth{1'b1}}) begin + mem_resp_read_uc_valid = 1'b1; + mem_resp_read_ready_o = mem_resp_read_uc_ready; + end else begin + mem_resp_read_miss_valid = 1'b1; + mem_resp_read_ready_o = mem_resp_read_miss_ready; + end + end + end + + assign mem_resp_read_uc = mem_resp_read_i; + assign mem_resp_read_miss = mem_resp_read_i; +`ifdef HPDCACHE_OPENPITON + assign mem_resp_read_miss_inval = mem_resp_read_inval_i; + assign mem_resp_read_miss_inval_nline = mem_resp_read_inval_nline_i; +`else + assign mem_resp_read_miss_inval = 1'b0; + assign mem_resp_read_miss_inval_nline = '0; +`endif + + // Write request interface + // + // There is a fixed-priority arbiter between: + // - the flush controller (higher priority) + // - the write buffer + // - the uncacheable request handler (lower priority) + logic [2:0] arb_mem_req_write_ready; + logic [2:0] arb_mem_req_write_valid; + hpdcache_mem_req_t [2:0] arb_mem_req_write; + + logic [2:0] arb_mem_req_write_data_valid; + logic [2:0] arb_mem_req_write_data_ready; + hpdcache_mem_req_w_t [2:0] arb_mem_req_write_data; + + // Split the ID space into 3 segments: + // 1111...1111 -> Uncached writes + // 1xxx...xxxx -> Flush writes (where at least one x is 0) + // 0xxx...xxxx -> Write buffer writes + function automatic hpdcache_mem_req_t hpdcache_req_write_sel_id( + hpdcache_mem_req_t req, int kind + ); + automatic hpdcache_mem_req_t ret_req; + ret_req = req; + // Request from the write buffer + unique if (kind == 0) begin + ret_req.mem_req_id = {1'b0, req.mem_req_id[0 +: HPDcacheCfg.u.memIdWidth-1]}; + end + // Request from the flush controller + else if (kind == 1) begin + ret_req.mem_req_id = {1'b1, req.mem_req_id[0 +: HPDcacheCfg.u.memIdWidth-1]}; + end + // Request from the uncached controller + else if (kind == 2) begin + ret_req.mem_req_id = '1; + end + return ret_req; + endfunction + + function automatic hpdcache_mem_resp_w_t hpdcache_resp_write_sel_id( + hpdcache_mem_resp_w_t resp, int kind + ); + automatic hpdcache_mem_resp_w_t ret_resp; + ret_resp = resp; + // Response to the write buffer + unique if (kind == 0) begin + ret_resp.mem_resp_w_id = {1'b0, resp.mem_resp_w_id[0 +: HPDcacheCfg.u.memIdWidth-1]}; + end + // Response to the flush controller + else if (kind == 1) begin + ret_resp.mem_resp_w_id = {1'b0, resp.mem_resp_w_id[0 +: HPDcacheCfg.u.memIdWidth-1]}; + end + // Response to the uncached controller + else if (kind == 2) begin + ret_resp.mem_resp_w_id = '1; + end + return ret_resp; + endfunction + + assign mem_req_write_wbuf_ready = arb_mem_req_write_ready[0]; + assign arb_mem_req_write_valid[0] = mem_req_write_wbuf_valid; + assign arb_mem_req_write[0] = hpdcache_req_write_sel_id(mem_req_write_wbuf, 0); + + assign mem_req_write_wbuf_data_ready = arb_mem_req_write_data_ready[0]; + assign arb_mem_req_write_data_valid[0] = mem_req_write_wbuf_data_valid; + assign arb_mem_req_write_data[0] = mem_req_write_wbuf_data; + + assign mem_req_write_flush_ready = arb_mem_req_write_ready[1]; + assign arb_mem_req_write_valid[1] = mem_req_write_flush_valid; + assign arb_mem_req_write[1] = hpdcache_req_write_sel_id(mem_req_write_flush, 1); + + assign mem_req_write_flush_data_ready = arb_mem_req_write_data_ready[1]; + assign arb_mem_req_write_data_valid[1] = mem_req_write_flush_data_valid; + assign arb_mem_req_write_data[1] = mem_req_write_flush_data; + + assign mem_req_write_uc_ready = arb_mem_req_write_ready[2]; + assign arb_mem_req_write_valid[2] = mem_req_write_uc_valid; + assign arb_mem_req_write[2] = hpdcache_req_write_sel_id(mem_req_write_uc, 2); + + assign mem_req_write_uc_data_ready = arb_mem_req_write_data_ready[2]; + assign arb_mem_req_write_data_valid[2] = mem_req_write_uc_data_valid; + assign arb_mem_req_write_data[2] = mem_req_write_uc_data; + + hpdcache_mem_req_write_arbiter #( + .N (3), + .hpdcache_mem_req_t (hpdcache_mem_req_t), + .hpdcache_mem_req_w_t (hpdcache_mem_req_w_t) + ) hpdcache_mem_req_write_arbiter_i ( + .clk_i, + .rst_ni, + + .mem_req_write_ready_o (arb_mem_req_write_ready), + .mem_req_write_valid_i (arb_mem_req_write_valid), + .mem_req_write_i (arb_mem_req_write), + + .mem_req_write_data_ready_o (arb_mem_req_write_data_ready), + .mem_req_write_data_valid_i (arb_mem_req_write_data_valid), + .mem_req_write_data_i (arb_mem_req_write_data), + + .mem_req_write_ready_i, + .mem_req_write_valid_o, + .mem_req_write_o (mem_req_write_o), + + .mem_req_write_data_ready_i, + .mem_req_write_data_valid_o, + .mem_req_write_data_o (mem_req_write_data_o) + ); + + // Write response interface + always_comb + begin : mem_resp_write_demux_comb + mem_resp_write_flush_valid = 1'b0; + mem_resp_write_wbuf_valid = 1'b0; + mem_resp_write_uc_valid = 1'b0; + mem_resp_write_ready_o = 1'b0; + if (mem_resp_write_valid_i) begin + if (mem_resp_write_i.mem_resp_w_id == {HPDcacheCfg.u.memIdWidth{1'b1}}) begin + mem_resp_write_uc_valid = 1'b1; + mem_resp_write_ready_o = mem_resp_write_uc_ready; + end else if (mem_resp_write_i.mem_resp_w_id[HPDcacheCfg.u.memIdWidth-1]) begin + mem_resp_write_flush_valid = 1'b1; + mem_resp_write_ready_o = mem_resp_write_flush_ready; + end else begin + mem_resp_write_wbuf_valid = 1'b1; + mem_resp_write_ready_o = mem_resp_write_wbuf_ready; + end + end + end + + assign mem_resp_write_wbuf = hpdcache_resp_write_sel_id(mem_resp_write_i, 0); + assign mem_resp_write_flush = hpdcache_resp_write_sel_id(mem_resp_write_i, 1); + assign mem_resp_write_uc = hpdcache_resp_write_sel_id(mem_resp_write_i, 2); + // }}} + + // Assertions + // {{{ +`ifndef HPDCACHE_ASSERT_OFF + assert property (@(posedge clk_i) disable iff (rst_ni !== 1'b1) + ctrl_flush_alloc |-> !cmo_flush_alloc) else + $error("Unsupported concurrent flush from ctrl and cmo"); + + if (!(HPDcacheCfg.u.wordWidth inside {32, 64, 128})) begin : gen_word_width_assertion + $fatal(1, "word width shall be 32 or 64"); + end + if (HPDcacheCfg.u.reqWords > HPDcacheCfg.u.accessWords) begin : gen_req_data_width_assertion + $fatal(1, "req data width shall be l.e. to cache access width"); + end + if (HPDcacheCfg.u.clWords < HPDcacheCfg.u.accessWords) begin : gen_access_width_assertion + $fatal(1, "cache access width shall be l.e. to cache-line width"); + end + if (HPDcacheCfg.u.clWords <= 1) begin : gen_cacheline_words_assertion + $fatal(1, "cacheline words shall be greater than 1"); + end + if (HPDcacheCfg.clWidth < HPDcacheCfg.u.memDataWidth) begin : gen_cacheline_mem_data_assertion + $fatal(1, "cacheline width shall be g.e. to memory interface data width"); + end + if ((2**HPDcacheCfg.u.memIdWidth - 1) < (HPDcacheCfg.u.mshrWays * HPDcacheCfg.u.mshrSets)) + begin : gen_mem_id_mshr_width_assertion + $fatal(1, "insufficient ID bits on the mem interface to transport reads"); + end + if (HPDcacheCfg.u.wtEn && (2**(HPDcacheCfg.u.memIdWidth - 1) < HPDcacheCfg.u.wbufDirEntries)) + begin : gen_mem_id_wbuf_width_assertion + $fatal(1, "insufficient ID bits on the mem interface to transport writes"); + end + if (HPDcacheCfg.u.wtEn && (HPDcacheCfg.wbufDataWidth > HPDcacheCfg.u.memDataWidth)) + begin : gen_mem_data_wbuf_width_assertion + $fatal(1, "write buffer data width shall be l.e. to mem interface data width"); + end + if (HPDcacheCfg.u.wbEn && + (2**(HPDcacheCfg.u.memIdWidth - 1) < (HPDcacheCfg.u.flushEntries + 1))) + begin : gen_mem_id_flush_width_assertion + $fatal(1, "insufficient ID bits on the mem interface to transport flushes"); + end + if (!HPDcacheCfg.u.wtEn && !HPDcacheCfg.u.wbEn) begin : gen_write_policy_assertion + $fatal(1, "the cache shall be configured to support WT, WB or both"); + end +`endif + // }}} + +endmodule diff --git a/hw/vendor/hpdcache/rtl/src/hpdcache.vlt b/hw/vendor/hpdcache/rtl/src/hpdcache.vlt new file mode 100644 index 00000000..2a35859e --- /dev/null +++ b/hw/vendor/hpdcache/rtl/src/hpdcache.vlt @@ -0,0 +1,29 @@ +`verilator_config +// +// Copyright 2023 CEA* +// *Commissariat a l'Energie Atomique et aux Energies Alternatives (CEA) +// +// SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 +// +// Licensed under the Solderpad Hardware License v 2.1 (the “License”); you +// may not use this file except in compliance with the License, or, at your +// option, the Apache License version 2.0. You may obtain a copy of the +// License at +// +// https://solderpad.org/licenses/SHL-2.1/ +// +// Unless required by applicable law or agreed to in writing, any work +// distributed under the License is distributed on an “AS IS” BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations +// under the License. +// + +// +// Authors Cesar Fuguet +// Creation Date April, 2021 +// Description Verilator's configuration file +// History +// +lint_off -rule PINCONNECTEMPTY +lint_off -rule DECLFILENAME diff --git a/hw/vendor/hpdcache/rtl/src/hpdcache_amo.sv b/hw/vendor/hpdcache/rtl/src/hpdcache_amo.sv new file mode 100644 index 00000000..fef55f83 --- /dev/null +++ b/hw/vendor/hpdcache/rtl/src/hpdcache_amo.sv @@ -0,0 +1,114 @@ +/* + * Copyright 2023 CEA* + * *Commissariat a l'Energie Atomique et aux Energies Alternatives (CEA) + * Copyright 2025 Inria, Universite Grenoble-Alpes, TIMA + * + * SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 + * + * Licensed under the Solderpad Hardware License v 2.1 (the “License”); you + * may not use this file except in compliance with the License, or, at your + * option, the Apache License version 2.0. You may obtain a copy of the + * License at + * + * https://solderpad.org/licenses/SHL-2.1/ + * + * Unless required by applicable law or agreed to in writing, any work + * distributed under the License is distributed on an “AS IS” BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +/* + * Authors : Cesar Fuguet + * Creation Date : May, 2021 + * Description : HPDcache AMO computing unit + * History : + */ +module hpdcache_amo +import hpdcache_pkg::*; + // Parameters + // {{{ +#( + parameter int unsigned DATA_WIDTH = 64, + parameter int unsigned ARITH_WIDTH = DATA_WIDTH, + parameter type user_t = logic, + parameter bit userEn = 1'b0 +) +// Ports +// {{{ +( + input logic [DATA_WIDTH-1:0] ld_data_i, + input user_t ld_user_i, + input logic [DATA_WIDTH-1:0] st_data_i, + input user_t st_user_i, + input hpdcache_uc_op_t op_i, + output logic [DATA_WIDTH-1:0] result_o, + output user_t user_o +); +// }}} + + logic signed [ARITH_WIDTH-1:0] ld_data_signed; + logic signed [ARITH_WIDTH-1:0] st_data_signed; + logic signed [ARITH_WIDTH-1:0] sum; + logic [ARITH_WIDTH-1:0] ld_data_unsigned; + logic [ARITH_WIDTH-1:0] st_data_unsigned; + logic [ARITH_WIDTH-1:0] arith_result; + logic ugt, sgt; + + assign ld_data_signed = ld_data_i[ARITH_WIDTH-1:0], + st_data_signed = st_data_i[ARITH_WIDTH-1:0], + ld_data_unsigned = ld_data_i[ARITH_WIDTH-1:0], + st_data_unsigned = st_data_i[ARITH_WIDTH-1:0]; + + assign ugt = (ld_data_unsigned > st_data_unsigned), + sgt = (ld_data_signed > st_data_signed), + sum = ld_data_signed + st_data_signed; + + always_comb + begin : amo_compute_comb + // Artitmetic ops + unique case (1'b1) + op_i.is_amo_add : arith_result = sum; + op_i.is_amo_and : arith_result = ld_data_unsigned & st_data_unsigned; + op_i.is_amo_or : arith_result = ld_data_unsigned | st_data_unsigned; + op_i.is_amo_xor : arith_result = ld_data_unsigned ^ st_data_unsigned; + op_i.is_amo_max : arith_result = sgt ? ld_data_unsigned : st_data_unsigned; + op_i.is_amo_maxu : arith_result = ugt ? ld_data_unsigned : st_data_unsigned; + op_i.is_amo_min : arith_result = sgt ? st_data_unsigned : ld_data_unsigned; + op_i.is_amo_minu : arith_result = ugt ? st_data_unsigned : ld_data_unsigned; + default : arith_result = '0; + endcase + // Non-arithmetic ops + unique case (1'b1) + op_i.is_amo_lr : begin + result_o = ld_data_i; + user_o = userEn ? ld_user_i : '0; + end + op_i.is_amo_sc : begin + result_o = st_data_i; + user_o = userEn ? st_user_i : '0; + end + op_i.is_amo_swap : begin + result_o = st_data_i; + user_o = userEn ? st_user_i : '0; + end + default : begin + result_o = {{DATA_WIDTH - ARITH_WIDTH{1'b0}}, arith_result}; + user_o = '0; + end + endcase + end + +// Assertions +// {{{ +`ifndef HPDCACHE_ASSERT_OFF + initial + begin : initial_assertions + assert (DATA_WIDTH >= ARITH_WIDTH) else + $error( "hpdcache_amo: DATA_WIDTH (%0d) must be greater than or equal to ARITH_WIDTH (%0d)" + , DATA_WIDTH, ARITH_WIDTH ); + end +`endif +// }}} + +endmodule diff --git a/hw/vendor/hpdcache/rtl/src/hpdcache_cbuf.sv b/hw/vendor/hpdcache/rtl/src/hpdcache_cbuf.sv new file mode 100644 index 00000000..d74d1bb0 --- /dev/null +++ b/hw/vendor/hpdcache/rtl/src/hpdcache_cbuf.sv @@ -0,0 +1,99 @@ +// Copyright (c) 2025 ETH Zurich, University of Bologna +// +// Copyright and related rights are licensed under the Solderpad Hardware +// License, Version 2.1 (the "License"); you may not use this file except in +// compliance with the License. You may obtain a copy of the License at +// http://solderpad.org/licenses/SHL-2.1. Unless required by applicable law +// or agreed to in writing, software, hardware and materials distributed under +// this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +// +// Authors : Riccardo Tedeschi +// Creation Date : April, 2025 +// Description : Coalesce buffer +// History : +// + +module hpdcache_cbuf +import hpdcache_pkg::*; + // Parameters + // {{{ +#( + parameter hpdcache_cfg_t HPDcacheCfg = '0, + parameter type hpdcache_req_data_t = logic, + parameter type hpdcache_req_user_t = logic, + parameter type hpdcache_req_be_t = logic, + parameter type cbuf_id_t = logic +) + // }}} + + // Ports + // {{{ +( + input logic clk_i, + input logic rst_ni, + input logic alloc_i, + input hpdcache_req_data_t alloc_wdata_i, + input hpdcache_req_user_t alloc_wuser_i, + input hpdcache_req_be_t alloc_be_i, + output cbuf_id_t alloc_id_o, + output logic alloc_full_o, + input logic ack_i, + input cbuf_id_t ack_id_i, + output hpdcache_req_data_t ack_wdata_o, + output hpdcache_req_user_t ack_wuser_o, + output hpdcache_req_be_t ack_be_o +); + // }}} + + typedef struct packed { + hpdcache_req_data_t wdata; + hpdcache_req_user_t wuser; + hpdcache_req_be_t be; + } cbuf_entry_t; + + cbuf_entry_t [HPDcacheCfg.u.cbufEntries-1:0] entries_q, entries_d; + logic [HPDcacheCfg.u.cbufEntries-1:0] valid_q, valid_d; + + assign alloc_full_o = &valid_q; + + assign ack_wdata_o = entries_q[ack_id_i].wdata; + assign ack_be_o = entries_q[ack_id_i].be; + assign ack_wuser_o = HPDcacheCfg.u.userEn ? entries_q[ack_id_i].wuser : '0; + + always_comb begin + valid_d = valid_q; + entries_d = entries_q; + + if (ack_i) begin + valid_d[ack_id_i] = 1'b0; + end + + if (alloc_i) begin + valid_d [alloc_id_o] = 1'b1; + entries_d[alloc_id_o].wdata = alloc_wdata_i; + entries_d[alloc_id_o].be = alloc_be_i; + if (HPDcacheCfg.u.userEn) entries_d[alloc_id_o].wuser = alloc_wuser_i; + end + end + + hpdcache_prio_bin_encoder #( + .N (HPDcacheCfg.u.cbufEntries) + ) cbuf_prio_bin_encoder_i ( + .val_i (~valid_q), + .val_o (alloc_id_o) + ); + + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + entries_q <= '0; + valid_q <= '0; + end else begin + entries_q <= entries_d; + valid_q <= valid_d; + end + end + +endmodule diff --git a/hw/vendor/hpdcache/rtl/src/hpdcache_cmo.sv b/hw/vendor/hpdcache/rtl/src/hpdcache_cmo.sv new file mode 100644 index 00000000..4ee1d797 --- /dev/null +++ b/hw/vendor/hpdcache/rtl/src/hpdcache_cmo.sv @@ -0,0 +1,671 @@ +/* + * Copyright 2023 CEA* + * *Commissariat a l'Energie Atomique et aux Energies Alternatives (CEA) + * Copyright 2025 Inria, Universite Grenoble-Alpes, TIMA + * + * SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 + * + * Licensed under the Solderpad Hardware License v 2.1 (the “License”); you + * may not use this file except in compliance with the License, or, at your + * option, the Apache License version 2.0. You may obtain a copy of the + * License at + * + * https://solderpad.org/licenses/SHL-2.1/ + * + * Unless required by applicable law or agreed to in writing, any work + * distributed under the License is distributed on an “AS IS” BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +/* + * Authors : Cesar Fuguet + * Creation Date : July, 2021 + * Description : HPDcache Cache-Management-Operation Handler + * History : + */ +module hpdcache_cmo +import hpdcache_pkg::*; +// Parameters +// {{{ +#( + parameter hpdcache_cfg_t HPDcacheCfg = '0, + + parameter type hpdcache_nline_t = logic, + parameter type hpdcache_tag_t = logic, + parameter type hpdcache_set_t = logic, + parameter type hpdcache_data_word_t = logic, + parameter type hpdcache_way_vector_t = logic, + + parameter type hpdcache_rsp_t = logic, + parameter type hpdcache_req_addr_t = logic, + parameter type hpdcache_req_tid_t = logic, + parameter type hpdcache_req_sid_t = logic, + parameter type hpdcache_req_data_t = logic +) +// }}} + +// Ports +// {{{ +( + input logic clk_i, + input logic rst_ni, + + // Global control signals + // {{{ + input logic wbuf_empty_i, + input logic mshr_empty_i, + input logic rtab_empty_i, + input logic ctrl_empty_i, + // }}} + + // Request interface + // {{{ + input logic req_valid_i, + output logic req_ready_o, + input hpdcache_cmoh_op_t req_op_i, + input hpdcache_req_addr_t req_addr_i, + input hpdcache_req_data_t req_wdata_i/*unused*/, + input hpdcache_req_sid_t req_sid_i, + input hpdcache_req_tid_t req_tid_i, + input logic req_need_rsp_i, + output logic req_wait_o, + // }}} + + // Dirty/Valid tracking interface + // {{{ + input logic dirty_set_en_i, + input hpdcache_set_t dirty_min_set_i, + input hpdcache_set_t dirty_max_set_i, + input logic valid_set_en_i, + input hpdcache_set_t valid_min_set_i, + input hpdcache_set_t valid_max_set_i, + output logic flush_all_o, + output logic inval_all_o, + // }}} + + // Core response interface + // {{{ + input logic core_rsp_ready_i, + output logic core_rsp_valid_o, + output hpdcache_rsp_t core_rsp_o, + // }}} + + // Write Buffer Interface + // {{{ + output logic wbuf_flush_all_o, + // }}} + + // Cache Directory Interface + // {{{ + output logic dir_check_nline_o, + output hpdcache_set_t dir_check_nline_set_o, + output hpdcache_tag_t dir_check_nline_tag_o, + input hpdcache_way_vector_t dir_check_nline_hit_way_i, + input logic dir_check_nline_wback_i, + input logic dir_check_nline_dirty_i, + + output logic dir_check_entry_o, + output hpdcache_set_t dir_check_entry_set_o, + output hpdcache_way_vector_t dir_check_entry_way_o, + input logic dir_check_entry_valid_i, + input logic dir_check_entry_wback_i, + input logic dir_check_entry_dirty_i, + input hpdcache_tag_t dir_check_entry_tag_i, + + output logic dir_updt_o, + output hpdcache_set_t dir_updt_set_o, + output hpdcache_way_vector_t dir_updt_way_o, + output logic dir_updt_valid_o, + output logic dir_updt_wback_o, + output logic dir_updt_dirty_o, + output logic dir_updt_fetch_o, + output hpdcache_tag_t dir_updt_tag_o, + // }}} + + // Flush Controller Interface + // {{{ + input logic flush_empty_i, + output logic flush_alloc_o, + input logic flush_alloc_ready_i, + output hpdcache_nline_t flush_alloc_nline_o, + output hpdcache_way_vector_t flush_alloc_way_o + // }}} +); +// }}} + +// Definition of constants and types +// {{{ + typedef enum { + CMOH_IDLE, + CMOH_FENCE_WAIT_WBUF_RTAB_EMPTY, + CMOH_WAIT_MSHR_RTAB_EMPTY, + CMOH_INVAL_CHECK_NLINE, + CMOH_INVAL_SET, + CMOH_FLUSH_ALL_FIRST, + CMOH_FLUSH_ALL_NEXT, + CMOH_FLUSH_ALL_LAST, + CMOH_FLUSH_NLINE_FIRST, + CMOH_FLUSH_NLINE_NEXT + } hpdcache_cmoh_fsm_t; +// }}} + +// Internal signals and registers +// {{{ + hpdcache_cmoh_fsm_t cmoh_fsm_q, cmoh_fsm_d; + hpdcache_cmoh_op_t cmoh_op_q, cmoh_op_d; + hpdcache_req_addr_t cmoh_addr_q, cmoh_addr_d; + hpdcache_way_vector_t cmoh_way_q, cmoh_way_d; + hpdcache_set_t cmoh_set_q, cmoh_set_d; + + logic cmoh_flush_req_valid_q, cmoh_flush_req_valid_d; + hpdcache_set_t cmoh_flush_req_set_q, cmoh_flush_req_set_d; + hpdcache_way_vector_t cmoh_flush_req_way_q, cmoh_flush_req_way_d; + logic cmoh_flush_req_inval_q, cmoh_flush_req_inval_d; + + logic cmoh_dir_check_nline_hit; + hpdcache_nline_t cmoh_nline; + hpdcache_set_t cmoh_set; + hpdcache_tag_t cmoh_tag; + logic cmoh_flush_req_w; + logic cmoh_flush_req_wok; + hpdcache_set_t cmoh_flush_req_set; + hpdcache_tag_t cmoh_flush_req_tag; + hpdcache_way_vector_t cmoh_flush_req_way; + + logic core_rsp_w, core_rsp_r, core_rsp_rok; + hpdcache_rsp_t core_rsp; + logic core_rsp_send_q, core_rsp_send_d; + + logic cmoh_set_incr, cmoh_inval_set_reset, cmoh_flush_set_reset, cmoh_set_last; + logic cmoh_way_incr, cmoh_way_reset, cmoh_way_last; +// }}} + +// CMO core response buffer +// {{{ + hpdcache_sync_buffer #( + .FEEDTHROUGH (1'b0), + .data_t (hpdcache_rsp_t) + ) cmoh_core_rsp_buffer_i( + .clk_i, + .rst_ni, + .w_i (core_rsp_w), + .wok_o (/*unused*/), + .wdata_i (core_rsp), + .r_i (core_rsp_r), + .rok_o (core_rsp_rok), + .rdata_o (core_rsp_o) + ); + + assign core_rsp_r = core_rsp_send_q & core_rsp_ready_i; + assign core_rsp_valid_o = core_rsp_rok & core_rsp_send_q; +// }}} + +// CMO request handler FSM +// {{{ + assign cmoh_nline = cmoh_addr_q[HPDcacheCfg.clOffsetWidth +: HPDcacheCfg.nlineWidth]; + assign cmoh_set = cmoh_nline[0 +: HPDcacheCfg.setWidth]; + assign cmoh_tag = cmoh_nline[HPDcacheCfg.setWidth +: HPDcacheCfg.tagWidth]; + + assign req_wait_o = (cmoh_fsm_q == CMOH_FENCE_WAIT_WBUF_RTAB_EMPTY) | + (cmoh_fsm_q == CMOH_WAIT_MSHR_RTAB_EMPTY); + + assign cmoh_dir_check_nline_hit = |dir_check_nline_hit_way_i; + + assign core_rsp = '{ + ruser: '0, + rdata: '0, + sid: req_sid_i, + tid: req_tid_i, + error: 1'b0, + aborted: 1'b0 + }; + + always_comb + begin : cmoh_fsm_comb + cmoh_fsm_d = cmoh_fsm_q; + + cmoh_op_d = cmoh_op_q; + cmoh_addr_d = cmoh_addr_q; + + cmoh_flush_req_valid_d = cmoh_flush_req_valid_q; + cmoh_flush_req_set_d = cmoh_flush_req_set_q; + cmoh_flush_req_way_d = cmoh_flush_req_way_q; + cmoh_flush_req_inval_d = cmoh_flush_req_inval_q; + + cmoh_set_incr = 1'b0; + cmoh_inval_set_reset = 1'b0; + cmoh_flush_set_reset = 1'b0; + + cmoh_way_incr = 1'b0; + cmoh_way_reset = 1'b0; + + flush_all_o = 1'b0; + inval_all_o = 1'b0; + + dir_check_nline_o = 1'b0; + dir_check_nline_set_o = cmoh_set; + dir_check_nline_tag_o = cmoh_tag; + dir_check_entry_o = 1'b0; + dir_check_entry_set_o = cmoh_set_q; + dir_check_entry_way_o = cmoh_way_q; + + dir_updt_o = 1'b0; + dir_updt_set_o = '0; + dir_updt_way_o = '0; + dir_updt_valid_o = 1'b0; + dir_updt_wback_o = 1'b0; + dir_updt_dirty_o = 1'b0; + dir_updt_fetch_o = 1'b0; + dir_updt_tag_o = '0; + + wbuf_flush_all_o = 1'b0; + + cmoh_flush_req_set = '0; + cmoh_flush_req_way = '0; + cmoh_flush_req_tag = '0; + + core_rsp_w = 1'b0; + core_rsp_send_d = core_rsp_send_q; + + req_ready_o = 1'b0; + + cmoh_fsm_d = cmoh_fsm_q; + + unique case (cmoh_fsm_q) + CMOH_IDLE: begin + req_ready_o = ~core_rsp_rok | core_rsp_r; + + if (core_rsp_r) begin + core_rsp_send_d = 1'b0; + end + + if (req_valid_i && req_ready_o) begin + core_rsp_w = req_need_rsp_i; + + unique case (1'b1) + req_op_i.is_fence: begin + // request to the write buffer to send all open entries + wbuf_flush_all_o = rtab_empty_i; + + // then wait for the write buffer to be empty + if (!rtab_empty_i || !wbuf_empty_i) begin + cmoh_fsm_d = CMOH_FENCE_WAIT_WBUF_RTAB_EMPTY; + end else begin + core_rsp_send_d = req_need_rsp_i; + end + end + + req_op_i.is_inval_by_nline, + req_op_i.is_inval_all, + req_op_i.is_flush_by_nline, + req_op_i.is_flush_all, + req_op_i.is_flush_inval_by_nline, + req_op_i.is_flush_inval_all: begin + cmoh_op_d = req_op_i; + cmoh_addr_d = req_addr_i; + cmoh_way_reset = 1'b1; + cmoh_fsm_d = CMOH_WAIT_MSHR_RTAB_EMPTY; + end + endcase + end + end + CMOH_FENCE_WAIT_WBUF_RTAB_EMPTY: begin + wbuf_flush_all_o = rtab_empty_i; + if (wbuf_empty_i && rtab_empty_i) begin + core_rsp_send_d = core_rsp_rok; + cmoh_fsm_d = CMOH_IDLE; + end + end + CMOH_WAIT_MSHR_RTAB_EMPTY: begin + if (mshr_empty_i && rtab_empty_i && ctrl_empty_i) begin + unique if (cmoh_op_q.is_inval_by_nline) begin + if (valid_set_en_i) begin + cmoh_fsm_d = CMOH_INVAL_CHECK_NLINE; + end else begin + core_rsp_send_d = core_rsp_rok; + cmoh_fsm_d = CMOH_IDLE; + end + end else if (cmoh_op_q.is_inval_all) begin + if (valid_set_en_i) begin + cmoh_inval_set_reset = 1'b1; + cmoh_fsm_d = CMOH_INVAL_SET; + end else begin + core_rsp_send_d = core_rsp_rok; + cmoh_fsm_d = CMOH_IDLE; + end + end else if (cmoh_op_q.is_flush_by_nline) begin + if (dirty_set_en_i) begin + cmoh_flush_req_inval_d = 1'b0; + cmoh_fsm_d = CMOH_FLUSH_NLINE_FIRST; + end else begin + core_rsp_send_d = core_rsp_rok; + cmoh_fsm_d = CMOH_IDLE; + end + end else if (cmoh_op_q.is_flush_all) begin + if (dirty_set_en_i) begin + cmoh_flush_set_reset = 1'b1; + cmoh_flush_req_inval_d = 1'b0; + cmoh_fsm_d = CMOH_FLUSH_ALL_FIRST; + end else begin + core_rsp_send_d = core_rsp_rok; + cmoh_fsm_d = CMOH_IDLE; + end + end else if (cmoh_op_q.is_flush_inval_by_nline) begin + if (valid_set_en_i) begin + cmoh_flush_req_inval_d = 1'b1; + cmoh_fsm_d = CMOH_FLUSH_NLINE_FIRST; + end else begin + core_rsp_send_d = core_rsp_rok; + cmoh_fsm_d = CMOH_IDLE; + end + end else if (cmoh_op_q.is_flush_inval_all) begin + if (valid_set_en_i) begin + cmoh_inval_set_reset = 1'b1; + cmoh_flush_req_inval_d = 1'b1; + cmoh_fsm_d = CMOH_FLUSH_ALL_FIRST; + end else begin + core_rsp_send_d = core_rsp_rok; + cmoh_fsm_d = CMOH_IDLE; + end + end + end + end + CMOH_INVAL_CHECK_NLINE: begin + dir_check_nline_o = 1'b1; + cmoh_fsm_d = CMOH_INVAL_SET; + end + CMOH_INVAL_SET: begin + unique case (1'b1) + // The CMO requests the invalidation of a given cacheline (or flush with + // invalidation when the cache does not support WB policy) + cmoh_op_q.is_inval_by_nline, + cmoh_op_q.is_flush_inval_by_nline: begin + /* FIXME this adds a DIR to DIR timing path. We should probably delay the + * invalidation of one cycle to ease the timing closure */ + dir_updt_o = cmoh_dir_check_nline_hit; + dir_updt_set_o = cmoh_set; + dir_updt_way_o = dir_check_nline_hit_way_i; + dir_updt_valid_o = 1'b0; + dir_updt_wback_o = 1'b0; + dir_updt_dirty_o = 1'b0; + dir_updt_fetch_o = 1'b0; + dir_updt_tag_o = '0; + + core_rsp_send_d = core_rsp_rok; + cmoh_fsm_d = CMOH_IDLE; + end + + // The CMO requests a full invalidation (or flush with invalidation when the + // cache does not support WB policy) + cmoh_op_q.is_inval_all, + cmoh_op_q.is_flush_inval_all: + begin + dir_updt_o = 1'b1; + dir_updt_set_o = cmoh_set_q; + dir_updt_way_o = {HPDcacheCfg.u.ways{1'b1}}; + dir_updt_valid_o = 1'b0; + dir_updt_wback_o = 1'b0; + dir_updt_dirty_o = 1'b0; + dir_updt_fetch_o = 1'b0; + dir_updt_tag_o = '0; + cmoh_set_incr = 1'b1; + if (cmoh_set_last) begin + inval_all_o = 1'b1; + core_rsp_send_d = core_rsp_rok; + cmoh_fsm_d = CMOH_IDLE; + end + end + endcase + end + CMOH_FLUSH_ALL_FIRST: begin + if (HPDcacheCfg.u.wbEn) begin + if (cmoh_flush_req_wok) begin + dir_check_entry_o = 1'b1; + cmoh_set_incr = cmoh_way_last; + cmoh_way_incr = 1'b1; + cmoh_flush_req_valid_d = 1'b1; + cmoh_flush_req_set_d = cmoh_set_q; + cmoh_flush_req_way_d = cmoh_way_q; + cmoh_fsm_d = CMOH_FLUSH_ALL_NEXT; + end + end else if (cmoh_flush_req_inval_q) begin + cmoh_fsm_d = CMOH_INVAL_SET; + end else begin + core_rsp_send_d = core_rsp_rok; + cmoh_fsm_d = CMOH_IDLE; + end + end + CMOH_FLUSH_ALL_NEXT: begin + if (cmoh_flush_req_valid_q) begin + dir_updt_o = dir_check_entry_valid_i & + (dir_check_entry_dirty_i | cmoh_flush_req_inval_q); + + dir_updt_set_o = cmoh_flush_req_set_q; + dir_updt_way_o = cmoh_flush_req_way_q; + dir_updt_valid_o = ~cmoh_flush_req_inval_q; + dir_updt_wback_o = ~cmoh_flush_req_inval_q & dir_check_entry_wback_i; + dir_updt_dirty_o = 1'b0; + dir_updt_fetch_o = 1'b0; + dir_updt_tag_o = dir_check_entry_tag_i; + + cmoh_flush_req_set = cmoh_flush_req_set_q; + cmoh_flush_req_way = cmoh_flush_req_way_q; + cmoh_flush_req_tag = dir_check_entry_tag_i; + + // The CMO handler needs to dedicate one cycle to + // check the directory and one cycle to update that entry. + // This means that the CMO handler takes 2 cycles per flush request + cmoh_flush_req_valid_d = 1'b0; + end + + if (!cmoh_flush_req_valid_q && cmoh_flush_req_wok) begin + dir_check_entry_o = 1'b1; + cmoh_set_incr = cmoh_way_last; + cmoh_way_incr = 1'b1; + cmoh_flush_req_valid_d = 1'b1; + cmoh_flush_req_set_d = cmoh_set_q; + cmoh_flush_req_way_d = cmoh_way_q; + if (cmoh_set_last && cmoh_way_last) begin + cmoh_fsm_d = CMOH_FLUSH_ALL_LAST; + end + end + end + CMOH_FLUSH_ALL_LAST: begin + cmoh_flush_req_valid_d = 1'b0; + if (cmoh_flush_req_valid_q) begin + dir_updt_o = dir_check_entry_valid_i & + (dir_check_entry_dirty_i | cmoh_flush_req_inval_q); + + dir_updt_set_o = cmoh_flush_req_set_q; + dir_updt_way_o = cmoh_flush_req_way_q; + dir_updt_valid_o = ~cmoh_flush_req_inval_q; + dir_updt_wback_o = ~cmoh_flush_req_inval_q & dir_check_entry_wback_i; + dir_updt_dirty_o = 1'b0; + dir_updt_fetch_o = 1'b0; + dir_updt_tag_o = dir_check_entry_tag_i; + cmoh_flush_req_set = cmoh_flush_req_set_q; + cmoh_flush_req_way = cmoh_flush_req_way_q; + cmoh_flush_req_tag = dir_check_entry_tag_i; + end + + // Make sure that all requests have been processed + if (flush_empty_i && !flush_alloc_o) begin + flush_all_o = 1'b1; + inval_all_o = cmoh_flush_req_inval_q; + core_rsp_send_d = core_rsp_rok; + cmoh_fsm_d = CMOH_IDLE; + end + end + CMOH_FLUSH_NLINE_FIRST: begin + if (HPDcacheCfg.u.wbEn) begin + if (cmoh_flush_req_wok) begin + dir_check_nline_o = 1'b1; + cmoh_flush_req_valid_d = 1'b1; + cmoh_fsm_d = CMOH_FLUSH_NLINE_NEXT; + end + end else if (cmoh_flush_req_inval_q) begin + cmoh_fsm_d = CMOH_INVAL_CHECK_NLINE; + end else begin + core_rsp_send_d = core_rsp_rok; + cmoh_fsm_d = CMOH_IDLE; + end + end + CMOH_FLUSH_NLINE_NEXT: begin + cmoh_flush_req_valid_d = 1'b0; + if (cmoh_flush_req_valid_q) begin + /* FIXME this adds a DIR to DIR timing path. We should probably delay the + * invalidation of one cycle to ease the timing closure */ + dir_updt_o = cmoh_dir_check_nline_hit; + dir_updt_set_o = cmoh_set; + dir_updt_way_o = dir_check_nline_hit_way_i; + dir_updt_valid_o = ~cmoh_flush_req_inval_q; + dir_updt_wback_o = ~cmoh_flush_req_inval_q & dir_check_nline_wback_i; + dir_updt_dirty_o = 1'b0; + dir_updt_fetch_o = 1'b0; + dir_updt_tag_o = cmoh_tag; + cmoh_flush_req_set = cmoh_set; + cmoh_flush_req_tag = cmoh_tag; + cmoh_flush_req_way = dir_check_nline_hit_way_i; + end + + // Make sure that all requests have been processed + if (flush_empty_i && !flush_alloc_o) begin + core_rsp_send_d = core_rsp_rok; + cmoh_fsm_d = CMOH_IDLE; + end + end + endcase + end + + assign cmoh_set_last = cmoh_op_q.is_flush_all ? + (cmoh_set_q == dirty_max_set_i) : + (cmoh_set_q == valid_max_set_i); + + always_comb + begin : set_incr_comb + cmoh_set_d = cmoh_set_q; + if (cmoh_inval_set_reset) begin + cmoh_set_d = valid_min_set_i; + end else if (cmoh_flush_set_reset) begin + cmoh_set_d = dirty_min_set_i; + end else if (cmoh_set_incr) begin + cmoh_set_d = cmoh_set_last ? '0 : cmoh_set_q + 1; + end + end + + assign cmoh_way_last = cmoh_way_q[HPDcacheCfg.u.ways - 1]; + + always_comb + begin : way_incr_comb + cmoh_way_d = cmoh_way_q; + if (cmoh_way_reset) begin + cmoh_way_d = hpdcache_way_vector_t'(1); + end else if (cmoh_way_incr) begin + cmoh_way_d = cmoh_way_last ? + hpdcache_way_vector_t'(1) : {cmoh_way_q[0 +: HPDcacheCfg.u.ways-1], 1'b0}; + end + end +// }}} + +// CMO request handler set state +// {{{ + always_ff @(posedge clk_i or negedge rst_ni) + begin + if (!rst_ni) begin + core_rsp_send_q <= 1'b0; + cmoh_flush_req_valid_q <= 1'b0; + cmoh_fsm_q <= CMOH_IDLE; + end else begin + core_rsp_send_q <= core_rsp_send_d; + cmoh_flush_req_valid_q <= cmoh_flush_req_valid_d; + cmoh_fsm_q <= cmoh_fsm_d; + end + end + + always_ff @(posedge clk_i) + begin + cmoh_op_q <= cmoh_op_d; + cmoh_addr_q <= cmoh_addr_d; + cmoh_way_q <= cmoh_way_d; + cmoh_set_q <= cmoh_set_d; + cmoh_flush_req_set_q <= cmoh_flush_req_set_d; + cmoh_flush_req_way_q <= cmoh_flush_req_way_d; + cmoh_flush_req_inval_q <= cmoh_flush_req_inval_d; + end +// }}} + +// CMO internal components +// {{{ + typedef struct packed { + hpdcache_nline_t nline; + hpdcache_way_vector_t way; + } cmoh_flush_req_t; + + if (HPDcacheCfg.u.wbEn) begin : gen_cmo_flush_fifo + cmoh_flush_req_t cmoh_flush_req_wdata, cmoh_flush_req_rdata; + + always_comb + begin : cmoh_flush_req_w_comb + cmoh_flush_req_w = 1'b0; + if (cmoh_flush_req_valid_q) begin + unique case (cmoh_fsm_q) + CMOH_FLUSH_ALL_NEXT, CMOH_FLUSH_ALL_LAST: + cmoh_flush_req_w = dir_check_entry_valid_i & dir_check_entry_dirty_i; + CMOH_FLUSH_NLINE_NEXT: + cmoh_flush_req_w = cmoh_dir_check_nline_hit & dir_check_nline_dirty_i; + endcase + end + end + + assign cmoh_flush_req_wdata = '{ + nline: {cmoh_flush_req_tag, cmoh_flush_req_set}, + way : cmoh_flush_req_way + }; + + hpdcache_fifo_reg #( + .FIFO_DEPTH (3), + .FEEDTHROUGH (1'b1), + .fifo_data_t (cmoh_flush_req_t) + ) flush_req_fifo_i( + .clk_i, + .rst_ni, + .w_i (cmoh_flush_req_w), + .wok_o (cmoh_flush_req_wok), + .wdata_i (cmoh_flush_req_wdata), + .r_i (flush_alloc_ready_i), + .rok_o (flush_alloc_o), + .rdata_o (cmoh_flush_req_rdata) + ); + + assign flush_alloc_nline_o = cmoh_flush_req_rdata.nline; + assign flush_alloc_way_o = cmoh_flush_req_rdata.way; + end else begin : gen_cmo_no_flush_fifo + assign cmoh_flush_req_w = 1'b0; + assign cmoh_flush_req_wok = 1'b1; + assign flush_alloc_o = 1'b0; + assign flush_alloc_nline_o = '0; + assign flush_alloc_way_o = '0; + end +// }}} + +// Assertions +// {{{ +`ifndef HPDCACHE_ASSERT_OFF + assert property (@(posedge clk_i) disable iff (rst_ni !== 1'b1) + req_valid_i -> $onehot({req_op_i.is_fence, + req_op_i.is_inval_by_nline, + req_op_i.is_inval_all, + req_op_i.is_flush_by_nline, + req_op_i.is_flush_all, + req_op_i.is_flush_inval_by_nline, + req_op_i.is_flush_inval_all})) else + $error("cmo_handler: invalid request"); + + assert property (@(posedge clk_i) disable iff (rst_ni !== 1'b1) + req_valid_i -> (cmoh_fsm_q == CMOH_IDLE)) else + $error("cmo_handler: new request received while busy"); +`endif +// }}} + +endmodule diff --git a/hw/vendor/hpdcache/rtl/src/hpdcache_core_arbiter.sv b/hw/vendor/hpdcache/rtl/src/hpdcache_core_arbiter.sv new file mode 100644 index 00000000..8eabcae5 --- /dev/null +++ b/hw/vendor/hpdcache/rtl/src/hpdcache_core_arbiter.sv @@ -0,0 +1,175 @@ +/* + * Copyright 2023 CEA* + * *Commissariat a l'Energie Atomique et aux Energies Alternatives (CEA) + * Copyright 2025 Inria, Universite Grenoble-Alpes, TIMA + * + * SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 + * + * Licensed under the Solderpad Hardware License v 2.1 (the “License”); you + * may not use this file except in compliance with the License, or, at your + * option, the Apache License version 2.0. You may obtain a copy of the + * License at + * + * https://solderpad.org/licenses/SHL-2.1/ + * + * Unless required by applicable law or agreed to in writing, any work + * distributed under the License is distributed on an “AS IS” BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +/* + * Authors : Cesar Fuguet + * Creation Date : September, 2023 + * Description : HPDcache request arbiter + * History : + */ +module hpdcache_core_arbiter +import hpdcache_pkg::*; + // Parameters + // {{{ +#( + parameter hpdcache_cfg_t HPDcacheCfg = '0, + parameter type hpdcache_tag_t = logic, + parameter type hpdcache_req_t = logic, + parameter type hpdcache_rsp_t = logic +) + // }}} + + // Ports + // {{{ +( + // Clock and reset signals + input logic clk_i, + input logic rst_ni, + + // Core request interface + // 1st cycle + input logic core_req_valid_i [HPDcacheCfg.u.nRequesters], + output logic core_req_ready_o [HPDcacheCfg.u.nRequesters], + input hpdcache_req_t core_req_i [HPDcacheCfg.u.nRequesters], + // 2nd cycle + input logic core_req_abort_i [HPDcacheCfg.u.nRequesters], + input hpdcache_tag_t core_req_tag_i [HPDcacheCfg.u.nRequesters], + input hpdcache_pma_t core_req_pma_i [HPDcacheCfg.u.nRequesters], + + // Core response interface + input logic core_rsp_valid_i, + input hpdcache_rsp_t core_rsp_i, + output logic core_rsp_valid_o [HPDcacheCfg.u.nRequesters], + output hpdcache_rsp_t core_rsp_o [HPDcacheCfg.u.nRequesters], + + // Granted request + output logic arb_req_valid_o, + input logic arb_req_ready_i, + output hpdcache_req_t arb_req_o, + output logic arb_abort_o, + output hpdcache_tag_t arb_tag_o, + output hpdcache_pma_t arb_pma_o +); + + // }}} + + // Declaration of internal signals + // {{{ + logic [HPDcacheCfg.u.nRequesters-1:0] core_req_valid; + hpdcache_req_t [HPDcacheCfg.u.nRequesters-1:0] core_req; + logic [HPDcacheCfg.u.nRequesters-1:0] core_req_abort; + hpdcache_tag_t [HPDcacheCfg.u.nRequesters-1:0] core_req_tag; + hpdcache_pma_t [HPDcacheCfg.u.nRequesters-1:0] core_req_pma; + + logic [HPDcacheCfg.u.nRequesters-1:0] arb_req_gnt_q, arb_req_gnt_d; + // }}} + + // Requesters arbiter + // {{{ + // Pack request ports + genvar gen_i; + + generate + for (gen_i = 0; gen_i < int'(HPDcacheCfg.u.nRequesters); gen_i++) begin : gen_core_req + assign core_req_ready_o[gen_i] = arb_req_gnt_d[gen_i] & arb_req_ready_i, + core_req_valid[gen_i] = core_req_valid_i[gen_i], + core_req[gen_i] = core_req_i[gen_i]; + + assign core_req_abort[gen_i] = core_req_abort_i[gen_i], + core_req_tag[gen_i] = core_req_tag_i[gen_i], + core_req_pma[gen_i] = core_req_pma_i[gen_i]; + end + endgenerate + + // Arbiter + hpdcache_fxarb #(.N(HPDcacheCfg.u.nRequesters)) req_arbiter_i + ( + .clk_i, + .rst_ni, + .req_i (core_req_valid), + .gnt_o (arb_req_gnt_d), + .ready_i (arb_req_ready_i) + ); + + // Request multiplexor + hpdcache_mux #( + .NINPUT (HPDcacheCfg.u.nRequesters), + .DATA_WIDTH ($bits(hpdcache_req_t)), + .ONE_HOT_SEL (1'b1) + ) core_req_mux_i ( + .data_i (core_req), + .sel_i (arb_req_gnt_d), + .data_o (arb_req_o) + ); + + // Request abort multiplexor + hpdcache_mux #( + .NINPUT (HPDcacheCfg.u.nRequesters), + .DATA_WIDTH (1), + .ONE_HOT_SEL (1'b1) + ) core_req_abort_mux_i ( + .data_i (core_req_abort), + .sel_i (arb_req_gnt_q), + .data_o (arb_abort_o) + ); + + // Tag Multiplexor + hpdcache_mux #( + .NINPUT (HPDcacheCfg.u.nRequesters), + .DATA_WIDTH ($bits(hpdcache_tag_t)), + .ONE_HOT_SEL (1'b1) + ) core_req_tag_mux_i ( + .data_i (core_req_tag), + .sel_i (arb_req_gnt_q), + .data_o (arb_tag_o) + ); + + // PMA Multiplexor + hpdcache_mux #( + .NINPUT (HPDcacheCfg.u.nRequesters), + .DATA_WIDTH ($bits(hpdcache_pma_t)), + .ONE_HOT_SEL (1'b1) + ) core_req_pma_mux_i ( + .data_i (core_req_pma), + .sel_i (arb_req_gnt_q), + .data_o (arb_pma_o) + ); + + // Save the grant signal for the tag in the next cycle + always_ff @(posedge clk_i or negedge rst_ni) + begin : arb_req_gnt_ff + if (!rst_ni) arb_req_gnt_q <= '0; + else arb_req_gnt_q <= arb_req_gnt_d; + end + + assign arb_req_valid_o = |arb_req_gnt_d; + // }}} + + // Response demultiplexor + // {{{ + always_comb + begin : resp_demux + for (int unsigned i = 0; i < HPDcacheCfg.u.nRequesters; i++) begin + core_rsp_valid_o[i] = core_rsp_valid_i && (i == hpdcache_uint32'(core_rsp_i.sid)); + core_rsp_o[i] = core_rsp_i; + end + end + // }}} +endmodule diff --git a/hw/vendor/hpdcache/rtl/src/hpdcache_ctrl.sv b/hw/vendor/hpdcache/rtl/src/hpdcache_ctrl.sv new file mode 100644 index 00000000..ea31ea0f --- /dev/null +++ b/hw/vendor/hpdcache/rtl/src/hpdcache_ctrl.sv @@ -0,0 +1,1373 @@ +/* + * Copyright 2023-2024 CEA* + * *Commissariat a l'Energie Atomique et aux Energies Alternatives (CEA) + * Copyright 2025 Inria, Universite Grenoble-Alpes, TIMA + * + * SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 + * + * Licensed under the Solderpad Hardware License v 2.1 (the “License”); you + * may not use this file except in compliance with the License, or, at your + * option, the Apache License version 2.0. You may obtain a copy of the + * License at + * + * https://solderpad.org/licenses/SHL-2.1/ + * + * Unless required by applicable law or agreed to in writing, any work + * distributed under the License is distributed on an “AS IS” BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +/* + * Authors : Cesar Fuguet + * Creation Date : April, 2021 + * Description : HPDcache controller + * History : + */ +module hpdcache_ctrl + // Package imports + // {{{ +import hpdcache_pkg::*; + // }}} + + // Parameters + // {{{ +#( + parameter hpdcache_cfg_t HPDcacheCfg = '0, + + parameter type hpdcache_nline_t = logic, + parameter type hpdcache_tag_t = logic, + parameter type hpdcache_set_t = logic, + parameter type hpdcache_word_t = logic, + parameter type hpdcache_data_word_t = logic, + parameter type hpdcache_data_be_t = logic, + parameter type hpdcache_dir_entry_t = logic, + parameter type hpdcache_way_vector_t = logic, + parameter type hpdcache_way_t = logic, + parameter type hpdcache_cl_user_t = logic, + + parameter type wbuf_addr_t = logic, + parameter type wbuf_user_t = logic, + parameter type wbuf_data_t = logic, + parameter type wbuf_be_t = logic, + + parameter type hpdcache_access_data_t = logic, + parameter type hpdcache_access_user_t = logic, + parameter type hpdcache_access_be_t = logic, + + parameter type hpdcache_req_addr_t = logic, + parameter type hpdcache_req_offset_t = logic, + parameter type hpdcache_req_tid_t = logic, + parameter type hpdcache_req_sid_t = logic, + parameter type hpdcache_req_user_t = logic, + parameter type hpdcache_req_data_t = logic, + parameter type hpdcache_req_be_t = logic, + + parameter type hpdcache_req_t = logic, + parameter type hpdcache_rsp_t = logic +) + // }}} + + // Ports + // {{{ +( + input logic clk_i, + input logic rst_ni, + + // Core request interface + input logic core_req_valid_i, + output logic core_req_ready_o, + input hpdcache_req_t core_req_i, + input logic core_req_abort_i, + input hpdcache_tag_t core_req_tag_i, + input hpdcache_pma_t core_req_pma_i, + + // Core response interface + output logic core_rsp_valid_o, + output hpdcache_rsp_t core_rsp_o, + + // Force the write buffer to send all pending writes + input logic wbuf_flush_i, + + // Global control signals + output logic cachedir_hit_o, + + // Miss handler interface + output logic st0_mshr_check_o, + output hpdcache_req_offset_t st0_mshr_check_offset_o, + output hpdcache_nline_t st1_mshr_check_nline_o, + input logic st1_mshr_hit_i, + input logic st1_mshr_alloc_ready_i, + input logic st1_mshr_alloc_full_i, + input logic st1_mshr_alloc_cbuf_full_i, + output logic st2_mshr_alloc_o, + output logic st2_mshr_alloc_cs_o, + output hpdcache_nline_t st2_mshr_alloc_nline_o, + output hpdcache_req_tid_t st2_mshr_alloc_tid_o, + output hpdcache_req_sid_t st2_mshr_alloc_sid_o, + output hpdcache_word_t st2_mshr_alloc_word_o, + output hpdcache_req_data_t st2_mshr_alloc_wdata_o, + output hpdcache_req_user_t st2_mshr_alloc_wuser_o, + output hpdcache_req_be_t st2_mshr_alloc_be_o, + output hpdcache_way_vector_t st2_mshr_alloc_victim_way_o, + output logic st2_mshr_alloc_need_rsp_o, + output logic st2_mshr_alloc_is_prefetch_o, + output logic st2_mshr_alloc_wback_o, + output logic st2_mshr_alloc_dirty_o, + + // Refill interface + input logic refill_req_valid_i, + output logic refill_req_ready_o, + input logic refill_is_error_i, + input logic refill_busy_i, + input logic refill_updt_sel_victim_i, + input hpdcache_set_t refill_set_i, + input hpdcache_way_vector_t refill_way_i, + input hpdcache_dir_entry_t refill_dir_entry_i, + input logic refill_write_dir_i, + input logic refill_write_data_i, + input hpdcache_word_t refill_word_i, + input hpdcache_access_user_t refill_user_i, + input hpdcache_access_data_t refill_data_i, + input logic refill_core_rsp_valid_i, + input hpdcache_rsp_t refill_core_rsp_i, + input hpdcache_nline_t refill_nline_i, + input logic refill_updt_rtab_i, + + // Flush interface + input logic flush_busy_i, + output hpdcache_nline_t flush_check_nline_o, + input logic flush_check_hit_i, + output logic flush_alloc_o, + input logic flush_alloc_ready_i, + output hpdcache_nline_t flush_alloc_nline_o, + output hpdcache_way_vector_t flush_alloc_way_o, + input logic flush_data_read_i, + input hpdcache_set_t flush_data_read_set_i, + input hpdcache_word_t flush_data_read_word_i, + input hpdcache_way_vector_t flush_data_read_way_i, + output hpdcache_access_data_t flush_data_read_data_o, + output hpdcache_access_user_t flush_data_read_user_o, + input logic flush_ack_i, + input hpdcache_nline_t flush_ack_nline_i, + + // Invalidate interface + input logic inval_check_dir_i, + input logic inval_write_dir_i, + input hpdcache_nline_t inval_nline_i, + output logic inval_hit_o, + + // Write buffer interface + input logic wbuf_empty_i, + output logic wbuf_flush_all_o, + output logic wbuf_write_o, + input logic wbuf_write_ready_i, + output wbuf_addr_t wbuf_write_addr_o, + output wbuf_user_t wbuf_write_user_o, + output wbuf_data_t wbuf_write_data_o, + output wbuf_be_t wbuf_write_be_o, + output logic wbuf_write_uncacheable_o, + input logic wbuf_read_hit_i, + output logic wbuf_read_flush_hit_o, + output hpdcache_req_addr_t wbuf_rtab_addr_o, + output logic wbuf_rtab_is_read_o, + input logic wbuf_rtab_hit_open_i, + input logic wbuf_rtab_hit_pend_i, + input logic wbuf_rtab_hit_sent_i, + input logic wbuf_rtab_not_ready_i, + + // Uncacheable request handler + input logic uc_busy_i, + output logic uc_lrsc_snoop_o, + output hpdcache_req_addr_t uc_lrsc_snoop_addr_o, + output hpdcache_req_size_t uc_lrsc_snoop_size_o, + output logic uc_req_valid_o, + output hpdcache_uc_op_t uc_req_op_o, + output hpdcache_req_addr_t uc_req_addr_o, + output hpdcache_req_size_t uc_req_size_o, + output hpdcache_req_data_t uc_req_data_o, + output hpdcache_req_user_t uc_req_user_o, + output hpdcache_req_be_t uc_req_be_o, + output logic uc_req_uc_o, + output hpdcache_req_sid_t uc_req_sid_o, + output hpdcache_req_tid_t uc_req_tid_o, + output logic uc_req_need_rsp_o, + input logic uc_wbuf_flush_all_i, + input logic uc_dir_amo_match_i, + input hpdcache_set_t uc_dir_amo_match_set_i, + input hpdcache_tag_t uc_dir_amo_match_tag_i, + input logic uc_dir_amo_updt_sel_victim_i, + output hpdcache_way_vector_t uc_dir_amo_hit_way_o, + input logic uc_data_amo_write_i, + input logic uc_data_amo_write_enable_i, + input hpdcache_set_t uc_data_amo_write_set_i, + input hpdcache_req_size_t uc_data_amo_write_size_i, + input hpdcache_word_t uc_data_amo_write_word_i, + input hpdcache_req_user_t uc_data_amo_write_user_i, + input hpdcache_req_data_t uc_data_amo_write_data_i, + input hpdcache_req_be_t uc_data_amo_write_be_i, + output logic uc_core_rsp_ready_o, + input logic uc_core_rsp_valid_i, + input hpdcache_rsp_t uc_core_rsp_i, + + // Cache Management Operation (CMO) + input logic cmo_busy_i, + input logic cmo_wait_i, + output logic cmo_req_valid_o, + output hpdcache_cmoh_op_t cmo_req_op_o, + output hpdcache_req_addr_t cmo_req_addr_o, + output hpdcache_req_data_t cmo_req_wdata_o, + output hpdcache_req_sid_t cmo_req_sid_o, + output hpdcache_req_tid_t cmo_req_tid_o, + output logic cmo_req_need_rsp_o, + output logic cmo_dirty_set_en_o, + output hpdcache_set_t cmo_dirty_min_set_o, + output hpdcache_set_t cmo_dirty_max_set_o, + output logic cmo_valid_set_en_o, + output hpdcache_set_t cmo_valid_min_set_o, + output hpdcache_set_t cmo_valid_max_set_o, + input logic cmo_wbuf_flush_all_i, + input logic cmo_flush_all_i, + input logic cmo_inval_all_i, + input logic cmo_dir_check_nline_i, + input hpdcache_set_t cmo_dir_check_nline_set_i, + input hpdcache_tag_t cmo_dir_check_nline_tag_i, + output hpdcache_way_vector_t cmo_dir_check_nline_hit_way_o, + output logic cmo_dir_check_nline_wback_o, + output logic cmo_dir_check_nline_dirty_o, + input logic cmo_dir_check_entry_i, + input hpdcache_set_t cmo_dir_check_entry_set_i, + input hpdcache_way_vector_t cmo_dir_check_entry_way_i, + output logic cmo_dir_check_entry_valid_o, + output logic cmo_dir_check_entry_wback_o, + output logic cmo_dir_check_entry_dirty_o, + output hpdcache_tag_t cmo_dir_check_entry_tag_o, + input logic cmo_dir_updt_i, + input hpdcache_set_t cmo_dir_updt_set_i, + input hpdcache_way_vector_t cmo_dir_updt_way_i, + input logic cmo_dir_updt_valid_i, + input logic cmo_dir_updt_wback_i, + input logic cmo_dir_updt_dirty_i, + input logic cmo_dir_updt_fetch_i, + input hpdcache_tag_t cmo_dir_updt_tag_i, + output logic cmo_core_rsp_ready_o, + input logic cmo_core_rsp_valid_i, + input hpdcache_rsp_t cmo_core_rsp_i, + + output logic rtab_empty_o, + output logic ctrl_empty_o, + + // Configuration signals + input logic cfg_enable_i, + input logic cfg_prefetch_updt_plru_i, + input logic cfg_rtab_single_entry_i, + input logic cfg_default_wb_i, + + // Performance events + output logic evt_cache_write_miss_o, + output logic evt_cache_read_miss_o, + output logic evt_uncached_req_o, + output logic evt_cmo_req_o, + output logic evt_write_req_o, + output logic evt_read_req_o, + output logic evt_prefetch_req_o, + output logic evt_req_on_hold_o, + output logic evt_rtab_rollback_o, + output logic evt_stall_refill_o, + output logic evt_stall_o +); + // }}} + + // Definition of internal registers + // {{{ + typedef logic [$clog2(HPDcacheCfg.u.rtabEntries)-1:0] rtab_ptr_t; + + typedef struct packed { + hpdcache_req_t req; + hpdcache_way_t way_fetch; + } rtab_entry_t; + // }}} + + // Definition of internal registers + // {{{ + logic st1_req_valid_q, st1_req_valid_d; + logic st1_req_is_error_q, st1_req_is_error_d; + hpdcache_req_t st1_req_q; + logic st1_req_rtab_q; + rtab_ptr_t st1_rtab_pop_try_ptr_q; + + logic st2_mshr_alloc_q, st2_mshr_alloc_d; + logic st2_mshr_alloc_is_prefetch_q; + logic st2_mshr_alloc_wback_q, st2_mshr_alloc_wback_d; + logic st2_mshr_alloc_dirty_q, st2_mshr_alloc_dirty_d; + logic st2_mshr_alloc_need_rsp_q, st2_mshr_alloc_need_rsp_d; + hpdcache_req_addr_t st2_mshr_alloc_addr_q; + hpdcache_req_sid_t st2_mshr_alloc_sid_q; + hpdcache_req_tid_t st2_mshr_alloc_tid_q; + hpdcache_req_data_t st2_mshr_alloc_wdata_q; + hpdcache_req_user_t st2_mshr_alloc_wuser_q; + hpdcache_req_be_t st2_mshr_alloc_be_q; + hpdcache_way_vector_t st2_mshr_alloc_victim_way_q; + + logic st2_flush_alloc_q, st2_flush_alloc_d; + hpdcache_nline_t st2_flush_alloc_nline_q; + hpdcache_way_vector_t st2_flush_alloc_way_q; + + logic st2_dir_updt_q, st2_dir_updt_d; + hpdcache_set_t st2_dir_updt_set_q; + hpdcache_way_vector_t st2_dir_updt_way_q; + hpdcache_tag_t st2_dir_updt_tag_q; + logic st2_dir_updt_valid_q, st2_dir_updt_valid_d; + logic st2_dir_updt_wback_q, st2_dir_updt_wback_d; + logic st2_dir_updt_dirty_q, st2_dir_updt_dirty_d; + logic st2_dir_updt_fetch_q, st2_dir_updt_fetch_d; + // }}} + + // Definition of internal signals + // {{{ + // Pipeline Stage 0 + hpdcache_req_t st0_req; + hpdcache_pma_t st0_req_pma; + logic st0_req_is_error; + logic st0_req_is_uncacheable; + logic st0_req_is_load; + logic st0_req_is_store; + logic st0_req_is_amo; + logic st0_req_is_cmo_fence; + logic st0_req_is_cmo_inval; + logic st0_req_is_cmo_prefetch; + logic st0_req_cachedir_read; + hpdcache_set_t st0_req_set; + hpdcache_word_t st0_req_word; + logic st0_rtab_pop_try_valid; + logic st0_rtab_pop_try_ready; + rtab_entry_t st0_rtab_pop_try_req; + rtab_ptr_t st0_rtab_pop_try_ptr; + logic st0_rtab_pop_try_error; + + // Pipeline Stage 1 + logic st1_rsp_valid; + logic st1_rsp_error; + logic st1_rsp_aborted; + hpdcache_req_t st1_req; + logic st1_req_abort; + logic st1_req_cachedata_write; + logic st1_req_cachedata_write_enable; + hpdcache_pma_t st1_req_pma; + hpdcache_tag_t st1_req_tag; + hpdcache_set_t st1_req_set; + hpdcache_word_t st1_req_word; + hpdcache_nline_t st1_req_nline; + hpdcache_req_addr_t st1_req_addr; + logic st1_victim_sel; + logic st1_req_updt_sel_victim; + logic st1_req_is_uncacheable; + logic st1_req_is_load; + logic st1_req_is_store; + logic st1_req_is_amo; + logic st1_req_is_amo_lr; + logic st1_req_is_amo_sc; + logic st1_req_is_amo_swap; + logic st1_req_is_amo_add; + logic st1_req_is_amo_and; + logic st1_req_is_amo_or; + logic st1_req_is_amo_xor; + logic st1_req_is_amo_max; + logic st1_req_is_amo_maxu; + logic st1_req_is_amo_min; + logic st1_req_is_amo_minu; + logic st1_req_is_cmo_inval; + logic st1_req_is_cmo_flush; + logic st1_req_is_cmo_fence; + logic st1_req_is_cmo_prefetch; + logic st1_req_wr_wt; + logic st1_req_wr_wb; + logic st1_req_wr_auto; + logic st1_dir_hit; + logic st1_dir_hit_wback; + logic st1_dir_hit_dirty; + logic st1_dir_hit_fetch; + hpdcache_way_vector_t st1_dir_hit_way; + hpdcache_way_t st1_dir_hit_way_index; + hpdcache_tag_t st1_dir_hit_tag; + logic st1_dir_victim_unavailable; + logic st1_dir_victim_valid; + logic st1_dir_victim_wback; + logic st1_dir_victim_dirty; + hpdcache_tag_t st1_dir_victim_tag; + hpdcache_way_vector_t st1_dir_victim_way; + hpdcache_nline_t st1_victim_nline; + logic st1_rtab_alloc; + logic st1_rtab_alloc_and_link; + logic st1_rtab_pop_try_commit; + logic st1_rtab_pop_try_rback; + hpdcache_rtab_deps_t st1_rtab_deps; + logic st1_rtab_check; + logic st1_rtab_check_hit; + + // Pipeline Stage 1/2 (depending on lowLatency setting) + logic core_rsp_valid; + logic core_rsp_error; + logic core_rsp_aborted; + hpdcache_req_tid_t core_rsp_tid; + hpdcache_req_sid_t core_rsp_sid; + + hpdcache_way_t refill_way_index; + + logic rtab_full; + logic rtab_fence; + + logic hpdcache_init_ready; + + logic data_req_read; + hpdcache_set_t data_req_read_set; + hpdcache_req_size_t data_req_read_size; + hpdcache_word_t data_req_read_word; + hpdcache_way_vector_t data_req_read_way; + hpdcache_req_user_t data_req_read_user; + hpdcache_req_data_t data_req_read_data; + // }}} + + // Decoding of the request in stage 0 + // {{{ + always_comb + begin : st0_req_pma_comb + st0_req_pma = core_req_i.pma; + + // force uncacheable requests if the cache is disabled + if (!cfg_enable_i) begin + st0_req_pma.uncacheable = 1'b1; + end + + // if either WB or WT is not supported, force write-policy + if (!HPDcacheCfg.u.wtEn) begin + st0_req_pma.wr_policy_hint = HPDCACHE_WR_POLICY_WB; + end + if (!HPDcacheCfg.u.wbEn) begin + st0_req_pma.wr_policy_hint = HPDCACHE_WR_POLICY_WT; + end + end + + // Select between request in the replay table or a new core requests + assign st0_req.addr_offset = st0_rtab_pop_try_valid ? st0_rtab_pop_try_req.req.addr_offset + : core_req_i.addr_offset; + assign st0_req.addr_tag = st0_rtab_pop_try_valid ? st0_rtab_pop_try_req.req.addr_tag + : core_req_i.addr_tag; + assign st0_req.wdata = st0_rtab_pop_try_valid ? st0_rtab_pop_try_req.req.wdata + : core_req_i.wdata; + assign st0_req.op = st0_rtab_pop_try_valid ? st0_rtab_pop_try_req.req.op + : core_req_i.op; + assign st0_req.be = st0_rtab_pop_try_valid ? st0_rtab_pop_try_req.req.be + : core_req_i.be; + assign st0_req.size = st0_rtab_pop_try_valid ? st0_rtab_pop_try_req.req.size + : core_req_i.size; + assign st0_req.sid = st0_rtab_pop_try_valid ? st0_rtab_pop_try_req.req.sid + : core_req_i.sid; + assign st0_req.tid = st0_rtab_pop_try_valid ? st0_rtab_pop_try_req.req.tid + : core_req_i.tid; + assign st0_req.need_rsp = st0_rtab_pop_try_valid ? st0_rtab_pop_try_req.req.need_rsp + : core_req_i.need_rsp; + assign st0_req.phys_indexed = st0_rtab_pop_try_valid ? 1'b1 : + core_req_i.phys_indexed; + assign st0_req.pma = st0_rtab_pop_try_valid ? st0_rtab_pop_try_req.req.pma + : st0_req_pma; + if (HPDcacheCfg.u.userEn) begin : gen_st0_req_wuser_useren + assign st0_req.wuser = st0_rtab_pop_try_valid ? st0_rtab_pop_try_req.req.wuser + : core_req_i.wuser; + end else begin : gen_st0_req_wuser_default + assign st0_req.wuser = '0; + end + + // Check if the request from the RTAB has been tagged with an error + assign st0_req_is_error = st0_rtab_pop_try_valid & st0_rtab_pop_try_error; + + // Decode operation in stage 0 + assign st0_req_is_uncacheable = st0_req.pma.uncacheable; + assign st0_req_is_load = is_load(st0_req.op); + assign st0_req_is_store = is_store(st0_req.op); + assign st0_req_is_amo = is_amo(st0_req.op); + assign st0_req_is_cmo_fence = is_cmo_fence(st0_req.op); + assign st0_req_is_cmo_inval = is_cmo_inval(st0_req.op); + assign st0_req_is_cmo_prefetch = is_cmo_prefetch(st0_req.op); + // }}} + + // Decode operation in stage 1 + // {{{ + always_comb + begin : st1_req_pma_comb + st1_req_pma = st1_req_q.phys_indexed ? st1_req_q.pma : core_req_pma_i; + + // force uncacheable requests if the cache is disabled + if (!cfg_enable_i) begin + st1_req_pma.uncacheable = 1'b1; + end + + // if either WB or WT is not supported, force write-policy + if (!HPDcacheCfg.u.wtEn) begin + st1_req_pma.wr_policy_hint = HPDCACHE_WR_POLICY_WB; + end + if (!HPDcacheCfg.u.wbEn) begin + st1_req_pma.wr_policy_hint = HPDCACHE_WR_POLICY_WT; + end + end + + // In case of replay or physically-indexed cache, the tag and PMA come + // from stage 0. Otherwise, this information come directly from the + // requester in stage 1 + assign st1_req_tag = st1_req_q.phys_indexed ? st1_req_q.addr_tag : core_req_tag_i; + + assign st1_req.addr_offset = st1_req_q.addr_offset; + assign st1_req.addr_tag = st1_req_rtab_q ? st1_req_q.addr_tag : st1_req_tag; + assign st1_req.wdata = st1_req_q.wdata; + assign st1_req.op = st1_req_q.op; + assign st1_req.be = st1_req_q.be; + assign st1_req.size = st1_req_q.size; + assign st1_req.sid = st1_req_q.sid; + assign st1_req.tid = st1_req_q.tid; + assign st1_req.need_rsp = st1_req_q.need_rsp; + assign st1_req.phys_indexed = st1_req_q.phys_indexed; + assign st1_req.pma = st1_req_rtab_q ? st1_req_q.pma : st1_req_pma; + assign st1_req.wuser = HPDcacheCfg.u.userEn ? st1_req_q.wuser : '0; + + // A requester can ask to abort a request it initiated on the + // previous cycle (stage 0). Useful in case of TLB miss for example + assign st1_req_abort = core_req_abort_i & ~st1_req.phys_indexed; + + assign st1_req_is_uncacheable = ~cfg_enable_i | st1_req.pma.uncacheable; + assign st1_req_is_load = is_load(st1_req.op); + assign st1_req_is_store = is_store(st1_req.op); + assign st1_req_is_amo = is_amo(st1_req.op); + assign st1_req_is_amo_lr = is_amo_lr(st1_req.op); + assign st1_req_is_amo_sc = is_amo_sc(st1_req.op); + assign st1_req_is_amo_swap = is_amo_swap(st1_req.op); + assign st1_req_is_amo_add = is_amo_add(st1_req.op); + assign st1_req_is_amo_and = is_amo_and(st1_req.op); + assign st1_req_is_amo_or = is_amo_or(st1_req.op); + assign st1_req_is_amo_xor = is_amo_xor(st1_req.op); + assign st1_req_is_amo_max = is_amo_max(st1_req.op); + assign st1_req_is_amo_maxu = is_amo_maxu(st1_req.op); + assign st1_req_is_amo_min = is_amo_min(st1_req.op); + assign st1_req_is_amo_minu = is_amo_minu(st1_req.op); + assign st1_req_is_cmo_inval = is_cmo_inval(st1_req.op); + assign st1_req_is_cmo_flush = is_cmo_flush(st1_req.op); + assign st1_req_is_cmo_fence = is_cmo_fence(st1_req.op); + assign st1_req_is_cmo_prefetch = is_cmo_prefetch(st1_req.op); + + // Decode write-policy hint + assign st1_req_wr_wt = (st1_req.pma.wr_policy_hint == HPDCACHE_WR_POLICY_WT); + assign st1_req_wr_wb = (st1_req.pma.wr_policy_hint == HPDCACHE_WR_POLICY_WB); + assign st1_req_wr_auto = (st1_req.pma.wr_policy_hint == HPDCACHE_WR_POLICY_AUTO); + // }}} + + // Cache controller protocol engine + // {{{ + hpdcache_ctrl_pe #( + .HPDcacheCfg(HPDcacheCfg), + .hpdcache_cl_user_t(hpdcache_cl_user_t) + ) hpdcache_ctrl_pe_i( + .core_req_valid_i, + .core_req_ready_o, + .rtab_req_valid_i (st0_rtab_pop_try_valid), + .rtab_req_ready_o (st0_rtab_pop_try_ready), + .refill_req_valid_i, + .refill_req_ready_o, + + .st0_req_is_error_i (st0_req_is_error), + .st0_req_is_uncacheable_i (st0_req_is_uncacheable), + .st0_req_need_rsp_i (st0_req.need_rsp), + .st0_req_is_load_i (st0_req_is_load), + .st0_req_is_store_i (st0_req_is_store), + .st0_req_is_amo_i (st0_req_is_amo), + .st0_req_is_cmo_fence_i (st0_req_is_cmo_fence), + .st0_req_is_cmo_inval_i (st0_req_is_cmo_inval), + .st0_req_is_cmo_prefetch_i (st0_req_is_cmo_prefetch), + .st0_req_mshr_check_o (st0_mshr_check_o), + .st0_req_cachedir_read_o (st0_req_cachedir_read), + + .st1_req_valid_i (st1_req_valid_q), + .st1_req_abort_i (st1_req_abort), + .st1_req_rtab_i (st1_req_rtab_q), + .st1_req_is_error_i (st1_req_is_error_q), + .st1_req_is_uncacheable_i (st1_req_is_uncacheable), + .st1_req_need_rsp_i (st1_req.need_rsp), + .st1_req_is_load_i (st1_req_is_load), + .st1_req_is_store_i (st1_req_is_store), + .st1_req_is_amo_i (st1_req_is_amo), + .st1_req_is_amo_lr_i (st1_req_is_amo_lr), + .st1_req_is_cmo_inval_i (st1_req_is_cmo_inval), + .st1_req_is_cmo_flush_i (st1_req_is_cmo_flush), + .st1_req_is_cmo_fence_i (st1_req_is_cmo_fence), + .st1_req_is_cmo_prefetch_i (st1_req_is_cmo_prefetch), + .st1_req_wr_wt_i (st1_req_wr_wt), + .st1_req_wr_wb_i (st1_req_wr_wb), + .st1_req_wr_auto_i (st1_req_wr_auto), + .st1_dir_hit_wback_i (st1_dir_hit_wback), + .st1_dir_hit_dirty_i (st1_dir_hit_dirty), + .st1_dir_hit_fetch_i (st1_dir_hit_fetch), + .st1_dir_victim_unavailable_i (st1_dir_victim_unavailable), + .st1_dir_victim_valid_i (st1_dir_victim_valid), + .st1_dir_victim_wback_i (st1_dir_victim_wback), + .st1_dir_victim_dirty_i (st1_dir_victim_dirty), + .st1_req_valid_o (st1_req_valid_d), + .st1_req_is_error_o (st1_req_is_error_d), + .st1_rsp_valid_o (st1_rsp_valid), + .st1_rsp_error_o (st1_rsp_error), + .st1_rsp_aborted_o (st1_rsp_aborted), + .st1_req_cachedir_sel_victim_o (st1_victim_sel), + .st1_req_cachedir_updt_sel_victim_o (st1_req_updt_sel_victim), + .st1_req_cachedata_write_o (st1_req_cachedata_write), + .st1_req_cachedata_write_enable_o (st1_req_cachedata_write_enable), + + .st2_mshr_alloc_i (st2_mshr_alloc_q), + .st2_mshr_alloc_is_prefetch_i (st2_mshr_alloc_is_prefetch_q), + .st2_mshr_alloc_wback_i (st2_mshr_alloc_wback_q), + .st2_mshr_alloc_dirty_i (st2_mshr_alloc_dirty_q), + .st2_mshr_alloc_o (st2_mshr_alloc_d), + .st2_mshr_alloc_cs_o (st2_mshr_alloc_cs_o), + .st2_mshr_alloc_need_rsp_o (st2_mshr_alloc_need_rsp_d), + .st2_mshr_alloc_wback_o (st2_mshr_alloc_wback_d), + .st2_mshr_alloc_dirty_o (st2_mshr_alloc_dirty_d), + + .st2_dir_updt_i (st2_dir_updt_q), + .st2_dir_updt_valid_i (st2_dir_updt_valid_q), + .st2_dir_updt_wback_i (st2_dir_updt_wback_q), + .st2_dir_updt_dirty_i (st2_dir_updt_dirty_q), + .st2_dir_updt_fetch_i (st2_dir_updt_fetch_q), + .st2_dir_updt_o (st2_dir_updt_d), + .st2_dir_updt_valid_o (st2_dir_updt_valid_d), + .st2_dir_updt_wback_o (st2_dir_updt_wback_d), + .st2_dir_updt_dirty_o (st2_dir_updt_dirty_d), + .st2_dir_updt_fetch_o (st2_dir_updt_fetch_d), + + .req_cachedata_read_o (data_req_read), + + .flush_busy_i, + .st1_flush_check_hit_i (flush_check_hit_i), + .st1_flush_alloc_ready_i (flush_alloc_ready_i), + .st2_flush_alloc_i (st2_flush_alloc_q), + .st2_flush_alloc_o (st2_flush_alloc_d), + + .rtab_full_i (rtab_full), + .rtab_fence_i (rtab_fence), + .rtab_check_o (st1_rtab_check), + .rtab_check_hit_i (st1_rtab_check_hit), + .st1_rtab_alloc_o (st1_rtab_alloc), + .st1_rtab_alloc_and_link_o (st1_rtab_alloc_and_link), + .st1_rtab_commit_o (st1_rtab_pop_try_commit), + .st1_rtab_rback_o (st1_rtab_pop_try_rback), + .st1_rtab_mshr_hit_o (st1_rtab_deps.mshr_hit), + .st1_rtab_mshr_full_o (st1_rtab_deps.mshr_full), + .st1_rtab_mshr_ready_o (st1_rtab_deps.mshr_ready), + .st1_rtab_write_miss_o (st1_rtab_deps.write_miss), + .st1_rtab_wbuf_hit_o (st1_rtab_deps.wbuf_hit), + .st1_rtab_wbuf_not_ready_o (st1_rtab_deps.wbuf_not_ready), + .st1_rtab_dir_unavailable_o (st1_rtab_deps.dir_unavailable), + .st1_rtab_dir_fetch_o (st1_rtab_deps.dir_fetch), + .st1_rtab_flush_hit_o (st1_rtab_deps.flush_hit), + .st1_rtab_flush_not_ready_o (st1_rtab_deps.flush_not_ready), + + .cachedir_hit_i (cachedir_hit_o), + .cachedir_init_ready_i (hpdcache_init_ready), + + .st1_mshr_alloc_ready_i (st1_mshr_alloc_ready_i), + .st1_mshr_hit_i (st1_mshr_hit_i), + .st1_mshr_full_i (st1_mshr_alloc_full_i), + .st1_mshr_cbuf_full_i (st1_mshr_alloc_cbuf_full_i), + + .refill_busy_i, + .refill_core_rsp_valid_i, + + .wbuf_write_valid_o (wbuf_write_o), + .wbuf_write_ready_i, + .wbuf_read_hit_i, + .wbuf_write_uncacheable_o, + .wbuf_read_flush_hit_o, + + .uc_busy_i, + .uc_req_valid_o, + .uc_core_rsp_ready_o, + + .cmo_busy_i, + .cmo_wait_i, + .cmo_req_valid_o, + .cmo_core_rsp_ready_o, + + .cfg_prefetch_updt_plru_i, + .cfg_default_wb_i, + + .evt_cache_write_miss_o, + .evt_cache_read_miss_o, + .evt_uncached_req_o, + .evt_cmo_req_o, + .evt_write_req_o, + .evt_read_req_o, + .evt_prefetch_req_o, + .evt_req_on_hold_o, + .evt_rtab_rollback_o, + .evt_stall_refill_o, + .evt_stall_o + ); + + // pipeline is empty + assign ctrl_empty_o = ~(st1_req_valid_q | st2_mshr_alloc_q | st2_dir_updt_q); + + // no available victim cacheline (all pre-allocated for replacement) + assign st1_dir_victim_unavailable = ~(|st1_dir_victim_way); + + // }}} + + // Replay table + // {{{ + rtab_entry_t st1_alloc_rtab; + + hpdcache_1hot_to_binary #( + .N (HPDcacheCfg.u.ways) + ) dir_hit_way_encoder_i( + .val_i (st1_dir_hit_way), + .val_o (st1_dir_hit_way_index) + ); + + hpdcache_1hot_to_binary #( + .N (HPDcacheCfg.u.ways) + ) refill_way_encoder_i( + .val_i (refill_way_i), + .val_o (refill_way_index) + ); + + assign st1_alloc_rtab = '{ + req : st1_req, + way_fetch : st1_dir_hit_way_index + }; + + hpdcache_rtab #( + .HPDcacheCfg (HPDcacheCfg), + .hpdcache_nline_t (hpdcache_nline_t), + .hpdcache_way_t (hpdcache_way_t), + .hpdcache_req_addr_t (hpdcache_req_addr_t), + .rtab_ptr_t (rtab_ptr_t), + .rtab_entry_t (rtab_entry_t) + ) hpdcache_rtab_i( + .clk_i, + .rst_ni, + + .empty_o (rtab_empty_o), + .full_o (rtab_full), + .fence_o (rtab_fence), + + .check_i (st1_rtab_check), + .check_nline_i (st1_req_nline), + .check_hit_o (st1_rtab_check_hit), + + .alloc_i (st1_rtab_alloc), + .alloc_and_link_i (st1_rtab_alloc_and_link), + .alloc_req_i (st1_alloc_rtab), + .alloc_deps_i (st1_rtab_deps), + + .pop_try_valid_o (st0_rtab_pop_try_valid), + .pop_try_i (st0_rtab_pop_try_ready), + .pop_try_req_o (st0_rtab_pop_try_req), + .pop_try_ptr_o (st0_rtab_pop_try_ptr), + .pop_try_error_o (st0_rtab_pop_try_error), + + .pop_commit_i (st1_rtab_pop_try_commit), + .pop_commit_ptr_i (st1_rtab_pop_try_ptr_q), + + .pop_rback_i (st1_rtab_pop_try_rback), + .pop_rback_ptr_i (st1_rtab_pop_try_ptr_q), + + .wbuf_addr_o (wbuf_rtab_addr_o), + .wbuf_is_read_o (wbuf_rtab_is_read_o), + .wbuf_hit_open_i (wbuf_rtab_hit_open_i), + .wbuf_hit_pend_i (wbuf_rtab_hit_pend_i), + .wbuf_hit_sent_i (wbuf_rtab_hit_sent_i), + .wbuf_not_ready_i (wbuf_rtab_not_ready_i), + + .miss_ready_i (st1_mshr_alloc_ready_i), + + .refill_i (refill_updt_rtab_i), + .refill_is_error_i, + .refill_nline_i, + .refill_way_index_i (refill_way_index), + + .flush_ack_i (flush_ack_i), + .flush_ack_nline_i (flush_ack_nline_i), + .flush_ready_i (flush_alloc_ready_i), + + .cfg_single_entry_i (cfg_rtab_single_entry_i) + ); + // }}} + + // Pipeline stage 1 registers + // {{{ + always_ff @(posedge clk_i) + begin : st1_req_payload_ff + if (core_req_ready_o | st0_rtab_pop_try_ready) begin + st1_req_q <= st0_req; + end + end + + always_ff @(posedge clk_i or negedge rst_ni) + begin : st1_req_valid_ff + if (!rst_ni) begin + st1_req_valid_q <= 1'b0; + st1_req_is_error_q <= 1'b0; + st1_req_rtab_q <= 1'b0; + st1_rtab_pop_try_ptr_q <= '0; + end else begin + st1_req_valid_q <= st1_req_valid_d; + st1_req_is_error_q <= st1_req_is_error_d; + if (core_req_ready_o | st0_rtab_pop_try_ready) begin + st1_req_rtab_q <= st0_rtab_pop_try_ready; + if (st0_rtab_pop_try_ready) begin + st1_rtab_pop_try_ptr_q <= st0_rtab_pop_try_ptr; + end + end + end + end + // }}} + + // Pipeline stage 2 registers + // {{{ + always_ff @(posedge clk_i) + begin : st2_metadata_ff + if (st2_mshr_alloc_d) begin + st2_mshr_alloc_need_rsp_q <= st2_mshr_alloc_need_rsp_d; + st2_mshr_alloc_addr_q <= st1_req_addr; + st2_mshr_alloc_sid_q <= st1_req.sid; + st2_mshr_alloc_tid_q <= st1_req.tid; + st2_mshr_alloc_wdata_q <= st1_req.wdata; + st2_mshr_alloc_wuser_q <= st1_req.wuser; + st2_mshr_alloc_be_q <= st1_req.be; + st2_mshr_alloc_is_prefetch_q <= st1_req_is_cmo_prefetch; + st2_mshr_alloc_wback_q <= st2_mshr_alloc_wback_d; + st2_mshr_alloc_dirty_q <= st2_mshr_alloc_dirty_d; + st2_mshr_alloc_victim_way_q <= st1_dir_victim_way; + end + + if (st2_flush_alloc_d) begin + st2_flush_alloc_nline_q <= st1_dir_hit ? st1_req_nline : st1_victim_nline; + st2_flush_alloc_way_q <= st1_dir_hit ? st1_dir_hit_way : st1_dir_victim_way; + end + + if (st2_dir_updt_d) begin + st2_dir_updt_tag_q <= st1_dir_hit ? st1_dir_hit_tag : st1_dir_victim_tag; + st2_dir_updt_set_q <= st1_req_set; + st2_dir_updt_way_q <= st1_dir_hit ? st1_dir_hit_way : st1_dir_victim_way; + st2_dir_updt_valid_q <= st2_dir_updt_valid_d; + st2_dir_updt_wback_q <= st2_dir_updt_wback_d; + st2_dir_updt_dirty_q <= st2_dir_updt_dirty_d; + st2_dir_updt_fetch_q <= st2_dir_updt_fetch_d; + end + end + + always_ff @(posedge clk_i or negedge rst_ni) + begin : st2_valid_ff + if (!rst_ni) begin + st2_mshr_alloc_q <= 1'b0; + st2_flush_alloc_q <= 1'b0; + st2_dir_updt_q <= 1'b0; + end else begin + st2_mshr_alloc_q <= st2_mshr_alloc_d; + st2_flush_alloc_q <= st2_flush_alloc_d; + st2_dir_updt_q <= st2_dir_updt_d; + end + end + // }}} + + // Controller for the HPDcache directory and data memory arrays + // {{{ + assign st0_req_set = st0_req.addr_offset[HPDcacheCfg.clOffsetWidth +: HPDcacheCfg.setWidth]; + assign st0_req_word = st0_req.addr_offset[HPDcacheCfg.wordByteIdxWidth +: + HPDcacheCfg.clWordIdxWidth]; + + assign st1_req_set = st1_req.addr_offset[HPDcacheCfg.clOffsetWidth +: HPDcacheCfg.setWidth]; + assign st1_req_word = st1_req.addr_offset[HPDcacheCfg.wordByteIdxWidth +: + HPDcacheCfg.clWordIdxWidth]; + assign st1_req_addr = {st1_req.addr_tag, st1_req.addr_offset}; + assign st1_req_nline = st1_req_addr[HPDcacheCfg.clOffsetWidth +: HPDcacheCfg.nlineWidth]; + + assign st1_victim_nline = {st1_dir_victim_tag, st1_req_set}; + + // When lowLatency, data SRAM is read at stage 0 but way selection is done at stage 1 + if (HPDcacheCfg.u.lowLatency) begin : gen_st0_data_sram_read + assign data_req_read_set = st0_req_set; + assign data_req_read_size = st0_req.size; + assign data_req_read_word = st0_req_word; + assign data_req_read_way = st1_dir_hit_way; + end + // When not lowLatency, data SRAM is read at stage 1 but way selection is done at stage 2 + else begin : gen_st1_data_sram_read + assign data_req_read_set = st1_req_set; + assign data_req_read_size = st1_req.size; + assign data_req_read_word = st1_req_word; + always_ff @(posedge clk_i) + begin : data_req_read_way_ff + data_req_read_way <= st1_dir_hit_way; + end + end + + hpdcache_memctrl #( + .HPDcacheCfg (HPDcacheCfg), + .hpdcache_nline_t (hpdcache_nline_t), + .hpdcache_tag_t (hpdcache_tag_t), + .hpdcache_set_t (hpdcache_set_t), + .hpdcache_word_t (hpdcache_word_t), + .hpdcache_way_vector_t (hpdcache_way_vector_t), + .hpdcache_dir_entry_t (hpdcache_dir_entry_t), + .hpdcache_cl_user_t (hpdcache_cl_user_t), + .hpdcache_data_word_t (hpdcache_data_word_t), + .hpdcache_data_be_t (hpdcache_data_be_t), + .hpdcache_req_data_t (hpdcache_req_data_t), + .hpdcache_req_user_t (hpdcache_req_user_t), + .hpdcache_req_be_t (hpdcache_req_be_t), + .hpdcache_access_data_t (hpdcache_access_data_t), + .hpdcache_access_user_t (hpdcache_access_user_t), + .hpdcache_access_be_t (hpdcache_access_be_t) + ) hpdcache_memctrl_i( + .clk_i, + .rst_ni, + + .ready_o (hpdcache_init_ready), + + .dir_match_i (st0_req_cachedir_read), + .dir_match_set_i (st0_req_set), + .dir_match_tag_i (st1_req.addr_tag), + .dir_updt_sel_victim_i (st1_req_updt_sel_victim), + .dir_hit_way_o (st1_dir_hit_way), + .dir_hit_tag_o (st1_dir_hit_tag), + .dir_hit_wback_o (st1_dir_hit_wback), + .dir_hit_dirty_o (st1_dir_hit_dirty), + .dir_hit_fetch_o (st1_dir_hit_fetch), + + .dir_updt_i (st2_dir_updt_q), + .dir_updt_set_i (st2_dir_updt_set_q), + .dir_updt_way_i (st2_dir_updt_way_q), + .dir_updt_tag_i (st2_dir_updt_tag_q), + .dir_updt_valid_i (st2_dir_updt_valid_q), + .dir_updt_wback_i (st2_dir_updt_wback_q), + .dir_updt_dirty_i (st2_dir_updt_dirty_q), + .dir_updt_fetch_i (st2_dir_updt_fetch_q), + + .dir_amo_match_i (uc_dir_amo_match_i), + .dir_amo_match_set_i (uc_dir_amo_match_set_i), + .dir_amo_match_tag_i (uc_dir_amo_match_tag_i), + .dir_amo_updt_sel_victim_i (uc_dir_amo_updt_sel_victim_i), + .dir_amo_hit_way_o (uc_dir_amo_hit_way_o), + + .dir_refill_i (refill_write_dir_i), + .dir_refill_set_i (refill_set_i), + .dir_refill_way_i (refill_way_i), + .dir_refill_entry_i (refill_dir_entry_i), + .dir_refill_updt_sel_victim_i (refill_updt_sel_victim_i), + + .dir_victim_sel_i (st1_victim_sel), + .dir_victim_set_i (st1_req_set), + .dir_victim_valid_o (st1_dir_victim_valid), + .dir_victim_wback_o (st1_dir_victim_wback), + .dir_victim_dirty_o (st1_dir_victim_dirty), + .dir_victim_tag_o (st1_dir_victim_tag), + .dir_victim_way_o (st1_dir_victim_way), + + .dir_inval_check_i (inval_check_dir_i), + .dir_inval_nline_i (inval_nline_i), + .dir_inval_write_i (inval_write_dir_i), + .dir_inval_hit_o (inval_hit_o), + + .dir_cmo_check_nline_i (cmo_dir_check_nline_i), + .dir_cmo_check_nline_set_i (cmo_dir_check_nline_set_i), + .dir_cmo_check_nline_tag_i (cmo_dir_check_nline_tag_i), + .dir_cmo_check_nline_hit_way_o (cmo_dir_check_nline_hit_way_o), + .dir_cmo_check_nline_wback_o (cmo_dir_check_nline_wback_o), + .dir_cmo_check_nline_dirty_o (cmo_dir_check_nline_dirty_o), + + .dir_cmo_check_entry_i (cmo_dir_check_entry_i), + .dir_cmo_check_entry_set_i (cmo_dir_check_entry_set_i), + .dir_cmo_check_entry_way_i (cmo_dir_check_entry_way_i), + .dir_cmo_check_entry_valid_o (cmo_dir_check_entry_valid_o), + .dir_cmo_check_entry_wback_o (cmo_dir_check_entry_wback_o), + .dir_cmo_check_entry_dirty_o (cmo_dir_check_entry_dirty_o), + .dir_cmo_check_entry_tag_o (cmo_dir_check_entry_tag_o), + + .dir_cmo_updt_i (cmo_dir_updt_i), + .dir_cmo_updt_set_i (cmo_dir_updt_set_i), + .dir_cmo_updt_way_i (cmo_dir_updt_way_i), + .dir_cmo_updt_tag_i (cmo_dir_updt_tag_i), + .dir_cmo_updt_valid_i (cmo_dir_updt_valid_i), + .dir_cmo_updt_wback_i (cmo_dir_updt_wback_i), + .dir_cmo_updt_dirty_i (cmo_dir_updt_dirty_i), + .dir_cmo_updt_fetch_i (cmo_dir_updt_fetch_i), + + .data_req_read_i (data_req_read), + .data_req_read_set_i (data_req_read_set), + .data_req_read_size_i (data_req_read_size), + .data_req_read_word_i (data_req_read_word), + .data_req_read_way_i (data_req_read_way), + .data_req_read_user_o (data_req_read_user), + .data_req_read_data_o (data_req_read_data), + + .data_req_write_i (st1_req_cachedata_write), + .data_req_write_enable_i (st1_req_cachedata_write_enable), + .data_req_write_set_i (st1_req_set), + .data_req_write_way_i (st1_dir_hit_way), + .data_req_write_size_i (st1_req.size), + .data_req_write_word_i (st1_req_word), + .data_req_write_user_i (st1_req.wuser), + .data_req_write_data_i (st1_req.wdata), + .data_req_write_be_i (st1_req.be), + + .data_amo_write_i (uc_data_amo_write_i), + .data_amo_write_enable_i (uc_data_amo_write_enable_i), + .data_amo_write_set_i (uc_data_amo_write_set_i), + .data_amo_write_size_i (uc_data_amo_write_size_i), + .data_amo_write_word_i (uc_data_amo_write_word_i), + .data_amo_write_user_i (uc_data_amo_write_user_i), + .data_amo_write_data_i (uc_data_amo_write_data_i), + .data_amo_write_be_i (uc_data_amo_write_be_i), + + .data_flush_read_i (flush_data_read_i), + .data_flush_read_set_i (flush_data_read_set_i), + .data_flush_read_way_i (flush_data_read_way_i), + .data_flush_read_word_i (flush_data_read_word_i), + .data_flush_read_data_o (flush_data_read_data_o), + .data_flush_read_user_o (flush_data_read_user_o), + + .data_refill_i (refill_write_data_i), + .data_refill_set_i (refill_set_i), + .data_refill_way_i (refill_way_i), + .data_refill_word_i (refill_word_i), + .data_refill_user_i (refill_user_i), + .data_refill_data_i (refill_data_i) + ); + + assign st1_dir_hit = |st1_dir_hit_way; + + assign cachedir_hit_o = st1_dir_hit; + // }}} + + // Write buffer outputs + // {{{ + assign wbuf_write_addr_o = st1_req_addr; + assign wbuf_write_data_o = st1_req.wdata; + assign wbuf_write_be_o = st1_req.be; + assign wbuf_flush_all_o = cmo_wbuf_flush_all_i | uc_wbuf_flush_all_i | wbuf_flush_i; + assign wbuf_write_user_o = HPDcacheCfg.u.userEn ? st1_req.wuser : '0; + // }}} + + // Miss handler outputs + // {{{ + assign st0_mshr_check_offset_o = st0_req.addr_offset; + assign st1_mshr_check_nline_o = st1_req_nline; + assign st2_mshr_alloc_o = st2_mshr_alloc_q; + assign st2_mshr_alloc_nline_o = st2_mshr_alloc_addr_q[HPDcacheCfg.clOffsetWidth +: + HPDcacheCfg.nlineWidth]; + assign st2_mshr_alloc_tid_o = st2_mshr_alloc_tid_q; + assign st2_mshr_alloc_sid_o = st2_mshr_alloc_sid_q; + assign st2_mshr_alloc_word_o = st2_mshr_alloc_addr_q[HPDcacheCfg.wordByteIdxWidth +: + HPDcacheCfg.clWordIdxWidth]; + assign st2_mshr_alloc_wdata_o = st2_mshr_alloc_wdata_q; + assign st2_mshr_alloc_be_o = st2_mshr_alloc_be_q; + assign st2_mshr_alloc_victim_way_o = st2_mshr_alloc_victim_way_q; + assign st2_mshr_alloc_need_rsp_o = st2_mshr_alloc_need_rsp_q; + assign st2_mshr_alloc_is_prefetch_o = st2_mshr_alloc_is_prefetch_q; + assign st2_mshr_alloc_wback_o = st2_mshr_alloc_wback_q; + assign st2_mshr_alloc_dirty_o = st2_mshr_alloc_dirty_q; + assign st2_mshr_alloc_wuser_o = HPDcacheCfg.u.userEn ? st2_mshr_alloc_wuser_q : '0; + // }}} + + // Uncacheable request handler outputs + // {{{ + assign uc_lrsc_snoop_o = st1_req_valid_q & st1_req_is_store, + uc_lrsc_snoop_addr_o = st1_req_addr, + uc_lrsc_snoop_size_o = st1_req.size, + uc_req_addr_o = st1_req_addr, + uc_req_size_o = st1_req.size, + uc_req_data_o = st1_req.wdata, + uc_req_user_o = HPDcacheCfg.u.userEn ? st1_req.wuser : '0, + uc_req_be_o = st1_req.be, + uc_req_uc_o = st1_req_is_uncacheable, + uc_req_sid_o = st1_req.sid, + uc_req_tid_o = st1_req.tid, + uc_req_need_rsp_o = st1_req.need_rsp, + uc_req_op_o.is_ld = st1_req_is_load, + uc_req_op_o.is_st = st1_req_is_store, + uc_req_op_o.is_amo_lr = st1_req_is_amo_lr, + uc_req_op_o.is_amo_sc = st1_req_is_amo_sc, + uc_req_op_o.is_amo_swap = st1_req_is_amo_swap, + uc_req_op_o.is_amo_add = st1_req_is_amo_add, + uc_req_op_o.is_amo_and = st1_req_is_amo_and, + uc_req_op_o.is_amo_or = st1_req_is_amo_or, + uc_req_op_o.is_amo_xor = st1_req_is_amo_xor, + uc_req_op_o.is_amo_max = st1_req_is_amo_max, + uc_req_op_o.is_amo_maxu = st1_req_is_amo_maxu, + uc_req_op_o.is_amo_min = st1_req_is_amo_min, + uc_req_op_o.is_amo_minu = st1_req_is_amo_minu; + // }}} + + // CMO request handler outputs + // {{{ + assign cmo_req_addr_o = st1_req_addr; + assign cmo_req_wdata_o = st1_req.wdata; + assign cmo_req_sid_o = st1_req.sid; + assign cmo_req_tid_o = st1_req.tid; + assign cmo_req_need_rsp_o = st1_req.need_rsp; + assign cmo_req_op_o.is_fence = st1_req_is_cmo_fence; + assign cmo_req_op_o.is_inval_by_nline = st1_req_is_cmo_inval & + is_cmo_inval_by_nline(st1_req.op); + assign cmo_req_op_o.is_inval_all = st1_req_is_cmo_inval & + is_cmo_inval_all(st1_req.op); + assign cmo_req_op_o.is_flush_by_nline = st1_req_is_cmo_flush & + is_cmo_flush_by_nline(st1_req.op); + assign cmo_req_op_o.is_flush_all = st1_req_is_cmo_flush & + is_cmo_flush_all(st1_req.op); + assign cmo_req_op_o.is_flush_inval_by_nline = st1_req_is_cmo_flush & + is_cmo_flush_inval_by_nline(st1_req.op); + assign cmo_req_op_o.is_flush_inval_all = st1_req_is_cmo_flush & + is_cmo_flush_inval_all(st1_req.op); + // }}} + + // Dirty/valid cachelines tracking to accelerate flushes and invalidations triggerd by CMOs + // {{{ + if (HPDcacheCfg.u.wbEn) begin : gen_cmo_dirty_set + hpdcache_set_t cmo_dirty_min_set_q, cmo_dirty_min_set_d; + hpdcache_set_t cmo_dirty_max_set_q, cmo_dirty_max_set_d; + logic cmo_dirty_set_en_q, cmo_dirty_set_en_d; + + always_comb + begin : cmo_dirty_min_max_set_comb + automatic hpdcache_uint32 v_min; + automatic hpdcache_uint32 v_max; + unique if (st2_dir_updt_q && st2_dir_updt_dirty_q) begin + // Cacheline updated by the pipeline + if (cmo_dirty_set_en_q) begin + v_min = hpdcache_min(hpdcache_uint32'(st2_dir_updt_set_q), + hpdcache_uint32'(cmo_dirty_min_set_q)); + v_max = hpdcache_max(hpdcache_uint32'(st2_dir_updt_set_q), + hpdcache_uint32'(cmo_dirty_max_set_q)); + end else begin + v_min = hpdcache_uint32'(st2_dir_updt_set_q); + v_max = hpdcache_uint32'(st2_dir_updt_set_q); + end + end else if (refill_write_dir_i && refill_dir_entry_i.dirty) begin + // Cacheline directly written during refill + if (cmo_dirty_set_en_q) begin + v_min = hpdcache_min(hpdcache_uint32'(refill_set_i), + hpdcache_uint32'(cmo_dirty_min_set_q)); + v_max = hpdcache_max(hpdcache_uint32'(refill_set_i), + hpdcache_uint32'(cmo_dirty_max_set_q)); + end else begin + v_min = hpdcache_uint32'(refill_set_i); + v_max = hpdcache_uint32'(refill_set_i); + end + end else begin + v_min = hpdcache_uint32'(cmo_dirty_min_set_q); + v_max = hpdcache_uint32'(cmo_dirty_max_set_q); + end + cmo_dirty_min_set_d = hpdcache_set_t'(v_min); + cmo_dirty_max_set_d = hpdcache_set_t'(v_max); + end + + always_comb + begin : cmo_dirty_set_en_comb + unique if ( + (st2_dir_updt_q && st2_dir_updt_dirty_q) || + (refill_write_dir_i && refill_dir_entry_i.dirty)) + begin + cmo_dirty_set_en_d = 1'b1; + end else if (cmo_flush_all_i) begin + cmo_dirty_set_en_d = 1'b0; + end else begin + cmo_dirty_set_en_d = cmo_dirty_set_en_q; + end + end + + always_ff @(posedge clk_i or negedge rst_ni) + begin : cmo_dirty_ff + if (!rst_ni) begin + cmo_dirty_set_en_q <= 1'b0; + cmo_dirty_min_set_q <= 0; + cmo_dirty_max_set_q <= 0; + end else begin + cmo_dirty_set_en_q <= cmo_dirty_set_en_d; + cmo_dirty_min_set_q <= cmo_dirty_min_set_d; + cmo_dirty_max_set_q <= cmo_dirty_max_set_d; + end + end + + assign cmo_dirty_set_en_o = cmo_dirty_set_en_q; + assign cmo_dirty_min_set_o = cmo_dirty_min_set_q; + assign cmo_dirty_max_set_o = cmo_dirty_max_set_q; + end else begin : gen_no_cmo_dirty_set + assign cmo_dirty_set_en_o = 1'b0; + assign cmo_dirty_min_set_o = '0; + assign cmo_dirty_max_set_o = '0; + end + + hpdcache_set_t cmo_valid_min_set_q, cmo_valid_min_set_d; + hpdcache_set_t cmo_valid_max_set_q, cmo_valid_max_set_d; + logic cmo_valid_set_en_q, cmo_valid_set_en_d; + + always_comb + begin : cmo_valid_min_max_set_comb + automatic hpdcache_uint32 v_min; + automatic hpdcache_uint32 v_max; + unique if (refill_write_dir_i && refill_dir_entry_i.valid) begin + if (cmo_valid_set_en_q) begin + v_min = hpdcache_min(hpdcache_uint32'(refill_set_i), + hpdcache_uint32'(cmo_valid_min_set_q)); + v_max = hpdcache_max(hpdcache_uint32'(refill_set_i), + hpdcache_uint32'(cmo_valid_max_set_q)); + end else begin + v_min = hpdcache_uint32'(refill_set_i); + v_max = hpdcache_uint32'(refill_set_i); + end + end else begin + v_min = hpdcache_uint32'(cmo_valid_min_set_q); + v_max = hpdcache_uint32'(cmo_valid_max_set_q); + end + cmo_valid_min_set_d = hpdcache_set_t'(v_min); + cmo_valid_max_set_d = hpdcache_set_t'(v_max); + end + + always_comb + begin : cmo_valid_set_en_comb + unique if (refill_write_dir_i && refill_dir_entry_i.valid) begin + cmo_valid_set_en_d = 1'b1; + end else if (cmo_inval_all_i) begin + cmo_valid_set_en_d = 1'b0; + end else begin + cmo_valid_set_en_d = cmo_valid_set_en_q; + end + end + + always_ff @(posedge clk_i or negedge rst_ni) + begin : cmo_valid_ff + if (!rst_ni) begin + cmo_valid_set_en_q <= 1'b0; + cmo_valid_min_set_q <= 0; + cmo_valid_min_set_q <= 0; + end else begin + cmo_valid_set_en_q <= cmo_valid_set_en_d; + cmo_valid_min_set_q <= cmo_valid_min_set_d; + cmo_valid_max_set_q <= cmo_valid_max_set_d; + end + end + + assign cmo_valid_set_en_o = cmo_valid_set_en_q; + assign cmo_valid_min_set_o = cmo_valid_min_set_q; + assign cmo_valid_max_set_o = cmo_valid_max_set_q; + // }}} + + // Flush controller outputs + // {{{ + assign flush_check_nline_o = st1_req_nline; + assign flush_alloc_o = st2_flush_alloc_q; + assign flush_alloc_nline_o = st2_flush_alloc_nline_q; + assign flush_alloc_way_o = st2_flush_alloc_way_q; + // }}} + + // Control of the response to the core + // {{{ + if (HPDcacheCfg.u.lowLatency) begin : gen_st2_core_rsp_comb + // When lowLatency, all responses to the core are sent on stage 1 + assign core_rsp_valid = st1_rsp_valid; + assign core_rsp_aborted = st1_rsp_aborted; + assign core_rsp_error = st1_rsp_error; + assign core_rsp_sid = st1_req.sid; + assign core_rsp_tid = st1_req.tid; + end else begin : gen_st2_core_rsp_ff + // When not lowLatency, delay all responses to the core by one cycle (stage 2) + always_ff @(posedge clk_i or negedge rst_ni) + begin : st2_core_rsp_ff + core_rsp_valid <= st1_rsp_valid; + core_rsp_aborted <= st1_rsp_aborted; + core_rsp_error <= st1_rsp_error; + core_rsp_sid <= st1_req.sid; + core_rsp_tid <= st1_req.tid; + end + end + + assign core_rsp_valid_o = refill_core_rsp_valid_i | + (uc_core_rsp_valid_i & uc_core_rsp_ready_o) | + (cmo_core_rsp_valid_i & cmo_core_rsp_ready_o) | + core_rsp_valid; + assign core_rsp_o.rdata = (refill_core_rsp_valid_i ? refill_core_rsp_i.rdata : + (cmo_core_rsp_valid_i ? cmo_core_rsp_i.rdata : + (uc_core_rsp_valid_i ? uc_core_rsp_i.rdata : + data_req_read_data))); + assign core_rsp_o.sid = (refill_core_rsp_valid_i ? refill_core_rsp_i.sid : + (cmo_core_rsp_valid_i ? cmo_core_rsp_i.sid : + (uc_core_rsp_valid_i ? uc_core_rsp_i.sid : + core_rsp_sid))); + assign core_rsp_o.tid = (refill_core_rsp_valid_i ? refill_core_rsp_i.tid : + (cmo_core_rsp_valid_i ? cmo_core_rsp_i.tid : + (uc_core_rsp_valid_i ? uc_core_rsp_i.tid : + core_rsp_tid))); + assign core_rsp_o.error = (refill_core_rsp_valid_i ? refill_core_rsp_i.error : + (cmo_core_rsp_valid_i ? cmo_core_rsp_i.error : + (uc_core_rsp_valid_i ? uc_core_rsp_i.error : + core_rsp_error))); + assign core_rsp_o.aborted = core_rsp_aborted; + if (HPDcacheCfg.u.userEn) begin : gen_core_rsp_o_ruser_useren + assign core_rsp_o.ruser = (refill_core_rsp_valid_i ? refill_core_rsp_i.ruser : + (cmo_core_rsp_valid_i ? cmo_core_rsp_i.ruser : + (uc_core_rsp_valid_i ? uc_core_rsp_i.ruser : + data_req_read_user))); + end else begin : gen_core_rsp_o_ruser_default + assign core_rsp_o.ruser = '0; + end + // }}} + + // Assertions + // {{{ +`ifndef HPDCACHE_ASSERT_OFF + // Check that the cache controller is being used by one and only one among a core request, the + // RTAB or the miss handler. + assert property (@(posedge clk_i) disable iff (rst_ni !== 1'b1) + $onehot0({core_req_ready_o, st0_rtab_pop_try_ready, refill_req_ready_o})) else + $error("ctrl: only one request can be served per cycle"); + + // Check that requests have a valid size field. The check is not necessary for the fence, + // invalidation and flush CMOs because these requests do not use the size field. + property prop_core_req_size_max; + @(posedge clk_i) disable iff (rst_ni !== 1'b1) ( + core_req_valid_i && core_req_ready_o && + !(is_cmo_fence(core_req_i.op) || + is_cmo_inval(core_req_i.op) || + is_cmo_flush(core_req_i.op)) + ) |-> ( + (2**core_req_i.size) <= HPDcacheCfg.reqDataBytes + ); + endproperty + + assert property (prop_core_req_size_max) else + $error("ctrl: bad SIZE for request"); + + // Check that stores and AMOs requests have a valid Byte Enable field. In particular, check + // that it is aligned with respect to the address + function automatic bit check_is_be_aligned( + input hpdcache_req_offset_t req_offset, + input hpdcache_req_be_t req_be + ); + int offset = int'(req_offset) % HPDcacheCfg.reqDataBytes; + return (((req_be >> offset) << offset) == req_be); + endfunction + + property prop_core_req_be_align; + @(posedge clk_i) disable iff (rst_ni !== 1'b1) ( + core_req_valid_i && core_req_ready_o && + (is_store(core_req_i.op) || is_amo(core_req_i.op)) + ) |-> ( + check_is_be_aligned(core_req_i.addr_offset, core_req_i.be) + ); + endproperty + + assert property (prop_core_req_be_align) else + $error("ctrl: bad BE alignment for request"); + + // Check that only one cache victim way is required when reserving a slot in the MSHR + assert property (@(posedge clk_i) disable iff (rst_ni !== 1'b1) + st2_mshr_alloc_q |-> $onehot(st2_mshr_alloc_victim_way_q)) else + $error("ctrl: no victim way selected during MSHR allocation"); +`endif + // }}} +endmodule diff --git a/hw/vendor/hpdcache/rtl/src/hpdcache_ctrl_pe.sv b/hw/vendor/hpdcache/rtl/src/hpdcache_ctrl_pe.sv new file mode 100644 index 00000000..957c5c65 --- /dev/null +++ b/hw/vendor/hpdcache/rtl/src/hpdcache_ctrl_pe.sv @@ -0,0 +1,1097 @@ +/* + * Copyright 2023 CEA* + * *Commissariat a l'Energie Atomique et aux Energies Alternatives (CEA) + * Copyright 2025 Inria, Universite Grenoble-Alpes, TIMA + * + * SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 + * + * Licensed under the Solderpad Hardware License v 2.1 (the “License”); you + * may not use this file except in compliance with the License, or, at your + * option, the Apache License version 2.0. You may obtain a copy of the + * License at + * + * https://solderpad.org/licenses/SHL-2.1/ + * + * Unless required by applicable law or agreed to in writing, any work + * distributed under the License is distributed on an “AS IS” BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +/* + * Authors : Cesar Fuguet + * Creation Date : April, 2021 + * Description : HPDcache Control Protocol Engine + * History : + */ +module hpdcache_ctrl_pe + // Package imports + // {{{ +import hpdcache_pkg::*; + // }}} + // Parameters + // {{{ +#( + parameter hpdcache_cfg_t HPDcacheCfg = '0, + parameter type hpdcache_cl_user_t = logic +) + // }}} + // Ports + // {{{ +( + // Requests + // {{{ + input logic core_req_valid_i, + output logic core_req_ready_o, + + input logic rtab_req_valid_i, + output logic rtab_req_ready_o, + + input logic refill_req_valid_i, + output logic refill_req_ready_o, + // }}} + + // Pipeline stage 0 + // {{{ + input logic st0_req_is_error_i, + input logic st0_req_is_uncacheable_i, + input logic st0_req_need_rsp_i, + input logic st0_req_is_load_i, + input logic st0_req_is_store_i, + input logic st0_req_is_amo_i, + input logic st0_req_is_cmo_fence_i, + input logic st0_req_is_cmo_inval_i, + input logic st0_req_is_cmo_prefetch_i, + output logic st0_req_mshr_check_o, + output logic st0_req_cachedir_read_o, + // }}} + + // Pipeline stage 1 + // {{{ + input logic st1_req_valid_i, + input logic st1_req_abort_i, + input logic st1_req_rtab_i, + input logic st1_req_is_error_i, + input logic st1_req_is_uncacheable_i, + input logic st1_req_need_rsp_i, + input logic st1_req_is_load_i, + input logic st1_req_is_store_i, + input logic st1_req_is_amo_i, + input logic st1_req_is_amo_lr_i, + input logic st1_req_is_cmo_inval_i, + input logic st1_req_is_cmo_flush_i, + input logic st1_req_is_cmo_fence_i, + input logic st1_req_is_cmo_prefetch_i, + input logic st1_req_wr_wt_i, + input logic st1_req_wr_wb_i, + input logic st1_req_wr_auto_i, + input logic st1_dir_hit_wback_i, + input logic st1_dir_hit_dirty_i, + input logic st1_dir_hit_fetch_i, + input logic st1_dir_victim_unavailable_i, + input logic st1_dir_victim_valid_i, + input logic st1_dir_victim_wback_i, + input logic st1_dir_victim_dirty_i, + output logic st1_req_valid_o, + output logic st1_req_is_error_o, + output logic st1_rsp_valid_o, + output logic st1_rsp_error_o, + output logic st1_rsp_aborted_o, + output logic st1_req_cachedir_sel_victim_o, + output logic st1_req_cachedir_updt_sel_victim_o, + output logic st1_req_cachedata_write_o, + output logic st1_req_cachedata_write_enable_o, + input logic st1_mshr_alloc_ready_i, + input logic st1_mshr_hit_i, + input logic st1_mshr_full_i, + input logic st1_mshr_cbuf_full_i, + // }}} + + // Pipeline stage 2 + // {{{ + input logic st2_mshr_alloc_i, + input logic st2_mshr_alloc_is_prefetch_i, + input logic st2_mshr_alloc_wback_i, + input logic st2_mshr_alloc_dirty_i, + output logic st2_mshr_alloc_o, + output logic st2_mshr_alloc_cs_o, + output logic st2_mshr_alloc_need_rsp_o, + output logic st2_mshr_alloc_wback_o, + output logic st2_mshr_alloc_dirty_o, + + input logic st2_dir_updt_i, + input logic st2_dir_updt_valid_i, + input logic st2_dir_updt_wback_i, + input logic st2_dir_updt_dirty_i, + input logic st2_dir_updt_fetch_i, + output logic st2_dir_updt_o, + output logic st2_dir_updt_valid_o, + output logic st2_dir_updt_wback_o, + output logic st2_dir_updt_dirty_o, + output logic st2_dir_updt_fetch_o, + // }}} + + // Cache data read enable + // {{{ + output logic req_cachedata_read_o, + // }}} + + // Replay + // {{{ + input logic rtab_full_i, + input logic rtab_fence_i, + output logic rtab_check_o, + input logic rtab_check_hit_i, + output logic st1_rtab_alloc_o, + output logic st1_rtab_alloc_and_link_o, + output logic st1_rtab_commit_o, + output logic st1_rtab_rback_o, + output logic st1_rtab_mshr_hit_o, + output logic st1_rtab_mshr_full_o, + output logic st1_rtab_mshr_ready_o, + output logic st1_rtab_write_miss_o, + output logic st1_rtab_wbuf_hit_o, + output logic st1_rtab_wbuf_not_ready_o, + output logic st1_rtab_dir_unavailable_o, + output logic st1_rtab_dir_fetch_o, + output logic st1_rtab_flush_hit_o, + output logic st1_rtab_flush_not_ready_o, + // }}} + + // Cache directory + // {{{ + input logic cachedir_hit_i, + input logic cachedir_init_ready_i, + // }}} + + // Refill interface + // {{{ + input logic refill_busy_i, + input logic refill_core_rsp_valid_i, + // }}} + + // Write buffer + // {{{ + input logic wbuf_write_ready_i, + input logic wbuf_read_hit_i, + output logic wbuf_write_valid_o, + output logic wbuf_write_uncacheable_o, + output logic wbuf_read_flush_hit_o, + // }}} + + // Flush controller + input logic flush_busy_i, + input logic st1_flush_check_hit_i, + input logic st1_flush_alloc_ready_i, + input logic st2_flush_alloc_i, + output logic st2_flush_alloc_o, + + // Uncacheable request handler + // {{{ + input logic uc_busy_i, + output logic uc_req_valid_o, + output logic uc_core_rsp_ready_o, + // }}} + + // Cache Management Operation (CMO) + // {{{ + input logic cmo_busy_i, + input logic cmo_wait_i, + output logic cmo_req_valid_o, + output logic cmo_core_rsp_ready_o, + // }}} + + // Configuration + // {{{ + input logic cfg_prefetch_updt_plru_i, + input logic cfg_default_wb_i, + // }}} + + // Performance events + // {{{ + output logic evt_cache_write_miss_o, + output logic evt_cache_read_miss_o, + output logic evt_uncached_req_o, + output logic evt_cmo_req_o, + output logic evt_write_req_o, + output logic evt_read_req_o, + output logic evt_prefetch_req_o, + output logic evt_req_on_hold_o, + output logic evt_rtab_rollback_o, + output logic evt_stall_refill_o, + output logic evt_stall_o + // }}} +); + // }}} + + // Definition of internal signals + // {{{ + logic st1_fence; + logic st1_rtab_alloc, st1_rtab_alloc_and_link; + logic st0_req_cachedata_read, st1_req_cachedata_read; + // }}} + + // Global control signals + // {{{ + + // Determine if the new request is a "fence". Here, fence instructions are + // considered those that need to be executed in program order + // (irrespectively of addresses). This means that all memory operations + // arrived before the "fence" instruction need to be finished, and only + // then the "fence" instruction is executed. In the same manner, all + // instructions following the "fence" need to wait the completion of this + // last before being executed. + assign st1_fence = st1_req_is_uncacheable_i | + st1_req_is_cmo_fence_i | + st1_req_is_cmo_inval_i | + st1_req_is_cmo_flush_i; + + // Trigger an event signal when a new request cannot consumed + assign evt_stall_o = core_req_valid_i & ~core_req_ready_o; + // }}} + + // Arbitration of responses to the core + // {{{ + assign uc_core_rsp_ready_o = ~refill_core_rsp_valid_i; + assign cmo_core_rsp_ready_o = ~refill_core_rsp_valid_i; + // }}} + + // Replay logic + // {{{ + // Replay table allocation + assign st1_rtab_alloc_o = st1_rtab_alloc & ~st1_req_rtab_i, + st1_rtab_alloc_and_link_o = st1_rtab_alloc_and_link, + st1_rtab_rback_o = st1_rtab_alloc & st1_req_rtab_i; + + // Performance event + assign evt_req_on_hold_o = st1_rtab_alloc | st1_rtab_alloc_and_link, + evt_rtab_rollback_o = st1_rtab_rback_o; + // }}} + + // Cachedata read enable + // {{{ + assign req_cachedata_read_o = st0_req_cachedata_read | st1_req_cachedata_read; + // }}} + + + // Data-cache control lines + // {{{ + always_comb + begin : hpdcache_ctrl_comb + automatic logic nop; + automatic logic st1_nop; // Do not consume a request in stage 0 because of stage 1 hazard + automatic logic st2_nop; // Do not consume a request in stage 0 because of stage 2 hazard + automatic logic st1_req_is_cacheable_store; + + uc_req_valid_o = 1'b0; + + cmo_req_valid_o = 1'b0; + + wbuf_write_valid_o = 1'b0; + wbuf_read_flush_hit_o = 1'b0; + wbuf_write_uncacheable_o = 1'b0; // unused + + core_req_ready_o = 1'b0; + rtab_req_ready_o = 1'b0; + refill_req_ready_o = 1'b0; + + st0_req_mshr_check_o = 1'b0; + st0_req_cachedir_read_o = 1'b0; + st0_req_cachedata_read = 1'b0; + + st1_req_valid_o = st1_req_valid_i; + st1_req_is_error_o = st1_req_is_error_i; + st1_req_is_cacheable_store = 1'b0; + st1_nop = 1'b0; + st1_req_cachedata_read = 1'b0; + st1_req_cachedata_write_o = 1'b0; + st1_req_cachedata_write_enable_o = 1'b0; + st1_req_cachedir_sel_victim_o = 1'b0; + st1_req_cachedir_updt_sel_victim_o = 1'b0; + st1_rsp_valid_o = 1'b0; + st1_rsp_error_o = 1'b0; + st1_rsp_aborted_o = 1'b0; + + st2_mshr_alloc_o = st2_mshr_alloc_i; + st2_mshr_alloc_cs_o = 1'b0; + st2_mshr_alloc_need_rsp_o = 1'b0; + st2_mshr_alloc_wback_o = st2_mshr_alloc_wback_i; + st2_mshr_alloc_dirty_o = st2_mshr_alloc_dirty_i; + + st2_flush_alloc_o = st2_flush_alloc_i; + + st2_dir_updt_o = st2_dir_updt_i; + st2_dir_updt_valid_o = st2_dir_updt_valid_i; + st2_dir_updt_wback_o = st2_dir_updt_wback_i; + st2_dir_updt_dirty_o = st2_dir_updt_dirty_i; + st2_dir_updt_fetch_o = st2_dir_updt_fetch_i; + + st2_nop = 1'b0; + + nop = 1'b0; + + rtab_check_o = 1'b0; + st1_rtab_alloc = 1'b0; + st1_rtab_alloc_and_link = 1'b0; + st1_rtab_commit_o = 1'b0; + st1_rtab_mshr_hit_o = 1'b0; + st1_rtab_mshr_full_o = 1'b0; + st1_rtab_mshr_ready_o = 1'b0; + st1_rtab_write_miss_o = 1'b0; + st1_rtab_wbuf_hit_o = 1'b0; + st1_rtab_wbuf_not_ready_o = 1'b0; + st1_rtab_dir_unavailable_o = 1'b0; + st1_rtab_dir_fetch_o = 1'b0; + st1_rtab_flush_hit_o = 1'b0; + st1_rtab_flush_not_ready_o = 1'b0; + + evt_cache_write_miss_o = 1'b0; + evt_cache_read_miss_o = 1'b0; + evt_uncached_req_o = 1'b0; + evt_cmo_req_o = 1'b0; + evt_write_req_o = 1'b0; + evt_read_req_o = 1'b0; + evt_prefetch_req_o = 1'b0; + evt_stall_refill_o = 1'b0; + + // Wait for the cache to be initialized + // {{{ + if (!cachedir_init_ready_i) begin + // initialization of the cache RAMs + end + // }}} + + // Refilling the cache + // {{{ + else if (refill_busy_i) begin + // miss handler has the control of the cache pipeline + evt_stall_refill_o = core_req_valid_i; + end + // }}} + + // Flush controller reading the cache + // {{{ + else if (flush_busy_i) begin + // flush controller has the control of the cache pipeline + end + // }}} + + // Normal pipeline operation + // {{{ + else begin + // Stage 2 request pending + // {{{ + // Allocate an entry in the MSHR + if (st2_mshr_alloc_i) begin + // Reset mshr alloc request + st2_mshr_alloc_o = 1'b0; + + // Enable the MSHR + st2_mshr_alloc_cs_o = 1'b1; + + // Introduce a NOP in the next cycle to prevent a hazard on the MSHR + st2_nop = 1'b1; + + // Performance event + evt_cache_read_miss_o = ~st2_mshr_alloc_is_prefetch_i; + evt_read_req_o = ~st2_mshr_alloc_is_prefetch_i; + evt_prefetch_req_o = st2_mshr_alloc_is_prefetch_i; + end + + // Flush a cacheline + if (st2_flush_alloc_i) begin + // Reset cache directory update request + st2_flush_alloc_o = 1'b0; + + // Introduce a NOP in the next cycle to prevent a hazard on the cache data + st2_nop = 1'b1; + end + + // Update the cache directory + if (st2_dir_updt_i) begin + // Reset cache directory update request + st2_dir_updt_o = 1'b0; + + // Introduce a NOP in the next cycle to prevent a hazard on the cache dir + st2_nop = 1'b1; + end + // }}} + + // Stage 1 request pending + // {{{ + if (st1_req_valid_i) begin + // Check if the request in stage 1 has a conflict with one of the + // request in the replay table. + rtab_check_o = ~st1_req_rtab_i & ~st1_fence; + + // Check if the current request is aborted. If so, respond to the + // core (when need_rsp is set) and set the aborted flag + if (st1_req_abort_i && !st1_req_rtab_i) begin + st1_rsp_valid_o = st1_req_need_rsp_i; + st1_rsp_aborted_o = 1'b1; + end + + else if (st1_req_is_error_i) begin + st1_rtab_commit_o = st1_req_rtab_i; + st1_rsp_valid_o = st1_req_need_rsp_i; + st1_rsp_error_o = st1_req_need_rsp_i; + + // Performance event + evt_write_req_o = st1_req_is_store_i; + end + + // Allocate a new entry in the replay table in case of conflict with + // an on-hold request + else if (rtab_check_o && rtab_check_hit_i) begin + st1_rtab_alloc_and_link = 1'b1; + + st1_nop = 1'b1; + end + + // CMO fence or invalidate + // {{{ + else if (st1_req_is_cmo_inval_i || + st1_req_is_cmo_flush_i || + st1_req_is_cmo_fence_i) + begin + cmo_req_valid_o = 1'b1; + st1_nop = 1'b1; + + // Performance event + evt_cmo_req_o = 1'b1; + end + // }}} + + // Uncacheable load, store or AMO request + // {{{ + else if (st1_req_is_uncacheable_i) begin + uc_req_valid_o = 1'b1; + st1_nop = 1'b1; + + // Performance event + evt_uncached_req_o = 1'b1; + end + // }}} + + // Cacheable request + // {{{ + else begin + // AMO cacheable request + // {{{ + if (st1_req_is_amo_i) begin + // Flush required but the controller is not ready + if (cachedir_hit_i && st1_dir_hit_dirty_i && !st1_flush_alloc_ready_i) + begin + st1_rtab_alloc = 1'b1; + st1_rtab_flush_not_ready_o = 1'b1; + st1_nop = 1'b1; + end + + // Pending miss on the same line + else if (st1_mshr_hit_i) begin + // Put the request in the replay table + st1_rtab_alloc = 1'b1; + st1_rtab_mshr_hit_o = 1'b1; + st1_nop = 1'b1; + end + + // Process the AMO request + else begin + uc_req_valid_o = 1'b1; + st1_nop = 1'b1; + + // If the request comes from the replay table, free the + // corresponding RTAB entry + st1_rtab_commit_o = st1_req_rtab_i; + + if (cachedir_hit_i) begin + // When the hit cacheline is dirty, flush its data to the memory + st2_flush_alloc_o = st1_dir_hit_dirty_i; + + // Update the directory: an AMO request clears the dirty bit + // because it triggers a flush of the cacheline before actually + // executing the AMO. + // An AMO does not set the dirty bit because it is always forwarded + // to the memory. Then the local copy is updated with respect + // to the old data from the memory. + // For CHERI, if we're doing an LR, we currently need to invalidate + // the line, as when we bring in the read from memory we don't know + // whether to clear the tag or not + st2_dir_updt_o = 1'b1; + st2_dir_updt_valid_o = HPDcacheCfg.u.capAmoEn ? !st1_req_is_amo_lr_i : 1'b1; + st2_dir_updt_wback_o = st1_dir_hit_wback_i; + st2_dir_updt_dirty_o = 1'b0; + + // If the cacheline has been pre-allocated for a pending miss, keep + // the fetch bit set + st2_dir_updt_fetch_o = st1_dir_hit_fetch_i; + end + + // Performance event + evt_uncached_req_o = 1'b1; + end + end + // }}} + + // Load cacheable request + // {{{ + if (|{st1_req_is_load_i, + st1_req_is_cmo_prefetch_i}) + begin + // Cache miss + // {{{ + if (!cachedir_hit_i) begin + // A cache miss inserts a nop into the pipeline + st1_nop = 1'b1; + + // If there is a match in the write buffer, send the entry right away + wbuf_read_flush_hit_o = 1'b1; + + // Select a victim cacheline + st1_req_cachedir_sel_victim_o = 1'b1; + + // Pending miss on the same line + if (st1_mshr_hit_i) begin + st1_rtab_alloc = 1'b1; + st1_rtab_mshr_hit_o = 1'b1; + end + + // No available slot in the MSHR + else if (st1_mshr_full_i) begin + st1_rtab_alloc = 1'b1; + st1_rtab_mshr_full_o = 1'b1; + end + + // All entries in the target set are being fetched + else if (st1_dir_victim_unavailable_i) begin + st1_rtab_alloc = 1'b1; + st1_rtab_dir_unavailable_o = 1'b1; + end + + // Hit on an open entry of the write buffer: wait for the entry to be + // acknowledged + else if (wbuf_read_hit_i) begin + // Put the request in the replay table + st1_rtab_alloc = 1'b1; + st1_rtab_wbuf_hit_o = 1'b1; + end + + // Miss Handler is not ready to send + else if (!st1_mshr_alloc_ready_i) begin + // Put the request on hold if the MISS HANDLER is not + // ready to send a new miss request. This is to prevent + // a deadlock between the read request channel and the + // read response channel. + // + // The request channel may be stalled by targets if they + // are not able to send a response (response is + // prioritary). Therefore, we need to put the request on + // hold to allow a possible refill read response to be + // accomplished. + st1_rtab_alloc = 1'b1; + st1_rtab_mshr_ready_o = 1'b1; + end + + // Flush pending on the miss cacheline + else if (st1_flush_check_hit_i) begin + st1_rtab_alloc = 1'b1; + st1_rtab_flush_hit_o = 1'b1; + end + + // Flush needed but the controller is not ready + else if (st1_dir_victim_dirty_i && !st1_flush_alloc_ready_i) begin + st1_rtab_alloc = 1'b1; + st1_rtab_flush_not_ready_o = 1'b1; + end + + // Forward the request to the next stage to allocate the + // entry in the MSHR and send the refill request + else begin + // When the victim cacheline is dirty, flush its data to the + // memory + st2_flush_alloc_o = st1_dir_victim_dirty_i; + + // If the request comes from the replay table, free the + // corresponding RTAB entry + st1_rtab_commit_o = st1_req_rtab_i; + + // Request a MSHR allocation + st2_mshr_alloc_o = 1'b1; + st2_mshr_alloc_need_rsp_o = st1_req_need_rsp_i; + st2_mshr_alloc_wback_o = (st1_req_wr_auto_i & cfg_default_wb_i) | + st1_req_wr_wb_i; + st2_mshr_alloc_dirty_o = 1'b0; + + // Update the cache directory state to FETCHING + st2_dir_updt_o = 1'b1; + st2_dir_updt_valid_o = st1_dir_victim_valid_i; + st2_dir_updt_wback_o = st1_dir_victim_wback_i; + st2_dir_updt_dirty_o = 1'b0; + st2_dir_updt_fetch_o = 1'b1; + end + end + // }}} + + // Cache hit + // {{{ + else begin + // Flush needed but the controller is not ready + if (st1_req_wr_wt_i && st1_dir_hit_wback_i && + st1_dir_hit_dirty_i && !st1_flush_alloc_ready_i) + begin + st1_rtab_alloc = 1'b1; + st1_rtab_flush_not_ready_o = 1'b1; + st1_nop = 1'b1; + end + + // Process the load + else begin + // If the request comes from the replay table, free the + // corresponding RTAB entry + st1_rtab_commit_o = st1_req_rtab_i; + + // Add a NOP when replaying a request, and there is no available + // request from the replay table. + st1_nop = st1_req_rtab_i & ~rtab_req_valid_i; + + // Update victim selection for the accessed set + st1_req_cachedir_updt_sel_victim_o = + ~st1_req_is_cmo_prefetch_i | + cfg_prefetch_updt_plru_i; + + // If not lowLatency, data is read from the cache in stage 1 + if (!HPDcacheCfg.u.lowLatency) begin + // Read data from the cache + st1_req_cachedata_read = 1'b1; + end + + // Respond to the core (if needed) + st1_rsp_valid_o = st1_req_need_rsp_i; + + // Performance event + evt_read_req_o = ~st1_req_is_cmo_prefetch_i; + evt_prefetch_req_o = st1_req_is_cmo_prefetch_i; + + // If the cacheline is currently pre-allocated to be replaced, we + // can only forward the data, but no state update is allowed. + if (!st1_dir_hit_fetch_i) begin + // Hint is write-through but the current state is not. The + // controller needs to update the state of the cacheline to WT + if (st1_req_wr_wt_i && st1_dir_hit_wback_i) begin + // Update the directory state of the cacheline to WT + st2_dir_updt_o = 1'b1; + st2_dir_updt_valid_o = 1'b1; + st2_dir_updt_wback_o = 1'b0; + st2_dir_updt_dirty_o = 1'b0; + st2_dir_updt_fetch_o = 1'b0; + + // Cacheline is dirty, flush its data to the memory + st2_flush_alloc_o = st1_dir_hit_dirty_i; + + st1_nop = 1'b1; + end + + // Hint is write-back but the current state is not. The + // controller needs to update the state of the cacheline to WB + // (clean) + if (st1_req_wr_wb_i && !st1_dir_hit_wback_i) begin + // Update the directory state of the cacheline to WB + st2_dir_updt_o = 1'b1; + st2_dir_updt_valid_o = 1'b1; + st2_dir_updt_wback_o = 1'b1; + st2_dir_updt_dirty_o = 1'b0; + st2_dir_updt_fetch_o = 1'b0; + + st1_nop = 1'b1; + end + end + end + end + // }}} + end + // }}} + + // Store cacheable request + // {{{ + if (st1_req_is_store_i) begin + // Add a NOP in the pipeline when: Replaying a request, the cache cannot + // accept a request from the core the next cycle. It can however accept + // a new request from the replay table + if (!HPDcacheCfg.u.lowLatency) begin + st1_nop = st1_req_rtab_i & ~rtab_req_valid_i; + end + + // Additional NOP case in lowLatency mode: Structural hazard on the cache + // data if the st0 request is a load operation. + else begin + st1_nop = ((core_req_valid_i | rtab_req_valid_i) & st0_req_is_load_i) | + (st1_req_rtab_i & ~rtab_req_valid_i); + end + + // Enable the data RAM in case of write. However, the actual write + // depends on the hit signal from the cache directory. + // + // IMPORTANT: this produces unnecessary power consumption in case of + // write misses, but removes timing paths between the cache directory + // RAM and the data RAM chip-select. + st1_req_cachedata_write_o = 1'b1; + + // Pending miss on the same line + if (st1_mshr_hit_i) begin + // Put the request in the replay table + st1_rtab_alloc = 1'b1; + st1_rtab_mshr_hit_o = 1'b1; + + st1_nop = 1'b1; + end + + // Hit in the flush controller + else if (st1_flush_check_hit_i) begin + // Put the request in the replay table + st1_rtab_alloc = 1'b1; + st1_rtab_flush_hit_o = 1'b1; + + st1_nop = 1'b1; + end + + // Cache miss + // {{{ + else if (!cachedir_hit_i) begin + // Write is write-back + // {{{ + if (st1_req_wr_wb_i || (st1_req_wr_auto_i && cfg_default_wb_i)) + begin + // Select a victim cacheline + st1_req_cachedir_sel_victim_o = 1'b1; + + // If there is a match in the write buffer, send the entry right + // away + wbuf_read_flush_hit_o = 1'b1; + + // Add a nop as the next cycle the controller needs to write in the + // MSHR and the directory + st1_nop = 1'b1; + + // Miss Handler is not ready to send + if (!st1_mshr_alloc_ready_i) begin + st1_rtab_alloc = 1'b1; + st1_rtab_mshr_ready_o = 1'b1; + end + + // No available slot in the MSHR + else if (st1_mshr_full_i) begin + // Put the request in the replay table + st1_rtab_alloc = 1'b1; + st1_rtab_mshr_full_o = 1'b1; + end + + // Hit on an entry of the write buffer: wait for the entry to be + // acknowledged + else if (wbuf_read_hit_i) begin + // Put the request in the replay table + st1_rtab_alloc = 1'b1; + st1_rtab_wbuf_hit_o = 1'b1; + end + + // no available victim cacheline (all currently pre-allocated and + // waiting to be refilled) + else if (st1_dir_victim_unavailable_i) begin + // Put the request in the replay table + st1_rtab_alloc = 1'b1; + st1_rtab_dir_unavailable_o = 1'b1; + end + + // Flush needed but the controller is not ready + else if (st1_dir_victim_dirty_i && !st1_flush_alloc_ready_i) begin + st1_rtab_alloc = 1'b1; + st1_rtab_flush_not_ready_o = 1'b1; + end + + else begin + // When the victim cacheline is dirty, flush its data to the + // memory + st2_flush_alloc_o = st1_dir_victim_dirty_i; + + // Update the directory state of the cacheline to FETCHING + st2_dir_updt_o = 1'b1; + st2_dir_updt_valid_o = st1_dir_victim_valid_i; + st2_dir_updt_wback_o = st1_dir_victim_wback_i; + st2_dir_updt_dirty_o = 1'b0; + st2_dir_updt_fetch_o = 1'b1; + + // Send a miss request to the memory (write-allocate) + st2_mshr_alloc_o = 1'b1; + st2_mshr_alloc_wback_o = 1'b1; + + // No available slot in the Coalesce Buffer: + // - Put the write operation into the replay table (but the + // read miss is triggered before hand to save some time) + if (st1_mshr_cbuf_full_i) begin + st2_mshr_alloc_need_rsp_o = 1'b0; + st2_mshr_alloc_dirty_o = 1'b0; + st1_rtab_alloc = 1'b1; + st1_rtab_write_miss_o = 1'b1; + end + + // The write can be completely process (coalesce buffer + // available): + // - Indicate to the MSHR that a response to the core is needed + // - Indicate a commit to RTAB if the request comes from it + else begin + st2_mshr_alloc_need_rsp_o = st1_req_need_rsp_i; + st2_mshr_alloc_dirty_o = 1'b1; + st1_rtab_commit_o = st1_req_rtab_i; + end + + // Performance event + evt_cache_write_miss_o = 1'b1; + evt_write_req_o = ~st1_mshr_cbuf_full_i; + end + end + // }}} + + // Write is write-through + // {{{ + else begin + // Request write into the write-buffer + wbuf_write_valid_o = 1'b1; + + // No available entry in the write buffer (or conflict on pending + // entry) + if (!wbuf_write_ready_i) begin + // Put the request in the replay table + st1_rtab_alloc = 1'b1; + st1_rtab_wbuf_not_ready_o = 1'b1; + + st1_nop = 1'b1; + end + + else begin + // If the request comes from the replay table, free the + // corresponding RTAB entry + st1_rtab_commit_o = st1_req_rtab_i; + + // Respond to the core (if needed) + st1_rsp_valid_o = st1_req_need_rsp_i; + + // Performance event + evt_cache_write_miss_o = 1'b1; + evt_write_req_o = 1'b1; + end + end + // }}} + end + // }}} + + // Cache hit + // {{{ + else begin + // The target cacheline is pre-allocated to be replaced. Put this write + // on-hold + if (st1_dir_hit_fetch_i) begin + // Put the request in the replay table + st1_rtab_alloc = 1'b1; + st1_rtab_dir_fetch_o = 1'b1; + + st1_nop = 1'b1; + end + + // Write is write-back + // {{{ + else if (st1_req_wr_wb_i || (st1_req_wr_auto_i && st1_dir_hit_wback_i)) + begin + // If there is a match in the write buffer, send the entry right + // away + wbuf_read_flush_hit_o = 1'b1; + + // Hit on an entry of the write buffer: wait for the entry to be + // acknowledged. + // + // This check is to avoid a possible future race when flushing + // a dirty cacheline if there is a pending write in the write + // buffer concerning the same cacheline + if (wbuf_read_hit_i) begin + // Put the request in the replay table + st1_rtab_alloc = 1'b1; + st1_rtab_wbuf_hit_o = 1'b1; + st1_nop = 1'b1; + + end else begin + // Update the directory state of the cacheline to dirty + if (!st1_dir_hit_wback_i || !st1_dir_hit_dirty_i) begin + st2_dir_updt_o = 1'b1; + st2_dir_updt_valid_o = 1'b1; + st2_dir_updt_wback_o = 1'b1; + st2_dir_updt_dirty_o = 1'b1; + st2_dir_updt_fetch_o = 1'b0; + + st1_nop = 1'b1; + end + + // If the request comes from the replay table, free the + // corresponding RTAB entry + st1_rtab_commit_o = st1_req_rtab_i; + + // Respond to the core (if needed) + st1_rsp_valid_o = st1_req_need_rsp_i; + + // Write in the data RAM + st1_req_cachedata_write_enable_o = 1'b1; + + // Update victim selection for the accessed set + st1_req_cachedir_updt_sel_victim_o = 1'b1; + + // Performance event + evt_write_req_o = 1'b1; + end + end + // }}} + + // Write is write-through + // {{{ + else begin + // Write in the write buffer unless we need to flush the cacheline + // first + wbuf_write_valid_o = ~st1_dir_hit_dirty_i; + + // The cache needs to flush the cacheline but the flush controller + // is not ready + if (st1_dir_hit_dirty_i && !st1_flush_alloc_ready_i) begin + st1_rtab_alloc = 1'b1; + st1_rtab_flush_not_ready_o = 1'b1; + + st1_nop = 1'b1; + end + + // Flush the cacheline but keep it in the cache + else if (st1_dir_hit_dirty_i) begin + // Flush cacheline data to the memory + st2_flush_alloc_o = 1'b1; + + // Update the state to WT in the directory + st2_dir_updt_o = 1'b1; + st2_dir_updt_valid_o = 1'b1; + st2_dir_updt_wback_o = 1'b0; + st2_dir_updt_dirty_o = 1'b0; + st2_dir_updt_fetch_o = 1'b0; + + // Put the request in the replay table while waiting for the + // memory flushing + st1_rtab_alloc = 1'b1; + st1_rtab_flush_hit_o = 1'b1; + + st1_nop = 1'b1; + end + + // No available entry in the write buffer (or conflict on pending + // entry) + else if (!wbuf_write_ready_i) begin + // Put the request in the replay table + st1_rtab_alloc = 1'b1; + st1_rtab_wbuf_not_ready_o = 1'b1; + + st1_nop = 1'b1; + end + + // The store can be performed in the write buffer and in the cache + else begin + // If the request comes from the replay table, free the + // corresponding RTAB entry + st1_rtab_commit_o = st1_req_rtab_i; + + // Respond to the core (if needed) + st1_rsp_valid_o = st1_req_need_rsp_i; + + // Update victim selection for the accessed set + st1_req_cachedir_updt_sel_victim_o = 1'b1; + + // Write in the data RAM + st1_req_cachedata_write_enable_o = 1'b1; + + // Performance event + evt_write_req_o = 1'b1; + end + end + // }}} + end + // }}} + end + // }}} + end + // }}} + end + // }}} + + // New request + // {{{ + nop = st1_nop | st2_nop; + + // New requests/refill are served according to the following priority: + // 0 - Refills/Invalidations (Highest priority) + // 1 - Replay Table + // 2 - Core (Lowest priority) + + // * IMPORTANT: When the replay table is full, the cache + // cannot accept new core requests to prevent a deadlock: If + // the core request needs to be put on hold, as there is no + // place the replay table, the pipeline needs to stall. If + // the pipeline is stalled, dependencies of on-hold requests + // cannot be solved, creating a deadlock + core_req_ready_o = core_req_valid_i + & ~rtab_req_valid_i + & ~refill_req_valid_i + & ~rtab_full_i + & ~cmo_busy_i + & ~uc_busy_i + & ~rtab_fence_i + & ~nop; + + rtab_req_ready_o = rtab_req_valid_i + & ~refill_req_valid_i + & (~cmo_busy_i | cmo_wait_i) + & ~nop; + + refill_req_ready_o = refill_req_valid_i + & (~cmo_busy_i | cmo_wait_i) + & ~st1_req_valid_i + & ~(st2_mshr_alloc_i | st2_dir_updt_i); + + // Forward the core/rtab request to stage 1 + st1_req_valid_o = core_req_ready_o | rtab_req_ready_o; + st1_req_is_error_o = st0_req_is_error_i; + + // New cacheable stage 0 request granted + // {{{ + // IMPORTANT: here the RAM is enabled independently if the + // request needs to be put on-hold. + // This increases the power consumption in that cases, but + // removes the timing paths RAM-to-RAM between the cache + // directory and the data array. + if ((core_req_ready_o | rtab_req_ready_o) && + !st0_req_is_uncacheable_i && + !st0_req_is_error_i) + begin + st1_req_is_cacheable_store = st1_req_valid_i & st1_req_is_store_i & + ~st1_req_is_uncacheable_i; + + if (HPDcacheCfg.u.lowLatency) begin + st0_req_cachedata_read = st0_req_is_load_i & + (~st1_req_is_cacheable_store | st1_req_is_error_i); + end + + if (st0_req_is_load_i | + st0_req_is_cmo_prefetch_i | + st0_req_is_store_i | + st0_req_is_amo_i ) + begin + st0_req_mshr_check_o = 1'b1; + st0_req_cachedir_read_o = 1'b1; + end + end + // }}} + // }}} + end + // }}} end of normal pipeline operation + end + // }}} +endmodule diff --git a/hw/vendor/hpdcache/rtl/src/hpdcache_flush.sv b/hw/vendor/hpdcache/rtl/src/hpdcache_flush.sv new file mode 100644 index 00000000..121f3bdc --- /dev/null +++ b/hw/vendor/hpdcache/rtl/src/hpdcache_flush.sv @@ -0,0 +1,417 @@ +/* + * Copyright 2024 CEA* + * *Commissariat a l'Energie Atomique et aux Energies Alternatives (CEA) + * Copyright 2025 Inria, Universite Grenoble-Alpes, TIMA + * + * SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 + * + * Licensed under the Solderpad Hardware License v 2.1 (the “License”); you + * may not use this file except in compliance with the License, or, at your + * option, the Apache License version 2.0. You may obtain a copy of the + * License at + * + * https://solderpad.org/licenses/SHL-2.1/ + * + * Unless required by applicable law or agreed to in writing, any work + * distributed under the License is distributed on an “AS IS” BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +/* + * Authors : Cesar Fuguet + * Creation Date : September, 2024 + * Description : HPDcache Flush Controller + * History : + */ +module hpdcache_flush +// {{{ +import hpdcache_pkg::*; +// Parameters +// {{{ +#( + parameter hpdcache_cfg_t HPDcacheCfg = '0, + + parameter type hpdcache_nline_t = logic, + parameter type hpdcache_set_t = logic, + parameter type hpdcache_word_t = logic, + parameter type hpdcache_way_vector_t = logic, + + parameter type hpdcache_access_data_t = logic, + parameter type hpdcache_access_user_t = logic, + + parameter type hpdcache_mem_id_t = logic, + parameter type hpdcache_mem_data_t = logic, + parameter type hpdcache_mem_user_t = logic, + parameter type hpdcache_mem_req_t = logic, + parameter type hpdcache_mem_req_w_t = logic, + parameter type hpdcache_mem_resp_w_t = logic +) +// }}} + +// Ports +// {{{ +( + input logic clk_i, + input logic rst_ni, + + // Global control signals + // {{{ + output logic flush_empty_o, + output logic flush_full_o, + output logic flush_busy_o, + // }}} + + // CHECK interface + // {{{ + input hpdcache_nline_t flush_check_nline_i, + output logic flush_check_hit_o, + // }}} + + // ALLOC interface + // {{{ + input logic flush_alloc_i, + output logic flush_alloc_ready_o, + input hpdcache_nline_t flush_alloc_nline_i, + input hpdcache_way_vector_t flush_alloc_way_i, + // }}} + + // CACHE DATA interface + // {{{ + output logic flush_data_read_o, + output hpdcache_set_t flush_data_read_set_o, + output hpdcache_word_t flush_data_read_word_o, + output hpdcache_way_vector_t flush_data_read_way_o, + input hpdcache_access_data_t flush_data_read_data_i, + input hpdcache_access_user_t flush_data_read_user_i, + // }}} + + // ACK monitoring interface + // {{{ + output logic flush_ack_o, + output hpdcache_nline_t flush_ack_nline_o, + // }}} + + // MEMORY interface + // {{{ + input logic mem_req_write_ready_i, + output logic mem_req_write_valid_o, + output hpdcache_mem_req_t mem_req_write_o, + + input logic mem_req_write_data_ready_i, + output logic mem_req_write_data_valid_o, + output hpdcache_mem_req_w_t mem_req_write_data_o, + + output logic mem_resp_write_ready_o, + input logic mem_resp_write_valid_i, + input hpdcache_mem_resp_w_t mem_resp_write_i + // }}} +); + + // Definition of constants and types + // {{{ + localparam int unsigned MemFlitPerAccess = hpdcache_max(HPDcacheCfg.accessWidth / HPDcacheCfg.u.memDataWidth, 1); + localparam int unsigned FlushEntries = HPDcacheCfg.u.flushEntries; + localparam int unsigned FlushIndexWidth = (FlushEntries > 1) ? $clog2(FlushEntries) : 1; + // FlushMaxEntries is equal to FlushEntries if it is a power of two + localparam int unsigned FlushMaxEntries = 2 ** FlushIndexWidth; + + typedef struct packed { + hpdcache_nline_t nline; + } flush_entry_t; + + typedef flush_entry_t [FlushEntries-1:0] flush_dir_t; + typedef logic [FlushIndexWidth-1:0] flush_dir_index_t; + + typedef enum { + FLUSH_IDLE, + FLUSH_SEND + } flush_fsm_e; + // }}} + + // Definition of internal signals and registers + // {{{ + logic [FlushEntries-1:0] flush_dir_valid_q; + flush_dir_t flush_dir_q; + flush_dir_index_t flush_dir_free_ptr; + logic [FlushEntries-1:0] flush_dir_free_bv; + logic [FlushEntries-1:0] flush_dir_alloc_bv; + flush_dir_index_t flush_dir_ack_ptr; + logic [FlushMaxEntries-1:0] flush_dir_ack_dec; + logic [FlushEntries-1:0] flush_dir_ack_bv; + hpdcache_set_t flush_set_q; + hpdcache_way_vector_t flush_way_q; + hpdcache_word_t flush_word_q, flush_word_d; + flush_fsm_e flush_fsm_q, flush_fsm_d; + + logic flush_eol; + logic flush_alloc; + hpdcache_set_t flush_alloc_set; + logic flush_ack; + logic flush_resizer_w, flush_resizer_wok; + logic flush_resizer_wlast; + + logic flush_mem_req_deq_user; + logic flush_mem_req_w, flush_mem_req_wok; + hpdcache_mem_req_t flush_mem_req_wmeta; + hpdcache_mem_data_t flush_mem_req_rdata; + hpdcache_mem_user_t flush_mem_req_ruser; + logic flush_mem_req_rlast; + + logic [FlushEntries-1:0] flush_check_hit; + + genvar gen_i; + // }}} + + // Flush FSM + // {{{ + assign flush_full_o = (&flush_dir_valid_q); + assign flush_empty_o = ~(|flush_dir_valid_q); + assign flush_busy_o = (flush_fsm_q != FLUSH_IDLE); + assign flush_alloc_set = flush_alloc_nline_i[0 +: HPDcacheCfg.setWidth]; + + // End Of cache Line (EOL) + // This signal is used to determine when the entire cacheline has been flushed (read from the + // cache and written into the resizer). + // Here we make the assumption that the number of cacheline words is a power of 2. + assign flush_eol = (flush_word_q == 0); + + assign flush_alloc_ready_o = (flush_fsm_q == FLUSH_IDLE) + & flush_resizer_wok + & flush_mem_req_wok + & ~flush_full_o; + + always_comb + begin : flush_fsm_comb + flush_fsm_d = flush_fsm_q; + flush_word_d = flush_word_q; + + flush_alloc = 1'b0; + + flush_data_read_o = 1'b0; + flush_data_read_set_o = flush_set_q; + flush_data_read_word_o = flush_word_q; + flush_data_read_way_o = flush_way_q; + + flush_mem_req_w = 1'b0; + + flush_resizer_w = 1'b0; + flush_resizer_wlast = 1'b0; + + unique case (flush_fsm_q) + FLUSH_IDLE: begin + flush_mem_req_w = flush_resizer_wok & ~flush_full_o & flush_alloc_i; + if (flush_alloc_i && flush_alloc_ready_o) begin + flush_data_read_o = 1'b1; + flush_data_read_set_o = flush_alloc_set; + flush_data_read_way_o = flush_alloc_way_i; + flush_data_read_word_o = 0; + + flush_alloc = 1'b1; + flush_word_d = flush_word_q + hpdcache_word_t'(HPDcacheCfg.u.accessWords); + flush_fsm_d = FLUSH_SEND; + end + end + FLUSH_SEND: begin + flush_resizer_w = 1'b1; + flush_resizer_wlast = flush_eol; + if (flush_resizer_wok) begin + flush_data_read_o = ~flush_eol; + if (flush_eol) begin + flush_fsm_d = FLUSH_IDLE; + end else begin + flush_word_d = flush_word_q + hpdcache_word_t'(HPDcacheCfg.u.accessWords); + end + end + end + endcase + end + + // Acknowledgement interface + assign flush_ack = mem_resp_write_valid_i; + assign flush_dir_ack_ptr = flush_dir_index_t'(mem_resp_write_i.mem_resp_w_id); + assign mem_resp_write_ready_o = 1'b1; + + assign flush_ack_o = flush_ack; + assign flush_ack_nline_o = flush_dir_q[flush_dir_ack_ptr].nline; + // }}} + + // Check logic + // {{{ + for (gen_i = 0; gen_i < FlushEntries; gen_i++) begin : gen_check + assign flush_check_hit[gen_i] = (flush_check_nline_i == flush_dir_q[gen_i].nline); + end + assign flush_check_hit_o = |(flush_dir_valid_q & ~flush_dir_ack_bv & flush_check_hit); + // }}} + + // Internal state + // {{{ + + // FSM + always_ff @(posedge clk_i or negedge rst_ni) + begin : flush_fsm_ff + if (!rst_ni) begin + flush_fsm_q <= FLUSH_IDLE; + flush_word_q <= '0; + end else begin + flush_fsm_q <= flush_fsm_d; + flush_word_q <= flush_word_d; + end + end + + // Directory valid + assign flush_dir_alloc_bv = flush_dir_free_bv & {FlushEntries{flush_alloc}}; + + always_ff @(posedge clk_i or negedge rst_ni) + begin : flush_dir_valid_ff + if (!rst_ni) begin + flush_dir_valid_q <= '0; + end else begin + flush_dir_valid_q <= (~flush_dir_valid_q & flush_dir_alloc_bv) | + ( flush_dir_valid_q & ~flush_dir_ack_bv ); + end + end + // }}} + + // Buffers + always_ff @(posedge clk_i) + begin : flush_dir_ff + if (flush_alloc) begin + flush_dir_q[flush_dir_free_ptr] <= '{ + nline: flush_alloc_nline_i + }; + flush_set_q <= flush_alloc_set; + flush_way_q <= flush_alloc_way_i; + end + end + // }}} + + // Internal components + // {{{ + hpdcache_decoder #(.N(FlushIndexWidth)) flush_ack_decoder_i( + .en_i (flush_ack), + .val_i (flush_dir_ack_ptr), + .val_o (flush_dir_ack_dec) + ); + + assign flush_dir_ack_bv = flush_dir_ack_dec[FlushEntries-1:0]; + + // Select a free entry in the flush directory + // + hpdcache_fxarb #(.N(FlushEntries)) flush_dir_free_arb_i( + .clk_i, + .rst_ni, + .req_i (~flush_dir_valid_q), + .gnt_o (flush_dir_free_bv), + .ready_i (flush_alloc) + ); + hpdcache_1hot_to_binary #(.N (FlushEntries)) flush_dir_free_ptr_bin_i( + .val_i (flush_dir_free_bv), + .val_o (flush_dir_free_ptr) + ); + + // FIFO for memory request metadata + // + localparam hpdcache_uint32 MemReqFlits = HPDcacheCfg.u.memDataWidth < HPDcacheCfg.clWidth ? + (HPDcacheCfg.clWidth / HPDcacheCfg.u.memDataWidth) - 1 : 0; + localparam hpdcache_uint ACCESS_MEM_RATIO = hpdcache_ceil_div(HPDcacheCfg.accessWidth, HPDcacheCfg.u.memDataWidth); + + assign flush_mem_req_wmeta = '{ + mem_req_addr: {flush_alloc_nline_i, {HPDcacheCfg.clOffsetWidth{1'b0}} }, + mem_req_len: hpdcache_mem_len_t'(MemReqFlits), + mem_req_size: get_hpdcache_mem_size(HPDcacheCfg.u.memDataWidth/8), + mem_req_id: hpdcache_mem_id_t'(flush_dir_free_ptr), + mem_req_command: HPDCACHE_MEM_WRITE, + mem_req_atomic: HPDCACHE_MEM_ATOMIC_ADD, /* NOP */ + mem_req_cacheable: 1'b1 + }; + hpdcache_fifo_reg #( + .FIFO_DEPTH (2), + .FEEDTHROUGH (1'b0), + .fifo_data_t (hpdcache_mem_req_t) + ) mem_req_meta_fifo_i( + .clk_i, + .rst_ni, + .w_i (flush_mem_req_w), + .wok_o (flush_mem_req_wok), + .wdata_i (flush_mem_req_wmeta), + .r_i (mem_req_write_ready_i), + .rok_o (mem_req_write_valid_o), + .rdata_o (mem_req_write_o) + ); + + // Resize data width from the cache controller to the NoC data width + // + hpdcache_data_resize #( + .WR_WIDTH (HPDcacheCfg.accessWidth), + .RD_WIDTH (HPDcacheCfg.u.memDataWidth), + .DEPTH (HPDcacheCfg.u.flushFifoDepth) + ) flush_data_resizer_i( + .clk_i, + .rst_ni, + + .w_i (flush_resizer_w), + .wok_o (flush_resizer_wok), + .wdata_i (flush_data_read_data_i), + .wlast_i (flush_resizer_wlast), + + .r_i (mem_req_write_data_ready_i), + .rok_o (mem_req_write_data_valid_o), + .rdata_o (flush_mem_req_rdata), + .rlast_o (/* open */) + ); + if (HPDcacheCfg.u.userEn) begin : gen_flush_mem_req_ruser_useren + hpdcache_data_resize #( + .WR_WIDTH (HPDcacheCfg.u.wordUserWidth*ACCESS_MEM_RATIO), + .RD_WIDTH (HPDcacheCfg.u.wordUserWidth*HPDcacheCfg.wordsPerMemFlit), + .DEPTH (HPDcacheCfg.u.flushFifoDepth) // XXX consider deriving from ratio to data width + ) flush_user_resizer_i( + .clk_i, + .rst_ni, + + .w_i (flush_resizer_w), + .wok_o (/* synced with data signals */), + .wdata_i ({ACCESS_MEM_RATIO{flush_data_read_user_i}}), + .wlast_i (flush_resizer_wlast), + + .r_i (mem_req_write_data_ready_i), + .rok_o (/* synced with data signals */), + .rdata_o (flush_mem_req_ruser), + .rlast_o (/* open */) + ); + end else begin : gen_flush_mem_req_ruser_default + assign flush_mem_req_ruser = '0; + end + + // Logic to detect the end of a packet + // + hpdcache_mem_len_t write_flits_cnt_q; + + assign flush_mem_req_rlast = (hpdcache_uint32'(write_flits_cnt_q) == MemReqFlits); + + always_ff @(posedge clk_i or negedge rst_ni) + begin + if (!rst_ni) begin + write_flits_cnt_q <= 0; + end else begin + if (mem_req_write_data_valid_o && mem_req_write_data_ready_i) begin + if (flush_mem_req_rlast) begin + write_flits_cnt_q <= 0; + end else begin + write_flits_cnt_q <= write_flits_cnt_q + 1; + end + end + end + end + + // Forward data flit to the NoC + // + assign mem_req_write_data_o = '{ + mem_req_w_data: flush_mem_req_rdata, + mem_req_w_user: flush_mem_req_ruser, + mem_req_w_be: '1, + mem_req_w_last: flush_mem_req_rlast + }; + // }}} + +endmodule diff --git a/hw/vendor/hpdcache/rtl/src/hpdcache_memctrl.sv b/hw/vendor/hpdcache/rtl/src/hpdcache_memctrl.sv new file mode 100644 index 00000000..bf2ac09f --- /dev/null +++ b/hw/vendor/hpdcache/rtl/src/hpdcache_memctrl.sv @@ -0,0 +1,1161 @@ +/* + * Copyright 2023 CEA* + * *Commissariat a l'Energie Atomique et aux Energies Alternatives (CEA) + * Copyright 2025 Inria, Universite Grenoble-Alpes, TIMA + * + * SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 + * + * Licensed under the Solderpad Hardware License v 2.1 (the “License”); you + * may not use this file except in compliance with the License, or, at your + * option, the Apache License version 2.0. You may obtain a copy of the + * License at + * + * https://solderpad.org/licenses/SHL-2.1/ + * + * Unless required by applicable law or agreed to in writing, any work + * distributed under the License is distributed on an “AS IS” BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +/* + * Authors : Cesar Fuguet + * Creation Date : April, 2021 + * Description : HPDcache Directory and Data Memory RAMs Controller + * History : + */ +module hpdcache_memctrl +import hpdcache_pkg::*; + // Parameters + // {{{ +#( + parameter hpdcache_cfg_t HPDcacheCfg = '0, + + parameter type hpdcache_nline_t = logic, + parameter type hpdcache_tag_t = logic, + parameter type hpdcache_set_t = logic, + parameter type hpdcache_word_t = logic, + parameter type hpdcache_way_vector_t = logic, + parameter type hpdcache_dir_entry_t = logic, + parameter type hpdcache_cl_user_t = logic, + + parameter type hpdcache_data_word_t = logic, + parameter type hpdcache_data_be_t = logic, + + parameter type hpdcache_req_data_t = logic, + parameter type hpdcache_req_user_t = logic, + parameter type hpdcache_req_be_t = logic, + + parameter type hpdcache_access_data_t = logic, + parameter type hpdcache_access_user_t = logic, + parameter type hpdcache_access_be_t = logic +) + // }}} + + // Ports + // {{{ +( + // Global clock and reset signals + // {{{ + input logic clk_i, + input logic rst_ni, + // }}} + + // Global control signals + // {{{ + output logic ready_o, + // }}} + + // DIR array access interface + // {{{ + input logic dir_match_i, + input hpdcache_set_t dir_match_set_i, + input hpdcache_tag_t dir_match_tag_i, + input logic dir_updt_sel_victim_i, + output hpdcache_way_vector_t dir_hit_way_o, + output hpdcache_tag_t dir_hit_tag_o, + output logic dir_hit_wback_o, + output logic dir_hit_dirty_o, + output logic dir_hit_fetch_o, + + input logic dir_updt_i, + input hpdcache_set_t dir_updt_set_i, + input hpdcache_way_vector_t dir_updt_way_i, + input hpdcache_tag_t dir_updt_tag_i, + input logic dir_updt_valid_i, + input logic dir_updt_wback_i, + input logic dir_updt_dirty_i, + input logic dir_updt_fetch_i, + + input logic dir_amo_match_i, + input hpdcache_set_t dir_amo_match_set_i, + input hpdcache_tag_t dir_amo_match_tag_i, + input logic dir_amo_updt_sel_victim_i, + output hpdcache_way_vector_t dir_amo_hit_way_o, + + input logic dir_refill_i, + input hpdcache_set_t dir_refill_set_i, + input hpdcache_way_vector_t dir_refill_way_i, + input hpdcache_dir_entry_t dir_refill_entry_i, + input logic dir_refill_updt_sel_victim_i, + + input logic dir_victim_sel_i, + input hpdcache_set_t dir_victim_set_i, + output logic dir_victim_valid_o, + output logic dir_victim_wback_o, + output logic dir_victim_dirty_o, + output hpdcache_tag_t dir_victim_tag_o, + output hpdcache_way_vector_t dir_victim_way_o, + + input logic dir_inval_check_i, + input hpdcache_nline_t dir_inval_nline_i, + input logic dir_inval_write_i, + output logic dir_inval_hit_o, + + input logic dir_cmo_check_nline_i, + input hpdcache_set_t dir_cmo_check_nline_set_i, + input hpdcache_tag_t dir_cmo_check_nline_tag_i, + output hpdcache_way_vector_t dir_cmo_check_nline_hit_way_o, + output logic dir_cmo_check_nline_wback_o, + output logic dir_cmo_check_nline_dirty_o, + + input logic dir_cmo_check_entry_i, + input hpdcache_set_t dir_cmo_check_entry_set_i, + input hpdcache_way_vector_t dir_cmo_check_entry_way_i, + output logic dir_cmo_check_entry_valid_o, + output logic dir_cmo_check_entry_wback_o, + output logic dir_cmo_check_entry_dirty_o, + output hpdcache_tag_t dir_cmo_check_entry_tag_o, + + input logic dir_cmo_updt_i, + input hpdcache_set_t dir_cmo_updt_set_i, + input hpdcache_way_vector_t dir_cmo_updt_way_i, + input hpdcache_tag_t dir_cmo_updt_tag_i, + input logic dir_cmo_updt_valid_i, + input logic dir_cmo_updt_wback_i, + input logic dir_cmo_updt_dirty_i, + input logic dir_cmo_updt_fetch_i, + // }}} + + // DATA array access interface + // {{{ + input logic data_req_read_i, + input hpdcache_set_t data_req_read_set_i, + input hpdcache_req_size_t data_req_read_size_i, + input hpdcache_word_t data_req_read_word_i, + input hpdcache_way_vector_t data_req_read_way_i, + output hpdcache_req_user_t data_req_read_user_o, + output hpdcache_req_data_t data_req_read_data_o, + + input logic data_req_write_i, + input logic data_req_write_enable_i, + input hpdcache_set_t data_req_write_set_i, + input hpdcache_way_vector_t data_req_write_way_i, + input hpdcache_req_size_t data_req_write_size_i, + input hpdcache_word_t data_req_write_word_i, + input hpdcache_req_user_t data_req_write_user_i, + input hpdcache_req_data_t data_req_write_data_i, + input hpdcache_req_be_t data_req_write_be_i, + + input logic data_amo_write_i, + input logic data_amo_write_enable_i, + input hpdcache_set_t data_amo_write_set_i, + input hpdcache_req_size_t data_amo_write_size_i, + input hpdcache_word_t data_amo_write_word_i, + input hpdcache_req_user_t data_amo_write_user_i, + input hpdcache_req_data_t data_amo_write_data_i, + input hpdcache_req_be_t data_amo_write_be_i, + + input logic data_flush_read_i, + input hpdcache_set_t data_flush_read_set_i, + input hpdcache_word_t data_flush_read_word_i, + input hpdcache_way_vector_t data_flush_read_way_i, + output hpdcache_access_user_t data_flush_read_user_o, + output hpdcache_access_data_t data_flush_read_data_o, + + input logic data_refill_i, + input hpdcache_set_t data_refill_set_i, + input hpdcache_way_vector_t data_refill_way_i, + input hpdcache_word_t data_refill_word_i, + input hpdcache_access_user_t data_refill_user_i, + input hpdcache_access_data_t data_refill_data_i + // }}} +); + // }}} + + // Definition of constants and types + // {{{ + localparam int unsigned HPDCACHE_DIR_RAM_WIDTH = $bits(hpdcache_dir_entry_t); + localparam int unsigned HPDCACHE_DIR_RAM_ADDR_WIDTH = $clog2(HPDcacheCfg.u.sets); + localparam int unsigned HPDCACHE_DATA_RAM_ENTR_PER_SET = HPDcacheCfg.u.clWords/ + HPDcacheCfg.u.accessWords; + localparam int unsigned HPDCACHE_DATA_RAM_DEPTH = HPDcacheCfg.u.sets* + HPDCACHE_DATA_RAM_ENTR_PER_SET; + localparam int unsigned HPDCACHE_DATA_RAM_WIDTH = HPDcacheCfg.u.dataWaysPerRamWord* + HPDcacheCfg.u.wordWidth; + localparam int unsigned HPDCACHE_DATA_RAM_ADDR_WIDTH = $clog2(HPDCACHE_DATA_RAM_DEPTH); + localparam int unsigned HPDCACHE_DATA_REQ_RATIO = HPDcacheCfg.u.accessWords/ + HPDcacheCfg.u.reqWords; + localparam int unsigned HPDCACHE_DATA_RAM_Y_CUTS = HPDcacheCfg.u.ways/ + HPDcacheCfg.u.dataWaysPerRamWord; + localparam int unsigned HPDCACHE_DATA_RAM_X_CUTS = HPDcacheCfg.u.accessWords; + localparam int unsigned HPDCACHE_ALL_CUTS = HPDCACHE_DATA_RAM_X_CUTS*HPDCACHE_DATA_RAM_Y_CUTS; + + typedef logic [HPDCACHE_DIR_RAM_ADDR_WIDTH-1:0] hpdcache_dir_addr_t; + + typedef logic [HPDCACHE_DATA_RAM_ADDR_WIDTH-1:0] hpdcache_data_ram_addr_t; + typedef hpdcache_data_word_t[HPDcacheCfg.u.dataWaysPerRamWord-1:0] hpdcache_data_ram_data_t; + typedef hpdcache_data_be_t [HPDcacheCfg.u.dataWaysPerRamWord-1:0] hpdcache_data_ram_be_t; + typedef logic [HPDCACHE_DATA_RAM_Y_CUTS-1:0] hpdcache_data_ram_row_idx_t; + typedef logic [$clog2(HPDcacheCfg.u.dataWaysPerRamWord)-1:0] hpdcache_data_ram_way_idx_t; + typedef logic [HPDCACHE_DATA_RAM_X_CUTS-1:0] hpdcache_data_row_enable_t; + typedef hpdcache_data_row_enable_t [HPDCACHE_DATA_RAM_Y_CUTS-1:0] hpdcache_data_enable_t; + + typedef hpdcache_data_ram_data_t + [HPDCACHE_DATA_RAM_Y_CUTS-1:0] + [HPDCACHE_DATA_RAM_X_CUTS-1:0] + hpdcache_data_entry_t; + typedef hpdcache_data_ram_data_t + [HPDCACHE_DATA_RAM_X_CUTS-1:0] + hpdcache_data_row_t; + typedef hpdcache_data_ram_be_t + [HPDCACHE_DATA_RAM_Y_CUTS-1:0] + [HPDCACHE_DATA_RAM_X_CUTS-1:0] + hpdcache_data_be_entry_t; + typedef hpdcache_data_ram_addr_t + [HPDCACHE_DATA_RAM_Y_CUTS-1:0] + [HPDCACHE_DATA_RAM_X_CUTS-1:0] + hpdcache_data_addr_t; + // }}} + + // Definition of functions + // {{{ + + // hpdcache_compute_data_ram_cs + // + // description: This function computes the chip-select signal for data + // RAMs depending on the request size and the word offset + function automatic hpdcache_data_row_enable_t hpdcache_compute_data_ram_cs( + input hpdcache_req_size_t size_i, + input hpdcache_word_t word_i); + + localparam hpdcache_uint32 OffWidth = + HPDcacheCfg.u.accessWords > 1 ? $clog2(HPDcacheCfg.u.accessWords) : 1; + + hpdcache_data_row_enable_t ret; + hpdcache_uint32 off; + + case (size_i) + 3'h0: ret = hpdcache_data_row_enable_t'({hpdcache_ceil_div( 8,HPDcacheCfg.u.wordWidth){1'b1}}); + 3'h1: ret = hpdcache_data_row_enable_t'({hpdcache_ceil_div( 16,HPDcacheCfg.u.wordWidth){1'b1}}); + 3'h2: ret = hpdcache_data_row_enable_t'({hpdcache_ceil_div( 32,HPDcacheCfg.u.wordWidth){1'b1}}); + 3'h3: ret = hpdcache_data_row_enable_t'({hpdcache_ceil_div( 64,HPDcacheCfg.u.wordWidth){1'b1}}); + 3'h4: ret = hpdcache_data_row_enable_t'({hpdcache_ceil_div(128,HPDcacheCfg.u.wordWidth){1'b1}}); + 3'h5: ret = hpdcache_data_row_enable_t'({hpdcache_ceil_div(256,HPDcacheCfg.u.wordWidth){1'b1}}); + default: ret = hpdcache_data_row_enable_t'({hpdcache_ceil_div(512,HPDcacheCfg.u.wordWidth){1'b1}}); + endcase + + off = HPDcacheCfg.u.accessWords > 1 ? hpdcache_uint'(word_i[0 +: OffWidth]) : 0; + return hpdcache_data_row_enable_t'(ret << off); + endfunction + + function automatic hpdcache_data_ram_row_idx_t hpdcache_way_to_data_ram_row( + input hpdcache_way_vector_t way); + hpdcache_data_ram_row_idx_t ret; + for (hpdcache_uint i = 0; i < HPDCACHE_DATA_RAM_Y_CUTS; i++) begin + ret[i] = |way[i*HPDcacheCfg.u.dataWaysPerRamWord +: HPDcacheCfg.u.dataWaysPerRamWord]; + end + return ret; + endfunction + + function automatic hpdcache_data_ram_way_idx_t hpdcache_way_to_data_ram_word( + input hpdcache_way_vector_t way); + for (hpdcache_uint i = 0; i < HPDcacheCfg.u.ways; i++) begin + if (way[i]) return hpdcache_data_ram_way_idx_t'(i % HPDcacheCfg.u.dataWaysPerRamWord); + end + return 0; + endfunction + + function automatic hpdcache_data_ram_addr_t hpdcache_set_to_data_ram_addr( + input hpdcache_set_t set, + input hpdcache_word_t word); + hpdcache_uint ret; + + ret = (hpdcache_uint'(set)*(HPDcacheCfg.u.clWords / HPDcacheCfg.u.accessWords)) + + (hpdcache_uint'(word) / HPDcacheCfg.u.accessWords); + + return hpdcache_data_ram_addr_t'(ret); + endfunction + + function automatic hpdcache_req_user_t req_user_from_cl_user( + input hpdcache_cl_user_t cl_user, + input hpdcache_word_t start_idx + ); + hpdcache_req_user_t req_user; + for (int i = 0; i < HPDcacheCfg.u.reqWords; i++) begin + req_user[i] = cl_user[start_idx + i]; + end + return req_user; + endfunction + + function automatic hpdcache_access_user_t access_user_from_cl_user( + input hpdcache_cl_user_t cl_user, + input hpdcache_word_t start_idx + ); + hpdcache_access_user_t access_user; + for (int i = 0; i < HPDcacheCfg.u.accessWords; i++) begin + access_user[i] = cl_user[start_idx + i]; + end + return access_user; + endfunction + + function automatic hpdcache_cl_user_t cl_user_from_req_user( + input hpdcache_req_user_t req_user, + input hpdcache_word_t start_idx + ); + hpdcache_cl_user_t cl_user = '0; + for (int i = 0; i < HPDcacheCfg.u.reqWords; i++) begin + cl_user[start_idx + i] = req_user[i]; + end + return cl_user; + endfunction + + function automatic hpdcache_cl_user_t cl_user_from_access_user( + input hpdcache_access_user_t access_user, + input hpdcache_word_t start_idx + ); + hpdcache_cl_user_t cl_user = '0; + for (int i = 0; i < HPDcacheCfg.u.accessWords; i++) begin + cl_user[start_idx + i] = access_user[i]; + end + return cl_user; + endfunction + + function automatic logic [HPDcacheCfg.u.clWords-1:0] + user_enable_from_byte_enable( + input hpdcache_access_be_t byte_enable, + input hpdcache_word_t word_idx + ); + logic [HPDcacheCfg.u.clWords-1:0] user_enable = '0; + user_enable[word_idx] = |byte_enable; + return user_enable; + endfunction + + // }}} + + // Definition of internal signals and registers + // {{{ + genvar gen_i, gen_j, gen_k; + + // Directory initialization signals and registers + logic init_q, init_d; + hpdcache_dir_addr_t init_set_q, init_set_d; + hpdcache_way_vector_t init_dir_cs; + + // Directory valid bit vector (one bit per set and way) + hpdcache_set_t dir_req_set_q; + hpdcache_way_vector_t dir_req_way_q; + hpdcache_dir_addr_t dir_addr; + hpdcache_way_vector_t dir_cs; + hpdcache_way_vector_t dir_we; + hpdcache_dir_entry_t [HPDcacheCfg.u.ways-1:0] dir_wentry; + hpdcache_dir_entry_t [HPDcacheCfg.u.ways-1:0] dir_rentry; + logic [HPDcacheCfg.u.ways-1:0] dir_valid; + logic [HPDcacheCfg.u.ways-1:0] dir_wback; + logic [HPDcacheCfg.u.ways-1:0] dir_dirty; + logic [HPDcacheCfg.u.ways-1:0] dir_fetch; + + hpdcache_dir_addr_t user_addr; + hpdcache_way_vector_t user_cs; + hpdcache_way_vector_t user_we; + logic [HPDcacheCfg.u.ways-1:0][HPDcacheCfg.u.clWords-1:0] user_watomenable; + hpdcache_cl_user_t [HPDcacheCfg.u.ways-1:0] user_wentry; + hpdcache_cl_user_t [HPDcacheCfg.u.ways-1:0] user_rentry; + + hpdcache_cl_user_t data_req_read_user_cl, data_flush_read_user_cl; + + hpdcache_data_addr_t data_addr; + hpdcache_data_enable_t data_cs; + hpdcache_data_enable_t data_we; + hpdcache_data_be_entry_t data_wbyteenable; + hpdcache_data_entry_t data_wentry; + hpdcache_data_entry_t data_rentry; + + logic data_write; + logic data_write_enable; + hpdcache_set_t data_write_set; + hpdcache_req_size_t data_write_size; + hpdcache_word_t data_write_word; + hpdcache_access_data_t data_write_data; + hpdcache_access_be_t data_write_be; + + hpdcache_access_data_t data_req_write_data; + hpdcache_access_be_t data_req_write_be; + + hpdcache_access_data_t data_amo_write_data; + hpdcache_access_be_t data_amo_write_be; + + hpdcache_way_vector_t data_way; + + hpdcache_data_ram_row_idx_t data_ram_row; + hpdcache_data_ram_way_idx_t data_ram_word; + + hpdcache_tag_t dir_inval_tag; + hpdcache_set_t dir_inval_set; + hpdcache_way_vector_t dir_inval_hit_way; + // }}} + + // Init FSM + // {{{ + always_comb + begin : init_comb + init_dir_cs = '0; + init_d = init_q; + init_set_d = init_set_q; + + unique case (init_q) + 1'b0: begin + init_d = (hpdcache_uint'(init_set_q) == (HPDcacheCfg.u.sets - 1)); + init_set_d = init_set_q + 1; + init_dir_cs = '1; + end + + 1'b1: begin + init_d = 1'b1; + init_set_d = init_set_q; + end + endcase + end + + assign ready_o = init_q; + + always_ff @(posedge clk_i or negedge rst_ni) + begin : init_ff + if (!rst_ni) begin + init_q <= 1'b0; + init_set_q <= 0; + end else begin + init_q <= init_d; + init_set_q <= init_set_d; + end + end + // }}} + + // Memory arrays + // {{{ + generate + genvar x, y, dir_w; + + for (dir_w = 0; dir_w < int'(HPDcacheCfg.u.ways); dir_w++) begin : gen_dir_sram + // Directory + // + hpdcache_sram #( + .DATA_SIZE (HPDCACHE_DIR_RAM_WIDTH), + .ADDR_SIZE (HPDCACHE_DIR_RAM_ADDR_WIDTH) + ) dir_sram ( + .clk (clk_i), + .rst_n (rst_ni), + .cs (dir_cs[dir_w]), + .we (dir_we[dir_w]), + .addr (dir_addr), + .wdata (dir_wentry[dir_w]), + .rdata (dir_rentry[dir_w]) + ); + + // User bits + // + if (HPDcacheCfg.u.userEn) begin : gen_user_rentry_useren + hpdcache_sram_watomenable #( + .DATA_SIZE ($bits(hpdcache_cl_user_t)), + .ADDR_SIZE (HPDCACHE_DIR_RAM_ADDR_WIDTH), + .ATOM_SIZE (HPDcacheCfg.u.wordUserWidth) + ) user_sram ( + .clk (clk_i), + .rst_n (rst_ni), + .cs (user_cs[dir_w]), + .we (user_we[dir_w]), + .addr (user_addr), + .wdata (user_wentry[dir_w]), + .watomenable (user_watomenable[dir_w]), + .rdata (user_rentry[dir_w]) + ); + end else begin : gen_user_rentry_default + assign user_rentry[dir_w] = '0; + end + end + + // Data + // + for (y = 0; y < int'(HPDCACHE_DATA_RAM_Y_CUTS); y++) begin : gen_data_sram_row + for (x = 0; x < int'(HPDCACHE_DATA_RAM_X_CUTS); x++) begin : gen_data_sram_col + if (HPDcacheCfg.u.dataRamByteEnable) begin : gen_data_sram_wbyteenable + hpdcache_sram_wbyteenable #( + .DATA_SIZE (HPDCACHE_DATA_RAM_WIDTH), + .ADDR_SIZE (HPDCACHE_DATA_RAM_ADDR_WIDTH) + ) data_sram ( + .clk (clk_i), + .rst_n (rst_ni), + .cs (data_cs[y][x]), + .we (data_we[y][x]), + .addr (data_addr[y][x]), + .wdata (data_wentry[y][x]), + .wbyteenable (data_wbyteenable[y][x]), + .rdata (data_rentry[y][x]) + ); + end else begin : gen_data_sram_wmask + hpdcache_data_ram_data_t data_wmask; + + // build the bitmask from the write byte enable signal + always_comb + begin : data_wmask_comb + for (int w = 0; w < HPDcacheCfg.u.dataWaysPerRamWord; w++) begin + for (int b = 0; b < HPDcacheCfg.u.wordWidth/8; b++) begin + data_wmask[w][8*b +: 8] = {8{data_wbyteenable[y][x][w][b]}}; + end + end + end + + hpdcache_sram_wmask #( + .DATA_SIZE (HPDCACHE_DATA_RAM_WIDTH), + .ADDR_SIZE (HPDCACHE_DATA_RAM_ADDR_WIDTH) + ) data_sram ( + .clk (clk_i), + .rst_n (rst_ni), + .cs (data_cs[y][x]), + .we (data_we[y][x]), + .addr (data_addr[y][x]), + .wdata (data_wentry[y][x]), + .wmask (data_wmask), + .rdata (data_rentry[y][x]) + ); + end + end + end + endgenerate + // }}} + + // Directory RAM request mux + // {{{ + assign dir_inval_set = dir_inval_nline_i[0 +: HPDcacheCfg.setWidth]; + assign dir_inval_tag = dir_inval_nline_i[HPDcacheCfg.setWidth +: HPDcacheCfg.tagWidth]; + + always_comb + begin : dir_ctrl_comb + unique case (1'b1) + // Cache directory initialization + ~init_q: begin + dir_addr = init_set_q; + dir_cs = init_dir_cs; + dir_we = '1; + dir_wentry = '0; + end + + // Cache directory match tag -> hit + dir_match_i: begin + dir_addr = dir_match_set_i; + dir_cs = '1; + dir_we = '0; + dir_wentry = '0; + end + + // Cache directory AMO match tag -> hit + dir_amo_match_i: begin + dir_addr = dir_amo_match_set_i; + dir_cs = '1; + dir_we = '0; + dir_wentry = '0; + end + + // Cache directory update + dir_refill_i: begin + dir_addr = dir_refill_set_i; + dir_cs = dir_refill_way_i; + dir_we = dir_refill_way_i; + dir_wentry = {HPDcacheCfg.u.ways{dir_refill_entry_i}}; + end + + // Cache directory invalidate check from the NoC + dir_inval_check_i: begin + dir_addr = dir_inval_set; + dir_cs = '1; + dir_we = '0; + dir_wentry = '0; + end + + // Cache directory invalidate from the NoC + dir_inval_write_i: begin + dir_addr = dir_inval_set; + dir_cs = dir_inval_hit_way; + dir_we = dir_inval_hit_way; + dir_wentry = '0; + end + + // Cache directory CMO match tag + dir_cmo_check_nline_i: begin + dir_addr = dir_cmo_check_nline_set_i; + dir_cs = '1; + dir_we = '0; + dir_wentry = '0; + end + + // Cache directory CMO match tag + dir_cmo_check_entry_i: begin + dir_addr = dir_cmo_check_entry_set_i; + dir_cs = dir_cmo_check_entry_way_i; + dir_we = '0; + dir_wentry = '0; + end + + // Cache directory CMO inval tag + dir_cmo_updt_i: begin + dir_addr = dir_cmo_updt_set_i; + dir_cs = dir_cmo_updt_way_i; + dir_we = dir_cmo_updt_way_i; + + for (hpdcache_uint i = 0; i < HPDcacheCfg.u.ways; i++) begin + dir_wentry[i] = '{ + valid: dir_cmo_updt_valid_i, + wback: dir_cmo_updt_wback_i, + dirty: dir_cmo_updt_dirty_i, + fetch: dir_cmo_updt_fetch_i, + tag : dir_cmo_updt_tag_i + }; + end + end + + // Cache directory match tag -> hit + dir_updt_i: begin + dir_addr = dir_updt_set_i; + dir_cs = dir_updt_way_i; + dir_we = dir_updt_way_i; + + for (hpdcache_uint i = 0; i < HPDcacheCfg.u.ways; i++) begin + dir_wentry[i] = '{ + valid: dir_updt_valid_i, + wback: dir_updt_wback_i, + dirty: dir_updt_dirty_i, + fetch: dir_updt_fetch_i, + tag : dir_updt_tag_i + }; + end + end + + // Do nothing + default: begin + dir_addr = dir_req_set_q; + dir_cs = '0; + dir_we = '0; + dir_wentry = '0; + end + endcase + end + + // }}} + + // Directory hit logic + // {{{ + hpdcache_tag_t [HPDcacheCfg.u.ways-1:0] dir_tags; + hpdcache_way_vector_t req_hit; + hpdcache_way_vector_t amo_hit; + hpdcache_way_vector_t cmo_hit; + hpdcache_way_vector_t inval_hit; + + for (gen_i = 0; gen_i < int'(HPDcacheCfg.u.ways); gen_i++) + begin : gen_dir_match_tag + assign dir_tags[gen_i] = dir_rentry[gen_i].tag; + + assign req_hit[gen_i] = (dir_tags[gen_i] == dir_match_tag_i); + assign amo_hit[gen_i] = (dir_tags[gen_i] == dir_amo_match_tag_i); + assign cmo_hit[gen_i] = (dir_tags[gen_i] == dir_cmo_check_nline_tag_i); + assign inval_hit[gen_i] = (dir_tags[gen_i] == dir_inval_tag); + + assign dir_hit_way_o[gen_i] = dir_valid[gen_i] & req_hit[gen_i]; + assign dir_amo_hit_way_o[gen_i] = dir_valid[gen_i] & amo_hit[gen_i]; + assign dir_cmo_check_nline_hit_way_o[gen_i] = dir_valid[gen_i] & cmo_hit[gen_i]; + assign dir_inval_hit_way[gen_i] = dir_valid[gen_i] & inval_hit[gen_i]; + end + + hpdcache_mux #( + .NINPUT (HPDcacheCfg.u.ways), + .DATA_WIDTH (HPDcacheCfg.tagWidth), + .ONE_HOT_SEL (1'b1) + ) hit_tag_mux_i( + .data_i (dir_tags), + .sel_i (dir_hit_way_o), + .data_o (dir_hit_tag_o) + ); + + assign dir_hit_wback_o = |(dir_hit_way_o & dir_wback); + assign dir_hit_dirty_o = |(dir_hit_way_o & dir_dirty); + assign dir_hit_fetch_o = |(dir_hit_way_o & dir_fetch); + + assign dir_cmo_check_nline_wback_o = |(dir_cmo_check_nline_hit_way_o & dir_wback); + assign dir_cmo_check_nline_dirty_o = |(dir_cmo_check_nline_hit_way_o & dir_dirty); + assign dir_cmo_check_entry_valid_o = |(dir_req_way_q & dir_valid); + assign dir_cmo_check_entry_wback_o = |(dir_req_way_q & dir_wback); + assign dir_cmo_check_entry_dirty_o = |(dir_req_way_q & dir_dirty); + hpdcache_mux #( + .NINPUT (HPDcacheCfg.u.ways), + .DATA_WIDTH (HPDcacheCfg.tagWidth), + .ONE_HOT_SEL (1'b1) + ) flush_tag_mux_i( + .data_i (dir_tags), + .sel_i (dir_req_way_q), + .data_o (dir_cmo_check_entry_tag_o) + ); + + assign dir_victim_valid_o = |(dir_victim_way_o & dir_valid); + assign dir_victim_wback_o = |(dir_victim_way_o & dir_wback); + assign dir_victim_dirty_o = |(dir_victim_way_o & dir_dirty); + hpdcache_mux #( + .NINPUT (HPDcacheCfg.u.ways), + .DATA_WIDTH (HPDcacheCfg.tagWidth), + .ONE_HOT_SEL (1'b1) + ) victim_tag_mux_i( + .data_i (dir_tags), + .sel_i (dir_victim_way_o), + .data_o (dir_victim_tag_o) + ); + + assign dir_inval_hit_o = |dir_inval_hit_way; + // }}} + + // Directory victim select logic + // {{{ + logic updt_sel_victim; + hpdcache_way_vector_t updt_sel_victim_way; + hpdcache_set_t updt_sel_victim_set; + + assign updt_sel_victim = dir_updt_sel_victim_i | + dir_refill_updt_sel_victim_i | + dir_amo_updt_sel_victim_i; + + assign updt_sel_victim_way = dir_updt_sel_victim_i ? dir_hit_way_o : + dir_refill_updt_sel_victim_i ? dir_refill_way_i : + dir_amo_hit_way_o; + + assign updt_sel_victim_set = dir_refill_updt_sel_victim_i ? dir_refill_set_i : + dir_req_set_q; + + for (gen_i = 0; gen_i < HPDcacheCfg.u.ways; gen_i++) begin : gen_dir_valid_bv + assign dir_valid[gen_i] = dir_rentry[gen_i].valid; + assign dir_wback[gen_i] = dir_rentry[gen_i].wback; + assign dir_dirty[gen_i] = dir_rentry[gen_i].dirty; + assign dir_fetch[gen_i] = dir_rentry[gen_i].fetch; + end + + + hpdcache_victim_sel #( + .HPDcacheCfg (HPDcacheCfg), + .hpdcache_set_t (hpdcache_set_t), + .hpdcache_way_vector_t (hpdcache_way_vector_t) + ) victim_sel_i( + .clk_i, + .rst_ni, + + .updt_i (updt_sel_victim), + .updt_set_i (updt_sel_victim_set), + .updt_way_i (updt_sel_victim_way), + + .sel_victim_i (dir_victim_sel_i), + .sel_dir_valid_i (dir_valid), + .sel_dir_wback_i (dir_wback), + .sel_dir_dirty_i (dir_dirty), + .sel_dir_fetch_i (dir_fetch), + .sel_victim_set_i (dir_victim_set_i), + .sel_victim_way_o (dir_victim_way_o) + ); + // }}} + + // Data RAM request multiplexor + // {{{ + + // Upsize the request interface to match the maximum access width of the data RAM + if (HPDCACHE_DATA_REQ_RATIO > 1) begin : gen_upsize_data_req_write + // demux request DATA + assign data_req_write_data = {HPDCACHE_DATA_REQ_RATIO{data_req_write_data_i}}; + + // demux request BE + hpdcache_demux #( + .NOUTPUT (HPDCACHE_DATA_REQ_RATIO), + .DATA_WIDTH (HPDcacheCfg.reqDataWidth/8), + .ONE_HOT_SEL (1'b0) + ) data_req_write_be_demux_i ( + .data_i (data_req_write_be_i), + .sel_i (data_req_write_word_i[HPDcacheCfg.reqWordIdxWidth +: + $clog2(HPDCACHE_DATA_REQ_RATIO)]), + .data_o (data_req_write_be) + ); + end else begin : gen_eqsize_data_req_write + assign data_req_write_data = data_req_write_data_i; + assign data_req_write_be = data_req_write_be_i; + end + + // Upsize the AMO data interface to match the maximum access width of the data RAM + if (HPDCACHE_DATA_REQ_RATIO > 1) begin : gen_upsize_amo_req_write + assign data_amo_write_data = {HPDCACHE_DATA_REQ_RATIO{data_amo_write_data_i}}; + + hpdcache_demux #( + .NOUTPUT (HPDCACHE_DATA_REQ_RATIO), + .DATA_WIDTH (HPDcacheCfg.reqDataWidth/8), + .ONE_HOT_SEL (1'b0) + ) amo_be_demux_i( + .data_i (data_amo_write_be_i), + .sel_i (data_amo_write_word_i[HPDcacheCfg.reqWordIdxWidth +: + $clog2(HPDCACHE_DATA_REQ_RATIO)]), + .data_o (data_amo_write_be) + ); + end else begin : gen_eqsize_amo_req_write + assign data_amo_write_data = data_amo_write_data_i; + assign data_amo_write_be = data_amo_write_be_i; + end + + // Multiplex between data write requests + always_comb + begin : data_write_comb + data_write = 1'b0; + data_write_enable = 1'b0; + data_write_set = '0; + data_write_size = '0; + data_write_word = '0; + data_write_data = '0; + data_write_be = '0; + + user_watomenable = '0; + user_wentry = '0; + + unique case (1'b1) + data_refill_i: begin + data_write = 1'b1; + data_write_enable = 1'b1; + data_write_set = data_refill_set_i; + data_write_size = hpdcache_req_size_t'($clog2(HPDcacheCfg.accessWidth/8)); + data_write_word = data_refill_word_i; + data_write_data = data_refill_data_i; + data_write_be = '1; + + if (HPDcacheCfg.u.userEn) begin + user_watomenable = {HPDcacheCfg.u.ways{HPDcacheCfg.u.clWords'(1) << data_refill_word_i}}; + user_wentry = {HPDcacheCfg.u.ways{cl_user_from_access_user(data_refill_user_i, data_refill_word_i)}}; + end + end + + data_req_write_i: begin + data_write = 1'b1; + data_write_enable = data_req_write_enable_i; + data_write_set = data_req_write_set_i; + data_write_size = data_req_write_size_i; + data_write_word = data_req_write_word_i; + data_write_data = data_req_write_data; + data_write_be = data_req_write_be; + + if (HPDcacheCfg.u.userEn) begin + user_watomenable = {HPDcacheCfg.u.ways{user_enable_from_byte_enable(data_req_write_be, data_req_write_word_i)}}; + user_wentry = {HPDcacheCfg.u.ways{cl_user_from_req_user(data_req_write_user_i, data_req_write_word_i)}}; + end + end + + data_amo_write_i: begin + data_write = 1'b1; + data_write_enable = data_amo_write_enable_i; + data_write_set = data_amo_write_set_i; + data_write_size = data_amo_write_size_i; + data_write_word = data_amo_write_word_i; + data_write_data = data_amo_write_data; + data_write_be = data_amo_write_be; + + if (HPDcacheCfg.u.userEn) begin + user_watomenable = {HPDcacheCfg.u.ways{user_enable_from_byte_enable(data_amo_write_be, data_amo_write_word_i)}}; + user_wentry = {HPDcacheCfg.u.ways{cl_user_from_req_user(data_amo_write_user_i, data_amo_write_word_i)}}; + end + end + + default: begin + end + endcase + end + + // Multiplex between read and write access on the data RAM + assign data_way = data_refill_i ? data_refill_way_i : + data_flush_read_i ? data_flush_read_way_i : + data_amo_write_i ? dir_amo_hit_way_o : + data_req_read_i ? data_req_read_way_i : + data_req_write_way_i; + + // Decode way index + assign data_ram_word = hpdcache_way_to_data_ram_word(data_way); + assign data_ram_row = hpdcache_way_to_data_ram_row(data_way); + assign user_we = (HPDcacheCfg.u.userEn && data_write) ? data_way : '0; + if (HPDcacheCfg.u.userEn) begin : gen_user_addr_useren + assign user_addr = data_refill_i ? data_refill_set_i : + data_flush_read_i ? data_flush_read_set_i : + data_amo_write_i ? data_amo_write_set_i : + data_req_read_i ? data_req_read_set_i : + /*data_req_write_i*/ data_req_write_set_i ; + end else begin : gen_user_addr_default + assign user_addr = '0; + end + + always_comb + begin : data_ctrl_comb + data_addr = '0; + data_cs = '0; + data_we = '0; + data_wbyteenable = '0; + data_wentry = '0; + + user_cs = '0; + + unique case (1'b1) + // Select data read inputs + data_req_read_i: begin + data_addr = {HPDCACHE_ALL_CUTS{ + hpdcache_set_to_data_ram_addr(data_req_read_set_i, data_req_read_word_i)} + }; + + for (int unsigned i = 0; i < HPDCACHE_DATA_RAM_Y_CUTS; i++) begin + data_cs[i] = hpdcache_compute_data_ram_cs(data_req_read_size_i, + data_req_read_word_i); + end + + if (HPDcacheCfg.u.userEn) user_cs = '1; + end + + // Select data flush read inputs + data_flush_read_i: begin + data_addr = {HPDCACHE_ALL_CUTS{ + hpdcache_set_to_data_ram_addr(data_flush_read_set_i, data_flush_read_word_i)} + }; + for (int unsigned i = 0; i < HPDCACHE_DATA_RAM_Y_CUTS; i++) begin + data_cs[i] = data_ram_row[i] ? '1 : '0; + end + + if(HPDcacheCfg.u.userEn) user_cs = '1; + end + + // Select data write inputs + data_write: begin + data_addr = {HPDCACHE_ALL_CUTS{hpdcache_set_to_data_ram_addr(data_write_set, + data_write_word)}}; + + for (int unsigned i = 0; i < HPDCACHE_DATA_RAM_Y_CUTS; i++) begin + for (int unsigned j = 0; j < HPDCACHE_DATA_RAM_X_CUTS; j++) begin + data_wentry[i][j] = {HPDcacheCfg.u.dataWaysPerRamWord{data_write_data[j]}}; + end + end + + for (int unsigned i = 0; i < HPDCACHE_DATA_RAM_Y_CUTS; i++) begin + data_cs[i] = hpdcache_compute_data_ram_cs(data_write_size, data_write_word); + + if (data_ram_row[i]) begin + data_we[i] = data_write_enable ? data_cs[i] : '0; + end + + // Build the write mask + for (int unsigned j = 0; j < HPDcacheCfg.u.accessWords; j++) begin + for (int unsigned k = 0; k < HPDcacheCfg.u.dataWaysPerRamWord; k++) begin + data_wbyteenable[i][j][k] = (k == hpdcache_uint'(data_ram_word)) ? + data_write_be[j] : '0; + end + end + end + + if (HPDcacheCfg.u.userEn) user_cs = data_way; + end + + default: begin + // Do nothing + end + endcase + end + // }}} + + // Data RAM read data multiplexor + // {{{ + hpdcache_req_data_t [HPDCACHE_DATA_REQ_RATIO-1:0][HPDcacheCfg.u.ways-1:0] data_read_words; + hpdcache_req_data_t [HPDcacheCfg.u.ways-1:0] data_read_req_word; + + // Organize the read data by words (all ways for the same word are contiguous) + for (gen_i = 0; gen_i < int'(HPDCACHE_DATA_REQ_RATIO); gen_i++) begin : gen_data_rentry_i + for (gen_j = 0; gen_j < int'(HPDcacheCfg.u.ways); gen_j++) begin : gen_data_rentry_j + for (gen_k = 0; gen_k < int'(HPDcacheCfg.u.reqWords); gen_k++) begin : gen_data_rentry_k + assign data_read_words[gen_i][gen_j][gen_k] = + data_rentry[(gen_j / HPDcacheCfg.u.dataWaysPerRamWord)] + [(gen_i * HPDcacheCfg.u.reqWords) + gen_k] + [(gen_j % HPDcacheCfg.u.dataWaysPerRamWord)]; + end + end + end + + // latch requested word index + hpdcache_word_t data_req_read_word_q; + always_ff @(posedge clk_i) + begin : data_req_read_word_ff + data_req_read_word_q <= data_req_read_word_i; + end + + // Mux the data according to the access word + if (HPDCACHE_DATA_REQ_RATIO > 1) begin : gen_req_width_lt_ram_width + hpdcache_mux #( + .NINPUT (HPDCACHE_DATA_REQ_RATIO), + .DATA_WIDTH (HPDcacheCfg.reqDataWidth*HPDcacheCfg.u.ways) + ) data_read_req_word_mux_i( + .data_i (data_read_words), + .sel_i (data_req_read_word_q[HPDcacheCfg.reqWordIdxWidth +: $clog2(HPDCACHE_DATA_REQ_RATIO)]), + .data_o (data_read_req_word) + ); + end + // Request data interface width is equal to the data RAM width + else begin : gen_req_width_eq_ram_width + assign data_read_req_word = data_read_words; + end + + if (HPDcacheCfg.u.userEn) begin : gen_req_read_user_o_useren + // Mux the user bits / data according to the hit way + hpdcache_mux #( + .NINPUT (HPDcacheCfg.u.ways), + .DATA_WIDTH ($bits(hpdcache_cl_user_t)), + .ONE_HOT_SEL (1'b1) + ) user_read_req_word_way_mux_i( + .data_i (user_rentry), + .sel_i (data_req_read_way_i), + .data_o (data_req_read_user_cl) + ); + assign data_req_read_user_o = req_user_from_cl_user(data_req_read_user_cl, data_req_read_word_q); + end else begin : gen_req_read_user_o_default + assign data_req_read_user_cl = '0; + assign data_req_read_user_o = '0; + end + hpdcache_mux #( + .NINPUT (HPDcacheCfg.u.ways), + .DATA_WIDTH (HPDcacheCfg.reqDataWidth), + .ONE_HOT_SEL (1'b1) + ) data_read_req_word_way_mux_i( + .data_i (data_read_req_word), + .sel_i (data_req_read_way_i), + .data_o (data_req_read_data_o) + ); + + + // Delay the accessed set for checking the tag from the directory in the + // next cycle (hit logic) + always_ff @(posedge clk_i) + begin : req_read_ff + if (dir_match_i || dir_amo_match_i || dir_cmo_check_nline_i || dir_inval_check_i) begin + dir_req_set_q <= dir_addr; + end + if (dir_cmo_check_entry_i) begin + dir_req_way_q <= dir_cmo_check_entry_way_i; + end + end + // }}} + + // Select flush data + // {{{ + hpdcache_data_ram_data_t + [HPDcacheCfg.u.accessWords-1:0] + data_flush_row_data; + + hpdcache_data_word_t + [HPDcacheCfg.u.dataWaysPerRamWord-1:0] + [HPDcacheCfg.u.accessWords-1:0] + data_flush_ways_data; + + hpdcache_data_ram_row_idx_t data_flush_row_index_q; + logic [HPDcacheCfg.u.dataWaysPerRamWord-1:0] data_flush_read_way; + hpdcache_word_t data_flush_read_word_q; + + always_ff @(posedge clk_i) + begin : data_flush_row_index_ff + if (data_flush_read_i) begin + data_flush_row_index_q <= data_ram_row; + data_flush_read_word_q <= data_flush_read_word_i; + end + end + + hpdcache_mux #( + .NINPUT (HPDcacheCfg.u.ways/HPDcacheCfg.u.dataWaysPerRamWord), + .DATA_WIDTH (HPDcacheCfg.accessWidth*HPDcacheCfg.u.dataWaysPerRamWord), + .ONE_HOT_SEL (1'b1) + ) data_read_flush_mux_row_i( + .data_i (data_rentry), + .sel_i (data_flush_row_index_q), + .data_o (data_flush_row_data) + ); + + for (gen_i = 0; gen_i < HPDcacheCfg.u.dataWaysPerRamWord; gen_i++) + begin : gen_data_flush_way_i + for (gen_j = 0; gen_j < HPDcacheCfg.u.accessWords; gen_j++) + begin : gen_data_flush_way_j + assign data_flush_ways_data[gen_i][gen_j] = data_flush_row_data[gen_j][gen_i]; + end + end + + always_comb + begin : decode_flush_read_way_comb + data_flush_read_way = '0; + for (int i = 0; i < HPDcacheCfg.u.dataWaysPerRamWord; i++) begin + for (int j = 0; j < HPDcacheCfg.u.ways; j += HPDcacheCfg.u.dataWaysPerRamWord) begin + data_flush_read_way[i] |= data_flush_read_way_i[i + j]; + end + end + end + + if (HPDcacheCfg.u.userEn) begin : gen_data_flush_read_user_o_useren + hpdcache_mux #( + .NINPUT (HPDcacheCfg.u.ways), + .DATA_WIDTH ($bits(hpdcache_cl_user_t)), + .ONE_HOT_SEL (1'b1) + ) user_read_flush_mux_way_i( + .data_i (user_rentry), + .sel_i (data_flush_read_way_i), + .data_o (data_flush_read_user_cl) + ); + assign data_flush_read_user_o = access_user_from_cl_user(data_flush_read_user_cl, data_flush_read_word_q); + end else begin : gen_data_flush_read_user_o_default + assign data_flush_read_user_cl = '0; + assign data_flush_read_user_o = '0; + end + hpdcache_mux #( + .NINPUT (HPDcacheCfg.u.dataWaysPerRamWord), + .DATA_WIDTH (HPDcacheCfg.accessWidth), + .ONE_HOT_SEL (1'b1) + ) data_read_flush_mux_way_i( + .data_i (data_flush_ways_data), + .sel_i (data_flush_read_way), + .data_o (data_flush_read_data_o) + ); + // }}} + + // Assertions + // {{{ +`ifndef HPDCACHE_ASSERT_OFF + for (gen_i = 0; gen_i < HPDcacheCfg.u.ways; gen_i++) begin : gen_check_dirty_state + check_dirty_state: assert property (@(posedge clk_i) + disable iff ((rst_ni !== 1'b1) || (init_q !== 1'b1)) + (dir_cs[gen_i] & ~dir_we[gen_i]) |=> (dir_dirty[gen_i] |-> dir_valid[gen_i])) else + $error("hpdcache_memctrl: wrong directory state - dirty but not valid"); + end + + concurrent_dir_access_assert: assert property (@(posedge clk_i) disable iff (rst_ni !== 1'b1) + $onehot0({dir_match_i, + dir_amo_match_i, + dir_refill_i, + dir_inval_check_i, + dir_inval_write_i, + dir_cmo_check_nline_i, + dir_cmo_check_entry_i, + dir_cmo_updt_i, + dir_updt_i})) else + $error("hpdcache_memctrl: more than one process is accessing the cache directory"); + + concurrent_data_access_assert: assert property (@(posedge clk_i) disable iff (rst_ni !== 1'b1) + $onehot0({data_req_read_i, + data_req_write_i, + data_amo_write_i, + data_refill_i, + data_flush_read_i})) else + $error("hpdcache_memctrl: more than one process is accessing the cache data"); +`endif + // }}} +endmodule diff --git a/hw/vendor/hpdcache/rtl/src/hpdcache_miss_handler.sv b/hw/vendor/hpdcache/rtl/src/hpdcache_miss_handler.sv new file mode 100644 index 00000000..f4a6eaea --- /dev/null +++ b/hw/vendor/hpdcache/rtl/src/hpdcache_miss_handler.sv @@ -0,0 +1,953 @@ +/* + * Copyright 2023 CEA* + * *Commissariat a l'Energie Atomique et aux Energies Alternatives (CEA) + * Copyright 2025 Inria, Universite Grenoble-Alpes, TIMA + * + * SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 + * + * Licensed under the Solderpad Hardware License v 2.1 (the “License”); you + * may not use this file except in compliance with the License, or, at your + * option, the Apache License version 2.0. You may obtain a copy of the + * License at + * + * https://solderpad.org/licenses/SHL-2.1/ + * + * Unless required by applicable law or agreed to in writing, any work + * distributed under the License is distributed on an “AS IS” BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +/* + * Authors : Cesar Fuguet + * Creation Date : April, 2021 + * Description : HPDcache Miss Handler + * History : + */ +module hpdcache_miss_handler +// {{{ +import hpdcache_pkg::*; +// Parameters +// {{{ +#( + parameter hpdcache_cfg_t HPDcacheCfg = '0, + + parameter type hpdcache_nline_t = logic, + parameter type hpdcache_set_t = logic, + parameter type hpdcache_tag_t = logic, + parameter type hpdcache_word_t = logic, + parameter type hpdcache_cl_user_t = logic, + + parameter type hpdcache_way_vector_t = logic, + parameter type hpdcache_way_t = logic, + + parameter type hpdcache_dir_entry_t = logic, + + parameter type hpdcache_refill_data_t = logic, + parameter type hpdcache_refill_user_t = logic, + + parameter type hpdcache_req_data_t = logic, + parameter type hpdcache_req_user_t = logic, + parameter type hpdcache_req_be_t = logic, + parameter type hpdcache_req_offset_t = logic, + parameter type hpdcache_req_sid_t = logic, + parameter type hpdcache_req_tid_t = logic, + + parameter type hpdcache_req_t = logic, + parameter type hpdcache_rsp_t = logic, + + parameter type hpdcache_mem_id_t = logic, + parameter type hpdcache_mem_req_t = logic, + parameter type hpdcache_mem_resp_r_t = logic +) +// }}} + +// Ports +// {{{ +( + input logic clk_i, + input logic rst_ni, + + // Global control signals + // {{{ + output logic mshr_empty_o, + output logic mshr_full_o, + // }}} + + // Configuration signals + // {{{ + input logic cfg_prefetch_updt_sel_victim_i, + // }}} + + // CHECK interface + // {{{ + input logic mshr_check_i, + input hpdcache_req_offset_t mshr_check_offset_i, + input hpdcache_nline_t mshr_check_nline_i, + output logic mshr_check_hit_o, + // }}} + + // MISS interface + // {{{ + // MISS request interface + output logic mshr_alloc_ready_o, + input logic mshr_alloc_i, + input logic mshr_alloc_cs_i, + input hpdcache_nline_t mshr_alloc_nline_i, + output logic mshr_alloc_full_o, + output logic mshr_alloc_cbuf_full_o, + input hpdcache_req_tid_t mshr_alloc_tid_i, + input hpdcache_req_sid_t mshr_alloc_sid_i, + input hpdcache_word_t mshr_alloc_word_i, + input hpdcache_way_vector_t mshr_alloc_victim_way_i, + input logic mshr_alloc_need_rsp_i, + input logic mshr_alloc_is_prefetch_i, + input logic mshr_alloc_wback_i, + input logic mshr_alloc_dirty_i, + input hpdcache_req_data_t mshr_alloc_wdata_i, + input hpdcache_req_user_t mshr_alloc_wuser_i, + input hpdcache_req_be_t mshr_alloc_be_i, + + // REFILL MISS / Invalidation interface + input logic refill_req_ready_i, + output logic refill_req_valid_o, + output logic refill_is_error_o, + output logic refill_busy_o, + output logic refill_updt_sel_victim_o, + output hpdcache_set_t refill_set_o, + output hpdcache_way_vector_t refill_way_o, + output hpdcache_dir_entry_t refill_dir_entry_o, + output logic refill_write_dir_o, + output logic refill_write_data_o, + output hpdcache_refill_user_t refill_user_o, + output hpdcache_refill_data_t refill_data_o, + output hpdcache_word_t refill_word_o, + output hpdcache_nline_t refill_nline_o, + output logic refill_updt_rtab_o, + + output logic inval_check_dir_o, + output logic inval_write_dir_o, + output hpdcache_nline_t inval_nline_o, + input logic inval_hit_i, + + // REFILL core response interface + output logic refill_core_rsp_valid_o, + output hpdcache_rsp_t refill_core_rsp_o, + // }}} + + // MEMORY interface + // {{{ + input logic mem_req_ready_i, + output logic mem_req_valid_o, + output hpdcache_mem_req_t mem_req_o, + + output logic mem_resp_ready_o, + input logic mem_resp_valid_i, + input hpdcache_mem_resp_r_t mem_resp_i, + input logic mem_resp_inval_i, + input hpdcache_nline_t mem_resp_inval_nline_i + // }}} +); +// }}} + + // Declaration of constants and types + // {{{ + localparam hpdcache_uint REFILL_REQ_RATIO = HPDcacheCfg.u.accessWords / + HPDcacheCfg.u.reqWords; + localparam hpdcache_uint REFILL_LAST_CHUNK_WORD = HPDcacheCfg.u.clWords - + HPDcacheCfg.u.accessWords; + localparam hpdcache_uint ACCESS_MEM_RATIO = hpdcache_ceil_div(HPDcacheCfg.accessWidth, HPDcacheCfg.u.memDataWidth); + + function automatic logic [HPDcacheCfg.u.wordUserWidth-1:0] and_reduce_user ( + input logic [(HPDcacheCfg.u.wordUserWidth*ACCESS_MEM_RATIO)-1:0] ruser + ); + logic [HPDcacheCfg.u.wordUserWidth-1:0] result; + int i, j; + result = '1; + for (j = 0; j < ACCESS_MEM_RATIO; j++) begin + for (i = 0; i < HPDcacheCfg.u.wordUserWidth; i++) begin + result[i] &= ruser[j*HPDcacheCfg.u.wordUserWidth + i]; + end + end + return result; + endfunction + + typedef enum logic { + MISS_REQ_IDLE = 1'b0, + MISS_REQ_SEND = 1'b1 + } miss_req_fsm_e; + + typedef enum { + REFILL_IDLE, + REFILL_WRITE, + REFILL_WRITE_DIR, + REFILL_INVAL + } refill_fsm_e; + + typedef struct packed { + hpdcache_mem_error_e r_error; + hpdcache_mem_id_t r_id; + logic is_inval; + hpdcache_nline_t inval_nline; + } mem_resp_metadata_t; + + typedef logic [HPDcacheCfg.mshrWayWidth-1:0] mshr_way_t; + typedef logic [HPDcacheCfg.mshrSetWidth-1:0] mshr_set_t; + + typedef logic [HPDcacheCfg.cbufEntryWidth-1:0] cbuf_id_t; + // }}} + + // Declaration of internal signals and registers + // {{{ + miss_req_fsm_e miss_req_fsm_q, miss_req_fsm_d; + mshr_way_t mshr_alloc_way_q, mshr_alloc_way_d; + hpdcache_nline_t mshr_alloc_nline_q; + + refill_fsm_e refill_fsm_q, refill_fsm_d; + hpdcache_set_t refill_set_q; + hpdcache_tag_t refill_tag_q; + hpdcache_way_t refill_way_q; + hpdcache_req_sid_t refill_sid_q; + hpdcache_req_tid_t refill_tid_q; + hpdcache_word_t refill_cnt_q, refill_cnt_d; + logic refill_need_rsp_q; + logic refill_is_prefetch_q; + logic refill_wback_q; + logic refill_dirty_q; + hpdcache_req_data_t refill_dirty_wdata_q; + hpdcache_req_user_t refill_dirty_wuser_q; + hpdcache_req_be_t refill_dirty_be_q; + hpdcache_word_t refill_core_rsp_word_q; + hpdcache_way_t refill_way; + logic refill_dirty; + logic refill_dirty_valid; + hpdcache_req_data_t refill_dirty_wdata; + hpdcache_req_user_t refill_dirty_wuser; + hpdcache_req_be_t refill_dirty_be; + + mem_resp_metadata_t refill_fifo_resp_meta_wdata, refill_fifo_resp_meta_rdata; + logic refill_fifo_resp_meta_w, refill_fifo_resp_meta_wok; + logic refill_fifo_resp_meta_r, refill_fifo_resp_meta_rok; + + logic refill_fifo_resp_data_w, refill_fifo_resp_data_wok; + hpdcache_refill_data_t refill_fifo_resp_data_rdata; + logic [(HPDcacheCfg.u.wordUserWidth*ACCESS_MEM_RATIO)-1:0] refill_fifo_resp_data_ruser; + logic refill_fifo_resp_data_r; + + logic refill_core_rsp_valid; + hpdcache_req_data_t refill_core_rsp_rdata; + hpdcache_req_user_t refill_core_rsp_ruser; + hpdcache_req_sid_t refill_core_rsp_sid; + hpdcache_req_tid_t refill_core_rsp_tid; + logic refill_core_rsp_error; + hpdcache_word_t refill_core_rsp_word; + hpdcache_rsp_t refill_core_rsp; + + hpdcache_set_t mshr_check_set; + hpdcache_tag_t mshr_check_tag; + logic mshr_alloc; + logic mshr_alloc_cs; + hpdcache_way_t mshr_alloc_victim_way; + cbuf_id_t mshr_alloc_cbuf_id; + logic mshr_ack; + logic mshr_ack_cs; + mshr_set_t mshr_ack_set; + mshr_way_t mshr_ack_way; + hpdcache_set_t mshr_ack_cache_set; + hpdcache_way_t mshr_ack_cache_way; + hpdcache_tag_t mshr_ack_cache_tag; + hpdcache_req_sid_t mshr_ack_src_id; + hpdcache_req_tid_t mshr_ack_req_id; + hpdcache_word_t mshr_ack_word; + logic mshr_ack_need_rsp; + logic mshr_ack_is_prefetch; + logic mshr_ack_wback; + logic mshr_ack_dirty; + cbuf_id_t mshr_ack_cbuf_id; + hpdcache_req_data_t mshr_ack_wdata; + hpdcache_req_user_t mshr_ack_wuser; + hpdcache_req_be_t mshr_ack_be; + logic mshr_empty; + // }}} + + // Miss Request FSM + // {{{ + always_comb + begin : miss_req_fsm_comb + mshr_alloc_ready_o = 1'b0; + mshr_alloc = 1'b0; + mshr_alloc_cs = 1'b0; + mem_req_valid_o = 1'b0; + + miss_req_fsm_d = miss_req_fsm_q; + + unique case (miss_req_fsm_q) + MISS_REQ_IDLE: begin + mshr_alloc_ready_o = 1'b1; + mshr_alloc = mshr_alloc_i; + mshr_alloc_cs = mshr_alloc_cs_i; + if (mshr_alloc_i) begin + miss_req_fsm_d = MISS_REQ_SEND; + end else begin + miss_req_fsm_d = MISS_REQ_IDLE; + end + end + MISS_REQ_SEND: begin + mem_req_valid_o = 1'b1; + if (mem_req_ready_i) begin + miss_req_fsm_d = MISS_REQ_IDLE; + end else begin + miss_req_fsm_d = MISS_REQ_SEND; + end + end + endcase + end + + localparam hpdcache_uint REFILL_REQ_SIZE = $clog2(HPDcacheCfg.u.memDataWidth / 8); + localparam hpdcache_uint REFILL_REQ_LEN = HPDcacheCfg.clWidth / HPDcacheCfg.u.memDataWidth; + + assign mem_req_o.mem_req_addr = {mshr_alloc_nline_q, {HPDcacheCfg.clOffsetWidth{1'b0}} }; + assign mem_req_o.mem_req_len = hpdcache_mem_len_t'(REFILL_REQ_LEN-1); + assign mem_req_o.mem_req_size = hpdcache_mem_size_t'(REFILL_REQ_SIZE); + assign mem_req_o.mem_req_command = HPDCACHE_MEM_READ; + assign mem_req_o.mem_req_atomic = HPDCACHE_MEM_ATOMIC_ADD; + assign mem_req_o.mem_req_cacheable = 1'b1; + + if ((HPDcacheCfg.u.mshrSets > 1) && (HPDcacheCfg.u.mshrWays > 1)) + begin : gen_mem_id_mshr_sets_and_ways_gt_1 + assign mem_req_o.mem_req_id = hpdcache_mem_id_t'({ + mshr_alloc_way_q, mshr_alloc_nline_q[0 +: HPDcacheCfg.mshrSetWidth]}); + end else if (HPDcacheCfg.u.mshrSets > 1) begin : gen_mem_id_mshr_sets_gt_1 + assign mem_req_o.mem_req_id = hpdcache_mem_id_t'( + mshr_alloc_nline_q[0 +: HPDcacheCfg.mshrSetWidth]); + end else if (HPDcacheCfg.u.mshrWays > 1) begin : gen_mem_id_mshr_ways_gt_1 + assign mem_req_o.mem_req_id = hpdcache_mem_id_t'(mshr_alloc_way_q); + end else begin : gen_mem_id_mshr_sets_and_ways_eq_1 + assign mem_req_o.mem_req_id = '0; + end + + always_ff @(posedge clk_i) + begin : miss_req_fsm_internal_ff + if (mshr_alloc) begin + mshr_alloc_way_q <= mshr_alloc_way_d; + mshr_alloc_nline_q <= mshr_alloc_nline_i; + end + end + + always_ff @(posedge clk_i or negedge rst_ni) + begin : miss_req_fsm_ff + if (!rst_ni) begin + miss_req_fsm_q <= MISS_REQ_IDLE; + end else begin + miss_req_fsm_q <= miss_req_fsm_d; + end + end + // }}} + + // Refill FSM + // {{{ + + // ask permission to the refill arbiter if there is a pending refill + assign refill_req_valid_o = refill_fsm_q == REFILL_IDLE ? refill_fifo_resp_meta_rok : 1'b0; + + always_comb + begin : miss_resp_fsm_comb + refill_updt_sel_victim_o = 1'b0; + refill_set_o = '0; + refill_way = '0; + refill_dirty = 1'b0; + refill_write_dir_o = 1'b0; + refill_write_data_o = 1'b0; + refill_updt_rtab_o = 1'b0; + refill_cnt_d = refill_cnt_q; + + refill_dirty_wdata = refill_dirty_wdata_q; + refill_dirty_be = refill_dirty_be_q; + refill_dirty_valid = 1'b0; + refill_dirty_wuser = HPDcacheCfg.u.userEn ? refill_dirty_wuser_q : '0; + + inval_check_dir_o = 1'b0; + inval_write_dir_o = 1'b0; + + refill_core_rsp_valid = 1'b0; + refill_core_rsp_sid = '0; + refill_core_rsp_tid = '0; + refill_core_rsp_error = 1'b0; + refill_core_rsp_word = '0; + + refill_fifo_resp_meta_r = 1'b0; + refill_fifo_resp_data_r = 1'b0; + + mshr_ack_cs = 1'b0; + mshr_ack = 1'b0; + + refill_fsm_d = refill_fsm_q; + + case (refill_fsm_q) + // Wait for refill responses + // {{{ + REFILL_IDLE: begin + if (refill_fifo_resp_meta_rok) begin + // anticipate the activation of the MSHR independently of the grant signal from + // the refill arbiter. This is to avoid the introduction of unnecessary timing + // paths (however there could be a minor augmentation of the power consumption) + mshr_ack_cs = ~refill_fifo_resp_meta_rdata.is_inval; + + // if the permission is granted, start refilling + if (refill_req_ready_i) begin + refill_set_o = mshr_ack_cache_set; + + if (refill_fifo_resp_meta_rdata.is_inval) begin + // check for a match with the line being invalidated in the cache dir + inval_check_dir_o = 1'b1; + + refill_fsm_d = REFILL_INVAL; + end else begin + // read the MSHR and reset the valid bit for the corresponding entry + mshr_ack = ~refill_fifo_resp_meta_rdata.is_inval; + + // initialize the counter for refill words + refill_cnt_d = 0; + refill_fsm_d = REFILL_WRITE; + end + end + end + end + // }}} + + // Write refill data into the cache + // {{{ + REFILL_WRITE: begin + automatic logic is_prefetch; + automatic hpdcache_uint core_rsp_word; + + // Respond to the core (when needed) + if (refill_cnt_q == 0) begin + core_rsp_word = hpdcache_uint'(mshr_ack_word)/HPDcacheCfg.u.accessWords; + + if (hpdcache_uint'(core_rsp_word) == 0) begin + refill_core_rsp_valid = mshr_ack_need_rsp; + refill_dirty_valid = mshr_ack_dirty; + end + + refill_core_rsp_sid = mshr_ack_src_id; + refill_core_rsp_tid = mshr_ack_req_id; + refill_core_rsp_error = refill_is_error_o; + refill_core_rsp_word = hpdcache_word_t'( + hpdcache_uint'(mshr_ack_word)/HPDcacheCfg.u.reqWords); + end else begin + automatic hpdcache_uint refill_cnt; + + core_rsp_word = hpdcache_uint'(refill_core_rsp_word_q)/ + HPDcacheCfg.u.accessWords; + refill_cnt = hpdcache_uint'(refill_cnt_q)/HPDcacheCfg.u.accessWords; + + if (core_rsp_word == refill_cnt) begin + refill_core_rsp_valid = refill_need_rsp_q; + refill_dirty_valid = refill_dirty_q; + end + + refill_core_rsp_sid = refill_sid_q; + refill_core_rsp_tid = refill_tid_q; + refill_core_rsp_error = refill_is_error_o; + refill_core_rsp_word = hpdcache_word_t'( + hpdcache_uint'(refill_core_rsp_word_q)/HPDcacheCfg.u.reqWords); + end + + // Write the the data in the cache data array + if (refill_cnt_q == 0) begin + refill_set_o = mshr_ack_cache_set; + refill_way = mshr_ack_cache_way; + is_prefetch = mshr_ack_is_prefetch; + refill_dirty = mshr_ack_dirty; + refill_dirty_wdata = mshr_ack_wdata; + refill_dirty_be = mshr_ack_be; + refill_dirty_wuser = HPDcacheCfg.u.userEn ? mshr_ack_wuser : '0; + end else begin + refill_set_o = refill_set_q; + refill_way = refill_way_q; + is_prefetch = refill_is_prefetch_q; + refill_dirty = refill_dirty_q; + refill_dirty_wdata = refill_dirty_wdata_q; + refill_dirty_be = refill_dirty_be_q; + refill_dirty_wuser = HPDcacheCfg.u.userEn ? refill_dirty_wuser_q : '0; + end + refill_write_data_o = ~refill_is_error_o; + + // Consume chunk of data from the FIFO buffer in the memory interface + refill_fifo_resp_data_r = 1'b1; + + // Update directory on the last chunk of data + refill_cnt_d = refill_cnt_q + hpdcache_word_t'(HPDcacheCfg.u.accessWords); + + if (hpdcache_uint'(refill_cnt_q) == REFILL_LAST_CHUNK_WORD) begin + if (REFILL_LAST_CHUNK_WORD == 0) begin + // Special case: if the cache-line data can be written in a single cycle, + // wait an additional cycle to write the directory. This allows to prevent + // a RAM-to-RAM timing path between the MSHR and the DIR. + refill_fsm_d = REFILL_WRITE_DIR; + end else begin + // Write the new entry in the cache directory + refill_write_dir_o = 1'b1; + + // Update the victim selection. Only in the following cases: + // - There is no error in response AND + // - It is a prefetch and the cfg_prefetch_updt_sel_victim_i is set OR + // - It is a read miss. + refill_updt_sel_victim_o = ~refill_is_error_o & + (~is_prefetch | cfg_prefetch_updt_sel_victim_i); + + // Update dependency flags in the retry table + refill_updt_rtab_o = 1'b1; + + // consume the response from the network + refill_fifo_resp_meta_r = 1'b1; + + refill_fsm_d = REFILL_IDLE; + end + end + end + // }}} + + // Write cache directory (this state is only visited when ACCESS_WORDS == CL_WORDS, + // this is when the entire cache-line can be written in a single cycle) + // {{{ + REFILL_WRITE_DIR: begin + // Select the target set and way + refill_set_o = refill_set_q; + refill_way = refill_way_q; + + // Write the new entry in the cache directory + refill_write_dir_o = 1'b1; + + // Update the victim selection. Only in the following cases: + // - There is no error in response AND + // - It is a prefetch and the cfg_prefetch_updt_sel_victim_i is set OR + // - It is a read miss. + refill_updt_sel_victim_o = ~refill_is_error_o & + (~refill_is_prefetch_q | cfg_prefetch_updt_sel_victim_i); + + // Update dependency flags in the retry table + refill_updt_rtab_o = 1'b1; + + // consume the response from the network + refill_fifo_resp_meta_r = 1'b1; + + refill_fsm_d = REFILL_IDLE; + end + // }}} + + // Invalidate the target cacheline (if it matches a valid cacheline) + // {{{ + REFILL_INVAL: begin + // Invalidate if there is a match + inval_write_dir_o = inval_hit_i; + + // consume the invalidation from the network + refill_fifo_resp_meta_r = 1'b1; + + refill_fsm_d = REFILL_IDLE; + end + + default: begin +`ifndef HPDCACHE_ASSERT_OFF + assert (1) $error("miss_handler: illegal state"); +`endif + end + endcase + end + + assign refill_is_error_o = (refill_fifo_resp_meta_rdata.r_error == HPDCACHE_MEM_RESP_NOK); + + assign refill_busy_o = (refill_fsm_q != REFILL_IDLE); + assign refill_nline_o = {refill_tag_q, refill_set_q}; + assign refill_word_o = refill_cnt_q; + + assign inval_nline_o = refill_fifo_resp_meta_rdata.inval_nline; + + assign mshr_check_tag = mshr_check_nline_i[HPDcacheCfg.setWidth +: HPDcacheCfg.tagWidth]; + assign mshr_check_set = mshr_check_offset_i[HPDcacheCfg.clOffsetWidth +: HPDcacheCfg.setWidth]; + + if (HPDcacheCfg.u.mshrSets > 1) begin : gen_mshr_set_gt_1 + // MSHR ack set and way + assign mshr_ack_set = refill_fifo_resp_meta_rdata.r_id[0 +: HPDcacheCfg.mshrSetWidth]; + if (HPDcacheCfg.u.mshrWays > 1) begin : gen_mshr_ack_way_gt_1 + assign mshr_ack_way = refill_fifo_resp_meta_rdata.r_id[HPDcacheCfg.mshrSetWidth +: + HPDcacheCfg.mshrWayWidth]; + end else begin : gen_mshr_ack_way_eq_1 + assign mshr_ack_way = '0; + end + end else begin : gen_mshr_set_eq_1 + // MSHR ack set and way + assign mshr_ack_set = '0; + if (HPDcacheCfg.u.mshrWays > 1) begin : gen_mshr_ack_way_gt_1 + assign mshr_ack_way = refill_fifo_resp_meta_rdata.r_id[0 +: HPDcacheCfg.mshrWayWidth]; + end else begin : gen_mshr_ack_way_eq_1 + assign mshr_ack_way = '0; + end + end + + // Write the new entry in the cache directory + // In case of error in the refill response, invalidate pre-allocated cache directory entry + assign refill_dir_entry_o = '{ + valid : ~refill_is_error_o, + wback : ~refill_is_error_o & refill_wback_q, + dirty : ~refill_is_error_o & refill_dirty_q, + fetch : 1'b0, + tag : refill_tag_q, + default :'0 + }; + + assign refill_core_rsp.rdata = refill_core_rsp_rdata; + assign refill_core_rsp.sid = refill_core_rsp_sid; + assign refill_core_rsp.tid = refill_core_rsp_tid; + assign refill_core_rsp.error = refill_core_rsp_error; + assign refill_core_rsp.aborted = 1'b0; + assign refill_core_rsp.ruser = HPDcacheCfg.u.userEn ? refill_core_rsp_ruser : '0; + + hpdcache_fifo_reg #( + .FIFO_DEPTH (1), + .FEEDTHROUGH (HPDcacheCfg.u.refillCoreRspFeedthrough), + .fifo_data_t (hpdcache_rsp_t) + ) i_refill_core_rsp_buf( + .clk_i, + .rst_ni, + .w_i (refill_core_rsp_valid), + .wok_o (/*unused*/), + .wdata_i (refill_core_rsp), + .r_i (1'b1), // core shall always be ready to consume a response + .rok_o (refill_core_rsp_valid_o), + .rdata_o (refill_core_rsp_o) + ); + + // refill's width is bigger than the width of the core's interface + if (REFILL_REQ_RATIO > 1) begin : gen_core_rsp_data_mux + hpdcache_mux #( + .NINPUT (REFILL_REQ_RATIO), + .DATA_WIDTH (HPDcacheCfg.reqDataWidth) + ) data_read_rsp_mux_i( + .data_i (refill_data_o), + .sel_i (refill_core_rsp_word[0 +: $clog2(REFILL_REQ_RATIO)]), + .data_o (refill_core_rsp_rdata) + ); + if (HPDcacheCfg.u.userEn) begin : gen_refill_core_rsp_ruser_useren + hpdcache_mux #( + .NINPUT (REFILL_REQ_RATIO), + .DATA_WIDTH (HPDcacheCfg.u.reqWord*HPDcacheCfg.u.wordUserWidth) + ) user_read_rsp_mux_i( + .data_i (refill_user_o), + .sel_i (refill_core_rsp_word[0 +: $clog2(REFILL_REQ_RATIO)]), + .data_o (refill_core_rsp_ruser) + ); + end else begin : gen_refill_core_rsp_ruser_default + assign refill_core_rsp_ruser = '0; + end + end + + // refill's width is equal to the width of the core's interface + else begin : gen_core_rsp_eqsize + assign refill_core_rsp_rdata = refill_data_o; + assign refill_core_rsp_ruser = HPDcacheCfg.u.userEn ? refill_user_o : '0; + end + + /* FIXME: when multiple chunks, in case of error, the error bit is not + * necessarily set on all chunks */ + assign refill_fifo_resp_meta_wdata = '{ + r_error : mem_resp_i.mem_resp_r_error, + r_id : mem_resp_i.mem_resp_r_id, + is_inval : mem_resp_inval_i, + inval_nline: mem_resp_inval_nline_i + }; + + hpdcache_fifo_reg #( + .FIFO_DEPTH (HPDcacheCfg.u.refillFifoDepth), + .fifo_data_t (mem_resp_metadata_t) + ) i_r_metadata_fifo ( + .clk_i, + .rst_ni, + + .w_i (refill_fifo_resp_meta_w), + .wok_o (refill_fifo_resp_meta_wok), + .wdata_i(refill_fifo_resp_meta_wdata), + + .r_i (refill_fifo_resp_meta_r), + .rok_o (refill_fifo_resp_meta_rok), + .rdata_o(refill_fifo_resp_meta_rdata) + ); + + hpdcache_data_resize #( + .WR_WIDTH (HPDcacheCfg.u.memDataWidth), + .RD_WIDTH (HPDcacheCfg.accessWidth), + .DEPTH (HPDcacheCfg.u.refillFifoDepth) + ) i_data_resize( + .clk_i, + .rst_ni, + + .w_i (refill_fifo_resp_data_w), + .wok_o (refill_fifo_resp_data_wok), + .wdata_i(mem_resp_i.mem_resp_r_data), + .wlast_i(mem_resp_i.mem_resp_r_last), + + .r_i (refill_fifo_resp_data_r), + .rok_o (/* unused */), + .rdata_o(refill_fifo_resp_data_rdata), + .rlast_o(/* unused */) + ); + + if (HPDcacheCfg.u.userEn) begin : gen_refill_fifo_resp_data_ruser_useren + hpdcache_data_resize #( + .WR_WIDTH (HPDcacheCfg.u.wordUserWidth*HPDcacheCfg.wordsPerMemFlit), + .RD_WIDTH (HPDcacheCfg.u.wordUserWidth*ACCESS_MEM_RATIO), + .DEPTH (HPDcacheCfg.u.refillFifoDepth) + ) i_user_resize( + .clk_i, + .rst_ni, + + .w_i (refill_fifo_resp_data_w), + .wok_o (/* use refill_fifo_resp_data_wok */), + .wdata_i(mem_resp_i.mem_resp_r_user), + .wlast_i(mem_resp_i.mem_resp_r_last), + + .r_i (refill_fifo_resp_data_r), + .rok_o (/* unused */), + .rdata_o(refill_fifo_resp_data_ruser), + .rlast_o(/* unused */) + ); + end else begin : gen_refill_fifo_resp_data_ruser_default + assign refill_fifo_resp_data_ruser = '0; + end + + // Refill data multiplexing logic + // Multiplexing has a byte granularity + // REFILL_REQ_RATIO is always greater or equal to 1 + // Use `accessBytes` bytes long signals + logic [HPDcacheCfg.accessBytes-1:0][7:0] clean_data; + hpdcache_refill_user_t clean_user; + assign clean_data = refill_fifo_resp_data_rdata; + // And reduce in case we have replicated user bits. + assign clean_user = HPDcacheCfg.u.userEn ? and_reduce_user(refill_fifo_resp_data_ruser) : '0; + + if (HPDcacheCfg.u.wbEn) begin : gen_refill_dirty_data + logic [HPDcacheCfg.accessBytes-1:0] dirty_be; + logic [HPDcacheCfg.accessBytes-1:0][7:0] dirty_data; + hpdcache_refill_user_t dirty_user; + logic [HPDcacheCfg.accessBytes-1:0][7:0] refill_data; + + assign dirty_be = {REFILL_REQ_RATIO{refill_dirty_be}}; + assign dirty_data = {REFILL_REQ_RATIO{refill_dirty_wdata}}; + assign dirty_user = HPDcacheCfg.u.userEn ? {REFILL_REQ_RATIO{refill_dirty_wuser}} : '0; + + // Iterate on all `accessBytes` bytes + for (genvar i = 0; i < HPDcacheCfg.accessBytes; i++) begin : gen_refill_mux + logic dirty_sel; + // Locate each byte to be replaced in the refill data + // The i-th byte is replaced if: + // 1) The refill counter is pointing to the correct refill data slice AND + // 2) The refill has dirty data to be coalesced AND + // 3) The i-th byte is dirty + if (REFILL_REQ_RATIO > 1) begin : gen_refill_sel_diffsize + always_comb begin + automatic hpdcache_uint v_current_rsp; + v_current_rsp = i / HPDcacheCfg.reqDataBytes; + + dirty_sel = refill_dirty_valid && dirty_be[i] && + v_current_rsp == refill_core_rsp_word[$clog2(REFILL_REQ_RATIO)-1:0]; + end + end else begin : gen_refill_sel_eqsize + assign dirty_sel = refill_dirty_valid && dirty_be[i]; + end + assign refill_data[i] = dirty_sel ? dirty_data[i] : clean_data[i]; + end + assign refill_data_o = hpdcache_refill_data_t'(refill_data); + if (HPDcacheCfg.u.userEn) begin : gen_refill_user_o_useren + // XXX TODO This assumes exactly one user field per 'accessBytes' + assign refill_user_o = (refill_dirty_valid && (dirty_be != 0)) ? dirty_user : clean_user; + end else begin : gen_refill_user_o_default + assign refill_user_o = '0; + end + + end else begin : gen_refill_no_dirty_data + // Reshape data to the expected type + assign refill_data_o = hpdcache_refill_data_t'(clean_data); + assign refill_user_o = hpdcache_refill_user_t'(clean_user); + end + + // The DATA fifo is only used for refill responses + assign refill_fifo_resp_data_w = mem_resp_valid_i & + ((refill_fifo_resp_meta_wok | ~mem_resp_i.mem_resp_r_last) & + ~mem_resp_inval_i); + + // The METADATA fifo is used for both refill responses and invalidations + assign refill_fifo_resp_meta_w = mem_resp_valid_i & + ((refill_fifo_resp_data_wok & mem_resp_i.mem_resp_r_last) | + mem_resp_inval_i); + + always_comb + begin : mem_resp_ready_comb + mem_resp_ready_o = 1'b0; + if (mem_resp_valid_i) begin + if (mem_resp_inval_i) begin + mem_resp_ready_o = refill_fifo_resp_meta_wok; + end else begin + mem_resp_ready_o = (refill_fifo_resp_meta_wok | ~mem_resp_i.mem_resp_r_last) & + refill_fifo_resp_data_wok; + end + end + end + + always_ff @(posedge clk_i or negedge rst_ni) + begin : miss_resp_fsm_ff + if (!rst_ni) begin + refill_fsm_q <= REFILL_IDLE; + end else begin + refill_fsm_q <= refill_fsm_d; + end + end + + always_ff @(posedge clk_i) + begin : miss_resp_fsm_internal_ff + if ((refill_fsm_q == REFILL_WRITE) && (refill_cnt_q == 0)) begin + refill_set_q <= mshr_ack_cache_set; + refill_way_q <= mshr_ack_cache_way; + refill_tag_q <= mshr_ack_cache_tag; + refill_sid_q <= mshr_ack_src_id; + refill_tid_q <= mshr_ack_req_id; + refill_need_rsp_q <= mshr_ack_need_rsp; + refill_is_prefetch_q <= mshr_ack_is_prefetch; + refill_wback_q <= mshr_ack_wback; + refill_dirty_q <= mshr_ack_dirty; + refill_dirty_wdata_q <= mshr_ack_wdata; + refill_dirty_wuser_q <= HPDcacheCfg.u.userEn ? mshr_ack_wuser : 1'b0; + refill_dirty_be_q <= mshr_ack_be; + refill_core_rsp_word_q <= mshr_ack_word; + end + refill_cnt_q <= refill_cnt_d; + end + // }}} + // }}} + + // Miss Status Holding Register component + // {{{ + hpdcache_mshr #( + .HPDcacheCfg (HPDcacheCfg), + .hpdcache_nline_t (hpdcache_nline_t), + .hpdcache_tag_t (hpdcache_tag_t), + .hpdcache_set_t (hpdcache_set_t), + .hpdcache_word_t (hpdcache_word_t), + .hpdcache_way_t (hpdcache_way_t), + + .hpdcache_req_tid_t (hpdcache_req_tid_t), + .hpdcache_req_sid_t (hpdcache_req_sid_t), + + .mshr_way_t (mshr_way_t), + .mshr_set_t (mshr_set_t), + + .cbuf_id_t (cbuf_id_t) + ) hpdcache_mshr_i( + .clk_i, + .rst_ni, + + .empty_o (mshr_empty), + .full_o (mshr_full_o), + + .check_i (mshr_check_i), + .check_set_i (mshr_check_set), + .check_tag_i (mshr_check_tag), + .hit_o (mshr_check_hit_o), + .alloc_i (mshr_alloc), + .alloc_cs_i (mshr_alloc_cs), + .alloc_nline_i (mshr_alloc_nline_i), + .alloc_req_id_i (mshr_alloc_tid_i), + .alloc_src_id_i (mshr_alloc_sid_i), + .alloc_word_i (mshr_alloc_word_i), + .alloc_victim_way_i (mshr_alloc_victim_way), + .alloc_need_rsp_i (mshr_alloc_need_rsp_i), + .alloc_is_prefetch_i (mshr_alloc_is_prefetch_i), + .alloc_wback_i (mshr_alloc_wback_i), + .alloc_dirty_i (mshr_alloc_dirty_i), + .alloc_cbuf_id_i (mshr_alloc_cbuf_id), + .alloc_full_o (mshr_alloc_full_o), + .alloc_way_o (mshr_alloc_way_d), + + .ack_i (mshr_ack), + .ack_cs_i (mshr_ack_cs), + .ack_set_i (mshr_ack_set), + .ack_way_i (mshr_ack_way), + .ack_req_id_o (mshr_ack_req_id), + .ack_src_id_o (mshr_ack_src_id), + .ack_cache_set_o (mshr_ack_cache_set), + .ack_cache_way_o (mshr_ack_cache_way), + .ack_cache_tag_o (mshr_ack_cache_tag), + .ack_word_o (mshr_ack_word), + .ack_need_rsp_o (mshr_ack_need_rsp), + .ack_is_prefetch_o (mshr_ack_is_prefetch), + .ack_wback_o (mshr_ack_wback), + .ack_dirty_o (mshr_ack_dirty), + .ack_cbuf_id_o (mshr_ack_cbuf_id) + ); + + hpdcache_1hot_to_binary #(.N(HPDcacheCfg.u.ways)) victim_way_encoder_i( + .val_i(mshr_alloc_victim_way_i), + .val_o(mshr_alloc_victim_way) + ); + + hpdcache_decoder #(.N(HPDcacheCfg.wayIndexWidth)) victim_way_decoder_i( + .en_i (refill_busy_o), + .val_i(refill_way), + .val_o(refill_way_o) + ); + + // Indicate to the cache controller that there is no pending miss. This + // is, when the MSHR is empty, and the MISS handler has finished of + // processing the last miss response. + assign mshr_empty_o = mshr_empty & ~refill_busy_o; + // }}} + + // Coalesce Buffer + // {{{ + if (HPDcacheCfg.u.wbEn) begin : gen_wb_cbuf + logic cbuf_alloc, cbuf_ack; + + assign cbuf_alloc = mshr_alloc_dirty_i && mshr_alloc; + assign cbuf_ack = refill_dirty && (refill_fsm_q == REFILL_WRITE) && (refill_cnt_q == 0); + + hpdcache_cbuf #( + .HPDcacheCfg (HPDcacheCfg), + .hpdcache_req_data_t (hpdcache_req_data_t), + .hpdcache_req_user_t (hpdcache_req_user_t), + .hpdcache_req_be_t (hpdcache_req_be_t), + .cbuf_id_t (cbuf_id_t) + ) hpdcache_cbuf_i( + .clk_i, + .rst_ni, + + .alloc_i (cbuf_alloc), + .alloc_wdata_i (mshr_alloc_wdata_i), + .alloc_wuser_i (mshr_alloc_wuser_i), + .alloc_be_i (mshr_alloc_be_i), + .alloc_id_o (mshr_alloc_cbuf_id), + .alloc_full_o (mshr_alloc_cbuf_full_o), + + .ack_i (cbuf_ack), + .ack_id_i (mshr_ack_cbuf_id), + .ack_wdata_o (mshr_ack_wdata), + .ack_wuser_o (mshr_ack_wuser), + .ack_be_o (mshr_ack_be) + ); + end else begin : gen_no_wb_cbuf + assign mshr_alloc_cbuf_id = '0; + assign mshr_alloc_cbuf_full_o = 1'b0; + assign mshr_ack_wdata = '0; + assign mshr_ack_wuser = '0; + assign mshr_ack_be = '0; + end + // }}} + + // Assertions + // {{{ +`ifndef HPDCACHE_ASSERT_OFF +`endif + // }}} + +endmodule +// }}} diff --git a/hw/vendor/hpdcache/rtl/src/hpdcache_mshr.sv b/hw/vendor/hpdcache/rtl/src/hpdcache_mshr.sv new file mode 100644 index 00000000..970daf1d --- /dev/null +++ b/hw/vendor/hpdcache/rtl/src/hpdcache_mshr.sv @@ -0,0 +1,423 @@ +/* + * Copyright 2023 CEA* + * *Commissariat a l'Energie Atomique et aux Energies Alternatives (CEA) + * Copyright 2025 Inria, Universite Grenoble-Alpes, TIMA + * + * SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 + * + * Licensed under the Solderpad Hardware License v 2.1 (the “License”); you + * may not use this file except in compliance with the License, or, at your + * option, the Apache License version 2.0. You may obtain a copy of the + * License at + * + * https://solderpad.org/licenses/SHL-2.1/ + * + * Unless required by applicable law or agreed to in writing, any work + * distributed under the License is distributed on an “AS IS” BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +/* + * Authors : Cesar Fuguet + * Creation Date : April, 2021 + * Description : HPDcache Miss Status Holding Register (MSHR) + * History : + */ +module hpdcache_mshr +import hpdcache_pkg::*; + // Parameters + // {{{ +#( + parameter hpdcache_cfg_t HPDcacheCfg = '0, + parameter type hpdcache_nline_t = logic, + parameter type hpdcache_tag_t = logic, + parameter type hpdcache_set_t = logic, + parameter type hpdcache_word_t = logic, + parameter type hpdcache_way_t = logic, + + parameter type hpdcache_req_tid_t = logic, + parameter type hpdcache_req_sid_t = logic, + + parameter type mshr_way_t = logic, + parameter type mshr_set_t = logic, + + parameter type cbuf_id_t = logic +) + // }}} + + // Ports + // {{{ +( + // Clock and reset signals + input logic clk_i, + input logic rst_ni, + + // Global control signals + output logic empty_o, + output logic full_o, + + // Check and allocation interface + input logic check_i, + input hpdcache_set_t check_set_i, + input hpdcache_tag_t check_tag_i, + output logic hit_o, + input logic alloc_i, + input logic alloc_cs_i, + input hpdcache_nline_t alloc_nline_i, + input hpdcache_req_tid_t alloc_req_id_i, + input hpdcache_req_sid_t alloc_src_id_i, + input hpdcache_word_t alloc_word_i, + input hpdcache_way_t alloc_victim_way_i, + input logic alloc_need_rsp_i, + input logic alloc_is_prefetch_i, + input logic alloc_wback_i, + input logic alloc_dirty_i, + input cbuf_id_t alloc_cbuf_id_i, + output logic alloc_full_o, + output mshr_way_t alloc_way_o, + + // Acknowledge interface + input logic ack_i, + input logic ack_cs_i, + input mshr_set_t ack_set_i, + input mshr_way_t ack_way_i, + output hpdcache_req_tid_t ack_req_id_o, + output hpdcache_req_sid_t ack_src_id_o, + output hpdcache_set_t ack_cache_set_o, + output hpdcache_way_t ack_cache_way_o, + output hpdcache_tag_t ack_cache_tag_o, + output hpdcache_word_t ack_word_o, + output logic ack_need_rsp_o, + output logic ack_is_prefetch_o, + output logic ack_wback_o, + output logic ack_dirty_o, + output cbuf_id_t ack_cbuf_id_o +); + // }}} + + // Definition of constants and types + // {{{ + typedef struct packed { + hpdcache_tag_t tag; + hpdcache_req_tid_t req_id; + hpdcache_req_sid_t src_id; + hpdcache_word_t word_idx; + hpdcache_way_t victim_way_idx; + logic wback; + logic dirty; + logic need_rsp; + logic is_prefetch; + cbuf_id_t cbuf_id; + } mshr_entry_t; + + + // Compute the width of MSHR entries depending on the support of write + // bitmask or not (write byte enable) + localparam int unsigned HPDCACHE_MSHR_ENTRY_BITS = $bits(mshr_entry_t); + + localparam int unsigned HPDCACHE_MSHR_RAM_ENTRY_BITS = + HPDcacheCfg.u.mshrRamByteEnable ? + ((HPDCACHE_MSHR_ENTRY_BITS + 7)/8) * 8 : // align to 8 bits + HPDCACHE_MSHR_ENTRY_BITS; // or use the exact number of bits + + typedef logic [HPDCACHE_MSHR_RAM_ENTRY_BITS-1:0] mshr_sram_data_t; + // }}} + + // Definition of internal wires and registers + // {{{ + logic [HPDcacheCfg.u.mshrSets*HPDcacheCfg.u.mshrWays-1:0] mshr_valid_q; + hpdcache_set_t [HPDcacheCfg.u.mshrSets*HPDcacheCfg.u.mshrWays-1:0] mshr_cache_set_q; + + hpdcache_set_t check_cache_set_q; + mshr_set_t check_set_st0, check_set_st1; + mshr_set_t alloc_set; + mshr_way_t ack_way_q; + + logic [HPDcacheCfg.u.mshrSets*HPDcacheCfg.u.mshrWays-1:0] mshr_valid_set, mshr_valid_rst; + mshr_entry_t [HPDcacheCfg.u.mshrWays-1:0] mshr_wentry; + mshr_sram_data_t [HPDcacheCfg.u.mshrWays-1:0] mshr_wdata; + mshr_entry_t [HPDcacheCfg.u.mshrWays-1:0] mshr_rentry; + mshr_sram_data_t [HPDcacheCfg.u.mshrWays-1:0] mshr_rdata; + + logic mshr_we; + logic mshr_cs; + mshr_set_t mshr_addr; + logic check; + // }}} + + // Control part for the allocation and check operations + // {{{ + + // The allocation operation is prioritary with respect to the check operation + assign check = check_i & ~alloc_i; + + if (HPDcacheCfg.u.mshrSets > 1) begin : gen_alloc_mshr_sets_gt_1 + assign check_set_st0 = check_set_i[0 +: HPDcacheCfg.mshrSetWidth]; + assign check_set_st1 = check_cache_set_q[0 +: HPDcacheCfg.mshrSetWidth]; + assign alloc_set = alloc_nline_i[0 +: HPDcacheCfg.mshrSetWidth]; + end else begin : gen_alloc_mshr_sets_eq_1 + assign check_set_st0 = '0; + assign check_set_st1 = '0; + assign alloc_set = '0; + end + + // Look for an available way in case of allocation + always_comb + begin + automatic mshr_way_t found_available_way; + + found_available_way = 0; + for (int unsigned i = 0; i < HPDcacheCfg.u.mshrWays; i++) begin + if (!mshr_valid_q[i*HPDcacheCfg.u.mshrSets + hpdcache_uint32'(alloc_set)]) begin + found_available_way = mshr_way_t'(i); + break; + end + end + alloc_way_o = found_available_way; + end + + // Look if the mshr can accept the checked nline (in case of allocation) + always_comb + begin + automatic bit found_available; + + found_available = 1'b0; + for (int unsigned i = 0; i < HPDcacheCfg.u.mshrWays; i++) begin + if (!mshr_valid_q[i*HPDcacheCfg.u.mshrSets + hpdcache_uint32'(check_set_st1)]) begin + found_available = 1'b1; + break; + end + end + alloc_full_o = ~found_available; + end + + // Write when there is an allocation operation + assign mshr_we = alloc_i; + + // Generate write data and mask depending on the available way + always_comb + begin + for (int unsigned i = 0; i < HPDcacheCfg.u.mshrWays; i++) begin + mshr_wentry[i].tag = alloc_nline_i[HPDcacheCfg.setWidth +: HPDcacheCfg.tagWidth]; + mshr_wentry[i].req_id = alloc_req_id_i; + mshr_wentry[i].src_id = alloc_src_id_i; + mshr_wentry[i].word_idx = alloc_word_i; + mshr_wentry[i].victim_way_idx = alloc_victim_way_i; + mshr_wentry[i].need_rsp = alloc_need_rsp_i; + mshr_wentry[i].is_prefetch = alloc_is_prefetch_i; + mshr_wentry[i].wback = alloc_wback_i; + mshr_wentry[i].dirty = alloc_dirty_i; + mshr_wentry[i].cbuf_id = alloc_cbuf_id_i; + end + end + // }}} + + // Shared control signals + // {{{ + hpdcache_uint mshr_alloc_slot; + hpdcache_uint mshr_ack_slot; + + if ((HPDcacheCfg.u.mshrSets > 1) && (HPDcacheCfg.u.mshrWays > 1)) + begin : gen_mshr_set_associative + assign mshr_alloc_slot = hpdcache_uint'({alloc_way_o, alloc_set}); + assign mshr_ack_slot = hpdcache_uint'({ ack_way_i, ack_set_i}); + end else if (HPDcacheCfg.u.mshrSets > 1) begin : gen_mshr_direct_mapped + assign mshr_alloc_slot = hpdcache_uint'(alloc_set); + assign mshr_ack_slot = hpdcache_uint'(ack_set_i); + end else if (HPDcacheCfg.u.mshrWays > 1) begin : gen_mshr_fully_associative + assign mshr_alloc_slot = hpdcache_uint'(alloc_way_o); + assign mshr_ack_slot = hpdcache_uint'(ack_way_i); + end else begin : gen_mshr_single_entry + assign mshr_alloc_slot = '0; + assign mshr_ack_slot = '0; + end + + assign mshr_cs = check_i | alloc_cs_i | ack_cs_i; + assign mshr_addr = ack_i ? ack_set_i : (alloc_i ? alloc_set : check_set_st0); + + always_comb + begin : mshr_valid_comb + for (hpdcache_uint i = 0; i < HPDcacheCfg.u.mshrSets*HPDcacheCfg.u.mshrWays; i++) begin + mshr_valid_rst[i] = (i == mshr_ack_slot) ? ack_i : 1'b0; + mshr_valid_set[i] = (i == mshr_alloc_slot) ? alloc_i : 1'b0; + end + end + // }}} + + // Read interface (ack) + // {{{ + assign ack_cache_set_o = mshr_cache_set_q[mshr_ack_slot]; + assign ack_cache_way_o = mshr_rentry[ack_way_q].victim_way_idx; + assign ack_cache_tag_o = mshr_rentry[ack_way_q].tag; + assign ack_req_id_o = mshr_rentry[ack_way_q].req_id; + assign ack_src_id_o = mshr_rentry[ack_way_q].src_id; + assign ack_word_o = mshr_rentry[ack_way_q].word_idx; + assign ack_need_rsp_o = mshr_rentry[ack_way_q].need_rsp; + assign ack_is_prefetch_o = mshr_rentry[ack_way_q].is_prefetch; + assign ack_wback_o = mshr_rentry[ack_way_q].wback; + assign ack_dirty_o = mshr_rentry[ack_way_q].dirty; + assign ack_cbuf_id_o = mshr_rentry[ack_way_q].cbuf_id; + // }}} + + // Global control signals + // {{{ + assign empty_o = ~|mshr_valid_q; + assign full_o = &mshr_valid_q; + + always_comb + begin : hit_comb + automatic bit [HPDcacheCfg.u.mshrWays-1:0] v_hit_way; + + for (int unsigned w = 0; w < HPDcacheCfg.u.mshrWays; w++) begin + automatic bit v_valid; + hpdcache_uint32 v_check_set_st1; + hpdcache_set_t v_check_set; + automatic bit v_match_set; + automatic bit v_match_tag; + + v_valid = mshr_valid_q[w*HPDcacheCfg.u.mshrSets + hpdcache_uint32'(check_set_st1)]; + v_check_set_st1 = hpdcache_uint32'(check_set_st1); + v_check_set = mshr_cache_set_q[w*HPDcacheCfg.u.mshrSets + v_check_set_st1]; + v_match_set = (v_check_set == check_cache_set_q); + v_match_tag = (mshr_rentry[w].tag == check_tag_i); + v_hit_way[w] = (v_valid && v_match_tag && v_match_set); + end + + hit_o = |v_hit_way; + end + // }}} + + // Internal state assignment + // {{{ + always_ff @(posedge clk_i or negedge rst_ni) + begin : mshr_ff_set + if (!rst_ni) begin + mshr_valid_q <= '0; + ack_way_q <= '0; + check_cache_set_q <= '0; + end else begin + mshr_valid_q <= (~mshr_valid_q & mshr_valid_set) | (mshr_valid_q & ~mshr_valid_rst); + if (ack_i) ack_way_q <= ack_way_i; + if (check) check_cache_set_q <= check_set_i; + end + end + // }}} + + // Internal components + // {{{ + typedef logic [HPDCACHE_MSHR_RAM_ENTRY_BITS/8-1:0] mshr_sram_wbyteenable_t; + typedef logic [HPDCACHE_MSHR_RAM_ENTRY_BITS-1:0] mshr_sram_wmask_t; + if (HPDcacheCfg.u.mshrRamByteEnable) begin : gen_mshr_wbyteenable + mshr_sram_wbyteenable_t [HPDcacheCfg.u.mshrWays-1:0] mshr_wbyteenable; + + always_comb + begin : mshr_wbyteenable_comb + for (int unsigned i = 0; i < HPDcacheCfg.u.mshrWays; i++) begin + mshr_wbyteenable[i] = (hpdcache_uint32'(alloc_way_o) == i) ? '1 : '0; + end + end + + if (HPDcacheCfg.u.mshrUseRegbank) begin : gen_mshr_regbank + hpdcache_regbank_wbyteenable_1rw #( + .DATA_SIZE (HPDcacheCfg.u.mshrWays*HPDCACHE_MSHR_RAM_ENTRY_BITS), + .ADDR_SIZE (HPDcacheCfg.mshrSetWidth), + .DEPTH (HPDcacheCfg.u.mshrSets) + ) mshr_mem( + .clk (clk_i), + .rst_n (rst_ni), + .cs (mshr_cs), + .we (mshr_we), + .addr (mshr_addr), + .wbyteenable (mshr_wbyteenable), + .wdata (mshr_wdata), + .rdata (mshr_rdata) + ); + end else begin : gen_mshr_sram + hpdcache_sram_wbyteenable #( + .DATA_SIZE (HPDcacheCfg.u.mshrWays*HPDCACHE_MSHR_RAM_ENTRY_BITS), + .ADDR_SIZE (HPDcacheCfg.mshrSetWidth), + .DEPTH (HPDcacheCfg.u.mshrSets) + ) mshr_mem( + .clk (clk_i), + .rst_n (rst_ni), + .cs (mshr_cs), + .we (mshr_we), + .addr (mshr_addr), + .wbyteenable (mshr_wbyteenable), + .wdata (mshr_wdata), + .rdata (mshr_rdata) + ); + end + end else begin : gen_mshr_wmask + mshr_sram_wmask_t [HPDcacheCfg.u.mshrWays-1:0] mshr_wmask; + + always_comb + begin : mshr_wmask_comb + for (int unsigned i = 0; i < HPDcacheCfg.u.mshrWays; i++) begin + mshr_wmask[i] = (hpdcache_uint32'(alloc_way_o) == i) ? '1 : '0; + end + end + + if (HPDcacheCfg.u.mshrUseRegbank) begin : gen_mshr_regbank + hpdcache_regbank_wmask_1rw #( + .DATA_SIZE (HPDcacheCfg.u.mshrWays*HPDCACHE_MSHR_RAM_ENTRY_BITS), + .ADDR_SIZE (HPDcacheCfg.mshrSetWidth), + .DEPTH (HPDcacheCfg.u.mshrSets) + ) mshr_mem( + .clk (clk_i), + .rst_n (rst_ni), + .cs (mshr_cs), + .we (mshr_we), + .addr (mshr_addr), + .wmask (mshr_wmask), + .wdata (mshr_wdata), + .rdata (mshr_rdata) + ); + end else begin : gen_mshr_sram + hpdcache_sram_wmask #( + .DATA_SIZE (HPDcacheCfg.u.mshrWays*HPDCACHE_MSHR_RAM_ENTRY_BITS), + .ADDR_SIZE (HPDcacheCfg.mshrSetWidth), + .DEPTH (HPDcacheCfg.u.mshrSets) + ) mshr_mem( + .clk (clk_i), + .rst_n (rst_ni), + .cs (mshr_cs), + .we (mshr_we), + .addr (mshr_addr), + .wmask (mshr_wmask), + .wdata (mshr_wdata), + .rdata (mshr_rdata) + ); + end + end + + always_ff @(posedge clk_i or negedge rst_ni) + begin + if (!rst_ni) begin + mshr_cache_set_q <= '0; + end else begin + if (alloc_i) begin + mshr_cache_set_q[mshr_alloc_slot] <= alloc_nline_i[0 +: HPDcacheCfg.setWidth]; + end + end + end + + always_comb + begin : ram_word_fitting_comb + for (int unsigned i = 0; i < HPDcacheCfg.u.mshrWays; i++) begin + mshr_wdata[i] = mshr_sram_data_t'(mshr_wentry[i]); + mshr_rentry[i] = mshr_entry_t'(mshr_rdata[i][0 +: HPDCACHE_MSHR_ENTRY_BITS]); + end + end + // }}} + + // Assertions + // {{{ +`ifndef HPDCACHE_ASSERT_OFF + one_command_assert: assert property (@(posedge clk_i) + (ack_i -> !(alloc_i || check_i))) else + $error("MSHR: ack with concurrent alloc or check"); +`endif + // }}} +endmodule diff --git a/hw/vendor/hpdcache/rtl/src/hpdcache_pkg.sv b/hw/vendor/hpdcache/rtl/src/hpdcache_pkg.sv new file mode 100644 index 00000000..5bae524a --- /dev/null +++ b/hw/vendor/hpdcache/rtl/src/hpdcache_pkg.sv @@ -0,0 +1,540 @@ +/* + * Copyright 2023 CEA* + * *Commissariat a l'Energie Atomique et aux Energies Alternatives (CEA) + * Copyright 2025 Inria, Universite Grenoble-Alpes, TIMA + * + * SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 + * + * Licensed under the Solderpad Hardware License v 2.1 (the “License”); you + * may not use this file except in compliance with the License, or, at your + * option, the Apache License version 2.0. You may obtain a copy of the + * License at + * + * https://solderpad.org/licenses/SHL-2.1/ + * + * Unless required by applicable law or agreed to in writing, any work + * distributed under the License is distributed on an “AS IS” BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +/* + * Authors : Cesar Fuguet + * Creation Date : April, 2021 + * Description : Write-Through (WT), High-Throughput (HTPUT) HPDcache Package + * History : + */ +package hpdcache_pkg; + // Utility definitions + // {{{ + typedef logic unsigned [7:0] hpdcache_uint8; + typedef logic signed [7:0] hpdcache_int8; + typedef logic unsigned [31:0] hpdcache_uint32; + typedef logic signed [31:0] hpdcache_int32; + typedef logic unsigned [63:0] hpdcache_uint64; + typedef logic signed [63:0] hpdcache_int64; + typedef hpdcache_uint32 hpdcache_uint; + typedef hpdcache_int32 hpdcache_int; + // }}} + + // Definition of constants and types for HPDcache directory memory + // {{{ + // Victim selection policy + typedef enum logic { + HPDCACHE_VICTIM_RANDOM = 1'b0, + HPDCACHE_VICTIM_PLRU = 1'b1 + } hpdcache_victim_sel_policy_t; + // }}} + + // Definition of interface with requesters + // {{{ + typedef logic [2:0] hpdcache_req_size_t; + + // Definition of operation codes + // {{{ + typedef enum logic [4:0] { + HPDCACHE_REQ_LOAD = 5'h00, + HPDCACHE_REQ_STORE = 5'h01, + // RESERVED = 5'h02, + // RESERVED = 5'h03, + HPDCACHE_REQ_AMO_LR = 5'h04, + HPDCACHE_REQ_AMO_SC = 5'h05, + HPDCACHE_REQ_AMO_SWAP = 5'h06, + HPDCACHE_REQ_AMO_ADD = 5'h07, + HPDCACHE_REQ_AMO_AND = 5'h08, + HPDCACHE_REQ_AMO_OR = 5'h09, + HPDCACHE_REQ_AMO_XOR = 5'h0a, + HPDCACHE_REQ_AMO_MAX = 5'h0b, + HPDCACHE_REQ_AMO_MAXU = 5'h0c, + HPDCACHE_REQ_AMO_MIN = 5'h0d, + HPDCACHE_REQ_AMO_MINU = 5'h0e, + // RESERVED = 5'h0f, + HPDCACHE_REQ_CMO_FENCE = 5'h10, + HPDCACHE_REQ_CMO_PREFETCH = 5'h11, + HPDCACHE_REQ_CMO_INVAL_NLINE = 5'h12, + HPDCACHE_REQ_CMO_INVAL_ALL = 5'h13, + HPDCACHE_REQ_CMO_FLUSH_NLINE = 5'h14, + HPDCACHE_REQ_CMO_FLUSH_ALL = 5'h15, + HPDCACHE_REQ_CMO_FLUSH_INVAL_NLINE = 5'h16, + HPDCACHE_REQ_CMO_FLUSH_INVAL_ALL = 5'h17 + } hpdcache_req_op_t; + // }}} + + // Definition of Write Policy Hint + // {{{ + typedef enum logic[2:0] { + HPDCACHE_WR_POLICY_AUTO = 3'b001, + HPDCACHE_WR_POLICY_WB = 3'b010, + HPDCACHE_WR_POLICY_WT = 3'b100 + } hpdcache_wr_policy_hint_t; + // }}} + + // Definition of PMA flags + // {{{ + typedef struct packed + { + logic uncacheable; + logic io; // FIXME: for future use + + // Write Policy Hint + hpdcache_wr_policy_hint_t wr_policy_hint; + } hpdcache_pma_t; + // }}} + + // Definition of functions + // {{{ + function automatic int unsigned hpdcache_ceil_div(int unsigned x, int unsigned y); + return (x + y - 1) / y; + endfunction + + function automatic int unsigned hpdcache_max(int unsigned x, int unsigned y); + return (x < y) ? y : x; + endfunction + + function automatic int unsigned hpdcache_min(int unsigned x, int unsigned y); + return (x < y) ? x : y; + endfunction + + function automatic logic is_load(input hpdcache_req_op_t op); + case (op) + HPDCACHE_REQ_LOAD: return 1'b1; + default: return 1'b0; + endcase + endfunction + + function automatic logic is_store(input hpdcache_req_op_t op); + case (op) + HPDCACHE_REQ_STORE: return 1'b1; + default: return 1'b0; + endcase + endfunction + + function automatic logic is_amo(input hpdcache_req_op_t op); + case (op) + HPDCACHE_REQ_AMO_LR, + HPDCACHE_REQ_AMO_SC, + HPDCACHE_REQ_AMO_SWAP, + HPDCACHE_REQ_AMO_ADD, + HPDCACHE_REQ_AMO_AND, + HPDCACHE_REQ_AMO_OR, + HPDCACHE_REQ_AMO_XOR, + HPDCACHE_REQ_AMO_MAX, + HPDCACHE_REQ_AMO_MAXU, + HPDCACHE_REQ_AMO_MIN, + HPDCACHE_REQ_AMO_MINU: + return 1'b1; + default: + return 1'b0; + endcase + endfunction + + function automatic logic is_amo_lr(input hpdcache_req_op_t op); + case (op) + HPDCACHE_REQ_AMO_LR: return 1'b1; + default: return 1'b0; + endcase + endfunction + + function automatic logic is_amo_sc(input hpdcache_req_op_t op); + case (op) + HPDCACHE_REQ_AMO_SC: return 1'b1; + default: return 1'b0; + endcase + endfunction + + function automatic logic is_amo_swap(input hpdcache_req_op_t op); + case (op) + HPDCACHE_REQ_AMO_SWAP: return 1'b1; + default: return 1'b0; + endcase + endfunction + + function automatic logic is_amo_add(input hpdcache_req_op_t op); + case (op) + HPDCACHE_REQ_AMO_ADD: return 1'b1; + default: return 1'b0; + endcase + endfunction + + function automatic logic is_amo_and(input hpdcache_req_op_t op); + case (op) + HPDCACHE_REQ_AMO_AND: return 1'b1; + default: return 1'b0; + endcase + endfunction + + function automatic logic is_amo_or(input hpdcache_req_op_t op); + case (op) + HPDCACHE_REQ_AMO_OR: return 1'b1; + default: return 1'b0; + endcase + endfunction + + function automatic logic is_amo_xor(input hpdcache_req_op_t op); + case (op) + HPDCACHE_REQ_AMO_XOR: return 1'b1; + default: return 1'b0; + endcase + endfunction + + function automatic logic is_amo_max(input hpdcache_req_op_t op); + case (op) + HPDCACHE_REQ_AMO_MAX: return 1'b1; + default: return 1'b0; + endcase + endfunction + + function automatic logic is_amo_maxu(input hpdcache_req_op_t op); + case (op) + HPDCACHE_REQ_AMO_MAXU: return 1'b1; + default: return 1'b0; + endcase + endfunction + + function automatic logic is_amo_min(input hpdcache_req_op_t op); + case (op) + HPDCACHE_REQ_AMO_MIN: return 1'b1; + default: return 1'b0; + endcase + endfunction + + function automatic logic is_amo_minu(input hpdcache_req_op_t op); + case (op) + HPDCACHE_REQ_AMO_MINU: return 1'b1; + default: return 1'b0; + endcase + endfunction + + function automatic logic is_cmo_inval(input hpdcache_req_op_t op); + return (op inside {HPDCACHE_REQ_CMO_INVAL_NLINE, HPDCACHE_REQ_CMO_INVAL_ALL}); + endfunction + + function automatic logic is_cmo_flush(input hpdcache_req_op_t op); + return (op inside {HPDCACHE_REQ_CMO_FLUSH_NLINE, + HPDCACHE_REQ_CMO_FLUSH_ALL, + HPDCACHE_REQ_CMO_FLUSH_INVAL_NLINE, + HPDCACHE_REQ_CMO_FLUSH_INVAL_ALL}); + endfunction + + function automatic logic is_cmo_fence(input hpdcache_req_op_t op); + return (op == HPDCACHE_REQ_CMO_FENCE); + endfunction + + function automatic logic is_cmo_prefetch(input hpdcache_req_op_t op); + return (op == HPDCACHE_REQ_CMO_PREFETCH); + endfunction + + function automatic logic is_cmo_inval_by_nline(input hpdcache_req_op_t op); + return (op == HPDCACHE_REQ_CMO_INVAL_NLINE); + endfunction + + function automatic logic is_cmo_inval_all(input hpdcache_req_op_t op); + return (op == HPDCACHE_REQ_CMO_INVAL_ALL); + endfunction + + function automatic logic is_cmo_flush_by_nline(input hpdcache_req_op_t op); + return (op == HPDCACHE_REQ_CMO_FLUSH_NLINE); + endfunction + + function automatic logic is_cmo_flush_all(input hpdcache_req_op_t op); + return (op == HPDCACHE_REQ_CMO_FLUSH_ALL); + endfunction + + function automatic logic is_cmo_flush_inval_by_nline(input hpdcache_req_op_t op); + return (op == HPDCACHE_REQ_CMO_FLUSH_INVAL_NLINE); + endfunction + + function automatic logic is_cmo_flush_inval_all(input hpdcache_req_op_t op); + return (op == HPDCACHE_REQ_CMO_FLUSH_INVAL_ALL); + endfunction + + function automatic logic is_cmo(input hpdcache_req_op_t op); + return (is_cmo_flush(op) || + is_cmo_fence(op) || + is_cmo_inval(op) || + is_cmo_prefetch(op)); + endfunction + + // }}} + // }}} + + // Definition of interface with memory + // {{{ + typedef logic [7:0] hpdcache_mem_len_t; + typedef logic [2:0] hpdcache_mem_size_t; + + typedef enum logic [1:0] { + HPDCACHE_MEM_RESP_OK = 2'b00, + HPDCACHE_MEM_RESP_NOK = 2'b01 + } hpdcache_mem_error_e; + + typedef enum logic [1:0] { + HPDCACHE_MEM_READ = 2'b00, + HPDCACHE_MEM_WRITE = 2'b01, + HPDCACHE_MEM_ATOMIC = 2'b10 + // Reserved = 2'b11 - TODO: CMO ? + } hpdcache_mem_command_e; + + typedef enum logic [3:0] { + HPDCACHE_MEM_ATOMIC_ADD = 4'b0000, + HPDCACHE_MEM_ATOMIC_CLR = 4'b0001, + HPDCACHE_MEM_ATOMIC_SET = 4'b0010, + HPDCACHE_MEM_ATOMIC_EOR = 4'b0011, + HPDCACHE_MEM_ATOMIC_SMAX = 4'b0100, + HPDCACHE_MEM_ATOMIC_SMIN = 4'b0101, + HPDCACHE_MEM_ATOMIC_UMAX = 4'b0110, + HPDCACHE_MEM_ATOMIC_UMIN = 4'b0111, + HPDCACHE_MEM_ATOMIC_SWAP = 4'b1000, + // Reserved = 4'b1001, + // Reserved = 4'b1010, + // Reserved = 4'b1011, + HPDCACHE_MEM_ATOMIC_LDEX = 4'b1100, + HPDCACHE_MEM_ATOMIC_STEX = 4'b1101 + // Reserved = 4'b1110, + // Reserved = 4'b1111 + } hpdcache_mem_atomic_e; + + function automatic hpdcache_mem_size_t get_hpdcache_mem_size(int unsigned bytes); + if (bytes == 0) return 0; + else if (bytes <= 2) return 1; + else if (bytes <= 4) return 2; + else if (bytes <= 8) return 3; + else if (bytes <= 16) return 4; + else if (bytes <= 32) return 5; + else if (bytes <= 64) return 6; + else if (bytes <= 128) return 7; + else begin +`ifndef HPDCACHE_ASSERT_OFF + assert (1) $error("hpdcache: unsupported number of bytes"); +`endif + return 0; + end + endfunction + // }}} + + // Definition of constants and types for the uncacheable request handler (UC) + // {{{ + typedef struct packed { + logic is_ld; + logic is_st; + logic is_amo_lr; + logic is_amo_sc; + logic is_amo_swap; + logic is_amo_add; + logic is_amo_and; + logic is_amo_or; + logic is_amo_xor; + logic is_amo_max; + logic is_amo_maxu; + logic is_amo_min; + logic is_amo_minu; + } hpdcache_uc_op_t; + // }}} + + // Definition of constants and types for the CMO request handler (CMOH) + // {{{ + typedef struct packed { + logic is_flush_inval_by_nline; + logic is_flush_inval_all; + logic is_flush_by_nline; + logic is_flush_all; + logic is_inval_by_nline; + logic is_inval_all; + logic is_fence; + } hpdcache_cmoh_op_t; + // }}} + + // Definition Replay Table (RTAB) dependencies + // {{{ + typedef struct packed { + logic mshr_hit; + logic mshr_full; + logic mshr_ready; + logic write_miss; + logic wbuf_hit; + logic wbuf_not_ready; + logic dir_unavailable; + logic dir_fetch; + logic flush_hit; + logic flush_not_ready; + } hpdcache_rtab_deps_t; + // }}} + + // Definition of parameters + // {{{ + typedef struct packed { + // Number of requesters + int unsigned nRequesters; + // Physical Address Width + int unsigned paWidth; + // Word width (bits) + int unsigned wordWidth; + // Word user width (bits) + int unsigned wordUserWidth; + // Number of sets + int unsigned sets; + // Number of ways + int unsigned ways; + // Cache-Line width (words) + int unsigned clWords; + // Number of words in the request data channels (request and response) + int unsigned reqWords; + // Request transaction ID width (bits) + int unsigned reqTransIdWidth; + // Request source ID width (bits) + int unsigned reqSrcIdWidth; + // Victim select + hpdcache_victim_sel_policy_t victimSel; + // Number of ways per RAM entry + int unsigned dataWaysPerRamWord; + // Number of sets per RAM + int unsigned dataSetsPerRam; + // DATA RAM macros implement write byte enable + // - Write byte enable (1'b1) + // - Write bit mask (1'b0) + bit dataRamByteEnable; + // Define the number of memory contiguous words that can be accessed + // simultaneously from the cache. + // - This limits the maximum width for the data channel from requesters + // - This impacts the refill latency (more ACCESS_WORDS -> less REFILL LATENCY) + int unsigned accessWords; + // MSHR number of sets + int unsigned mshrSets; + // MSHR number of ways + int unsigned mshrWays; + // MSHR number of ways in the same SRAM word (entry) + int unsigned mshrWaysPerRamWord; + // MSHR number of sets in the same SRAM + int unsigned mshrSetsPerRam; + // MSHR macros implement write byte enable + // - Write byte enable (1'b1) + // - Write bit mask (1'b0) + bit mshrRamByteEnable; + // MSHR uses whether FFs or SRAM + bit mshrUseRegbank; + // Store and refill coalesce buffer entries + int unsigned cbufEntries; + // Use feedthrough FIFOs from the refill handler to the core + bit refillCoreRspFeedthrough; + // Depth of the refill FIFO + int refillFifoDepth; + // Write-Buffer number of entries in the directory + int unsigned wbufDirEntries; + // Write-Buffer number of entries in the data buffer + int unsigned wbufDataEntries; + // Write-Buffer number of words per entry + int unsigned wbufWords; + // Write-Buffer threshold counter width (in bits) + int unsigned wbufTimecntWidth; + // Number of entries in the replay table + int unsigned rtabEntries; + // Number of entries in the flush directory + int unsigned flushEntries; + // Depth of the flush FIFO + int unsigned flushFifoDepth; + // Width of the address in the memory interface + int unsigned memAddrWidth; + // Width of the ID in the memory interface + int unsigned memIdWidth; + // Width of the data in the memory interface + int unsigned memDataWidth; + // Enable support for the write-through policy + bit wtEn; + // Enable support for the write-back policy + bit wbEn; + // Whether to enable user bit handling + bit userEn; + // Whether to enable capability atomic handling + bit capAmoEn; + // Enable fast loads. + // Perform loads in 1 cycle at the cost of structural hazard for stores + bit lowLatency; + } hpdcache_user_cfg_t; + + typedef struct packed { + // User configuration parameters + hpdcache_user_cfg_t u; + + // Internal parameters + int unsigned clWidth; + int unsigned clWordIdxWidth; + int unsigned clOffsetWidth; + int unsigned wordByteIdxWidth; + int unsigned wayIndexWidth; + int unsigned setWidth; + int unsigned nlineWidth; + int unsigned tagWidth; + int unsigned reqWordIdxWidth; + int unsigned reqOffsetWidth; + int unsigned reqDataWidth; + int unsigned reqDataBytes; + int unsigned mshrSetWidth; + int unsigned mshrWayWidth; + int unsigned cbufEntryWidth; + int unsigned wbufDataWidth; + int unsigned wbufDirPtrWidth; + int unsigned wbufDataPtrWidth; + int unsigned accessWidth; + int unsigned accessBytes; + int unsigned wordsPerMemFlit; + int unsigned amoWidth; + } hpdcache_cfg_t; + + function automatic hpdcache_cfg_t hpdcacheBuildConfig(input hpdcache_user_cfg_t p); + hpdcache_cfg_t ret; + + ret.u = p; + + ret.clWidth = p.clWords * p.wordWidth; + ret.clOffsetWidth = $clog2(ret.clWidth / 8); + ret.clWordIdxWidth = $clog2(p.clWords); + ret.wordByteIdxWidth = $clog2(p.wordWidth / 8); + ret.wayIndexWidth = (p.ways > 1) ? $clog2(p.ways) : 1; + ret.setWidth = $clog2(p.sets); + ret.nlineWidth = p.paWidth - ret.clOffsetWidth; + ret.tagWidth = ret.nlineWidth - ret.setWidth; + ret.reqWordIdxWidth = $clog2(p.reqWords); + ret.reqOffsetWidth = p.paWidth - ret.tagWidth; + ret.reqDataWidth = p.reqWords * p.wordWidth; + ret.reqDataBytes = ret.reqDataWidth/8; + + ret.mshrSetWidth = (p.mshrSets > 1) ? $clog2(p.mshrSets) : 1; + ret.mshrWayWidth = (p.mshrWays > 1) ? $clog2(p.mshrWays) : 1; + + ret.cbufEntryWidth = (p.cbufEntries > 1) ? $clog2(p.cbufEntries) : 1; + + ret.wbufDataWidth = ret.reqDataWidth*p.wbufWords; + ret.wbufDirPtrWidth = $clog2(p.wbufDirEntries); + ret.wbufDataPtrWidth = $clog2(p.wbufDataEntries); + + ret.accessWidth = p.accessWords * p.wordWidth; + ret.accessBytes = ret.accessWidth/8; + + ret.wordsPerMemFlit = hpdcache_ceil_div(p.memDataWidth, p.wordWidth); + + ret.amoWidth = p.capAmoEn ? 128 : 64; + + return ret; + endfunction + // }}} +endpackage diff --git a/hw/vendor/hpdcache/rtl/src/hpdcache_rtab.sv b/hw/vendor/hpdcache/rtl/src/hpdcache_rtab.sv new file mode 100644 index 00000000..a4e37544 --- /dev/null +++ b/hw/vendor/hpdcache/rtl/src/hpdcache_rtab.sv @@ -0,0 +1,690 @@ +/* + * Copyright 2023 CEA* + * *Commissariat a l'Energie Atomique et aux Energies Alternatives (CEA) + * Copyright 2025 Inria, Universite Grenoble-Alpes, TIMA + * + * SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 + * + * Licensed under the Solderpad Hardware License v 2.1 (the “License”); you + * may not use this file except in compliance with the License, or, at your + * option, the Apache License version 2.0. You may obtain a copy of the + * License at + * + * https://solderpad.org/licenses/SHL-2.1/ + * + * Unless required by applicable law or agreed to in writing, any work + * distributed under the License is distributed on an “AS IS” BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +/* + * Authors : Cesar Fuguet + * Creation Date : September, 2021 + * Description : HPDcache Replay Table + * History : + */ +module hpdcache_rtab +import hpdcache_pkg::*; +// Parameters +// {{{ +#( + parameter hpdcache_cfg_t HPDcacheCfg = '0, + + parameter type hpdcache_nline_t = logic, + parameter type hpdcache_way_t = logic, + + parameter type hpdcache_req_addr_t = logic, + + parameter type rtab_ptr_t = logic, + parameter type rtab_entry_t = logic +) +// }}} + +// Ports +// {{{ +( + // Clock and reset signals + input logic clk_i, + input logic rst_ni, + + // Global control signals + output logic empty_o, // RTAB is empty + output logic full_o, // RTAB is full + output logic fence_o, // There is a pending instruction with fence in the RTAB + + // Check RTAB signals + // This interface allows to check if there is an address-overlapping + // request in the RTAB with respect to the given nline. + input logic check_i, // Check for hit (nline) in the RTAB + input hpdcache_nline_t check_nline_i, + output logic check_hit_o, + + // Allocate signals + // This interface allows to allocate a new request in a new linked list + input logic alloc_i, + input logic alloc_and_link_i, + input rtab_entry_t alloc_req_i, + input hpdcache_rtab_deps_t alloc_deps_i, + + // Pop signals + // This interface allows to read (and remove) a request from the RTAB + output logic pop_try_valid_o, // Request ready to be replayed + input logic pop_try_i, + output rtab_entry_t pop_try_req_o, + output rtab_ptr_t pop_try_ptr_o, + output logic pop_try_error_o, + + // Pop Commit signals + // This interface allows to actually remove a popped request + input logic pop_commit_i, + input rtab_ptr_t pop_commit_ptr_i, + + // Pop Rollback signals + // This interface allows to put back a popped request + input logic pop_rback_i, + input rtab_ptr_t pop_rback_ptr_i, + + // Control signals from/to WBUF + output hpdcache_req_addr_t wbuf_addr_o, // address to check against ongoing writes + output logic wbuf_is_read_o, // monitored request is read + input logic wbuf_hit_open_i, // Hit on open entry in the write buf + input logic wbuf_hit_pend_i, // Hit on pend entry in the write buf + input logic wbuf_hit_sent_i, // Hit on sent entry in the write buf + input logic wbuf_not_ready_i, // Write buffer cannot accept the write + + // Control signals from the Miss Handler + input logic miss_ready_i, // Miss Handler is ready + + // Control signals from the Refill Handler + input logic refill_i, // Active refill + input logic refill_is_error_i, // Active refill has error bit set + input hpdcache_nline_t refill_nline_i, // Cache-line index being refilled + input hpdcache_way_t refill_way_index_i, // Way index being refilled + + // Control signals from the Flush Controller + input logic flush_ack_i, // Flush acknowledged + input hpdcache_nline_t flush_ack_nline_i, // Cache-line flush being acknowledged + input logic flush_ready_i, // Flush controller is available + + // Configuration parameters + input logic cfg_single_entry_i // Enable only one entry of the table +); +// }}} + +// Definition of constants, types and functions +// {{{ + localparam int N = HPDcacheCfg.u.rtabEntries; + + function automatic rtab_ptr_t rtab_bv_to_index( + input logic [N-1:0] bv); + for (int i = 0; i < N; i++) begin + if (bv[i]) return rtab_ptr_t'(i); + end + return 0; + endfunction + + function automatic logic [N-1:0] rtab_index_to_bv( + input rtab_ptr_t index); + logic [N-1:0] bv; + + for (int i = 0; i < N; i++) begin + bv[i] = (rtab_ptr_t'(i) == index); + end + return bv; + endfunction + + function automatic bit rtab_mshr_set_equal( + input hpdcache_nline_t x, + input hpdcache_nline_t y); + if (HPDcacheCfg.u.mshrSets > 1) begin + return (x[0 +: HPDcacheCfg.mshrSetWidth] == y[0 +: HPDcacheCfg.mshrSetWidth]); + end else begin + return 1'b1; + end + endfunction + + function automatic logic [N-1:0] rtab_next(rtab_ptr_t [N-1:0] next, rtab_ptr_t x); + return rtab_index_to_bv(next[x]); + endfunction + + typedef enum { + POP_TRY_HEAD, + POP_TRY_NEXT, + POP_TRY_NEXT_WAIT + } rtab_pop_try_state_e; +// }}} + +// Internal signals and registers +// {{{ + rtab_entry_t [N-1:0] req_q; + rtab_ptr_t [N-1:0] next_q; + + rtab_pop_try_state_e pop_try_state_q, pop_try_state_d; + logic [N-1:0] pop_try_next_q, pop_try_next_d; + + logic [N-1:0] valid_q; + logic [N-1:0] valid_set, valid_rst; + logic [N-1:0] alloc_valid_set; + logic [N-1:0] pop_commit_valid_rst; + logic [N-1:0] pop_rback_bv; + + // Bits indicating if the corresponding entry is an error response + logic [N-1:0] error_q; + logic [N-1:0] error_set, error_rst; + logic refill_is_error; + + // Bits indicating if the corresponding entry is the head of a linked list + logic [N-1:0] head_q; + logic [N-1:0] head_set, head_rst; + logic [N-1:0] alloc_head_set, alloc_head_rst; + logic [N-1:0] pop_try_head_rst; + logic [N-1:0] pop_commit_head_set; + logic [N-1:0] pop_rback_head_set; + + // Bits indicating if the corresponding entry is the tail of a linked list + logic [N-1:0] tail_q; + logic [N-1:0] tail_set, tail_rst; + logic [N-1:0] alloc_tail_set, alloc_tail_rst; + + // Bits indicating the pending dependencies of request + hpdcache_rtab_deps_t[N-1:0] deps_q; + hpdcache_rtab_deps_t[N-1:0] deps_set, deps_rst; + hpdcache_rtab_deps_t[N-1:0] alloc_deps_set; + hpdcache_rtab_deps_t[N-1:0] pop_rback_deps_set; + + logic [N-1:0] nodeps; + hpdcache_nline_t [N-1:0] nline; + hpdcache_req_addr_t [N-1:0] addr; + logic [N-1:0] is_read_bv; + logic [N-1:0] is_amo_bv; + logic [N-1:0] fence_bv; + logic [N-1:0] check_hit; + logic [N-1:0] match_check_nline; + logic [N-1:0] match_check_tail; + logic [N-1:0] match_refill_mshr_set; + logic [N-1:0] match_refill_nline; + logic [N-1:0] match_refill_set; + logic [N-1:0] match_refill_way; + logic [N-1:0] match_flush_nline; + + logic [N-1:0] free; + logic [N-1:0] free_alloc; + logic alloc; + + logic [N-1:0] pop_rback_ptr_bv; + logic [N-1:0] pop_try_bv; + logic [N-1:0] ready; + + genvar gen_i; +// }}} + +// Compute global control signals +// {{{ + // compute if entries are ready to be replayed + for (gen_i = 0; gen_i < N; gen_i++) begin : gen_nodeps + assign nodeps[gen_i] = ~(|deps_q[gen_i]); + end + + assign ready = valid_q & head_q & nodeps; + assign free = ~valid_q; + + // compute the free vector (one-hot signal) + hpdcache_prio_1hot_encoder #( + .N (N) + ) free_encoder_i ( + .val_i (free), + .val_o (free_alloc) + ); + + // full and empty signals + assign empty_o = &(~valid_q); + assign full_o = &( valid_q) | (|valid_q & cfg_single_entry_i); + assign fence_o = |fence_bv; +// }}} + +// Check interface +// {{{ + for (gen_i = 0; gen_i < N; gen_i++) begin : gen_check + assign addr[gen_i] = {req_q[gen_i].req.addr_tag, req_q[gen_i].req.addr_offset}; + assign nline[gen_i] = addr[gen_i][HPDcacheCfg.clOffsetWidth +: HPDcacheCfg.nlineWidth]; + assign match_check_nline[gen_i] = (check_nline_i == nline[gen_i]); + assign is_read_bv[gen_i] = is_load(req_q[gen_i].req.op) | + is_cmo_prefetch(req_q[gen_i].req.op); + assign is_amo_bv[gen_i] = is_amo(req_q[gen_i].req.op); + end + + assign fence_bv = valid_q & is_amo_bv; + assign check_hit = valid_q & match_check_nline; + assign check_hit_o = |check_hit; + assign match_check_tail = check_hit & tail_q; +// }}} + +// Allocation process +// {{{ + assign alloc = alloc_i | alloc_and_link_i; + + // Set the valid bit-vector of the replay table + assign alloc_valid_set = free_alloc & {N{alloc}}; + + // Set of head and tail bit-vectors during an allocation + // - The head bit is only set when creating a new linked-list + // - The tail bit is always set because new requests are added on the tail. + assign alloc_head_set = free_alloc & {N{alloc_i}}; + assign alloc_tail_set = alloc_valid_set; + + // Reset of head and tail bit-vectors during an allocation + // - When doing an allocation and link, head bit shall be reset + // - when doing an allocation and link, the "prev" tail shall be reset + assign alloc_head_rst = free_alloc & {N{alloc_and_link_i}}; + assign alloc_tail_rst = match_check_tail & {N{alloc_and_link_i}}; + + // Set the dependency bits for the allocated entry + for (gen_i = 0; gen_i < N; gen_i++) begin : gen_deps_set + assign alloc_deps_set[gen_i] = alloc_valid_set[gen_i] ? alloc_deps_i : '0; + end +// }}} + +// Update replay table dependencies +// {{{ + for (gen_i = 0; gen_i < N; gen_i++) begin : gen_match_refill + assign match_refill_mshr_set[gen_i] = rtab_mshr_set_equal(refill_nline_i, nline[gen_i]); + assign match_refill_nline[gen_i] = (refill_nline_i == nline[gen_i]); + assign match_refill_set[gen_i] = (refill_nline_i[0 +: HPDcacheCfg.setWidth] == + nline[gen_i][0 +: HPDcacheCfg.setWidth]); + assign match_refill_way[gen_i] = (refill_way_index_i == req_q[gen_i].way_fetch); + end + + for (gen_i = 0; gen_i < N; gen_i++) begin : gen_match_flush + assign match_flush_nline[gen_i] = (flush_ack_nline_i == nline[gen_i]); + end + + // Update write buffer hit dependencies + // {{{ + // Build a bit-vector with HEAD requests waiting for a conflict in the wbuf + logic [N-1:0] wbuf_rd_pending, wbuf_wr_pending; + logic [N-1:0] wbuf_rd_gnt, wbuf_wr_gnt; + logic [ 1:0] wbuf_pending; + logic [ 1:0] wbuf_gnt; + logic wbuf_ready; + logic [N-1:0] wbuf_sel; + + for (gen_i = 0; gen_i < N; gen_i++) begin : gen_wbuf_pending + assign wbuf_rd_pending[gen_i] = valid_q[gen_i] & head_q[gen_i] & + deps_q[gen_i].wbuf_hit; + assign wbuf_wr_pending[gen_i] = valid_q[gen_i] & head_q[gen_i] & + deps_q[gen_i].wbuf_not_ready; + end + + // Choose in a round-robin manner a ready transaction waiting for a conflict in the wbuf + hpdcache_rrarb #( + .N (N) + ) wbuf_rd_pending_arb_i ( + .clk_i, + .rst_ni, + .req_i (wbuf_rd_pending), + .gnt_o (wbuf_rd_gnt), + .ready_i (wbuf_gnt[0] & wbuf_ready) + ); + + hpdcache_rrarb #( + .N (N) + ) wbuf_wr_pending_arb_i ( + .clk_i, + .rst_ni, + .req_i (wbuf_wr_pending), + .gnt_o (wbuf_wr_gnt), + .ready_i (wbuf_gnt[1] & wbuf_ready) + ); + + assign wbuf_pending = {|wbuf_wr_gnt, |wbuf_rd_gnt}, + wbuf_ready = |(pop_try_bv & (wbuf_rd_gnt | wbuf_wr_gnt)); + + hpdcache_fxarb #( + .N (2) + ) wbuf_pending_arb_i ( + .clk_i, + .rst_ni, + .req_i (wbuf_pending), + .gnt_o (wbuf_gnt), + .ready_i (wbuf_ready) + ); + + assign wbuf_sel = wbuf_gnt[0] ? wbuf_rd_gnt : + wbuf_gnt[1] ? wbuf_wr_gnt : '0; + + hpdcache_mux #( + .NINPUT (N), + .DATA_WIDTH ($bits(hpdcache_req_addr_t)), + .ONE_HOT_SEL (1'b1) + ) wbuf_pending_addr_mux_i ( + .data_i (addr), + .sel_i (wbuf_sel), + .data_o (wbuf_addr_o) + ); + + hpdcache_mux #( + .NINPUT (N), + .DATA_WIDTH (1), + .ONE_HOT_SEL (1'b1) + ) wbuf_pending_is_read_mux_i ( + .data_i (is_read_bv), + .sel_i (wbuf_sel), + .data_o (wbuf_is_read_o) + ); + // }}} + + always_comb + begin : deps_rst_comb + deps_rst = '0; + + for (int i = 0; i < N; i++) begin + + // reset write buffer dependency bits with the output from the write buffer + // {{{ + if (wbuf_sel[i]) begin + deps_rst[i].wbuf_hit = ~(wbuf_hit_open_i | wbuf_hit_pend_i | wbuf_hit_sent_i); + deps_rst[i].wbuf_not_ready = ~wbuf_not_ready_i; + end + // }}} + + // Update miss handler dependency + // {{{ + deps_rst[i].mshr_ready = miss_ready_i; + // }}} + + // Update refill dependencies + // {{{ + if (refill_i) begin + deps_rst[i].mshr_full = match_refill_mshr_set[i]; + deps_rst[i].mshr_hit = match_refill_nline[i]; + deps_rst[i].write_miss = match_refill_nline[i]; + deps_rst[i].dir_unavailable = match_refill_set[i]; + deps_rst[i].dir_fetch = match_refill_set[i] & match_refill_way[i]; + end + // }}} + + // Update flush dependencies + // {{{ + deps_rst[i].flush_hit = flush_ack_i & match_flush_nline[i]; + deps_rst[i].flush_not_ready = flush_ready_i; + // }}} + end + end +// }}} + +// Pop interface +// {{{ + logic [N-1:0] pop_sel; + + // Pop try process + // {{{ + logic [N-1:0] pop_gnt; + logic pop_head; + + hpdcache_rrarb #( + .N (N) + ) pop_arb_i ( + .clk_i, + .rst_ni, + .req_i (ready), + .gnt_o (pop_gnt), + .ready_i (pop_head) + ); + + always_comb + begin : req_valid_comb + case(pop_try_state_q) + POP_TRY_HEAD : pop_try_valid_o = |ready; + POP_TRY_NEXT : pop_try_valid_o = 1'b1; + POP_TRY_NEXT_WAIT: pop_try_valid_o = 1'b1; + default : pop_try_valid_o = 1'b0; + endcase + end + + always_comb + begin : pop_entry_sel_comb + pop_try_state_d = pop_try_state_q; + pop_try_next_d = pop_try_next_q; + pop_head = 1'b0; + pop_sel = '0; + + case (pop_try_state_q) + POP_TRY_HEAD: begin + // This FSM may be in this state after forwarding the tail of + // a list. In that case, a rollback may arrive in this cycle. + pop_sel = pop_gnt; + if (!pop_rback_i && pop_try_valid_o) begin + if (pop_try_i) begin + // If the request interface accepts the request, go to the next request + // in the list (if the current request is not the tail). Otherwise, stay in + // the same state to to forward a request from a new list + pop_head = 1'b1; + if ((pop_gnt & ~tail_q) != 0) begin + pop_try_state_d = POP_TRY_NEXT; + pop_try_next_d = rtab_next(next_q, pop_try_ptr_o); + end + end + end + end + POP_TRY_NEXT: begin + pop_sel = pop_try_next_q; + if (pop_rback_i) begin + pop_try_state_d = POP_TRY_HEAD; + end else begin + if (pop_try_i) begin + // If the request interface accepts the new request, go to the next request + // in the list (if the current request is not the tail). Otherwise, return + // to the POP_TRY_HEAD state to forward a request from a new list + if ((pop_try_next_q & ~tail_q) != 0) begin + pop_try_state_d = POP_TRY_NEXT; + pop_try_next_d = rtab_next(next_q, pop_try_ptr_o); + end else begin + pop_try_state_d = POP_TRY_HEAD; + end + end else begin + // If the request interface is not ready to consume the new request, wait + // until it is + pop_try_state_d = POP_TRY_NEXT_WAIT; + end + end + end + POP_TRY_NEXT_WAIT: begin + // Wait for the current request to be accepted. Then go to the next request in the + // list or to a new list + pop_sel = pop_try_next_q; + if (pop_try_i) begin + if ((pop_try_next_q & ~tail_q) != 0) begin + pop_try_state_d = POP_TRY_NEXT; + pop_try_next_d = rtab_next(next_q, pop_try_ptr_o); + end else begin + pop_try_state_d = POP_TRY_HEAD; + end + end + end + default: begin + end + endcase + end + + assign pop_commit_head_set = '0; + + hpdcache_mux #( + .NINPUT (N), + .DATA_WIDTH ($bits(rtab_entry_t)), + .ONE_HOT_SEL (1'b1) + ) pop_mux_i ( + .data_i (req_q), + .sel_i (pop_sel), + .data_o (pop_try_req_o) + ); + + // Temporarily unset the head bit of the popped request to prevent it to be rescheduled + assign pop_try_bv = pop_sel & {N{pop_try_i}}; + assign pop_try_head_rst = pop_try_bv; + + + // Forward the index of the entry being popped. This is used later by the + // commit or rollback operations + assign pop_try_ptr_o = rtab_bv_to_index(pop_sel); + + // Forward the error bit + assign pop_try_error_o = |(pop_sel & error_q); + // }}} + + // Pop commit process + // {{{ + // Invalidate the entry being popped (head of the linked list) + assign pop_commit_valid_rst = {N{pop_commit_i}} & rtab_index_to_bv(pop_commit_ptr_i); + // }}} + + // Pop rollback process + // {{{ + // Set again the head bit of the rolled-back request + assign pop_rback_ptr_bv = rtab_index_to_bv(pop_rback_ptr_i); + assign pop_rback_bv = {N{pop_rback_i}} & pop_rback_ptr_bv; + assign pop_rback_head_set = pop_rback_bv; + + for (gen_i = 0; gen_i < N; gen_i++) begin : gen_pop_rback_set + assign pop_rback_deps_set[gen_i] = pop_rback_bv[gen_i] ? alloc_deps_i : '0; + end + // }}} +// }}} + +// Internal state assignment +// {{{ + assign head_set = alloc_head_set | pop_commit_head_set | pop_rback_head_set; + assign head_rst = alloc_head_rst | pop_try_head_rst; + + assign tail_set = alloc_tail_set; + assign tail_rst = alloc_tail_rst; + + assign valid_set = alloc_valid_set; + assign valid_rst = pop_commit_valid_rst; + + assign refill_is_error = refill_i & refill_is_error_i; + + // Set the error flag + // - If the pending request is a write miss and the corresponding refill response is an error, + // then set the error bit of the pending request to abort it when replayed + for (gen_i = 0; gen_i < N; gen_i++) begin : gen_error_set + assign error_set[gen_i] = valid_q[gen_i] & + deps_q[gen_i].write_miss & + match_refill_nline[gen_i] & + refill_is_error; + end + assign error_rst = pop_commit_valid_rst; + + assign deps_set = alloc_deps_set | pop_rback_deps_set; + + always_ff @(posedge clk_i or negedge rst_ni) + begin : rtab_valid_ff + if (!rst_ni) begin + valid_q <= '0; + error_q <= '0; + head_q <= '0; + tail_q <= '0; + deps_q <= '0; + next_q <= '0; + end else begin + valid_q <= (~valid_q & valid_set) | (valid_q & ~valid_rst); + + // error flags + error_q <= (~error_q & error_set) | (error_q & ~error_rst); + + // update head and tail flags + head_q <= (~head_q & head_set) | (head_q & ~head_rst); + tail_q <= (~tail_q & tail_set) | (tail_q & ~tail_rst); + + // update dependency flags + deps_q <= (~deps_q & deps_set) | + ( deps_q & ~deps_rst); + + // update the next pointers + for (int i = 0; i < N; i++) begin + if (alloc_and_link_i && match_check_tail[i]) begin + next_q[i] <= rtab_bv_to_index(free_alloc); + end + end + end + end + + always_ff @(posedge clk_i or negedge rst_ni) + begin : pop_try_ff + if (!rst_ni) begin + pop_try_state_q <= POP_TRY_HEAD; + pop_try_next_q <= '0; + end else begin + pop_try_state_q <= pop_try_state_d; + pop_try_next_q <= pop_try_next_d; + end + end + + always_ff @(posedge clk_i) + begin : rtab_ff + for (int i = 0; i < N; i++) begin + // Update the request array + // A RTAB request is stored at allocation time, but can be modified during + // a roll-back. Some fields such as the way_fetch are part of the RTAB request, and + // may need to be modified when rolling it back + if (valid_set[i] | pop_rback_bv[i]) begin + req_q[i] <= alloc_req_i; + end + end + end +// }}} + +// Assertions +// {{{ +`ifndef HPDCACHE_ASSERT_OFF + assert property (@(posedge clk_i) disable iff (rst_ni !== 1'b1) + check_i |-> $onehot0(match_check_tail)) else + $error("rtab: more than one entry matching"); + + assert property (@(posedge clk_i) disable iff (rst_ni !== 1'b1) + alloc_and_link_i |-> (check_i & check_hit_o)) else + $error("rtab: alloc and link shall be performed in case of check hit"); + + assert property (@(posedge clk_i) disable iff (rst_ni !== 1'b1) + alloc_and_link_i |-> + ({alloc_req_i.req.addr_tag, + alloc_req_i.req.addr_offset[HPDcacheCfg.clOffsetWidth +: + HPDcacheCfg.setWidth]} == check_nline_i)) else + $error("rtab: nline for alloc and link shall match the one being checked"); + + assert property (@(posedge clk_i) disable iff (rst_ni !== 1'b1) + alloc_i |-> !alloc_and_link_i) else + $error("rtab: only one allocation per cycle is allowed"); + +`ifndef VERILATOR + assert property (@(posedge clk_i) disable iff (rst_ni !== 1'b1) + pop_try_i |-> ##1 (pop_commit_i | pop_rback_i)) else + $error("rtab: a pop try shall be followed by a commit or rollback"); +`endif + + assert property (@(posedge clk_i) disable iff (rst_ni !== 1'b1) + pop_commit_i |-> valid_q[pop_commit_ptr_i]) else + $error("rtab: commiting an invalid entry"); + + assert property (@(posedge clk_i) disable iff (rst_ni !== 1'b1) + pop_rback_i |-> valid_q[pop_rback_ptr_i]) else + $error("rtab: rolling-back an invalid entry"); + + assert property (@(posedge clk_i) disable iff (rst_ni !== 1'b1) + pop_rback_i |-> !pop_try_i) else + $error("rtab: cache shall not accept a new request while rolling back"); + + assert property (@(posedge clk_i) disable iff (rst_ni !== 1'b1) + pop_rback_i |-> ~alloc) else + $error("rtab: trying to allocate a new request while rolling back"); + + assert property (@(posedge clk_i) disable iff (rst_ni !== 1'b1) + alloc |-> ~full_o) else + $error("rtab: trying to allocate while the table is full"); + + assert property (@(posedge clk_i) disable iff (rst_ni !== 1'b1) + alloc_and_link_i |-> ~cfg_single_entry_i) else + $error("rtab: trying to link a request in single entry mode"); +`endif +// }}} +endmodule diff --git a/hw/vendor/hpdcache/rtl/src/hpdcache_uncached.sv b/hw/vendor/hpdcache/rtl/src/hpdcache_uncached.sv new file mode 100644 index 00000000..08c1c1fa --- /dev/null +++ b/hw/vendor/hpdcache/rtl/src/hpdcache_uncached.sv @@ -0,0 +1,1206 @@ +/* + * Copyright 2023 CEA* + * *Commissariat a l'Energie Atomique et aux Energies Alternatives (CEA) + * Copyright 2025 Inria, Universite Grenoble-Alpes, TIMA + * + * SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 + * + * Licensed under the Solderpad Hardware License v 2.1 (the “License”); you + * may not use this file except in compliance with the License, or, at your + * option, the Apache License version 2.0. You may obtain a copy of the + * License at + * + * https://solderpad.org/licenses/SHL-2.1/ + * + * Unless required by applicable law or agreed to in writing, any work + * distributed under the License is distributed on an “AS IS” BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +/* + * Authors : Cesar Fuguet + * Creation Date : May, 2021 + * Description : HPDcache uncached and AMO request handler + * History : + */ +module hpdcache_uncached +import hpdcache_pkg::*; + // Parameters + // {{{ +#( + parameter hpdcache_cfg_t HPDcacheCfg = '0, + + parameter type hpdcache_nline_t = logic, + parameter type hpdcache_tag_t = logic, + parameter type hpdcache_set_t = logic, + parameter type hpdcache_offset_t = logic, + parameter type hpdcache_word_t = logic, + + parameter type hpdcache_req_addr_t = logic, + parameter type hpdcache_req_tid_t = logic, + parameter type hpdcache_req_sid_t = logic, + parameter type hpdcache_req_user_t = logic, + parameter type hpdcache_req_data_t = logic, + parameter type hpdcache_req_be_t = logic, + + parameter type hpdcache_way_vector_t = logic, + + parameter type hpdcache_req_t = logic, + parameter type hpdcache_rsp_t = logic, + parameter type hpdcache_mem_id_t = logic, + parameter type hpdcache_mem_req_t = logic, + parameter type hpdcache_mem_req_w_t = logic, + parameter type hpdcache_mem_resp_r_t = logic, + parameter type hpdcache_mem_resp_w_t = logic +) + // }}} + + // Ports + // {{{ +( + input logic clk_i, + input logic rst_ni, + + // Global control signals + // {{{ + input logic wbuf_empty_i, + input logic mshr_empty_i, + input logic rtab_empty_i, + input logic ctrl_empty_i, + input logic flush_empty_i, + // }}} + + // Cache-side request interface + // {{{ + input logic req_valid_i, + output logic req_ready_o, + input hpdcache_uc_op_t req_op_i, + input hpdcache_req_addr_t req_addr_i, + input hpdcache_req_size_t req_size_i, + input hpdcache_req_data_t req_data_i, + input hpdcache_req_user_t req_user_i, + input hpdcache_req_be_t req_be_i, + input logic req_uc_i, + input hpdcache_req_sid_t req_sid_i, + input hpdcache_req_tid_t req_tid_i, + input logic req_need_rsp_i, + // }}} + + // Write buffer interface + // {{{ + output logic wbuf_flush_all_o, + // }}} + + // AMO Cache Interface + // {{{ + output logic dir_amo_match_o, + output hpdcache_set_t dir_amo_match_set_o, + output hpdcache_tag_t dir_amo_match_tag_o, + output logic dir_amo_updt_sel_victim_o, + input hpdcache_way_vector_t dir_amo_hit_way_i, + + output logic data_amo_write_o, + output logic data_amo_write_enable_o, + output hpdcache_set_t data_amo_write_set_o, + output hpdcache_req_size_t data_amo_write_size_o, + output hpdcache_word_t data_amo_write_word_o, + output hpdcache_req_user_t data_amo_write_user_o, + output hpdcache_req_data_t data_amo_write_data_o, + output hpdcache_req_be_t data_amo_write_be_o, + // }}} + + // LR/SC reservation buffer + // {{{ + input logic lrsc_snoop_i, + input hpdcache_req_addr_t lrsc_snoop_addr_i, + input hpdcache_req_size_t lrsc_snoop_size_i, + // }}} + + // Core response interface + // {{{ + input logic core_rsp_ready_i, + output logic core_rsp_valid_o, + output hpdcache_rsp_t core_rsp_o, + // }}} + + // MEMORY interfaces + // {{{ + // Memory request unique identifier + input hpdcache_mem_id_t mem_read_id_i, + input hpdcache_mem_id_t mem_write_id_i, + + // Read interface + input logic mem_req_read_ready_i, + output logic mem_req_read_valid_o, + output hpdcache_mem_req_t mem_req_read_o, + + output logic mem_resp_read_ready_o, + input logic mem_resp_read_valid_i, + input hpdcache_mem_resp_r_t mem_resp_read_i, + + // Write interface + input logic mem_req_write_ready_i, + output logic mem_req_write_valid_o, + output hpdcache_mem_req_t mem_req_write_o, + + input logic mem_req_write_data_ready_i, + output logic mem_req_write_data_valid_o, + output hpdcache_mem_req_w_t mem_req_write_data_o, + + output logic mem_resp_write_ready_o, + input logic mem_resp_write_valid_i, + input hpdcache_mem_resp_w_t mem_resp_write_i, + // }}} + + // Configuration interface + // {{{ + input logic cfg_error_on_cacheable_amo_i + // }}} +); + // }}} + +// Definition of constants and types +// {{{ + localparam hpdcache_uint MEM_REQ_RATIO = HPDcacheCfg.u.memDataWidth/HPDcacheCfg.reqDataWidth; + localparam hpdcache_uint MEM_REQ_WORD_INDEX_WIDTH = $clog2(MEM_REQ_RATIO); + localparam hpdcache_uint REQ_MEM_RATIO = HPDcacheCfg.reqDataWidth/HPDcacheCfg.u.memDataWidth; + localparam hpdcache_uint REQ_MEM_WORD_INDEX_WIDTH = $clog2(REQ_MEM_RATIO); + + typedef enum { + UC_IDLE, + UC_WAIT_PENDING, + UC_MEM_REQ, + UC_MEM_W_REQ, + UC_MEM_WDATA_REQ, + UC_MEM_W_AND_WDATA2_REQ, + UC_MEM_WDATA2_REQ, + UC_MEM_WAIT_RSP, + UC_CORE_RSP, + UC_AMO_READ_DIR, + UC_AMO_WRITE_DATA + } hpdcache_uc_fsm_t; + + localparam logic AMO_SC_SUCCESS = 1'b0; + localparam logic AMO_SC_FAILURE = 1'b1; + + function automatic logic [HPDcacheCfg.amoWidth-1:0] prepare_amo_data_operand( + input logic [HPDcacheCfg.amoWidth-1:0] data_i, + input hpdcache_req_size_t size_i, + input hpdcache_req_addr_t addr_i, + input logic sign_extend_i + ); + localparam AMO_WIDTH_MSB_SELECT = $clog2(HPDcacheCfg.amoWidth/8)-1; + // 128-bits AMOs are already aligned, thus do nothing + if (size_i == hpdcache_req_size_t'(4)) begin + return data_i; + end + + // 64-bits AMOs: never sign extend into the metadata + else if (size_i == hpdcache_req_size_t'(3)) begin + localparam hpdcache_uint dword_count = HPDcacheCfg.amoWidth / 64; + automatic logic[dword_count-1:0][63:0] dwords; + automatic logic[63:0] shifted_dword; + for (int i = 0; i < dword_count; i = i+1) begin + dwords[i] = data_i[i*64+:64]; + end + shifted_dword = dword_count > 1 ? dwords[addr_i[hpdcache_max(AMO_WIDTH_MSB_SELECT,3):3]] : dwords[0]; + return {{64{1'b0}}, shifted_dword}; + end + + // 32-bits AMOs + else if (size_i == hpdcache_req_size_t'(2)) begin + localparam hpdcache_uint word_count = HPDcacheCfg.amoWidth / 32; + automatic logic[word_count-1:0][31:0] words; + automatic logic[31:0] shifted_word; + for (int i = 0; i < word_count; i = i+1) begin + words[i] = data_i[i*32+:32]; + end + shifted_word = words[addr_i[AMO_WIDTH_MSB_SELECT:2]]; + return {{64{1'b0}}, {32{sign_extend_i & shifted_word[31]}}, shifted_word}; + end + + // 16-bits AMOs + else if (size_i == hpdcache_req_size_t'(1)) begin + localparam hpdcache_uint hword_count = HPDcacheCfg.amoWidth / 16; + automatic logic[hword_count-1:0][15:0] hwords; + automatic logic[15:0] shifted_hword; + for (int i = 0; i < hword_count; i = i+1) begin + hwords[i] = data_i[i*16+:16]; + end + shifted_hword = hwords[addr_i[AMO_WIDTH_MSB_SELECT:1]]; + return {{64{1'b0}}, {48{sign_extend_i & shifted_hword[15]}}, shifted_hword}; + end + + // 8-bits AMOs + else begin + localparam hpdcache_uint byte_count = HPDcacheCfg.amoWidth / 8; + automatic logic[byte_count-1:0][7:0] bytes; + automatic logic[7:0] shifted_byte; + for (int i = 0; i < byte_count; i = i+1) begin + bytes[i] = data_i[i*8+:8]; + end + shifted_byte = bytes[addr_i[AMO_WIDTH_MSB_SELECT:0]]; + return {{64{1'b0}}, {56{sign_extend_i & shifted_byte[7]}}, shifted_byte}; + end + endfunction; + + function automatic logic [HPDcacheCfg.amoWidth-1:0] prepare_amo_data_result( + input logic [HPDcacheCfg.amoWidth-1:0] data_i, + input hpdcache_req_size_t size_i + ); + // 128-bits AMOs are already aligned, thus do nothing + if (size_i == hpdcache_req_size_t'(4)) begin + return data_i; + end + + // 64-bits AMOs + else if (size_i == hpdcache_req_size_t'(3)) begin + return {2{data_i[63:0]}}; + end + + // 32-bits AMOs + else if (size_i == hpdcache_req_size_t'(2)) begin + return {4{data_i[31:0]}}; + end + + // 16-bits AMOs + else if (size_i == hpdcache_req_size_t'(1)) begin + return {8{data_i[15:0]}}; + end + + // 8-bits AMOs + else begin + return {16{data_i[7:0]}}; + end + endfunction; + + function automatic logic amo_need_sign_extend(hpdcache_uc_op_t op); + unique case (1'b1) + op.is_amo_add, + op.is_amo_max, + op.is_amo_min: return 1'b1; + default : return 1'b0; + endcase; + endfunction +// }}} + +// Internal signals and registers +// {{{ + hpdcache_uc_fsm_t uc_fsm_q, uc_fsm_d; + hpdcache_uc_op_t req_op_q; + hpdcache_req_addr_t req_addr_q; + hpdcache_req_size_t req_size_q; + hpdcache_req_data_t req_data_q; + hpdcache_req_user_t req_user_q; + hpdcache_req_be_t req_be_q; + logic req_uc_q; + hpdcache_req_sid_t req_sid_q; + hpdcache_req_tid_t req_tid_q; + logic req_need_rsp_q; + logic multiflit; + logic req_is_cap; + logic no_pend_trans; + + logic uc_sc_retcode_q, uc_sc_retcode_d; + + hpdcache_req_data_t rsp_rdata_q, rsp_rdata_d; + hpdcache_req_user_t rsp_ruser_q, rsp_ruser_d; + logic rsp_error_set, rsp_error_rst; + logic rsp_error_q; + logic mem_resp_write_valid_q, mem_resp_write_valid_d; + logic mem_resp_read_valid_q, mem_resp_read_valid_d; + logic mem_resp_read_finishing; + + hpdcache_req_data_t mem_req_write_data; + hpdcache_req_user_t mem_req_write_user; + + logic [HPDcacheCfg.amoWidth-1:0] amo_req_ld_data; + logic amo_req_ld_user; + logic [HPDcacheCfg.amoWidth-1:0] amo_ld_data; + logic amo_ld_user; + logic [HPDcacheCfg.amoWidth-1:0] amo_req_st_data; + logic amo_req_st_user; + logic [HPDcacheCfg.amoWidth-1:0] amo_st_data; + logic amo_st_user; + logic [HPDcacheCfg.amoWidth-1:0] amo_result_data; + logic amo_result_user; + logic [HPDcacheCfg.amoWidth-1:0] amo_write_data; + logic amo_write_user; +// }}} + +// LR/SC reservation buffer logic +// {{{ + logic lrsc_rsrv_valid_q; + hpdcache_req_addr_t lrsc_rsrv_addr_q, lrsc_rsrv_addr_d; + hpdcache_nline_t lrsc_rsrv_nline; + hpdcache_offset_t lrsc_rsrv_word; + + hpdcache_offset_t lrsc_snoop_words; + hpdcache_nline_t lrsc_snoop_nline; + hpdcache_offset_t lrsc_snoop_base, lrsc_snoop_end; + logic lrsc_snoop_hit; + logic lrsc_snoop_reset; + + hpdcache_nline_t lrsc_uc_nline; + hpdcache_offset_t lrsc_uc_word; + logic lrsc_uc_hit; + logic lrsc_uc_set, lrsc_uc_reset; + + // NOTE: Reservation set for LR instruction is always 16-bytes in this + // implementation. + assign lrsc_rsrv_nline = lrsc_rsrv_addr_q[HPDcacheCfg.clOffsetWidth +: + HPDcacheCfg.nlineWidth]; + assign lrsc_rsrv_word = lrsc_rsrv_addr_q[0 +: HPDcacheCfg.clOffsetWidth] >> 4; + + // Check hit on LR/SC reservation for snoop port (normal write accesses) + assign lrsc_snoop_words = (lrsc_snoop_size_i < 4) ? + 1 : hpdcache_offset_t'((8'h1 << lrsc_snoop_size_i) >> 4); + assign lrsc_snoop_nline = lrsc_snoop_addr_i[HPDcacheCfg.clOffsetWidth +: + HPDcacheCfg.nlineWidth]; + assign lrsc_snoop_base = lrsc_snoop_addr_i[0 +: HPDcacheCfg.clOffsetWidth] >> 4; + assign lrsc_snoop_end = lrsc_snoop_base + lrsc_snoop_words; + + assign lrsc_snoop_hit = lrsc_rsrv_valid_q & (lrsc_rsrv_nline == lrsc_snoop_nline) & + (lrsc_rsrv_word >= lrsc_snoop_base) & + (lrsc_rsrv_word < lrsc_snoop_end ); + + assign lrsc_snoop_reset = lrsc_snoop_i & lrsc_snoop_hit; + + // Check hit on LR/SC reservation for AMOs and SC + assign lrsc_uc_nline = req_addr_i[HPDcacheCfg.clOffsetWidth +: HPDcacheCfg.nlineWidth]; + assign lrsc_uc_word = req_addr_i[0 +: HPDcacheCfg.clOffsetWidth] >> 4; + + assign lrsc_uc_hit = lrsc_rsrv_valid_q & (lrsc_rsrv_nline == lrsc_uc_nline) & + (lrsc_rsrv_word == lrsc_uc_word); +// }}} + + assign no_pend_trans = wbuf_empty_i && + mshr_empty_i && + rtab_empty_i && + ctrl_empty_i && + flush_empty_i; + + assign multiflit = HPDcacheCfg.u.capAmoEn && req_size_q == hpdcache_req_size_t'(4); + assign req_is_cap = HPDcacheCfg.u.capAmoEn && HPDcacheCfg.u.userEn && req_size_q == hpdcache_req_size_t'(4); + assign mem_resp_read_finishing = mem_resp_read_valid_i && mem_resp_read_i.mem_resp_r_last; + +// Uncacheable request FSM +// {{{ + always_comb + begin : uc_fsm_comb + mem_resp_write_valid_d = mem_resp_write_valid_q; + mem_resp_read_valid_d = mem_resp_read_valid_q; + rsp_error_set = 1'b0; + rsp_error_rst = 1'b0; + lrsc_rsrv_addr_d = lrsc_rsrv_addr_q; + uc_sc_retcode_d = uc_sc_retcode_q; + wbuf_flush_all_o = 1'b0; + lrsc_uc_set = 1'b0; + lrsc_uc_reset = 1'b0; + + uc_fsm_d = uc_fsm_q; + + unique case (uc_fsm_q) + // Wait for a request + // {{{ + UC_IDLE: begin + + if (req_valid_i) begin + wbuf_flush_all_o = 1'b1; + + unique case (1'b1) + req_op_i.is_ld, + req_op_i.is_st: begin + if (no_pend_trans) begin + uc_fsm_d = UC_MEM_REQ; + end else begin + uc_fsm_d = UC_WAIT_PENDING; + end + end + + req_op_i.is_amo_swap, + req_op_i.is_amo_add, + req_op_i.is_amo_and, + req_op_i.is_amo_or, + req_op_i.is_amo_xor, + req_op_i.is_amo_max, + req_op_i.is_amo_maxu, + req_op_i.is_amo_min, + req_op_i.is_amo_minu, + req_op_i.is_amo_lr: begin + // Reset LR/SC reservation if AMO matches its address + lrsc_uc_reset = ~req_op_i.is_amo_lr & lrsc_uc_hit; + + if (!req_uc_i && cfg_error_on_cacheable_amo_i) begin + rsp_error_set = 1'b1; + uc_fsm_d = UC_CORE_RSP; + end else begin + if (no_pend_trans) begin + uc_fsm_d = UC_MEM_REQ; + end else begin + uc_fsm_d = UC_WAIT_PENDING; + end + end + end + + req_op_i.is_amo_sc: begin + if (!req_uc_i && cfg_error_on_cacheable_amo_i) begin + rsp_error_set = 1'b1; + uc_fsm_d = UC_CORE_RSP; + end else begin + // Reset previous reservation (if any) + lrsc_uc_reset = 1'b1; + + // SC with valid reservation + if (lrsc_uc_hit) begin + if (no_pend_trans) begin + uc_fsm_d = UC_MEM_REQ; + end else begin + uc_fsm_d = UC_WAIT_PENDING; + end + end + // SC with no valid reservation, thus respond with the failure code + else begin + uc_sc_retcode_d = AMO_SC_FAILURE; + uc_fsm_d = UC_CORE_RSP; + end + end + end + + default: begin + if (req_need_rsp_i) begin + rsp_error_set = 1'b1; + uc_fsm_d = UC_CORE_RSP; + end + end + endcase + end + end + // }}} + + // Wait for all pending transactions to be completed + // {{{ + UC_WAIT_PENDING: begin + if (no_pend_trans) begin + uc_fsm_d = UC_MEM_REQ; + end else begin + uc_fsm_d = UC_WAIT_PENDING; + end + end + // }}} + + // Send request to memory + // {{{ + UC_MEM_REQ: begin + uc_fsm_d = UC_MEM_REQ; + + mem_resp_write_valid_d = 1'b0; + mem_resp_read_valid_d = 1'b0; + + unique case (1'b1) + req_op_q.is_ld, + req_op_q.is_amo_lr: begin + if (mem_req_read_ready_i) begin + uc_fsm_d = UC_MEM_WAIT_RSP; + end + end + + req_op_q.is_st, + req_op_q.is_amo_sc, + req_op_q.is_amo_swap, + req_op_q.is_amo_add, + req_op_q.is_amo_and, + req_op_q.is_amo_or, + req_op_q.is_amo_xor, + req_op_q.is_amo_max, + req_op_q.is_amo_maxu, + req_op_q.is_amo_min, + req_op_q.is_amo_minu: begin + if (mem_req_write_ready_i && mem_req_write_data_ready_i) begin + uc_fsm_d = multiflit ? UC_MEM_WDATA2_REQ : UC_MEM_WAIT_RSP; + end else if (mem_req_write_ready_i) begin + uc_fsm_d = UC_MEM_WDATA_REQ; + end else if (mem_req_write_data_ready_i) begin + uc_fsm_d = multiflit ? UC_MEM_W_AND_WDATA2_REQ : UC_MEM_W_REQ; + end + end + endcase + end + // }}} + + // Send write address + // {{{ + UC_MEM_W_REQ: begin + mem_resp_write_valid_d = mem_resp_write_valid_q | mem_resp_write_valid_i; + mem_resp_read_valid_d = mem_resp_read_valid_q | mem_resp_read_finishing; + + if (mem_req_write_ready_i) begin + uc_fsm_d = UC_MEM_WAIT_RSP; + end else begin + uc_fsm_d = UC_MEM_W_REQ; + end + end + // }}} + + // Send write address and second write data flit + // {{{ + UC_MEM_W_AND_WDATA2_REQ: begin + mem_resp_write_valid_d = mem_resp_write_valid_q | mem_resp_write_valid_i; + mem_resp_read_valid_d = mem_resp_read_valid_q | mem_resp_read_finishing; + + if (mem_req_write_ready_i && mem_req_write_data_ready_i) begin + uc_fsm_d = UC_MEM_WAIT_RSP; + end else if (mem_req_write_ready_i) begin + uc_fsm_d = UC_MEM_WDATA2_REQ; + end else if (mem_req_write_data_ready_i) begin + uc_fsm_d = UC_MEM_W_REQ; + end else begin + uc_fsm_d = UC_MEM_W_AND_WDATA2_REQ; + end + end + // }}} + + // Send write data + // {{{ + UC_MEM_WDATA_REQ: begin + mem_resp_write_valid_d = mem_resp_write_valid_q | mem_resp_write_valid_i; + mem_resp_read_valid_d = mem_resp_read_valid_q | mem_resp_read_finishing; + + if (mem_req_write_data_ready_i) begin + uc_fsm_d = multiflit ? UC_MEM_WDATA2_REQ : UC_MEM_WAIT_RSP; + end else begin + uc_fsm_d = UC_MEM_WDATA_REQ; + end + end + // }}} + // Send second flit of write data + // {{{ + UC_MEM_WDATA2_REQ: begin + mem_resp_write_valid_d = mem_resp_write_valid_q | mem_resp_write_valid_i; + mem_resp_read_valid_d = mem_resp_read_valid_q | mem_resp_read_finishing; + + if (mem_req_write_data_ready_i) begin + uc_fsm_d = UC_MEM_WAIT_RSP; + end else begin + uc_fsm_d = UC_MEM_WDATA_REQ; + end + end + // }}} + + // Wait for the response from the memory + // {{{ + UC_MEM_WAIT_RSP: begin + automatic bit rd_error; + automatic bit wr_error; + + uc_fsm_d = UC_MEM_WAIT_RSP; + mem_resp_write_valid_d = mem_resp_write_valid_q | mem_resp_write_valid_i; + mem_resp_read_valid_d = mem_resp_read_valid_q | mem_resp_read_finishing; + + rd_error = mem_resp_read_finishing && + ( mem_resp_read_i.mem_resp_r_error == HPDCACHE_MEM_RESP_NOK); + wr_error = mem_resp_write_valid_i && + (mem_resp_write_i.mem_resp_w_error == HPDCACHE_MEM_RESP_NOK); + rsp_error_set = req_need_rsp_q & (rd_error | wr_error); + + unique case (1'b1) + req_op_q.is_ld: begin + if (mem_resp_read_finishing) begin + if (req_need_rsp_q) begin + uc_fsm_d = UC_CORE_RSP; + end else begin + uc_fsm_d = UC_IDLE; + end + end + end + req_op_q.is_st: begin + if (mem_resp_write_valid_i) begin + if (req_need_rsp_q) begin + uc_fsm_d = UC_CORE_RSP; + end else begin + uc_fsm_d = UC_IDLE; + end + end + end + req_op_q.is_amo_lr: begin + if (mem_resp_read_finishing) begin + // set a new reservation + if (!rd_error) + begin + lrsc_uc_set = 1'b1; + lrsc_rsrv_addr_d = req_addr_q; + end + // in case of a memory error, do not make the reservation and + // invalidate an existing one (if valid) + else begin + lrsc_uc_reset = 1'b1; + end + + if (req_uc_q || rd_error) begin + uc_fsm_d = UC_CORE_RSP; + end else begin + uc_fsm_d = UC_AMO_READ_DIR; + end + end + end + req_op_q.is_amo_sc: begin + if (mem_resp_write_valid_i) begin + automatic bit is_atomic; + + is_atomic = mem_resp_write_i.mem_resp_w_is_atomic && !wr_error; + uc_sc_retcode_d = is_atomic ? AMO_SC_SUCCESS : AMO_SC_FAILURE; + + if (req_uc_q || !is_atomic) begin + uc_fsm_d = UC_CORE_RSP; + end else begin + uc_fsm_d = UC_AMO_READ_DIR; + end + end + end + req_op_q.is_amo_swap, + req_op_q.is_amo_add, + req_op_q.is_amo_and, + req_op_q.is_amo_or, + req_op_q.is_amo_xor, + req_op_q.is_amo_max, + req_op_q.is_amo_maxu, + req_op_q.is_amo_min, + req_op_q.is_amo_minu: begin + // wait for both old data and write acknowledged were received + if ((mem_resp_read_finishing && mem_resp_write_valid_i) || + (mem_resp_read_finishing && mem_resp_write_valid_q) || + (mem_resp_read_valid_q && mem_resp_write_valid_i)) + begin + if (req_uc_q || rsp_error_q || rd_error || wr_error) begin + uc_fsm_d = UC_CORE_RSP; + end else begin + uc_fsm_d = UC_AMO_READ_DIR; + end + end + end + endcase + end + // }}} + + // Send the response to the requester + // {{{ + UC_CORE_RSP: begin + if (core_rsp_ready_i) begin + rsp_error_rst = 1'b1; + uc_fsm_d = UC_IDLE; + end else begin + uc_fsm_d = UC_CORE_RSP; + end + end + // }}} + + // Check for a cache hit on the AMO target address + // {{{ + UC_AMO_READ_DIR: begin + uc_fsm_d = UC_AMO_WRITE_DATA; + end + // }}} + + // Write the locally computed AMO result in the cache + // {{{ + UC_AMO_WRITE_DATA: begin + uc_fsm_d = UC_CORE_RSP; + end + // }}} + endcase + end +// }}} + +// AMO unit +// {{{ + + if (HPDcacheCfg.reqDataWidth > HPDcacheCfg.amoWidth) begin : gen_amo_data_width_gt_amo_width + localparam hpdcache_uint AMO_WORD_INDEX_WIDTH = $clog2(HPDcacheCfg.reqDataWidth/HPDcacheCfg.amoWidth); + hpdcache_mux #( + .NINPUT (HPDcacheCfg.reqDataWidth/HPDcacheCfg.amoWidth), + .DATA_WIDTH (HPDcacheCfg.amoWidth), + .ONE_HOT_SEL (1'b0) + ) amo_ld_data_mux_i ( + .data_i (rsp_rdata_q), + .sel_i (req_addr_q[$clog2(HPDcacheCfg.amoWidth/8) +: AMO_WORD_INDEX_WIDTH]), + .data_o (amo_req_ld_data) + ); + + if (HPDcacheCfg.u.userEn) begin : gen_amo_req_ld_user_userEn + hpdcache_mux #( + .NINPUT (HPDcacheCfg.reqDataWidth/HPDcacheCfg.amoWidth), + .DATA_WIDTH (1), + .ONE_HOT_SEL (1'b0) + ) amo_ld_data_mux_i ( + .data_i (rsp_ruser_q), + .sel_i (req_addr_q[$clog2(HPDcacheCfg.amoWidth/8) +: AMO_WORD_INDEX_WIDTH]), + .data_o (amo_req_ld_user) + ); + end else begin : gen_amo_req_ld_user_default + assign amo_req_ld_user = '0; + end + + hpdcache_mux #( + .NINPUT (HPDcacheCfg.reqDataWidth/HPDcacheCfg.amoWidth), + .DATA_WIDTH (HPDcacheCfg.amoWidth), + .ONE_HOT_SEL (1'b0) + ) amo_st_data_mux_i ( + .data_i (req_data_q), + .sel_i (req_addr_q[$clog2(HPDcacheCfg.amoWidth/8) +: AMO_WORD_INDEX_WIDTH]), + .data_o (amo_req_st_data) + ); + + if (HPDcacheCfg.u.userEn) begin : gen_amo_req_st_user_userEn + hpdcache_mux #( + .NINPUT (HPDcacheCfg.reqDataWidth/HPDcacheCfg.amoWidth), + .DATA_WIDTH (1), + .ONE_HOT_SEL (1'b0) + ) amo_ld_data_mux_i ( + .data_i (req_user_q), + .sel_i (req_addr_q[$clog2(HPDcacheCfg.amoWidth/8) +: AMO_WORD_INDEX_WIDTH]), + .data_o (amo_req_st_user) + ); + end else begin : gen_amo_req_st_user_default + assign amo_req_st_user = '0; + end + end else begin : gen_amo_data_width_leq_amo_width + assign amo_req_ld_data = rsp_rdata_q; + assign amo_req_ld_user = HPDcacheCfg.u.userEn ? rsp_ruser_q : '0; + assign amo_req_st_data = req_data_q; + assign amo_req_st_user = HPDcacheCfg.u.userEn ? req_user_q : '0; + end + + assign amo_ld_data = prepare_amo_data_operand(amo_req_ld_data, req_size_q, + req_addr_q, amo_need_sign_extend(req_op_q)); + assign amo_ld_user = req_is_cap ? amo_req_ld_user : '0; + assign amo_st_data = prepare_amo_data_operand(amo_req_st_data, req_size_q, + req_addr_q, amo_need_sign_extend(req_op_q)); + assign amo_st_user = req_is_cap ? amo_req_st_user : '0; + + hpdcache_amo #( + .DATA_WIDTH (HPDcacheCfg.amoWidth), + .ARITH_WIDTH (64), + .user_t (hpdcache_req_user_t), + .userEn (HPDcacheCfg.u.userEn) + ) amo_unit_i ( + .ld_data_i (amo_ld_data), + .ld_user_i (amo_ld_user), + .st_data_i (amo_st_data), + .st_user_i (amo_st_user), + .op_i (req_op_q), + .result_o (amo_result_data), + .user_o (amo_result_user) + ); + + assign dir_amo_match_o = (uc_fsm_q == UC_AMO_READ_DIR); + assign dir_amo_match_set_o = req_addr_q[HPDcacheCfg.clOffsetWidth +: HPDcacheCfg.setWidth]; + assign dir_amo_match_tag_o = req_addr_q[(HPDcacheCfg.clOffsetWidth + HPDcacheCfg.setWidth) +: + HPDcacheCfg.tagWidth]; + assign dir_amo_updt_sel_victim_o = (uc_fsm_q == UC_AMO_WRITE_DATA); + + assign data_amo_write_o = (uc_fsm_q == UC_AMO_WRITE_DATA); + assign data_amo_write_enable_o = |dir_amo_hit_way_i; + assign data_amo_write_set_o = req_addr_q[HPDcacheCfg.clOffsetWidth +: HPDcacheCfg.setWidth]; + assign data_amo_write_size_o = req_size_q; + assign data_amo_write_word_o = req_addr_q[HPDcacheCfg.wordByteIdxWidth +: + HPDcacheCfg.clWordIdxWidth]; + assign data_amo_write_be_o = req_be_q; + + assign amo_write_data = prepare_amo_data_result(amo_result_data, req_size_q); + assign amo_write_user = req_is_cap ? amo_result_user : '0; + if (HPDcacheCfg.reqDataWidth >= HPDcacheCfg.amoWidth) begin : gen_amo_ram_write_data_ge_amo_width + assign data_amo_write_data_o = {HPDcacheCfg.reqDataWidth/HPDcacheCfg.amoWidth{amo_write_data}}; + assign data_amo_write_user_o = {HPDcacheCfg.reqDataWidth/HPDcacheCfg.amoWidth{amo_write_user}}; + end else begin : gen_amo_ram_write_data_lt_amo_width + assign data_amo_write_data_o = amo_write_data; + assign data_amo_write_user_o = amo_write_user; + end +// }}} + +// Core response outputs +// {{{ + assign req_ready_o = (uc_fsm_q == UC_IDLE), + core_rsp_valid_o = (uc_fsm_q == UC_CORE_RSP); +// }}} + +// Memory read request outputs +// {{{ + always_comb + begin : mem_req_read_comb + mem_req_read_o.mem_req_addr = req_addr_q; + mem_req_read_o.mem_req_len = multiflit ? 1 : 0; + mem_req_read_o.mem_req_size = multiflit ? hpdcache_req_size_t'(3) : req_size_q; + mem_req_read_o.mem_req_id = mem_read_id_i; + mem_req_read_o.mem_req_cacheable = 1'b0; + mem_req_read_o.mem_req_command = HPDCACHE_MEM_READ; + mem_req_read_o.mem_req_atomic = HPDCACHE_MEM_ATOMIC_ADD; + + unique case (1'b1) + req_op_q.is_ld: begin + mem_req_read_valid_o = (uc_fsm_q == UC_MEM_REQ); + end + req_op_q.is_amo_lr: begin + mem_req_read_o.mem_req_command = HPDCACHE_MEM_ATOMIC; + mem_req_read_o.mem_req_atomic = HPDCACHE_MEM_ATOMIC_LDEX; + mem_req_read_valid_o = (uc_fsm_q == UC_MEM_REQ); + end + default: begin + mem_req_read_valid_o = 1'b0; + end + endcase + end +// }}} + +// Memory write request outputs +// {{{ + always_comb + begin : mem_req_write_comb + mem_req_write_data = req_data_q; + mem_req_write_user = req_user_q; + mem_req_write_o.mem_req_addr = req_addr_q; + mem_req_write_o.mem_req_len = multiflit ? 1 : 0; + mem_req_write_o.mem_req_size = multiflit ? hpdcache_req_size_t'(3) : req_size_q; + mem_req_write_o.mem_req_id = mem_write_id_i; + mem_req_write_o.mem_req_cacheable = 1'b0; + unique case (1'b1) + req_op_q.is_amo_sc: begin + mem_req_write_o.mem_req_command = HPDCACHE_MEM_ATOMIC; + mem_req_write_o.mem_req_atomic = HPDCACHE_MEM_ATOMIC_STEX; + end + req_op_q.is_amo_swap: begin + mem_req_write_o.mem_req_command = HPDCACHE_MEM_ATOMIC; + mem_req_write_o.mem_req_atomic = HPDCACHE_MEM_ATOMIC_SWAP; + end + req_op_q.is_amo_add: begin + mem_req_write_o.mem_req_command = HPDCACHE_MEM_ATOMIC; + mem_req_write_o.mem_req_atomic = HPDCACHE_MEM_ATOMIC_ADD; + end + req_op_q.is_amo_and: begin + mem_req_write_data = ~mem_req_write_data; + mem_req_write_o.mem_req_command = HPDCACHE_MEM_ATOMIC; + mem_req_write_o.mem_req_atomic = HPDCACHE_MEM_ATOMIC_CLR; + end + req_op_q.is_amo_or: begin + mem_req_write_o.mem_req_command = HPDCACHE_MEM_ATOMIC; + mem_req_write_o.mem_req_atomic = HPDCACHE_MEM_ATOMIC_SET; + end + req_op_q.is_amo_xor: begin + mem_req_write_o.mem_req_command = HPDCACHE_MEM_ATOMIC; + mem_req_write_o.mem_req_atomic = HPDCACHE_MEM_ATOMIC_EOR; + end + req_op_q.is_amo_max: begin + mem_req_write_o.mem_req_command = HPDCACHE_MEM_ATOMIC; + mem_req_write_o.mem_req_atomic = HPDCACHE_MEM_ATOMIC_SMAX; + end + req_op_q.is_amo_maxu: begin + mem_req_write_o.mem_req_command = HPDCACHE_MEM_ATOMIC; + mem_req_write_o.mem_req_atomic = HPDCACHE_MEM_ATOMIC_UMAX; + end + req_op_q.is_amo_min: begin + mem_req_write_o.mem_req_command = HPDCACHE_MEM_ATOMIC; + mem_req_write_o.mem_req_atomic = HPDCACHE_MEM_ATOMIC_SMIN; + end + req_op_q.is_amo_minu: begin + mem_req_write_o.mem_req_command = HPDCACHE_MEM_ATOMIC; + mem_req_write_o.mem_req_atomic = HPDCACHE_MEM_ATOMIC_UMIN; + end + default: begin + mem_req_write_o.mem_req_command = HPDCACHE_MEM_WRITE; + mem_req_write_o.mem_req_atomic = HPDCACHE_MEM_ATOMIC_ADD; + end + endcase + + unique case (uc_fsm_q) + UC_MEM_REQ: begin + unique case (1'b1) + req_op_q.is_st, + req_op_q.is_amo_sc, + req_op_q.is_amo_swap, + req_op_q.is_amo_add, + req_op_q.is_amo_and, + req_op_q.is_amo_or, + req_op_q.is_amo_xor, + req_op_q.is_amo_max, + req_op_q.is_amo_maxu, + req_op_q.is_amo_min, + req_op_q.is_amo_minu: begin + mem_req_write_data_valid_o = 1'b1; + mem_req_write_valid_o = 1'b1; + end + + default: begin + mem_req_write_data_valid_o = 1'b0; + mem_req_write_valid_o = 1'b0; + end + endcase + end + + UC_MEM_W_REQ: begin + mem_req_write_valid_o = 1'b1; + mem_req_write_data_valid_o = 1'b0; + end + + UC_MEM_WDATA_REQ: begin + mem_req_write_valid_o = 1'b0; + mem_req_write_data_valid_o = 1'b1; + end + + UC_MEM_WDATA2_REQ: begin + mem_req_write_valid_o = 1'b0; + mem_req_write_data_valid_o = 1'b1; + end + + UC_MEM_W_AND_WDATA2_REQ: begin + mem_req_write_valid_o = 1'b1; + mem_req_write_data_valid_o = 1'b1; + end + + default: begin + mem_req_write_valid_o = 1'b0; + mem_req_write_data_valid_o = 1'b0; + end + endcase + end + + // memory data width is bigger than the width of the core's interface + if (HPDcacheCfg.u.capAmoEn) begin : gen_mem_req_write_data_capAmoEn + // Currently only support REQ=128, MEM=64 + if (REQ_MEM_RATIO == 2) begin : gen_downsize_mem_req_data + hpdcache_req_addr_t flit_addr; + assign flit_addr = req_addr_q + (uc_fsm_q inside {UC_MEM_WDATA2_REQ, UC_MEM_W_AND_WDATA2_REQ} ? 'h8 : 'h0); + assign mem_req_write_data_o.mem_req_w_data = mem_req_write_data >> (flit_addr[$clog2(HPDcacheCfg.u.memDataWidth/8) +: REQ_MEM_WORD_INDEX_WIDTH] * HPDcacheCfg.u.memDataWidth); + assign mem_req_write_data_o.mem_req_w_be = req_be_q >> (flit_addr[$clog2(HPDcacheCfg.u.memDataWidth/8) +: REQ_MEM_WORD_INDEX_WIDTH] * (HPDcacheCfg.u.memDataWidth/8)); + assign mem_req_write_data_o.mem_req_w_user = mem_req_write_user; + end + end else begin : gen_mem_req_write_data_default + // Currently only support MEM >= REQ + if (MEM_REQ_RATIO > 1) begin : gen_upsize_mem_req_data + // replicate data + assign mem_req_write_data_o.mem_req_w_data = {MEM_REQ_RATIO{mem_req_write_data}}; + assign mem_req_write_data_o.mem_req_w_user = mem_req_write_user; + + // demultiplex the byte-enable + hpdcache_demux #( + .NOUTPUT (MEM_REQ_RATIO), + .DATA_WIDTH (HPDcacheCfg.reqDataWidth/8) + ) mem_write_be_demux_i ( + .data_i (req_be_q), + .sel_i (req_addr_q[$clog2(HPDcacheCfg.reqDataWidth/8) +: + MEM_REQ_WORD_INDEX_WIDTH]), + .data_o (mem_req_write_data_o.mem_req_w_be) + ); + end + // memory data width is equal to the width of the core's interface + else if (MEM_REQ_RATIO == 1) begin : gen_eqsize_mem_req_data + assign mem_req_write_data_o.mem_req_w_data = mem_req_write_data; + assign mem_req_write_data_o.mem_req_w_be = req_be_q; + assign mem_req_write_data_o.mem_req_w_user = mem_req_write_user; + end + end + + assign mem_req_write_data_o.mem_req_w_last = !multiflit || uc_fsm_q inside {UC_MEM_WDATA2_REQ, UC_MEM_W_AND_WDATA2_REQ}; +// }}} + +// Response handling +// {{{ + logic [HPDcacheCfg.amoWidth-1:0] sc_retcode; + logic [HPDcacheCfg.amoWidth-1:0] sc_rdata_dword; + hpdcache_req_data_t sc_rdata; + + assign sc_retcode = {{HPDcacheCfg.amoWidth-1{1'b0}}, uc_sc_retcode_q}; + assign sc_rdata_dword = prepare_amo_data_result(sc_retcode, req_size_q); + if (HPDcacheCfg.reqDataWidth >= HPDcacheCfg.amoWidth) begin : gen_sc_rdata_ge_amo_width + assign sc_rdata = {HPDcacheCfg.reqDataWidth/HPDcacheCfg.amoWidth{sc_rdata_dword}}; + end else begin : gen_sc_rdata_lt_amo_width + assign sc_rdata = sc_rdata_dword; + end + + assign core_rsp_o.rdata = req_op_q.is_amo_sc ? sc_rdata : rsp_rdata_q; + assign core_rsp_o.ruser = req_op_q.is_amo_sc ? 1'b0 : rsp_ruser_q; + assign core_rsp_o.sid = req_sid_q; + assign core_rsp_o.tid = req_tid_q; + assign core_rsp_o.error = rsp_error_q; + assign core_rsp_o.aborted = 1'b0; + + if (HPDcacheCfg.u.capAmoEn) begin : gen_rsp_rdata_capAmoEn + // Currently only support REQ=128, MEM=64 + if (REQ_MEM_RATIO == 2) begin : gen_upsize_core_rsp_data + assign rsp_rdata_d = (multiflit && mem_resp_read_i.mem_resp_r_last) ? {mem_resp_read_i.mem_resp_r_data, rsp_rdata_q[0][63:0] /* XXX fixme */} : {2{mem_resp_read_i.mem_resp_r_data}}; + assign rsp_ruser_d = (multiflit && mem_resp_read_i.mem_resp_r_last) ? {rsp_ruser_q && mem_resp_read_i.mem_resp_r_user} : mem_resp_read_i.mem_resp_r_user; + end + end else begin : gen_rsp_rdata_default + // Currently only support MEM >= REQ + // Resize the memory response data to the core response width + // memory data width is bigger than the width of the core's interface + if (MEM_REQ_RATIO > 1) begin : gen_downsize_core_rsp_data + hpdcache_mux #( + .NINPUT (MEM_REQ_RATIO), + .DATA_WIDTH (HPDcacheCfg.reqDataWidth) + ) data_read_rsp_mux_i( + .data_i (mem_resp_read_i.mem_resp_r_data), + .sel_i (req_addr_q[$clog2(HPDcacheCfg.reqDataWidth/8) +: + MEM_REQ_WORD_INDEX_WIDTH]), + .data_o (rsp_rdata_d) + ); + + if (HPDcacheCfg.u.userEn) begin : gen_rsp_ruser_userEn + hpdcache_mux #( + .NINPUT (MEM_REQ_RATIO), + .DATA_WIDTH (HPDcacheCfg.reqWords * HPDcacheCfg.reqWordUserWidth) + ) user_read_rsp_mux_i( + .data_i (mem_resp_read_i.mem_resp_r_user), + .sel_i (req_addr_q[$clog2(HPDcacheCfg.reqDataWidth/8) +: + MEM_REQ_WORD_INDEX_WIDTH]), + .data_o (rsp_ruser_d) + ); + end else begin : gen_rsp_ruser_default + assign rsp_ruser_d = '0; + end + end + // memory data width is equal to the width of the core's interface + else if (MEM_REQ_RATIO == 1) begin : gen_eqsize_core_rsp_data + assign rsp_rdata_d = mem_resp_read_i.mem_resp_r_data; + assign rsp_ruser_d = HPDcacheCfg.u.userEn ? mem_resp_read_i.mem_resp_r_user : '0; + end + end + + // This FSM is always ready to accept the response + assign mem_resp_read_ready_o = 1'b1, + mem_resp_write_ready_o = 1'b1; +// }}} + +// Set cache request registers +// {{{ + always_ff @(posedge clk_i) + begin : req_data_ff + if (req_valid_i && req_ready_o) begin + req_data_q <= req_data_i; + req_user_q <= req_user_i; + req_be_q <= req_be_i; + req_sid_q <= req_sid_i; + req_tid_q <= req_tid_i; + end + end + + always_ff @(posedge clk_i or negedge rst_ni) + begin : req_ctrl_ff + if (!rst_ni) begin + req_op_q <= '0; + req_addr_q <= '0; + req_size_q <= '0; + req_uc_q <= 1'b0; + req_need_rsp_q <= req_need_rsp_i; + end else if (req_valid_i && req_ready_o) begin + req_op_q <= req_op_i; + req_addr_q <= req_addr_i; + req_size_q <= req_size_i; + req_uc_q <= req_uc_i; + req_need_rsp_q <= req_need_rsp_i; + end + end +// }}} + +// Uncacheable request FSM set state +// {{{ + logic lrsc_rsrv_valid_set, lrsc_rsrv_valid_reset; + + assign lrsc_rsrv_valid_set = lrsc_uc_set, + lrsc_rsrv_valid_reset = lrsc_uc_reset | lrsc_snoop_reset; + + always_ff @(posedge clk_i or negedge rst_ni) + begin : uc_fsm_ff + if (!rst_ni) begin + uc_fsm_q <= UC_IDLE; + lrsc_rsrv_valid_q <= 1'b0; + end else begin + uc_fsm_q <= uc_fsm_d; + lrsc_rsrv_valid_q <= (~lrsc_rsrv_valid_q & lrsc_rsrv_valid_set ) | + ( lrsc_rsrv_valid_q & ~lrsc_rsrv_valid_reset); + end + end + + always_ff @(posedge clk_i) + begin : uc_amo_ff + lrsc_rsrv_addr_q <= lrsc_rsrv_addr_d; + uc_sc_retcode_q <= uc_sc_retcode_d; + end +// }}} + +// Response registers +// {{{ + always_ff @(posedge clk_i) + begin + if (mem_resp_read_valid_i) begin + rsp_rdata_q <= rsp_rdata_d; + rsp_ruser_q <= rsp_ruser_d; + end + mem_resp_write_valid_q <= mem_resp_write_valid_d; + mem_resp_read_valid_q <= mem_resp_read_valid_d; + end + + always_ff @(posedge clk_i or negedge rst_ni) + begin + if (!rst_ni) begin + rsp_error_q <= 1'b0; + end else begin + rsp_error_q <= (~rsp_error_q & rsp_error_set) | + ( rsp_error_q & ~rsp_error_rst); + end + end +// }}} + +// Assertions +// {{{ +`ifndef HPDCACHE_ASSERT_OFF + assert property (@(posedge clk_i) disable iff (rst_ni !== 1'b1) + (req_valid_i && req_op_i.is_ld) -> req_uc_i) else + $error("uc_handler: unexpected load request on cacheable region"); + + assert property (@(posedge clk_i) disable iff (rst_ni !== 1'b1) + (req_valid_i && req_op_i.is_st) -> req_uc_i) else + $error("uc_handler: unexpected store request on cacheable region"); + + assert property (@(posedge clk_i) disable iff (rst_ni !== 1'b1) + (req_valid_i && (req_op_i.is_amo_lr || + req_op_i.is_amo_sc || + req_op_i.is_amo_swap || + req_op_i.is_amo_add || + req_op_i.is_amo_and || + req_op_i.is_amo_or || + req_op_i.is_amo_xor || + req_op_i.is_amo_max || + req_op_i.is_amo_maxu || + req_op_i.is_amo_min || + req_op_i.is_amo_minu )) -> req_need_rsp_i) else + $error("uc_handler: amo requests shall need a response"); + + assert property (@(posedge clk_i) disable iff (rst_ni !== 1'b1) + (req_valid_i && (req_op_i.is_amo_lr || + req_op_i.is_amo_sc || + req_op_i.is_amo_swap || + req_op_i.is_amo_add || + req_op_i.is_amo_and || + req_op_i.is_amo_or || + req_op_i.is_amo_xor || + req_op_i.is_amo_max || + req_op_i.is_amo_maxu || + req_op_i.is_amo_min || + req_op_i.is_amo_minu )) -> (req_size_i inside {0,1,2,3,4})) else + $error("uc_handler: amo requests shall be at most 16 bytes wide"); + + assert property (@(posedge clk_i) disable iff (rst_ni !== 1) + (HPDcacheCfg.u.userEn -> HPDcacheCfg.reqDataWidth == 128)) else + $error("uc_handler: user_bits only handled as tags on 128-bit access width"); + + assert property (@(posedge clk_i) disable iff (rst_ni !== 1) + ((HPDcacheCfg.reqDataWidth == 128) -> $bits(hpdcache_req_user_t) == 1)) else + $error("uc_handler: must have exactly one tag bit with 128-bit access width"); + + assert property (@(posedge clk_i) disable iff (rst_ni !== 1) + (mem_resp_write_valid_i || mem_resp_read_valid_i) -> (uc_fsm_q == UC_MEM_WAIT_RSP)) else + $error("uc_handler: unexpected response from memory"); +`endif +// }}} + +endmodule diff --git a/hw/vendor/hpdcache/rtl/src/hpdcache_victim_plru.sv b/hw/vendor/hpdcache/rtl/src/hpdcache_victim_plru.sv new file mode 100644 index 00000000..9ea6c631 --- /dev/null +++ b/hw/vendor/hpdcache/rtl/src/hpdcache_victim_plru.sv @@ -0,0 +1,146 @@ +/* + * Copyright 2023 CEA* + * *Commissariat a l'Energie Atomique et aux Energies Alternatives (CEA) + * Copyright 2025 Inria, Universite Grenoble-Alpes, TIMA + * + * SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 + * + * Licensed under the Solderpad Hardware License v 2.1 (the “License”); you + * may not use this file except in compliance with the License, or, at your + * option, the Apache License version 2.0. You may obtain a copy of the + * License at + * + * https://solderpad.org/licenses/SHL-2.1/ + * + * Unless required by applicable law or agreed to in writing, any work + * distributed under the License is distributed on an “AS IS” BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +/* + * Authors : Cesar Fuguet + * Creation Date : May, 2021 + * Description : HPDcache Pseudo-LRU replacement policy + * History : + */ +module hpdcache_victim_plru +import hpdcache_pkg::*; + // Parameters + // {{{ +#( + parameter hpdcache_cfg_t HPDcacheCfg = '0, + + localparam type set_t = logic [$clog2(HPDcacheCfg.u.sets)-1:0], + localparam type way_vector_t = logic [HPDcacheCfg.u.ways-1:0] +) + // }}} + + // Ports + // {{{ +( + input logic clk_i, + input logic rst_ni, + + // PLRU update interface + input logic updt_i, + input set_t updt_set_i, + input way_vector_t updt_way_i, + + // Victim selection interface + input logic sel_victim_i, /* unused */ + input way_vector_t sel_dir_valid_i, + input way_vector_t sel_dir_wback_i, + input way_vector_t sel_dir_dirty_i, + input way_vector_t sel_dir_fetch_i, + input set_t sel_victim_set_i, + output way_vector_t sel_victim_way_o +); + // }}} + + // Internal signals and registers + // {{{ + way_vector_t [HPDcacheCfg.u.sets-1:0] plru_q, plru_d; + + way_vector_t updt_plru; + logic unused_available, plru_available, clean_available, dirty_available; + way_vector_t unused_ways, plru_ways, clean_ways, dirty_ways; + way_vector_t unused_victim_way, plru_victim_way, clean_victim_way, dirty_victim_way; + // }}} + + // Victim way selection + // {{{ + assign unused_ways = ~sel_dir_fetch_i & ~sel_dir_valid_i; + assign plru_ways = ~sel_dir_fetch_i & sel_dir_valid_i & ~plru_q[sel_victim_set_i]; + assign clean_ways = ~sel_dir_fetch_i & sel_dir_valid_i & ~sel_dir_dirty_i; + assign dirty_ways = ~sel_dir_fetch_i & sel_dir_valid_i & sel_dir_dirty_i; + + hpdcache_prio_1hot_encoder #(.N(HPDcacheCfg.u.ways)) + unused_victim_select_i( + .val_i (unused_ways), + .val_o (unused_victim_way) + ); + + hpdcache_prio_1hot_encoder #(.N(HPDcacheCfg.u.ways)) + plru_victim_select_i( + .val_i (plru_ways), + .val_o (plru_victim_way) + ); + + hpdcache_prio_1hot_encoder #(.N(HPDcacheCfg.u.ways)) + clean_victim_select_i( + .val_i (clean_ways), + .val_o (clean_victim_way) + ); + + hpdcache_prio_1hot_encoder #(.N(HPDcacheCfg.u.ways)) + dirty_victim_select_i( + .val_i (dirty_ways), + .val_o (dirty_victim_way) + ); + + assign unused_available = |unused_ways; + assign plru_available = |plru_ways; + assign clean_available = |clean_ways; + assign dirty_available = |dirty_ways; + + always_comb + begin : victim_way_selection_comb + priority case (1'b1) + unused_available: sel_victim_way_o = unused_victim_way; + plru_available: sel_victim_way_o = plru_victim_way; + clean_available: sel_victim_way_o = clean_victim_way; + dirty_available: sel_victim_way_o = dirty_victim_way; + default: sel_victim_way_o = '0; + endcase + end + // }}} + + // Pseudo-LRU update process + // {{{ + assign updt_plru = plru_q[updt_set_i] | updt_way_i; + + always_comb + begin : plru_update_comb + plru_d = plru_q; + + // When accessing a cache-line, set the corresponding PLRU bit + // If all PLRU bits of a given set are equal to 1, reset them all + // but the currently accessed way + if (updt_i) plru_d[updt_set_i] = &updt_plru ? updt_way_i : updt_plru; + end + // }}} + + // Set state process + // {{{ + always_ff @(posedge clk_i or negedge rst_ni) + begin : lru_ff + if (!rst_ni) begin + plru_q <= '0; + end else begin + if (updt_i) plru_q <= plru_d; + end + end + // }}} + +endmodule diff --git a/hw/vendor/hpdcache/rtl/src/hpdcache_victim_random.sv b/hw/vendor/hpdcache/rtl/src/hpdcache_victim_random.sv new file mode 100644 index 00000000..11b38dfa --- /dev/null +++ b/hw/vendor/hpdcache/rtl/src/hpdcache_victim_random.sv @@ -0,0 +1,126 @@ +/* + * Copyright 2023,2024 CEA* + * *Commissariat a l'Energie Atomique et aux Energies Alternatives (CEA) + * Copyright 2025 Inria, Universite Grenoble-Alpes, TIMA + * + * SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 + * + * Licensed under the Solderpad Hardware License v 2.1 (the “License”); you + * may not use this file except in compliance with the License, or, at your + * option, the Apache License version 2.0. You may obtain a copy of the + * License at + * + * https://solderpad.org/licenses/SHL-2.1/ + * + * Unless required by applicable law or agreed to in writing, any work + * distributed under the License is distributed on an “AS IS” BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +/* + * Authors : Cesar Fuguet + * Creation Date : September, 2024 + * Description : HPDcache pseudo-random replacement policy + * History : + */ +module hpdcache_victim_random +import hpdcache_pkg::*; + // Parameters + // {{{ +#( + parameter hpdcache_cfg_t HPDcacheCfg = '0, + + localparam type set_t = logic [$clog2(HPDcacheCfg.u.sets)-1:0], + localparam type way_vector_t = logic [HPDcacheCfg.u.ways-1:0] +) + // }}} + + // Ports + // {{{ +( + input logic clk_i, + input logic rst_ni, + + // Update interface + input logic updt_i, /* unused */ + input set_t updt_set_i, /* unused */ + input way_vector_t updt_way_i, /* unused */ + + // Victim selection interface + input logic sel_victim_i, + input way_vector_t sel_dir_valid_i, + input way_vector_t sel_dir_wback_i, /* unused */ + input way_vector_t sel_dir_dirty_i, + input way_vector_t sel_dir_fetch_i, + input set_t sel_victim_set_i, /* unused */ + output way_vector_t sel_victim_way_o +); + // }}} + + // Internal signals and registers + // {{{ + logic unused_available, rand_available, clean_available, dirty_available; + way_vector_t unused_ways, rand_ways, clean_ways, dirty_ways; + way_vector_t unused_victim_way, rand_victim_way, clean_victim_way, dirty_victim_way; + logic [7:0] lfsr_val; + // }}} + + // Victim way selection + // {{{ + assign unused_ways = ~sel_dir_fetch_i & ~sel_dir_valid_i; + assign rand_ways = ~sel_dir_fetch_i & sel_dir_valid_i & rand_victim_way; + assign clean_ways = ~sel_dir_fetch_i & sel_dir_valid_i & ~sel_dir_dirty_i; + assign dirty_ways = ~sel_dir_fetch_i & sel_dir_valid_i & sel_dir_dirty_i; + + hpdcache_lfsr #(.WIDTH(8)) + lfsr_i( + .clk_i, + .rst_ni, + .shift_i (sel_victim_i & ~unused_available & rand_available), + .val_o (lfsr_val) + ); + + hpdcache_decoder #(.N(HPDcacheCfg.wayIndexWidth)) + rand_way_decoder_i( + .en_i (1'b1), + .val_i (lfsr_val[0 +: HPDcacheCfg.wayIndexWidth]), + .val_o (rand_victim_way) + ); + + hpdcache_prio_1hot_encoder #(.N(HPDcacheCfg.u.ways)) + unused_victim_select_i( + .val_i (unused_ways), + .val_o (unused_victim_way) + ); + + hpdcache_prio_1hot_encoder #(.N(HPDcacheCfg.u.ways)) + clean_victim_select_i( + .val_i (clean_ways), + .val_o (clean_victim_way) + ); + + hpdcache_prio_1hot_encoder #(.N(HPDcacheCfg.u.ways)) + dirty_victim_select_i( + .val_i (dirty_ways), + .val_o (dirty_victim_way) + ); + + assign unused_available = |unused_ways; + assign rand_available = |rand_ways; + assign clean_available = |clean_ways; + assign dirty_available = |dirty_ways; + + always_comb + begin : victim_way_selection_comb + priority case (1'b1) + unused_available: sel_victim_way_o = unused_victim_way; + rand_available: sel_victim_way_o = rand_victim_way; + clean_available: sel_victim_way_o = clean_victim_way; + dirty_available: sel_victim_way_o = dirty_victim_way; + default: sel_victim_way_o = '0; + endcase + end + // }}} + +endmodule diff --git a/hw/vendor/hpdcache/rtl/src/hpdcache_victim_sel.sv b/hw/vendor/hpdcache/rtl/src/hpdcache_victim_sel.sv new file mode 100644 index 00000000..fa8c8fb8 --- /dev/null +++ b/hw/vendor/hpdcache/rtl/src/hpdcache_victim_sel.sv @@ -0,0 +1,122 @@ +/* + * Copyright 2023 CEA* + * *Commissariat a l'Energie Atomique et aux Energies Alternatives (CEA) + * Copyright 2025 Inria, Universite Grenoble-Alpes, TIMA + * + * SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 + * + * Licensed under the Solderpad Hardware License v 2.1 (the “License”); you + * may not use this file except in compliance with the License, or, at your + * option, the Apache License version 2.0. You may obtain a copy of the + * License at + * + * https://solderpad.org/licenses/SHL-2.1/ + * + * Unless required by applicable law or agreed to in writing, any work + * distributed under the License is distributed on an “AS IS” BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +/* + * Authors : Cesar Fuguet + * Creation Date : Mars, 2024 + * Description : HPDcache Victim Selection + * History : + */ +module hpdcache_victim_sel +import hpdcache_pkg::*; +// Parameters +// {{{ +#( + parameter hpdcache_cfg_t HPDcacheCfg = '0, + + parameter type hpdcache_set_t = logic, + parameter type hpdcache_way_vector_t = logic +) +// }}} + +// Ports +// {{{ +( + input logic clk_i, + input logic rst_ni, + + // Victim policy update interface + input logic updt_i, + input hpdcache_set_t updt_set_i, + input hpdcache_way_vector_t updt_way_i, + + // Victim selection interface + input logic sel_victim_i, + input hpdcache_way_vector_t sel_dir_valid_i, + input hpdcache_way_vector_t sel_dir_wback_i, + input hpdcache_way_vector_t sel_dir_dirty_i, + input hpdcache_way_vector_t sel_dir_fetch_i, + input hpdcache_set_t sel_victim_set_i, + output hpdcache_way_vector_t sel_victim_way_o +); +// }}} + + // ----------------------------------------------------------------------- + // Direct mapped cache (one way) + if (HPDcacheCfg.u.ways == 1) + begin : gen_single_way_victim_sel + assign sel_victim_way_o = 1'b1; + end + + // ----------------------------------------------------------------------- + // Set-associative cache with pseudo random victim selection + else if (HPDcacheCfg.u.victimSel == HPDCACHE_VICTIM_RANDOM) + begin : gen_random_victim_sel + hpdcache_victim_random #( + .HPDcacheCfg (HPDcacheCfg) + ) victim_rand_i( + .clk_i, + .rst_ni, + + .updt_i, + .updt_set_i, + .updt_way_i, + + .sel_victim_i, + .sel_dir_valid_i, + .sel_dir_wback_i, + .sel_dir_dirty_i, + .sel_dir_fetch_i, + .sel_victim_set_i, + .sel_victim_way_o + ); + end + + // ----------------------------------------------------------------------- + // Set-associative cache with pseudo least-recently-used victim selection + else if (HPDcacheCfg.u.victimSel == HPDCACHE_VICTIM_PLRU) + begin : gen_plru_victim_sel + hpdcache_victim_plru #( + .HPDcacheCfg (HPDcacheCfg) + ) victim_plru_i( + .clk_i, + .rst_ni, + + .updt_i, + .updt_set_i, + .updt_way_i, + + .sel_victim_i, + .sel_dir_valid_i, + .sel_dir_wback_i, + .sel_dir_dirty_i, + .sel_dir_fetch_i, + .sel_victim_set_i, + .sel_victim_way_o + ); + end + +`ifndef HPDCACHE_ASSERT_OFF + initial victim_sel_assert: + assert (HPDcacheCfg.u.victimSel inside {HPDCACHE_VICTIM_RANDOM, HPDCACHE_VICTIM_PLRU}) + else $fatal(1, "unsupported victim selection policy"); +`endif + +endmodule diff --git a/hw/vendor/hpdcache/rtl/src/hpdcache_wbuf.sv b/hw/vendor/hpdcache/rtl/src/hpdcache_wbuf.sv new file mode 100644 index 00000000..77152462 --- /dev/null +++ b/hw/vendor/hpdcache/rtl/src/hpdcache_wbuf.sv @@ -0,0 +1,763 @@ +/* + * Copyright 2023 CEA* + * *Commissariat a l'Energie Atomique et aux Energies Alternatives (CEA) + * Copyright 2025 Inria, Universite Grenoble-Alpes, TIMA + * + * SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 + * + * Licensed under the Solderpad Hardware License v 2.1 (the “License”); you + * may not use this file except in compliance with the License, or, at your + * option, the Apache License version 2.0. You may obtain a copy of the + * License at + * + * https://solderpad.org/licenses/SHL-2.1/ + * + * Unless required by applicable law or agreed to in writing, any work + * distributed under the License is distributed on an “AS IS” BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +/* + * Authors : Cesar Fuguet + * Creation Date : April, 2021 + * Description : HPDcache Write Buffer + * History : + */ +module hpdcache_wbuf +import hpdcache_pkg::*; + // Parameters + // {{{ +#( + parameter hpdcache_cfg_t HPDcacheCfg = '0, + + parameter type wbuf_addr_t = logic, + parameter type wbuf_timecnt_t = logic, + + parameter type hpdcache_mem_id_t = logic, + parameter type hpdcache_mem_req_t = logic, + parameter type hpdcache_mem_req_w_t = logic, + parameter type hpdcache_mem_resp_w_t = logic, + + localparam int unsigned WBUF_WORD_WIDTH = HPDcacheCfg.u.reqWords*HPDcacheCfg.u.wordWidth, + localparam type wbuf_user_t = logic [HPDcacheCfg.u.reqWords-1:0][HPDcacheCfg.u.wordUserWidth-1:0], + localparam type wbuf_data_t = logic [WBUF_WORD_WIDTH-1:0], + localparam type wbuf_be_t = logic [WBUF_WORD_WIDTH/8-1:0] +) + // }}} + + // Ports + // {{{ +( + // Clock and reset signals + input logic clk_i, + input logic rst_ni, + + // Global control signals + output logic empty_o, + output logic full_o, + input logic flush_all_i, + + // Configuration signals + // Timer threshold + input wbuf_timecnt_t cfg_threshold_i, + // Reset timer on write + input logic cfg_reset_timecnt_on_write_i, + // Sequentialize write-after-write hazards + input logic cfg_sequential_waw_i, + // Inhibit write coalescing + input logic cfg_inhibit_write_coalescing_i, + + // Write interface + input logic write_i, + output logic write_ready_o, + input wbuf_addr_t write_addr_i, + input wbuf_user_t write_user_i, + input wbuf_data_t write_data_i, + input wbuf_be_t write_be_i, // byte-enable + input logic write_uc_i, // uncacheable write + + // Read hit interface + input wbuf_addr_t read_addr_i, + output logic read_hit_o, + input logic read_flush_hit_i, + + // Replay hit interface + input wbuf_addr_t replay_addr_i, + input logic replay_is_read_i, + output logic replay_open_hit_o, + output logic replay_pend_hit_o, + output logic replay_sent_hit_o, + output logic replay_not_ready_o, + + // Memory interface + input logic mem_req_write_ready_i, + output logic mem_req_write_valid_o, + output hpdcache_mem_req_t mem_req_write_o, + + input logic mem_req_write_data_ready_i, + output logic mem_req_write_data_valid_o, + output hpdcache_mem_req_w_t mem_req_write_data_o, + + output logic mem_resp_write_ready_o, + input logic mem_resp_write_valid_i, + input hpdcache_mem_resp_w_t mem_resp_write_i +); + // }}} + + // Definition of constants, types and functions + // {{{ + localparam int unsigned WBUF_DIR_NENTRIES = HPDcacheCfg.u.wbufDirEntries; + localparam int unsigned WBUF_DATA_NENTRIES = HPDcacheCfg.u.wbufDataEntries; + localparam int unsigned WBUF_DATA_NWORDS = HPDcacheCfg.u.wbufWords; + localparam int unsigned WBUF_OFFSET_WIDTH = $clog2((WBUF_WORD_WIDTH/8)*WBUF_DATA_NWORDS); + localparam int unsigned WBUF_TAG_WIDTH = HPDcacheCfg.u.paWidth - WBUF_OFFSET_WIDTH; + localparam int unsigned WBUF_WORD_OFFSET = $clog2(WBUF_WORD_WIDTH/8); + localparam int WBUF_SEND_FIFO_DEPTH = WBUF_DATA_NENTRIES; + localparam int unsigned WBUF_READ_MATCH_WIDTH = HPDcacheCfg.nlineWidth; + localparam int unsigned WBUF_MEM_DATA_RATIO = (HPDcacheCfg.u.memDataWidth+HPDcacheCfg.wbufDataWidth-1)/ + HPDcacheCfg.wbufDataWidth; + localparam int unsigned WBUF_MEM_DATA_WORD_INDEX_WIDTH = $clog2(WBUF_MEM_DATA_RATIO); + + typedef wbuf_user_t [WBUF_DATA_NWORDS-1:0] wbuf_user_buf_t; + typedef wbuf_data_t [WBUF_DATA_NWORDS-1:0] wbuf_data_buf_t; + typedef wbuf_be_t [WBUF_DATA_NWORDS-1:0] wbuf_be_buf_t; + typedef logic unsigned [HPDcacheCfg.wbufDirPtrWidth-1:0] wbuf_dir_ptr_t; + typedef logic unsigned [HPDcacheCfg.wbufDataPtrWidth-1:0] wbuf_data_ptr_t; + typedef logic unsigned [WBUF_TAG_WIDTH-1:0] wbuf_tag_t; + typedef logic unsigned [HPDcacheCfg.nlineWidth-1:0] wbuf_match_t; + + typedef enum logic [1:0] { + WBUF_FREE = 2'b00, // unused/free slot + WBUF_OPEN = 2'b01, // there are pending writes in this slot + WBUF_PEND = 2'b10, // the slot is waiting to be sent + WBUF_SENT = 2'b11 // the slot is sent and waits for the memory acknowledge + } wbuf_state_e; + + typedef struct packed { + wbuf_data_ptr_t ptr; + wbuf_timecnt_t cnt; + wbuf_tag_t tag; + logic uc; + } wbuf_dir_entry_t; + + typedef struct packed { + wbuf_user_buf_t user; + wbuf_data_buf_t data; + wbuf_be_buf_t be; + } wbuf_data_entry_t; + + typedef struct packed { + wbuf_data_ptr_t data_ptr; + wbuf_tag_t data_tag; + } wbuf_send_data_t; + + typedef struct packed { + wbuf_tag_t meta_tag; + wbuf_dir_ptr_t meta_id; + logic meta_uc; + } wbuf_send_meta_t; + + function automatic void wbuf_user_write( + output wbuf_user_buf_t wbuf_ret_user, + input wbuf_user_buf_t wbuf_old_user, + input wbuf_user_buf_t wbuf_new_user, + input wbuf_be_buf_t wbuf_new_be); + for (int unsigned w = 0; w < WBUF_DATA_NWORDS; w++) begin + if (HPDcacheCfg.u.userEn) begin + wbuf_ret_user[w] = |wbuf_new_be[w] ? wbuf_new_user[w] + : wbuf_old_user[w]; + end else wbuf_ret_user[w] = '0; + end + endfunction + + function automatic void wbuf_data_write( + output wbuf_data_buf_t wbuf_ret_data, + output wbuf_be_buf_t wbuf_ret_be, + input wbuf_data_buf_t wbuf_old_data, + input wbuf_be_buf_t wbuf_old_be, + input wbuf_data_buf_t wbuf_new_data, + input wbuf_be_buf_t wbuf_new_be); + for (int unsigned w = 0; w < WBUF_DATA_NWORDS; w++) begin + for (int unsigned b = 0; b < WBUF_WORD_WIDTH/8; b++) begin + wbuf_ret_data[w][b*8 +: 8] = wbuf_new_be[w][b] ? + wbuf_new_data[w][b*8 +: 8] : + wbuf_old_data[w][b*8 +: 8]; + end + wbuf_ret_be[w] = wbuf_old_be[w] | wbuf_new_be[w]; + end + endfunction + + function automatic wbuf_match_t wbuf_tag_to_match_addr(wbuf_tag_t tag); + return tag[WBUF_TAG_WIDTH - 1:WBUF_TAG_WIDTH - WBUF_READ_MATCH_WIDTH]; + endfunction + // }}} + + // Definition of internal wires and registers + // {{{ + wbuf_state_e [WBUF_DIR_NENTRIES-1:0] wbuf_dir_state_q, wbuf_dir_state_d; + wbuf_dir_entry_t [WBUF_DIR_NENTRIES-1:0] wbuf_dir_q, wbuf_dir_d; + logic [WBUF_DATA_NENTRIES-1:0] wbuf_data_valid_q, wbuf_data_valid_d; + wbuf_data_entry_t [WBUF_DATA_NENTRIES-1:0] wbuf_data_q, wbuf_data_d; + + logic wbuf_dir_free; + logic [WBUF_DIR_NENTRIES-1:0] wbuf_dir_free_ptr_bv; + logic wbuf_data_free; + wbuf_data_ptr_t wbuf_data_free_ptr; + logic [WBUF_DATA_NENTRIES-1:0] wbuf_data_free_ptr_bv; + + logic wbuf_data_w_init; + logic wbuf_data_w; + wbuf_data_ptr_t wbuf_data_w_ptr; + + logic wbuf_write_free; + logic wbuf_write_hit_open; + logic wbuf_write_hit_pend; + logic wbuf_write_hit_sent; + wbuf_dir_ptr_t wbuf_write_hit_open_dir_ptr; + wbuf_dir_ptr_t wbuf_write_hit_pend_dir_ptr; + + logic [WBUF_DIR_NENTRIES-1:0] wbuf_dir_free_bv; + logic [WBUF_DIR_NENTRIES-1:0] wbuf_dir_open_bv; + logic [WBUF_DIR_NENTRIES-1:0] wbuf_dir_pend_bv; + logic [WBUF_DIR_NENTRIES-1:0] wbuf_dir_sent_bv; + + logic [WBUF_DIR_NENTRIES-1:0] wbuf_send_grant; + wbuf_send_meta_t [WBUF_DIR_NENTRIES-1:0] wbuf_meta_pend; + wbuf_data_ptr_t [WBUF_DIR_NENTRIES-1:0] wbuf_meta_pend_data_ptr; + wbuf_data_ptr_t wbuf_meta_send_data_ptr; + + logic send_meta_valid; + logic send_meta_ready; + wbuf_send_meta_t wbuf_meta_send, wbuf_meta_send_q; + + logic send_data_ready; + logic send_data_w; + wbuf_send_data_t send_data_d; + wbuf_send_data_t send_data_q; + + wbuf_addr_t send_tag; + wbuf_user_buf_t send_user; + wbuf_data_buf_t send_data; + wbuf_be_buf_t send_be; + + wbuf_dir_ptr_t ack_id; + logic ack_error; + + wbuf_tag_t write_tag; + wbuf_user_buf_t write_user; + wbuf_data_buf_t write_data; + wbuf_be_buf_t write_be; + + logic [WBUF_DIR_NENTRIES-1:0] replay_match; + logic [WBUF_DIR_NENTRIES-1:0] replay_open_hit; + logic [WBUF_DIR_NENTRIES-1:0] replay_pend_hit; + logic [WBUF_DIR_NENTRIES-1:0] replay_sent_hit; + + genvar gen_i; + // }}} + + // Global control signals + // {{{ + for (gen_i = 0; gen_i < WBUF_DIR_NENTRIES; gen_i++) begin : gen_dir_state_bv + assign wbuf_dir_free_bv[gen_i] = (wbuf_dir_state_q[gen_i] == WBUF_FREE); + assign wbuf_dir_open_bv[gen_i] = (wbuf_dir_state_q[gen_i] == WBUF_OPEN); + assign wbuf_dir_pend_bv[gen_i] = (wbuf_dir_state_q[gen_i] == WBUF_PEND); + assign wbuf_dir_sent_bv[gen_i] = (wbuf_dir_state_q[gen_i] == WBUF_SENT); + end + + always_comb + begin : empty_comb + empty_o = 1'b1; + for (int unsigned i = 0; i < WBUF_DIR_NENTRIES; i++) begin + empty_o &= wbuf_dir_free_bv[i]; + end + end + + always_comb + begin : full_comb + full_o = 1'b1; + for (int unsigned i = 0; i < WBUF_DIR_NENTRIES; i++) begin + full_o &= ~wbuf_dir_free_bv[i]; + end + end + // }}} + + // Write control + // {{{ + assign write_tag = write_addr_i[HPDcacheCfg.u.paWidth-1:WBUF_OFFSET_WIDTH]; + assign ack_id = mem_resp_write_i.mem_resp_w_id[0 +: HPDcacheCfg.wbufDirPtrWidth]; + assign ack_error = (mem_resp_write_i.mem_resp_w_error != HPDCACHE_MEM_RESP_OK); + + always_comb + begin : wbuf_write_data_comb + for (int unsigned w = 0; w < WBUF_DATA_NWORDS; w++) begin + write_data[w] = write_data_i; + write_user[w] = HPDcacheCfg.u.userEn ? write_user_i : '0; + end + end + + if (WBUF_OFFSET_WIDTH > WBUF_WORD_OFFSET) begin : gen_wbuf_write_be_gt + always_comb + begin : wbuf_write_be_comb + for (int unsigned w = 0; w < WBUF_DATA_NWORDS; w++) begin + if (w == hpdcache_uint32'(write_addr_i[WBUF_OFFSET_WIDTH-1:WBUF_WORD_OFFSET])) + begin + write_be[w] = write_be_i; + end else begin + write_be[w] = '0; + end + end + end + end else begin : gen_wbuf_write_be_le + always_comb + begin : wbuf_write_be_comb + for (int unsigned w = 0; w < WBUF_DATA_NWORDS; w++) begin + write_be[w] = write_be_i; + end + end + end + + hpdcache_fxarb #( + .N (WBUF_DIR_NENTRIES) + ) dir_free_rrarb_i( + .clk_i, + .rst_ni, + .req_i (wbuf_dir_free_bv), + .gnt_o (wbuf_dir_free_ptr_bv), + .ready_i (write_i & wbuf_write_free) + ); + + hpdcache_fxarb #( + .N (WBUF_DATA_NENTRIES) + ) data_free_rrarb_i( + .clk_i, + .rst_ni, + .req_i (~wbuf_data_valid_q), + .gnt_o (wbuf_data_free_ptr_bv), + .ready_i (write_i & wbuf_write_free) + ); + + hpdcache_1hot_to_binary #( + .N (WBUF_DATA_NENTRIES) + ) data_free_ptr_binary_i( + .val_i (wbuf_data_free_ptr_bv), + .val_o (wbuf_data_free_ptr) + ); + + assign wbuf_dir_free = |wbuf_dir_free_bv; + assign wbuf_data_free = ~(&wbuf_data_valid_q); + + always_comb + begin : wbuf_write_hit_comb + wbuf_write_hit_open = 1'b0; + wbuf_write_hit_pend = 1'b0; + wbuf_write_hit_sent = 1'b0; + + wbuf_write_hit_open_dir_ptr = 0; + wbuf_write_hit_pend_dir_ptr = 0; + for (int unsigned i = 0; i < WBUF_DIR_NENTRIES; i++) begin + if (wbuf_dir_q[i].tag == write_tag) begin + unique case (wbuf_dir_state_q[i]) + WBUF_OPEN: begin + wbuf_write_hit_open = 1'b1; + wbuf_write_hit_open_dir_ptr = wbuf_dir_ptr_t'(i); + end + WBUF_PEND: begin + wbuf_write_hit_pend = 1'b1; + wbuf_write_hit_pend_dir_ptr = wbuf_dir_ptr_t'(i); + end + WBUF_SENT: begin + wbuf_write_hit_sent = 1'b1; + end + default: begin + /* do nothing */ + end + endcase + end + end + end + + // Check if there is a match between the read address and the tag of one + // of the used slots in the write buffer directory + always_comb + begin : read_hit_comb + automatic logic [WBUF_DIR_NENTRIES-1:0] read_hit; + + for (int unsigned i = 0; i < WBUF_DIR_NENTRIES; i++) begin + read_hit[i] = 1'b0; + unique case (wbuf_dir_state_q[i]) + WBUF_OPEN, WBUF_PEND, WBUF_SENT: begin + automatic wbuf_addr_t wbuf_addr; + automatic wbuf_match_t wbuf_tag; + automatic wbuf_match_t read_tag; + + wbuf_addr = wbuf_addr_t'(wbuf_dir_q[i].tag) << WBUF_OFFSET_WIDTH; + read_tag = read_addr_i[HPDcacheCfg.u.paWidth-1:HPDcacheCfg.u.paWidth - + WBUF_READ_MATCH_WIDTH]; + wbuf_tag = wbuf_addr [HPDcacheCfg.u.paWidth-1:HPDcacheCfg.u.paWidth - + WBUF_READ_MATCH_WIDTH]; + read_hit[i] = (read_tag == wbuf_tag) ? 1'b1 : 1'b0; + end + default: begin + /* do nothing */ + end + endcase + end + + read_hit_o = |read_hit; + end + + // Check if there is a match between the replay address and the tag of one + // of the used slots in the write buffer directory + generate + for (gen_i = 0; gen_i < WBUF_DIR_NENTRIES; gen_i++) begin : gen_replay_match + assign replay_match[gen_i] = replay_is_read_i ? + /* replay is read: compare address block tag (e.g. cache line) */ + (wbuf_tag_to_match_addr(wbuf_dir_q[gen_i].tag) == + replay_addr_i[HPDcacheCfg.u.paWidth - 1: + HPDcacheCfg.u.paWidth - WBUF_READ_MATCH_WIDTH]) : + /* replay is write: compare wbuf tag */ + (wbuf_dir_q[gen_i].tag == + replay_addr_i[HPDcacheCfg.u.paWidth - 1: + HPDcacheCfg.u.paWidth - WBUF_TAG_WIDTH]); + + assign replay_open_hit[gen_i] = replay_match[gen_i] & wbuf_dir_open_bv[gen_i]; + assign replay_pend_hit[gen_i] = replay_match[gen_i] & wbuf_dir_pend_bv[gen_i]; + assign replay_sent_hit[gen_i] = replay_match[gen_i] & wbuf_dir_sent_bv[gen_i]; + end + endgenerate + + assign replay_open_hit_o = |replay_open_hit; + assign replay_pend_hit_o = |replay_pend_hit; + assign replay_sent_hit_o = |replay_sent_hit; + + always_comb + begin : replay_wbuf_not_ready_comb + replay_not_ready_o = 1'b0; + if (replay_pend_hit_o) begin + replay_not_ready_o = 1'b1; + end else if (replay_sent_hit_o && cfg_sequential_waw_i) begin + replay_not_ready_o = 1'b1; + end else if (!replay_open_hit_o && (!wbuf_dir_free || !wbuf_data_free)) begin + replay_not_ready_o = 1'b1; + end + end + + assign wbuf_write_free = + wbuf_dir_free + & wbuf_data_free + & ~wbuf_write_hit_open + & ~wbuf_write_hit_pend + & ~(wbuf_write_hit_sent & cfg_sequential_waw_i); + + assign write_ready_o = wbuf_write_free + | ((wbuf_write_hit_open | wbuf_write_hit_pend) + & ~cfg_inhibit_write_coalescing_i); + // }}} + + // Update control + // {{{ + always_comb + begin : wbuf_update_comb + automatic bit timeout; + automatic bit write_hit; + automatic bit read_hit; + automatic bit match_open_ptr; + automatic bit match_pend_ptr; + automatic bit match_free; + automatic bit send; + + timeout = 1'b0; + write_hit = 1'b0; + read_hit = 1'b0; + match_open_ptr = 1'b0; + match_pend_ptr = 1'b0; + match_free = 1'b0; + send = 1'b0; + + wbuf_dir_state_d = wbuf_dir_state_q; + wbuf_dir_d = wbuf_dir_q; + + wbuf_data_w_init = 1'b0; + wbuf_data_w = 1'b0; + wbuf_data_w_ptr = '0; + + for (int unsigned i = 0; i < WBUF_DIR_NENTRIES; i++) begin + unique case (wbuf_dir_state_q[i]) + WBUF_FREE: begin + match_free = wbuf_write_free & wbuf_dir_free_ptr_bv[i]; + + if (write_i && match_free) begin + send = (cfg_threshold_i == 0) + | write_uc_i + | flush_all_i + | cfg_inhibit_write_coalescing_i; + + wbuf_dir_state_d[i] = send ? WBUF_PEND : WBUF_OPEN; + wbuf_dir_d[i].tag = write_tag; + wbuf_dir_d[i].cnt = 0; + wbuf_dir_d[i].ptr = wbuf_data_free_ptr; + wbuf_dir_d[i].uc = write_uc_i; + + wbuf_data_w_init = 1'b1; + wbuf_data_w = 1'b1; + wbuf_data_w_ptr = wbuf_data_free_ptr; + end + end + + WBUF_OPEN: begin + match_open_ptr = (i == hpdcache_uint32'(wbuf_write_hit_open_dir_ptr)); + timeout = (wbuf_dir_q[i].cnt == (cfg_threshold_i - 1)); + read_hit = read_flush_hit_i & wbuf_write_hit_open & match_open_ptr; + write_hit = write_i + & wbuf_write_hit_open + & match_open_ptr + & ~cfg_inhibit_write_coalescing_i; + + if (!flush_all_i) begin + if (write_hit && cfg_reset_timecnt_on_write_i) begin + timeout = 1'b0; + wbuf_dir_d[i].cnt = 0; + end else if (!timeout) begin + wbuf_dir_d[i].cnt = wbuf_dir_q[i].cnt + 1; + end + + if (read_hit | timeout | cfg_inhibit_write_coalescing_i) begin + wbuf_dir_state_d[i] = WBUF_PEND; + end + end else begin + wbuf_dir_state_d[i] = WBUF_PEND; + end + + if (write_hit) begin + wbuf_data_w = 1'b1; + wbuf_data_w_ptr = wbuf_dir_q[i].ptr; + end + end + + WBUF_PEND: begin + match_pend_ptr = (i == hpdcache_uint32'(wbuf_write_hit_pend_dir_ptr)); + write_hit = write_i + & wbuf_write_hit_pend + & match_pend_ptr + & ~cfg_inhibit_write_coalescing_i; + + if (write_hit) begin + wbuf_data_w = 1'b1; + wbuf_data_w_ptr = wbuf_dir_q[i].ptr; + end + + if (wbuf_send_grant[i] && send_data_ready && send_meta_ready) begin + wbuf_dir_state_d[i] = WBUF_SENT; + end + end + + WBUF_SENT: begin + if (mem_resp_write_valid_i && (i == hpdcache_uint32'(ack_id))) begin + wbuf_dir_state_d[i] = WBUF_FREE; + end + end + endcase + end + end + + always_comb + begin : wbuf_data_write_comb + automatic wbuf_be_buf_t buf_be; + + wbuf_data_d = wbuf_data_q; + buf_be = wbuf_data_w_init ? '0 : wbuf_data_q[wbuf_data_w_ptr].be; + + if (wbuf_data_w) begin + wbuf_user_write( + wbuf_data_d[wbuf_data_w_ptr].user, + wbuf_data_q[wbuf_data_w_ptr].user, + write_user, + write_be); + wbuf_data_write( + wbuf_data_d[wbuf_data_w_ptr].data, + wbuf_data_d[wbuf_data_w_ptr].be, + wbuf_data_q[wbuf_data_w_ptr].data, + buf_be, + write_data, + write_be); + end + end + + always_comb + begin : wbuf_data_valid_comb + wbuf_data_valid_d = wbuf_data_valid_q; + + // allocate a free data buffer on new write + if (write_i && wbuf_write_free) begin + wbuf_data_valid_d[wbuf_data_free_ptr] = 1'b1; + end + + // de-allocate a data buffer as soon as it is send + if (mem_req_write_data_valid_o && mem_req_write_data_ready_i) begin + wbuf_data_valid_d[send_data_q.data_ptr] = 1'b0; + end + end + // }}} + + // Send control + // {{{ + for (genvar i = 0; i < WBUF_DIR_NENTRIES; i++) begin : gen_wbuf_dir_pend + assign wbuf_meta_pend[i].meta_tag = wbuf_dir_q[i].tag; + assign wbuf_meta_pend[i].meta_id = i; + assign wbuf_meta_pend[i].meta_uc = wbuf_dir_q[i].uc; + assign wbuf_meta_pend_data_ptr[i] = wbuf_dir_q[i].ptr; + end + + hpdcache_rrarb #( + .N (WBUF_DIR_NENTRIES) + ) pend_rrarb_i( + .clk_i, + .rst_ni, + .req_i (wbuf_dir_pend_bv), + .gnt_o (wbuf_send_grant), + .ready_i (send_data_ready & send_meta_ready) + ); + + hpdcache_mux #( + .NINPUT (WBUF_DIR_NENTRIES), + .DATA_WIDTH ($bits(wbuf_send_meta_t)), + .ONE_HOT_SEL (1'b1) + ) wbuf_send_meta_mux_i( + .data_i (wbuf_meta_pend), + .sel_i (wbuf_send_grant), + .data_o (wbuf_meta_send) + ); + + hpdcache_mux #( + .NINPUT (WBUF_DIR_NENTRIES), + .DATA_WIDTH ($bits(wbuf_data_ptr_t)), + .ONE_HOT_SEL (1'b1) + ) wbuf_send_data_ptr_mux_i( + .data_i (wbuf_meta_pend_data_ptr), + .sel_i (wbuf_send_grant), + .data_o (wbuf_meta_send_data_ptr) + ); + + // Data channel + assign send_data_w = (|wbuf_dir_pend_bv) & send_meta_ready; + assign send_data_d.data_ptr = wbuf_meta_send_data_ptr; + assign send_data_d.data_tag = wbuf_meta_send.meta_tag; + + hpdcache_fifo_reg #( + .FIFO_DEPTH (WBUF_SEND_FIFO_DEPTH), + .FEEDTHROUGH (1'b0), + .fifo_data_t (wbuf_send_data_t) + ) send_data_ptr_fifo_i ( + .clk_i, + .rst_ni, + .w_i (send_data_w), + .wok_o (send_data_ready), + .wdata_i (send_data_d), + .r_i (mem_req_write_data_ready_i), + .rok_o (mem_req_write_data_valid_o), + .rdata_o (send_data_q) + ); + + assign send_tag = wbuf_addr_t'(send_data_q.data_tag); + assign send_data = wbuf_data_q[send_data_q.data_ptr].data; + assign send_be = wbuf_data_q[send_data_q.data_ptr].be; + assign send_user = HPDcacheCfg.u.userEn ? wbuf_data_q[send_data_q.data_ptr].user : '0; + + // Meta-data channel + assign send_meta_valid = (|wbuf_dir_pend_bv) & send_data_ready; + + hpdcache_fifo_reg #( + .FIFO_DEPTH (WBUF_SEND_FIFO_DEPTH), + .FEEDTHROUGH (1'b0), + .fifo_data_t (wbuf_send_meta_t) + ) send_meta_fifo_i ( + .clk_i, + .rst_ni, + .w_i (send_meta_valid), + .wok_o (send_meta_ready), + .wdata_i (wbuf_meta_send), + .r_i (mem_req_write_ready_i), + .rok_o (mem_req_write_valid_o), + .rdata_o (wbuf_meta_send_q) + ); + // }}} + + // Memory Address and Data Interface + // {{{ + assign mem_req_write_o.mem_req_addr = { wbuf_meta_send_q.meta_tag, {WBUF_OFFSET_WIDTH{1'b0}} }; + assign mem_req_write_o.mem_req_len = 0; + assign mem_req_write_o.mem_req_size = get_hpdcache_mem_size(HPDcacheCfg.wbufDataWidth/8); + assign mem_req_write_o.mem_req_id = hpdcache_mem_id_t'(wbuf_meta_send_q.meta_id); + assign mem_req_write_o.mem_req_command = HPDCACHE_MEM_WRITE; + assign mem_req_write_o.mem_req_atomic = HPDCACHE_MEM_ATOMIC_ADD; + assign mem_req_write_o.mem_req_cacheable = ~wbuf_meta_send_q.meta_uc; + + assign mem_req_write_data_o.mem_req_w_last = 1'b1; + + if (WBUF_MEM_DATA_RATIO > 1) begin : gen_wbuf_data_upsizing + logic [HPDcacheCfg.wbufDataWidth/8-1:0][WBUF_MEM_DATA_RATIO-1:0] mem_req_be; + + // demux send BE + hpdcache_demux #( + .NOUTPUT (WBUF_MEM_DATA_RATIO), + .DATA_WIDTH (HPDcacheCfg.wbufDataWidth/8), + .ONE_HOT_SEL (1'b0) + ) mem_write_be_demux_i ( + .data_i (send_be), + .sel_i (send_tag[0 +: WBUF_MEM_DATA_WORD_INDEX_WIDTH]), + .data_o (mem_req_be) + ); + + assign mem_req_write_data_o.mem_req_w_data = {WBUF_MEM_DATA_RATIO{send_data}}, + mem_req_write_data_o.mem_req_w_be = mem_req_be, + mem_req_write_data_o.mem_req_w_user = HPDcacheCfg.u.userEn ? {WBUF_MEM_DATA_RATIO{send_user}} : '0; + + end else if (WBUF_MEM_DATA_RATIO == 1) begin : gen_wbuf_data_forwarding + assign mem_req_write_data_o.mem_req_w_data = send_data, + mem_req_write_data_o.mem_req_w_be = send_be, + mem_req_write_data_o.mem_req_w_user = HPDcacheCfg.u.userEn ? send_user : '0; + end + + assign mem_resp_write_ready_o = 1'b1; + // }}} + + // Internal state assignment + // {{{ + always_ff @(posedge clk_i) wbuf_data_q <= wbuf_data_d; + + always_ff @(posedge clk_i or negedge rst_ni) + begin : wbuf_state_ff + if (!rst_ni) begin + wbuf_dir_q <= '0; + wbuf_dir_state_q <= {WBUF_DIR_NENTRIES{WBUF_FREE}}; + wbuf_data_valid_q <= '0; + end else begin + wbuf_dir_q <= wbuf_dir_d; + wbuf_dir_state_q <= wbuf_dir_state_d; + wbuf_data_valid_q <= wbuf_data_valid_d; + end + end + // }}} + + // Assertions + // {{{ +`ifndef HPDCACHE_ASSERT_OFF + if (!(WBUF_DATA_NWORDS inside {1, 2, 4, 8, 16})) + begin : gen_wbuf_data_words_assertion + $fatal(1, "WBUF: width of data buffers must be a power of 2"); + end + if (HPDcacheCfg.u.memDataWidth < HPDcacheCfg.wbufDataWidth) + begin : gen_mem_data_width_assertion + $fatal(1, "WBUF: width of mem interface shall be g.e. to wbuf width"); + end + ack_sent_assert: assert property (@(posedge clk_i) disable iff (rst_ni !== 1'b1) + (mem_resp_write_valid_i -> (wbuf_dir_state_q[ack_id] == WBUF_SENT))) else + $error("WBUF: acknowledging a not SENT slot"); + send_valid_data_assert: assert property (@(posedge clk_i) disable iff (rst_ni !== 1'b1) + (mem_req_write_data_valid_o -> (wbuf_data_valid_q[send_data_q.data_ptr] == 1'b1))) else + $error("WBUF: sending a not valid data"); +`endif + // }}} +endmodule diff --git a/hw/vendor/hpdcache/rtl/src/hwpf_stride/hwpf_stride.sv b/hw/vendor/hpdcache/rtl/src/hwpf_stride/hwpf_stride.sv new file mode 100644 index 00000000..fd846bd9 --- /dev/null +++ b/hw/vendor/hpdcache/rtl/src/hwpf_stride/hwpf_stride.sv @@ -0,0 +1,379 @@ +/* + * Copyright 2023 CEA* + * *Commissariat a l'Energie Atomique et aux Energies Alternatives (CEA) + * + * SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 + * + * Licensed under the Solderpad Hardware License v 2.1 (the “License”); you + * may not use this file except in compliance with the License, or, at your + * option, the Apache License version 2.0. You may obtain a copy of the + * License at + * + * https://solderpad.org/licenses/SHL-2.1/ + * + * Unless required by applicable law or agreed to in writing, any work + * distributed under the License is distributed on an “AS IS” BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +/* + * Authors : Riccardo Alidori, Cesar Fuguet + * Maintainers(s): Cesar Fuguet + * Creation Date : June, 2021 + * Description : HPDcache Linear Hardware Memory Prefetcher. + * History : + */ +module hwpf_stride +import hwpf_stride_pkg::*; +import hpdcache_pkg::*; +// Parameters +// {{{ +#( + parameter hpdcache_cfg_t HPDcacheCfg = '0, + parameter type hpdcache_nline_t = logic, + parameter type hpdcache_tag_t = logic, + parameter type hpdcache_set_t = logic, + parameter type hpdcache_req_t = logic, + parameter type hpdcache_rsp_t = logic +) +// }}} + +// Ports +// {{{ +( + input logic clk_i, + input logic rst_ni, + + // CSR + input logic csr_base_set_i, + input hwpf_stride_base_t csr_base_i, + input logic csr_param_set_i, + input hwpf_stride_param_t csr_param_i, + input logic csr_throttle_set_i, + input hwpf_stride_throttle_t csr_throttle_i, + + output hwpf_stride_base_t csr_base_o, + output hwpf_stride_param_t csr_param_o, + output hwpf_stride_throttle_t csr_throttle_o, + + // If high, the prefetcher is enabled and active + output logic busy_o, + + // Snooping + // Address to snoop on requests ports + output hpdcache_nline_t snoop_nline_o, + // If set to one, the snoop address matched one of the requests + input logic snoop_match_i, + + // D-Cache interface + output logic hpdcache_req_valid_o, + input logic hpdcache_req_ready_i, + output hpdcache_req_t hpdcache_req_o, + input logic hpdcache_rsp_valid_i, + input hpdcache_rsp_t hpdcache_rsp_i +); +// }}} + + // Definition of constants + // {{{ + localparam int STRIDE_WIDTH = $bits(csr_param_i.stride); + localparam int NBLOCKS_WIDTH = $bits(csr_param_i.nblocks); + localparam int NLINES_WIDTH = $bits(csr_param_i.nlines); + localparam int NWAIT_WIDTH = $bits(csr_throttle_i.nwait); + localparam int INFLIGHT_WIDTH = $bits(csr_throttle_i.ninflight); + localparam int NLINES_CNT_WIDTH = NLINES_WIDTH; + // }}} + + // Internal registers and signals + // {{{ + // FSM + typedef enum { + IDLE, + SNOOP, + SEND_REQ, + WAIT, + DONE, + ABORT + } hwpf_stride_fsm_t; + + hwpf_stride_fsm_t state_d, state_q; + + logic [NBLOCKS_WIDTH-1:0] nblocks_cnt_d, nblocks_cnt_q; + logic [NLINES_CNT_WIDTH-1:0] nlines_cnt_d, nlines_cnt_q; + logic [NWAIT_WIDTH-1:0] nwait_cnt_d, nwait_cnt_q; + logic [INFLIGHT_WIDTH-1:0] inflight_cnt_d, inflight_cnt_q; + logic inflight_inc, inflight_dec; + + hwpf_stride_base_t csr_base_q; + hwpf_stride_base_t shadow_base_q, shadow_base_d; + hwpf_stride_param_t csr_param_q; + hwpf_stride_param_t shadow_param_q, shadow_param_d; + hwpf_stride_throttle_t csr_throttle_q; + hwpf_stride_throttle_t shadow_throttle_q, shadow_throttle_d; + hpdcache_nline_t request_nline_q, request_nline_d; + + hpdcache_set_t hpdcache_req_set; + hpdcache_tag_t hpdcache_req_tag; + + logic csr_base_update; + hpdcache_nline_t increment_stride; + logic is_inflight_max; + + // Default assignment + assign increment_stride = hpdcache_nline_t'(shadow_param_q.stride) + 1'b1; + assign inflight_dec = hpdcache_rsp_valid_i; + assign snoop_nline_o = shadow_base_q.base_cline; + assign is_inflight_max = (shadow_throttle_q.ninflight == '0) ? + 1'b0 : (inflight_cnt_q >= shadow_throttle_q.ninflight); + assign csr_base_o = csr_base_q; + assign csr_param_o = csr_param_q; + assign csr_throttle_o = csr_throttle_q; + // }}} + + // Dcache outputs + // {{{ + assign hpdcache_req_set = request_nline_q[0 +: HPDcacheCfg.setWidth], + hpdcache_req_tag = request_nline_q[HPDcacheCfg.setWidth +: HPDcacheCfg.tagWidth]; + + assign hpdcache_req_o.addr_offset = { hpdcache_req_set, {HPDcacheCfg.clOffsetWidth{1'b0}} }, + hpdcache_req_o.wdata = '0, + hpdcache_req_o.op = HPDCACHE_REQ_CMO_PREFETCH, + hpdcache_req_o.be = '1, + hpdcache_req_o.size = '0, + hpdcache_req_o.sid = '0, // this is set when connecting to the dcache + hpdcache_req_o.tid = '0, // this is set by the wrapper of the prefetcher + hpdcache_req_o.need_rsp = 1'b1, + hpdcache_req_o.phys_indexed = 1'b1, + hpdcache_req_o.addr_tag = hpdcache_req_tag, + hpdcache_req_o.pma.uncacheable = 1'b0, + hpdcache_req_o.pma.io = 1'b0; + // }}} + + // Set state of internal registers + // {{{ + always_ff @(posedge clk_i or negedge rst_ni) + begin + if (!rst_ni) begin + csr_base_q <= '0; + csr_param_q <= '0; + shadow_base_q <= '0; + shadow_param_q <= '0; + shadow_throttle_q <= '0; + request_nline_q <= '0; + state_q <= IDLE; + end else begin + if (csr_base_set_i) csr_base_q <= csr_base_i; + else if (csr_base_update) csr_base_q <= shadow_base_d; + if (csr_param_set_i) csr_param_q <= csr_param_i; + if (csr_throttle_set_i) csr_throttle_q <= csr_throttle_i; + shadow_base_q <= shadow_base_d; + shadow_param_q <= shadow_param_d; + shadow_throttle_q <= shadow_throttle_d; + request_nline_q <= request_nline_d; + state_q <= state_d; + end + end + // }}} + + // Update internal counters + // {{{ + always_comb begin : inflight_cnt + inflight_cnt_d = inflight_cnt_q; + + // Every time we send a dcache request, increment the counter + if ( inflight_inc ) begin + inflight_cnt_d++; + end + + // Every time we got a response from the cache, decrement the counter + if ( inflight_dec && ( inflight_cnt_q > 0 )) begin + inflight_cnt_d--; + end + end + + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + nblocks_cnt_q <= '0; + nlines_cnt_q <= '0; + nwait_cnt_q <= '0; + inflight_cnt_q <= '0; + end else begin + nblocks_cnt_q <= nblocks_cnt_d; + nlines_cnt_q <= nlines_cnt_d; + nwait_cnt_q <= nwait_cnt_d; + inflight_cnt_q <= inflight_cnt_d; + end + end + // }}} + + // FSM + // {{{ + always_comb begin : fsm_control + // default assignments + hpdcache_req_valid_o = 1'b0; + nblocks_cnt_d = nblocks_cnt_q; + nlines_cnt_d = nlines_cnt_q; + nwait_cnt_d = nwait_cnt_q; + inflight_inc = 1'b0; + busy_o = 1'b0; + csr_base_update = 1'b0; + + shadow_base_d = shadow_base_q; + shadow_param_d = shadow_param_q; + shadow_throttle_d = shadow_throttle_q; + request_nline_d = request_nline_q; + state_d = state_q; + + unique case ( state_q ) + + IDLE: begin + // If enabled, go snooping the dcache ports + if ( csr_base_q.enable ) begin + shadow_base_d = csr_base_q; + if (( csr_param_q.nlines > 0 ) || ( csr_param_q.nblocks > 0 )) begin + shadow_param_d = csr_param_q; + shadow_throttle_d = csr_throttle_q; + state_d = SNOOP; + end else begin + // no prefetch needed, disarm immediately + shadow_base_d.enable = 1'b0; + csr_base_update = 1'b1; + end + end + end + + + SNOOP: begin + if ( csr_base_q.enable ) begin + // If a snooper matched an address, send the request + if ( snoop_match_i ) begin + state_d = SEND_REQ; + + if ( shadow_param_q.nlines == 0 ) begin + // skip the first block + request_nline_d = shadow_base_q.base_cline + + hpdcache_nline_t'(increment_stride); + nblocks_cnt_d = ( shadow_param_q.nblocks > 0 ) ? + shadow_param_q.nblocks - 1 : 0; + nlines_cnt_d = 0; + + // update the base cacheline to the first one of the next block + shadow_base_d.base_cline = request_nline_d; + end else begin + // skip the first cacheline (of the first block) + request_nline_d = shadow_base_q.base_cline + 1'b1; + nblocks_cnt_d = shadow_param_q.nblocks; + nlines_cnt_d = shadow_param_q.nlines - 1; + end + end + end else begin + state_d = IDLE; + end + end + + + SEND_REQ: begin + busy_o = 1'b1; + + // make the prefetch request to memory + hpdcache_req_valid_o = 1'b1; + + // we've got a grant, so we can move to the next request + if ( hpdcache_req_ready_i ) begin + inflight_inc = 1'b1; + + if ( nlines_cnt_q == 0 ) begin + // go to the first cacheline of the next block + request_nline_d = shadow_base_q.base_cline + + hpdcache_nline_t'(increment_stride); + nblocks_cnt_d = ( nblocks_cnt_q > 0 ) ? nblocks_cnt_q - 1 : 0; + nlines_cnt_d = shadow_param_q.nlines; + + // update the base cacheline to the first one of the next block + shadow_base_d.base_cline = request_nline_d; + end else begin + // go to the next cacheline (within the same block) + request_nline_d = request_nline_q + 1'b1; + nlines_cnt_d = nlines_cnt_q - 1; + end + + // if the NWAIT parameter is equal 0, we can issue a request every cycle + if (( nblocks_cnt_q == 0 ) && ( nlines_cnt_q == 0 )) begin + state_d = DONE; + end else if ( shadow_throttle_q.nwait == 0 ) begin + // Wait if the number of inflight requests is greater than + // the maximum indicated. Otherwise, send the next request + state_d = is_inflight_max ? WAIT : SEND_REQ; + end else begin + // Wait the indicated cycles before sending the next request + nwait_cnt_d = shadow_throttle_q.nwait; + state_d = WAIT; + end + + if ( !csr_base_q.enable ) state_d = ABORT; + end + end + + + WAIT: begin + // Wait until: + // - the indicated number of wait cycles between requests is reached (nwait) + // - the number of inflight requests is below the indicated maximum (ninflight) + busy_o = 1'b1; + if ( csr_base_q.enable ) begin + if ( !is_inflight_max && ( nwait_cnt_q == 0 )) begin + state_d = SEND_REQ; + end + + if ( nwait_cnt_q > 0 ) begin + nwait_cnt_d = nwait_cnt_q - 1; + end + end else begin + state_d = ABORT; + end + end + + + DONE: begin + busy_o = 1'b1; + if ( csr_base_q.enable ) begin + if (( inflight_cnt_q == 0 ) && !is_inflight_max && ( nwait_cnt_q == 0 )) begin + // Copy back shadow base register into the user visible one + csr_base_update = 1'b1; + + // Check the rearm bit + if ( shadow_base_q.rearm ) begin + state_d = SNOOP; + end else begin + state_d = IDLE; + + // disarm the prefetcher + shadow_base_d.enable = 1'b0; + end + + // Check the cycle bit + if ( shadow_base_q.cycle ) begin + // restore the base address + shadow_base_d.base_cline = csr_base_q.base_cline; + end + end + + if ( nwait_cnt_q > 0 ) begin + nwait_cnt_d = nwait_cnt_q - 1; + end + end else begin + state_d = ABORT; + end + end + + ABORT: begin + busy_o = 1'b1; + if ( inflight_cnt_q == 0 ) begin + state_d = IDLE; + end + end + endcase + end + // }}} +endmodule diff --git a/hw/vendor/hpdcache/rtl/src/hwpf_stride/hwpf_stride_arb.sv b/hw/vendor/hpdcache/rtl/src/hwpf_stride/hwpf_stride_arb.sv new file mode 100644 index 00000000..1fc54967 --- /dev/null +++ b/hw/vendor/hpdcache/rtl/src/hwpf_stride/hwpf_stride_arb.sv @@ -0,0 +1,121 @@ +/* + * Copyright 2023 CEA* + * *Commissariat a l'Energie Atomique et aux Energies Alternatives (CEA) + * + * SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 + * + * Licensed under the Solderpad Hardware License v 2.1 (the “License”); you + * may not use this file except in compliance with the License, or, at your + * option, the Apache License version 2.0. You may obtain a copy of the + * License at + * + * https://solderpad.org/licenses/SHL-2.1/ + * + * Unless required by applicable law or agreed to in writing, any work + * distributed under the License is distributed on an “AS IS” BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +/* + * Author(s) : Riccardo Alidori, Cesar Fuguet + * Creation Date : June, 2021 + * Description : Hw prefetchers arbiter + * History : + */ +module hwpf_stride_arb +import hpdcache_pkg::*; +// Parameters +// {{{ +#( + parameter int NUM_HW_PREFETCH = 4, + + parameter type hpdcache_req_t = logic, + parameter type hpdcache_rsp_t = logic +) +// }}} + +// Ports +// {{{ +( + input logic clk_i, + input logic rst_ni, + + // Dcache input interface + input logic [NUM_HW_PREFETCH-1:0] hwpf_stride_req_valid_i, + output logic [NUM_HW_PREFETCH-1:0] hwpf_stride_req_ready_o, + input hpdcache_req_t [NUM_HW_PREFETCH-1:0] hwpf_stride_req_i, + output logic [NUM_HW_PREFETCH-1:0] hwpf_stride_rsp_valid_o, + output hpdcache_rsp_t [NUM_HW_PREFETCH-1:0] hwpf_stride_rsp_o, // Not used + + // Dcache output interface + output logic hpdcache_req_valid_o, + input logic hpdcache_req_ready_i, + output hpdcache_req_t hpdcache_req_o, + input logic hpdcache_rsp_valid_i, + input hpdcache_rsp_t hpdcache_rsp_i // Not used +); +// }}} + + // Internal signals + // {{{ + logic [NUM_HW_PREFETCH-1:0] hwpf_stride_req_valid; + hpdcache_req_t [NUM_HW_PREFETCH-1:0] hwpf_stride_req; + logic [NUM_HW_PREFETCH-1:0] arb_req_gnt; + // }}} + + // Requesters arbiter + // {{{ + // Pack request ports + genvar gen_i; + generate + for (gen_i = 0; gen_i < NUM_HW_PREFETCH; gen_i++) begin : gen_hwpf_stride_req + assign hwpf_stride_req_ready_o[gen_i] = arb_req_gnt[gen_i] & hpdcache_req_ready_i, + hwpf_stride_req_valid[gen_i] = hwpf_stride_req_valid_i[gen_i], + hwpf_stride_req[gen_i] = hwpf_stride_req_i[gen_i]; + end + endgenerate + + // Arbiter + hpdcache_rrarb #( + .N (NUM_HW_PREFETCH) + ) hwpf_stride_req_arbiter_i ( + .clk_i, + .rst_ni, + .req_i (hwpf_stride_req_valid), + .gnt_o (arb_req_gnt), + .ready_i (hpdcache_req_ready_i) + ); + + // Request Multiplexor + hpdcache_mux #( + .NINPUT (NUM_HW_PREFETCH), + .DATA_WIDTH ($bits(hpdcache_req_t)), + .ONE_HOT_SEL (1'b1) + ) hwpf_stride_req_mux_i ( + .data_i (hwpf_stride_req), + .sel_i (arb_req_gnt), + .data_o (hpdcache_req_o) + ); + + assign hpdcache_req_valid_o = |arb_req_gnt; + // }}} + + // Response demultiplexor + // {{{ + // As the HW prefetcher does not need the TID field in the request, we + // use it to transport the identifier of the specific hardware + // prefetcher. + // This way we share the same SID for all HW prefetchers. Using + // different SIDs means that we need different ports to the cache and + // we actually want to reduce those. + always_comb + begin : resp_demux + for (int unsigned i = 0; i < NUM_HW_PREFETCH; i++) begin + hwpf_stride_rsp_valid_o[i] = hpdcache_rsp_valid_i && + (i == hpdcache_uint32'(hpdcache_rsp_i.tid)); + hwpf_stride_rsp_o[i] = hpdcache_rsp_i; + end + end + // }}} +endmodule diff --git a/hw/vendor/hpdcache/rtl/src/hwpf_stride/hwpf_stride_pkg.sv b/hw/vendor/hpdcache/rtl/src/hwpf_stride/hwpf_stride_pkg.sv new file mode 100644 index 00000000..3470b786 --- /dev/null +++ b/hw/vendor/hpdcache/rtl/src/hwpf_stride/hwpf_stride_pkg.sv @@ -0,0 +1,68 @@ +/* + * Copyright 2023 CEA* + * *Commissariat a l'Energie Atomique et aux Energies Alternatives (CEA) + * + * SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 + * + * Licensed under the Solderpad Hardware License v 2.1 (the “License”); you + * may not use this file except in compliance with the License, or, at your + * option, the Apache License version 2.0. You may obtain a copy of the + * License at + * + * https://solderpad.org/licenses/SHL-2.1/ + * + * Unless required by applicable law or agreed to in writing, any work + * distributed under the License is distributed on an “AS IS” BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +/* + * Authors : Cesar Fuguet + * Creation Date : January, 2023 + * Description : High-Performance, Data-cache (HPDcache) HW memory + * prefetcher package + * History : + */ +package hwpf_stride_pkg; + // Base address configuration register of the hardware memory prefetcher + // {{{ + typedef struct packed { + logic [63:6] base_cline; + logic [5:3] unused; + logic cycle; + logic rearm; + logic enable; + } hwpf_stride_base_t; + // }}} + + // Parameters configuration register of the hardware memory prefetcher + // {{{ + typedef struct packed { + logic [63:48] nblocks; + logic [47:32] nlines; + logic [31:0] stride; + } hwpf_stride_param_t; + // }}} + + // Throttle configuration register of the hardware memory prefetcher + // {{{ + typedef struct packed { + logic [31:16] ninflight; + logic [15:0] nwait; + } hwpf_stride_throttle_t; + // }}} + + // Status register of the hardware memory prefetcher + // {{{ + typedef struct packed { + logic [63:48] unused1; + logic [47:32] busy; + logic free; + logic [30:20] unused0; + logic [19:16] free_index; + logic [15:0] enabled; + } hwpf_stride_status_t; + // }}} + +endpackage diff --git a/hw/vendor/hpdcache/rtl/src/hwpf_stride/hwpf_stride_wrapper.sv b/hw/vendor/hpdcache/rtl/src/hwpf_stride/hwpf_stride_wrapper.sv new file mode 100644 index 00000000..df9cf975 --- /dev/null +++ b/hw/vendor/hpdcache/rtl/src/hwpf_stride/hwpf_stride_wrapper.sv @@ -0,0 +1,289 @@ +/* + * Copyright 2023 CEA* + * *Commissariat a l'Energie Atomique et aux Energies Alternatives (CEA) + * + * SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 + * + * Licensed under the Solderpad Hardware License v 2.1 (the “License”); you + * may not use this file except in compliance with the License, or, at your + * option, the Apache License version 2.0. You may obtain a copy of the + * License at + * + * https://solderpad.org/licenses/SHL-2.1/ + * + * Unless required by applicable law or agreed to in writing, any work + * distributed under the License is distributed on an “AS IS” BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +/* + * Authors : Riccardo Alidori, Cesar Fuguet + * Creation Date : June, 2021 + * Description : Linear Hardware Memory Prefetcher wrapper. + * History : + */ +`include "hpdcache_typedef.svh" + +module hwpf_stride_wrapper +import hwpf_stride_pkg::*; +import hpdcache_pkg::*; +// Parameters +// {{{ +#( + parameter hpdcache_cfg_t HPDcacheCfg = '0, + parameter int unsigned NUM_HW_PREFETCH = 4, + parameter int unsigned NUM_SNOOP_PORTS = 1, + + // Request Interface Definitions + // {{{ + parameter type hpdcache_tag_t = logic, + parameter type hpdcache_req_offset_t = logic, + parameter type hpdcache_req_data_t = logic, + parameter type hpdcache_req_be_t = logic, + parameter type hpdcache_req_sid_t = logic, + parameter type hpdcache_req_tid_t = logic, + parameter type hpdcache_req_t = logic, + parameter type hpdcache_rsp_t = logic + // }}} +) +// }}} + +// Ports +// {{{ +( + input logic clk_i, + input logic rst_ni, + + // CSR + // {{{ + input logic [NUM_HW_PREFETCH-1:0] hwpf_stride_base_set_i, + input hwpf_stride_base_t [NUM_HW_PREFETCH-1:0] hwpf_stride_base_i, + output hwpf_stride_base_t [NUM_HW_PREFETCH-1:0] hwpf_stride_base_o, + + input logic [NUM_HW_PREFETCH-1:0] hwpf_stride_param_set_i, + input hwpf_stride_param_t [NUM_HW_PREFETCH-1:0] hwpf_stride_param_i, + output hwpf_stride_param_t [NUM_HW_PREFETCH-1:0] hwpf_stride_param_o, + + input logic [NUM_HW_PREFETCH-1:0] hwpf_stride_throttle_set_i, + input hwpf_stride_throttle_t [NUM_HW_PREFETCH-1:0] hwpf_stride_throttle_i, + output hwpf_stride_throttle_t [NUM_HW_PREFETCH-1:0] hwpf_stride_throttle_o, + + output hwpf_stride_status_t hwpf_stride_status_o, + // }}} + + // Snooping + // {{{ + input logic [NUM_SNOOP_PORTS-1:0] snoop_valid_i, + input logic [NUM_SNOOP_PORTS-1:0] snoop_abort_i, + input hpdcache_req_offset_t [NUM_SNOOP_PORTS-1:0] snoop_addr_offset_i, + input hpdcache_tag_t [NUM_SNOOP_PORTS-1:0] snoop_addr_tag_i, + input logic [NUM_SNOOP_PORTS-1:0] snoop_phys_indexed_i, + // }}} + + // Dcache interface + // {{{ + input hpdcache_req_sid_t hpdcache_req_sid_i, + output logic hpdcache_req_valid_o, + input logic hpdcache_req_ready_i, + output hpdcache_req_t hpdcache_req_o, + output logic hpdcache_req_abort_o, + output hpdcache_tag_t hpdcache_req_tag_o, + output hpdcache_pma_t hpdcache_req_pma_o, + input logic hpdcache_rsp_valid_i, + input hpdcache_rsp_t hpdcache_rsp_i + // }}} +); +// }}} + + // Internal registers + // {{{ + typedef logic [HPDcacheCfg.nlineWidth-1:0] hpdcache_nline_t; + typedef logic [HPDcacheCfg.setWidth-1:0] hpdcache_set_t; + // }}} + + // Internal registers + // {{{ + logic [NUM_SNOOP_PORTS-1:0] snoop_valid_q; + hpdcache_req_offset_t [NUM_SNOOP_PORTS-1:0] snoop_addr_offset_q; + // }}} + + // Internal signals + // {{{ + logic [NUM_HW_PREFETCH-1:0] hwpf_stride_enable; + logic [NUM_HW_PREFETCH-1:0] hwpf_stride_free; + logic [NUM_HW_PREFETCH-1:0] hwpf_stride_status_busy; + logic [3:0] hwpf_stride_status_free_idx; + + hpdcache_nline_t [NUM_HW_PREFETCH-1:0] hwpf_snoop_nline; + logic [NUM_HW_PREFETCH-1:0] hwpf_snoop_match; + + logic [NUM_HW_PREFETCH-1:0] hwpf_stride_req_valid; + logic [NUM_HW_PREFETCH-1:0] hwpf_stride_req_ready; + hpdcache_req_t [NUM_HW_PREFETCH-1:0] hwpf_stride_req; + + logic [NUM_HW_PREFETCH-1:0] hwpf_stride_arb_in_req_valid; + logic [NUM_HW_PREFETCH-1:0] hwpf_stride_arb_in_req_ready; + hpdcache_req_t [NUM_HW_PREFETCH-1:0] hwpf_stride_arb_in_req; + logic [NUM_HW_PREFETCH-1:0] hwpf_stride_arb_in_rsp_valid; + hpdcache_rsp_t [NUM_HW_PREFETCH-1:0] hwpf_stride_arb_in_rsp; + // }}} + + // Assertions + // {{{ +`ifndef HPDCACHE_ASSERT_OFF + if (NUM_HW_PREFETCH > 16) begin : gen_max_hwpf_stride_assertion + $fatal(1, "hwpf_stride: maximum number of HW prefetchers is 16"); + end +`endif + // }}} + + // Compute the status information + // {{{ + always_comb begin: hwpf_stride_priority_encoder + hwpf_stride_status_free_idx = '0; + for (int unsigned i = 0; i < NUM_HW_PREFETCH; i++) begin + if (hwpf_stride_free[i]) begin + hwpf_stride_status_free_idx = i; + break; + end + end + end + + // Free flag of engines + assign hwpf_stride_free = ~(hwpf_stride_enable | hwpf_stride_status_busy); + // Busy flags + assign hwpf_stride_status_o[63:32] = {{32-NUM_HW_PREFETCH{1'b0}}, hwpf_stride_status_busy}; + // Global free flag + assign hwpf_stride_status_o[31] = |hwpf_stride_free; + // Free Index + assign hwpf_stride_status_o[30:16] = {11'b0, hwpf_stride_status_free_idx}; + // Enable flags + assign hwpf_stride_status_o[15:0] = {{16-NUM_HW_PREFETCH{1'b0}}, hwpf_stride_enable}; + // }}} + + // Hardware prefetcher engines + // {{{ + for (genvar j = 0; j < NUM_SNOOP_PORTS; j++) begin : gen_hwpf_snoop + always_ff @(posedge clk_i or negedge rst_ni) + begin : snoop_ff + if (!rst_ni) begin + snoop_valid_q[j] <= 1'b0; + snoop_addr_offset_q[j] <= '0; + end else begin + if (snoop_phys_indexed_i[j]) begin + snoop_valid_q[j] <= snoop_valid_i[j]; + snoop_addr_offset_q[j] <= snoop_addr_offset_i[j]; + end + end + end + end + + for (genvar i = 0; i < NUM_HW_PREFETCH; i++) begin : gen_hwpf_stride + assign hwpf_stride_enable[i] = hwpf_stride_base_o[i].enable; + + // Compute snoop match signals + // {{{ + always_comb + begin : snoop_comb + hwpf_snoop_match[i] = 1'b0; + for (int j = 0; j < NUM_SNOOP_PORTS; j++) begin + automatic logic snoop_valid; + automatic hpdcache_req_offset_t snoop_offset; + automatic hpdcache_nline_t snoop_nline; + + if (snoop_phys_indexed_i[j]) begin + snoop_valid = snoop_valid_i[j]; + snoop_offset = snoop_addr_offset_i[j]; + end else begin + snoop_valid = snoop_valid_q[j]; + snoop_offset = snoop_addr_offset_q[j]; + end + snoop_nline = {snoop_addr_tag_i[j], snoop_offset}; + hwpf_snoop_match[i] |= (snoop_valid && !snoop_abort_i[j] && + (hwpf_snoop_nline[i] == snoop_nline)); + end + end + // }}} + + hwpf_stride #( + .HPDcacheCfg (HPDcacheCfg), + .hpdcache_nline_t (hpdcache_nline_t), + .hpdcache_tag_t (hpdcache_tag_t), + .hpdcache_set_t (hpdcache_set_t), + .hpdcache_req_t (hpdcache_req_t), + .hpdcache_rsp_t (hpdcache_rsp_t) + ) hwpf_stride_i( + .clk_i, + .rst_ni, + + .csr_base_set_i (hwpf_stride_base_set_i[i]), + .csr_base_i (hwpf_stride_base_i[i]), + .csr_param_set_i (hwpf_stride_param_set_i[i]), + .csr_param_i (hwpf_stride_param_i[i]), + .csr_throttle_set_i (hwpf_stride_throttle_set_i[i]), + .csr_throttle_i (hwpf_stride_throttle_i[i]), + + .csr_base_o (hwpf_stride_base_o[i]), + .csr_param_o (hwpf_stride_param_o[i]), + .csr_throttle_o (hwpf_stride_throttle_o[i]), + + .busy_o (hwpf_stride_status_busy[i]), + + .snoop_nline_o (hwpf_snoop_nline[i]), + .snoop_match_i (hwpf_snoop_match[i]), + + .hpdcache_req_valid_o (hwpf_stride_req_valid[i]), + .hpdcache_req_ready_i (hwpf_stride_req_ready[i]), + .hpdcache_req_o (hwpf_stride_req[i]), + .hpdcache_rsp_valid_i (hwpf_stride_arb_in_rsp_valid[i]), + .hpdcache_rsp_i (hwpf_stride_arb_in_rsp[i]) + ); + + assign hwpf_stride_req_ready[i] = hwpf_stride_arb_in_req_ready[i], + hwpf_stride_arb_in_req_valid[i] = hwpf_stride_req_valid[i], + hwpf_stride_arb_in_req[i].addr_offset = hwpf_stride_req[i].addr_offset, + hwpf_stride_arb_in_req[i].wdata = hwpf_stride_req[i].wdata, + hwpf_stride_arb_in_req[i].op = hwpf_stride_req[i].op, + hwpf_stride_arb_in_req[i].be = hwpf_stride_req[i].be, + hwpf_stride_arb_in_req[i].size = hwpf_stride_req[i].size, + hwpf_stride_arb_in_req[i].sid = hpdcache_req_sid_i, + hwpf_stride_arb_in_req[i].tid = hpdcache_req_tid_t'(i), + hwpf_stride_arb_in_req[i].need_rsp = hwpf_stride_req[i].need_rsp, + hwpf_stride_arb_in_req[i].phys_indexed = hwpf_stride_req[i].phys_indexed, + hwpf_stride_arb_in_req[i].addr_tag = '0, + hwpf_stride_arb_in_req[i].pma = '0; + end + // }}} + + // Hardware prefetcher arbiter betweem engines + // {{{ + hwpf_stride_arb #( + .NUM_HW_PREFETCH (NUM_HW_PREFETCH), + .hpdcache_req_t (hpdcache_req_t), + .hpdcache_rsp_t (hpdcache_rsp_t) + ) hwpf_stride_arb_i ( + .clk_i, + .rst_ni, + + // DCache input interface + .hwpf_stride_req_valid_i (hwpf_stride_arb_in_req_valid), + .hwpf_stride_req_ready_o (hwpf_stride_arb_in_req_ready), + .hwpf_stride_req_i (hwpf_stride_arb_in_req), + .hwpf_stride_rsp_valid_o (hwpf_stride_arb_in_rsp_valid), + .hwpf_stride_rsp_o (hwpf_stride_arb_in_rsp), + + // DCache output interface + .hpdcache_req_valid_o, + .hpdcache_req_ready_i, + .hpdcache_req_o, + .hpdcache_rsp_valid_i, + .hpdcache_rsp_i + ); + + assign hpdcache_req_abort_o = 1'b0, // unused on physically indexed requests + hpdcache_req_tag_o = '0, // unused on physically indexed requests + hpdcache_req_pma_o = '0; // unused on physically indexed requests + // }}} + +endmodule diff --git a/hw/vendor/hpdcache/rtl/src/target/cva6/cva6_hpdcache_cmo_if_adapter.sv b/hw/vendor/hpdcache/rtl/src/target/cva6/cva6_hpdcache_cmo_if_adapter.sv new file mode 100644 index 00000000..a487006c --- /dev/null +++ b/hw/vendor/hpdcache/rtl/src/target/cva6/cva6_hpdcache_cmo_if_adapter.sv @@ -0,0 +1,186 @@ +/* + * Copyright 2023 CEA* + * *Commissariat a l'Energie Atomique et aux Energies Alternatives (CEA) + * + * SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 + * + * Licensed under the Solderpad Hardware License v 2.1 (the “License”); you + * may not use this file except in compliance with the License, or, at your + * option, the Apache License version 2.0. You may obtain a copy of the + * License at + * + * https://solderpad.org/licenses/SHL-2.1/ + * + * Unless required by applicable law or agreed to in writing, any work + * distributed under the License is distributed on an “AS IS” BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +/* + * Authors : Cesar Fuguet + * Creation Date : April, 2021 + * Description : Interface adapter for the CMO interface of the CVA6 core + * History : + */ +module cva6_hpdcache_cmo_if_adapter +import hpdcache_pkg::*; + +// Parameters +// {{{ +#( + parameter type cmo_req_t = logic, + parameter type cmo_rsp_t = logic +) +// }}} + +// Ports +// {{{ +( + // Clock and active-low reset pins + input logic clk_i, + input logic rst_ni, + + // Port ID + input hpdcache_pkg::hpdcache_req_sid_t dcache_req_sid_i, + + // Request/response ports from/to the CVA6 core + input cmo_req_t cva6_cmo_req_i, + output cmo_rsp_t cva6_cmo_resp_o, + + // Request port to the L1 Dcache + output logic dcache_req_valid_o, + input logic dcache_req_ready_i, + output hpdcache_pkg::hpdcache_req_t dcache_req_o, + + // Response port from the L1 Dcache + input logic dcache_rsp_valid_i, + input hpdcache_pkg::hpdcache_rsp_t dcache_rsp_i +); +// }}} + + // Internal nets and registers + // {{{ + enum { + FORWARD_IDLE, + FORWARD_CMO, + FORWARD_CMO_ACK + } forward_state_q, forward_state_d; + + logic forward_cmo; + hpdcache_pkg::hpdcache_req_t dcache_req_cmo; + logic [ariane_pkg::TRANS_ID_BITS-1:0] cmo_tid_q, cmo_tid_d; + logic cmo_ack; + logic stall; + // }}} + + // Request forwarding + // {{{ + always_comb + begin : req_forward_comb + forward_state_d = forward_state_q; + forward_cmo = 1'b0; + cmo_tid_d = cmo_tid_q; + cmo_ack = 1'b0; + stall = 1'b0; + + case (forward_state_q) + FORWARD_IDLE: begin + if (cva6_cmo_req_i.req) begin + stall = ~dcache_req_ready_i; + forward_cmo = 1'b1; + cmo_tid_d = cva6_cmo_req_i.trans_id; + if (!dcache_req_ready_i) begin + forward_state_d = FORWARD_CMO; + end else begin + forward_state_d = FORWARD_CMO_ACK; + end + end + end + + FORWARD_CMO: begin + stall = ~dcache_req_ready_i; + forward_cmo = 1'b1; + if (dcache_req_ready_i) begin + forward_state_d = FORWARD_CMO_ACK; + end + end + + FORWARD_CMO_ACK: begin + stall = 1'b1; + cmo_ack = 1'b1; + if (cva6_cmo_req_i.req) begin + stall = ~dcache_req_ready_i; + forward_cmo = 1'b1; + cmo_tid_d = cva6_cmo_req_i.trans_id; + if (!dcache_req_ready_i) begin + forward_state_d = FORWARD_CMO; + end else begin + forward_state_d = FORWARD_CMO_ACK; + end + end else begin + forward_state_d = FORWARD_IDLE; + end + end + endcase + end + + always_ff @(posedge clk_i or negedge rst_ni) + begin : forward_ff + if (!rst_ni) begin + forward_state_q <= FORWARD_IDLE; + cmo_tid_q <= '0; + end else begin + forward_state_q <= forward_state_d; + cmo_tid_q <= cmo_tid_d; + end + end + + // CMO request + // {{{ + always_comb + begin : cmo_req + dcache_req_cmo.addr = hpdcache_req_addr_t'(cva6_cmo_req_i.address); + dcache_req_cmo.need_rsp = 1'b0; + dcache_req_cmo.uncacheable = 1'b0; + dcache_req_cmo.sid = dcache_req_sid_i; + dcache_req_cmo.tid = cva6_cmo_req_i.trans_id; + dcache_req_cmo.wdata = '0; + dcache_req_cmo.be = '0; + dcache_req_cmo.op = HPDCACHE_REQ_CMO; + dcache_req_cmo.size = '0; + case (cva6_cmo_req_i.cmo_op) + ariane_pkg::CMO_CLEAN, + ariane_pkg::CMO_FLUSH, + ariane_pkg::CMO_ZERO: begin + // FIXME + end + ariane_pkg::CMO_INVAL: begin + dcache_req_cmo.size = HPDCACHE_REQ_CMO_INVAL_NLINE; + end + ariane_pkg::CMO_PREFETCH_R, + ariane_pkg::CMO_PREFETCH_W: begin + dcache_req_cmo.size = HPDCACHE_REQ_CMO_PREFETCH; + end + ariane_pkg::CMO_CLEAN_ALL, + ariane_pkg::CMO_FLUSH_ALL: begin + end + ariane_pkg::CMO_INVAL_ALL: begin + dcache_req_cmo.size = HPDCACHE_REQ_CMO_INVAL_ALL; + end + endcase + end + // }}} + + assign dcache_req_valid_o = forward_cmo, + dcache_req_o = dcache_req_cmo, + cva6_cmo_resp_o.req_ready = ~stall; + // }}} + + // Response forwarding + // {{{ + assign cva6_cmo_resp_o.ack = cmo_ack, + cva6_cmo_resp_o.trans_id = cmo_tid_q; + // }}} + +endmodule diff --git a/hw/vendor/hpdcache/rtl/src/target/cva6/cva6_op_hpdcache_params_pkg.sv b/hw/vendor/hpdcache/rtl/src/target/cva6/cva6_op_hpdcache_params_pkg.sv new file mode 100644 index 00000000..c55af126 --- /dev/null +++ b/hw/vendor/hpdcache/rtl/src/target/cva6/cva6_op_hpdcache_params_pkg.sv @@ -0,0 +1,261 @@ +/* + * Copyright 2023 CEA* + * *Commissariat a l'Energie Atomique et aux Energies Alternatives (CEA) + * + * SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 + * + * Licensed under the Solderpad Hardware License v 2.1 (the “License”); you + * may not use this file except in compliance with the License, or, at your + * option, the Apache License version 2.0. You may obtain a copy of the + * License at + * + * https://solderpad.org/licenses/SHL-2.1/ + * + * Unless required by applicable law or agreed to in writing, any work + * distributed under the License is distributed on an “AS IS” BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +/* + * Authors : Cesar Fuguet + * Creation Date : April, 2023 + * Description : Generic parameters package for the HPDcache. All parameters + * can be overriden by Verilog preprocessor definitions. + * History : + */ +`ifdef PITON_ARIANE + `include "l15.tmp.h" + `include "define.tmp.h" +`endif +package hpdcache_params_pkg; + // Definition of global constants for the HPDcache data and directory + // {{{ + `ifndef CONF_HPDCACHE_PA_WIDTH + `define CONF_HPDCACHE_PA_WIDTH 49 + `endif + localparam int unsigned PARAM_PA_WIDTH = `CONF_HPDCACHE_PA_WIDTH; + + // HPDcache number of sets + `ifndef CONF_HPDCACHE_SETS + `define CONF_HPDCACHE_SETS 128 + `endif + + `ifdef PITON_ARIANE + `ifndef CONFIG_L1D_SIZE + localparam int unsigned PARAM_SETS = 128; + `else + localparam int unsigned PARAM_SETS = (`CONFIG_L1D_SIZE/`CONFIG_L1D_ASSOCIATIVITY)/(`CONFIG_L1D_CACHELINE_WIDTH/8); + `endif + `else + localparam int unsigned PARAM_SETS = `CONF_HPDCACHE_SETS; + `endif + + // HPDcache number of ways + `ifndef CONF_HPDCACHE_WAYS + `define CONF_HPDCACHE_WAYS 4 + `endif + + `ifdef PITON_ARIANE + `ifndef CONFIG_L1D_ASSOCIATIVITY + localparam int unsigned PARAM_WAYS = 4; + `else + localparam int unsigned PARAM_WAYS = `CONFIG_L1D_ASSOCIATIVITY; + `endif + `else + localparam int unsigned PARAM_WAYS = `CONF_HPDCACHE_WAYS; + `endif + + // HPDcache word width (bits) + `ifndef CONF_HPDCACHE_WORD_WIDTH + `define CONF_HPDCACHE_WORD_WIDTH 64 + `endif + localparam int unsigned PARAM_WORD_WIDTH = `CONF_HPDCACHE_WORD_WIDTH; + + // HPDcache cache-line width (bits) + `ifndef CONF_HPDCACHE_CL_WORDS + `define CONF_HPDCACHE_CL_WORDS 8 + `endif + + `ifdef PITON_ARIANE + localparam int unsigned PARAM_CL_WORDS = `CONFIG_L1D_CACHELINE_WIDTH/PARAM_WORD_WIDTH; //16 Bytes per cache-line harcoded + `else + localparam int unsigned PARAM_CL_WORDS = `CONF_HPDCACHE_CL_WORDS; + `endif + + // HPDcache number of words in the request data channels (request and response) + `ifndef CONF_HPDCACHE_REQ_WORDS + `define CONF_HPDCACHE_REQ_WORDS 1 + `endif + localparam int unsigned PARAM_REQ_WORDS = `CONF_HPDCACHE_REQ_WORDS; + + // HPDcache request transaction ID width (bits) + `ifndef CONF_HPDCACHE_REQ_TRANS_ID_WIDTH + `define CONF_HPDCACHE_REQ_TRANS_ID_WIDTH 7 + `endif + localparam int unsigned PARAM_REQ_TRANS_ID_WIDTH = `CONF_HPDCACHE_REQ_TRANS_ID_WIDTH; + + // HPDcache request source ID width (bits) + `ifndef CONF_HPDCACHE_REQ_SRC_ID_WIDTH + `define CONF_HPDCACHE_REQ_SRC_ID_WIDTH 3 + `endif + localparam int unsigned PARAM_REQ_SRC_ID_WIDTH = `CONF_HPDCACHE_REQ_SRC_ID_WIDTH; + + // HPDcache victim select + `ifndef CONF_HPDCACHE_VICTIM_SEL + `define CONF_HPDCACHE_VICTIM_SEL 1 + `endif + localparam int unsigned PARAM_VICTIM_SEL = `CONF_HPDCACHE_VICTIM_SEL; + // }}} + + // Definition of constants and types for HPDcache data memory + // {{{ + `ifndef CONF_HPDCACHE_DATA_WAYS_PER_RAM_WORD + `define CONF_HPDCACHE_DATA_WAYS_PER_RAM_WORD 2 + `endif + localparam int unsigned PARAM_DATA_WAYS_PER_RAM_WORD = `CONF_HPDCACHE_DATA_WAYS_PER_RAM_WORD; + + `ifndef CONF_HPDCACHE_DATA_SETS_PER_RAM + `define CONF_HPDCACHE_DATA_SETS_PER_RAM PARAM_SETS + `endif + localparam int unsigned PARAM_DATA_SETS_PER_RAM = `CONF_HPDCACHE_DATA_SETS_PER_RAM; + + // HPDcache DATA RAM macros implement write byte enable + // - Write byte enable (1'b1) + // - Write bit mask (1'b0) + `ifndef CONF_HPDCACHE_DATA_RAM_WBYTEENABLE + `define CONF_HPDCACHE_DATA_RAM_WBYTEENABLE 0 + `endif + localparam bit PARAM_DATA_RAM_WBYTEENABLE = `CONF_HPDCACHE_DATA_RAM_WBYTEENABLE; + + // Define the number of memory contiguous words that can be accessed + // simultaneously from the cache. + // - This limits the maximum width for the data channel from requesters + // - This impacts the refill latency (more ACCESS_WORDS -> less REFILL LATENCY) + `ifndef CONF_HPDCACHE_ACCESS_WORDS + `define CONF_HPDCACHE_ACCESS_WORDS 4 + `endif + + `ifdef PITON_ARIANE + localparam int unsigned PARAM_ACCESS_WORDS = 2; + `else + localparam int unsigned PARAM_ACCESS_WORDS = `CONF_HPDCACHE_ACCESS_WORDS; + `endif + // }}} + + // Definition of constants and types for the Miss Status Holding Register (MSHR) + // {{{ + // HPDcache MSHR number of sets + `ifndef CONF_HPDCACHE_MSHR_SETS + `define CONF_HPDCACHE_MSHR_SETS 64 + `endif + + `ifdef PITON_ARIANE + localparam int unsigned PARAM_MSHR_SETS = 1; + `else + localparam int unsigned PARAM_MSHR_SETS = `CONF_HPDCACHE_MSHR_SETS; + `endif + + // HPDcache MSHR number of ways + `ifndef CONF_HPDCACHE_MSHR_WAYS + `define CONF_HPDCACHE_MSHR_WAYS 2 + `endif + localparam int unsigned PARAM_MSHR_WAYS = `CONF_HPDCACHE_MSHR_WAYS; + + // HPDcache MSHR number of ways in the same SRAM word + `ifndef CONF_HPDCACHE_MSHR_WAYS_PER_RAM_WORD + `define CONF_HPDCACHE_MSHR_WAYS_PER_RAM_WORD 2 + `endif + localparam int unsigned PARAM_MSHR_WAYS_PER_RAM_WORD = `CONF_HPDCACHE_MSHR_WAYS_PER_RAM_WORD; + + // HPDcache MSHR number of sets in the same SRAM + `ifndef CONF_HPDCACHE_MSHR_SETS_PER_RAM + `define CONF_HPDCACHE_MSHR_SETS_PER_RAM PARAM_MSHR_SETS + `endif + localparam int unsigned PARAM_MSHR_SETS_PER_RAM = `CONF_HPDCACHE_MSHR_SETS_PER_RAM; + + // HPDcache MSHR macros implement write byte enable + // - Write byte enable (1'b1) + // - Write bit mask (1'b0) + `ifndef CONF_HPDCACHE_MSHR_RAM_WBYTEENABLE + `define CONF_HPDCACHE_MSHR_RAM_WBYTEENABLE 0 + `endif + localparam bit PARAM_MSHR_RAM_WBYTEENABLE = `CONF_HPDCACHE_MSHR_RAM_WBYTEENABLE; + + // HPDcache MSHR whether uses FFs or SRAM + `ifndef CONF_HPDCACHE_MSHR_USE_REGBANK + `define CONF_HPDCACHE_MSHR_USE_REGBANK 0 + `endif + localparam bit PARAM_MSHR_USE_REGBANK = `CONF_HPDCACHE_MSHR_USE_REGBANK; + + // HPDcache feedthrough FIFOs from the refill handler to the core + `ifndef CONF_HPDCACHE_REFILL_CORE_RSP_FEEDTHROUGH + `define CONF_HPDCACHE_REFILL_CORE_RSP_FEEDTHROUGH 1'b1 + `endif + localparam bit PARAM_REFILL_CORE_RSP_FEEDTHROUGH = `CONF_HPDCACHE_REFILL_CORE_RSP_FEEDTHROUGH; + + // HPDcache depth of the refill FIFO + `ifndef CONF_HPDCACHE_REFILL_FIFO_DEPTH + `define CONF_HPDCACHE_REFILL_FIFO_DEPTH 32'd2 + `endif + + `ifdef PITON_ARIANE + /* FIXME: Should we increase it even more? */ + localparam int PARAM_REFILL_FIFO_DEPTH = (PARAM_MSHR_SETS*PARAM_MSHR_WAYS) + 32'd10; + `else + localparam int PARAM_REFILL_FIFO_DEPTH = `CONF_HPDCACHE_REFILL_FIFO_DEPTH + `endif + // }}} + + // Definition of constants and types for the Write Buffer (WBUF) + // {{{ + // HPDcache Write-Buffer number of entries in the directory + `ifndef CONF_HPDCACHE_WBUF_DIR_ENTRIES + `define CONF_HPDCACHE_WBUF_DIR_ENTRIES 16 + `endif + `ifdef PITON_ARIANE + localparam int unsigned PARAM_WBUF_DIR_ENTRIES = 4; + `else + localparam int unsigned PARAM_WBUF_DIR_ENTRIES = `CONF_HPDCACHE_WBUF_DIR_ENTRIES; + `endif + + // HPDcache Write-Buffer number of entries in the data buffer + `ifndef CONF_HPDCACHE_WBUF_DATA_ENTRIES + `define CONF_HPDCACHE_WBUF_DATA_ENTRIES 4 + `endif + localparam int unsigned PARAM_WBUF_DATA_ENTRIES = `CONF_HPDCACHE_WBUF_DATA_ENTRIES; + + // HPDcache Write-Buffer number of words per entry + `ifndef CONF_HPDCACHE_WBUF_WORDS + `define CONF_HPDCACHE_WBUF_WORDS PARAM_REQ_WORDS + `endif + + `ifdef PITON_ARIANE + localparam int unsigned PARAM_WBUF_WORDS = 1; + `else + localparam int unsigned PARAM_WBUF_WORDS = `CONF_HPDCACHE_WBUF_WORDS; + `endif + + // HPDcache Write-Buffer threshold counter width (in bits) + `ifndef CONF_HPDCACHE_WBUF_TIMECNT_WIDTH + `define CONF_HPDCACHE_WBUF_TIMECNT_WIDTH 4 + `endif + localparam int unsigned PARAM_WBUF_TIMECNT_WIDTH = `CONF_HPDCACHE_WBUF_TIMECNT_WIDTH; + // }}} + + // HPDCACHE feedthrough FIFOs from the write-buffer to the NoC + `ifndef CONF_HPDCACHE_WBUF_SEND_FEEDTHROUGH + // Currently WBUF FIFOs don't support feedthrough + `define CONF_HPDCACHE_WBUF_SEND_FEEDTHROUGH 1'b0 + `endif + localparam bit PARAM_WBUF_SEND_FEEDTHROUGH = `CONF_HPDCACHE_WBUF_SEND_FEEDTHROUGH; + + // Definition of constants and types for the Replay Table (RTAB) + // {{{ + `ifndef CONF_HPDCACHE_RTAB_ENTRIES + `define CONF_HPDCACHE_RTAB_ENTRIES 8 + `endif + localparam int PARAM_RTAB_ENTRIES = `CONF_HPDCACHE_RTAB_ENTRIES; + // }}} + +endpackage diff --git a/hw/vendor/hpdcache/rtl/src/utils/hpdcache_l15_req_arbiter.sv b/hw/vendor/hpdcache/rtl/src/utils/hpdcache_l15_req_arbiter.sv new file mode 100644 index 00000000..c42b35c5 --- /dev/null +++ b/hw/vendor/hpdcache/rtl/src/utils/hpdcache_l15_req_arbiter.sv @@ -0,0 +1,227 @@ +/* + * Copyright 2023 CEA* + * *Commissariat a l'Energie Atomique et aux Energies Alternatives (CEA) + * + * SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 + * + * Licensed under the Solderpad Hardware License v 2.1 (the “License”); you + * may not use this file except in compliance with the License, or, at your + * option, the Apache License version 2.0. You may obtain a copy of the + * License at + * + * https://solderpad.org/licenses/SHL-2.1/ + * + * Unless required by applicable law or agreed to in writing, any work + * distributed under the License is distributed on an “AS IS” BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +/* + * Authors : Noelia Oliete, Cesar Fuguet + * Creation Date : June, 2023 + * Description : Dcache Memory Req Channels Arbiter + * History : + */ +module hpdcache_l15_req_arbiter import hpdcache_pkg::*; +// Parameters +// {{{ +#( + parameter hpdcache_uint N = 0, + parameter type hpdcache_mem_req_t = logic, + parameter type hpdcache_mem_req_w_t = logic, + parameter type req_portid_t = logic +) +// }}} + +// Ports +// {{{ +( + input logic clk_i, + input logic rst_ni, + + output logic mem_req_ready_o [N-1:0], + input logic mem_req_valid_i [N-1:0], + input req_portid_t mem_req_pid_i [N-1:0], + input hpdcache_mem_req_t mem_req_i [N-1:0], + + //Data input + input logic mem_req_data_valid_i [N-1:0], + input hpdcache_mem_req_w_t mem_req_data_i [N-1:0], + + input logic mem_req_ready_i, + output logic mem_req_valid_o, + + + //Data output + output req_portid_t mem_req_pid_o, + output hpdcache_mem_req_t mem_req_o, + output hpdcache_mem_req_w_t mem_req_data_o, + output logic mem_req_index_o [N-1:0] +); +// }}} + + typedef enum { + REQ_IDLE, + REQ_META_SENT, + REQ_DATA_SENT, + REQ_WAIT_READY + } req_send_fsm_t; + + req_send_fsm_t req_send_fsm_q, req_send_fsm_d; + logic req_valid; + logic req_data_valid; + + logic [N-1:0] mem_arb_req_valid; + logic [N-1:0] mem_arb_req_data_valid; + req_portid_t [N-1:0] mem_arb_req_pid; + hpdcache_mem_req_t [N-1:0] mem_arb_req; + hpdcache_mem_req_w_t [N-1:0] mem_arb_req_data; + + logic [N-1:0] mem_arb_req_gnt; + logic mem_arb_req_ready; + + genvar gen_i; + + generate + for (gen_i = 0; gen_i < int'(N); gen_i++) begin : pack_inputs_gen + assign mem_arb_req_valid [gen_i] = mem_req_valid_i[gen_i], + mem_arb_req_data_valid[gen_i] = mem_req_data_valid_i[gen_i], + mem_arb_req_pid [gen_i] = mem_req_pid_i[gen_i], + mem_arb_req [gen_i] = mem_req_i[gen_i], + mem_arb_req_data [gen_i] = mem_req_data_i[gen_i]; + + end + endgenerate + + // Fixed-priority arbiter + hpdcache_fxarb #( + .N (N) + ) hpdcache_fxarb_mem_req_i ( + .clk_i, + .rst_ni, + .req_i (mem_arb_req_valid), + .gnt_o (mem_arb_req_gnt), + .ready_i (mem_arb_req_ready) + ); + + assign req_valid = |(mem_arb_req_gnt & mem_arb_req_valid); + assign req_data_valid = |(mem_arb_req_gnt & mem_arb_req_data_valid); + + // Request sent FSM + // + // This FSM allows to make sure that the request and its corresponding + // data are sent in order. This is, when a requester sends a request, this + // FSM keeps the grant signal on this requester until it has sent the + // corresponding data. + // + // {{{ + always_comb + begin : req_send_fsm_comb + req_send_fsm_d = req_send_fsm_q; + mem_arb_req_ready = 1'b0; + case (req_send_fsm_q) + REQ_IDLE: begin + if (req_valid && req_data_valid) begin //Request valid and arbiter ready + if (mem_req_ready_i) begin //Data is also valid + mem_arb_req_ready = 1'b1; + req_send_fsm_d = REQ_IDLE; + end + end else if (req_valid) begin //Data is not valid + req_send_fsm_d = REQ_META_SENT; + end else if (req_data_valid) begin //Data valid and arbiter ready + req_send_fsm_d = REQ_DATA_SENT; + end + end + REQ_META_SENT: begin//Wait for valid data + if (req_data_valid) begin + if (mem_req_ready_i) begin + mem_arb_req_ready = 1'b1; + req_send_fsm_d = REQ_IDLE; + end else begin + req_send_fsm_d = REQ_WAIT_READY; + end + end + end + REQ_DATA_SENT: begin //Wait for valid request + if (req_valid) begin + if (mem_req_ready_i) begin + mem_arb_req_ready = 1'b1; + req_send_fsm_d = REQ_IDLE; + end else begin + req_send_fsm_d = REQ_WAIT_READY; + end + end + end + REQ_WAIT_READY: begin + if (mem_req_ready_i) begin + mem_arb_req_ready = 1'b1; + req_send_fsm_d = REQ_IDLE; + end + end + endcase + end + + always_ff @(posedge clk_i or negedge rst_ni) + begin : req_send_fsm_ff + if (!rst_ni) begin + req_send_fsm_q <= REQ_IDLE; + end else begin + req_send_fsm_q <= req_send_fsm_d; + end + end + // }}} + + //Request ready + generate + for (gen_i = 0; gen_i < int'(N); gen_i++) begin : req_ready_gen + assign mem_req_ready_o[gen_i] = + (mem_arb_req_gnt[gen_i] & mem_req_ready_i) & + (req_send_fsm_q != REQ_META_SENT) & (req_send_fsm_q != REQ_DATA_SENT); + end + endgenerate + + // Output assignments + // {{{ + // Request valid when both data and request are valid + assign mem_req_valid_o = req_valid & (req_send_fsm_q != REQ_META_SENT) & + req_data_valid & (req_send_fsm_q != REQ_DATA_SENT); + generate + for (gen_i = 0; gen_i < int'(N); gen_i++) begin : pack_outputs_gen + assign mem_req_index_o [gen_i] = mem_arb_req_gnt[gen_i]; + end + endgenerate + + //Data, request and port selected + hpdcache_mux #( + .NINPUT (N), + .DATA_WIDTH ($bits(req_portid_t)), + .ONE_HOT_SEL (1'b1) + ) mem_req_portid_mux_i ( + .data_i (mem_arb_req_pid), + .sel_i (mem_arb_req_gnt), + .data_o (mem_req_pid_o) + ); + + hpdcache_mux #( + .NINPUT (N), + .DATA_WIDTH ($bits(hpdcache_mem_req_t)), + .ONE_HOT_SEL (1'b1) + ) mem_req_mux_i ( + .data_i (mem_arb_req), + .sel_i (mem_arb_req_gnt), + .data_o (mem_req_o) + ); + + hpdcache_mux #( + .NINPUT (N), + .DATA_WIDTH ($bits(hpdcache_mem_req_w_t)), + .ONE_HOT_SEL (1'b1) + ) mem_data_req_mux_i ( + .data_i (mem_arb_req_data), + .sel_i (mem_arb_req_gnt), + .data_o (mem_req_data_o) + ); + // }}} + +endmodule \ No newline at end of file diff --git a/hw/vendor/hpdcache/rtl/src/utils/hpdcache_l15_resp_demux.sv b/hw/vendor/hpdcache/rtl/src/utils/hpdcache_l15_resp_demux.sv new file mode 100644 index 00000000..a18e997b --- /dev/null +++ b/hw/vendor/hpdcache/rtl/src/utils/hpdcache_l15_resp_demux.sv @@ -0,0 +1,106 @@ +/* + * Copyright 2023 CEA* + * *Commissariat a l'Energie Atomique et aux Energies Alternatives (CEA) + * + * SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 + * + * Licensed under the Solderpad Hardware License v 2.1 (the “License”); you + * may not use this file except in compliance with the License, or, at your + * option, the Apache License version 2.0. You may obtain a copy of the + * License at + * + * https://solderpad.org/licenses/SHL-2.1/ + * + * Unless required by applicable law or agreed to in writing, any work + * distributed under the License is distributed on an “AS IS” BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +/* + * Authors : Cesar Fuguet + * Creation Date : June, 2022 + * Description : Dcache Memory Reponse Demultiplexer + * History : + */ +module hpdcache_l15_resp_demux +// Parameters +// {{{ +#( + parameter int N = 0, + parameter type resp_t = logic, + parameter type resp_id_t = logic, + parameter type req_portid_t = logic, + localparam int RT_DEPTH = (1 << $bits(resp_id_t)), + localparam type rt_t = resp_id_t [RT_DEPTH-1:0] + +) +// }}} + +// Ports +// {{{ +( + input logic clk_i, + input logic rst_ni, + + output logic mem_resp_ready_o, + input logic mem_resp_valid_i, + input resp_t mem_resp_i, + + input logic mem_resp_ready_i [N-1:0], + output logic mem_resp_valid_o [N-1:0], + output resp_t mem_resp_o [N-1:0], + + input req_portid_t mem_sel_i +); +// }}} + + logic [N-1:0] mem_resp_demux_valid; + resp_t [N-1:0] mem_resp_demux; + logic [N-1:0] mem_resp_demux_ready; + req_portid_t mem_resp_demux_sel; + + // Route the response according to the response ID and the routing table + assign mem_resp_demux_sel = mem_sel_i; + + // Forward the response to the corresponding output port + hpdcache_demux #( + .NOUTPUT (N), + .DATA_WIDTH (1), + .ONE_HOT_SEL (0) + ) i_resp_valid_demux ( + .data_i (mem_resp_valid_i), + .sel_i (mem_resp_demux_sel), + .data_o (mem_resp_demux_valid) + ); + + hpdcache_demux #( + .NOUTPUT (N), + .DATA_WIDTH ($bits(resp_t)), + .ONE_HOT_SEL (0) + ) i_resp_demux ( + .data_i (mem_resp_i), + .sel_i (mem_resp_demux_sel), + .data_o (mem_resp_demux) + ); + + hpdcache_mux #( + .NINPUT (N), + .DATA_WIDTH (1), + .ONE_HOT_SEL (0) + ) i_resp_ready_mux ( + .data_i (mem_resp_demux_ready), + .sel_i (mem_resp_demux_sel), + .data_o (mem_resp_ready_o) + ); + + // Pack/unpack responses + generate + for (genvar gen_i = 0; gen_i < int'(N); gen_i++) begin : pack_unpack_resp_gen + assign mem_resp_valid_o [gen_i] = mem_resp_demux_valid [gen_i]; + assign mem_resp_o [gen_i] = mem_resp_demux [gen_i]; + assign mem_resp_demux_ready [gen_i] = mem_resp_ready_i [gen_i]; + end + endgenerate + +endmodule : hpdcache_l15_resp_demux \ No newline at end of file diff --git a/hw/vendor/hpdcache/rtl/src/utils/hpdcache_mem_req_read_arbiter.sv b/hw/vendor/hpdcache/rtl/src/utils/hpdcache_mem_req_read_arbiter.sv new file mode 100644 index 00000000..73e3f81a --- /dev/null +++ b/hw/vendor/hpdcache/rtl/src/utils/hpdcache_mem_req_read_arbiter.sv @@ -0,0 +1,89 @@ +/* + * Copyright 2023 CEA* + * *Commissariat a l'Energie Atomique et aux Energies Alternatives (CEA) + * + * SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 + * + * Licensed under the Solderpad Hardware License v 2.1 (the “License”); you + * may not use this file except in compliance with the License, or, at your + * option, the Apache License version 2.0. You may obtain a copy of the + * License at + * + * https://solderpad.org/licenses/SHL-2.1/ + * + * Unless required by applicable law or agreed to in writing, any work + * distributed under the License is distributed on an “AS IS” BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +/* + * Authors : Cesar Fuguet + * Creation Date : April, 2021 + * Description : Dcache Memory Read Request Channel Arbiter + * History : + */ +module hpdcache_mem_req_read_arbiter +// Parameters +// {{{ +#( + parameter int unsigned N = 0, + parameter type hpdcache_mem_req_t = logic +) +// }}} + +// Ports +// {{{ +( + input logic clk_i, + input logic rst_ni, + + output logic [N-1:0] mem_req_read_ready_o, + input logic [N-1:0] mem_req_read_valid_i, + input hpdcache_mem_req_t [N-1:0] mem_req_read_i, + + input logic mem_req_read_ready_i, + output logic mem_req_read_valid_o, + output hpdcache_mem_req_t mem_req_read_o +); +// }}} + + logic [N-1:0] mem_read_arb_req_gnt; + + logic req_valid; + + genvar gen_i; + + assign req_valid = |(mem_read_arb_req_gnt & mem_req_read_valid_i); + + // Fixed-priority arbiter + hpdcache_fxarb #( + .N (N) + ) hpdcache_fxarb_mem_req_write_i ( + .clk_i, + .rst_ni, + .req_i (mem_req_read_valid_i), + .gnt_o (mem_read_arb_req_gnt), + .ready_i (mem_req_read_ready_i) + ); + + // Demultiplexor for the ready signal + for (gen_i = 0; gen_i < int'(N); gen_i++) begin : gen_req_ready + assign mem_req_read_ready_o[gen_i] = mem_req_read_ready_i & mem_read_arb_req_gnt[gen_i] & + mem_req_read_valid_i[gen_i]; + end + + assign mem_req_read_valid_o = req_valid; + + // Multiplexor for requests + hpdcache_mux #( + .NINPUT (N), + .DATA_WIDTH ($bits(hpdcache_mem_req_t)), + .ONE_HOT_SEL (1'b1) + ) mem_read_req_mux_i ( + .data_i (mem_req_read_i), + .sel_i (mem_read_arb_req_gnt), + .data_o (mem_req_read_o) + ); + +endmodule diff --git a/hw/vendor/hpdcache/rtl/src/utils/hpdcache_mem_req_write_arbiter.sv b/hw/vendor/hpdcache/rtl/src/utils/hpdcache_mem_req_write_arbiter.sv new file mode 100644 index 00000000..7cb95ae1 --- /dev/null +++ b/hw/vendor/hpdcache/rtl/src/utils/hpdcache_mem_req_write_arbiter.sv @@ -0,0 +1,189 @@ +/* + * Copyright 2023 CEA* + * *Commissariat a l'Energie Atomique et aux Energies Alternatives (CEA) + * + * SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 + * + * Licensed under the Solderpad Hardware License v 2.1 (the “License”); you + * may not use this file except in compliance with the License, or, at your + * option, the Apache License version 2.0. You may obtain a copy of the + * License at + * + * https://solderpad.org/licenses/SHL-2.1/ + * + * Unless required by applicable law or agreed to in writing, any work + * distributed under the License is distributed on an “AS IS” BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +/* + * Authors : Cesar Fuguet + * Creation Date : April, 2021 + * Description : Dcache Memory Write Channels Arbiter + * History : + */ +module hpdcache_mem_req_write_arbiter +// Parameters +// {{{ +#( + parameter int unsigned N = 0, + parameter type hpdcache_mem_req_t = logic, + parameter type hpdcache_mem_req_w_t = logic +) +// }}} +// Ports +// {{{ +( + input logic clk_i, + input logic rst_ni, + + output logic [N-1:0] mem_req_write_ready_o, + input logic [N-1:0] mem_req_write_valid_i, + input hpdcache_mem_req_t [N-1:0] mem_req_write_i, + + output logic [N-1:0] mem_req_write_data_ready_o, + input logic [N-1:0] mem_req_write_data_valid_i, + input hpdcache_mem_req_w_t [N-1:0] mem_req_write_data_i, + + input logic mem_req_write_ready_i, + output logic mem_req_write_valid_o, + output hpdcache_mem_req_t mem_req_write_o, + + input logic mem_req_write_data_ready_i, + output logic mem_req_write_data_valid_o, + output hpdcache_mem_req_w_t mem_req_write_data_o +); +// }}} + + // Types and wires + // {{{ + typedef logic [N-1:0] arb_gnt_t; + + logic req_valid, req_data_valid, req_data_last; + + arb_gnt_t mem_write_arb_req_data_last; + arb_gnt_t mem_write_arb_req_gnt, mem_write_arb_req_gnt_q; + + logic mem_write_arb_req_r, mem_write_arb_req_w; + logic mem_write_arb_req_rok, mem_write_arb_req_wok; + logic mem_write_arb_req_ready; + + logic mem_req_write_w; + logic mem_req_write_wok; + hpdcache_mem_req_t mem_req_write; + + genvar gen_i; + // }}} + + // Combinational logic + // {{{ + for (gen_i = 0; gen_i < int'(N); gen_i++) begin : gen_bitvectors + assign mem_write_arb_req_data_last[gen_i] = mem_req_write_data_i[gen_i].mem_req_w_last; + + assign mem_req_write_ready_o[gen_i] = mem_write_arb_req_gnt[gen_i] & + mem_write_arb_req_ready; + + assign mem_req_write_data_ready_o[gen_i] = mem_write_arb_req_gnt_q[gen_i] & + mem_write_arb_req_rok & + mem_req_write_data_ready_i; + end + + assign req_valid = |(mem_write_arb_req_gnt & mem_req_write_valid_i); + assign req_data_valid = |(mem_write_arb_req_gnt_q & mem_req_write_data_valid_i); + assign req_data_last = |(mem_write_arb_req_gnt_q & mem_write_arb_req_data_last); + + // Accept a new request when the grant FIFO is not full and the NoC can accept the request + assign mem_write_arb_req_ready = mem_write_arb_req_wok & mem_req_write_wok; + + // Write a grant decision into the FIFO + assign mem_write_arb_req_w = req_valid & mem_req_write_wok; + + // Read grant FIFO when the NoC is able to receive the data and it is the last flit of data + assign mem_write_arb_req_r = mem_req_write_data_ready_i & + req_data_valid & + req_data_last; + + // Accept a new request when the grant FIFO is not full and the NoC can accept the request + assign mem_req_write_w = req_valid & mem_write_arb_req_wok; + + // Forward the data to the NoC if there is any and there is a grant decision in the FIFO + assign mem_req_write_data_valid_o = req_data_valid & mem_write_arb_req_rok; + // }}} + + // Fixed-priority arbiter + // {{{ + hpdcache_fxarb #( + .N (N) + ) hpdcache_fxarb_mem_req_write_i( + .clk_i, + .rst_ni, + .req_i (mem_req_write_valid_i), + .gnt_o (mem_write_arb_req_gnt), + .ready_i (mem_write_arb_req_ready) + ); + // }}} + + // Request FIFO + // {{{ + hpdcache_fifo_reg #( + .FIFO_DEPTH (1), + .FEEDTHROUGH (1'b1), + .fifo_data_t (hpdcache_mem_req_t) + ) req_fifo_i( + .clk_i, + .rst_ni, + .w_i (mem_req_write_w), + .wok_o (mem_req_write_wok), + .wdata_i (mem_req_write), + .r_i (mem_req_write_ready_i), + .rok_o (mem_req_write_valid_o), + .rdata_o (mem_req_write_o) + ); + // }}} + + // Grant signal FIFO + // {{{ + hpdcache_fifo_reg #( + .FIFO_DEPTH (2), + .FEEDTHROUGH (1'b0), + .fifo_data_t (arb_gnt_t) + ) req_gnt_fifo_i( + .clk_i, + .rst_ni, + .w_i (mem_write_arb_req_w), + .wok_o (mem_write_arb_req_wok), + .wdata_i (mem_write_arb_req_gnt), + .r_i (mem_write_arb_req_r), + .rok_o (mem_write_arb_req_rok), + .rdata_o (mem_write_arb_req_gnt_q) + ); + // }}} + + // Mux requests + // {{{ + hpdcache_mux #( + .NINPUT (N), + .DATA_WIDTH ($bits(hpdcache_mem_req_t)), + .ONE_HOT_SEL (1'b1) + ) req_mux_i( + .data_i (mem_req_write_i), + .sel_i (mem_write_arb_req_gnt), + .data_o (mem_req_write) + ); + // }}} + + // Mux data + // {{{ + hpdcache_mux #( + .NINPUT (N), + .DATA_WIDTH ($bits(hpdcache_mem_req_w_t)), + .ONE_HOT_SEL (1'b1) + ) data_mux_i( + .data_i (mem_req_write_data_i), + .sel_i (mem_write_arb_req_gnt_q), + .data_o (mem_req_write_data_o) + ); + // }}} + +endmodule diff --git a/hw/vendor/hpdcache/rtl/src/utils/hpdcache_mem_resp_demux.sv b/hw/vendor/hpdcache/rtl/src/utils/hpdcache_mem_resp_demux.sv new file mode 100644 index 00000000..c1502a98 --- /dev/null +++ b/hw/vendor/hpdcache/rtl/src/utils/hpdcache_mem_resp_demux.sv @@ -0,0 +1,108 @@ +/* + * Copyright 2023 CEA* + * *Commissariat a l'Energie Atomique et aux Energies Alternatives (CEA) + * + * SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 + * + * Licensed under the Solderpad Hardware License v 2.1 (the “License”); you + * may not use this file except in compliance with the License, or, at your + * option, the Apache License version 2.0. You may obtain a copy of the + * License at + * + * https://solderpad.org/licenses/SHL-2.1/ + * + * Unless required by applicable law or agreed to in writing, any work + * distributed under the License is distributed on an “AS IS” BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +/* + * Authors : Cesar Fuguet + * Creation Date : June, 2022 + * Description : Dcache Memory Reponse Demultiplexer + * History : + */ +module hpdcache_mem_resp_demux +// Parameters +// {{{ +#( + parameter int N = 0, + parameter type resp_t = logic, + parameter type resp_id_t = logic, + + localparam int RT_DEPTH = (1 << $bits(resp_id_t)), + localparam type rt_t = resp_id_t [RT_DEPTH-1:0] +) +// }}} + +// Ports +// {{{ +( + input logic clk_i, + input logic rst_ni, + + output logic mem_resp_ready_o, + input logic mem_resp_valid_i, + input resp_id_t mem_resp_id_i, + input resp_t mem_resp_i, + + input logic mem_resp_ready_i [N-1:0], + output logic mem_resp_valid_o [N-1:0], + output resp_t mem_resp_o [N-1:0], + + input rt_t mem_resp_rt_i +); +// }}} + + typedef logic [$clog2(N)-1:0] sel_t; + + logic [N-1:0] mem_resp_demux_valid; + resp_t [N-1:0] mem_resp_demux; + logic [N-1:0] mem_resp_demux_ready; + sel_t mem_resp_demux_sel; + + // Route the response according to the response ID and the routing table + assign mem_resp_demux_sel = mem_resp_rt_i[int'(mem_resp_id_i)]; + + // Forward the response to the corresponding output port + hpdcache_demux #( + .NOUTPUT (N), + .DATA_WIDTH (1), + .ONE_HOT_SEL (0) + ) i_resp_valid_demux ( + .data_i (mem_resp_valid_i), + .sel_i (mem_resp_demux_sel), + .data_o (mem_resp_demux_valid) + ); + + hpdcache_demux #( + .NOUTPUT (N), + .DATA_WIDTH ($bits(resp_t)), + .ONE_HOT_SEL (0) + ) i_resp_demux ( + .data_i (mem_resp_i), + .sel_i (mem_resp_demux_sel), + .data_o (mem_resp_demux) + ); + + hpdcache_mux #( + .NINPUT (N), + .DATA_WIDTH (1), + .ONE_HOT_SEL (0) + ) i_resp_ready_mux ( + .data_i (mem_resp_demux_ready), + .sel_i (mem_resp_demux_sel), + .data_o (mem_resp_ready_o) + ); + + // Pack/unpack responses + generate + for (genvar gen_i = 0; gen_i < int'(N); gen_i++) begin : pack_unpack_resp_gen + assign mem_resp_valid_o [gen_i] = mem_resp_demux_valid [gen_i]; + assign mem_resp_o [gen_i] = mem_resp_demux [gen_i]; + assign mem_resp_demux_ready [gen_i] = mem_resp_ready_i [gen_i]; + end + endgenerate + +endmodule : hpdcache_mem_resp_demux diff --git a/hw/vendor/hpdcache/rtl/src/utils/hpdcache_mem_to_axi_read.sv b/hw/vendor/hpdcache/rtl/src/utils/hpdcache_mem_to_axi_read.sv new file mode 100644 index 00000000..3219e404 --- /dev/null +++ b/hw/vendor/hpdcache/rtl/src/utils/hpdcache_mem_to_axi_read.sv @@ -0,0 +1,97 @@ +/* + * Copyright 2023 CEA* + * *Commissariat a l'Energie Atomique et aux Energies Alternatives (CEA) + * + * SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 + * + * Licensed under the Solderpad Hardware License v 2.1 (the “License”); you + * may not use this file except in compliance with the License, or, at your + * option, the Apache License version 2.0. You may obtain a copy of the + * License at + * + * https://solderpad.org/licenses/SHL-2.1/ + * + * Unless required by applicable law or agreed to in writing, any work + * distributed under the License is distributed on an “AS IS” BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +/* + * Authors : Cesar Fuguet + * Creation Date : April, 2021 + * Description : Dcache memory request to axi read channels + * History : + */ +module hpdcache_mem_to_axi_read +import hpdcache_pkg::*; +#( + parameter type hpdcache_mem_req_t = logic, + parameter type hpdcache_mem_resp_r_t = logic, + parameter type ar_chan_t = logic, + parameter type r_chan_t = logic, + parameter bit userEn = 1'b0 +) +( + output logic req_ready_o, + input logic req_valid_i, + input hpdcache_mem_req_t req_i, + + input logic resp_ready_i, + output logic resp_valid_o, + output hpdcache_mem_resp_r_t resp_o, + + output logic axi_ar_valid_o, + output ar_chan_t axi_ar_o, + input logic axi_ar_ready_i, + + input logic axi_r_valid_i, + input r_chan_t axi_r_i, + output logic axi_r_ready_o +); + + logic lock; + axi_pkg::cache_t cache; + hpdcache_mem_error_e resp; + + assign lock = (req_i.mem_req_command == HPDCACHE_MEM_ATOMIC) && + (req_i.mem_req_atomic == HPDCACHE_MEM_ATOMIC_LDEX); + + assign cache = req_i.mem_req_cacheable ? + axi_pkg::CACHE_BUFFERABLE | + axi_pkg::CACHE_MODIFIABLE | + axi_pkg::CACHE_RD_ALLOC | + axi_pkg::CACHE_WR_ALLOC : axi_pkg::CACHE_MODIFIABLE; + + always_comb + begin : resp_decode_comb + case (axi_r_i.resp) + axi_pkg::RESP_SLVERR, + axi_pkg::RESP_DECERR: resp = HPDCACHE_MEM_RESP_NOK; + default: resp = HPDCACHE_MEM_RESP_OK; + endcase + end + + assign req_ready_o = axi_ar_ready_i, + axi_ar_valid_o = req_valid_i, + axi_ar_o.id = req_i.mem_req_id, + axi_ar_o.addr = req_i.mem_req_addr, + axi_ar_o.len = req_i.mem_req_len, + axi_ar_o.size = req_i.mem_req_size, + axi_ar_o.burst = axi_pkg::BURST_INCR, + axi_ar_o.lock = lock, + axi_ar_o.cache = cache, + axi_ar_o.prot = '0, + axi_ar_o.qos = '0, + axi_ar_o.region = '0, + axi_ar_o.user = '0; + + assign axi_r_ready_o = resp_ready_i, + resp_valid_o = axi_r_valid_i, + resp_o.mem_resp_r_error = resp, + resp_o.mem_resp_r_id = axi_r_i.id, + resp_o.mem_resp_r_data = axi_r_i.data, + resp_o.mem_resp_r_user = userEn ? axi_r_i.user : '0, + resp_o.mem_resp_r_last = axi_r_i.last; + +endmodule diff --git a/hw/vendor/hpdcache/rtl/src/utils/hpdcache_mem_to_axi_write.sv b/hw/vendor/hpdcache/rtl/src/utils/hpdcache_mem_to_axi_write.sv new file mode 100644 index 00000000..46cf7260 --- /dev/null +++ b/hw/vendor/hpdcache/rtl/src/utils/hpdcache_mem_to_axi_write.sv @@ -0,0 +1,149 @@ +/* + * Copyright 2023 CEA* + * *Commissariat a l'Energie Atomique et aux Energies Alternatives (CEA) + * + * SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 + * + * Licensed under the Solderpad Hardware License v 2.1 (the “License”); you + * may not use this file except in compliance with the License, or, at your + * option, the Apache License version 2.0. You may obtain a copy of the + * License at + * + * https://solderpad.org/licenses/SHL-2.1/ + * + * Unless required by applicable law or agreed to in writing, any work + * distributed under the License is distributed on an “AS IS” BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +/* + * Authors : Cesar Fuguet + * Creation Date : April, 2021 + * Description : Dcache memory request to axi write channels + * History : + */ +module hpdcache_mem_to_axi_write +import hpdcache_pkg::*; +#( + parameter type hpdcache_mem_req_t = logic, + parameter type hpdcache_mem_req_w_t = logic, + parameter type hpdcache_mem_resp_w_t = logic, + parameter type aw_chan_t = logic, + parameter type w_chan_t = logic, + parameter type b_chan_t = logic, + parameter bit userEn = 1'b0 +) +( + output logic req_ready_o, + input logic req_valid_i, + input hpdcache_mem_req_t req_i, + + output logic req_data_ready_o, + input logic req_data_valid_i, + input hpdcache_mem_req_w_t req_data_i, + + input logic resp_ready_i, + output logic resp_valid_o, + output hpdcache_mem_resp_w_t resp_o, + + output logic axi_aw_valid_o, + output aw_chan_t axi_aw_o, + input logic axi_aw_ready_i, + + output logic axi_w_valid_o, + output w_chan_t axi_w_o, + input logic axi_w_ready_i, + + input logic axi_b_valid_i, + input b_chan_t axi_b_i, + output logic axi_b_ready_o +); + + logic lock; + axi_pkg::atop_t atop; + axi_pkg::cache_t cache; + hpdcache_mem_error_e resp; + + always_comb + begin : atop_comb + lock = 1'b0; + atop = '0; + case (req_i.mem_req_command) + HPDCACHE_MEM_ATOMIC: begin + case (req_i.mem_req_atomic) + HPDCACHE_MEM_ATOMIC_STEX: lock = 1'b1; + HPDCACHE_MEM_ATOMIC_ADD : atop = {axi_pkg::ATOP_ATOMICLOAD, + axi_pkg::ATOP_LITTLE_END, + axi_pkg::ATOP_ADD}; + HPDCACHE_MEM_ATOMIC_CLR : atop = {axi_pkg::ATOP_ATOMICLOAD, + axi_pkg::ATOP_LITTLE_END, + axi_pkg::ATOP_CLR}; + HPDCACHE_MEM_ATOMIC_SET : atop = {axi_pkg::ATOP_ATOMICLOAD, + axi_pkg::ATOP_LITTLE_END, + axi_pkg::ATOP_SET}; + HPDCACHE_MEM_ATOMIC_EOR : atop = {axi_pkg::ATOP_ATOMICLOAD, + axi_pkg::ATOP_LITTLE_END, + axi_pkg::ATOP_EOR}; + HPDCACHE_MEM_ATOMIC_SMAX: atop = {axi_pkg::ATOP_ATOMICLOAD, + axi_pkg::ATOP_LITTLE_END, + axi_pkg::ATOP_SMAX}; + HPDCACHE_MEM_ATOMIC_SMIN: atop = {axi_pkg::ATOP_ATOMICLOAD, + axi_pkg::ATOP_LITTLE_END, + axi_pkg::ATOP_SMIN}; + HPDCACHE_MEM_ATOMIC_UMAX: atop = {axi_pkg::ATOP_ATOMICLOAD, + axi_pkg::ATOP_LITTLE_END, + axi_pkg::ATOP_UMAX}; + HPDCACHE_MEM_ATOMIC_UMIN: atop = {axi_pkg::ATOP_ATOMICLOAD, + axi_pkg::ATOP_LITTLE_END, + axi_pkg::ATOP_UMIN}; + HPDCACHE_MEM_ATOMIC_SWAP: atop = axi_pkg::ATOP_ATOMICSWAP; + endcase + end + endcase + end + + assign cache = (req_i.mem_req_cacheable && !lock) ? + axi_pkg::CACHE_BUFFERABLE | + axi_pkg::CACHE_MODIFIABLE | + axi_pkg::CACHE_RD_ALLOC | + axi_pkg::CACHE_WR_ALLOC : axi_pkg::CACHE_MODIFIABLE; + + always_comb + begin : resp_decode_comb + case (axi_b_i.resp) + axi_pkg::RESP_SLVERR, + axi_pkg::RESP_DECERR: resp = HPDCACHE_MEM_RESP_NOK; + default: resp = HPDCACHE_MEM_RESP_OK; + endcase + end + + assign req_ready_o = axi_aw_ready_i, + axi_aw_valid_o = req_valid_i, + axi_aw_o.id = req_i.mem_req_id, + axi_aw_o.addr = req_i.mem_req_addr, + axi_aw_o.len = req_i.mem_req_len, + axi_aw_o.size = req_i.mem_req_size, + axi_aw_o.burst = axi_pkg::BURST_INCR, + axi_aw_o.lock = lock, + axi_aw_o.cache = cache, + axi_aw_o.prot = '0, + axi_aw_o.qos = '0, + axi_aw_o.region = '0, + axi_aw_o.atop = atop, + axi_aw_o.user = '0; + + assign req_data_ready_o = axi_w_ready_i, + axi_w_valid_o = req_data_valid_i, + axi_w_o.data = req_data_i.mem_req_w_data, + axi_w_o.strb = req_data_i.mem_req_w_be, + axi_w_o.last = req_data_i.mem_req_w_last, + axi_w_o.user = userEn ? req_data_i.mem_req_w_user : '0; + + assign axi_b_ready_o = resp_ready_i, + resp_valid_o = axi_b_valid_i, + resp_o.mem_resp_w_error = resp, + resp_o.mem_resp_w_id = axi_b_i.id, + resp_o.mem_resp_w_is_atomic = (axi_b_i.resp == axi_pkg::RESP_EXOKAY); + +endmodule diff --git a/hw/vendor/hpdcache/rtl/src/utils/hpdcache_to_l15.sv b/hw/vendor/hpdcache/rtl/src/utils/hpdcache_to_l15.sv new file mode 100644 index 00000000..5846b361 --- /dev/null +++ b/hw/vendor/hpdcache/rtl/src/utils/hpdcache_to_l15.sv @@ -0,0 +1,457 @@ +/* + * Copyright 2023 CEA* + * *Commissariat a l'Energie Atomique et aux Energies Alternatives (CEA) + * + * SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 + * + * Licensed under the Solderpad Hardware License v 2.1 (the “License”); you + * may not use this file except in compliance with the License, or, at your + * option, the Apache License version 2.0. You may obtain a copy of the + * License at + * + * https://solderpad.org/licenses/SHL-2.1/ + * + * Unless required by applicable law or agreed to in writing, any work + * distributed under the License is distributed on an “AS IS” BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +/* + * Authors : Noelia Oliete, Cesar Fuguet + * Creation Date : June, 2023 + * Description : L1.5, L1I and HPDC adapter + * History : + */ +module hpdcache_to_l15 import hpdcache_pkg::*; import wt_cache_pkg::*; +// Parameters +// {{{ +#( + parameter hpdcache_uint NumPorts = 6, + parameter [$clog2(NumPorts)-1:0] IcachePort = 0, + parameter [$clog2(NumPorts)-1:0] DcachePort = 1, + parameter [$clog2(NumPorts)-1:0] DcacheWbufPort = 2, + parameter [$clog2(NumPorts)-1:0] DcacheUncReadPort = 3, + parameter [$clog2(NumPorts)-1:0] DcacheUncWritePort = 4, + parameter [$clog2(NumPorts)-1:0] DcacheAmoPort = 5, + + parameter bit SwapEndianess = 1, + parameter hpdcache_uint HPDcacheMemDataWidth = 128, + parameter bit WriteByteMaskEnabled = 0, + // Default value is compatible with CVA6 + parameter logic [2:0] IcacheNoCachableSize = 3'b011, + + parameter type hpdcache_mem_req_t = logic, + parameter type hpdcache_mem_req_w_t = logic, + parameter type hpdcache_mem_id_t = logic, + parameter type hpdcache_mem_addr_t = logic, + parameter type hpdcache_mem_resp_t = logic, + parameter type req_portid_t = logic +) +// }}} + +// Ports +// {{{ +( + input logic clk_i, + input logic rst_ni, + + output logic req_ready_o, + input logic req_valid_i, + input req_portid_t req_pid_i, + input hpdcache_mem_req_t req_i, + input hpdcache_mem_req_w_t req_data_i, + input logic req_index_i [NumPorts-2:0], + + input logic resp_ready_i, + output logic resp_valid_o, + output req_portid_t resp_pid_o, + output hpdcache_mem_resp_t resp_o, + + input logic hpdc_inval_ready_i, + + output logic sc_backoff_over_o, + + output wt_cache_pkg::l15_req_t l15_req_o, + + input wt_cache_pkg::l15_rtrn_t l15_rtrn_i + +); +// }}} + + // Internal functions + // {{{ + // Swap endianess in a 8B word + function automatic logic[7:0] swendian8B(input logic[7:0] in); + automatic logic[7:0] out; + for(int k=0; k<8;k++)begin + out[k] = in[7-k]; + end + return out; + endfunction + // }}} + + // Internal types + // {{{ + // Maximum number of thread ids ( or Maximum number of on-fly requests) + localparam NUM_THREAD_IDS = 2 ** wt_cache_pkg::L15_TID_WIDTH; + + typedef enum { + IDLE, + WAITING_HEADER_ACK, + WAITING_ACK + } thread_id_fsm_t; + + // L15_TIDs entry table to save the HPDC threadids and portids + typedef hpdcache_mem_id_t [NUM_THREAD_IDS-1:0] hpdc_thid_l15et_t; + typedef req_portid_t [NUM_THREAD_IDS-1:0] hpdc_pid_l15et_t; + typedef logic [wt_cache_pkg::L15_TID_WIDTH-1:0] free_thid_t; + + + // }}} + + // Declaration of internal registers/signals + // {{{ + // Request type + logic req_is_ifill; + logic req_is_read; + logic req_is_write; + logic req_is_unc_read; + logic req_is_unc_write; + logic req_is_atomic; + ariane_pkg::amo_t req_amo_op_type; + // Data & Byte mask sended by the request + logic [HPDcacheMemDataWidth-1:0] req_wdata; + logic [HPDcacheMemDataWidth/8-1:0] req_wbe; + // FSM State + thread_id_fsm_t th_state_q, th_state_d; + // HPDC Req ID + hpdc_thid_l15et_t hpdc_tid_q, hpdc_tid_d; + // HPDC Req Port ID + hpdc_pid_l15et_t hpdc_pid_q, hpdc_pid_d; + // Initial value of the free list + free_thid_t [NUM_THREAD_IDS-1:0] free_thid_list; + // Thread id available to send a request to L1.5 + logic free_thid; + // L1.5 Req Thread ID + free_thid_t req_thid; + // L1.5 Req Valid + logic req_valid; + // L1.5 Req Ready + logic req_ready; + // Size of the unc. store request. Its compulsory to replicate the data of the request according to its size. + hpdcache_mem_addr_t req_wt_address; + hpdcache_pkg::hpdcache_mem_size_t req_wt_size; + logic [2:0] req_wt_offset; + logic [$clog2(HPDcacheMemDataWidth/8)-1:0] first_one_pos,num_ones; + // Invalidations + logic mem_inval_icache_valid; + logic mem_inval_dcache_valid; + logic mem_only_inval; + // LR/SC Back-off mechanism + logic sc_pass; + logic sc_fail; + // }}} + + + // Request + // {{{ + // Determines that the request has been processed by the L1.5 + assign req_ready_o = req_ready; + // Determines that a request is valid + assign l15_req_o.l15_val = req_valid, + // Determines if we can hold the coming response + l15_req_o.l15_req_ack = (mem_inval_dcache_valid) ? (mem_only_inval) ? l15_rtrn_i.l15_val & hpdc_inval_ready_i : // DCACHE Invalidation + l15_rtrn_i.l15_val & hpdc_inval_ready_i & resp_ready_i : // Response + DCACHE Invalidation + l15_rtrn_i.l15_val & resp_ready_i, // Response/ICACHE Invalidation + // Request type + l15_req_o.l15_rqtype = (req_is_ifill) ? L15_IMISS_RQ : + (req_is_read || req_is_unc_read) ? L15_LOAD_RQ : + (req_is_write || req_is_unc_write) ? L15_STORE_RQ : L15_ATOMIC_RQ, + l15_req_o.l15_nc = ~req_i.mem_req_cacheable, + // IMiss Unch: 4B Cach: 32B cacheline; Load/Store/AMO Max 16B cacheline (other possible sizes: 1,2,4,8B) + l15_req_o.l15_size = (req_is_ifill) ? ((req_i.mem_req_cacheable) ? 3'b110 : IcacheNoCachableSize) : // IMiss + (req_is_write && ~WriteByteMaskEnabled) ? req_wt_size + 1'b1: // Store (1/2/4/8) + req_i.mem_req_size + 1'b1, // Load & Unc. Load & Unc. Store (1/2/4/8) & AMO (4/8) & Store (8) + l15_req_o.l15_threadid = req_thid, + // If WBME=0, the store req. hast to be aligned to its size (by default its aligned to WBUF entry size). + l15_req_o.l15_address = (req_is_write && ~WriteByteMaskEnabled) ? req_wt_address : req_i.mem_req_addr, + // Swap Endiannes and replicate transfers shorter than a dword for Unc. Store requests + l15_req_o.l15_data = (SwapEndianess) ? (req_is_unc_write) ? swendian64(repData64(req_wdata[63:0],req_wt_offset[2:0],req_wt_size[1:0])) : + swendian64(req_wdata[63:0]) : + (req_is_unc_write) ? repData64(req_wdata[63:0],req_wt_offset[2:0],req_wt_size[1:0]) : + req_wdata[63:0], + l15_req_o.l15_data_next_entry = '0, // unused in Ariane (only used for CAS atomic requests) + l15_req_o.l15_csm_data = '0, // unused in Ariane (only used for coherence domain restriction features) + l15_req_o.l15_amo_op = req_amo_op_type, + l15_req_o.l15_prefetch = '0, // unused in openpiton + l15_req_o.l15_invalidate_cacheline = '0, // unused by Ariane as L1 has no ECC at the moment + l15_req_o.l15_blockstore = '0, // unused in openpiton + l15_req_o.l15_blockinitstore = '0, // unused in openpiton + l15_req_o.l15_l1rplway = '0, // Not used for this adapter + l15_req_o.l15_be = swendian8B(req_wbe[0 +: 8] | req_wbe[HPDcacheMemDataWidth/8-1 -: 8]); + + + // Type of request based on the request mux index port (req_index_i: IcachePort->IMISS, DcachePort->Read DcacheWbufPort-> Write DcacheUncReadPort-> Un.Read DcacheUncWritePort-> Un. Write/AMO) + assign req_is_ifill = req_index_i[IcachePort], + req_is_read = req_index_i[DcachePort] & (req_i.mem_req_command==hpdcache_pkg::HPDCACHE_MEM_READ), // Load + req_is_write = req_index_i[DcacheWbufPort] & (req_i.mem_req_command==hpdcache_pkg::HPDCACHE_MEM_WRITE), // Store + req_is_unc_read = req_index_i[DcacheUncReadPort] & (req_i.mem_req_command==hpdcache_pkg::HPDCACHE_MEM_READ), // Unc. Load + req_is_unc_write = req_index_i[DcacheUncWritePort] & (req_i.mem_req_command==hpdcache_pkg::HPDCACHE_MEM_WRITE), // Unc. Store + req_is_atomic = req_index_i[DcacheUncWritePort] & (req_i.mem_req_command==hpdcache_pkg::HPDCACHE_MEM_ATOMIC);// AMO + + // Data sended by the request + // If the request is a AMO_CLR, its translated as a AMO_AND. Therefore, the data sended has to be inverted + assign req_wdata = (req_is_atomic & req_i.mem_req_atomic==HPDCACHE_MEM_ATOMIC_CLR) ? ~req_data_i.mem_req_w_data : req_data_i.mem_req_w_data, + req_wbe = req_data_i.mem_req_w_be[HPDcacheMemDataWidth/8-1:0]; + + // }}} + + + // Response + // {{{ + // Determines if the response received is valid for the response demux + assign resp_valid_o = (mem_only_inval & mem_inval_dcache_valid) ? '0 : // Just a DCACHE Invalidation + l15_rtrn_i.l15_val, // Response/ICACHE Invalidation (optional + DCACHE Inval) + // ICACHE Invalidations demux port 0 otherwise saved port id + resp_pid_o = (mem_only_inval & mem_inval_icache_valid) ? IcachePort : + (l15_rtrn_i.l15_returntype==L15_CPX_RESTYPE_ATOMIC_RES) ? DcacheAmoPort : hpdc_pid_q[l15_rtrn_i.l15_threadid]; + + assign resp_o.mem_resp_error = (l15_rtrn_i.l15_returntype==L15_ERR_RET) ? HPDCACHE_MEM_RESP_NOK : HPDCACHE_MEM_RESP_OK, // Should be always HPDCACHE_MEM_RESP_OK, unused in openpiton + resp_o.mem_resp_id = hpdc_tid_q[l15_rtrn_i.l15_threadid], + resp_o.mem_resp_r_last = '1, // OpenPiton sends the entire data in 1 cycle + resp_o.mem_resp_w_is_atomic = sc_pass, + resp_o.mem_resp_r_data = (SwapEndianess) ? {swendian64(l15_rtrn_i.l15_data_3), + swendian64(l15_rtrn_i.l15_data_2), + swendian64(l15_rtrn_i.l15_data_1), + swendian64(l15_rtrn_i.l15_data_0)} : + {l15_rtrn_i.l15_data_3, + l15_rtrn_i.l15_data_2, + l15_rtrn_i.l15_data_1, + l15_rtrn_i.l15_data_0}; + + // Invalidation request + assign resp_o.mem_inval_icache_valid = mem_inval_icache_valid, + resp_o.mem_inval_dcache_valid = mem_inval_dcache_valid, + resp_o.mem_inval = l15_rtrn_i.l15_inval_address; + + assign mem_inval_icache_valid = (l15_rtrn_i.l15_inval_icache_inval || l15_rtrn_i.l15_inval_icache_all_way) & l15_rtrn_i.l15_val, + mem_inval_dcache_valid = (l15_rtrn_i.l15_inval_dcache_inval || l15_rtrn_i.l15_inval_dcache_all_way) & l15_rtrn_i.l15_val, + mem_only_inval = l15_rtrn_i.l15_returntype==L15_EVICT_REQ; + // }}} + + + // Control of the threaids used to access to L1.5. + // {{{ + // FSM to control the L1.5 access protocol (Requests) + // {{{ + always_comb + begin: request_fsm_comb + th_state_d = th_state_q; + hpdc_tid_d = hpdc_tid_q; + hpdc_pid_d = hpdc_pid_q; + req_valid = '0; + req_ready = '0; + unique case (th_state_q) + IDLE: begin + // Valid request and available thread id + if (req_valid_i && free_thid) begin + req_valid = '1; + if (l15_rtrn_i.l15_header_ack) begin + if (l15_rtrn_i.l15_ack) begin + hpdc_tid_d[req_thid] = req_i.mem_req_id; // Save the request id + hpdc_pid_d[req_thid] = req_pid_i; // Save the port id + req_ready = '1; + th_state_d = IDLE; + end else begin + th_state_d = WAITING_ACK; + end + end else begin + th_state_d = WAITING_HEADER_ACK; + end + end else begin + th_state_d = IDLE; + end + end + // Waiting for valid header_ack + WAITING_HEADER_ACK: begin + req_valid = '1; + if (req_valid_i && l15_rtrn_i.l15_header_ack) begin + if (l15_rtrn_i.l15_ack) begin + hpdc_tid_d[req_thid] = req_i.mem_req_id; // Save the request id + hpdc_pid_d[req_thid] = req_pid_i; // Save the port id + req_ready = '1; + th_state_d = IDLE; + end else begin + th_state_d = WAITING_ACK; + end + end else begin + th_state_d = WAITING_HEADER_ACK; + end + end + // Waiting for valid ack (Request valid = 0, but the rest of values have to be maintained) + WAITING_ACK: begin + if (req_valid_i && l15_rtrn_i.l15_ack) begin + hpdc_tid_d[req_thid] = req_i.mem_req_id; // Save the request id + hpdc_pid_d[req_thid] = req_pid_i; // Save the port id + req_ready = '1; + th_state_d = IDLE; + end else begin + th_state_d = WAITING_ACK; + end + end + endcase + end + + + + always_ff @(posedge clk_i or negedge rst_ni) + begin: thread_id_fsm_ff + if (!rst_ni) begin + th_state_q <= IDLE; + for (int i = 0; i < NUM_THREAD_IDS; i++) begin + hpdc_tid_q[i] <= '0; + hpdc_pid_q[i] <= '0; + end + end else begin + th_state_q <= th_state_d; + for (int i = 0; i < NUM_THREAD_IDS; i++) begin + hpdc_tid_q[i] <= hpdc_tid_d[i]; + hpdc_pid_q[i] <= hpdc_pid_d[i]; + end + end + end + // }}} + + // Free list of thread ids + // {{{ + + // Generates the initial value of the free thread id list + always_comb + begin: free_thid_list_init_comb + for (int i = 0; i < NUM_THREAD_IDS; i++) begin + free_thid_list[i] = i; + end + end + + hpdcache_fifo_reg_initialized #( + .FIFO_DEPTH (NUM_THREAD_IDS), + .fifo_data_t (free_thid_t) + ) i_free_threadid_fifo ( + .clk_i, + .rst_ni, + // Valid input if its a valid response + .w_i (resp_ready_i && l15_rtrn_i.l15_val && ~mem_only_inval), + .wok_o (/*unused*/), // Should always be ready to write + // Return thid used for the response + .wdata_i (l15_rtrn_i.l15_threadid), + // Request sended + .r_i (req_valid_i && l15_rtrn_i.l15_ack), + // Thread id available + .rok_o (free_thid), + // Thread id used for the request + .rdata_o (req_thid), + // Initial value/state of the list + .initial_value_i (free_thid_list) + ); + // }}} + // }}} + + // Combinational logic to obtain the store size for data replication and aligned addresses + // {{{ + + always_comb + begin: lzc_comb + first_one_pos = '0; + for (int unsigned i = hpdcache_uint32'(HPDcacheMemDataWidth/8); i > 0; i--) begin + if (req_wbe[i-1]) begin + first_one_pos = i-1; + break; + end + end + end + + assign num_ones = $countones(req_wbe); + + always_comb + begin:rst_size_comb + unique case (num_ones) + 4'b0001: begin + req_wt_size = '0; //1B + req_wt_offset = first_one_pos[2:0]; + req_wt_address = {req_i.mem_req_addr[HPDCACHE_PA_WIDTH-1:3],req_wt_offset[2:0]}; + end + 4'b0010: begin + req_wt_size = 3'b001; //2B + req_wt_offset = (first_one_pos[2:0]-1'b1); + req_wt_address = {req_i.mem_req_addr[HPDCACHE_PA_WIDTH-1:3],req_wt_offset[2:0]}; + end + 4'b0100: begin + req_wt_size = 3'b010; //4B + req_wt_offset = (first_one_pos[2:0]-2'b11); + req_wt_address = {req_i.mem_req_addr[HPDCACHE_PA_WIDTH-1:3],req_wt_offset[2:0]}; + end + 4'b1000: begin + req_wt_size = 3'b011; //8B + req_wt_offset = '0; + req_wt_address = req_i.mem_req_addr; //Already aligned + end + default: begin + req_wt_size = 3'b011; //8B + req_wt_offset = '0; + req_wt_address = req_i.mem_req_addr; //Already aligned + end + endcase + end + // }}} + + // AMO support + // {{{ + // Translator of the atomic operation request type (HPDC->OpenPiton) + // {{{ + always_comb + begin:atomic_req_op_type_comb + unique case (req_i.mem_req_atomic) + HPDCACHE_MEM_ATOMIC_ADD: req_amo_op_type = ariane_pkg::AMO_ADD; + HPDCACHE_MEM_ATOMIC_CLR: req_amo_op_type = ariane_pkg::AMO_AND; + HPDCACHE_MEM_ATOMIC_SET: req_amo_op_type = ariane_pkg::AMO_OR; + HPDCACHE_MEM_ATOMIC_EOR: req_amo_op_type = ariane_pkg::AMO_XOR; + HPDCACHE_MEM_ATOMIC_SMAX: req_amo_op_type = ariane_pkg::AMO_MAX; + HPDCACHE_MEM_ATOMIC_SMIN: req_amo_op_type = ariane_pkg::AMO_MIN; + HPDCACHE_MEM_ATOMIC_UMAX: req_amo_op_type = ariane_pkg::AMO_MAXU; + HPDCACHE_MEM_ATOMIC_UMIN: req_amo_op_type = ariane_pkg::AMO_MINU; + HPDCACHE_MEM_ATOMIC_SWAP: req_amo_op_type = ariane_pkg::AMO_SWAP; + HPDCACHE_MEM_ATOMIC_LDEX: req_amo_op_type = ariane_pkg::AMO_LR; + HPDCACHE_MEM_ATOMIC_STEX: req_amo_op_type = ariane_pkg::AMO_SC; + default: req_amo_op_type = ariane_pkg::AMO_NONE; + endcase + end + // }}} + + // Back-off mechanism for LR/SC completion guarantee + // {{{ + exp_backoff #( + .Seed(3), + .MaxExp(16) + ) i_exp_backoff ( + .clk_i, + .rst_ni, + .set_i ( sc_fail ), + .clr_i ( sc_pass ), + .is_zero_o ( sc_backoff_over_o ) + ); + + assign sc_pass = (l15_rtrn_i.l15_returntype!=L15_CPX_RESTYPE_ATOMIC_RES) ? 1'b0 : + (|l15_rtrn_i.l15_data_0) ? 1'b0 : 1'b1; // AMO_SC pass if data=0 + + assign sc_fail = (l15_rtrn_i.l15_returntype!=L15_CPX_RESTYPE_ATOMIC_RES) ? 1'b0 : + (|l15_rtrn_i.l15_data_0) ? 1'b1 : 1'b0; // AMO_SC fail if data!=0 + // }}} + // }}} + + // Assertions + // {{{ + initial assert (NUM_THREAD_IDS == (hpdcache_pkg::HPDCACHE_MSHR_SETS * hpdcache_pkg::HPDCACHE_MSHR_WAYS)) else + $error("The number of thread ids should be equal to the number of MSHRs of the D$"); + // }}} +endmodule diff --git a/hw/vendor/hpdcache/rtl/tb/.gitignore b/hw/vendor/hpdcache/rtl/tb/.gitignore new file mode 100644 index 00000000..9ab2398c --- /dev/null +++ b/hw/vendor/hpdcache/rtl/tb/.gitignore @@ -0,0 +1,2 @@ +build/ +logs/ diff --git a/hw/vendor/hpdcache/rtl/tb/Makefile b/hw/vendor/hpdcache/rtl/tb/Makefile new file mode 100644 index 00000000..ea800a9e --- /dev/null +++ b/hw/vendor/hpdcache/rtl/tb/Makefile @@ -0,0 +1,356 @@ +## +# Copyright 2023,2024 CEA* +# *Commissariat a l'Energie Atomique et aux Energies Alternatives (CEA) +# Copyright 2025 Inria, Universite Grenoble-Alpes, TIMA +# +# SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 +# +# Licensed under the Solderpad Hardware License v 2.1 (the “License”); you +# may not use this file except in compliance with the License, or, at your +# option, the Apache License version 2.0. You may obtain a copy of the +# License at +# +# https://solderpad.org/licenses/SHL-2.1/ +# +# Unless required by applicable law or agreed to in writing, any work +# distributed under the License is distributed on an “AS IS” BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +## +## +# Author : Cesar Fuguet +# Date : October, 2024 +# Description: HPDCACHE Test Makefile +## +-include user_conf.mk + +TB_DIR := $(shell dirname $(realpath $(firstword $(MAKEFILE_LIST)))) +HPDCACHE_DIR ?= $(TB_DIR)/../.. + +ifndef SYSTEMC_LIBDIR +$(error "SYSTEMC_LIBDIR not defined") +endif + +VERILATOR ?= verilator +VERILATOR_COV ?= verilator_coverage +TOUCH := touch +RM := rm -f +MKDIR := mkdir -p +GDB := gdb +ECHO := echo +SCANLOG := $(TB_DIR)/scripts/scan_logs.pl -listwarnings -listerrors +SHELL := bash + +Q ?= @ +NTRANSACTIONS ?= 10000 +TIMEOUT ?= $$(( $(NTRANSACTIONS)*1000 )) +SEQUENCE ?= random +LOG_LEVEL ?= 1 +SEED ?= 1234 +ERROR_LIMIT ?= 0 +NTESTS ?= 128 +TRACE ?= 0 + +BUILD_DIR := build +LOG_DIR := logs +COV_DIR := coverage +COV_HTML_DIR := coverage_html +VERILATE_LOG := $(BUILD_DIR)/verilate.log +BUILD_LOG := $(BUILD_DIR)/build.log + +RUN_LOG ?= $(LOG_DIR)/run_$(SEQUENCE)_$(SEED).log +TRACE_FILE ?= $(LOG_DIR)/run_$(SEQUENCE)_$(SEED).vcd +COV_FILE ?= $(COV_DIR)/cov_$(SEQUENCE)_$(SEED).dat +COV_MERGEFILE ?= $(COV_DIR)/cov_result.dat +DATE_TIME ?= $(shell date "+%Y_%m_%d-%H%M") + +export CXX := $(CXX) +export LINK := $(CXX) +export CC := $(CC) +export PERL5LIB := $(TB_DIR)/scripts/perl5 + +CONF_DEFINES = + +ifdef CONFIG +include $(CONFIG) +else +include configs/default_config.mk +endif + +include rtl_conf.mk + +USER_CPPFLAGS += -I$(TB_DIR) \ + -I$(TB_DIR)/sc_verif_lib/include \ + -I$(TB_DIR)/sc_verif_lib/modules/generic_cache/include \ + -I$(TB_DIR)/sc_verif_lib/modules/mem_model/include \ + -DDEBUG_HPDCACHE_TEST_SCOREBOARD=1 \ + -DDEBUG_HPDCACHE_TEST_MEM_RESP_MODEL=1 \ + $(if $(filter 1,$(DEBUG)),-DDEBUG) \ + $(CONF_DEFINES) + +VERILATOR_FLAGS += -Wall -Wno-fatal \ + -Werror-PINMISSING \ + -Werror-IMPLICIT \ + -Werror-UNDRIVEN \ + -Werror-MULTIDRIVEN \ + -Wno-PINCONNECTEMPTY \ + --report-unoptflat \ + --pins-bv 2 \ + -O3 -I$(HPDCACHE_DIR)/rtl/src \ + --x-assign unique --x-initial unique + +ifneq ($(DISABLE_TRACE),1) +$(info make: Tracing support enabled) +VERILATOR_FLAGS += --trace --trace-structs +else +$(info make: Tracing support disabled) +endif + +ifneq ($(DISABLE_ASSERT),1) +$(info make: Assertions support enabled) +VERILATOR_FLAGS += --assert +else +$(info make: Assertions support disabled) +endif + +ifeq ($(PROF),1) +$(info make: Profiling support enabled) +VERILATOR_FLAGS += --prof-cfuncs -CFLAGS "-g -pg" +else +$(info make: Profiling support disabled) +VERILATOR_FLAGS += -CFLAGS "-O3" +endif + +ifeq ($(COV),1) +$(shell mkdir -p $(COV_DIR)) +$(info make: Coverage support enabled) +VERILATOR_FLAGS += --coverage +else +$(info make: Coverage support disabled) +endif + +VERILATOR_FLAGS += $(CONF_DEFINES) + +VERILATOR_MAKEFLAGS += USER_CPPFLAGS="$(USER_CPPFLAGS)" \ + USER_LDFLAGS="-Wl,-rpath,$(SYSTEMC_LIBDIR)" \ + USER_LDLIBS="-lscv" + +VERILATOR_RUNFLAGS += +verilator+seed+$(SEED) \ + +verilator+rand+reset+1 \ + +verilator+error+limit+50 \ + -m $(TIMEOUT) \ + -n $(NTRANSACTIONS) \ + -s $(SEQUENCE) \ + -l $(LOG_LEVEL) \ + -r $(SEED) \ + -e $(ERROR_LIMIT) \ + $(if $(filter 1,$(TRACE)), +trace -t $(TRACE_FILE)) \ + $(if $(filter 1,$(COV)),-c $(COV_FILE)) + +TB_TOP := hpdcache_tb +DUT := hpdcache_wrapper + +export SC_COPYRIGHT_MESSAGE=DISABLE + +.PHONY: all +all: help + +.PHONY: verilate build run +verilate: $(VERILATE_LOG) +build: $(BUILD_LOG) +run: $(RUN_LOG) + +define help_message +help: make [target] [arguments] + +This is a Verilator (with SystemC) testbench for the HPDcache block. + +It is a standalone testbench where the HPDcache is not connected to a processor +core. Requests are generated by agents using a sequence defined by the user. +Some sequences are already provided in the sequence_lib subdirectory. + +The testbench also implements a memory response model that acts as the main +memory of the system. + +All the requests and responses to/from the HPDcache are validated automatically +by the testbench scoreboard. + +targets: + +verilate Convert RTL into C++/SystemC sources + +build Compile DUT and testbench sources + + -j + +run Execute the specified test sequence on the + testbench + + [SEQUENCE=$(SEQUENCE)] - test sequence + [TRACE=$(TRACE)] - generate waveform (on VCD format) + [TIMEOUT=$(TIMEOUT)] - maximum nb of run cycles + [LOG_LEVEL=$(LOG_LEVEL)] - level of log verbosity (low = 0, 1, 2, high = 3) + [NTRANSACTIONS=$(NTRANSACTIONS)] - nb of transactions during the test + [SEED=$(SEED)] - random seed + [ERROR_LIMIT=$(ERROR_LIMIT)] - limit of errors during simulation to stop + +nonregression Execute a a non-regression test suite. + This suite executes the specified sequence + NTESTS nb of times. Each time a + different random seed is used + + [SEQUENCE=$(SEQUENCE)] + [TIMEOUT=$(TIMEOUT)] + [LOG_LEVEL=$(LOG_LEVEL)] + [NTRANSACTIONS=$(NTRANSACTIONS)] + [NTESTS=$(NTESTS)] - nb of times the sequence is executed +endef + +SVLOG_SOURCES=\ +$(subst $${HPDCACHE_DIR},$(HPDCACHE_DIR),\ +$(shell sed -n -e '/.*\.sv/p' \ + $(HPDCACHE_DIR)/rtl/hpdcache.Flist hpdcache.vlt.Flist)) + +export help_message + +.PHONY: help +help: + $(Q)$(ECHO) "$${help_message}" + +.PHONY: $(RUN_LOG) +$(RUN_LOG): + $(Q)$(ECHO) "make: run (LOG: $@)" + $(Q)$(MKDIR) $(dir $@) + $(Q)$(RM) $(TRACE_FILE) + $(Q)$(BUILD_DIR)/V$(DUT) $(VERILATOR_RUNFLAGS) >& $@ + $(Q)$(ECHO) "RUN FINISHED" >> $@ + $(Q)$(SCANLOG) -pat scripts/scan_patterns/run_patterns.pat \ + -att scripts/scan_patterns/run_attributes.pat \ + -nowarn $@ >$@.scan 2>&1 + +.PHONY: debug +debug: build + $(Q)$(MKDIR) $(LOG_DIR) + $(Q)$(RM) $(TRACE_FILE) + $(Q)$(GDB) $(BUILD_DIR)/V$(DUT) -ex "b sc_main" -ex "run $(VERILATOR_RUNFLAGS)" + +.PHONY: nonreg +nonreg: nonregression + +.PHONY: nonregression +nonregression: + $(Q)$(ECHO) "Running non-regression testsuite with SEQUENCE=$(SEQUENCE)..." + $(Q)$(MKDIR) $(LOG_DIR)/nonreg_$(DATE_TIME) + $(Q)i=1 ; \ + for s in $(shell head -n $(NTESTS) scripts/random_numbers.dat | tr '\n' ' ') ; do \ + $(ECHO) "[$$i/$(NTESTS)] Running sequence $(SEQUENCE) SEED=$$s" ; \ + ((i++)) ; \ + $(MAKE) -s run SEED=$$s \ + RUN_LOG=$(LOG_DIR)/nonreg_$(DATE_TIME)/$(SEQUENCE)_$$s.log ; \ + done ; + $(Q)$(SCANLOG) -pat scripts/scan_patterns/run_patterns.pat \ + -att scripts/scan_patterns/run_attributes.pat \ + -nowarn $(LOG_DIR)/nonreg_$(DATE_TIME)/$(SEQUENCE)_*.log \ + |& tee $(LOG_DIR)/nonreg_$(DATE_TIME)/nonreg.scan.log + +.PHONY: cov +cov: $(COV_MERGEFILE) $(COV_MERGEFILE).info + $(Q)$(ECHO) "make: compile coverage data" + $(Q)$(MKDIR) $(COV_DIR) + $(Q)$(VERILATOR_COV) --annotate-points --annotate $(COV_DIR)/result $< + +$(COV_MERGEFILE): $(filter-out $(COV_MERGEFILE), $(wildcard $(COV_DIR)/*.dat)) + $(Q)$(MKDIR) $(COV_DIR) + $(Q)$(ECHO) "Merging coverage data into the $@ file" + $(Q)$(VERILATOR_COV) --annotate-points --write $@ $^ + +$(COV_MERGEFILE).info: $(filter-out $(COV_MERGEFILE), $(wildcard $(COV_DIR)/*.dat)) + $(Q)$(MKDIR) $(COV_DIR) + $(Q)$(ECHO) "Merging coverage data into the $@ file" + $(Q)$(VERILATOR_COV) --annotate-points --write-info $@ $^ + +.PHONY: cov_html +cov_html: cov $(COV_HTML_DIR)/index.html + +$(COV_HTML_DIR)/index.html: $(COV_MERGEFILE).info + genhtml --branch-coverage --function-coverage -o coverage_html $< + +$(VERILATE_LOG): print_config $(SVLOG_SOURCES) $(TB_TOP).cpp + $(Q)$(ECHO) "Verilating the RTL sources... (LOG: $@)" + $(Q)$(MKDIR) $(dir $@) + $(Q)$(VERILATOR) --sc $(VERILATOR_FLAGS) -Mdir $(BUILD_DIR) \ + $(if $(filter $(DISABLE_WAIVERS),1),,scripts/verilate_waivers.vlt) \ + +incdir+$(HPDCACHE_DIR)/rtl/include \ + $(SVLOG_SOURCES) \ + --exe $(TB_TOP).cpp --top-module $(DUT) >& $@ + $(Q)$(ECHO) "VERILATE FINISHED" >> $@ + $(Q)$(SCANLOG) -pat scripts/scan_patterns/verilate_patterns.pat \ + -nowarn $@ |& tee $@.scan + $(Q)$(ECHO) "Verilate done" + +$(BUILD_LOG): print_config $(VERILATE_LOG) $(TB_TOP).cpp + $(Q)$(ECHO) "Build the testbench... (LOG: $@)" + $(Q)$(MKDIR) $(dir $@) + $(Q)$(MAKE) -C $(BUILD_DIR) -f "V$(DUT).mk" "V$(DUT)" $(VERILATOR_MAKEFLAGS) >& $@ + $(Q)$(ECHO) "BUILD FINISHED" >> $@ + $(Q)$(SCANLOG) -pat scripts/scan_patterns/build_patterns.pat \ + -nowarn $@ |& tee $@.scan + $(Q)$(ECHO) "Build done" + +.PHONY: clean clean_log clean_cover clean_all distclean +clean: + $(Q)$(ECHO) "Cleaning build directory..." + $(Q)$(RM) -r $(BUILD_DIR) + +clean_log: + $(Q)$(ECHO) "Cleaning logs directory..." + $(Q)$(RM) -r $(LOG_DIR) + +clean_cover: + $(Q)$(ECHO) "Cleaning coverage directory..." + $(Q)$(RM) -r $(COV_DIR) $(COV_HTML_DIR) + +clean_all: clean clean_log clean_cover +distclean: clean_all + +print_config: + @printf "CONF_HPDCACHE_PA_WIDTH=$(CONF_HPDCACHE_PA_WIDTH)\n" + @printf "CONF_HPDCACHE_SETS=$(CONF_HPDCACHE_SETS)\n" + @printf "CONF_HPDCACHE_WAYS=$(CONF_HPDCACHE_WAYS)\n" + @printf "CONF_HPDCACHE_WORD_WIDTH=$(CONF_HPDCACHE_WORD_WIDTH)\n" + @printf "CONF_HPDCACHE_WORD_USER_WIDTH=$(CONF_HPDCACHE_WORD_USER_WIDTH)\n" + @printf "CONF_HPDCACHE_CL_WORDS=$(CONF_HPDCACHE_CL_WORDS)\n" + @printf "CONF_HPDCACHE_REQ_WORDS=$(CONF_HPDCACHE_REQ_WORDS)\n" + @printf "CONF_HPDCACHE_REQ_TRANS_ID_WIDTH=$(CONF_HPDCACHE_REQ_TRANS_ID_WIDTH)\n" + @printf "CONF_HPDCACHE_REQ_SRC_ID_WIDTH=$(CONF_HPDCACHE_REQ_SRC_ID_WIDTH)\n" + @printf "CONF_HPDCACHE_VICTIM_SEL=$(CONF_HPDCACHE_VICTIM_SEL)\n" + @printf "CONF_HPDCACHE_DATA_WAYS_PER_RAM_WORD=$(CONF_HPDCACHE_DATA_WAYS_PER_RAM_WORD)\n" + @printf "CONF_HPDCACHE_DATA_SETS_PER_RAM=$(CONF_HPDCACHE_DATA_SETS_PER_RAM)\n" + @printf "CONF_HPDCACHE_DATA_RAM_WBYTEENABLE=$(CONF_HPDCACHE_DATA_RAM_WBYTEENABLE)\n" + @printf "CONF_HPDCACHE_ACCESS_WORDS=$(CONF_HPDCACHE_ACCESS_WORDS)\n" + @printf "CONF_HPDCACHE_WBUF_DIR_ENTRIES=$(CONF_HPDCACHE_WBUF_DIR_ENTRIES)\n" + @printf "CONF_HPDCACHE_WBUF_DATA_ENTRIES=$(CONF_HPDCACHE_WBUF_DATA_ENTRIES)\n" + @printf "CONF_HPDCACHE_WBUF_WORDS=$(CONF_HPDCACHE_WBUF_WORDS)\n" + @printf "CONF_HPDCACHE_WBUF_TIMECNT_WIDTH=$(CONF_HPDCACHE_WBUF_TIMECNT_WIDTH)\n" + @printf "CONF_HPDCACHE_WBUF_SEND_FEEDTHROUGH=$(CONF_HPDCACHE_WBUF_SEND_FEEDTHROUGH)\n" + @printf "CONF_HPDCACHE_MSHR_SETS=$(CONF_HPDCACHE_MSHR_SETS)\n" + @printf "CONF_HPDCACHE_MSHR_WAYS=$(CONF_HPDCACHE_MSHR_WAYS)\n" + @printf "CONF_HPDCACHE_MSHR_WAYS_PER_RAM_WORD=$(CONF_HPDCACHE_MSHR_WAYS_PER_RAM_WORD)\n" + @printf "CONF_HPDCACHE_MSHR_SETS_PER_RAM=$(CONF_HPDCACHE_MSHR_SETS_PER_RAM)\n" + @printf "CONF_HPDCACHE_MSHR_RAM_WBYTEENABLE=$(CONF_HPDCACHE_MSHR_RAM_WBYTEENABLE)\n" + @printf "CONF_HPDCACHE_MSHR_USE_REGBANK=$(CONF_HPDCACHE_MSHR_USE_REGBANK)\n" + @printf "CONF_HPDCACHE_CBUF_ENTRIES=$(CONF_HPDCACHE_CBUF_ENTRIES)\n" + @printf "CONF_HPDCACHE_REFILL_FIFO_DEPTH=$(CONF_HPDCACHE_REFILL_FIFO_DEPTH)\n" + @printf "CONF_HPDCACHE_REFILL_CORE_RSP_FEEDTHROUGH=$(CONF_HPDCACHE_REFILL_CORE_RSP_FEEDTHROUGH)\n" + @printf "CONF_HPDCACHE_RTAB_ENTRIES=$(CONF_HPDCACHE_RTAB_ENTRIES)\n" + @printf "CONF_HPDCACHE_FLUSH_ENTRIES=$(CONF_HPDCACHE_FLUSH_ENTRIES)\n" + @printf "CONF_HPDCACHE_FLUSH_FIFO_DEPTH=$(CONF_HPDCACHE_FLUSH_FIFO_DEPTH)\n" + @printf "CONF_HPDCACHE_MEM_ADDR_WIDTH=$(CONF_HPDCACHE_MEM_ADDR_WIDTH)\n" + @printf "CONF_HPDCACHE_MEM_ID_WIDTH=$(CONF_HPDCACHE_MEM_ID_WIDTH)\n" + @printf "CONF_HPDCACHE_MEM_DATA_WIDTH=$(CONF_HPDCACHE_MEM_DATA_WIDTH)\n" + @printf "CONF_HPDCACHE_WT_ENABLE=$(CONF_HPDCACHE_WT_ENABLE)\n" + @printf "CONF_HPDCACHE_WB_ENABLE=$(CONF_HPDCACHE_WB_ENABLE)\n" + @printf "CONF_HPDCACHE_USER_ENABLE=$(CONF_HPDCACHE_USER_ENABLE)\n" + @printf "CONF_HPDCACHE_CAP_AMO_ENABLE=$(CONF_HPDCACHE_CAP_AMO_ENABLE)\n" + @printf "CONF_HPDCACHE_LOW_LATENCY=$(CONF_HPDCACHE_LOW_LATENCY)\n" diff --git a/hw/vendor/hpdcache/rtl/tb/README.md b/hw/vendor/hpdcache/rtl/tb/README.md new file mode 100644 index 00000000..2f6585fb --- /dev/null +++ b/hw/vendor/hpdcache/rtl/tb/README.md @@ -0,0 +1,103 @@ +# Verilator-compatible testbench for the HPDcache block + +This is a Verilator-compatible (with SystemC) testbench for the HPDcache block. + +It is a standalone testbench where the HPDcache is not connected to a processor +core. Requests are generated by agents using a sequence defined by the user. +Some sequences are already provided in the sequence_lib subdirectory. + +The testbench also implements a memory response model that acts as the main +memory of the system. + +All the requests and responses to/from the HPDcache are validated automatically +by the testbench scoreboard. + +## Prerequisites + +This testbench requires the following tools and libraries pre-installed: + +- [Verilator](https://verilator.org/guide/latest/install.html) - Version 5.018 or newer +- [SystemC Library](https://www.accellera.org/downloads/standards/systemc) - Version 2.3.4 or newer +- [SystemC Verification Library (SCV)](https://www.accellera.org/downloads/standards/systemc) - Version 2.0.1 or newer +- Perl - Version 5 + +Please follow the corresponding installation instructions for those packages to +install them in your system prior to the execution of this testbench. + +You need to set the following environment variables (bash: `$ export`, csh: `$ setenv`): + +- Set the SYSTEMC_LIBDIR to the libdir directory of the SystemC library installation + (e.g. `$ export SYSTEMC_LIBDIR=/lib-linux64`) +- Add the bin/ subdirectory of the Verilator installation into your PATH + (e.g. `$ export PATH=/bin:${PATH}`) + +## Usage + +Show the help message of the testbench: + +```bash +$ make help +``` + +### The basic usage of the testbench is to execute the following sequence of commands + +1. Verilate the RTL (convert to C++) and build the testbench: + +Both stages with a single command: + +```bash +$ make build -j +``` + +Or both stages separately: + +```bash +$ make verilate +$ make build -j +``` + +If you have any errors or warnings, you can look into them in more detail on +their corresponding log files: + +- build/verilate.log +- build/build.log + +2. Run a simulation choosing one sequence. It can be for example the random +sequence (defined in the file sequence_lib/hpdcache_test_random_seq.h): + +```bash +$ make run SEQUENCE=random LOG_LEVEL=1 NTRANSACTIONS=10000 SEED=42 +``` + +If you need to debug the simulation waveforms, you can generate them in VCD +format using passing the TRACE=1 argument to the run command of the makefile: + +```bash +$ make run SEQUENCE=random LOG_LEVEL=1 NTRANSACTIONS=10000 SEED=42 TRACE=1 +``` + +Then to save some disk space, you can convert the VCD file to the FST format +using the vcd2fst script. For example, to convert the VCD file generated by the +previous command, you can use the following command: + +```bash +$ ./scripts/vcd2fst.sh logs/run_random_42.vcd +``` + +This will replace the `logs/run_random_42.vcd` by the `logs/run_random_42.vcd.fst`. + +### Non-regression suite + +First build the testbench as explained above. + +Then, the following example command will run 32 tests (with 32 different seeds) +using the random sequence: + +```bash +$ make nonregression SEQUENCE=random LOG_LEVEL=1 NTRANSACTIONS=10000 NTESTS=32 +``` + +### Logs + +The build logs are written in the `build/` subdirectory. The simulation logs +are written in the `logs/` subdirectory. diff --git a/hw/vendor/hpdcache/rtl/tb/configs/default_config.mk b/hw/vendor/hpdcache/rtl/tb/configs/default_config.mk new file mode 100644 index 00000000..2adf4121 --- /dev/null +++ b/hw/vendor/hpdcache/rtl/tb/configs/default_config.mk @@ -0,0 +1,64 @@ +## +# Copyright 2023,2024 CEA* +# *Commissariat a l'Energie Atomique et aux Energies Alternatives (CEA) +# Copyright 2025 Inria, Universite Grenoble-Alpes, TIMA +# +# SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 +# +# Licensed under the Solderpad Hardware License v 2.1 (the “License”); you +# may not use this file except in compliance with the License, or, at your +# option, the Apache License version 2.0. You may obtain a copy of the +# License at +# +# https://solderpad.org/licenses/SHL-2.1/ +# +# Unless required by applicable law or agreed to in writing, any work +# distributed under the License is distributed on an “AS IS” BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +## +## +# Author : Cesar Fuguet +# Date : October, 2024 +# Description: HPDCACHE Test Makefile +## +CONF_HPDCACHE_PA_WIDTH=56 +CONF_HPDCACHE_WORD_WIDTH=64 +CONF_HPDCACHE_WORD_USER_WIDTH=1 +CONF_HPDCACHE_SETS=128 +CONF_HPDCACHE_WAYS=4 +CONF_HPDCACHE_CL_WORDS=4 +CONF_HPDCACHE_REQ_WORDS=1 +CONF_HPDCACHE_REQ_TRANS_ID_WIDTH=6 +CONF_HPDCACHE_REQ_SRC_ID_WIDTH=3 +CONF_HPDCACHE_VICTIM_SEL=HPDCACHE_VICTIM_PLRU +CONF_HPDCACHE_DATA_WAYS_PER_RAM_WORD=2 +CONF_HPDCACHE_DATA_SETS_PER_RAM=$(CONF_HPDCACHE_SETS) +CONF_HPDCACHE_DATA_RAM_WBYTEENABLE=1 +CONF_HPDCACHE_ACCESS_WORDS=4 +CONF_HPDCACHE_MSHR_SETS=1 +CONF_HPDCACHE_MSHR_WAYS=8 +CONF_HPDCACHE_MSHR_WAYS_PER_RAM_WORD=$(CONF_HPDCACHE_MSHR_WAYS) +CONF_HPDCACHE_MSHR_SETS_PER_RAM=$(CONF_HPDCACHE_MSHR_SETS) +CONF_HPDCACHE_MSHR_RAM_WBYTEENABLE=1 +CONF_HPDCACHE_MSHR_USE_REGBANK=1 +CONF_HPDCACHE_CBUF_ENTRIES=4 +CONF_HPDCACHE_REFILL_CORE_RSP_FEEDTHROUGH=1 +CONF_HPDCACHE_REFILL_FIFO_DEPTH=2 +CONF_HPDCACHE_WBUF_DIR_ENTRIES=8 +CONF_HPDCACHE_WBUF_DATA_ENTRIES=8 +CONF_HPDCACHE_WBUF_WORDS=2 +CONF_HPDCACHE_WBUF_TIMECNT_WIDTH=3 +CONF_HPDCACHE_WBUF_SEND_FEEDTHROUGH=0 +CONF_HPDCACHE_RTAB_ENTRIES=4 +CONF_HPDCACHE_FLUSH_ENTRIES=4 +CONF_HPDCACHE_FLUSH_FIFO_DEPTH=2 +CONF_HPDCACHE_MEM_ADDR_WIDTH=64 +CONF_HPDCACHE_MEM_ID_WIDTH=4 +CONF_HPDCACHE_MEM_DATA_WIDTH=256 +CONF_HPDCACHE_WT_ENABLE=1 +CONF_HPDCACHE_WB_ENABLE=1 +CONF_HPDCACHE_USER_ENABLE=0 +CONF_HPDCACHE_CAP_AMO_ENABLE=0 +CONF_HPDCACHE_LOW_LATENCY=1 diff --git a/hw/vendor/hpdcache/rtl/tb/configs/embedded_config.mk b/hw/vendor/hpdcache/rtl/tb/configs/embedded_config.mk new file mode 100644 index 00000000..379fc24b --- /dev/null +++ b/hw/vendor/hpdcache/rtl/tb/configs/embedded_config.mk @@ -0,0 +1,64 @@ +## +# Copyright 2023,2024 CEA* +# *Commissariat a l'Energie Atomique et aux Energies Alternatives (CEA) +# Copyright 2025 Inria, Universite Grenoble-Alpes, TIMA +# +# SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 +# +# Licensed under the Solderpad Hardware License v 2.1 (the “License”); you +# may not use this file except in compliance with the License, or, at your +# option, the Apache License version 2.0. You may obtain a copy of the +# License at +# +# https://solderpad.org/licenses/SHL-2.1/ +# +# Unless required by applicable law or agreed to in writing, any work +# distributed under the License is distributed on an “AS IS” BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +## +## +# Author : Cesar Fuguet +# Date : October, 2024 +# Description: HPDCACHE Test Makefile +## +CONF_HPDCACHE_PA_WIDTH=34 +CONF_HPDCACHE_WORD_WIDTH=32 +CONF_HPDCACHE_WORD_USER_WIDTH=1 +CONF_HPDCACHE_SETS=32 +CONF_HPDCACHE_WAYS=4 +CONF_HPDCACHE_CL_WORDS=4 +CONF_HPDCACHE_REQ_WORDS=1 +CONF_HPDCACHE_REQ_TRANS_ID_WIDTH=3 +CONF_HPDCACHE_REQ_SRC_ID_WIDTH=3 +CONF_HPDCACHE_VICTIM_SEL=HPDCACHE_VICTIM_RANDOM +CONF_HPDCACHE_DATA_WAYS_PER_RAM_WORD=4 +CONF_HPDCACHE_DATA_SETS_PER_RAM=$(CONF_HPDCACHE_SETS) +CONF_HPDCACHE_DATA_RAM_WBYTEENABLE=1 +CONF_HPDCACHE_ACCESS_WORDS=4 +CONF_HPDCACHE_MSHR_SETS=1 +CONF_HPDCACHE_MSHR_WAYS=4 +CONF_HPDCACHE_MSHR_WAYS_PER_RAM_WORD=$(CONF_HPDCACHE_MSHR_WAYS) +CONF_HPDCACHE_MSHR_SETS_PER_RAM=$(CONF_HPDCACHE_MSHR_SETS) +CONF_HPDCACHE_MSHR_RAM_WBYTEENABLE=0 +CONF_HPDCACHE_MSHR_USE_REGBANK=1 +CONF_HPDCACHE_CBUF_ENTRIES=2 +CONF_HPDCACHE_REFILL_CORE_RSP_FEEDTHROUGH=1 +CONF_HPDCACHE_REFILL_FIFO_DEPTH=2 +CONF_HPDCACHE_WBUF_DIR_ENTRIES=4 +CONF_HPDCACHE_WBUF_DATA_ENTRIES=2 +CONF_HPDCACHE_WBUF_WORDS=1 +CONF_HPDCACHE_WBUF_TIMECNT_WIDTH=3 +CONF_HPDCACHE_WBUF_SEND_FEEDTHROUGH=0 +CONF_HPDCACHE_RTAB_ENTRIES=2 +CONF_HPDCACHE_FLUSH_ENTRIES=4 +CONF_HPDCACHE_FLUSH_FIFO_DEPTH=2 +CONF_HPDCACHE_MEM_ADDR_WIDTH=64 +CONF_HPDCACHE_MEM_ID_WIDTH=4 +CONF_HPDCACHE_MEM_DATA_WIDTH=64 +CONF_HPDCACHE_WT_ENABLE=1 +CONF_HPDCACHE_WB_ENABLE=0 +CONF_HPDCACHE_USER_ENABLE=0 +CONF_HPDCACHE_CAP_AMO_ENABLE=0 +CONF_HPDCACHE_LOW_LATENCY=1 diff --git a/hw/vendor/hpdcache/rtl/tb/configs/hpc_config.mk b/hw/vendor/hpdcache/rtl/tb/configs/hpc_config.mk new file mode 100644 index 00000000..a2a491ed --- /dev/null +++ b/hw/vendor/hpdcache/rtl/tb/configs/hpc_config.mk @@ -0,0 +1,64 @@ +## +# Copyright 2023,2024 CEA* +# *Commissariat a l'Energie Atomique et aux Energies Alternatives (CEA) +# Copyright 2025 Inria, Universite Grenoble-Alpes, TIMA +# +# SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 +# +# Licensed under the Solderpad Hardware License v 2.1 (the “License”); you +# may not use this file except in compliance with the License, or, at your +# option, the Apache License version 2.0. You may obtain a copy of the +# License at +# +# https://solderpad.org/licenses/SHL-2.1/ +# +# Unless required by applicable law or agreed to in writing, any work +# distributed under the License is distributed on an “AS IS” BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +## +## +# Author : Cesar Fuguet +# Date : October, 2024 +# Description: HPDCACHE Test Makefile +## +CONF_HPDCACHE_PA_WIDTH=56 +CONF_HPDCACHE_WORD_WIDTH=64 +CONF_HPDCACHE_WORD_USER_WIDTH=1 +CONF_HPDCACHE_SETS=64 +CONF_HPDCACHE_WAYS=8 +CONF_HPDCACHE_CL_WORDS=8 +CONF_HPDCACHE_REQ_WORDS=4 +CONF_HPDCACHE_REQ_TRANS_ID_WIDTH=6 +CONF_HPDCACHE_REQ_SRC_ID_WIDTH=3 +CONF_HPDCACHE_VICTIM_SEL=HPDCACHE_VICTIM_PLRU +CONF_HPDCACHE_DATA_WAYS_PER_RAM_WORD=2 +CONF_HPDCACHE_DATA_SETS_PER_RAM=$(CONF_HPDCACHE_SETS) +CONF_HPDCACHE_DATA_RAM_WBYTEENABLE=1 +CONF_HPDCACHE_ACCESS_WORDS=4 +CONF_HPDCACHE_MSHR_SETS=32 +CONF_HPDCACHE_MSHR_WAYS=2 +CONF_HPDCACHE_MSHR_WAYS_PER_RAM_WORD=$(CONF_HPDCACHE_MSHR_WAYS) +CONF_HPDCACHE_MSHR_SETS_PER_RAM=$(CONF_HPDCACHE_MSHR_SETS) +CONF_HPDCACHE_MSHR_RAM_WBYTEENABLE=1 +CONF_HPDCACHE_MSHR_USE_REGBANK=0 +CONF_HPDCACHE_CBUF_ENTRIES=4 +CONF_HPDCACHE_REFILL_CORE_RSP_FEEDTHROUGH=1 +CONF_HPDCACHE_REFILL_FIFO_DEPTH=2 +CONF_HPDCACHE_WBUF_DIR_ENTRIES=16 +CONF_HPDCACHE_WBUF_DATA_ENTRIES=8 +CONF_HPDCACHE_WBUF_WORDS=2 +CONF_HPDCACHE_WBUF_TIMECNT_WIDTH=3 +CONF_HPDCACHE_WBUF_SEND_FEEDTHROUGH=0 +CONF_HPDCACHE_RTAB_ENTRIES=4 +CONF_HPDCACHE_FLUSH_ENTRIES=8 +CONF_HPDCACHE_FLUSH_FIFO_DEPTH=4 +CONF_HPDCACHE_MEM_ADDR_WIDTH=64 +CONF_HPDCACHE_MEM_ID_WIDTH=7 +CONF_HPDCACHE_MEM_DATA_WIDTH=512 +CONF_HPDCACHE_WT_ENABLE=1 +CONF_HPDCACHE_WB_ENABLE=1 +CONF_HPDCACHE_USER_ENABLE=0 +CONF_HPDCACHE_CAP_AMO_ENABLE=0 +CONF_HPDCACHE_LOW_LATENCY=0 diff --git a/hw/vendor/hpdcache/rtl/tb/hpdcache.vlt.Flist b/hw/vendor/hpdcache/rtl/tb/hpdcache.vlt.Flist new file mode 100644 index 00000000..c89e404d --- /dev/null +++ b/hw/vendor/hpdcache/rtl/tb/hpdcache.vlt.Flist @@ -0,0 +1,34 @@ +## +# Copyright 2023,2024 CEA* +# *Commissariat a l'Energie Atomique et aux Energies Alternatives (CEA) +# +# SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 +# +# Licensed under the Solderpad Hardware License v 2.1 (the “License”); you +# may not use this file except in compliance with the License, or, at your +# option, the Apache License version 2.0. You may obtain a copy of the +# License at +# +# https://solderpad.org/licenses/SHL-2.1/ +# +# Unless required by applicable law or agreed to in writing, any work +# distributed under the License is distributed on an “AS IS” BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +## +## +# Author : Cesar Fuguet +# Date : October, 2024 +# Description: HPDCACHE Testbench Filelist +## +# list of HPDcache files +-F ${HPDCACHE_DIR}/rtl/hpdcache.Flist + +# RAM macros +${HPDCACHE_DIR}/rtl/src/common/macros/behav/hpdcache_sram_1rw.sv +${HPDCACHE_DIR}/rtl/src/common/macros/behav/hpdcache_sram_wbyteenable_1rw.sv +${HPDCACHE_DIR}/rtl/src/common/macros/behav/hpdcache_sram_wmask_1rw.sv + +# Simulation wrapper for Verilator +hpdcache_wrapper.sv diff --git a/hw/vendor/hpdcache/rtl/tb/hpdcache_tb.cpp b/hw/vendor/hpdcache/rtl/tb/hpdcache_tb.cpp new file mode 100644 index 00000000..c38846cf --- /dev/null +++ b/hw/vendor/hpdcache/rtl/tb/hpdcache_tb.cpp @@ -0,0 +1,520 @@ +/** + * Copyright 2023,2024 CEA* + * *Commissariat a l'Energie Atomique et aux Energies Alternatives (CEA) + * Copyright 2025 Inria, Universite Grenoble-Alpes, TIMA + * + * SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 + * + * Licensed under the Solderpad Hardware License v 2.1 (the “License”); you + * may not use this file except in compliance with the License, or, at your + * option, the Apache License version 2.0. You may obtain a copy of the + * License at + * + * https://solderpad.org/licenses/SHL-2.1/ + * + * Unless required by applicable law or agreed to in writing, any work + * distributed under the License is distributed on an “AS IS” BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +/** + * Author : Cesar Fuguet + * Date : October, 2024 + * Description: Definition of the entry point for the HPDCACHE testbench + */ +#include +#include +#include +#include +#include +#include +#include + +#include +#if VM_TRACE +# include +#endif + +#include "logger.h" +#include "Vhpdcache_wrapper.h" + +#include "hpdcache_test_defs.h" +#include "hpdcache_test_agent.h" +#include "hpdcache_test_scoreboard.h" +#include "hpdcache_test_mem_resp_model.h" +#include "hpdcache_test_sequence.h" +#include "sequence_lib/hpdcache_test_random_seq.h" +#include "sequence_lib/hpdcache_test_read_seq.h" +#include "sequence_lib/hpdcache_test_write_seq.h" +#include "sequence_lib/hpdcache_test_unique_set_seq.h" + +class hpdcache_test +{ +public: + uint64_t max_cycles; + uint64_t max_trans; + size_t error_limit; + bool trace_on; + std::string trace_name; + +private: + std::string covname; + std::shared_ptr tf; + std::shared_ptr seq; + +public: + hpdcache_test() : + max_cycles(1ULL << 30), + max_trans(100), + error_limit(0), + covname(""), + tf(nullptr), + seq(nullptr) + { + top = std::make_shared ("i_top"); + hpdcache_test_agent_i = std::make_shared ("i_agent"); + hpdcache_test_mem_resp_model_i = std::make_shared ("i_mem"); + hpdcache_test_scoreboard_i = std::make_shared ("i_scoreboard"); + } + + void build() + { + std::cout << "Building the testbench..." << std::endl; + + top->clk_i (clk_i); + top->rst_ni (rst_ni); + top->wbuf_flush_i (wbuf_flush); + top->core_req_valid_i (core_req_valid); + top->core_req_ready_o (core_req_ready); + top->core_req_i (core_req); + top->core_req_abort_i (core_req_abort); + top->core_req_tag_i (core_req_tag); + top->core_req_pma_i (core_req_pma); + top->core_rsp_valid_o (core_rsp_valid); + top->core_rsp_o (core_rsp); + top->mem_req_read_ready_i (mem_req_read_ready); + top->mem_req_read_valid_o (mem_req_read_valid); + top->mem_req_read_addr_o (mem_req_read_addr); + top->mem_req_read_len_o (mem_req_read_len); + top->mem_req_read_size_o (mem_req_read_size); + top->mem_req_read_id_o (mem_req_read_id); + top->mem_req_read_command_o (mem_req_read_command); + top->mem_req_read_atomic_o (mem_req_read_atomic); + top->mem_req_read_cacheable_o (mem_req_read_cacheable); + top->mem_resp_read_ready_o (mem_resp_read_ready); + top->mem_resp_read_valid_i (mem_resp_read_valid); + top->mem_resp_read_error_i (mem_resp_read_error); + top->mem_resp_read_id_i (mem_resp_read_id); + top->mem_resp_read_data_i (mem_resp_read_data); + top->mem_resp_read_last_i (mem_resp_read_last); + top->mem_req_write_ready_i (mem_req_write_ready); + top->mem_req_write_valid_o (mem_req_write_valid); + top->mem_req_write_addr_o (mem_req_write_addr); + top->mem_req_write_len_o (mem_req_write_len); + top->mem_req_write_size_o (mem_req_write_size); + top->mem_req_write_id_o (mem_req_write_id); + top->mem_req_write_command_o (mem_req_write_command); + top->mem_req_write_atomic_o (mem_req_write_atomic); + top->mem_req_write_cacheable_o (mem_req_write_cacheable); + top->mem_req_write_data_ready_i (mem_req_write_data_ready); + top->mem_req_write_data_valid_o (mem_req_write_data_valid); + top->mem_req_write_data_o (mem_req_write_data); + top->mem_req_write_be_o (mem_req_write_be); + top->mem_req_write_last_o (mem_req_write_last); + top->mem_resp_write_ready_o (mem_resp_write_ready); + top->mem_resp_write_valid_i (mem_resp_write_valid); + top->mem_resp_write_is_atomic_i (mem_resp_write_is_atomic); + top->mem_resp_write_error_i (mem_resp_write_error); + top->mem_resp_write_id_i (mem_resp_write_id); + top->evt_cache_write_miss_o (evt_cache_write_miss); + top->evt_cache_read_miss_o (evt_cache_read_miss); + top->evt_uncached_req_o (evt_uncached_req); + top->evt_cmo_req_o (evt_cmo_req); + top->evt_write_req_o (evt_write_req); + top->evt_read_req_o (evt_read_req); + top->evt_prefetch_req_o (evt_prefetch_req); + top->evt_req_on_hold_o (evt_req_on_hold); + top->evt_rtab_rollback_o (evt_rtab_rollback); + top->evt_stall_refill_o (evt_stall_refill); + top->evt_stall_o (evt_stall); + top->wbuf_empty_o (wbuf_empty); + top->cfg_enable_i (cfg_enable); + top->cfg_wbuf_threshold_i (cfg_wbuf_threshold); + top->cfg_wbuf_reset_timecnt_on_write_i (cfg_wbuf_reset_timecnt_on_write); + top->cfg_wbuf_sequential_waw_i (cfg_wbuf_sequential_waw); + top->cfg_wbuf_inhibit_write_coalescing_i (cfg_wbuf_inhibit_write_coalescing); + top->cfg_prefetch_updt_plru_i (cfg_prefetch_updt_plru); + top->cfg_error_on_cacheable_amo_i (cfg_error_on_cacheable_amo); + top->cfg_rtab_single_entry_i (cfg_rtab_single_entry); + top->cfg_default_wb_i (cfg_default_wb); + + hpdcache_test_agent_i->clk_i (clk_i); + hpdcache_test_agent_i->rst_ni (rst_ni); + hpdcache_test_agent_i->core_req_valid_o (core_req_valid); + hpdcache_test_agent_i->core_req_ready_i (core_req_ready); + hpdcache_test_agent_i->core_req_o (core_req); + hpdcache_test_agent_i->core_req_tag_o (core_req_tag); + hpdcache_test_agent_i->core_req_pma_o (core_req_pma); + hpdcache_test_agent_i->core_req_abort_o (core_req_abort); + hpdcache_test_agent_i->core_rsp_valid_i (core_rsp_valid); + hpdcache_test_agent_i->core_rsp_i (core_rsp); + hpdcache_test_agent_i->sb_core_req_o (sb_core_req); + hpdcache_test_agent_i->sb_core_resp_o (sb_core_resp); + + hpdcache_test_mem_resp_model_i->clk_i (clk_i); + hpdcache_test_mem_resp_model_i->rst_ni (rst_ni); + hpdcache_test_mem_resp_model_i->mem_req_read_ready_o (mem_req_read_ready); + hpdcache_test_mem_resp_model_i->mem_req_read_valid_i (mem_req_read_valid); + hpdcache_test_mem_resp_model_i->mem_req_read_addr_i (mem_req_read_addr); + hpdcache_test_mem_resp_model_i->mem_req_read_len_i (mem_req_read_len); + hpdcache_test_mem_resp_model_i->mem_req_read_size_i (mem_req_read_size); + hpdcache_test_mem_resp_model_i->mem_req_read_id_i (mem_req_read_id); + hpdcache_test_mem_resp_model_i->mem_req_read_command_i (mem_req_read_command); + hpdcache_test_mem_resp_model_i->mem_req_read_atomic_i (mem_req_read_atomic); + hpdcache_test_mem_resp_model_i->mem_req_read_cacheable_i (mem_req_read_cacheable); + hpdcache_test_mem_resp_model_i->mem_resp_read_ready_i (mem_resp_read_ready); + hpdcache_test_mem_resp_model_i->mem_resp_read_valid_o (mem_resp_read_valid); + hpdcache_test_mem_resp_model_i->mem_resp_read_error_o (mem_resp_read_error); + hpdcache_test_mem_resp_model_i->mem_resp_read_id_o (mem_resp_read_id); + hpdcache_test_mem_resp_model_i->mem_resp_read_data_o (mem_resp_read_data); + hpdcache_test_mem_resp_model_i->mem_resp_read_last_o (mem_resp_read_last); + hpdcache_test_mem_resp_model_i->mem_req_write_ready_o (mem_req_write_ready); + hpdcache_test_mem_resp_model_i->mem_req_write_valid_i (mem_req_write_valid); + hpdcache_test_mem_resp_model_i->mem_req_write_addr_i (mem_req_write_addr); + hpdcache_test_mem_resp_model_i->mem_req_write_len_i (mem_req_write_len); + hpdcache_test_mem_resp_model_i->mem_req_write_size_i (mem_req_write_size); + hpdcache_test_mem_resp_model_i->mem_req_write_id_i (mem_req_write_id); + hpdcache_test_mem_resp_model_i->mem_req_write_command_i (mem_req_write_command); + hpdcache_test_mem_resp_model_i->mem_req_write_atomic_i (mem_req_write_atomic); + hpdcache_test_mem_resp_model_i->mem_req_write_cacheable_i (mem_req_write_cacheable); + hpdcache_test_mem_resp_model_i->mem_req_write_data_ready_o (mem_req_write_data_ready); + hpdcache_test_mem_resp_model_i->mem_req_write_data_valid_i (mem_req_write_data_valid); + hpdcache_test_mem_resp_model_i->mem_req_write_data_i (mem_req_write_data); + hpdcache_test_mem_resp_model_i->mem_req_write_be_i (mem_req_write_be); + hpdcache_test_mem_resp_model_i->mem_req_write_last_i (mem_req_write_last); + hpdcache_test_mem_resp_model_i->mem_resp_write_ready_i (mem_resp_write_ready); + hpdcache_test_mem_resp_model_i->mem_resp_write_valid_o (mem_resp_write_valid); + hpdcache_test_mem_resp_model_i->mem_resp_write_is_atomic_o (mem_resp_write_is_atomic); + hpdcache_test_mem_resp_model_i->mem_resp_write_error_o (mem_resp_write_error); + hpdcache_test_mem_resp_model_i->mem_resp_write_id_o (mem_resp_write_id); + hpdcache_test_mem_resp_model_i->sb_mem_read_req_o (sb_mem_read_req); + hpdcache_test_mem_resp_model_i->sb_mem_read_resp_o (sb_mem_read_resp); + hpdcache_test_mem_resp_model_i->sb_mem_write_req_o (sb_mem_write_req); + hpdcache_test_mem_resp_model_i->sb_mem_write_resp_o (sb_mem_write_resp); + + hpdcache_test_scoreboard_i->clk_i (clk_i); + hpdcache_test_scoreboard_i->core_req_i (sb_core_req); + hpdcache_test_scoreboard_i->core_resp_i (sb_core_resp); + hpdcache_test_scoreboard_i->mem_read_req_i (sb_mem_read_req); + hpdcache_test_scoreboard_i->mem_read_resp_i (sb_mem_read_resp); + hpdcache_test_scoreboard_i->mem_write_req_i (sb_mem_write_req); + hpdcache_test_scoreboard_i->mem_write_resp_i (sb_mem_write_resp); + hpdcache_test_scoreboard_i->evt_cache_write_miss_i (evt_cache_write_miss); + hpdcache_test_scoreboard_i->evt_cache_read_miss_i (evt_cache_read_miss); + hpdcache_test_scoreboard_i->evt_uncached_req_i (evt_uncached_req); + hpdcache_test_scoreboard_i->evt_cmo_req_i (evt_cmo_req); + hpdcache_test_scoreboard_i->evt_write_req_i (evt_write_req); + hpdcache_test_scoreboard_i->evt_read_req_i (evt_read_req); + hpdcache_test_scoreboard_i->evt_prefetch_req_i (evt_prefetch_req); + hpdcache_test_scoreboard_i->evt_req_on_hold_i (evt_req_on_hold); + hpdcache_test_scoreboard_i->evt_rtab_rollback_i (evt_rtab_rollback); + hpdcache_test_scoreboard_i->evt_stall_refill_i (evt_stall_refill); + hpdcache_test_scoreboard_i->evt_stall_i (evt_stall); + + seq->set_max_transactions(this->max_trans); + seq->set_mem_resp_model(hpdcache_test_mem_resp_model_i); + hpdcache_test_agent_i->add_sequence(seq); + hpdcache_test_scoreboard_i->set_sequence(seq); + hpdcache_test_scoreboard_i->set_mem_resp_model(hpdcache_test_mem_resp_model_i); + hpdcache_test_scoreboard_i->set_error_limit(error_limit); + } + + void simulate() + { + std::chrono::time_point start, end; + uint64_t cycles; + + std::cout << "Starting the simulation..." << std::endl; + std::cout << *seq << std::endl; + + cycles = 0; + start = std::chrono::system_clock::now(); + sc_start(sc_core::SC_ZERO_TIME); + + if (trace_on) this->trace(trace_name); + + wbuf_flush.write (false); + cfg_enable.write (true); + cfg_wbuf_threshold.write (1); + cfg_wbuf_reset_timecnt_on_write.write (true); + cfg_wbuf_sequential_waw.write (false); + cfg_wbuf_inhibit_write_coalescing.write (false); + cfg_prefetch_updt_plru.write (false); + cfg_error_on_cacheable_amo.write (false); + cfg_rtab_single_entry.write (false); + cfg_default_wb.write (false); + + Verilated::assertOn(false); + rst_ni = 1; + for (cycles = 0; cycles < 5; ++cycles) { + clk_i = 0; + sc_start(500, sc_core::SC_PS); + clk_i = 1; + sc_start(500, sc_core::SC_PS); + } + Verilated::assertOn(true); + rst_ni = 0; + for (cycles = 0; cycles < 5; ++cycles) { + clk_i = 0; + sc_start(500, sc_core::SC_PS); + clk_i = 1; + sc_start(500, sc_core::SC_PS); + } + rst_ni = 1; + for (; cycles < max_cycles; ++cycles) { + clk_i = 0; + sc_start(500, sc_core::SC_PS); + if (Verilated::gotFinish()) break; + clk_i = 1; + sc_start(500, sc_core::SC_PS); + if (Verilated::gotFinish()) break; + } + end = std::chrono::system_clock::now(); + std::cout << "Finishing the simulation..." << std::endl; + + int ms = std::chrono::duration_cast(end - start).count(); + std::cout << "Simulation wall clock time (sec): " + << std::fixed << std::setprecision(2) << (double)ms/1000 + << std::endl; + std::cout << "Simulation real frequency : " + << std::fixed << std::setprecision(2) << (double)cycles/ms << " KHz" + << std::endl; + } + + void set_sequence(std::string seq_name) + { + if (seq != nullptr) { + std::cout << "error: only one sequence supported" << std::endl; + exit(EXIT_FAILURE); + } + + if (seq_name == "random") { + seq = std::make_shared("random"); + } else if (seq_name == "read") { + seq = std::make_shared("read"); + } else if (seq_name == "write") { + seq = std::make_shared("write"); + } else if (seq_name == "unique_set") { + seq = std::make_shared("unique_set"); + } else { + std::cout << "error: sequence " << seq_name << " not found" << std::endl; + exit(EXIT_FAILURE); + } + } + + void trace(const std::string tracename) + { +#if VM_TRACE + std::cout << "Dumping waves into " << tracename << std::endl; + tf = std::make_shared(); + Verilated::traceEverOn(true); + top->trace(tf.get(), 99); // Trace 99 levels of hierarchy + tf->open(tracename.c_str()); +#endif + } + + void coverage(const std::string filename) + { + this->covname = filename; + } + + ~hpdcache_test() + { +#if VM_TRACE + if (tf != nullptr) { + tf->close(); + } +#endif +#if VM_COVERAGE + if (covname != "") { + VerilatedCov::write(covname.c_str()); + } +#endif + } + +private: + + + std::shared_ptr top; + std::shared_ptr hpdcache_test_agent_i; + std::shared_ptr hpdcache_test_mem_resp_model_i; + std::shared_ptr hpdcache_test_scoreboard_i; + + sc_core::sc_signal clk_i; + sc_core::sc_signal rst_ni; + sc_core::sc_signal wbuf_flush; + sc_core::sc_signal core_req_valid; + sc_core::sc_signal core_req_ready; + sc_core::sc_signal > core_req; + sc_core::sc_signal core_req_abort; + sc_core::sc_signal > core_req_tag; + sc_core::sc_signal > core_req_pma; + sc_core::sc_signal core_rsp_valid; + sc_core::sc_signal > core_rsp; + + sc_core::sc_signal mem_req_read_ready; + sc_core::sc_signal mem_req_read_valid; + sc_core::sc_signal > mem_req_read_addr; + sc_core::sc_signal > mem_req_read_len; + sc_core::sc_signal > mem_req_read_size; + sc_core::sc_signal > mem_req_read_id; + sc_core::sc_signal > mem_req_read_command; + sc_core::sc_signal > mem_req_read_atomic; + sc_core::sc_signal mem_req_read_cacheable; + sc_core::sc_signal mem_resp_read_ready; + sc_core::sc_signal mem_resp_read_valid; + sc_core::sc_signal > mem_resp_read_error; + sc_core::sc_signal > mem_resp_read_id; + sc_core::sc_signal > mem_resp_read_data; + sc_core::sc_signal mem_resp_read_last; + sc_core::sc_signal mem_req_write_ready; + sc_core::sc_signal mem_req_write_valid; + sc_core::sc_signal > mem_req_write_addr; + sc_core::sc_signal > mem_req_write_len; + sc_core::sc_signal > mem_req_write_size; + sc_core::sc_signal > mem_req_write_id; + sc_core::sc_signal > mem_req_write_command; + sc_core::sc_signal > mem_req_write_atomic; + sc_core::sc_signal mem_req_write_cacheable; + sc_core::sc_signal mem_req_write_data_ready; + sc_core::sc_signal mem_req_write_data_valid; + sc_core::sc_signal > mem_req_write_data; + sc_core::sc_signal > mem_req_write_be; + sc_core::sc_signal mem_req_write_last; + sc_core::sc_signal mem_resp_write_ready; + sc_core::sc_signal mem_resp_write_valid; + sc_core::sc_signal mem_resp_write_is_atomic; + sc_core::sc_signal > mem_resp_write_error; + sc_core::sc_signal > mem_resp_write_id; + + sc_core::sc_fifo sb_core_req; + sc_core::sc_fifo sb_core_resp; + sc_core::sc_fifo sb_mem_read_req; + sc_core::sc_fifo sb_mem_read_resp; + sc_core::sc_fifo sb_mem_write_req; + sc_core::sc_fifo sb_mem_write_resp; + + sc_core::sc_signal evt_cache_write_miss; + sc_core::sc_signal evt_cache_read_miss; + sc_core::sc_signal evt_uncached_req; + sc_core::sc_signal evt_cmo_req; + sc_core::sc_signal evt_write_req; + sc_core::sc_signal evt_read_req; + sc_core::sc_signal evt_prefetch_req; + sc_core::sc_signal evt_req_on_hold; + sc_core::sc_signal evt_rtab_rollback; + sc_core::sc_signal evt_stall_refill; + sc_core::sc_signal evt_stall; + + sc_core::sc_signal wbuf_empty; + + sc_core::sc_signal cfg_enable; + sc_core::sc_signal > cfg_wbuf_threshold; + sc_core::sc_signal cfg_wbuf_reset_timecnt_on_write; + sc_core::sc_signal cfg_wbuf_sequential_waw; + sc_core::sc_signal cfg_wbuf_inhibit_write_coalescing; + sc_core::sc_signal cfg_prefetch_updt_plru; + sc_core::sc_signal cfg_error_on_cacheable_amo; + sc_core::sc_signal cfg_rtab_single_entry; + sc_core::sc_signal cfg_default_wb; +}; + +void usage(const char* argv) +{ + +} + +int sc_main(int argc, char** argv) +{ + hpdcache_test test; + bool write_coverage_data; + + Verilated::commandArgs(argc, argv); + for (;;) { + int c; + int option_index; + static struct option long_options[] = { + {"help", no_argument, 0, 'h' }, + {"max-cycles", required_argument, 0, 'm' }, + {"seed", required_argument, 0, 'r' }, + {"log-level", required_argument, 0, 'l' }, + {"trace", required_argument, 0, 't' }, + {"coverage", required_argument, 0, 'c' }, + {"sequence", required_argument, 0, 's' }, + {0, 0 , 0, 0 } + }; + + option_index = 0; + c = getopt_long(argc, argv, "hm:n:r:c:l:t:s:e:", long_options, &option_index); + if (c == -1) break; + + switch (c) { + case '?': case 'h': + usage(argv[0]); + return 0; + case 'm': + test.max_cycles = atoll(optarg); + break; + case 'n': + test.max_trans = atoll(optarg); + break; + case 'r': + { + unsigned long int seed = strtol(optarg, NULL, 0); + std::cout << "info: setting random seed to " << seed << std::endl; + srand(seed); + scv_random::set_global_seed(seed); + break; + } + case 't': + test.trace_on = true; + test.trace_name = optarg; + break; + case 'c': + test.coverage(optarg); + break; + case 'l': { + const int level = atoi(optarg); + sc_core::sc_verbosity verbosity; + + switch (level) { + case 0: verbosity = sc_core::SC_NONE ; break; + case 1: verbosity = sc_core::SC_LOW ; break; + case 2: verbosity = sc_core::SC_MEDIUM; break; + case 3: verbosity = sc_core::SC_HIGH ; break; + case 4: verbosity = sc_core::SC_FULL ; break; + case 5: verbosity = sc_core::SC_DEBUG ; break; + default: + std::cout << "error: unsupported verbosity level" << std::endl; + exit(EXIT_FAILURE); + } + + std::cout << "info: setting log level to " << level << std::endl; + Logger::set_log_level(level); + sc_core::sc_report_handler::set_verbosity_level(verbosity); + break; + } + case 's': + test.set_sequence(optarg); + break; + case 'e': + test.error_limit = atoll(optarg); + break; + } + } + + test.build(); + test.simulate(); + return 0; +} diff --git a/hw/vendor/hpdcache/rtl/tb/hpdcache_test_agent.h b/hw/vendor/hpdcache/rtl/tb/hpdcache_test_agent.h new file mode 100644 index 00000000..5f4365ca --- /dev/null +++ b/hw/vendor/hpdcache/rtl/tb/hpdcache_test_agent.h @@ -0,0 +1,103 @@ +/** + * Copyright 2023,2024 CEA* + * *Commissariat a l'Energie Atomique et aux Energies Alternatives (CEA) + * Copyright 2025 Inria, Universite Grenoble-Alpes, TIMA + * + * SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 + * + * Licensed under the Solderpad Hardware License v 2.1 (the “License”); you + * may not use this file except in compliance with the License, or, at your + * option, the Apache License version 2.0. You may obtain a copy of the + * License at + * + * https://solderpad.org/licenses/SHL-2.1/ + * + * Unless required by applicable law or agreed to in writing, any work + * distributed under the License is distributed on an “AS IS” BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +/** + * Author : Cesar Fuguet + * Date : September, 2024 + * Description: Class definition of the hpdcache_test_agent + */ +#ifndef __HPDCACHE_TEST_AGENT_H__ +#define __HPDCACHE_TEST_AGENT_H__ + +#include +#include +#include +#include +#include + +#include "agent.h" +#include "sequence.h" +#include "hpdcache_test_driver.h" +#include "hpdcache_test_defs.h" +#include "logger.h" + +class hpdcache_test_agent : public Agent +{ +public: + sc_out core_req_valid_o; + sc_in core_req_ready_i; + sc_out > core_req_o; + sc_out > core_req_tag_o; + sc_out > core_req_pma_o; + sc_out core_req_abort_o; + + sc_in core_rsp_valid_i; + sc_in > core_rsp_i; + + sc_fifo_out sb_core_req_o; + sc_fifo_out sb_core_resp_o; + + hpdcache_test_agent(sc_core::sc_module_name nm) + : Agent(nm) + , core_req_valid_o("core_req_valid_o") + , core_req_ready_i("core_req_ready_i") + , core_req_o("core_req_o") + , core_req_tag_o("core_req_tag_o") + , core_req_pma_o("core_req_pma_o") + , core_req_abort_o("core_req_abort_o") + , core_rsp_valid_i("core_rsp_valid_i") + , core_rsp_i("core_rsp_i") + , sb_core_req_o("sb_core_req_o") + , sb_core_resp_o("sb_core_resp_o") + { + std::string driver_name; + + driver_name = std::string(nm) + "_driver"; + + driver = std::make_shared(driver_name.c_str()); + driver->clk_i (Agent::clk_i); + driver->rst_ni (Agent::rst_ni); + + driver->core_req_valid_o (core_req_valid_o); + driver->core_req_ready_i (core_req_ready_i); + driver->core_req_o (core_req_o); + driver->core_req_tag_o (core_req_tag_o); + driver->core_req_pma_o (core_req_pma_o); + driver->core_req_abort_o (core_req_abort_o); + + driver->core_rsp_valid_i (core_rsp_valid_i); + driver->core_rsp_i (core_rsp_i); + + driver->sb_core_req_o (sb_core_req_o); + driver->sb_core_resp_o (sb_core_resp_o); + + driver->transaction_fifo_i (Agent::transaction_fifo); + driver->transaction_ret_o (Agent::transaction_ret); + } + + ~hpdcache_test_agent() + { + } + +private: + std::shared_ptr driver; +}; + +#endif /* __HPDCACHE_TEST_AGENT_H__ */ diff --git a/hw/vendor/hpdcache/rtl/tb/hpdcache_test_amo.h b/hw/vendor/hpdcache/rtl/tb/hpdcache_test_amo.h new file mode 100644 index 00000000..38844380 --- /dev/null +++ b/hw/vendor/hpdcache/rtl/tb/hpdcache_test_amo.h @@ -0,0 +1,90 @@ +/** + * Copyright 2023,2024 CEA* + * *Commissariat a l'Energie Atomique et aux Energies Alternatives (CEA) + * + * SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 + * + * Licensed under the Solderpad Hardware License v 2.1 (the “License”); you + * may not use this file except in compliance with the License, or, at your + * option, the Apache License version 2.0. You may obtain a copy of the + * License at + * + * https://solderpad.org/licenses/SHL-2.1/ + * + * Unless required by applicable law or agreed to in writing, any work + * distributed under the License is distributed on an “AS IS” BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +/** + * Author : Cesar Fuguet + * Date : October, 2024 + * Description: Atomic Memory Operations (AMO) Utility Class + */ +#ifndef __HPDCACHE_TEST_AMO_H__ +#define __HPDCACHE_TEST_AMO_H__ + +#include + +class hpdcache_test_transaction_req; + +class hpdcache_test_amo +{ +protected: + + hpdcache_test_amo() + { + /* empty */ + } + + ~hpdcache_test_amo() + { + /* empty */ + } + +public: + + static uint64_t compute_amo( + unsigned atop, + uint64_t ld_data, + uint64_t st_data, + unsigned bytes) + { + bool umax = (ld_data > st_data); + bool smax; + if (bytes == 4) { + smax = ((int64_t)((int32_t)ld_data) > (int64_t)((int32_t)st_data)); + } else { + smax = (((int64_t)ld_data) > ((int64_t)st_data)); + } + + uint64_t result; + switch (atop) { + case hpdcache_test_transaction_req::HPDCACHE_REQ_AMO_SWAP: + result = st_data; break; + case hpdcache_test_transaction_req::HPDCACHE_REQ_AMO_ADD: + result = ld_data + st_data; break; + case hpdcache_test_transaction_req::HPDCACHE_REQ_AMO_AND: + result = ld_data & st_data; break; + case hpdcache_test_transaction_req::HPDCACHE_REQ_AMO_OR: + result = ld_data | st_data; break; + case hpdcache_test_transaction_req::HPDCACHE_REQ_AMO_XOR: + result = ld_data ^ st_data; break; + case hpdcache_test_transaction_req::HPDCACHE_REQ_AMO_MAX: + result = smax ? ld_data : st_data; break; + case hpdcache_test_transaction_req::HPDCACHE_REQ_AMO_MAXU: + result = umax ? ld_data : st_data; break; + case hpdcache_test_transaction_req::HPDCACHE_REQ_AMO_MIN: + result = smax ? st_data : ld_data; break; + case hpdcache_test_transaction_req::HPDCACHE_REQ_AMO_MINU: + result = umax ? st_data : ld_data; break; + default: + sc_assert(false && "unknown atomic operation"); return 0; + } + + return result; + } +}; + +#endif /* __HPDCACHE_TEST_AMO_H__ */ diff --git a/hw/vendor/hpdcache/rtl/tb/hpdcache_test_defs.h b/hw/vendor/hpdcache/rtl/tb/hpdcache_test_defs.h new file mode 100644 index 00000000..470b6ff1 --- /dev/null +++ b/hw/vendor/hpdcache/rtl/tb/hpdcache_test_defs.h @@ -0,0 +1,192 @@ +/** + * Copyright 2023,2024 CEA* + * *Commissariat a l'Energie Atomique et aux Energies Alternatives (CEA) + * Copyright 2025 Inria, Universite Grenoble-Alpes, TIMA + * + * SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 + * + * Licensed under the Solderpad Hardware License v 2.1 (the “License”); you + * may not use this file except in compliance with the License, or, at your + * option, the Apache License version 2.0. You may obtain a copy of the + * License at + * + * https://solderpad.org/licenses/SHL-2.1/ + * + * Unless required by applicable law or agreed to in writing, any work + * distributed under the License is distributed on an “AS IS” BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +/** + * Author : Cesar Fuguet + * Date : October, 2024 + * Description: HPDCACHE test global definitions + */ +#ifndef __HPDCACHE_TEST_DEFS_H__ +#define __HPDCACHE_TEST_DEFS_H__ + +#include + +#define HPDCACHE_TEST_DEFS_LOG2(x) \ + (x <= 1 ? 0 : \ + x <= 2 ? 1 : \ + x <= 4 ? 2 : \ + x <= 8 ? 3 : \ + x <= 16 ? 4 : \ + x <= 32 ? 5 : \ + x <= 64 ? 6 : \ + x <= 128 ? 7 : \ + x <= 256 ? 8 : \ + x <= 512 ? 9 : \ + x <= 1024 ? 10 : \ + x <= 2048 ? 11 : \ + x <= 4096 ? 12 : \ + x <= 8192 ? 13 : \ + x <= 16384 ? 14 : 0) + +#define SCOREBOARD_RAM_SIZE (16 << 20) // Bytes + +#ifndef CONF_HPDCACHE_WAYS +#define HPDCACHE_WAYS 8 +#else +#define HPDCACHE_WAYS (CONF_HPDCACHE_WAYS) +#endif + +#ifndef CONF_HPDCACHE_SETS +#define HPDCACHE_SETS 64 +#else +#define HPDCACHE_SETS (CONF_HPDCACHE_SETS) +#endif + +#ifndef CONF_HPDCACHE_CL_WORDS +#define HPDCACHE_CL_WORDS 8 +#else +#define HPDCACHE_CL_WORDS (CONF_HPDCACHE_CL_WORDS) +#endif + +#ifndef CONF_HPDCACHE_WORD_WIDTH +#define HPDCACHE_WORD_WIDTH 64 +#else +#define HPDCACHE_WORD_WIDTH (CONF_HPDCACHE_WORD_WIDTH) +#endif + +#ifndef CONF_HPDCACHE_WORD_USER_WIDTH +#define HPDCACHE_WORD_USER_WIDTH 1 +#else +#define HPDCACHE_WORD_USER_WIDTH (CONF_HPDCACHE_WORD_WIDTH) +#endif + +#ifndef CONF_HPDCACHE_MSHR_SETS +#define HPDCACHE_MSHR_SETS 1 +#else +#define HPDCACHE_MSHR_SETS (CONF_HPDCACHE_MSHR_SETS) +#endif + +#ifndef CONF_HPDCACHE_MSHR_WAYS +#define HPDCACHE_MSHR_WAYS 8 +#else +#define HPDCACHE_MSHR_WAYS (CONF_HPDCACHE_MSHR_WAYS) +#endif + +#ifndef CONF_HPDCACHE_PA_WIDTH +#define HPDCACHE_PA_WIDTH 49 +#else +#define HPDCACHE_PA_WIDTH (CONF_HPDCACHE_PA_WIDTH) +#endif + +#ifndef CONF_HPDCACHE_REQ_WORDS +#define HPDCACHE_REQ_WORDS 1 +#else +#define HPDCACHE_REQ_WORDS (CONF_HPDCACHE_REQ_WORDS) +#endif + +#define HPDCACHE_REQ_DATA_WIDTH ((HPDCACHE_REQ_WORDS)*(HPDCACHE_WORD_WIDTH)) + +#ifndef CONF_HPDCACHE_MEM_ADDR_WIDTH +#define HPDCACHE_MEM_ADDR_WIDTH 64 +#else +#define HPDCACHE_MEM_ADDR_WIDTH (CONF_HPDCACHE_MEM_ADDR_WIDTH) +#endif + +#ifndef CONF_HPDCACHE_MEM_DATA_WIDTH +#define HPDCACHE_MEM_DATA_WIDTH 512 +#else +#define HPDCACHE_MEM_DATA_WIDTH (CONF_HPDCACHE_MEM_DATA_WIDTH) +#endif + +#ifndef CONF_HPDCACHE_MEM_ID_WIDTH +#define HPDCACHE_MEM_ID_WIDTH 4 +#else +#define HPDCACHE_MEM_ID_WIDTH (CONF_HPDCACHE_MEM_ID_WIDTH) +#endif + +#ifndef CONF_HPDCACHE_WBUF_WORDS +#define HPDCACHE_WBUF_WORDS 2 +#else +#define HPDCACHE_WBUF_WORDS (CONF_HPDCACHE_WBUF_WORDS) +#endif + +#ifndef CONF_HPDCACHE_WBUF_DIR_ENTRIES +#define HPDCACHE_WBUF_DIR_ENTRIES 8 +#else +#define HPDCACHE_WBUF_DIR_ENTRIES (CONF_HPDCACHE_WBUF_DIR_ENTRIES) +#endif + +#ifndef CONF_HPDCACHE_WBUF_DATA_ENTRIES +#define HPDCACHE_WBUF_DATA_ENTRIES 4 +#else +#define HPDCACHE_WBUF_DATA_ENTRIES (CONF_HPDCACHE_WBUF_DATA_ENTRIES) +#endif + +#ifndef CONF_HPDCACHE_REQ_SRC_ID_WIDTH +#define HPDCACHE_REQ_SRC_ID_WIDTH 3 +#else +#define HPDCACHE_REQ_SRC_ID_WIDTH (CONF_HPDCACHE_REQ_SRC_ID_WIDTH) +#endif + +#ifndef CONF_HPDCACHE_REQ_TRANS_ID_WIDTH +#define HPDCACHE_REQ_TRANS_ID_WIDTH 6 +#else +#define HPDCACHE_REQ_TRANS_ID_WIDTH (CONF_HPDCACHE_REQ_TRANS_ID_WIDTH) +#endif + +#define NREQUESTERS 8 + +#define HPDCACHE_SET_WIDTH HPDCACHE_TEST_DEFS_LOG2(HPDCACHE_SETS) +#define HPDCACHE_CL_OFFSET_WIDTH HPDCACHE_TEST_DEFS_LOG2(((HPDCACHE_CL_WORDS)*(HPDCACHE_WORD_WIDTH))/8) +#define HPDCACHE_NLINE_WIDTH ((HPDCACHE_PA_WIDTH) - (HPDCACHE_CL_OFFSET_WIDTH)) +#define HPDCACHE_TAG_WIDTH ((HPDCACHE_NLINE_WIDTH) - (HPDCACHE_SET_WIDTH)) +#define HPDCACHE_ADDR_OFFSET_WIDTH ((HPDCACHE_PA_WIDTH) - (HPDCACHE_TAG_WIDTH)) + +#define HPDCACHE_REQ_OP_WIDTH 5 +#define HPDCACHE_REQ_SIZE_WIDTH 3 +#define HPDCACHE_REQ_PMA_WIDTH 5 +#define HPDCACHE_REQ_NEED_RSP_WIDTH 1 +#define HPDCACHE_REQ_PHYS_INDEXED_WIDTH 1 +#define HPDCACHE_REQ_USER_WIDTH 1 +#define HPDCACHE_RSP_ERROR_WIDTH 1 +#define HPDCACHE_RSP_ABORTED_WIDTH 1 +#define HPDCACHE_RSP_USER_WIDTH 1 + +#define HPDCACHE_CORE_REQ_WIDTH ((HPDCACHE_ADDR_OFFSET_WIDTH) + \ + (HPDCACHE_REQ_DATA_WIDTH) + \ + (HPDCACHE_REQ_OP_WIDTH) + \ + ((HPDCACHE_REQ_DATA_WIDTH)/8) + \ + (HPDCACHE_REQ_SIZE_WIDTH) + \ + (HPDCACHE_REQ_SRC_ID_WIDTH) + \ + (HPDCACHE_REQ_TRANS_ID_WIDTH) + \ + (HPDCACHE_REQ_NEED_RSP_WIDTH) + \ + (HPDCACHE_REQ_PHYS_INDEXED_WIDTH) + \ + (HPDCACHE_TAG_WIDTH) + \ + (HPDCACHE_REQ_PMA_WIDTH) + \ + (HPDCACHE_REQ_USER_WIDTH)) + +#define HPDCACHE_CORE_RSP_WIDTH ((HPDCACHE_REQ_DATA_WIDTH) + \ + (HPDCACHE_REQ_SRC_ID_WIDTH) + \ + (HPDCACHE_REQ_TRANS_ID_WIDTH) + \ + (HPDCACHE_RSP_ERROR_WIDTH) + \ + (HPDCACHE_RSP_ABORTED_WIDTH) + \ + (HPDCACHE_RSP_USER_WIDTH)) + +#endif // __HPDCACHE_TEST_DEFS_H__ diff --git a/hw/vendor/hpdcache/rtl/tb/hpdcache_test_driver.h b/hw/vendor/hpdcache/rtl/tb/hpdcache_test_driver.h new file mode 100644 index 00000000..f990f988 --- /dev/null +++ b/hw/vendor/hpdcache/rtl/tb/hpdcache_test_driver.h @@ -0,0 +1,213 @@ +/** + * Copyright 2023,2024 CEA* + * *Commissariat a l'Energie Atomique et aux Energies Alternatives (CEA) + * Copyright 2025 Inria, Universite Grenoble-Alpes, TIMA + * + * SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 + * + * Licensed under the Solderpad Hardware License v 2.1 (the “License”); you + * may not use this file except in compliance with the License, or, at your + * option, the Apache License version 2.0. You may obtain a copy of the + * License at + * + * https://solderpad.org/licenses/SHL-2.1/ + * + * Unless required by applicable law or agreed to in writing, any work + * distributed under the License is distributed on an “AS IS” BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +/** + * Author : Cesar Fuguet + * Date : October, 2024 + * Description: class definition of the HPDCACHE test driver + */ +#ifndef __HPDCACHE_TEST_DRIVER_H__ +#define __HPDCACHE_TEST_DRIVER_H__ + +#include +#include +#include +#include "logger.h" +#include "driver.h" +#include "hpdcache_test_transaction.h" +#include "hpdcache_test_defs.h" + +using namespace sc_core; +using namespace sc_dt; + +class hpdcache_test_driver : public Driver +{ +public: + sc_out core_req_valid_o; + sc_in core_req_ready_i; + sc_out > core_req_o; + sc_out core_req_abort_o; + sc_out > core_req_tag_o; + sc_out > core_req_pma_o; + + sc_in core_rsp_valid_i; + sc_in > core_rsp_i; + + sc_fifo_out sb_core_req_o; + sc_fifo_out sb_core_resp_o; + + hpdcache_test_driver(sc_core::sc_module_name nm) : Driver(nm) + { + SC_THREAD(drive_request); + sensitive << clk_i.pos(); + + SC_THREAD(monitor_response); + sensitive << clk_i.neg(); + }; + +private: + +#if SC_VERSION_MAJOR < 3 + SC_HAS_PROCESS(hpdcache_test_driver); +#endif + + typedef std::shared_ptr transaction_ptr; + + static inline uint64_t core_get_req_tag(const transaction_ptr &t) + { + sc_bv ret; + ret = t->req_addr.range(HPDCACHE_PA_WIDTH - 1, HPDCACHE_ADDR_OFFSET_WIDTH); + return ret.to_uint64(); + } + + static inline uint32_t core_get_req_pma(const transaction_ptr &t) + { + int ret = 0; + if (t->is_wr_policy_auto()) ret |= (0x1 << 0); + if (t->is_wr_policy_wb()) ret |= (0x1 << 1); + if (t->is_wr_policy_wt()) ret |= (0x1 << 2); + if (t->req_io) ret |= (0x1 << 3); + if (t->req_uncacheable) ret |= (0x1 << 4); + return ret; + } + + static sc_bv core_req_to_bv(const transaction_ptr &t) + { + const int unsigned USER_POS = 0; + const int unsigned PMA_POS = USER_POS + HPDCACHE_REQ_USER_WIDTH; + const int unsigned TAG_POS = PMA_POS + HPDCACHE_REQ_PMA_WIDTH; + const int unsigned PHYS_INDEXED_POS = TAG_POS + HPDCACHE_TAG_WIDTH; + const int unsigned NEED_RSP_POS = PHYS_INDEXED_POS + HPDCACHE_REQ_PHYS_INDEXED_WIDTH; + const int unsigned TRANS_ID_POS = NEED_RSP_POS + HPDCACHE_REQ_NEED_RSP_WIDTH; + const int unsigned SRC_ID_POS = TRANS_ID_POS + HPDCACHE_REQ_TRANS_ID_WIDTH; + const int unsigned SIZE_POS = SRC_ID_POS + HPDCACHE_REQ_SRC_ID_WIDTH; + const int unsigned BE_POS = SIZE_POS + HPDCACHE_REQ_SIZE_WIDTH; + const int unsigned OP_POS = BE_POS + (HPDCACHE_REQ_DATA_WIDTH/8); + const int unsigned WDATA_POS = OP_POS + HPDCACHE_REQ_OP_WIDTH; + const int unsigned ADDR_POS = WDATA_POS + HPDCACHE_REQ_DATA_WIDTH; + sc_bv ret; + + ret.range(USER_POS + HPDCACHE_REQ_USER_WIDTH - 1, USER_POS) = + 0; + ret.range(PMA_POS + HPDCACHE_REQ_PMA_WIDTH - 1, PMA_POS) = + core_get_req_pma(t); + ret.range(TAG_POS + HPDCACHE_TAG_WIDTH - 1, TAG_POS) = + core_get_req_tag(t); + ret.range(PHYS_INDEXED_POS + HPDCACHE_REQ_PHYS_INDEXED_WIDTH - 1, PHYS_INDEXED_POS) = + t->req_phys_indexed ? 1 : 0; + ret.range(NEED_RSP_POS + HPDCACHE_REQ_NEED_RSP_WIDTH - 1, NEED_RSP_POS) = + t->req_need_rsp ? 1 : 0; + ret.range(TRANS_ID_POS + HPDCACHE_REQ_TRANS_ID_WIDTH - 1, TRANS_ID_POS) = + t->req_tid; + ret.range(SRC_ID_POS + HPDCACHE_REQ_SRC_ID_WIDTH - 1, SRC_ID_POS) = + t->req_sid; + ret.range(SIZE_POS + HPDCACHE_REQ_SIZE_WIDTH - 1, SIZE_POS) = + t->req_size; + ret.range(BE_POS + (HPDCACHE_REQ_DATA_WIDTH/8) - 1, BE_POS) = + t->req_be; + ret.range(OP_POS + HPDCACHE_REQ_OP_WIDTH - 1, OP_POS) = + t->req_op; + ret.range(WDATA_POS + HPDCACHE_REQ_DATA_WIDTH - 1, WDATA_POS) = + t->req_wdata; + ret.range(ADDR_POS + HPDCACHE_ADDR_OFFSET_WIDTH - 1, ADDR_POS) = + t->req_addr.range(HPDCACHE_ADDR_OFFSET_WIDTH, 0); + + return ret; + } + + static void core_resp_to_struct(hpdcache_test_transaction_resp &resp, const sc_bv &bv) + { + const int unsigned USER_POS = 0; + const int unsigned ABORTED_POS = USER_POS + HPDCACHE_RSP_USER_WIDTH; + const int unsigned ERROR_POS = ABORTED_POS + HPDCACHE_RSP_ABORTED_WIDTH; + const int unsigned TRANS_ID_POS = ERROR_POS + HPDCACHE_RSP_ERROR_WIDTH; + const int unsigned SRC_ID_POS = TRANS_ID_POS + HPDCACHE_REQ_TRANS_ID_WIDTH; + const int unsigned RDATA_POS = SRC_ID_POS + HPDCACHE_REQ_SRC_ID_WIDTH; + + // user field not currently passed to testbench + resp.rsp_aborted = (bv.range(ABORTED_POS + HPDCACHE_RSP_ABORTED_WIDTH - 1, ABORTED_POS).to_uint() > 0); + resp.rsp_error = (bv.range(ERROR_POS + HPDCACHE_RSP_ERROR_WIDTH - 1, ERROR_POS).to_uint() > 0); + resp.rsp_tid = bv.range(TRANS_ID_POS + HPDCACHE_REQ_TRANS_ID_WIDTH - 1, TRANS_ID_POS); + resp.rsp_sid = bv.range(SRC_ID_POS + HPDCACHE_REQ_SRC_ID_WIDTH - 1, SRC_ID_POS); + resp.rsp_rdata = bv.range(RDATA_POS + HPDCACHE_REQ_DATA_WIDTH - 1, RDATA_POS); + } + + + void drive_request() + { + transaction_ptr t; + for (;;) { + t = std::dynamic_pointer_cast( + transaction_fifo_i.read()); + + if (t == nullptr) break; + + core_req_valid_o.write(true); + core_req_o.write(core_req_to_bv(t)); + do wait(); while (!core_req_ready_i.read()); + core_req_valid_o.write(false); + + // send the tag + core_req_tag_o.write(core_get_req_tag(t)); + core_req_pma_o.write(core_get_req_pma(t)); + core_req_abort_o.write(t->is_aborted()); + + transaction_ret_o.write(t->get_id()); + + // send request to the scoreboard + sb_core_req_o.write(*t); + } + + // FIXME : I should find a better way to know when all transactions + // have been completed. Otherwise, finish the simulation in a different + // place (e.g. the scoreboard that knows all pending transactions) + for (int i = 0; i < 10000; i++) { + wait(); + } + + Verilated::gotFinish(true); + } + + void drive_tag() + { + for (;;) { + + wait(); + + } + } + + void monitor_response() + { + for (;;) { + wait(); + + if (core_rsp_valid_i.read()) { + hpdcache_test_transaction_resp resp; + core_resp_to_struct(resp, core_rsp_i.read()); + + // send response to the scoreboard + sb_core_resp_o.write(resp); + } + } + } +}; + +#endif // __HPDCACHE_TEST_DRIVER_H__ diff --git a/hw/vendor/hpdcache/rtl/tb/hpdcache_test_mem_resp_model.h b/hw/vendor/hpdcache/rtl/tb/hpdcache_test_mem_resp_model.h new file mode 100644 index 00000000..52e2f2f8 --- /dev/null +++ b/hw/vendor/hpdcache/rtl/tb/hpdcache_test_mem_resp_model.h @@ -0,0 +1,471 @@ +/** + * Copyright 2023,2024 CEA* + * *Commissariat a l'Energie Atomique et aux Energies Alternatives (CEA) + * Copyright 2025 Inria, Universite Grenoble-Alpes, TIMA + * + * SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 + * + * Licensed under the Solderpad Hardware License v 2.1 (the “License”); you + * may not use this file except in compliance with the License, or, at your + * option, the Apache License version 2.0. You may obtain a copy of the + * License at + * + * https://solderpad.org/licenses/SHL-2.1/ + * + * Unless required by applicable law or agreed to in writing, any work + * distributed under the License is distributed on an “AS IS” BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +/** + * Author : Cesar Fuguet + * Date : October, 2024 + * Description: Memory model for the HPDCACHE testbench + */ +#ifndef __HPDCACHE_TEST_MEM_RESP_MODEL_H__ +#define __HPDCACHE_TEST_MEM_RESP_MODEL_H__ + +#include +#include +#include +#include +#include "hpdcache_test_defs.h" +#include "hpdcache_test_mem_resp_model_base.h" +#include "mem_model.h" +#include "logger.h" + +#define DEBUG_HPDCACHE_TEST_MEM_RESP_MODEL 1 + +class hpdcache_test_mem_resp_model : public sc_module, public hpdcache_test_mem_resp_model_base +{ +public: + + sc_in clk_i; + sc_in rst_ni; + + sc_out mem_req_read_ready_o; + sc_in mem_req_read_valid_i; + sc_in > mem_req_read_addr_i; + sc_in > mem_req_read_len_i; + sc_in > mem_req_read_size_i; + sc_in > mem_req_read_id_i; + sc_in > mem_req_read_command_i; + sc_in > mem_req_read_atomic_i; + sc_in mem_req_read_cacheable_i; + + sc_in mem_resp_read_ready_i; + sc_out mem_resp_read_valid_o; + sc_out > mem_resp_read_error_o; + sc_out > mem_resp_read_id_o; + sc_out > mem_resp_read_data_o; + sc_out mem_resp_read_last_o; + + sc_out mem_req_write_ready_o; + sc_in mem_req_write_valid_i; + sc_in > mem_req_write_addr_i; + sc_in > mem_req_write_len_i; + sc_in > mem_req_write_size_i; + sc_in > mem_req_write_id_i; + sc_in > mem_req_write_command_i; + sc_in > mem_req_write_atomic_i; + sc_in mem_req_write_cacheable_i; + + sc_out mem_req_write_data_ready_o; + sc_in mem_req_write_data_valid_i; + sc_in > mem_req_write_data_i; + sc_in > mem_req_write_be_i; + sc_in mem_req_write_last_i; + + sc_in mem_resp_write_ready_i; + sc_out mem_resp_write_valid_o; + sc_out mem_resp_write_is_atomic_o; + sc_out > mem_resp_write_error_o; + sc_out > mem_resp_write_id_o; + + sc_fifo_out sb_mem_read_req_o; + sc_fifo_out sb_mem_read_resp_o; + sc_fifo_out sb_mem_write_req_o; + sc_fifo_out sb_mem_write_resp_o; + +private: + +#if SC_VERSION_MAJOR < 3 + SC_HAS_PROCESS(hpdcache_test_mem_resp_model); +#endif + +public: + + hpdcache_test_mem_resp_model(sc_module_name nm) : + sc_module(nm), + hpdcache_test_mem_resp_model_base(std::string(nm)) + { + SC_THREAD(read_process); + sensitive << clk_i.neg(); + + SC_THREAD(read_response_process); + sensitive << clk_i.pos(); + + SC_THREAD(write_address_process); + sensitive << clk_i.neg(); + + SC_THREAD(write_data_process); + sensitive << clk_i.neg(); + + SC_THREAD(write_process); + sensitive << clk_i.pos(); + + SC_THREAD(write_response_process); + sensitive << clk_i.pos(); + } + +private: + + void readOperation() + { + hpdcache_test_transaction_mem_read_req req; + hpdcache_test_transaction_mem_read_resp resp; + + // consume the request from the request ports + req.addr = mem_req_read_addr_i.read().to_uint(); + req.len = mem_req_read_len_i.read().to_uint(); + req.size = mem_req_read_size_i.read().to_uint(); + req.id = mem_req_read_id_i.read().to_uint(); + req.command = mem_req_read_command_i.read().to_uint(); + req.atomic = mem_req_read_atomic_i.read().to_uint(); + req.cacheable = mem_req_read_cacheable_i.read(); + sb_mem_read_req_o.write(req); // send request to scoreboard + + ra_ready_delay->next(); + for (int i = 0; i < ra_ready_delay->read(); i++) wait(); + + mem_req_read_ready_o.write(true); + wait(); + mem_req_read_ready_o.write(false); + + // check if the address is in an error segment. If it is, send a + // response with the error flag asserted + uint64_t addr = req.addr; + uint64_t end_addr = addr + (1ULL << req.size); + if (within_error_region(addr, end_addr)) { + for (int i = 0; i < (req.len + 1); i++) { + resp.error = 1; + resp.id = req.id; + resp.last = (i == req.len); + while (!read_resp_fifo.nb_write(resp)) wait(); + } + return; + } + + // do the read operation on the memory array + size_t words = (1 << req.size)/8; + if (words == 0) words = 1; + + if (req.is_ldex()) { + const uint64_t n = 1 << req.size; + excl_buf_m[req.id].valid = true; + excl_buf_m[req.id].base_addr = addr; + excl_buf_m[req.id].end_addr = addr + n; + } + + for (int i = 0; i < (req.len + 1); i++) { + for (int w = 0; w < words; w++) { + uint64_t word_addr = (addr >> 3) + w; + uint64_t ld_data = memory_m->readMemory(word_addr); + uint64_t r = word_addr % MEM_NOC_DATA_WORDS; + resp.data.range((r + 1)*64 - 1, r*64) = ld_data; + +#if DEBUG_HPDCACHE_TEST_MEM_RESP_MODEL + if (check_verbosity(sc_core::SC_DEBUG)) { + std::cout << sc_time_stamp().to_string() + << " / MEM_RESP_MODEL_DEBUG: reading memory" + << " / address = 0x" << std::hex << word_addr*8 << std::dec + << " / load data = 0x" << std::hex << ld_data << std::dec + << std::endl; + } +#endif + } + + // send response + resp.error = 0; + resp.id = req.id; + resp.last = (i == req.len); + while (!read_resp_fifo.nb_write(resp)) wait(); + + addr = ((addr >> 3) + words) << 3; + } + } + + void writeOperation(hpdcache_test_transaction_mem_write_req req) + { + hpdcache_test_transaction_mem_write_resp resp; + + unsigned int command = req.command; + unsigned bytes = (1ULL << req.size); + uint64_t addr = req.addr; + uint64_t end_addr = addr + bytes; + uint64_t word_addr = addr >> 3; + bool excl_ok = false; + + unsigned int atop = req.atomic; + bool is_amo = ((command == hpdcache_mem_command_e::HPDCACHE_MEM_ATOMIC) && + (atop != hpdcache_mem_atomic_e::HPDCACHE_MEM_ATOMIC_STEX)); + + // check if the address is in an error segment. If it is, send a + // response with the error flag asserted + if (within_error_region(addr, end_addr)) { + if (is_amo) { + hpdcache_test_transaction_mem_read_resp read_resp; + read_resp.data = 0; + read_resp.error = 0; + read_resp.id = req.id; + read_resp.last = true; + while (!read_resp_fifo.nb_write(read_resp)) wait(); + } + + resp.is_atomic = 0; + resp.error = 1; + resp.id = req.id; + while (!write_resp_fifo.nb_write(resp)) wait(); + return; + } + + if (req.is_stex()) { + excl_reservation_buf_t& e = excl_buf_m[req.id]; + if (e.valid) { + e.valid = false; + excl_ok = within_region(addr, end_addr, e.base_addr, e.end_addr); + } + } + + // compute the AMO result + uint64_t word = word_addr % MEM_NOC_DATA_WORDS; + uint64_t ld_data; + uint64_t st_data; + uint64_t amo_result; + + if (is_amo) { + unsigned offset = (addr % 8) * 8; + + ld_data = memory_m->readMemory(word_addr); + st_data = req.data.range((word + 1)*64 - 1, word*64).to_uint64(); + if (bytes == 4) { + ld_data = static_cast(ld_data >> offset); + st_data = static_cast(st_data >> offset); + } + amo_result = compute_amo(static_cast(atop), ld_data, st_data, bytes); + if (offset) { + ld_data <<= offset; + st_data <<= offset; + amo_result <<= offset; + } + +#if DEBUG_HPDCACHE_TEST_MEM_RESP_MODEL + if (check_verbosity(sc_core::SC_DEBUG)) { + std::cout << sc_time_stamp().to_string() + << " / MEM_RESP_MODEL_DEBUG: computing amo word" + << " / load data = 0x" << std::hex << ld_data << std::dec + << " / store data = 0x" << std::hex << st_data << std::dec + << " / amo result = 0x" << std::hex << amo_result << std::dec + << std::endl; + } +#endif + } + + // compute the number of words to write + if (!req.is_stex() || excl_ok) { + size_t words = bytes/8; + if (words == 0) words = 1; + + // do the write operation on the memory array + for (int w = 0; w < words; w++) { + unsigned int i = word + w; + uint8_t be = req.be.range((i + 1)*8 - 1, i*8).to_uint(); + + // skip the write operation if the byte enable is all 0 + if (be == 0) continue; + + st_data = is_amo ? amo_result : req.data.range((i + 1)*64 - 1, i*64).to_uint64(); + memory_m->writeMemory(word_addr + w, st_data, mem_model::beToMask(be)); + +#if DEBUG_HPDCACHE_TEST_MEM_RESP_MODEL + if (check_verbosity(sc_core::SC_DEBUG)) { + std::cout << sc_time_stamp().to_string() + << " / MEM_RESP_MODEL_DEBUG: writing memory" + << " / address = 0x" << std::hex << ((word_addr + w)*8) << std::dec + << " / store data = 0x" << std::hex << st_data << std::dec + << " / store be = 0x" << std::hex << (uint32_t)be << std::dec + << std::endl; + } +#endif + } + + // send the old data for AMO on the read response channel + if (is_amo) { + hpdcache_test_transaction_mem_read_resp read_resp; + read_resp.data = 0; + read_resp.data.range((word + 1)*64 - 1, word*64) = ld_data; + read_resp.error = 0; + read_resp.id = req.id; + read_resp.last = true; + while (!read_resp_fifo.nb_write(read_resp)) wait(); + } + } + + // send the write acknowledge on the write response channel + resp.is_atomic = req.is_stex() && excl_ok; + resp.error = 0; + resp.id = req.id; + while (!write_resp_fifo.nb_write(resp)) wait(); + } + + void read_response_process() + { + hpdcache_test_transaction_mem_read_resp read_resp; + + mem_resp_read_valid_o.write(false); + for (;;) { + while (!read_resp_fifo.nb_read(read_resp)) wait(); + rd_valid_delay->next(); + for (int i = 0; i < rd_valid_delay->read(); i++) wait(); + sb_mem_read_resp_o.write(read_resp); // send response to scoreboard + mem_resp_read_valid_o.write(true); + mem_resp_read_error_o.write(read_resp.error); + mem_resp_read_id_o.write(read_resp.id); + mem_resp_read_data_o.write(read_resp.data); + mem_resp_read_last_o.write(read_resp.last); + do wait(); while (!mem_resp_read_ready_i.read()); + mem_resp_read_valid_o.write(false); + } + } + + void write_response_process() + { + hpdcache_test_transaction_mem_write_resp resp; + + mem_resp_write_valid_o.write(false); + for (;;) { + while (!write_resp_fifo.nb_read(resp)) wait(); + wb_valid_delay->next(); + for (int i = 0; i < wb_valid_delay->read(); i++) wait(); + sb_mem_write_resp_o.write(resp); // send response to scoreboard + mem_resp_write_valid_o.write(true); + mem_resp_write_is_atomic_o.write(resp.is_atomic); + mem_resp_write_error_o.write(resp.error); + mem_resp_write_id_o.write(resp.id); + do wait(); while (!mem_resp_write_ready_i.read()); + mem_resp_write_valid_o.write(false); + } + } + + void read_process() + { + mem_req_read_ready_o.write(false); + for (;;) { + if (mem_req_read_valid_i.read()) { + readOperation(); + } else { + wait(); + } + } + } + + void write_address_process() + { + // + // This process consumes beats on the Write Channel + // + mem_write_req_flit_t r; + for (;;) { + mem_req_write_ready_o.write(false); + + // Wait for a write request + while (!mem_req_write_valid_i.read()) wait(); + + // Wait for a random delay before setting the ready signal + wa_ready_delay->next(); + for (int i = 0; i < wa_ready_delay->read(); ++i) wait(); + + // Set the ready signal + mem_req_write_ready_o.write(true); + + // Forward the request to the write process + r.addr = mem_req_write_addr_i.read().to_uint(); + r.len = mem_req_write_len_i.read().to_uint(); + r.size = mem_req_write_size_i.read().to_uint(); + r.id = mem_req_write_id_i.read().to_uint(); + r.command = mem_req_write_command_i.read().to_uint(); + r.atomic = mem_req_write_atomic_i.read().to_uint(); + r.cacheable = mem_req_write_cacheable_i.read(); + while (!write_req_fifo.nb_write(r)) wait(); + + wait(); + } + } + + void write_data_process() + { + // + // This process consumes beats on the Write Data Channel + // + mem_write_req_data_flit_t r; + for (;;) { + mem_req_write_data_ready_o.write(false); + + // Wait for a data beat + while (!mem_req_write_data_valid_i.read()) wait(); + + // Wait for a random delay before setting the ready signal + wd_ready_delay->next(); + for (int i = 0; i < wd_ready_delay->read(); ++i) wait(); + + // Set the ready signal + mem_req_write_data_ready_o.write(true); + + r.data = mem_req_write_data_i.read(); + r.be = mem_req_write_be_i.read(); + r.last = mem_req_write_last_i.read(); + while (!write_req_data_fifo.nb_write(r)) wait(); + + wait(); + } + } + + void write_process() + { + mem_write_req_flit_t req_meta; + mem_write_req_data_flit_t req_data; + hpdcache_test_transaction_mem_write_req req; + + for (;;) { + while (!write_req_fifo.nb_read(req_meta)) wait(); + while (!write_req_data_fifo.nb_read(req_data)) wait(); + wait(); + + if (req_meta.len != 0) { + std::cout << sc_time_stamp().to_string() + << " / Error: this model currently supports single" + << " flit transactions" << std::endl; + } + + req.addr = req_meta.addr; + req.len = req_meta.len; + req.size = req_meta.size; + req.id = req_meta.id; + req.command = req_meta.command; + req.atomic = req_meta.atomic; + req.cacheable = req_meta.cacheable; + req.data = req_data.data; + req.be = req_data.be; + req.last = req_data.last; + + // send request to scoreboard + sb_mem_write_req_o.write(req); + + // make the write operation + writeOperation(req); + } + } +}; + +#endif /* __HPDCACHE_TEST_MEM_RESP_MODEL_H__ */ diff --git a/hw/vendor/hpdcache/rtl/tb/hpdcache_test_mem_resp_model_base.h b/hw/vendor/hpdcache/rtl/tb/hpdcache_test_mem_resp_model_base.h new file mode 100644 index 00000000..aedf5385 --- /dev/null +++ b/hw/vendor/hpdcache/rtl/tb/hpdcache_test_mem_resp_model_base.h @@ -0,0 +1,298 @@ +/** + * Copyright 2023,2024 CEA* + * *Commissariat a l'Energie Atomique et aux Energies Alternatives (CEA) + * Copyright 2025 Inria, Universite Grenoble-Alpes, TIMA + * + * SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 + * + * Licensed under the Solderpad Hardware License v 2.1 (the “License”); you + * may not use this file except in compliance with the License, or, at your + * option, the Apache License version 2.0. You may obtain a copy of the + * License at + * + * https://solderpad.org/licenses/SHL-2.1/ + * + * Unless required by applicable law or agreed to in writing, any work + * distributed under the License is distributed on an “AS IS” BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +/** + * Author : Cesar Fuguet + * Date : October, 2024 + * Description: Memory model for the HPDCACHE testbench + */ +#ifndef __HPDCACHE_TEST_MEM_RESP_MODEL_BASE_H__ +#define __HPDCACHE_TEST_MEM_RESP_MODEL_BASE_H__ + +#include +#include +#include +#include +#include "hpdcache_test_defs.h" +#include "mem_model.h" +#include "logger.h" + +class hpdcache_test_mem_resp_model_base +{ +public: + + struct segment_t { + uint64_t base_addr; + uint64_t end_addr; + bool error; + + segment_t(uint64_t base, uint64_t end, bool error) : + base_addr(base), end_addr(end), error(error) {}; + }; + + struct excl_reservation_buf_t + { + bool valid; + uint64_t base_addr; + uint64_t end_addr; + }; + + using hpdcache_mem_atomic_e = hpdcache_test_transaction_mem_req::hpdcache_mem_atomic_e; + using hpdcache_mem_command_e = hpdcache_test_transaction_mem_req::hpdcache_mem_command_e; + +protected: + + class mem_write_req_flit_t + { + public: + + uint64_t addr; + uint32_t len; + uint32_t size; + uint32_t id; + uint32_t command; + uint32_t atomic; + bool cacheable; + + const std::string to_string() const + { + std::stringstream os; + + os << "MEM_WRITE" + << " / ADDR = 0x" << std::hex << addr << std::dec + << " / LEN = 0d" << std::dec << len << std::dec + << " / ID = 0x" << std::hex << id << std::dec; + return os.str(); + } + + friend std::ostream& operator<< (std::ostream& os, const mem_write_req_flit_t& r) + { + os << r.to_string(); + return os; + } + }; + + class mem_write_req_data_flit_t + { + public: + + sc_bv data; + sc_bv be; + bool last; + + const std::string to_string() const + { + std::stringstream os; + + os << "MEM_WRITE_DATA" + << " / DATA = 0x" << std::hex << data << std::dec + << " / BE = 0x" << std::hex << be << std::dec + << (last ? " / LAST" : ""); + return os.str(); + } + + friend std::ostream& operator<< (std::ostream& os, const mem_write_req_data_flit_t& r) + { + os << r.to_string(); + return os; + } + }; + + sc_fifo read_resp_fifo; + sc_fifo write_req_fifo; + sc_fifo write_req_data_fifo; + sc_fifo write_resp_fifo; + + std::vector errorsegs; + mem_model *memory_m; + excl_reservation_buf_t excl_buf_m[1 << HPDCACHE_MEM_ID_WIDTH]; + + scv_smart_ptr ra_ready_delay; + scv_smart_ptr rd_valid_delay; + scv_smart_ptr wa_ready_delay; + scv_smart_ptr wd_ready_delay; + scv_smart_ptr wb_valid_delay; + + static constexpr unsigned int MEM_NOC_DATA_WORDS = HPDCACHE_MEM_DATA_WIDTH/64; + + bool check_verbosity(sc_core::sc_verbosity verbosity) + { + return (sc_core::sc_report_handler::get_verbosity_level() >= verbosity); + } + +public: + + hpdcache_test_mem_resp_model_base(const std::string &nm) : + read_resp_fifo(2), + write_resp_fifo(2) + { + std::string mem_model_name; + mem_model_name = mem_model_name + "_" + nm; + memory_m = new mem_model(mem_model_name.c_str(), mem_model::MEM_MODEL_INIT_RANDOM); + + // set default values for delay distributions + scv_bag> ra_delay_distribution; + ra_delay_distribution.push(pair(0, 2), 90); + ra_delay_distribution.push(pair(3, 8), 8); + ra_delay_distribution.push(pair(9, 32), 2); + ra_ready_delay->set_mode(ra_delay_distribution); + + scv_bag> rd_delay_distribution; + rd_delay_distribution.push(pair(0, 2), 8); + rd_delay_distribution.push(pair(3, 8), 90); + rd_delay_distribution.push(pair(9, 64), 2); + rd_valid_delay->set_mode(rd_delay_distribution); + + scv_bag> wa_delay_distribution; + wa_delay_distribution.push(pair(0, 2), 90); + wa_delay_distribution.push(pair(3, 8), 8); + wa_delay_distribution.push(pair(9, 32), 2); + wa_ready_delay->set_mode(wa_delay_distribution); + + scv_bag> wd_delay_distribution; + wd_delay_distribution.push(pair(0, 2), 90); + wd_delay_distribution.push(pair(3, 8), 8); + wd_delay_distribution.push(pair(9, 32), 2); + wd_ready_delay->set_mode(wd_delay_distribution); + + scv_bag> wb_delay_distribution; + wb_delay_distribution.push(pair(0, 2), 8); + wb_delay_distribution.push(pair(3, 8), 90); + wb_delay_distribution.push(pair(9, 64), 2); + wb_valid_delay->set_mode(wb_delay_distribution); + } + + ~hpdcache_test_mem_resp_model_base() + { + delete memory_m; + } + + void add_error_segment(const segment_t& s) + { + errorsegs.push_back(s); + } + + void set_ra_ready_delay_distribution(scv_bag> &dist) + { + ra_ready_delay->set_mode(dist); + } + + scv_smart_ptr get_ra_ready_delay_distribution() + { + return ra_ready_delay; + } + + void set_rd_valid_delay_distribution(scv_bag> &dist) + { + rd_valid_delay->set_mode(dist); + } + + scv_smart_ptr get_rd_valid_delay_distribution() + { + return rd_valid_delay; + } + + void set_wa_ready_delay_distribution(scv_bag> &dist) + { + wa_ready_delay->set_mode(dist); + } + + scv_smart_ptr get_wa_ready_delay_distribution() + { + return wa_ready_delay; + } + + void set_wd_ready_delay_distribution(scv_bag> &dist) + { + wd_ready_delay->set_mode(dist); + } + + scv_smart_ptr get_wd_ready_delay_distribution() + { + return wd_ready_delay; + } + + void set_wb_valid_delay_distribution(scv_bag> &dist) + { + wb_valid_delay->set_mode(dist); + } + + scv_smart_ptr get_wb_valid_delay_distribution() + { + return wb_valid_delay; + } + + bool within_region(uint64_t base0, uint64_t end0, uint64_t base1, uint64_t end1) + { + if (end0 <= base1) return false; + if (base0 >= end1) return false; + return true; + } + + bool within_error_region(uint64_t base, uint64_t end) + { + for (const segment_t& s : errorsegs) { + if (within_region(s.base_addr, s.end_addr, base, end)) { + return s.error; + } + } + return false; + } + + uint64_t compute_amo(hpdcache_test_transaction_mem_req::hpdcache_mem_atomic_e atop, + uint64_t ld_data, + uint64_t st_data, + unsigned bytes) + { + bool umax = (ld_data > st_data); + bool smax; + if (bytes == 4) { + smax = ((int64_t)((int32_t)ld_data) > (int64_t)((int32_t)st_data)); + } else { + smax = (((int64_t)ld_data) > ((int64_t)st_data)); + } + + switch (atop) { + case hpdcache_mem_atomic_e::HPDCACHE_MEM_ATOMIC_ADD : + return ld_data + st_data; + case hpdcache_mem_atomic_e::HPDCACHE_MEM_ATOMIC_CLR : + return ld_data & ~st_data; + case hpdcache_mem_atomic_e::HPDCACHE_MEM_ATOMIC_SET : + return ld_data | st_data; + case hpdcache_mem_atomic_e::HPDCACHE_MEM_ATOMIC_EOR : + return ld_data ^ st_data; + case hpdcache_mem_atomic_e::HPDCACHE_MEM_ATOMIC_SMAX: + return smax ? ld_data : st_data; + case hpdcache_mem_atomic_e::HPDCACHE_MEM_ATOMIC_UMAX: + return umax ? ld_data : st_data; + case hpdcache_mem_atomic_e::HPDCACHE_MEM_ATOMIC_SMIN: + return smax ? st_data : ld_data; + case hpdcache_mem_atomic_e::HPDCACHE_MEM_ATOMIC_UMIN: + return umax ? st_data : ld_data; + case hpdcache_mem_atomic_e::HPDCACHE_MEM_ATOMIC_SWAP: + return st_data; + } + + sc_assert(false && "unknown atomic operation"); + return 0; + } +}; + +#endif /* __HPDCACHE_TEST_MEM_RESP_MODEL_BASE_H__ */ diff --git a/hw/vendor/hpdcache/rtl/tb/hpdcache_test_scoreboard.h b/hw/vendor/hpdcache/rtl/tb/hpdcache_test_scoreboard.h new file mode 100644 index 00000000..a2cb2996 --- /dev/null +++ b/hw/vendor/hpdcache/rtl/tb/hpdcache_test_scoreboard.h @@ -0,0 +1,1099 @@ +/** + * Copyright 2023,2024 CEA* + * *Commissariat a l'Energie Atomique et aux Energies Alternatives (CEA) + * Copyright 2025 Inria, Universite Grenoble-Alpes, TIMA + * + * SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 + * + * Licensed under the Solderpad Hardware License v 2.1 (the “License”); you + * may not use this file except in compliance with the License, or, at your + * option, the Apache License version 2.0. You may obtain a copy of the + * License at + * + * https://solderpad.org/licenses/SHL-2.1/ + * + * Unless required by applicable law or agreed to in writing, any work + * distributed under the License is distributed on an “AS IS” BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +/** + * Author : Cesar Fuguet + * Date : October, 2024 + * Description: Class definition of the hpdcache_test_scoreboard + */ +#ifndef __HPDCACHE_TEST_SCOREBOARD_H__ +#define __HPDCACHE_TEST_SCOREBOARD_H__ + +#include +#include +#include +#include +#include +#include + +#include "hpdcache_test_sequence.h" +#include "hpdcache_test_mem_resp_model_base.h" +#include "hpdcache_test_driver.h" +#include "hpdcache_test_defs.h" +#include "hpdcache_test_amo.h" +#include "logger.h" +#include "generic_cache_directory_plru.h" +#include "ram_model.h" + +// FIXME currently this cannot be set because there are some race conditions +// badly handled. The race conditions are when there is a refill on a given +// set and at the same time a request on the same set (but different tag). In +// that case, the LRU bits are updated in a undefined order (it depends on the +// internal arbiter of the hpdcache that decides whether to handle the request +// or the refill first). +// +// #define ENABLE_CACHE_DIR_VERIF 1 + +class hpdcache_test_scoreboard : public sc_module +{ +public: + + sc_in clk_i; + + sc_fifo_in core_req_i; + sc_fifo_in core_resp_i; + + sc_fifo_in mem_read_req_i; + sc_fifo_in mem_read_resp_i; + + sc_fifo_in mem_write_req_i; + sc_fifo_in mem_write_resp_i; + + sc_in evt_cache_write_miss_i; + sc_in evt_cache_read_miss_i; + sc_in evt_uncached_req_i; + sc_in evt_cmo_req_i; + sc_in evt_write_req_i; + sc_in evt_read_req_i; + sc_in evt_prefetch_req_i; + sc_in evt_req_on_hold_i; + sc_in evt_rtab_rollback_i; + sc_in evt_stall_refill_i; + sc_in evt_stall_i; + + hpdcache_test_scoreboard(sc_core::sc_module_name nm) : + sc_module(nm), + core_req_i("core_req_i"), + core_resp_i("core_resp_i"), + mem_read_req_i("mem_read_req_i"), + mem_read_resp_i("mem_read_resp_i"), + mem_write_req_i("mem_write_req_i"), + mem_write_resp_i("mem_write_resp_i"), + nb_cycles(0), + nb_cycles_effective(0), + nb_core_req(0), + nb_core_req_need_rsp(0), + nb_core_resp(0), + nb_mem_read_req(0), + nb_mem_read_resp(0), + nb_mem_write_req(0), + nb_mem_write_resp(0), + nb_error(0), + sb_error_limit_m(0), + evt_cache_write_miss(0), + evt_cache_read_miss(0), + evt_uncached_req(0), + evt_cmo_req(0), + evt_write_req(0), + evt_read_req(0), + evt_prefetch_req(0), + evt_req_on_hold(0), + evt_rtab_rollback(0), + evt_stall_refill(0), + evt_stall(0), + seq(nullptr), + mem_resp_model(nullptr), + sc_is_atomic(false), +#if ENABLE_CACHE_DIR_VERIF + cache_dir_m (std::make_shared("hpdcache_dir", + HPDCACHE_NWAYS, HPDCACHE_NSETS, HPDCACHE_NWORDS*8)), +#endif + ram_m(std::make_shared("ram")) + { + SC_THREAD(core_req_process); + SC_THREAD(core_resp_process); + SC_THREAD(mem_read_req_process); + SC_THREAD(mem_read_resp_process); + SC_THREAD(mem_write_req_process); + SC_THREAD(mem_write_resp_process); + + SC_METHOD(perf_events_process); + sensitive << clk_i.pos(); + } + + ~hpdcache_test_scoreboard() + { + std::stringstream ss; + if (nb_core_resp != nb_core_req_need_rsp) { + ss.str(""); + ss << "number of responses (" << nb_core_resp + << ") is different than the number of requests (" + << nb_core_req_need_rsp << ")"; + print_error(ss.str()); + } + if (evt_cache_write_miss > evt_write_req) { + ss.str(""); + ss << "number of write misses (" << evt_cache_write_miss + << ") is bigger than the number of write requests (" + << evt_write_req << ")"; + print_error(ss.str()); + } + if (evt_cache_read_miss > evt_read_req) { + ss.str(""); + ss << "number of read misses (" << evt_cache_read_miss + << ") is bigger than the number of read requests (" + << evt_read_req << ")"; + print_error(ss.str()); + } + if (seq->ids_size() > 0) { + ss.str(""); + ss << "unresponded ids:"; + for (auto it : seq->get_ids()) { + ss << " 0x" << std::hex << it << std::dec; + } + print_error(ss.str()); + } + + + if (check_verbosity(sc_core::SC_LOW)) { + ss.str(""); + ss << "SCOREBOARD STATISTICS" << std::endl + << "Status" << std::endl + << "--------------------------------------------------" << std::endl + << "SB.NB_ERROR : " << nb_error << std::endl + << std::endl + + << "Instrumentation" << std::endl + << "--------------------------------------------------" << std::endl + << "SB.NB_CYCLES : " << nb_cycles_effective << std::endl + << "SB.NB_CORE_REQ : " << nb_core_req << std::endl + << "SB.NB_CORE_RESP : " << nb_core_resp << std::endl + << "SB.NB_MEM_READ_REQ : " << nb_mem_read_req << std::endl + << "SB.NB_MEM_READ_RESP : " << nb_mem_read_resp << std::endl + << "SB.NB_MEM_WRITE_REQ : " << nb_mem_write_req << std::endl + << "SB.NB_MEM_WRITE_RESP : " << nb_mem_write_resp << std::endl + << "CACHE.WRITE_MISSES : " << evt_cache_write_miss << std::endl + << "CACHE.READ_MISSES : " << evt_cache_read_miss << std::endl + << "CACHE.UNCACHED_REQUESTS : " << evt_uncached_req << std::endl + << "CACHE.CMO_REQUESTS : " << evt_cmo_req << std::endl + << "CACHE.WRITE_REQUESTS : " << evt_write_req << std::endl + << "CACHE.READ_REQUESTS : " << evt_read_req << std::endl + << "CACHE.PREFETCH_REQUESTS : " << evt_prefetch_req << std::endl + << "CACHE.ON_HOLD_REQUESTS : " << evt_req_on_hold << std::endl + << "CACHE.RTAB_ROLLBACK : " << evt_rtab_rollback << std::endl + << "CACHE.STALL_REFILL : " << evt_stall_refill << std::endl + << "CACHE.STALL : " << evt_stall << std::endl + << std::endl + + << "Computed values" << std::endl + << "---------------" << std::endl + << "Cycles per request : " + << ( nb_core_req > 0 ? (double)nb_cycles_effective/nb_core_req : 0) << std::endl + << "Read miss rate : " + << ( evt_read_req > 0 ? (double)evt_cache_read_miss/evt_read_req : 0) << std::endl + << "Write miss rate : " + << (evt_write_req > 0 ? (double)evt_cache_write_miss/evt_write_req : 0) << std::endl; + + std::cout << ss.str() << std::endl; + } + } + + void set_sequence(std::shared_ptr p) + { + seq = p; + } + + void set_mem_resp_model(std::shared_ptr p) + { + mem_resp_model = p; + } + + void set_error_limit(size_t error_limit) + { + sb_error_limit_m = error_limit; + } + +private: + + uint64_t nb_cycles; + uint64_t nb_cycles_effective; + uint64_t nb_core_req; + uint64_t nb_core_req_need_rsp; + uint64_t nb_core_resp; + uint64_t nb_mem_read_req; + uint64_t nb_mem_read_resp; + uint64_t nb_mem_write_req; + uint64_t nb_mem_write_resp; + + size_t nb_error; + size_t sb_error_limit_m; + + uint64_t evt_cache_write_miss; + uint64_t evt_cache_read_miss; + uint64_t evt_uncached_req; + uint64_t evt_cmo_req; + uint64_t evt_write_req; + uint64_t evt_read_req; + uint64_t evt_prefetch_req; + uint64_t evt_req_on_hold; + uint64_t evt_rtab_rollback; + uint64_t evt_stall_refill; + uint64_t evt_stall; + + std::shared_ptr seq; + std::shared_ptr mem_resp_model; + + static constexpr unsigned int CORE_REQ_WORDS = HPDCACHE_REQ_WORDS; + static constexpr unsigned int CORE_REQ_WORD_BYTES = HPDCACHE_WORD_WIDTH/8; + static constexpr unsigned int CORE_REQ_BYTES = CORE_REQ_WORDS*CORE_REQ_WORD_BYTES; + + struct inflight_entry_t { + uint64_t time; + uint32_t tid; + uint64_t addr; + bool need_rsp; + bool hit; + bool is_read; + bool is_write; + bool is_amo; + bool is_amo_sc; + bool is_amo_lr; + bool is_atomic; + bool is_error; + unsigned op; + bool is_uncacheable; + uint8_t bytes; + uint64_t wdata [CORE_REQ_WORDS]; + uint8_t be [CORE_REQ_WORDS]; + uint64_t rdata [CORE_REQ_WORDS]; + uint8_t rv [CORE_REQ_WORDS]; + + const std::string to_string() const + { + std::stringstream ss; + ss << "@0x" + << std::hex << addr << std::dec + << (hit ? " / hit" : " / miss") + << (is_read ? " / read" : + is_write ? " / write" : + is_amo ? " / amo" : + is_amo_sc ? " / sc" : + is_amo_lr ? " / lr" : "") + << " / " << hpdcache_test_transaction_req::op_to_string(op) + << (is_uncacheable ? " / uncacheable" : "") + << " / bytes = " << (unsigned)bytes; + + return ss.str(); + } + + friend std::ostream& operator<< (std::ostream& os, const inflight_entry_t& req) + { + os << req.to_string(); + return os; + } + }; + + struct inflight_mem_entry_t { + uint64_t addr; + uint8_t bytes; + bool is_uncacheable; + bool is_error; + const inflight_entry_t *core_req_ptr; + }; + + struct lrsc_reservation_buf_t + { + bool valid; + uint64_t base_addr; + uint64_t end_addr; + bool is_atomic; + }; + + static inline bool within_region(uint64_t addr, uint64_t base, uint64_t end) + { + if (addr < base) return false; + if (addr > end) return false; + return true; + } + + static inline uint64_t get_nline(uint64_t addr) + { + return addr >> HPDCACHE_CL_OFFSET_WIDTH; + } + + typedef std::map inflight_map_t; + typedef std::pair inflight_map_pair_t; + typedef std::map inflight_mem_map_t; + typedef std::pair inflight_mem_map_pair_t; + typedef ram_model ram_t; + +#if ENABLE_CACHE_DIR_VERIF + std::shared_ptr cache_dir_m; +#endif + + inflight_map_t inflight_m; + inflight_mem_map_t inflight_mem_read_m; + inflight_mem_map_t inflight_mem_write_m; + lrsc_reservation_buf_t lrsc_buf_m; + std::shared_ptr ram_m; + + sc_fifo inflight_amo_req_m; + bool sc_is_atomic; + +#if SC_VERSION_MAJOR < 3 + SC_HAS_PROCESS(hpdcache_test_scoreboard); +#endif + + void perf_events_process() + { + nb_cycles++; + if (evt_cache_write_miss_i.read()) evt_cache_write_miss++; + if (evt_cache_read_miss_i.read()) evt_cache_read_miss++; + if (evt_uncached_req_i.read()) evt_uncached_req++; + if (evt_cmo_req_i.read()) evt_cmo_req++; + if (evt_write_req_i.read()) evt_write_req++; + if (evt_read_req_i.read()) evt_read_req++; + if (evt_prefetch_req_i.read()) evt_prefetch_req++; + if (evt_req_on_hold_i.read()) evt_req_on_hold++; + if (evt_rtab_rollback_i.read()) evt_rtab_rollback++; + if (evt_stall_refill_i.read()) evt_stall_refill++; + if (evt_stall_i.read()) evt_stall++; + } + + static uint64_t align_to(uint64_t val, uint64_t align) + { + return (val/align)*align; + } + + bool check_verbosity(sc_core::sc_verbosity verbosity) + { + return (sc_core::sc_report_handler::get_verbosity_level() >= verbosity); + } + + void print_error(const std::string &msg) + { + std::cout << sc_time_stamp().to_string() + << " / SB_ERROR: " << msg << std::endl; + + nb_error++; + if (sb_error_limit_m > 0 && (nb_error >= sb_error_limit_m)) { + std::cout << "SB_ERROR: number of errors exceeded the limit (" + << sb_error_limit_m << ")" + << std::endl; + + std::exit(1); + } + } + + void print_debug(const std::string &msg) + { + std::cout << sc_time_stamp().to_string() + << " / SB_DEBUG: " << msg << std::endl; + } + + void core_req_process() + { + hpdcache_test_transaction_req req; + for (;;) { + req = core_req_i.read(); + nb_core_req++; + + if (check_verbosity(sc_core::SC_MEDIUM)) { + std::cout << sc_time_stamp().to_string() << " / " << req << std::endl; + } + + // count the number of requests that need a response + if (req.req_need_rsp) nb_core_req_need_rsp++; + + uint32_t req_id = req.req_tid.to_uint(); + uint64_t req_addr = req.req_addr.to_uint64(); + + inflight_map_t::const_iterator it = inflight_m.find(req_id); + if (it != inflight_m.end()) { + std::stringstream ss; + ss << "core request ID " + << "0x" << std::hex << req_id << std::dec + << " matches an inflight request"; + print_error(ss.str()); + continue; + } + + if (req.is_cmo()) { +#if ENABLE_CACHE_DIR_VERIF + switch (req.req_size.to_uint()) { + case hpdcache_test_transaction_req::HPDCACHE_CMO_INVAL_NLINE: + { + cache_dir_m->inval(req_addr); + break; + } + case hpdcache_test_transaction_req::HPDCACHE_CMO_INVAL_SET_WAY: + { + uint64_t wdata = req.req_wdata.range(HPDCACHE_NWAYS-1, 0).to_uint64(); + if (wdata == 0) break; + for (int way = 0; way < HPDCACHE_NWAYS; way++) { + if ((wdata & (1 << way)) != 0) { + cache_dir_m->inval(way, cache_dir_m->getAddrSet(req_addr)); + } + } + break; + } + case hpdcache_test_transaction_req::HPDCACHE_CMO_INVAL_ALL: + { + for (int way = 0; way < HPDCACHE_NWAYS; way++) { + for (int set = 0; set < HPDCACHE_NSETS; set++) { + cache_dir_m->inval(way, set); + } + } + break; + } + } +#endif + if (!req.req_need_rsp) { + // release response ID on the sequence + seq->deallocate_id(req_id); + continue; + } + } + + bool hit = false; + +#if ENABLE_CACHE_DIR_VERIF + size_t hit_way; + size_t hit_set; + if ((req.is_load() || + req.is_store() || + req.is_amo_lr() || + req.is_amo_sc() || + req.is_amo()) && !req.is_uncacheable()) + { + hit = cache_dir_m->access(req_addr, &hit_way, &hit_set); + } +#endif + + inflight_entry_t e; + e.time = nb_cycles; + e.tid = req_id; + e.addr = req_addr; + e.need_rsp = req.req_need_rsp; + e.hit = hit; + e.is_read = req.is_load(); + e.is_write = req.is_store(); + e.is_amo = req.is_amo(); + e.is_amo_lr = req.is_amo_lr(); + e.is_amo_sc = req.is_amo_sc(); + e.is_uncacheable = req.req_uncacheable; + e.is_error = false; + e.bytes = 1 << req.req_size.to_uint(); + e.op = req.req_op.to_uint(); + if (req.is_store() || req.is_amo_sc() || req.is_amo()) { + for (int i = 0; i < CORE_REQ_WORDS; i++) { + e.wdata[i] = req.req_wdata.range((i + 1)*HPDCACHE_WORD_WIDTH - 1, + i*HPDCACHE_WORD_WIDTH).to_uint64(); + e.be[i] = req.req_be.range((i + 1)*CORE_REQ_WORD_BYTES - 1, + i*CORE_REQ_WORD_BYTES).to_uint(); + } + } + + // Look for the request address into the error memory segments + if (mem_resp_model && (!req.is_cmo() || req.is_cmo_prefetch())) { + e.is_error = mem_resp_model->within_error_region(e.addr, e.addr + e.bytes); + } + + uint64_t aligned_addr = align_to(req_addr, HPDCACHE_REQ_DATA_WIDTH/8); + if (!e.is_error && (req.is_load() || req.is_amo_lr() || req.is_amo())) { + ram_m->read( + reinterpret_cast(e.rdata), + HPDCACHE_REQ_DATA_WIDTH/8, + aligned_addr, + e.rv); + +#if DEBUG_HPDCACHE_TEST_SCOREBOARD + if (check_verbosity(sc_core::SC_DEBUG)) { + for (int i = 0; i < CORE_REQ_WORDS; i++) { + if (e.rv[i]) { + std::stringstream ss; + ss << "check response for @0x" + << std::hex << aligned_addr + i*CORE_REQ_WORD_BYTES << std::dec + << " / expected = 0x" + << std::hex << e.rdata[i] << std::dec + << " / valid = 0x" + << std::hex << (unsigned)e.rv[i] << std::dec; + + print_debug(ss.str()); + } + } + } +#endif + } + + // Compute if the SC address matches the one of a previous LR reservation + if (req.is_amo_lr()) { + if (!e.is_error) { + if (check_verbosity(sc_core::SC_DEBUG)) { + print_debug("making LR reservation"); + } + lrsc_buf_m.valid = true; + lrsc_buf_m.base_addr = e.addr; + lrsc_buf_m.end_addr = e.addr + 8; + lrsc_buf_m.is_atomic = false; // initialization + } else { + if (check_verbosity(sc_core::SC_DEBUG)) { + print_debug("invalidating previous LR reservation"); + } + lrsc_buf_m.valid = false; + } + } + + // Manage invalidation of the LR/SC reservation buffer + if (req.is_amo() || req.is_amo_sc() || req.is_store()) { + bool addr_match; + + // Compute if the SC address matches the one of a previous LR reservation + addr_match = lrsc_buf_m.valid && ((e.addr >> 3) == (lrsc_buf_m.base_addr >> 3)); + + if (req.is_amo_sc()) { + // SC can get an error response only if there is a valid + // reservation, otherwise the response shall be SC_FAILURE (but + // without error) + e.is_error = e.is_error && addr_match; + + // If there is a previous reservation (previous LR) on the address + // accessed by a SC operation, then the acccess MAY be atomic. The + // scoreboard needs to wait for the acknowledgement from the memory to + // actually know the atomicity of the access. + e.is_atomic = addr_match; + } + + // Invalidate the active reservation (independently of the address match in + // case of SC because as specified in the RISC-V ISA, one condition to be + // fulfilled by a SC to succeed is that there is no other SC is between the LR + // and itself in program order). + if (addr_match || req.is_amo_sc()) { + if (check_verbosity(sc_core::SC_DEBUG)) { + print_debug("invalidate LR reservation"); + } + lrsc_buf_m.valid = false; + } + } + + if (req.is_amo() || req.is_amo_lr() || (req.is_amo_sc() && e.is_atomic)) { + if (check_verbosity(sc_core::SC_DEBUG)) { + print_debug("atomic operation shall be forwarded to memory"); + } + // Share transaction information with the memory interface threads + if (!inflight_amo_req_m.nb_write(e)) { + print_error("inflight AMO request fifo is full"); + continue; + } + } + + // keep track of written data + if (req.is_store() && !e.is_error) { + ram_m->write( + reinterpret_cast(e.wdata), + reinterpret_cast(e.be), + HPDCACHE_REQ_DATA_WIDTH/8, + aligned_addr); + +#if DEBUG_HPDCACHE_TEST_SCOREBOARD + if (check_verbosity(sc_core::SC_DEBUG)) { + for (int i = 0; i < CORE_REQ_WORDS; i++) { + if (e.be[i]) { + std::stringstream ss; + ss << "store sb.mem @0x" + << std::hex << aligned_addr + i*CORE_REQ_WORD_BYTES << std::dec + << " = 0x" + << std::hex << e.wdata[i] << std::dec + << " / be = 0x" + << std::hex << (unsigned)e.be[i] << std::dec; + + print_debug(ss.str()); + } + } + } +#endif + } + + if (req.req_need_rsp) { + // add new core request into the table of inflight requests + inflight_m.insert(inflight_map_pair_t(req_id, e)); + } else { + // deallocate the ID immediately for requests with no response + seq->deallocate_id(req_id); + } + } + } + + void core_resp_process() + { + hpdcache_test_transaction_resp resp; + for (;;) { + resp = core_resp_i.read(); + nb_core_resp++; + + nb_cycles_effective = nb_cycles; + + if (check_verbosity(sc_core::SC_MEDIUM)) { + std::cout << sc_time_stamp().to_string() << " / " << resp << std::endl; + } + + // check if there is a matching request for the received response + inflight_map_t::const_iterator it = inflight_m.find(resp.rsp_tid.to_uint()); + if (it == inflight_m.end()) { + std::stringstream ss; + ss << "core response ID " + << "0x" << std::hex << resp.rsp_tid.to_uint() << std::dec + << " does not match any inflight request"; + print_error(ss.str()); + continue; + } + + const inflight_entry_t &e = it->second; + +#if DEBUG_HPDCACHE_TEST_SCOREBOARD + if (check_verbosity(sc_core::SC_DEBUG)) { + print_debug(e.to_string()); + } +#endif + + if (!e.is_write && (resp.rsp_error != e.is_error)) { + print_error("unexpected value in the error flag of the response"); + continue; + } + + if (!e.is_error) { + const uint64_t aligned_addr = align_to(e.addr, CORE_REQ_BYTES); + const int _word = (e.addr / CORE_REQ_WORD_BYTES) % CORE_REQ_WORDS; + const int _byte = e.addr % CORE_REQ_BYTES; + + // check the response status for SC operations + bool sc_ok; + if (e.is_amo_sc) { + uint64_t sc_resp = resp.rsp_rdata.range((_word + 1)*HPDCACHE_WORD_WIDTH - 1, + _word*HPDCACHE_WORD_WIDTH).to_uint64(); + if (e.bytes == 4) { + sc_resp = (uint32_t)sc_resp; + } + + sc_ok = e.is_atomic && sc_is_atomic; + if (sc_ok) { + ram_m->write( + reinterpret_cast(&e.wdata[_word]), + reinterpret_cast(&e.be[_word]), + CORE_REQ_WORD_BYTES, align_to(e.addr, CORE_REQ_WORD_BYTES)); + + // check response status + if (sc_resp != 0) { + print_error("response shall be SC_SUCCESS"); + } + } else if (sc_resp != 1) { + print_error("response shall be SC_FAILURE"); + } + } + + // check the response data + if (e.is_read || e.is_amo || e.is_amo_lr) { + uint64_t rdata[CORE_REQ_WORDS]; + for (int i = 0; i < CORE_REQ_WORDS; i++) { + rdata[i] = resp.rsp_rdata.range((i + 1)*HPDCACHE_WORD_WIDTH - 1, + i*HPDCACHE_WORD_WIDTH).to_uint64(); + } + + // check the received data + // {{{ + for (int i = _byte; i < (_byte + e.bytes); i++) { + const uint8_t recv = reinterpret_cast(rdata)[i]; + const uint8_t expc = reinterpret_cast(e.rdata)[i]; + const uint8_t rv = reinterpret_cast(e.rv)[i/CORE_REQ_WORD_BYTES]; + + if ((rv >> (i % CORE_REQ_WORD_BYTES)) & 0x1) { + if (recv != expc) { + std::stringstream ss; + ss << "response data is wrong" + << " / @0x" + << std::hex << aligned_addr + i << std::dec + << " / actual = 0x" + << std::hex << (unsigned)recv << std::dec + << " / expected = 0x" + << std::hex << (unsigned)expc << std::dec; + print_error(ss.str()); + } + } + } + // }}} + + // update the memory with the computed AMO word + // {{{ + if (e.is_amo) { + unsigned offset = (e.addr % CORE_REQ_WORD_BYTES) * CORE_REQ_WORD_BYTES; + uint64_t amo_new = e.wdata[_word]; + uint64_t amo_old = rdata[_word]; + + if (e.bytes == 4) { + amo_new = static_cast(amo_new >> offset); + amo_old = static_cast(amo_old >> offset); + } + uint64_t amo_res = hpdcache_test_amo::compute_amo(e.op, amo_old, amo_new, e.bytes); + uint8_t amo_be = e.be[_word]; + + amo_res = (amo_res << offset); + ram_m->write( + reinterpret_cast(&amo_res), + reinterpret_cast(&amo_be), + CORE_REQ_WORD_BYTES, align_to(e.addr, CORE_REQ_WORD_BYTES)); + +#if DEBUG_HPDCACHE_TEST_SCOREBOARD + if (check_verbosity(sc_core::SC_DEBUG)) { + if (amo_be) { + std::stringstream ss; + ss << hpdcache_test_transaction_req::op_to_string(e.op) + << " sb.mem @0x" + << std::hex << align_to(e.addr, CORE_REQ_WORD_BYTES) << std::dec + << " = 0x" + << std::hex << amo_res << std::dec + << " / be = 0x" + << std::hex << (unsigned)amo_be << std::dec; + + print_debug(ss.str()); + } + } +#endif + } + // }}} + } + } + + // remove request from the inflight table + inflight_m.erase(it); + + // release response ID on the sequence + seq->deallocate_id(resp.rsp_tid.to_uint()); + } + } + + void mem_read_req_process() + { + hpdcache_test_transaction_mem_read_req req; + for (;;) { + req = mem_read_req_i.read(); + nb_mem_read_req++; + + if (check_verbosity(sc_core::SC_MEDIUM)) { + std::cout << sc_time_stamp().to_string() << " / " << req << std::endl; + } + + uint32_t req_id = req.id; + + inflight_mem_map_t::const_iterator it = inflight_mem_read_m.find(req_id); + if (it != inflight_mem_read_m.end()) { + std::stringstream ss; + ss << "memory read request ID " + << "0x" << std::hex << req_id << std::dec + << " matches an inflight request"; + print_error(ss.str()); + continue; + } + + // find the associated core request + const inflight_entry_t *core_req = nullptr; + for (const auto &cr : inflight_m) { + const inflight_entry_t &_cr = cr.second; + if (get_nline(_cr.addr) == get_nline(req.addr)) { + // get first occurence + if (core_req == nullptr) { + core_req = &_cr; + continue; + } + + // if multiple occurences, get the oldest + if (_cr.time < core_req->time) { + core_req = &_cr; + } + } + } + + const uint64_t bytes = (1ULL << req.size); + + // add new memory read request into the table of inflight memory requests + inflight_mem_entry_t e; + e.addr = req.addr; + e.bytes = bytes; + e.is_uncacheable = !req.cacheable; + e.is_error = mem_resp_model->within_error_region(e.addr, e.addr + bytes); + e.core_req_ptr = core_req; + + inflight_mem_read_m.insert(inflight_mem_map_pair_t(req_id, e)); + + if (req.is_ldex()) { + inflight_entry_t inflight_ret; + if (inflight_amo_req_m.num_available() > 1) { + print_error("there shall be a single inflight atomic operation"); + } + if (!inflight_amo_req_m.nb_read(inflight_ret)) { + print_error("unexpected load-exclusive request"); + continue; + } + } + } + } + + void mem_read_resp_process() + { + hpdcache_test_transaction_mem_read_resp resp; + for (;;) { + resp = mem_read_resp_i.read(); + nb_mem_read_resp++; + + if (check_verbosity(sc_core::SC_MEDIUM)) { + std::cout << sc_time_stamp().to_string() << " / " << resp << std::endl; + } + + inflight_mem_map_t::const_iterator it = inflight_mem_read_m.find(resp.id); + if (it == inflight_mem_read_m.end()) { + std::stringstream ss; + ss << "memory read response ID " + << "0x" << std::hex << resp.id << std::dec + << " does not match any inflight request"; + print_error(ss.str()); + continue; + } + + // get a pointer to the originating core request + const inflight_mem_entry_t *mem_req = &it->second; + const inflight_entry_t *core_req = mem_req->core_req_ptr; + +#if ENABLE_CACHE_DIR_VERIF + if (!mem_req->is_uncacheable) { + bool hit = cache_dir_m->hit(mem_req->addr, nullptr, nullptr); + if (hit) { + print_error("memory read miss response while there is a corresponding line in the cache"); + } + + // replace a line in the cache in case of a read miss response + uint64_t victim_tag; + size_t victim_way; + size_t victim_set; + bool victim_valid = false; + victim_valid = cache_dir_m->repl(mem_req->addr, &victim_tag, &victim_way, &victim_set); + +#if DEBUG_HPDCACHE_TEST_SCOREBOARD + if (check_verbosity(sc_core::SC_DEBUG)) { + std::stringstream ss; + + unsigned int lru_vector = 0; + for (int way = 0; way < cache_dir_m->getWays(); way++) { + if (cache_dir_m->getCachePlru(way, cache_dir_m->getAddrSet(mem_req->addr))) { + lru_vector |= 1 << way; + } + } + + if (victim_valid) { + ss << "TEST_SB.cache_dir / replacing @0x" + << std::hex << cache_dir_m->getAddr(victim_tag, victim_set) << std::dec + << " (set = 0x" << std::hex << victim_set << std::dec + << ", tag = 0x" << std::hex << victim_tag << std::dec + << ") / @0x" << std::hex << mem_req->addr << std::dec + << " (tag = 0x" << std::hex << cache_dir_m->getAddrTag(mem_req->addr) << std::dec + << ", way = 0x" << std::hex << victim_way << std::dec + << ", lru = 0x" << std::hex << lru_vector << std::dec + << ")"; + } else { + ss << "TEST_SB.cache_dir / allocating @0x" + << std::hex << std::hex << mem_req->addr << std::dec + << " (set = 0x" << std::hex << cache_dir_m->getAddrSet(mem_req->addr) << std::dec + << ", tag = 0x" << std::hex << cache_dir_m->getAddrTag(mem_req->addr) << std::dec + << ", way = 0x" << std::hex << victim_way << std::dec + << ", lru = 0x" << std::hex << lru_vector << std::dec + << ")"; + } + print_debug(ss.str()); + } +#endif + } +#endif + if (!resp.error) { + const int MEM_WORDS = HPDCACHE_MEM_DATA_WIDTH/64; + const int MEM_BYTES = HPDCACHE_MEM_DATA_WIDTH/8; + const uint64_t aligned_addr = align_to(mem_req->addr, MEM_BYTES); + const int _word = (mem_req->addr / MEM_BYTES) % MEM_WORDS; + const int _byte = mem_req->addr % MEM_BYTES; + + // update uninitialized bytes of the scoreboard memory with the + // response from the external memory + // {{{ + uint64_t rdata[MEM_WORDS]; + for (int i = 0; i < MEM_WORDS; i++) { + rdata[i] = resp.data.range((i + 1)*64 - 1, i*64).to_uint64(); + } + + uint8_t unset[MEM_WORDS]; + memset(unset, 0, sizeof(unset)); + for (int i = 0; i < MEM_BYTES; i++) { + unset[i/8] |= (ram_m->getBmap(aligned_addr + i) ? 0x0 : 0x1) << (i % 8); + } + + uint8_t be[MEM_WORDS]; + int bytes = mem_req->bytes; + memset(be, 0, sizeof(be)); + for (int i = _word; bytes > 0; i++) { + uint8_t mask = static_cast(((1UL << bytes) - 1) << (_byte - i*8)); + be[i] = mask & unset[i]; + bytes -= 8; + } + + ram_m->write( + reinterpret_cast(rdata), + reinterpret_cast(be), + MEM_BYTES, + aligned_addr); + +#if DEBUG_HPDCACHE_TEST_SCOREBOARD + if (check_verbosity(sc_core::SC_DEBUG)) { + for (int i = 0; i < MEM_WORDS; i++) { + if (be[i]) { + std::stringstream ss; + ss << "update sb.mem @0x" + << std::hex << aligned_addr + i*8 << std::dec + << " = 0x" + << std::hex << rdata[i] << std::dec + << " / be = 0x" + << std::hex << (unsigned)be[i] << std::dec; + print_debug(ss.str()); + } + } + } +#endif + // }}} + } + + // remove request from the inflight table + if (resp.last) { + inflight_mem_read_m.erase(it); + } + } + } + + void mem_write_req_process() + { + hpdcache_test_transaction_mem_write_req req; + for (;;) { + req = mem_write_req_i.read(); + nb_mem_write_req++; + + if (check_verbosity(sc_core::SC_MEDIUM)) { + std::cout << sc_time_stamp().to_string() << " / " << req << std::endl; + } + + uint32_t req_id = req.id; + uint64_t bytes = (1ULL << req.size); + + inflight_mem_map_t::const_iterator it = inflight_mem_write_m.find(req_id); + if (it != inflight_mem_write_m.end()) { + std::stringstream ss; + ss << "memory write request ID " + << "0x" << std::hex << req_id << std::dec + << " matches an inflight request"; + print_error(ss.str()); + continue; + } + + // find the associated core request + const inflight_entry_t *core_req = nullptr; + for (const auto &cr : inflight_m) { + const inflight_entry_t &_cr = cr.second; + if (get_nline(_cr.addr) == get_nline(req.addr)) { + // get first occurence + if (core_req == nullptr) { + core_req = &_cr; + continue; + } + + // if multiple occurences, get the oldest + if (_cr.time < core_req->time) { + core_req = &_cr; + } + } + } + + // add new memory write request into the table of inflight memory requests + inflight_mem_entry_t e; + e.addr = req.addr; + e.bytes = bytes; + e.is_uncacheable = !req.cacheable; + e.is_error = mem_resp_model->within_error_region(e.addr, e.addr + bytes); + e.core_req_ptr = core_req; + + inflight_mem_write_m.insert(inflight_mem_map_pair_t(req_id, e)); + + if (req.is_amo()) { + inflight_entry_t inflight_ret; + if (inflight_amo_req_m.num_available() > 1) { + print_error("there shall be a single inflight atomic operation"); + } + if (!inflight_amo_req_m.nb_read(inflight_ret)) { + print_error("unexpected AMO request"); + continue; + } + if (core_req == nullptr) { + print_error("memory AMO request with no associated core request"); + } + inflight_mem_read_m.insert(inflight_mem_map_pair_t(req_id, e)); + } + + if (req.is_stex()) { + inflight_entry_t inflight_ret; + if (inflight_amo_req_m.num_available() > 1) { + print_error("there shall be a single inflight atomic operation"); + } + if (!inflight_amo_req_m.nb_read(inflight_ret)) { + print_error("unexpected store-exclusive request"); + continue; + } + if (!inflight_ret.is_atomic) { + print_error("store exclusive access with no valid reservation"); + continue; + } + if (core_req == nullptr) { + print_error("memory store-conditional request with no associated core request"); + } + } + } + } + + void mem_write_resp_process() + { + hpdcache_test_transaction_mem_write_resp resp; + for (;;) { + resp = mem_write_resp_i.read(); + nb_mem_write_resp++; + + if (check_verbosity(sc_core::SC_MEDIUM)) { + std::cout << sc_time_stamp().to_string() << " / " << resp << std::endl; + } + + inflight_mem_map_t::const_iterator it = inflight_mem_write_m.find(resp.id); + if (it == inflight_mem_write_m.end()) { + std::stringstream ss; + ss << "memory write response ID " + << "0x" << std::hex << resp.id << std::dec + << " does not match any inflight request"; + print_error(ss.str()); + continue; + } + + sc_is_atomic = resp.is_atomic; + + // remove request from the inflight table + inflight_mem_write_m.erase(it); + } + } +}; + +#endif /* __HPDCACHE_TEST_SCOREBOARD_H__ */ diff --git a/hw/vendor/hpdcache/rtl/tb/hpdcache_test_sequence.h b/hw/vendor/hpdcache/rtl/tb/hpdcache_test_sequence.h new file mode 100644 index 00000000..54ea0b89 --- /dev/null +++ b/hw/vendor/hpdcache/rtl/tb/hpdcache_test_sequence.h @@ -0,0 +1,268 @@ +/** + * Copyright 2023,2024 CEA* + * *Commissariat a l'Energie Atomique et aux Energies Alternatives (CEA) + * Copyright 2025 Inria, Universite Grenoble-Alpes, TIMA + * + * SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 + * + * Licensed under the Solderpad Hardware License v 2.1 (the “License”); you + * may not use this file except in compliance with the License, or, at your + * option, the Apache License version 2.0. You may obtain a copy of the + * License at + * + * https://solderpad.org/licenses/SHL-2.1/ + * + * Unless required by applicable law or agreed to in writing, any work + * distributed under the License is distributed on an “AS IS” BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +/** + * Author : Cesar Fuguet + * Date : October, 2024 + * Description: Base class definition of the HPDCACHE test sequence + */ +#ifndef __HPDCACHE_TEST_SEQUENCE_H__ +#define __HPDCACHE_TEST_SEQUENCE_H__ + +#include +#include "scv.h" +#include "hpdcache_test_defs.h" +#include "sequence.h" +#include "hpdcache_test_transaction.h" +#include "hpdcache_test_mem_resp_model_base.h" + +class hpdcache_test_sequence : public Sequence +{ + const int HPDCACHE_REQ_MAX_TRANS_ID = 1 << HPDCACHE_REQ_TRANS_ID_WIDTH; + +public: + + hpdcache_test_sequence(sc_core::sc_module_name nm, std::string seq_name) : + Sequence (nm) + , name (seq_name) + , max_transactions (100) + , segptr ("segptr") + , seg_distribution ("seg_distribution") + , delay ("delay") + , delay_distribution ("delay_distribution") + , amo_size ("amo_size") + , amo_size_distribution ("amo_size_distribution") + , amo_sc_do ("amo_sc_do") + , amo_sc_do_distribution ("amo_sc_do_distribution") + , op ("op") + , op_distribution ("op_distribution") + , op_amo ("op_amo") + , op_amo_distribution ("op_amo_distribution") + , wr_policy ("wr_policy") + , wr_policy_distribution ("wr_policy_distribution") + { + std::cout << "Building " << nm << std::endl; + } + + class hpdcache_test_memory_segment + { + public: + enum wr_policy_e { + WR_POLICY_AUTO = 0, + WR_POLICY_WB = 1, + WR_POLICY_WT = 2, + WR_POLICY_RANDOM = 3 + }; + + private: + sc_bv base; + sc_bv length; + bool uncached; + bool amo_supported; + wr_policy_e wr_policy_hint; + + public: + hpdcache_test_memory_segment() : + base (0) + , length (0) + , uncached (false) + , amo_supported (false) + , wr_policy_hint (WR_POLICY_AUTO) + { /* empty */ } + + const uint64_t get_base() const + { + return base.to_uint64(); + } + + const uint64_t get_length() const + { + return length.to_uint64(); + } + + const uint64_t get_end() const + { + return base.to_uint64() + (length.to_uint64() - 1ULL); + } + + const bool is_uncached() const + { + return uncached; + } + + const bool is_amo_supported() const + { + return amo_supported; + } + + const wr_policy_e get_wr_policy_hint() const + { + return wr_policy_hint; + } + + void set_base(uint64_t base) + { + this->base = sc_bv(base); + } + + void set_length(uint64_t length) + { + this->length = sc_bv(length); + } + + void set_uncached(bool uncached) + { + this->uncached = uncached; + } + + void set_amo_supported(bool amo_supported) + { + this->amo_supported = amo_supported; + } + + const void set_wr_policy_hint(wr_policy_e wr_policy_hint) + { + this->wr_policy_hint = wr_policy_hint; + } + }; + + bool is_available_id() + { + return (ids.size() < HPDCACHE_REQ_MAX_TRANS_ID); + } + + unsigned int allocate_id() + { + unsigned int id; + + assert (is_available_id()); + + id = rand() % HPDCACHE_REQ_MAX_TRANS_ID; + for (;;) { + auto it = std::find(ids.begin(), ids.end(), id); + if (it == ids.end()) { + ids.insert(it, id); + break; + } + id = (id + 1) % HPDCACHE_REQ_MAX_TRANS_ID; + } + return id; + } + + void deallocate_id(unsigned int id) + { + auto it = std::find(ids.begin(), ids.end(), id); + if (it != ids.end()) { + ids.erase(it); + return; + } + + std::cout << "SEQ_ERROR: acknowledging an ID that is not currently used" << std::endl; + } + + void set_max_transactions(size_t max_transactions) + { + this->max_transactions = max_transactions; + } + + std::list::const_iterator ids_cbegin() + { + return ids.cbegin(); + } + + std::list::const_iterator ids_cend() + { + return ids.cend(); + } + + const std::list &get_ids() + { + return ids; + } + + size_t ids_size() + { + return ids.size(); + } + + void set_mem_resp_model(std::shared_ptr p) + { + mem_resp_model = p; + } + + std::shared_ptr get_mem_resp_model() + { + return mem_resp_model; + } + + void send_transaction(std::shared_ptr t, int delay = 1) + { + // send transaction to the driver + transaction_fifo_o->write(t); + + // wait and consume driver acknowledgement (this is blocking) + transaction_ret_i.read(); + + // release the previously used transaction object + release_transaction(t); + + // add some delay between two consecutive commands + for (int i = 0; i < delay; i++) wait(); + } + + const std::string to_string() const + { + std::stringstream os; + os << "Sequence " << name << " / Max Transactions = " << max_transactions << std::endl; + return os.str(); + } + + friend std::ostream& operator<< (std::ostream& os, + const hpdcache_test_sequence& seq) + { + os << seq.to_string(); + return os; + } + + +protected: + + std::list ids; + std::string name; + size_t max_transactions; + scv_smart_ptr segptr; + scv_bag seg_distribution; + scv_smart_ptr delay; + scv_bag> delay_distribution; + scv_smart_ptr amo_size; + scv_bag amo_size_distribution; + scv_smart_ptr amo_sc_do; + scv_bag amo_sc_do_distribution; + scv_smart_ptr op; + scv_bag op_distribution; + scv_smart_ptr op_amo; + scv_bag op_amo_distribution; + scv_smart_ptr wr_policy; + scv_bag wr_policy_distribution; + + std::shared_ptr mem_resp_model; +}; + +#endif // __HPDCACHE_TEST_SEQUENCE_H__ diff --git a/hw/vendor/hpdcache/rtl/tb/hpdcache_test_transaction.h b/hw/vendor/hpdcache/rtl/tb/hpdcache_test_transaction.h new file mode 100644 index 00000000..084022e8 --- /dev/null +++ b/hw/vendor/hpdcache/rtl/tb/hpdcache_test_transaction.h @@ -0,0 +1,581 @@ +/** + * Copyright 2023,2024 CEA* + * *Commissariat a l'Energie Atomique et aux Energies Alternatives (CEA) + * Copyright 2025 Inria, Universite Grenoble-Alpes, TIMA + * + * SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 + * + * Licensed under the Solderpad Hardware License v 2.1 (the “License”); you + * may not use this file except in compliance with the License, or, at your + * option, the Apache License version 2.0. You may obtain a copy of the + * License at + * + * https://solderpad.org/licenses/SHL-2.1/ + * + * Unless required by applicable law or agreed to in writing, any work + * distributed under the License is distributed on an “AS IS” BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +/** + * Author : Cesar Fuguet + * Date : October, 2024 + * Description: Class definitions for hpdcache_test transactions + */ +#ifndef __HPDCACHE_TEST_TRANSACTION_H__ +#define __HPDCACHE_TEST_TRANSACTION_H__ + +#include +#include +#include "transaction.h" +#include "hpdcache_test_defs.h" + +using namespace sc_dt; + +class hpdcache_test_transaction_req : public Transaction +{ +public: + enum hpdcache_req_op_e { + HPDCACHE_REQ_LOAD = 0x00, + HPDCACHE_REQ_STORE = 0x01, + // RESERVED = 0x02, + // RESERVED = 0x03, + HPDCACHE_REQ_AMO_LR = 0x04, + HPDCACHE_REQ_AMO_SC = 0x05, + HPDCACHE_REQ_AMO_SWAP = 0x06, + HPDCACHE_REQ_AMO_ADD = 0x07, + HPDCACHE_REQ_AMO_AND = 0x08, + HPDCACHE_REQ_AMO_OR = 0x09, + HPDCACHE_REQ_AMO_XOR = 0x0a, + HPDCACHE_REQ_AMO_MAX = 0x0b, + HPDCACHE_REQ_AMO_MAXU = 0x0c, + HPDCACHE_REQ_AMO_MIN = 0x0d, + HPDCACHE_REQ_AMO_MINU = 0x0e, + // RESERVED = 0x0f, + HPDCACHE_REQ_CMO_FENCE = 0x10, + HPDCACHE_REQ_CMO_PREFETCH = 0x11, + HPDCACHE_REQ_CMO_INVAL_NLINE = 0x12, + HPDCACHE_REQ_CMO_INVAL_ALL = 0x13, + HPDCACHE_REQ_CMO_FLUSH_NLINE = 0x14, + HPDCACHE_REQ_CMO_FLUSH_ALL = 0x15, + HPDCACHE_REQ_CMO_FLUSH_INVAL_NLINE = 0x16, + HPDCACHE_REQ_CMO_FLUSH_INVAL_ALL = 0x17 + }; + + enum hpdcache_wr_policy_hint_e { + HPDCACHE_WR_POLICY_AUTO = 0b001, + HPDCACHE_WR_POLICY_WB = 0b010, + HPDCACHE_WR_POLICY_WT = 0b100 + }; + + typedef std::shared_ptr transaction_ptr; + + // request + sc_bv req_addr; + sc_bv req_wdata; + sc_bv req_op; + sc_bv req_be; + sc_bv req_size; + sc_bv req_sid; + sc_bv req_tid; + bool req_need_rsp; + bool req_phys_indexed; + bool req_uncacheable; + bool req_io; + hpdcache_wr_policy_hint_e req_wr_policy_hint; + bool req_abort; + + hpdcache_test_transaction_req() + { + req_addr = 0; + req_wdata = 0; + req_op = 0; + req_be = 0; + req_size = 0; + req_sid = 0; + req_tid = 0; + req_need_rsp = false; + req_phys_indexed = false; + req_uncacheable = false; + req_io = false; + req_wr_policy_hint = HPDCACHE_WR_POLICY_AUTO; + req_abort = false; + } + + bool is_aborted() const + { + return req_abort; + } + + bool is_uncacheable() const + { + return req_uncacheable; + } + + bool is_io() const + { + return req_io; + } + + bool is_wr_policy_wb() const + { + return (req_wr_policy_hint == HPDCACHE_WR_POLICY_WB); + } + + bool is_wr_policy_wt() const + { + return (req_wr_policy_hint == HPDCACHE_WR_POLICY_WT); + } + + bool is_wr_policy_auto() const + { + return (req_wr_policy_hint == HPDCACHE_WR_POLICY_AUTO); + } + + bool is_load() const + { + return (req_op.to_uint() == HPDCACHE_REQ_LOAD); + } + + bool is_store() const + { + return (req_op.to_uint() == HPDCACHE_REQ_STORE); + } + + bool is_amo() const + { + unsigned op = this->req_op.to_uint(); + return ((op == HPDCACHE_REQ_AMO_SWAP) || + (op == HPDCACHE_REQ_AMO_ADD) || + (op == HPDCACHE_REQ_AMO_AND) || + (op == HPDCACHE_REQ_AMO_OR) || + (op == HPDCACHE_REQ_AMO_XOR) || + (op == HPDCACHE_REQ_AMO_MAX) || + (op == HPDCACHE_REQ_AMO_MAXU) || + (op == HPDCACHE_REQ_AMO_MIN) || + (op == HPDCACHE_REQ_AMO_MINU)); + } + + bool is_amo_lr() const + { + return (req_op.to_uint() == HPDCACHE_REQ_AMO_LR); + } + + bool is_amo_sc() const + { + return (req_op.to_uint() == HPDCACHE_REQ_AMO_SC); + } + + bool is_cmo() const + { + unsigned op = this->req_op.to_uint(); + return ((op == HPDCACHE_REQ_CMO_FENCE) || + (op == HPDCACHE_REQ_CMO_PREFETCH) || + (op == HPDCACHE_REQ_CMO_INVAL_NLINE) || + (op == HPDCACHE_REQ_CMO_INVAL_ALL) || + (op == HPDCACHE_REQ_CMO_FLUSH_NLINE) || + (op == HPDCACHE_REQ_CMO_FLUSH_ALL) || + (op == HPDCACHE_REQ_CMO_FLUSH_INVAL_NLINE) || + (op == HPDCACHE_REQ_CMO_FLUSH_INVAL_ALL)); + } + + bool is_cmo_prefetch() const + { + unsigned op = this->req_op.to_uint(); + return (op == HPDCACHE_REQ_CMO_PREFETCH); + } + + static const char* op_to_string(unsigned int op) + { + switch (op) { + case HPDCACHE_REQ_LOAD : return "LOAD"; + case HPDCACHE_REQ_STORE : return "STORE"; + case HPDCACHE_REQ_AMO_LR : return "LR"; + case HPDCACHE_REQ_AMO_SC : return "SC"; + case HPDCACHE_REQ_AMO_SWAP : return "AMO_SWAP"; + case HPDCACHE_REQ_AMO_ADD : return "AMO_ADD"; + case HPDCACHE_REQ_AMO_AND : return "AMO_AND"; + case HPDCACHE_REQ_AMO_OR : return "AMO_OR"; + case HPDCACHE_REQ_AMO_XOR : return "AMO_XOR"; + case HPDCACHE_REQ_AMO_MAX : return "AMO_MAX"; + case HPDCACHE_REQ_AMO_MAXU : return "AMO_MAXU"; + case HPDCACHE_REQ_AMO_MIN : return "AMO_MIN"; + case HPDCACHE_REQ_AMO_MINU : return "AMO_MINU"; + case HPDCACHE_REQ_CMO_FENCE : return "CMO_FENCE"; + case HPDCACHE_REQ_CMO_PREFETCH : return "CMO_PREFETCH"; + case HPDCACHE_REQ_CMO_INVAL_NLINE : return "CMO_INVAL_NLINE"; + case HPDCACHE_REQ_CMO_INVAL_ALL : return "CMO_INVAL_ALL"; + case HPDCACHE_REQ_CMO_FLUSH_NLINE : return "CMO_FLUSH_NLINE"; + case HPDCACHE_REQ_CMO_FLUSH_ALL : return "CMO_FLUSH_ALL"; + case HPDCACHE_REQ_CMO_FLUSH_INVAL_NLINE : return "CMO_FLUSH_INVAL_NLINE"; + case HPDCACHE_REQ_CMO_FLUSH_INVAL_ALL : return "CMO_FLUSH_INVAL_ALL"; + default : return "UNKNOWN"; + } + } + + static const char* cmo_to_string(unsigned int cmo) + { + switch (cmo) { + case HPDCACHE_REQ_CMO_FENCE : return "CMO_FENCE"; + case HPDCACHE_REQ_CMO_PREFETCH : return "CMO_PREFETCH"; + case HPDCACHE_REQ_CMO_INVAL_NLINE : return "CMO_INVAL_NLINE"; + case HPDCACHE_REQ_CMO_INVAL_ALL : return "CMO_INVAL_ALL"; + case HPDCACHE_REQ_CMO_FLUSH_NLINE : return "CMO_FLUSH_NLINE"; + case HPDCACHE_REQ_CMO_FLUSH_ALL : return "CMO_FLUSH_ALL"; + case HPDCACHE_REQ_CMO_FLUSH_INVAL_NLINE : return "CMO_FLUSH_INVAL_NLINE"; + case HPDCACHE_REQ_CMO_FLUSH_INVAL_ALL : return "CMO_FLUSH_INVAL_ALL"; + default : return "UNKNOWN"; + } + } + + static const char* wr_policy_to_string(hpdcache_wr_policy_hint_e hint) + { + switch (hint) { + case HPDCACHE_WR_POLICY_AUTO : return "WR_POLICY_AUTO"; + case HPDCACHE_WR_POLICY_WB : return "WR_POLICY_WB"; + case HPDCACHE_WR_POLICY_WT : return "WR_POLICY_WT"; + default : return "UNKNOWN"; + } + } + + const std::string to_string() const + { + std::stringstream os; + bool contains_data = false; + unsigned op = this->req_op.to_uint(); + + switch (op) { + case HPDCACHE_REQ_STORE: + case HPDCACHE_REQ_AMO_SC: + case HPDCACHE_REQ_AMO_SWAP: + case HPDCACHE_REQ_AMO_ADD: + case HPDCACHE_REQ_AMO_AND: + case HPDCACHE_REQ_AMO_OR: + case HPDCACHE_REQ_AMO_XOR: + case HPDCACHE_REQ_AMO_MAX: + case HPDCACHE_REQ_AMO_MAXU: + case HPDCACHE_REQ_AMO_MIN: + case HPDCACHE_REQ_AMO_MINU: + contains_data = true; + break; + } + + os << "CORE_REQ / @ = " << req_addr.to_string(SC_HEX) + << " / " << op_to_string(op) + << " / SID = 0x" << std::hex << req_sid.to_uint() << std::dec + << " / TID = 0x" << std::hex << req_tid.to_uint() << std::dec + << " / WR_POLICY_HINT = " << wr_policy_to_string(req_wr_policy_hint); + + if (!is_cmo()) { + os << (req_uncacheable ? " / UNCACHED" : " / CACHED") + << " / SIZE = " << req_size.to_string(SC_HEX) + << (req_need_rsp ? " / NEED_RSP" : " / NO NEED_RSP") + << (req_phys_indexed ? " / PHYS_INDEXED" : " / VIRT_INDEXED"); + + if (contains_data) { + os << " / WDATA = " << req_wdata.to_string(SC_HEX) + << " / BE = " << req_be.to_string(SC_HEX); + } + } else { + os << " / " << cmo_to_string(op); + } + + return os.str(); + } + + friend std::ostream& operator<< (std::ostream& os, + const hpdcache_test_transaction_req& req) + { + os << req.to_string(); + return os; + } +}; + +class hpdcache_test_transaction_resp : public Transaction +{ +public: + // response + sc_bv rsp_rdata; + sc_bv rsp_sid; + sc_bv rsp_tid; + bool rsp_error; + bool rsp_aborted; + + hpdcache_test_transaction_resp() + { + rsp_rdata = 0; + rsp_sid = 0; + rsp_tid = 0; + rsp_error = 0; + rsp_aborted = 0; + } + + typedef std::shared_ptr transaction_ptr; + + const std::string to_string() const + { + std::stringstream os; + + os << "CORE_RESP / RDATA = " << rsp_rdata.to_string(SC_HEX) + << " / SID = 0x" << std::hex << rsp_sid.to_uint() << std::dec + << " / TID = 0x" << std::hex << rsp_tid.to_uint() << std::dec + << (rsp_error ? " / ERROR" : "") + << (rsp_aborted ? " / ABORTED" : ""); + + return os.str(); + } + + friend std::ostream& operator<< (std::ostream& os, + const hpdcache_test_transaction_resp& resp) + { + os << resp.to_string(); + return os; + } +}; + +class hpdcache_test_transaction_mem_req +{ +public: + + uint64_t addr; + uint32_t len; + uint32_t size; + uint32_t id; + uint32_t command; + uint32_t atomic; + bool cacheable; + + hpdcache_test_transaction_mem_req() : + addr(0), + len(0), + size(0), + id(0), + command(0), + atomic(0), + cacheable(false) + { + + } + + enum hpdcache_mem_command_e + { + HPDCACHE_MEM_READ = 0x0, + HPDCACHE_MEM_WRITE = 0x1, + HPDCACHE_MEM_ATOMIC = 0x2 + }; + + enum hpdcache_mem_atomic_e { + HPDCACHE_MEM_ATOMIC_ADD = 0x0, + HPDCACHE_MEM_ATOMIC_CLR = 0x1, + HPDCACHE_MEM_ATOMIC_SET = 0x2, + HPDCACHE_MEM_ATOMIC_EOR = 0x3, + HPDCACHE_MEM_ATOMIC_SMAX = 0x4, + HPDCACHE_MEM_ATOMIC_SMIN = 0x5, + HPDCACHE_MEM_ATOMIC_UMAX = 0x6, + HPDCACHE_MEM_ATOMIC_UMIN = 0x7, + HPDCACHE_MEM_ATOMIC_SWAP = 0x8, + HPDCACHE_MEM_ATOMIC_LDEX = 0xc, + HPDCACHE_MEM_ATOMIC_STEX = 0xd + }; + + bool is_amo() const + { + return ((command == hpdcache_mem_command_e::HPDCACHE_MEM_ATOMIC) && + (atomic != hpdcache_mem_atomic_e::HPDCACHE_MEM_ATOMIC_STEX) && + (atomic != hpdcache_mem_atomic_e::HPDCACHE_MEM_ATOMIC_LDEX)); + } + + bool is_stex() const + { + return ((command == hpdcache_mem_command_e::HPDCACHE_MEM_ATOMIC) && + (atomic == hpdcache_mem_atomic_e::HPDCACHE_MEM_ATOMIC_STEX)); + } + + bool is_ldex() const + { + return ((command == hpdcache_mem_command_e::HPDCACHE_MEM_ATOMIC) && + (atomic == hpdcache_mem_atomic_e::HPDCACHE_MEM_ATOMIC_LDEX)); + } + + static const char* command_to_string(unsigned int command) + { + switch (command) { + case HPDCACHE_MEM_READ : return "READ"; + case HPDCACHE_MEM_WRITE : return "STORE"; + case HPDCACHE_MEM_ATOMIC : return "ATOMIC"; + default : return "UNKNOWN"; + } + } + + static const char* atomic_to_string(unsigned int atomic) + { + switch (atomic) { + case HPDCACHE_MEM_ATOMIC_ADD : return "ATOMIC_ADD"; + case HPDCACHE_MEM_ATOMIC_CLR : return "ATOMIC_CLR"; + case HPDCACHE_MEM_ATOMIC_SET : return "ATOMIC_SET"; + case HPDCACHE_MEM_ATOMIC_EOR : return "ATOMIC_EOR"; + case HPDCACHE_MEM_ATOMIC_SMAX : return "ATOMIC_SMAX"; + case HPDCACHE_MEM_ATOMIC_SMIN : return "ATOMIC_SMIN"; + case HPDCACHE_MEM_ATOMIC_UMAX : return "ATOMIC_UMAX"; + case HPDCACHE_MEM_ATOMIC_UMIN : return "ATOMIC_UMIN"; + case HPDCACHE_MEM_ATOMIC_SWAP : return "ATOMIC_SWAP"; + case HPDCACHE_MEM_ATOMIC_LDEX : return "ATOMIC_LDEX"; + case HPDCACHE_MEM_ATOMIC_STEX : return "ATOMIC_STEX"; + default : return "UNKNOWN"; + } + } + + virtual const std::string to_string() const = 0; +}; + +class hpdcache_test_transaction_mem_read_req: public hpdcache_test_transaction_mem_req +{ +public: + + hpdcache_test_transaction_mem_read_req() : + hpdcache_test_transaction_mem_req() + { + + } + + const std::string to_string() const + { + std::stringstream os; + + os << "MEM_READ_REQ / @ = " << std::hex << addr << std::dec + << " / CMD = " << command_to_string(command) + << " / LEN = " << std::hex << len << std::dec + << " / SIZE = " << std::hex << size << std::dec + << " / ID = 0x" << std::hex << id << std::dec + << (cacheable ? " / CACHEABLE" : " / UNCACHEABLE"); + return os.str(); + } + + friend std::ostream& operator<< (std::ostream& os, + const hpdcache_test_transaction_mem_read_req& req) + { + os << req.to_string(); + return os; + } +}; + +class hpdcache_test_transaction_mem_write_req: public hpdcache_test_transaction_mem_req +{ +public: + + sc_bv data; + sc_bv be; + bool last; + + hpdcache_test_transaction_mem_write_req() : + data(0), + be(0), + last(false), + hpdcache_test_transaction_mem_req() + { + + } + + const std::string to_string() const + { + std::stringstream os; + + os << "MEM_WRITE_REQ / @ = " << std::hex << addr << std::dec + << " / COMMAND = " << command_to_string(command); + + if (command == HPDCACHE_MEM_ATOMIC) { + os << " / ATOMIC = " << atomic_to_string(atomic); + } + + os << " / LEN = " << std::hex << len << std::dec + << " / SIZE = " << std::hex << size << std::dec + << " / ID = 0x" << std::hex << id << std::dec + << " / DATA = " << data.to_string(SC_HEX) + << " / BE = " << be.to_string(SC_HEX) + << (last ? " / LAST" : " ") + << (cacheable ? " / CACHEABLE" : " / UNCACHEABLE"); + return os.str(); + } + + friend std::ostream& operator<< (std::ostream& os, + const hpdcache_test_transaction_mem_write_req& req) + { + os << req.to_string(); + return os; + } +}; + +class hpdcache_test_transaction_mem_read_resp +{ +public: + + uint32_t error; + uint32_t id; + sc_bv data; + bool last; + + hpdcache_test_transaction_mem_read_resp() : + error(0), + id(0), + data(0), + last(false) + { + + } + + const std::string to_string() const + { + std::stringstream os; + + os << "MEM_READ_RESP / RDATA = " << data.to_string(SC_HEX) + << " / ID = 0x" << std::hex << id << std::dec + << (last ? " / LAST" : "") + << (error != 0 ? " / ERROR" : ""); + return os.str(); + } + + friend std::ostream& operator<< (std::ostream& os, + const hpdcache_test_transaction_mem_read_resp& resp) + { + os << resp.to_string(); + return os; + } +}; + +class hpdcache_test_transaction_mem_write_resp +{ +public: + + bool is_atomic; + uint32_t error; + uint32_t id; + + hpdcache_test_transaction_mem_write_resp() : + is_atomic(false), + error(0), + id(0) + { + + } + + const std::string to_string() const + { + std::stringstream os; + + os << "MEM_WRITE_RESP" + << " / ID = 0x" << std::hex << id << std::dec + << (error != 0 ? " / ERROR" : "") + << (is_atomic ? " / ATOMIC" : ""); + return os.str(); + } + + friend std::ostream& operator<< (std::ostream& os, + const hpdcache_test_transaction_mem_write_resp& resp) + { + os << resp.to_string(); + return os; + } +}; + +#endif /* __HPDCACHE_TEST_TRANSACTION_H__ */ diff --git a/hw/vendor/hpdcache/rtl/tb/hpdcache_wrapper.sv b/hw/vendor/hpdcache/rtl/tb/hpdcache_wrapper.sv new file mode 100644 index 00000000..01151214 --- /dev/null +++ b/hw/vendor/hpdcache/rtl/tb/hpdcache_wrapper.sv @@ -0,0 +1,383 @@ +/** + * Copyright 2023,2024 CEA* + * *Commissariat a l'Energie Atomique et aux Energies Alternatives (CEA) + * Copyright 2025 Inria, Universite Grenoble-Alpes, TIMA + * + * SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 + * + * Licensed under the Solderpad Hardware License v 2.1 (the “License”); you + * may not use this file except in compliance with the License, or, at your + * option, the Apache License version 2.0. You may obtain a copy of the + * License at + * + * https://solderpad.org/licenses/SHL-2.1/ + * + * Unless required by applicable law or agreed to in writing, any work + * distributed under the License is distributed on an “AS IS” BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +/** + * Author(s) : Cesar Fuguet + * Date : October, 2024 + * Description: HPDcache testbench wrapper + */ +`include "hpdcache_typedef.svh" + +module hpdcache_wrapper +import hpdcache_pkg::*; + // Parameters + // {{{ +#( + localparam hpdcache_user_cfg_t UserCfg = '{ + nRequesters: (4'b1 << `CONF_HPDCACHE_REQ_SRC_ID_WIDTH), + paWidth: `CONF_HPDCACHE_PA_WIDTH, + wordWidth: `CONF_HPDCACHE_WORD_WIDTH, + wordUserWidth: `CONF_HPDCACHE_WORD_USER_WIDTH, + sets: `CONF_HPDCACHE_SETS, + ways: `CONF_HPDCACHE_WAYS, + clWords: `CONF_HPDCACHE_CL_WORDS, + reqWords: `CONF_HPDCACHE_REQ_WORDS, + reqTransIdWidth: `CONF_HPDCACHE_REQ_TRANS_ID_WIDTH, + reqSrcIdWidth: `CONF_HPDCACHE_REQ_SRC_ID_WIDTH, + victimSel: `CONF_HPDCACHE_VICTIM_SEL, + dataWaysPerRamWord: `CONF_HPDCACHE_DATA_WAYS_PER_RAM_WORD, + dataSetsPerRam: `CONF_HPDCACHE_DATA_SETS_PER_RAM, + dataRamByteEnable: `CONF_HPDCACHE_DATA_RAM_WBYTEENABLE, + accessWords: `CONF_HPDCACHE_ACCESS_WORDS, + mshrSets: `CONF_HPDCACHE_MSHR_SETS, + mshrWays: `CONF_HPDCACHE_MSHR_WAYS, + mshrWaysPerRamWord: `CONF_HPDCACHE_MSHR_WAYS_PER_RAM_WORD, + mshrSetsPerRam: `CONF_HPDCACHE_MSHR_SETS_PER_RAM, + mshrRamByteEnable: `CONF_HPDCACHE_MSHR_RAM_WBYTEENABLE, + mshrUseRegbank: `CONF_HPDCACHE_MSHR_USE_REGBANK, + cbufEntries: `CONF_HPDCACHE_CBUF_ENTRIES, + refillCoreRspFeedthrough: `CONF_HPDCACHE_REFILL_CORE_RSP_FEEDTHROUGH, + refillFifoDepth: `CONF_HPDCACHE_REFILL_FIFO_DEPTH, + wbufDirEntries: `CONF_HPDCACHE_WBUF_DIR_ENTRIES, + wbufDataEntries: `CONF_HPDCACHE_WBUF_DATA_ENTRIES, + wbufWords: `CONF_HPDCACHE_WBUF_WORDS, + wbufTimecntWidth: `CONF_HPDCACHE_WBUF_TIMECNT_WIDTH, + rtabEntries: `CONF_HPDCACHE_RTAB_ENTRIES, + flushEntries: `CONF_HPDCACHE_FLUSH_ENTRIES, + flushFifoDepth: `CONF_HPDCACHE_FLUSH_FIFO_DEPTH, + memAddrWidth: `CONF_HPDCACHE_MEM_ADDR_WIDTH, + memIdWidth: `CONF_HPDCACHE_MEM_ID_WIDTH, + memDataWidth: `CONF_HPDCACHE_MEM_DATA_WIDTH, + wtEn: `CONF_HPDCACHE_WT_ENABLE, + wbEn: `CONF_HPDCACHE_WB_ENABLE, + userEn: `CONF_HPDCACHE_USER_ENABLE, + capAmoEn: `CONF_HPDCACHE_CAP_AMO_ENABLE, + lowLatency: `CONF_HPDCACHE_LOW_LATENCY + }, + + localparam hpdcache_cfg_t Cfg = hpdcacheBuildConfig(UserCfg), + localparam type wbuf_timecnt_t = logic unsigned [Cfg.u.wbufTimecntWidth-1:0], + + // Request Interface Definitions + // {{{ + localparam type hpdcache_tag_t = logic [Cfg.tagWidth-1:0], + localparam type hpdcache_data_word_t = logic [Cfg.u.wordWidth-1:0], + localparam type hpdcache_data_be_t = logic [Cfg.u.wordWidth/8-1:0], + localparam type hpdcache_req_offset_t = logic [Cfg.reqOffsetWidth-1:0], + localparam type hpdcache_req_data_t = hpdcache_data_word_t [Cfg.u.reqWords-1:0], + localparam type hpdcache_req_be_t = hpdcache_data_be_t [Cfg.u.reqWords-1:0], + localparam type hpdcache_req_sid_t = logic [Cfg.u.reqSrcIdWidth-1:0], + localparam type hpdcache_req_tid_t = logic [Cfg.u.reqTransIdWidth-1:0], + localparam type hpdcache_req_t = + `HPDCACHE_DECL_REQ_T( + hpdcache_req_offset_t, + hpdcache_req_data_t, + hpdcache_req_be_t, + hpdcache_req_sid_t, + hpdcache_req_tid_t, + hpdcache_tag_t), + localparam type hpdcache_rsp_t = + `HPDCACHE_DECL_RSP_T( + hpdcache_req_data_t, + hpdcache_req_sid_t, + hpdcache_req_tid_t), + // }}} + + localparam type hpdcache_mem_addr_t = logic [Cfg.u.memAddrWidth-1:0], + localparam type hpdcache_mem_id_t = logic [Cfg.u.memIdWidth-1:0], + localparam type hpdcache_mem_data_t = logic [Cfg.u.memDataWidth-1:0], + localparam type hpdcache_mem_be_t = logic [Cfg.u.memDataWidth/8-1:0] +) + // }}} + + // Ports + // {{{ +( + // Clock and reset signals + input wire logic clk_i, + input wire logic rst_ni, + + // Force the write buffer to send all pending writes + input wire logic wbuf_flush_i, + + // Core request interface + // 1st cycle + input logic core_req_valid_i, + output logic core_req_ready_o, + input hpdcache_req_t core_req_i, + // 2nd cycle + input logic core_req_abort_i, + input hpdcache_tag_t core_req_tag_i, + input hpdcache_pma_t core_req_pma_i, + + // Core response interface + output var logic core_rsp_valid_o, + output var hpdcache_rsp_t core_rsp_o, + + // Memory read interface + input wire logic mem_req_read_ready_i, + output wire logic mem_req_read_valid_o, + output wire hpdcache_mem_addr_t mem_req_read_addr_o, + output wire hpdcache_mem_len_t mem_req_read_len_o, + output wire hpdcache_mem_size_t mem_req_read_size_o, + output wire hpdcache_mem_id_t mem_req_read_id_o, + output wire hpdcache_mem_command_e mem_req_read_command_o, + output wire hpdcache_mem_atomic_e mem_req_read_atomic_o, + output wire logic mem_req_read_cacheable_o, + + output var logic mem_resp_read_ready_o, + input wire logic mem_resp_read_valid_i, + input wire hpdcache_mem_error_e mem_resp_read_error_i, + input wire hpdcache_mem_id_t mem_resp_read_id_i, + input wire hpdcache_mem_data_t mem_resp_read_data_i, + input wire logic mem_resp_read_last_i, + + // Memory write interface + input wire logic mem_req_write_ready_i, + output wire logic mem_req_write_valid_o, + output wire hpdcache_mem_addr_t mem_req_write_addr_o, + output wire hpdcache_mem_len_t mem_req_write_len_o, + output wire hpdcache_mem_size_t mem_req_write_size_o, + output wire hpdcache_mem_id_t mem_req_write_id_o, + output wire hpdcache_mem_command_e mem_req_write_command_o, + output wire hpdcache_mem_atomic_e mem_req_write_atomic_o, + output wire logic mem_req_write_cacheable_o, + + input wire logic mem_req_write_data_ready_i, + output wire logic mem_req_write_data_valid_o, + output wire hpdcache_mem_data_t mem_req_write_data_o, + output wire hpdcache_mem_be_t mem_req_write_be_o, + output wire logic mem_req_write_last_o, + + output var logic mem_resp_write_ready_o, + input wire logic mem_resp_write_valid_i, + input wire logic mem_resp_write_is_atomic_i, + input wire hpdcache_mem_error_e mem_resp_write_error_i, + input wire hpdcache_mem_id_t mem_resp_write_id_i, + + // Performance events + output wire logic evt_cache_write_miss_o, + output wire logic evt_cache_read_miss_o, + output wire logic evt_uncached_req_o, + output wire logic evt_cmo_req_o, + output wire logic evt_write_req_o, + output wire logic evt_read_req_o, + output wire logic evt_prefetch_req_o, + output wire logic evt_req_on_hold_o, + output wire logic evt_rtab_rollback_o, + output wire logic evt_stall_refill_o, + output wire logic evt_stall_o, + + // Status interface + output wire logic wbuf_empty_o, + + // Configuration interface + input wire logic cfg_enable_i, + input wire wbuf_timecnt_t cfg_wbuf_threshold_i, + input wire logic cfg_wbuf_reset_timecnt_on_write_i, + input wire logic cfg_wbuf_sequential_waw_i, + input wire logic cfg_wbuf_inhibit_write_coalescing_i, + input wire logic cfg_prefetch_updt_plru_i, + input wire logic cfg_error_on_cacheable_amo_i, + input wire logic cfg_rtab_single_entry_i, + input wire logic cfg_default_wb_i +); + // }}} + + // Declaration of internal types + // {{{ + `HPDCACHE_TYPEDEF_MEM_REQ_T(hpdcache_mem_req_t, hpdcache_mem_addr_t, hpdcache_mem_id_t); + `HPDCACHE_TYPEDEF_MEM_RESP_R_T(hpdcache_mem_resp_r_t, hpdcache_mem_id_t, hpdcache_mem_data_t); + `HPDCACHE_TYPEDEF_MEM_REQ_W_T(hpdcache_mem_req_w_t, hpdcache_mem_data_t, hpdcache_mem_be_t); + `HPDCACHE_TYPEDEF_MEM_RESP_W_T(hpdcache_mem_resp_w_t, hpdcache_mem_id_t); + // }}} + + // Declaration of internal signals + // {{{ + localparam int unsigned NREQUESTERS = Cfg.u.nRequesters; + + logic core_req_valid [NREQUESTERS]; + logic core_req_ready [NREQUESTERS]; + hpdcache_req_t core_req [NREQUESTERS]; + logic core_req_abort [NREQUESTERS]; + hpdcache_tag_t core_req_tag [NREQUESTERS]; + hpdcache_pma_t core_req_pma [NREQUESTERS]; + + // Core response interface + logic core_rsp_valid [NREQUESTERS]; + hpdcache_rsp_t core_rsp [NREQUESTERS]; + + hpdcache_mem_req_t mem_req_read; + hpdcache_mem_resp_r_t mem_resp_read; + hpdcache_mem_req_t mem_req_write; + hpdcache_mem_req_w_t mem_req_write_data; + hpdcache_mem_resp_w_t mem_resp_write; + // }}} + + // Write/read to/from memory interfaces + // {{{ + assign mem_req_read_addr_o = mem_req_read.mem_req_addr, + mem_req_read_len_o = mem_req_read.mem_req_len, + mem_req_read_size_o = mem_req_read.mem_req_size, + mem_req_read_id_o = mem_req_read.mem_req_id, + mem_req_read_command_o = mem_req_read.mem_req_command, + mem_req_read_atomic_o = mem_req_read.mem_req_atomic, + mem_req_read_cacheable_o = mem_req_read.mem_req_cacheable; + + assign mem_resp_read.mem_resp_r_error = mem_resp_read_error_i, + mem_resp_read.mem_resp_r_id = mem_resp_read_id_i, + mem_resp_read.mem_resp_r_data = mem_resp_read_data_i, + mem_resp_read.mem_resp_r_last = mem_resp_read_last_i, + mem_resp_read.mem_resp_r_user = '0; + + assign mem_req_write_addr_o = mem_req_write.mem_req_addr, + mem_req_write_len_o = mem_req_write.mem_req_len, + mem_req_write_size_o = mem_req_write.mem_req_size, + mem_req_write_id_o = mem_req_write.mem_req_id, + mem_req_write_command_o = mem_req_write.mem_req_command, + mem_req_write_atomic_o = mem_req_write.mem_req_atomic, + mem_req_write_cacheable_o = mem_req_write.mem_req_cacheable; + + assign mem_req_write_data_o = mem_req_write_data.mem_req_w_data, + mem_req_write_be_o = mem_req_write_data.mem_req_w_be, + mem_req_write_last_o = mem_req_write_data.mem_req_w_last; + + assign mem_resp_write.mem_resp_w_is_atomic = mem_resp_write_is_atomic_i, + mem_resp_write.mem_resp_w_error = mem_resp_write_error_i, + mem_resp_write.mem_resp_w_id = mem_resp_write_id_i; + // }}} + + always_comb + begin : core_req_routing_comb + core_req_ready_o = core_req_valid_i && core_req_ready[core_req_i.sid]; + for (int i = 0; i < NREQUESTERS; i++) begin + core_req_valid [i] = core_req_valid_i && (core_req_i.sid == hpdcache_req_sid_t'(i)); + core_req [i] = core_req_i; + core_req_abort [i] = core_req_abort_i; + core_req_tag [i] = core_req_tag_i; + core_req_pma [i] = core_req_pma_i; + end + end + + always_comb + begin : core_rsp_routing_comb + core_rsp_valid_o = '0; + core_rsp_o = '0; + for (int i = 0; i < NREQUESTERS; i++) begin + if (core_rsp_valid[i]) begin + core_rsp_valid_o = 1'b1; + core_rsp_o = core_rsp[i]; + break; + end + end + end + + hpdcache #( + .HPDcacheCfg (Cfg), + .wbuf_timecnt_t (wbuf_timecnt_t), + .hpdcache_tag_t (hpdcache_tag_t), + .hpdcache_data_word_t (hpdcache_data_word_t), + .hpdcache_data_be_t (hpdcache_data_be_t), + .hpdcache_req_offset_t (hpdcache_req_offset_t), + .hpdcache_req_data_t (hpdcache_req_data_t), + .hpdcache_req_be_t (hpdcache_req_be_t), + .hpdcache_req_sid_t (hpdcache_req_sid_t), + .hpdcache_req_tid_t (hpdcache_req_tid_t), + .hpdcache_req_t (hpdcache_req_t), + .hpdcache_rsp_t (hpdcache_rsp_t), + .hpdcache_mem_addr_t (hpdcache_mem_addr_t), + .hpdcache_mem_id_t (hpdcache_mem_id_t), + .hpdcache_mem_data_t (hpdcache_mem_data_t), + .hpdcache_mem_be_t (hpdcache_mem_be_t), + .hpdcache_mem_req_t (hpdcache_mem_req_t), + .hpdcache_mem_req_w_t (hpdcache_mem_req_w_t), + .hpdcache_mem_resp_r_t (hpdcache_mem_resp_r_t), + .hpdcache_mem_resp_w_t (hpdcache_mem_resp_w_t) + ) i_hpdcache( + .clk_i, + .rst_ni, + + .wbuf_flush_i, + + .core_req_valid_i (core_req_valid), + .core_req_ready_o (core_req_ready), + .core_req_i (core_req), + .core_req_abort_i (core_req_abort), + .core_req_tag_i (core_req_tag), + .core_req_pma_i (core_req_pma), + + .core_rsp_valid_o (core_rsp_valid), + .core_rsp_o (core_rsp), + + .mem_req_read_ready_i (mem_req_read_ready_i), + .mem_req_read_valid_o (mem_req_read_valid_o), + .mem_req_read_o (mem_req_read), + + .mem_resp_read_ready_o (mem_resp_read_ready_o), + .mem_resp_read_valid_i (mem_resp_read_valid_i), + .mem_resp_read_i (mem_resp_read), + + .mem_req_write_ready_i (mem_req_write_ready_i), + .mem_req_write_valid_o (mem_req_write_valid_o), + .mem_req_write_o (mem_req_write), + + .mem_req_write_data_ready_i (mem_req_write_data_ready_i), + .mem_req_write_data_valid_o (mem_req_write_data_valid_o), + .mem_req_write_data_o (mem_req_write_data), + + .mem_resp_write_ready_o (mem_resp_write_ready_o), + .mem_resp_write_valid_i (mem_resp_write_valid_i), + .mem_resp_write_i (mem_resp_write), + + .evt_cache_write_miss_o, + .evt_cache_read_miss_o, + .evt_uncached_req_o, + .evt_cmo_req_o, + .evt_write_req_o, + .evt_read_req_o, + .evt_prefetch_req_o, + .evt_req_on_hold_o, + .evt_rtab_rollback_o, + .evt_stall_refill_o, + .evt_stall_o, + + .wbuf_empty_o, + + .cfg_enable_i, + .cfg_wbuf_threshold_i, + .cfg_wbuf_reset_timecnt_on_write_i, + .cfg_wbuf_sequential_waw_i, + .cfg_wbuf_inhibit_write_coalescing_i, + .cfg_prefetch_updt_plru_i, + .cfg_error_on_cacheable_amo_i, + .cfg_rtab_single_entry_i, + .cfg_default_wb_i + ); + + // Assertions/Coverage + // {{{ + // pragma translate_off + wbuf_not_ready_cover: cover property ( + @(posedge clk_i) i_hpdcache.hpdcache_ctrl_i.wbuf_write_o & + ~i_hpdcache.hpdcache_ctrl_i.wbuf_write_ready_i); + // pragma translate_on + // }}} + +endmodule diff --git a/hw/vendor/hpdcache/rtl/tb/rtl_conf.mk b/hw/vendor/hpdcache/rtl/tb/rtl_conf.mk new file mode 100644 index 00000000..c6e70f13 --- /dev/null +++ b/hw/vendor/hpdcache/rtl/tb/rtl_conf.mk @@ -0,0 +1,142 @@ +## +# Copyright 2023,2024 CEA* +# *Commissariat a l'Energie Atomique et aux Energies Alternatives (CEA) +# Copyright 2025 Inria, Universite Grenoble-Alpes, TIMA +# +# SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 +# +# Licensed under the Solderpad Hardware License v 2.1 (the “License”); you +# may not use this file except in compliance with the License, or, at your +# option, the Apache License version 2.0. You may obtain a copy of the +# License at +# +# https://solderpad.org/licenses/SHL-2.1/ +# +# Unless required by applicable law or agreed to in writing, any work +# distributed under the License is distributed on an “AS IS” BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +## +## +# Author : Cesar Fuguet +# Date : October, 2024 +# Description: HPDCACHE Test Makefile +## +ifdef CONF_HPDCACHE_PA_WIDTH + CONF_DEFINES += -DCONF_HPDCACHE_PA_WIDTH=$(CONF_HPDCACHE_PA_WIDTH) +endif +ifdef CONF_HPDCACHE_SETS + CONF_DEFINES += -DCONF_HPDCACHE_SETS=$(CONF_HPDCACHE_SETS) +endif +ifdef CONF_HPDCACHE_WAYS + CONF_DEFINES += -DCONF_HPDCACHE_WAYS=$(CONF_HPDCACHE_WAYS) +endif +ifdef CONF_HPDCACHE_WORD_WIDTH + CONF_DEFINES += -DCONF_HPDCACHE_WORD_WIDTH=$(CONF_HPDCACHE_WORD_WIDTH) +endif +ifdef CONF_HPDCACHE_WORD_USER_WIDTH + CONF_DEFINES += -DCONF_HPDCACHE_WORD_USER_WIDTH=$(CONF_HPDCACHE_WORD_USER_WIDTH) +endif +ifdef CONF_HPDCACHE_CL_WORDS + CONF_DEFINES += -DCONF_HPDCACHE_CL_WORDS=$(CONF_HPDCACHE_CL_WORDS) +endif +ifdef CONF_HPDCACHE_REQ_WORDS + CONF_DEFINES += -DCONF_HPDCACHE_REQ_WORDS=$(CONF_HPDCACHE_REQ_WORDS) +endif +ifdef CONF_HPDCACHE_REQ_TRANS_ID_WIDTH + CONF_DEFINES += -DCONF_HPDCACHE_REQ_TRANS_ID_WIDTH=$(CONF_HPDCACHE_REQ_TRANS_ID_WIDTH) +endif +ifdef CONF_HPDCACHE_REQ_SRC_ID_WIDTH + CONF_DEFINES += -DCONF_HPDCACHE_REQ_SRC_ID_WIDTH=$(CONF_HPDCACHE_REQ_SRC_ID_WIDTH) +endif +ifdef CONF_HPDCACHE_VICTIM_SEL + CONF_DEFINES += -DCONF_HPDCACHE_VICTIM_SEL=$(CONF_HPDCACHE_VICTIM_SEL) +endif +ifdef CONF_HPDCACHE_DATA_WAYS_PER_RAM_WORD + CONF_DEFINES += -DCONF_HPDCACHE_DATA_WAYS_PER_RAM_WORD=$(CONF_HPDCACHE_DATA_WAYS_PER_RAM_WORD) +endif +ifdef CONF_HPDCACHE_DATA_SETS_PER_RAM + CONF_DEFINES += -DCONF_HPDCACHE_DATA_SETS_PER_RAM=$(CONF_HPDCACHE_DATA_SETS_PER_RAM) +endif +ifdef CONF_HPDCACHE_DATA_RAM_WBYTEENABLE + CONF_DEFINES += -DCONF_HPDCACHE_DATA_RAM_WBYTEENABLE=$(CONF_HPDCACHE_DATA_RAM_WBYTEENABLE) +endif +ifdef CONF_HPDCACHE_ACCESS_WORDS + CONF_DEFINES += -DCONF_HPDCACHE_ACCESS_WORDS=$(CONF_HPDCACHE_ACCESS_WORDS) +endif +ifdef CONF_HPDCACHE_WBUF_DIR_ENTRIES + CONF_DEFINES += -DCONF_HPDCACHE_WBUF_DIR_ENTRIES=$(CONF_HPDCACHE_WBUF_DIR_ENTRIES) +endif +ifdef CONF_HPDCACHE_WBUF_DATA_ENTRIES + CONF_DEFINES += -DCONF_HPDCACHE_WBUF_DATA_ENTRIES=$(CONF_HPDCACHE_WBUF_DATA_ENTRIES) +endif +ifdef CONF_HPDCACHE_WBUF_WORDS + CONF_DEFINES += -DCONF_HPDCACHE_WBUF_WORDS=$(CONF_HPDCACHE_WBUF_WORDS) +endif +ifdef CONF_HPDCACHE_WBUF_TIMECNT_WIDTH + CONF_DEFINES += -DCONF_HPDCACHE_WBUF_TIMECNT_WIDTH=$(CONF_HPDCACHE_WBUF_TIMECNT_WIDTH) +endif +ifdef CONF_HPDCACHE_WBUF_SEND_FEEDTHROUGH + CONF_DEFINES += -DCONF_HPDCACHE_WBUF_SEND_FEEDTHROUGH=$(CONF_HPDCACHE_WBUF_SEND_FEEDTHROUGH) +endif +ifdef CONF_HPDCACHE_MSHR_SETS + CONF_DEFINES += -DCONF_HPDCACHE_MSHR_SETS=$(CONF_HPDCACHE_MSHR_SETS) +endif +ifdef CONF_HPDCACHE_MSHR_WAYS + CONF_DEFINES += -DCONF_HPDCACHE_MSHR_WAYS=$(CONF_HPDCACHE_MSHR_WAYS) +endif +ifdef CONF_HPDCACHE_MSHR_WAYS_PER_RAM_WORD + CONF_DEFINES += -DCONF_HPDCACHE_MSHR_WAYS_PER_RAM_WORD=$(CONF_HPDCACHE_MSHR_WAYS_PER_RAM_WORD) +endif +ifdef CONF_HPDCACHE_MSHR_SETS_PER_RAM + CONF_DEFINES += -DCONF_HPDCACHE_MSHR_SETS_PER_RAM=$(CONF_HPDCACHE_MSHR_SETS_PER_RAM) +endif +ifdef CONF_HPDCACHE_MSHR_RAM_WBYTEENABLE + CONF_DEFINES += -DCONF_HPDCACHE_MSHR_RAM_WBYTEENABLE=$(CONF_HPDCACHE_MSHR_RAM_WBYTEENABLE) +endif +ifdef CONF_HPDCACHE_MSHR_USE_REGBANK + CONF_DEFINES += -DCONF_HPDCACHE_MSHR_USE_REGBANK=$(CONF_HPDCACHE_MSHR_USE_REGBANK) +endif +ifdef CONF_HPDCACHE_CBUF_ENTRIES + CONF_DEFINES += -DCONF_HPDCACHE_CBUF_ENTRIES=$(CONF_HPDCACHE_CBUF_ENTRIES) +endif +ifdef CONF_HPDCACHE_REFILL_FIFO_DEPTH + CONF_DEFINES += -DCONF_HPDCACHE_REFILL_FIFO_DEPTH=$(CONF_HPDCACHE_REFILL_FIFO_DEPTH) +endif +ifdef CONF_HPDCACHE_REFILL_CORE_RSP_FEEDTHROUGH + CONF_DEFINES += -DCONF_HPDCACHE_REFILL_CORE_RSP_FEEDTHROUGH=$(CONF_HPDCACHE_REFILL_CORE_RSP_FEEDTHROUGH) +endif +ifdef CONF_HPDCACHE_RTAB_ENTRIES + CONF_DEFINES += -DCONF_HPDCACHE_RTAB_ENTRIES=$(CONF_HPDCACHE_RTAB_ENTRIES) +endif +ifdef CONF_HPDCACHE_FLUSH_ENTRIES + CONF_DEFINES += -DCONF_HPDCACHE_FLUSH_ENTRIES=$(CONF_HPDCACHE_FLUSH_ENTRIES) +endif +ifdef CONF_HPDCACHE_FLUSH_FIFO_DEPTH + CONF_DEFINES += -DCONF_HPDCACHE_FLUSH_FIFO_DEPTH=$(CONF_HPDCACHE_FLUSH_FIFO_DEPTH) +endif +ifdef CONF_HPDCACHE_MEM_ADDR_WIDTH + CONF_DEFINES += -DCONF_HPDCACHE_MEM_ADDR_WIDTH=$(CONF_HPDCACHE_MEM_ADDR_WIDTH) +endif +ifdef CONF_HPDCACHE_MEM_ID_WIDTH + CONF_DEFINES += -DCONF_HPDCACHE_MEM_ID_WIDTH=$(CONF_HPDCACHE_MEM_ID_WIDTH) +endif +ifdef CONF_HPDCACHE_MEM_DATA_WIDTH + CONF_DEFINES += -DCONF_HPDCACHE_MEM_DATA_WIDTH=$(CONF_HPDCACHE_MEM_DATA_WIDTH) +endif +ifdef CONF_HPDCACHE_WT_ENABLE + CONF_DEFINES += -DCONF_HPDCACHE_WT_ENABLE=$(CONF_HPDCACHE_WT_ENABLE) +endif +ifdef CONF_HPDCACHE_WB_ENABLE + CONF_DEFINES += -DCONF_HPDCACHE_WB_ENABLE=$(CONF_HPDCACHE_WB_ENABLE) +endif +ifdef CONF_HPDCACHE_USER_ENABLE + CONF_DEFINES += -DCONF_HPDCACHE_USER_ENABLE=$(CONF_HPDCACHE_USER_ENABLE) +endif +ifdef CONF_HPDCACHE_CAP_AMO_ENABLE + CONF_DEFINES += -DCONF_HPDCACHE_CAP_AMO_ENABLE=$(CONF_HPDCACHE_CAP_AMO_ENABLE) +endif +ifdef CONF_HPDCACHE_LOW_LATENCY + CONF_DEFINES += -DCONF_HPDCACHE_LOW_LATENCY=$(CONF_HPDCACHE_LOW_LATENCY) +endif diff --git a/hw/vendor/hpdcache/rtl/tb/sc_verif_lib/include/agent.h b/hw/vendor/hpdcache/rtl/tb/sc_verif_lib/include/agent.h new file mode 100644 index 00000000..7ca593d1 --- /dev/null +++ b/hw/vendor/hpdcache/rtl/tb/sc_verif_lib/include/agent.h @@ -0,0 +1,78 @@ +/** + * Copyright 2023,2024 CEA* + * *Commissariat a l'Energie Atomique et aux Energies Alternatives (CEA) + * + * SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 + * + * Licensed under the Solderpad Hardware License v 2.1 (the “License”); you + * may not use this file except in compliance with the License, or, at your + * option, the Apache License version 2.0. You may obtain a copy of the + * License at + * + * https://solderpad.org/licenses/SHL-2.1/ + * + * Unless required by applicable law or agreed to in writing, any work + * distributed under the License is distributed on an “AS IS” BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +/** + * Author : Cesar Fuguet + * Date : October, 2024 + * Description: Generic class definition of the agent + */ +#ifndef __AGENT_H__ +#define __AGENT_H__ + +#include +#include +#include +#include +#include "transaction.h" +#include "sequence.h" + +class Agent : public sc_core::sc_module +{ + typedef std::list> Sequence_list; + typedef Sequence_list::const_iterator Sequence_iterator; + +public: + sc_core::sc_in clk_i; + sc_core::sc_in rst_ni; + + Agent(sc_core::sc_module_name nm) + : sc_module(nm) + , clk_i("clk_i") + , rst_ni("rst_ni") + , transaction_fifo("transaction_fifo") + , transaction_ret("transaction_ret") {/*empty*/} + + virtual ~Agent() { + slist.clear(); + } + + void add_sequence(std::shared_ptr seq) + { + seq->clk_i (clk_i); + seq->rst_ni (rst_ni); + seq->transaction_fifo_o (transaction_fifo); + seq->transaction_ret_i (transaction_ret); + + slist.push_back(seq); + } + + Sequence_list get_sequence_list() + { + return slist; + } + +protected: + sc_core::sc_fifo> transaction_fifo; + sc_core::sc_fifo transaction_ret; + +private: + Sequence_list slist; +}; + +#endif // __AGENT_H__ diff --git a/hw/vendor/hpdcache/rtl/tb/sc_verif_lib/include/driver.h b/hw/vendor/hpdcache/rtl/tb/sc_verif_lib/include/driver.h new file mode 100644 index 00000000..6c0b228b --- /dev/null +++ b/hw/vendor/hpdcache/rtl/tb/sc_verif_lib/include/driver.h @@ -0,0 +1,55 @@ +/** + * Copyright 2023,2024 CEA* + * *Commissariat a l'Energie Atomique et aux Energies Alternatives (CEA) + * + * SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 + * + * Licensed under the Solderpad Hardware License v 2.1 (the “License”); you + * may not use this file except in compliance with the License, or, at your + * option, the Apache License version 2.0. You may obtain a copy of the + * License at + * + * https://solderpad.org/licenses/SHL-2.1/ + * + * Unless required by applicable law or agreed to in writing, any work + * distributed under the License is distributed on an “AS IS” BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +/** + * Author : Cesar Fuguet + * Date : October, 2024 + * Description: Generic class definition of the driver + */ +#ifndef __DRIVER_H__ +#define __DRIVER_H__ + +#include +#include +#include "driver.h" + +class Transaction; + +class Driver : public sc_core::sc_module +{ +public: + sc_core::sc_in < bool > clk_i; + sc_core::sc_in < bool > rst_ni; + sc_core::sc_fifo_in < std::shared_ptr > transaction_fifo_i; + sc_core::sc_fifo_out < uint64_t > transaction_ret_o; + + Driver(sc_core::sc_module_name nm) + : sc_module(nm) + , clk_i("clk_i") + , rst_ni("rst_ni") + , transaction_fifo_i("transaction_fifo_i") + , transaction_ret_o("transaction_ret_o") + { + }; + + virtual ~Driver() {} +}; + +#endif // __DRIVER_H__ + diff --git a/hw/vendor/hpdcache/rtl/tb/sc_verif_lib/include/logger.h b/hw/vendor/hpdcache/rtl/tb/sc_verif_lib/include/logger.h new file mode 100644 index 00000000..1423b7f6 --- /dev/null +++ b/hw/vendor/hpdcache/rtl/tb/sc_verif_lib/include/logger.h @@ -0,0 +1,95 @@ +/** + * Copyright 2023,2024 CEA* + * *Commissariat a l'Energie Atomique et aux Energies Alternatives (CEA) + * + * SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 + * + * Licensed under the Solderpad Hardware License v 2.1 (the “License”); you + * may not use this file except in compliance with the License, or, at your + * option, the Apache License version 2.0. You may obtain a copy of the + * License at + * + * https://solderpad.org/licenses/SHL-2.1/ + * + * Unless required by applicable law or agreed to in writing, any work + * distributed under the License is distributed on an “AS IS” BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +/** + * Author : Cesar Fuguet + * Date : October, 2024 + * Description: Class definition of the Logger + */ +#ifndef __LOGGER_H__ +#define __LOGGER_H__ + +#include +#include + +class Logger +{ +private: + static int log_level; + +public: + enum { + LOG_NONE = 0, + + LOG_LOW = 1, + LOG_MEDIUM = 2, + LOG_HIGH = 3, + + LOG_WARNING = LOG_LOW, + LOG_INFO = LOG_MEDIUM, + LOG_DEBUG = LOG_HIGH + } log_level_e; + + Logger() {} + virtual ~Logger() {} + + static void set_log_level(int level) + { + log_level = level; + } + + static int get_log_level() + { + return log_level; + } + + static int is_warning_enabled() + { + return log_level >= LOG_WARNING; + } + + static int is_info_enabled() + { + return log_level >= LOG_INFO; + } + + static int is_debug_enabled() + { + return log_level >= LOG_DEBUG; + } + + static void info(const std::string &msg) + { + if (log_level >= LOG_INFO) std::cout << msg; + } + + static void warning(const std::string &msg) + { + if (log_level >= LOG_WARNING) std::cout << msg; + } + + static void debug(const std::string &msg) + { + if (log_level >= LOG_DEBUG) std::cout << msg; + } +}; + +int Logger::log_level = Logger::LOG_INFO; + +#endif /* __LOGGER_H__ */ diff --git a/hw/vendor/hpdcache/rtl/tb/sc_verif_lib/include/sequence.h b/hw/vendor/hpdcache/rtl/tb/sc_verif_lib/include/sequence.h new file mode 100644 index 00000000..f7b5b945 --- /dev/null +++ b/hw/vendor/hpdcache/rtl/tb/sc_verif_lib/include/sequence.h @@ -0,0 +1,82 @@ +/** + * Copyright 2023,2024 CEA* + * *Commissariat a l'Energie Atomique et aux Energies Alternatives (CEA) + * + * SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 + * + * Licensed under the Solderpad Hardware License v 2.1 (the “License”); you + * may not use this file except in compliance with the License, or, at your + * option, the Apache License version 2.0. You may obtain a copy of the + * License at + * + * https://solderpad.org/licenses/SHL-2.1/ + * + * Unless required by applicable law or agreed to in writing, any work + * distributed under the License is distributed on an “AS IS” BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +/** + * Author : Cesar Fuguet + * Date : October, 2024 + * Description: Generic class definition of the sequence + */ +#ifndef __SEQUENCE_H__ +#define __SEQUENCE_H__ + +#include +#include +#include "transaction_pool.h" + +class Transaction; + +class Sequence : public sc_core::sc_module +{ +public: + sc_core::sc_in clk_i; + sc_core::sc_in rst_ni; + sc_core::sc_fifo_out> transaction_fifo_o; + sc_core::sc_fifo_in transaction_ret_i; + + Sequence(sc_core::sc_module_name nm) + : sc_module(nm) + , clk_i("clk_i") + , rst_ni("rst_ni") + , transaction_fifo_o("transaction_fifo_o") + , transaction_ret_i("transaction_ret_i") + { + } + + virtual ~Sequence() {}; + + template + std::shared_ptr acquire_transaction() + { + std::shared_ptr ret = transaction_pool.acquire_transaction(); + *ret = T(); // initialize + return ret; + } + + template + std::shared_ptr acquire_transaction(const T& init) + { + std::shared_ptr ret = transaction_pool.acquire_transaction(); + uint64_t id = ret->get_id(); + *ret = init; // copy + ret->set_id(id); // restore new id + return ret; + } + + template + void release_transaction(std::shared_ptr t) + { + transaction_pool.release_transaction(t); + } + +protected: + Transaction_pool transaction_pool; +}; + +#endif // __SEQUENCE_H__ + diff --git a/hw/vendor/hpdcache/rtl/tb/sc_verif_lib/include/transaction.h b/hw/vendor/hpdcache/rtl/tb/sc_verif_lib/include/transaction.h new file mode 100644 index 00000000..54264474 --- /dev/null +++ b/hw/vendor/hpdcache/rtl/tb/sc_verif_lib/include/transaction.h @@ -0,0 +1,51 @@ +/** + * Copyright 2023,2024 CEA* + * *Commissariat a l'Energie Atomique et aux Energies Alternatives (CEA) + * + * SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 + * + * Licensed under the Solderpad Hardware License v 2.1 (the “License”); you + * may not use this file except in compliance with the License, or, at your + * option, the Apache License version 2.0. You may obtain a copy of the + * License at + * + * https://solderpad.org/licenses/SHL-2.1/ + * + * Unless required by applicable law or agreed to in writing, any work + * distributed under the License is distributed on an “AS IS” BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +/** + * Author : Cesar Fuguet + * Date : October, 2024 + * Description: Class definition of the Transaction + */ +#ifndef __TRANSACTION_H__ +#define __TRANSACTION_H__ + +#include + +class Transaction +{ +public: + Transaction() : __id(0) {} + virtual ~Transaction() {} + virtual const std::string to_string() const = 0; + + void set_id(uint64_t id) + { + __id = id; + }; + + uint64_t get_id() { + return __id; + }; + +protected: + uint64_t __id; // transaction ID +}; + +#endif /* __TRANSACTION_H__ */ + diff --git a/hw/vendor/hpdcache/rtl/tb/sc_verif_lib/include/transaction_pool.h b/hw/vendor/hpdcache/rtl/tb/sc_verif_lib/include/transaction_pool.h new file mode 100644 index 00000000..c7d93c09 --- /dev/null +++ b/hw/vendor/hpdcache/rtl/tb/sc_verif_lib/include/transaction_pool.h @@ -0,0 +1,108 @@ +/** + * Copyright 2023,2024 CEA* + * *Commissariat a l'Energie Atomique et aux Energies Alternatives (CEA) + * + * SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 + * + * Licensed under the Solderpad Hardware License v 2.1 (the “License”); you + * may not use this file except in compliance with the License, or, at your + * option, the Apache License version 2.0. You may obtain a copy of the + * License at + * + * https://solderpad.org/licenses/SHL-2.1/ + * + * Unless required by applicable law or agreed to in writing, any work + * distributed under the License is distributed on an “AS IS” BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +/** + * Author : Cesar Fuguet + * Date : October, 2024 + * Description: Generic class definition of the transaction pool + */ +#ifndef __TRANSACTION_POOL_H__ +#define __TRANSACTION_POOL_H__ + +#include +#include +#include +#include +#include +#include +#include +#include "transaction.h" + +class Transaction_pool +{ + typedef std::list> Transaction_list; + +public: + Transaction_pool(int trans_bundle_sz = 10) + : trans_bundle_sz(trans_bundle_sz) + , nb_trans(0), nb_allocated(0), nb_release(0) + { + } + + template + std::shared_ptr acquire_transaction() + { + std::shared_ptr ret; + + static_assert(std::is_convertible::value, + "T must derive from Transaction"); + + // no available transaction, create some some new ones + if (pool.empty()) { + for (int i = 0; i < trans_bundle_sz; i++) { + std::shared_ptr t = std::make_shared(); + assert (t != nullptr); + nb_allocated++; + pool.push_back(t); + } + } + + // return a free transaction object + ret = pool.front(); + pool.pop_front(); + + // update the transaction ID + ret->set_id(nb_trans++); + + return std::dynamic_pointer_cast(ret); + } + + template + void release_transaction(std::shared_ptr &t) + { + static_assert(std::is_convertible::value, + "T must derive from Transaction"); + + nb_release++; + pool.push_back(std::dynamic_pointer_cast(t)); + } + + ~Transaction_pool() { + std::stringstream ss; + + pool.clear(); + + ss << "TRANSACTION POOL" << std::endl + << "Number of allocated transactions: " << nb_allocated << std::endl + << "Number of acquired transactions : " << nb_trans << std::endl + << "Number of released transactions : " << nb_release << std::endl; + + Logger::debug(ss.str()); + } + +private: + Transaction_list pool; + int trans_bundle_sz; + uint64_t nb_trans; + uint64_t nb_allocated; + uint64_t nb_release; +}; + +#endif // __TRANSACTION_POOL_H__ + diff --git a/hw/vendor/hpdcache/rtl/tb/sc_verif_lib/modules/generic_cache/include/generic_cache_data.h b/hw/vendor/hpdcache/rtl/tb/sc_verif_lib/modules/generic_cache/include/generic_cache_data.h new file mode 100644 index 00000000..ab5134d9 --- /dev/null +++ b/hw/vendor/hpdcache/rtl/tb/sc_verif_lib/modules/generic_cache/include/generic_cache_data.h @@ -0,0 +1,123 @@ +/** + * Copyright 2023,2024 CEA* + * *Commissariat a l'Energie Atomique et aux Energies Alternatives (CEA) + * + * SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 + * + * Licensed under the Solderpad Hardware License v 2.1 (the “License”); you + * may not use this file except in compliance with the License, or, at your + * option, the Apache License version 2.0. You may obtain a copy of the + * License at + * + * https://solderpad.org/licenses/SHL-2.1/ + * + * Unless required by applicable law or agreed to in writing, any work + * distributed under the License is distributed on an “AS IS” BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +/** + * Author : Cesar Fuguet + * Date : October, 2024 + * Description: Class definition of a generic cache data + */ +#ifndef __GENERIC_CACHE_DATA_H__ +#define __GENERIC_CACHE_DATA_H__ + +#include +#include + +class GenericCacheData +{ +public: + + GenericCacheData(const std::string &name, + size_t nways, + size_t nsets, + size_t nwords) + : name_m(name), + data_m(nways*nsets*nwords, 0), + ways_m(nways), + sets_m(nsets), + words_m(nwords) + { + sc_assert(nsets > 0); + sc_assert(nways > 0); + sc_assert(nwords > 0); + } + + ~GenericCacheData() + { + } + + inline uint64_t getAddrSet(uint64_t addr) + { + return (addr / (words_m * 8)) % sets_m; + } + + inline uint64_t getAddrWord(uint64_t addr) + { + return (addr / 8) % words_m; + } + + inline void write(uint64_t wdata, uint8_t be, size_t way, size_t set, size_t word) + { + uint64_t mask = 0; + for (int i = 0; i < 8; i++) { + if ((be >> i) & 0x1) { + mask |= 0xff << (i*8); + } + } + + uint64_t prev = getCacheData(way, set, word); + getCacheData(way, set, word) = (prev & ~mask) | (wdata & mask);; + +#ifdef DEBUG_GENERIC_CACHE_DATA + std::cout << "DEBUG: cache data : writing " + << "0x" << std::hex << getCacheData(way, set, word) << std::dec + << " / set = " + << set + << " / way = " + << way + << " / word = " + << word + << std::endl; +#endif + } + + inline uint64_t read(size_t way, size_t set, size_t word) + { + uint64_t ret = getCacheData(way, set, word); + +#ifdef DEBUG_GENERIC_CACHE_DATA + std::cout << "DEBUG: cache data : reading " + << "0x" << std::hex << ret << std::dec + << " / set = " + << set + << " / way = " + << way + << " / word = " + << word + << std::endl; +#endif + + return ret; + } + +protected: + + std::string name_m; + std::vector data_m; + + size_t ways_m; + size_t sets_m; + size_t words_m; + + inline uint64_t &getCacheData(size_t way, size_t set, size_t word) + { + return data_m[way*(sets_m*words_m) + (set*words_m) + word]; + } +}; + +#endif diff --git a/hw/vendor/hpdcache/rtl/tb/sc_verif_lib/modules/generic_cache/include/generic_cache_directory_base.h b/hw/vendor/hpdcache/rtl/tb/sc_verif_lib/modules/generic_cache/include/generic_cache_directory_base.h new file mode 100644 index 00000000..e9686e91 --- /dev/null +++ b/hw/vendor/hpdcache/rtl/tb/sc_verif_lib/modules/generic_cache/include/generic_cache_directory_base.h @@ -0,0 +1,239 @@ +/** + * Copyright 2023,2024 CEA* + * *Commissariat a l'Energie Atomique et aux Energies Alternatives (CEA) + * + * SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 + * + * Licensed under the Solderpad Hardware License v 2.1 (the “License”); you + * may not use this file except in compliance with the License, or, at your + * option, the Apache License version 2.0. You may obtain a copy of the + * License at + * + * https://solderpad.org/licenses/SHL-2.1/ + * + * Unless required by applicable law or agreed to in writing, any work + * distributed under the License is distributed on an “AS IS” BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +/** + * Author : Cesar Fuguet + * Date : October, 2024 + * Description: Class definition of a generic cache directory + */ +#ifndef __GENERIC_CACHE_DIRECTORY_BASE_H__ +#define __GENERIC_CACHE_DIRECTORY_BASE_H__ + +#include +#include + +class GenericCacheDirectoryBase +{ +protected: + + std::string name_m; + uint64_t *tag_m; + bool *val_m; + + size_t ways_m; + size_t sets_m; + size_t bytes_m; + +public: + + GenericCacheDirectoryBase(const std::string &name, + size_t nways, + size_t nsets, + size_t nbytes) + : name_m(name), + ways_m(nways), + sets_m(nsets), + bytes_m(nbytes) + { + sc_assert(nsets > 0); + sc_assert(nways > 0); + sc_assert(nbytes > 0); + + tag_m = new uint64_t [nways*nsets]; + val_m = new bool [nways*nsets]; + } + + ~GenericCacheDirectoryBase() + { + delete [] tag_m; + delete [] val_m; + } + + inline const size_t& getWays() const + { + return ways_m; + } + + inline const size_t& getSets() const + { + return sets_m; + } + + inline const size_t& getBytesPerLine() const + { + return bytes_m; + } + + inline uint64_t &getCacheTag(size_t way, size_t set) + { + return tag_m[(way*sets_m) + set]; + } + + inline bool &getCacheValid(size_t way, size_t set) + { + return val_m[(way*sets_m) + set]; + } + + inline uint64_t getNline(uint64_t tag, size_t set) + { + return tag*sets_m + set; + } + + inline uint64_t getNline(uint64_t addr) + { + return addr / bytes_m; + } + + inline uint64_t getAddr(uint64_t tag, size_t set) + { + return getNline(tag, set) * bytes_m; + } + + inline uint64_t getAddrTag(uint64_t addr) + { + return addr / (sets_m * bytes_m); + } + + inline uint64_t getAddrSet(uint64_t addr) + { + return (addr / bytes_m) % sets_m; + } + + void reset() + { + for (unsigned int way = 0; way < ways_m; way++) { + for (unsigned int set = 0; set < sets_m; set++) { + getCacheValid(way, set) = false; + getCacheTag(way, set) = 0; + } + } + } + + bool hit(uint64_t addr, size_t *hit_way, size_t *hit_set) + { + uint64_t tag; + size_t set; + + set = getAddrSet(addr); + tag = getAddrTag(addr); + for (size_t way = 0; way < ways_m; way++) + { + if (getCacheValid(way, set) && (tag == getCacheTag(way, set))) + { + if (hit_way != nullptr) { + *hit_way = way; + } + if (hit_set != nullptr) { + *hit_set = set; + } + return true; + } + } + if (hit_way != nullptr) { + *hit_way = UINT_MAX; + } + if (hit_set != nullptr) { + *hit_set = UINT_MAX; + } + return false; + } + + inline bool inval(size_t way, size_t set) + { + bool ret = getCacheValid(way, set); + getCacheValid(way,set) = false; + return ret; + } + + inline bool inval(uint64_t addr) + { + uint64_t tag; + size_t set; + + set = getAddrSet(addr); + tag = getAddrTag(addr); + + for (size_t way = 0; way < ways_m; way++) { + if (getCacheTag(way,set) == tag) { + getCacheValid(way,set) = false; + return true; + } + } + return false; + } + + virtual bool repl(uint64_t addr, + uint64_t *victim_tag, + size_t *victim_way, + size_t *victim_set) = 0; + + virtual void replUpdate(size_t way, + size_t set) = 0; + + inline bool alloc(uint64_t addr, + size_t *alloc_way, + size_t *alloc_set, + uint64_t *victim_tag) + { + return repl(addr, victim_tag, alloc_way, alloc_set); + } + + bool access(uint64_t addr, + size_t *hit_way, + size_t *hit_set, + bool update_repl_flags = true) + { + size_t _hit_way; + size_t _hit_set; + + if (hit(addr, &_hit_way, &_hit_set)) { + if (update_repl_flags) { + replUpdate(_hit_way, _hit_set); + } + if (hit_way != nullptr) *hit_way = _hit_way; + if (hit_set != nullptr) *hit_set = _hit_set; + return true; + } + + if (hit_way != nullptr) *hit_way = UINT_MAX; + if (hit_set != nullptr) *hit_set = UINT_MAX; + return false; + } + + void printTrace() + { + std::cout << name_m << std::endl; + + for (size_t way = 0; way < ways_m ; way++) + { + for (size_t set = 0; set < sets_m; set++) + { + if (getCacheValid(way, set)) { + std::cout << " set = " << set + << " / way = " << way + << " / tag = " << std::hex << getCacheTag(way, set) << std::dec + << std::endl; + } + } + } + } + +}; + +#endif diff --git a/hw/vendor/hpdcache/rtl/tb/sc_verif_lib/modules/generic_cache/include/generic_cache_directory_plru.h b/hw/vendor/hpdcache/rtl/tb/sc_verif_lib/modules/generic_cache/include/generic_cache_directory_plru.h new file mode 100644 index 00000000..36bf803a --- /dev/null +++ b/hw/vendor/hpdcache/rtl/tb/sc_verif_lib/modules/generic_cache/include/generic_cache_directory_plru.h @@ -0,0 +1,150 @@ +/** + * Copyright 2023,2024 CEA* + * *Commissariat a l'Energie Atomique et aux Energies Alternatives (CEA) + * + * SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 + * + * Licensed under the Solderpad Hardware License v 2.1 (the “License”); you + * may not use this file except in compliance with the License, or, at your + * option, the Apache License version 2.0. You may obtain a copy of the + * License at + * + * https://solderpad.org/licenses/SHL-2.1/ + * + * Unless required by applicable law or agreed to in writing, any work + * distributed under the License is distributed on an “AS IS” BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +/** + * Author : Cesar Fuguet + * Date : October, 2024 + * Description: Class definition of a cache directory using a Pseudo-LRU + * replacement policy + */ +#ifndef __GENERIC_CACHE_DIRECTORY_PLRU_H__ +#define __GENERIC_CACHE_DIRECTORY_PLRU_H__ + +#include +#include + +#include "generic_cache_directory_base.h" + +class GenericCacheDirectoryPlru : public GenericCacheDirectoryBase +{ + bool *plru_m; + +public: + + GenericCacheDirectoryPlru(const std::string &name, + size_t nways, + size_t nsets, + size_t nbytes) + : GenericCacheDirectoryBase(name, nways, nsets, nbytes) + { + plru_m = new bool [nways*nsets]; + } + + ~GenericCacheDirectoryPlru() + { + delete [] plru_m; + } + + bool &getCachePlru(size_t way, size_t set) + { + return plru_m[way*sets_m + set]; + } + + void reset() + { + GenericCacheDirectoryBase::reset(); + + for (size_t way = 0; way < ways_m; way++) { + for (size_t set = 0; set < sets_m; set++) { + getCachePlru(way, set) = false; + } + } + } + + virtual bool repl(uint64_t addr, + uint64_t* victim_tag, + size_t* victim_way, + size_t* victim_set) + { + uint64_t tag; + size_t set; + + set = getAddrSet(addr); + tag = getAddrTag(addr); + + // look if there is an empty way + for (size_t way = 0; way < ways_m; way++) { + if (!getCacheValid(way, set)) { + if (victim_tag != nullptr) { + *victim_tag = 0; + } + if (victim_way != nullptr) { + *victim_way = way; + } + if (victim_set != nullptr) { + *victim_set = set; + } + // set the new information + getCacheValid(way, set) = true; + getCacheTag(way, set) = tag; + replUpdate(way, set); + return false; + } + } + + // look for a non-recently used way (plru bit unset) + for (size_t way = 0; way < ways_m; way++) { + if (!getCachePlru(way, set)) { + // get victim entry informations + if (victim_tag != nullptr) { + *victim_tag = getCacheTag(way, set); + } + if (victim_way != nullptr) { + *victim_way = way; + } + if (victim_set != nullptr) { + *victim_set = set; + } + + // set the new information + getCacheTag(way, set) = tag; + replUpdate(way, set); + return true; + } + } + + sc_assert(false && "error: all plru bits are set"); + return false; + } + + virtual void replUpdate(size_t way, size_t set) + { + bool reset; + + // set the accessed way to recently used + getCachePlru(way, set) = true; + + // check if all recently used bits are set + reset = true; + for (int _way = 0; _way < ways_m; _way++) { + reset = reset && getCachePlru(_way, set); + } + + // if all PLRU bits are set, reset them all but the last accessed one + if (reset) { + for (int _way = 0; _way < ways_m; _way++) { + if (_way != way) { + getCachePlru(_way, set) = false; + } + } + } + } +}; + +#endif /* __GENERIC_CACHE_DIRECTORY_PLRU_H__ */ diff --git a/hw/vendor/hpdcache/rtl/tb/sc_verif_lib/modules/mem_model/include/mem_model.h b/hw/vendor/hpdcache/rtl/tb/sc_verif_lib/modules/mem_model/include/mem_model.h new file mode 100644 index 00000000..e3919f63 --- /dev/null +++ b/hw/vendor/hpdcache/rtl/tb/sc_verif_lib/modules/mem_model/include/mem_model.h @@ -0,0 +1,140 @@ +/** + * Copyright 2023,2024 CEA* + * *Commissariat a l'Energie Atomique et aux Energies Alternatives (CEA) + * + * SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 + * + * Licensed under the Solderpad Hardware License v 2.1 (the “License”); you + * may not use this file except in compliance with the License, or, at your + * option, the Apache License version 2.0. You may obtain a copy of the + * License at + * + * https://solderpad.org/licenses/SHL-2.1/ + * + * Unless required by applicable law or agreed to in writing, any work + * distributed under the License is distributed on an “AS IS” BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +/** + * Author : Cesar Fuguet + * Date : October, 2024 + * Description: Generic Memory model + */ +#ifndef __MEM_MODEL_H__ +#define __MEM_MODEL_H__ + +#include +#include + +class mem_model +{ +public: + + enum mem_model_init_mode_e + { + MEM_MODEL_INIT_VALUE, + MEM_MODEL_INIT_RANDOM + }; + + typedef std::map mem_array_t; + typedef std::pair mem_array_pair_t; + + mem_model(const char* name, + mem_model_init_mode_e init_mode = MEM_MODEL_INIT_RANDOM, + uint64_t init_val = 0) : + name_m(name), + init_mode_m(MEM_MODEL_INIT_RANDOM), + init_val_m(init_val) + { + } + + ~mem_model() + { + } + + mem_array_t *getMemPtr() + { + return &mem_array_m; + } + + const char* getName() + { + return name_m.c_str(); + } + + uint64_t readMemory(uint64_t word_addr) + { + mem_array_t::const_iterator it; + + // check if the target address was already accessed + it = mem_array_m.find(word_addr); + if (it != mem_array_m.end()) { + return it->second; + } + + // addr not found, thus add it to the memory and return the init number + uint64_t value; + switch (init_mode_m) { + case MEM_MODEL_INIT_RANDOM: + value = ((uint64_t)rand() << 32) | (uint64_t)rand(); + break; + case MEM_MODEL_INIT_VALUE: + value = init_val_m; + break; + } + mem_array_m.insert(mem_array_pair_t(word_addr, value)); + return value; + } + + void writeMemory(uint64_t word_addr, uint64_t data, uint64_t mask) + { + mem_array_t::iterator it; + + // check if the target address was already accessed + it = mem_array_m.find(word_addr); + if (it != mem_array_m.end()) { + it->second = (it->second & ~mask) | (data & mask); + return; + } + + // address not found, thus add it to the memory and merge the + // written data with random data + uint64_t value; + switch (init_mode_m) { + case MEM_MODEL_INIT_RANDOM: + value = ((uint64_t)rand() << 32) | (uint64_t)rand(); + break; + case MEM_MODEL_INIT_VALUE: + value = init_val_m; + break; + } + uint64_t newval = (value & ~mask) | (data & mask); + mem_array_m.insert(mem_array_pair_t(word_addr, newval)); + } + + static uint64_t beToMask(uint8_t be) + { + uint64_t mask; + + // compute the write mask + mask = 0; + for (int j = 0; j < 8; j++) { + if ((be >> j) & 1) { + mask |= (0xffull) << (j*8); + } + } + + return mask; + } + +protected: + + std::string name_m; + mem_array_t mem_array_m; + mem_model_init_mode_e init_mode_m; + uint64_t init_val_m; +}; + +#endif /* __MEM_MODEL_H__ */ diff --git a/hw/vendor/hpdcache/rtl/tb/sc_verif_lib/modules/mem_model/include/ram_model.h b/hw/vendor/hpdcache/rtl/tb/sc_verif_lib/modules/mem_model/include/ram_model.h new file mode 100644 index 00000000..97160e69 --- /dev/null +++ b/hw/vendor/hpdcache/rtl/tb/sc_verif_lib/modules/mem_model/include/ram_model.h @@ -0,0 +1,138 @@ +/** + * Copyright 2023,2024 CEA* + * *Commissariat a l'Energie Atomique et aux Energies Alternatives (CEA) + * + * SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 + * + * Licensed under the Solderpad Hardware License v 2.1 (the “License”); you + * may not use this file except in compliance with the License, or, at your + * option, the Apache License version 2.0. You may obtain a copy of the + * License at + * + * https://solderpad.org/licenses/SHL-2.1/ + * + * Unless required by applicable law or agreed to in writing, any work + * distributed under the License is distributed on an “AS IS” BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +/** + * Author : Cesar Fuguet + * Date : October, 2024 + * Description: Generic RAM model + */ +#ifndef __RAM_MODEL_H__ +#define __RAM_MODEL_H__ + +#include +#include +#include +#include + +template +class ram_model +{ +public: + + ram_model(const char* name) : name_m(name) + { + mem_m = std::make_shared>(); + bmap_m = std::make_shared>(); + + memset(bmap_m->data(), 0, N/8); + } + + ~ram_model() + { + } + + uint8_t* getMemPtr() + { + return mem_m->data(); + } + + const char* getName() + { + return name_m.c_str(); + } + + void read(uint8_t *buf, size_t n, uint64_t addr, uint8_t *valid = nullptr) + { + uint64_t off = addr % N; + + if (valid != nullptr) { + memset(valid, 0, (n + 7)/8); + } + + for (size_t i = 0; i < n; ++i) { + uint8_t rdata; + rdata = (*mem_m)[off + i]; + if (valid != nullptr) { + if (getBmap(off + i)) { + setBit(valid[i/8], i % 8); + } + } + buf[i] = rdata; + +#if DEBUG + std::cout << "reading @0x" + << std::hex + << off + i + << " / rdata = 0x" + << (unsigned)rdata + << std::dec << std::endl; +#endif + } + } + + void write(const uint8_t *buf, const uint8_t *be, size_t n, uint64_t addr) + { + uint64_t off = addr % N; + for (size_t i = 0; i < n; ++i) { + if (getBit(be[i / 8], i % 8)) { + (*mem_m)[off + i] = buf[i]; + setBmap(off + i); + +#if DEBUG + std::cout << "writing @0x" + << std::hex + << off + i + << " / wdata = 0x" + << (unsigned)buf[i] + << std::dec << std::endl; +#endif + } + } + } + + inline bool getBmap(uint64_t addr) const + { + uint64_t off = addr % N; + return getBit((*bmap_m)[off / 8], off % 8) ? true : false; + } + +protected: + + static inline int getBit(uint8_t byte, size_t pos) + { + return (byte >> pos) & 0x1; + } + + static inline void setBit(uint8_t &byte, size_t pos) + { + byte |= (1 << pos); + } + + inline void setBmap(uint64_t addr) + { + uint64_t off = addr % N; + setBit((*bmap_m)[off / 8], off % 8); + } + + std::string name_m; + std::shared_ptr> mem_m; + std::shared_ptr> bmap_m; +}; + +#endif /* __ram_model_H__ */ diff --git a/hw/vendor/hpdcache/rtl/tb/scripts/perl5/Text/Aligner.pm b/hw/vendor/hpdcache/rtl/tb/scripts/perl5/Text/Aligner.pm new file mode 100644 index 00000000..fdc2c69d --- /dev/null +++ b/hw/vendor/hpdcache/rtl/tb/scripts/perl5/Text/Aligner.pm @@ -0,0 +1,486 @@ +# Text::Aligner - Align text in columns +package Text::Aligner; + +use strict; +use warnings; + +use 5.008; + +BEGIN { + use Exporter (); + use vars qw ($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS); + $VERSION = '0.13'; + @ISA = qw (Exporter); + @EXPORT = qw (); + @EXPORT_OK = qw ( align); + %EXPORT_TAGS = (); +} + +# this is a non-method, and currently the only user interface +sub align ($@) { + my $ali = Text::Aligner->new( shift); + $ali->_alloc( map ref eq 'SCALAR' ? $$_ : $_, @_); + if ( defined wantarray ) { + my @just = map $ali->_justify( ref eq 'SCALAR' ? $$_ : $_), @_; + return @just if wantarray; + return join "\n", @just, ''; + } else { + for ( @_ ) { + $_ = $ali->_justify( $_) for ref eq 'SCALAR' ? $$_ : $_; # one-shot + } + } +} + +### class Text::Aligner + +sub _new { # internal constructor + my $class = shift; + my ( $width, $pos) = @_; # both method-or-coderef (this is very general) + bless { + width => $width, + pos => $pos, + left => Text::Aligner::MaxKeeper->new, + right => Text::Aligner::MaxKeeper->new, + }, $class; +} + +# Construct an aligner +sub new { + my ( $class, $spec) = @_; + $spec ||= 0; # left alignment is default + my $al; + if ( !ref( $spec) and $spec =~ s/^auto/num/ ) { + $al = Text::Aligner::Auto->_new( $spec); + } else { + $al = $class->_new( _compile_alispec( $spec)); + } + $al; +} + +# return left and right field widths for an object +sub _measure0 { + my $al = shift; + my $obj = shift; + $obj = '' unless defined $obj; + my ( $w, $p); + if ( ref $obj ) { + ( $w, $p) = ( $obj->$al->{ width}->(), $obj->$al->{ pos}->() ); + } else { + ( $w, $p) = ( $al->{ width}->( $obj), $al->{ pos}->( $obj) ); + } + $_ ||= 0 for $w, $p; + ( $p, $w - $p); +} + +use Term::ANSIColor 2.02; + +# return left and right field widths for an object +sub _measure { + my $al = shift; + my $obj = shift; + $obj = '' unless defined $obj; + my ( $wmeth, $pmeth) = @{ $al}{ qw( width pos)}; + + # support colorized strings + $obj = Term::ANSIColor::colorstrip($obj) unless ref $obj; + + my $w = ref $wmeth ? $wmeth->( $obj) : $obj->$wmeth; + my $p = ref $pmeth ? $pmeth->( $obj) : $obj->$pmeth; + $_ ||= 0 for $w, $p; + ( $p, $w - $p); +} + +# Return left and right maxima, or nothing if the aligner is empty +sub _status { + my @lr = ( $_[ 0]->{ left}->max, $_[ 0]->{ right}->max); + # $l and $r should be both defined or undefined, unless the + # MaxKeeper memory is corrupted by forgetting unremembered things. + return unless defined( $lr[ 0]) and defined( $lr[ 1]); + @lr; +} + +# remember alignment requirements +sub _alloc { + my $al = shift; + for ( @_ ) { +# $_ ||= ''; print "allocing '$_'\n"; + my ( $l, $r) = $al->_measure( $_); + $al->{ left}->remember( $l); # space needed left of pos + $al->{ right}->remember( $r); # ...and right of pos + } + $al; +} + +# release alignment requirement. it disturbs an aligner deeply to forget +# things it hasn't remembered. the effects may be delayed. +sub _forget { + my $al = shift; + for ( map defined() ? $_ : '', @_ ) { +# print "forgetting '$_'\n"; + my ( $l, $r) = $al->_measure( $_); + $al->{ left}->forget( $l); + $al->{ right}->forget( $r); + } + $al; +} + +sub _spaces { + my ($repeat_count) = @_; + return (($repeat_count > 0) ? (' ' x $repeat_count) : ''); +} + +# justify a string. a string is aligned within the aligner's field, and +# filled with blanks or cut to size, as appropriate. a string that has +# been allocated will never be trimmed (that is the point of allocation). +# if the aligner is empty it returns the string unaltered. +sub _justify { + my $al = shift; + my $str = shift; +# print "justifying '$str'\n"; + $str .= ''; # stringify (objects, numbers, undef) + my ( $l_pad, $r_pad) = $al->_padding( $str); + substr( $str, 0, -$l_pad) = '' if $l_pad < 0; # trim if negative + substr( $str, $r_pad) = '' if $r_pad < 0; # ... both ends + return _spaces($l_pad) . $str . _spaces($r_pad); # pad if positive +} + +# return two numbers that indicate how many blanks are needed on each side +# of a string to justify it. Negative values mean trim that many characters. +# an empty aligner returns ( 0, 0), so doesn't change anything. +sub _padding { + my $al = shift; + my $str = shift; + my ( $this_l, $this_r) = $al->_measure( $str); + my ( $l_pad, $r_pad) = ( 0, 0); + if ( $al->_status ) { + ( $l_pad, $r_pad) = $al->_status; + $l_pad -= $this_l; + $r_pad -= $this_r; + } + ( $l_pad, $r_pad); +} + +# _compile_alispec() returns positioners according to specification. In +# effect, it is the interpreter for alignment specifications. + +sub _compile_alispec { # it's a dirty job... + my $width = sub { length shift }; # this is always so for string aligners + my $pos; # the positioner we actually compile + local $_ = shift || ''; # alignment specification + if ( ref() eq 'Regexp' ) { + my $regex = $_; # lexical copy! + $pos = sub { + local $_ = shift; + return m/$regex/ ? $-[ 0] : length; # assume match after string + }; + } else { + s/^left/0/; + s/^center/0.5/; + s/^right/1/; + if ( _is_number( $_) ) { + my $proportion = $_; # use lexical copy + $pos = sub { int( $proportion*length shift) }; + } elsif ( $_ =~ /^(?:num|point)(?:\((.*))?/ ) { + my $point = defined $1 ? $1 : ''; + $point =~ s/\)$//; # ignore trailing paren, if present + length $point or $point = '.'; + $pos = sub { index( shift() . $point, $point) } + } else { + $pos = sub { 0 }; + } + } + ( $width, $pos); +} + +# decide if a string is a number. (see perlfaq4). +sub _is_number { + my ($x) = @_; + return 0 unless defined $x; + return 0 if $x !~ /\d/; + return 1 if $x =~ /^-?\d+\.?\d*$/; + $x = Term::ANSIColor::colorstrip($x); + $x =~ /^-?\d+\.?\d*$/ +} + +package Text::Aligner::Auto; +# Combined numeric and left alignment. Numbers are aligned numerically, +# other strings are left-aligned. The resulting columns are interleaved +# flush left and filled on the right if necessary. + +sub _new { # only called by Text::Aligner->new() + my $class = shift; + my $numspec = shift; # currently ignored + bless { + num => Text::Aligner->new( 'num'), # align numbers among themselves + other => Text::Aligner->new, # left-align anything else + }, $class; +} + +sub _alloc { + my $aa = shift; + my @num = grep _is_number( $_), @_; + my @other = grep !_is_number( $_), @_; + $aa->{ num}->_alloc( @num); + $aa->{ other}->_alloc( @other); + $aa; +} + +sub _forget { + my $aa = shift; + $aa->{ num}->_forget( grep _is_number( $_), @_); + $aa->{ other}->_forget( grep !_is_number( $_), @_); + $aa; +} + +# Justify as required +sub _justify { + my ( $aa, $str) = @_; + # align according to type + $str = $aa->{ _is_number( $str) ? 'num' : 'other'}->_justify( $str); + my $combi = Text::Aligner->new; # left-justify pre-aligned string + # initialise to size of partial aligners. (don't initialise from + # empty aligner) + $combi->_alloc( $aa->{ num}->_justify( '')) if $aa->{ num}->_status; + $combi->_alloc( $aa->{ other}->_justify( '')) if $aa->{ other}->_status; + $combi->_justify( $str); +} + +# for convenience +BEGIN { # import _is_number() + *_is_number = \ &Text::Aligner::_is_number; +} + +package Text::Aligner::MaxKeeper; +# Keep the maximum of a dynamic set of numbers. Optimized for the case of +# a relatively small range of numbers that may occur repeatedly. + +sub new { + bless { + max => undef, + seen => {}, + }, shift; +} + +sub max { $_[ 0]->{ max} } + +sub remember { + my ( $mk, $val) = @_; + _to_max( $mk->{ max}, $val); + $mk->{ seen}->{ $val}++; + $mk; +} + +sub forget { + my ( $mk, $val) = @_; + if ( exists $mk->{ seen}->{ $val} ) { + my $seen = $mk->{ seen}; + unless ( --$seen->{ $val} ) { + delete $seen->{ $val}; + if ( $mk->{ max} == $val ) { + # lost the maximum, recalculate + undef $mk->{ max}; + _to_max( $mk->{ max}, keys %$seen); + } + } + } + $mk; +} + +sub _to_max { + my $var = \ shift; + defined $_ and ( not defined $$var or $$var < $_) and $$var = $_ for @_; + $$var; +} + +########################################### main pod documentation begin ## + + +1; #this line is important and will help the module return a true value + +__END__ + +=pod + +=encoding UTF-8 + +=head1 NAME + +Text::Aligner + +=head1 VERSION + +version 0.13 + +=head1 SYNOPSIS + + use Text::Aligner qw( align ); + + # Print the words "just a test!" right-justified each on a line: + + my @lines = align( 'right', qw( just a test!); + print "$_\n" for @lines; + +=head1 DESCRIPTION + +Text::Aligner exports a single function, align(), which is +used to justify strings to various alignment styles. The +alignment specification is the first argument, followed by +any number of scalars which are subject to alignment. + +The operation depends on context. In list context, a list of +the justified scalars is returned. In scalar context, the +justified arguments are joined into a single string with newlines +appended. The original arguments remain unchanged. In void +context, in-place justification is attempted. In this case, all +arguments must be lvalues. + +Align() also does one level of scalar dereferencing. That is, +whenever one of the arguments is a scalar reference, the scalar +pointed to is aligned instead. Other references are simply stringified. +An undefined argument is interpreted as an empty string without +complaint. + +Alignment respects colorizing escape sequences a la L +which means it knows that these sequences don't take up space on +the screen. + +=head1 NAME + +Text::Aligner - module to align text. + +=head1 ALIGNMENT + +The first argument of the align() function is an alignment style, a +single scalar. + +It can be one of the strings "left", "right", "center", "num", "point", +or "auto", or a regular expression (qr/.../), or a coderef. + +A default style of "left" is assumed for every other value, including +"" and undef. + +"left", "right" and "center" have the obvious meanings. These can +also be given as numbers 0, 1, and 0.5 respectively. (Other numbers +are also possible, but probably not very useful). + +"num", and its synonym "point", specify that the decimal points be +aligned (assumed on the right, unless present). Arbitrary (non-numeric) +strings are also aligned in this manner, so they end up one column left +of the (possibly assumed) decimal point, flush right with any integers. +For the occasional string like "inf", or "-" for missing values, this +may be the right place. A string-only column ends up right-aligned +(unless there are points present). + +The "auto" style separates numeric strings (that are composed of +"-", ".", and digits in the usual manner) and aligns them numerically. +Other strings are left aligned with the number that sticks out +farthest to the left. This gives left alignment for string-only +columns and numeric alignment for columns of numbers. In mixed +columns, strings are reasonably placed to serve as column headings +or intermediate titles. + +With "num" (and "point") it is possible to specify another character +for the decimal point in the form "num(,)". In fact, you can specify +any string after a leading "(", and the closing ")" is optional. +"point(=>)" could be used to align certain pieces of Perl code. This +option is currently not available with "auto" alignment (because +recognition of numbers is Anglo-centric). + +If a regular expression is specified, the points are aligned where +the first match of the regex starts. A match is assumed immediately +after the string if it doesn't match. + +A regular expression is a powerful way of alignment specification. It +can replace most others easily, except center alignment and, of course, +the double action of "auto". + +=head1 POSITIONERS + +For entirely self-defined forms of alignment, a coderef, also known +as a positioner, can be given instead of an alignment style. This +code will be called once or more times with the string to be aligned +as its argument. It must return two numbers, a width and a position, +that describe how to align a string with other strings. + +The width should normally be the length of the string. The position +defines a point relative to the beginning of the string, which is +aligned with the positions given for other strings. + +A zero position for all strings results in left alignment, positioning +to the end of the string results in right alignment, and returning +half the length gives center alignment. "num" alignment is realized +by marking the position of the decimal point. + +Note that the position you return is a relative measure. Adding a +constant value to all positions results in no change in alignment. +It doesn't have to point inside the string (as in right alignment, +where it points one character past the end of the string). + +The first return value of a positioner should almost always be the +length of the given string. However, it may be useful to lie about +the string length if the string contains escape sequences that occupy +no place on screen. + +=head1 SUBROUTINES + +=head2 align($style, $str) + +See above. + +=head2 new(...) + +For internal use. + +=head1 USAGE + + use Text::Aligner qw( align ); + + align( $style, $str, ...); + + $style must be given and must be an alignment specification. + Any number of scalars can follow. An argument that contains a + scalar reference is dereferenced before it is used. In scalar + and list context, the aligned strings are returned. In void + context, the values are aligned in place and must be lvalues. + +=head1 BUGS + +None known as of release, but... + +=head1 AUTHOR + + Anno Siegel + CPAN ID: ANNO + +=head1 COPYRIGHT + +Copyright (c) 2002 Anno Siegel. All rights reserved. +This program is free software; you can redistribute +it and/or modify it under the terms of the ISC license. + +(This program had been licensed under the same terms as Perl itself up to +version 1.118 released on 2011, and was relicensed by permission of its +originator). + +The full text of the license can be found in the +LICENSE file included with this module. + +=head1 SEE ALSO + +perl(1) + +L . + +=head1 AUTHOR + +Shlomi Fish + +=head1 COPYRIGHT AND LICENSE + +This software is Copyright (c) 2002 by Anno Siegel. + +This is free software, licensed under: + + The MIT (X11) License + +=cut diff --git a/hw/vendor/hpdcache/rtl/tb/scripts/perl5/Text/CSV.pm b/hw/vendor/hpdcache/rtl/tb/scripts/perl5/Text/CSV.pm new file mode 100644 index 00000000..59362624 --- /dev/null +++ b/hw/vendor/hpdcache/rtl/tb/scripts/perl5/Text/CSV.pm @@ -0,0 +1,2705 @@ +package Text::CSV; + + +use strict; +use Exporter; +use Carp (); +use vars qw( $VERSION $DEBUG @ISA @EXPORT_OK ); +@ISA = qw( Exporter ); +@EXPORT_OK = qw( csv ); + +BEGIN { + $VERSION = '1.97'; + $DEBUG = 0; +} + +# if use CSV_XS, requires version +my $Module_XS = 'Text::CSV_XS'; +my $Module_PP = 'Text::CSV_PP'; +my $XS_Version = '1.02'; + +my $Is_Dynamic = 0; + +my @PublicMethods = qw/ + version error_diag error_input + known_attributes csv + PV IV NV +/; +# + +# Check the environment variable to decide worker module. + +unless ($Text::CSV::Worker) { + $Text::CSV::DEBUG and Carp::carp("Check used worker module..."); + + if ( exists $ENV{PERL_TEXT_CSV} ) { + if ($ENV{PERL_TEXT_CSV} eq '0' or $ENV{PERL_TEXT_CSV} eq 'Text::CSV_PP') { + _load_pp() or Carp::croak $@; + } + elsif ($ENV{PERL_TEXT_CSV} eq '1' or $ENV{PERL_TEXT_CSV} =~ /Text::CSV_XS\s*,\s*Text::CSV_PP/) { + _load_xs() or _load_pp() or Carp::croak $@; + } + elsif ($ENV{PERL_TEXT_CSV} eq '2' or $ENV{PERL_TEXT_CSV} eq 'Text::CSV_XS') { + _load_xs() or Carp::croak $@; + } + else { + Carp::croak "The value of environmental variable 'PERL_TEXT_CSV' is invalid."; + } + } + else { + _load_xs() or _load_pp() or Carp::croak $@; + } + +} + +sub new { # normal mode + my $proto = shift; + my $class = ref($proto) || $proto; + + unless ( $proto ) { # for Text::CSV_XS/PP::new(0); + return eval qq| $Text::CSV::Worker\::new( \$proto ) |; + } + + #if (ref $_[0] and $_[0]->{module}) { + # Carp::croak("Can't set 'module' in non dynamic mode."); + #} + + if ( my $obj = $Text::CSV::Worker->new(@_) ) { + $obj->{_MODULE} = $Text::CSV::Worker; + bless $obj, $class; + return $obj; + } + else { + return; + } + + +} + + +sub require_xs_version { $XS_Version; } + + +sub module { + my $proto = shift; + return !ref($proto) ? $Text::CSV::Worker + : ref($proto->{_MODULE}) ? ref($proto->{_MODULE}) : $proto->{_MODULE}; +} + +*backend = *module; + + +sub is_xs { + return $_[0]->module eq $Module_XS; +} + + +sub is_pp { + return $_[0]->module eq $Module_PP; +} + + +sub is_dynamic { $Is_Dynamic; } + +sub _load_xs { _load($Module_XS, $XS_Version) } + +sub _load_pp { _load($Module_PP) } + +sub _load { + my ($module, $version) = @_; + $version ||= ''; + + $Text::CSV::DEBUG and Carp::carp "Load $module."; + + eval qq| use $module $version |; + + return if $@; + + push @Text::CSV::ISA, $module; + $Text::CSV::Worker = $module; + + local $^W; + no strict qw(refs); + + for my $method (@PublicMethods) { + *{"Text::CSV::$method"} = \&{"$module\::$method"}; + } + return 1; +} + + + +1; +__END__ + +=pod + +=head1 NAME + +Text::CSV - comma-separated values manipulator (using XS or PurePerl) + + +=head1 SYNOPSIS + + use Text::CSV; + + my @rows; + my $csv = Text::CSV->new ( { binary => 1 } ) # should set binary attribute. + or die "Cannot use CSV: ".Text::CSV->error_diag (); + + open my $fh, "<:encoding(utf8)", "test.csv" or die "test.csv: $!"; + while ( my $row = $csv->getline( $fh ) ) { + $row->[2] =~ m/pattern/ or next; # 3rd field should match + push @rows, $row; + } + $csv->eof or $csv->error_diag(); + close $fh; + + $csv->eol ("\r\n"); + + open $fh, ">:encoding(utf8)", "new.csv" or die "new.csv: $!"; + $csv->print ($fh, $_) for @rows; + close $fh or die "new.csv: $!"; + + # + # parse and combine style + # + + $status = $csv->combine(@columns); # combine columns into a string + $line = $csv->string(); # get the combined string + + $status = $csv->parse($line); # parse a CSV string into fields + @columns = $csv->fields(); # get the parsed fields + + $status = $csv->status (); # get the most recent status + $bad_argument = $csv->error_input (); # get the most recent bad argument + $diag = $csv->error_diag (); # if an error occurred, explains WHY + + $status = $csv->print ($io, $colref); # Write an array of fields + # immediately to a file $io + $colref = $csv->getline ($io); # Read a line from file $io, + # parse it and return an array + # ref of fields + $csv->column_names (@names); # Set column names for getline_hr () + $ref = $csv->getline_hr ($io); # getline (), but returns a hashref + $eof = $csv->eof (); # Indicate if last parse or + # getline () hit End Of File + + $csv->types(\@t_array); # Set column types + +=head1 DESCRIPTION + +Text::CSV is a thin wrapper for L-compatible modules now. +All the backend modules provide facilities for the composition and +decomposition of comma-separated values. Text::CSV uses Text::CSV_XS +by default, and when Text::CSV_XS is not available, falls back on +L, which is bundled in the same distribution as this module. + +=head1 CHOOSING BACKEND + +This module respects an environmental variable called C +when it decides a backend module to use. If this environmental variable +is not set, it tries to load Text::CSV_XS, and if Text::CSV_XS is not +available, falls back on Text::CSV_PP; + +If you always don't want it to fall back on Text::CSV_PP, set the variable +like this (C may be C, C and the likes, depending +on your environment): + + > export PERL_TEXT_CSV=Text::CSV_XS + +If you prefer Text::CSV_XS to Text::CSV_PP (default), then: + + > export PERL_TEXT_CSV=Text::CSV_XS,Text::CSV_PP + +You may also want to set this variable at the top of your test files, in order +not to be bothered with incompatibilities between backends (you need to wrap +this in C, and set before actually C-ing Text::CSV module, as it +decides its backend as soon as it's loaded): + + BEGIN { $ENV{PERL_TEXT_CSV}='Text::CSV_PP'; } + use Text::CSV; + +=head1 NOTES + +This section is taken from Text::CSV_XS. + +=head2 Embedded newlines + +B: The default behavior is to accept only ASCII characters +in the range from C<0x20> (space) to C<0x7E> (tilde). This means that the +fields can not contain newlines. If your data contains newlines embedded in +fields, or characters above C<0x7E> (tilde), or binary data, you B> +set C<< binary => 1 >> in the call to L. To cover the widest range of +parsing options, you will always want to set binary. + +But you still have the problem that you have to pass a correct line to the +L method, which is more complicated from the usual point of usage: + + my $csv = Text::CSV->new ({ binary => 1, eol => $/ }); + while (<>) { # WRONG! + $csv->parse ($_); + my @fields = $csv->fields (); + } + +this will break, as the C might read broken lines: it does not care +about the quoting. If you need to support embedded newlines, the way to go +is to B pass L|/eol> in the parser (it accepts C<\n>, C<\r>, +B C<\r\n> by default) and then + + my $csv = Text::CSV->new ({ binary => 1 }); + open my $fh, "<", $file or die "$file: $!"; + while (my $row = $csv->getline ($fh)) { + my @fields = @$row; + } + +The old(er) way of using global file handles is still supported + + while (my $row = $csv->getline (*ARGV)) { ... } + +=head2 Unicode + +Unicode is only tested to work with perl-5.8.2 and up. + +See also L. + +The simplest way to ensure the correct encoding is used for in- and output +is by either setting layers on the filehandles, or setting the L +argument for L. + + open my $fh, "<:encoding(UTF-8)", "in.csv" or die "in.csv: $!"; +or + my $aoa = csv (in => "in.csv", encoding => "UTF-8"); + + open my $fh, ">:encoding(UTF-8)", "out.csv" or die "out.csv: $!"; +or + csv (in => $aoa, out => "out.csv", encoding => "UTF-8"); + +On parsing (both for L and L), if the source is marked +being UTF8, then all fields that are marked binary will also be marked UTF8. + +On combining (L and L): if any of the combining fields +was marked UTF8, the resulting string will be marked as UTF8. Note however +that all fields I the first field marked UTF8 and contained 8-bit +characters that were not upgraded to UTF8, these will be C in the +resulting string too, possibly causing unexpected errors. If you pass data +of different encoding, or you don't know if there is different encoding, +force it to be upgraded before you pass them on: + + $csv->print ($fh, [ map { utf8::upgrade (my $x = $_); $x } @data ]); + +For complete control over encoding, please use L: + + use Text::CSV::Encoded; + my $csv = Text::CSV::Encoded->new ({ + encoding_in => "iso-8859-1", # the encoding comes into Perl + encoding_out => "cp1252", # the encoding comes out of Perl + }); + + $csv = Text::CSV::Encoded->new ({ encoding => "utf8" }); + # combine () and print () accept *literally* utf8 encoded data + # parse () and getline () return *literally* utf8 encoded data + + $csv = Text::CSV::Encoded->new ({ encoding => undef }); # default + # combine () and print () accept UTF8 marked data + # parse () and getline () return UTF8 marked data + +=head2 BOM + +BOM (or Byte Order Mark) handling is available only inside the L +method. This method supports the following encodings: C, C, +C, C, C, C, C, C, +C, and C. See L. + +If a file has a BOM, the easiest way to deal with that is + + my $aoh = csv (in => $file, detect_bom => 1); + +All records will be encoded based on the detected BOM. + +This implies a call to the L method, which defaults to also set +the L. So this is B the same as + + my $aoh = csv (in => $file, headers => "auto"); + +which only reads the first record to set L but ignores any +meaning of possible present BOM. + +=head1 METHODS + +This section is also taken from Text::CSV_XS. + +=head2 version + +(Class method) Returns the current module version. + +=head2 new + +(Class method) Returns a new instance of class Text::CSV. The attributes +are described by the (optional) hash ref C<\%attr>. + + my $csv = Text::CSV->new ({ attributes ... }); + +The following attributes are available: + +=head3 eol + + my $csv = Text::CSV->new ({ eol => $/ }); + $csv->eol (undef); + my $eol = $csv->eol; + +The end-of-line string to add to rows for L or the record separator +for L. + +When not passed in a B instance, the default behavior is to accept +C<\n>, C<\r>, and C<\r\n>, so it is probably safer to not specify C at +all. Passing C or the empty string behave the same. + +When not passed in a B instance, records are not terminated at +all, so it is probably wise to pass something you expect. A safe choice for +C on output is either C<$/> or C<\r\n>. + +Common values for C are C<"\012"> (C<\n> or Line Feed), C<"\015\012"> +(C<\r\n> or Carriage Return, Line Feed), and C<"\015"> (C<\r> or Carriage +Return). The L|/eol> attribute cannot exceed 7 (ASCII) characters. + +If both C<$/> and L|/eol> equal C<"\015">, parsing lines that end on +only a Carriage Return without Line Feed, will be Ld correct. + +=head3 sep_char + + my $csv = Text::CSV->new ({ sep_char => ";" }); + $csv->sep_char (";"); + my $c = $csv->sep_char; + +The char used to separate fields, by default a comma. (C<,>). Limited to a +single-byte character, usually in the range from C<0x20> (space) to C<0x7E> +(tilde). When longer sequences are required, use L|/sep>. + +The separation character can not be equal to the quote character or to the +escape character. + +=head3 sep + + my $csv = Text::CSV->new ({ sep => "\N{FULLWIDTH COMMA}" }); + $csv->sep (";"); + my $sep = $csv->sep; + +The chars used to separate fields, by default undefined. Limited to 8 bytes. + +When set, overrules L|/sep_char>. If its length is one byte it +acts as an alias to L|/sep_char>. + +=head3 quote_char + + my $csv = Text::CSV->new ({ quote_char => "'" }); + $csv->quote_char (undef); + my $c = $csv->quote_char; + +The character to quote fields containing blanks or binary data, by default +the double quote character (C<">). A value of undef suppresses quote chars +(for simple cases only). Limited to a single-byte character, usually in the +range from C<0x20> (space) to C<0x7E> (tilde). When longer sequences are +required, use L|/quote>. + +C can not be equal to L|/sep_char>. + +=head3 quote + + my $csv = Text::CSV->new ({ quote => "\N{FULLWIDTH QUOTATION MARK}" }); + $csv->quote ("'"); + my $quote = $csv->quote; + +The chars used to quote fields, by default undefined. Limited to 8 bytes. + +When set, overrules L|/quote_char>. If its length is one byte +it acts as an alias to L|/quote_char>. + +=head3 escape_char + + my $csv = Text::CSV->new ({ escape_char => "\\" }); + $csv->escape_char (":"); + my $c = $csv->escape_char; + +The character to escape certain characters inside quoted fields. This is +limited to a single-byte character, usually in the range from C<0x20> +(space) to C<0x7E> (tilde). + +The C defaults to being the double-quote mark (C<">). In other +words the same as the default L|/quote_char>. This means that +doubling the quote mark in a field escapes it: + + "foo","bar","Escape ""quote mark"" with two ""quote marks""","baz" + +If you change the L|/quote_char> without changing the +C, the C will still be the double-quote (C<">). +If instead you want to escape the L|/quote_char> by doubling +it you will need to also change the C to be the same as what +you have changed the L|/quote_char> to. + +Setting C to or C<""> will disable escaping completely +and is greatly discouraged. This will also disable C. + +The escape character can not be equal to the separation character. + +=head3 binary + + my $csv = Text::CSV->new ({ binary => 1 }); + $csv->binary (0); + my $f = $csv->binary; + +If this attribute is C<1>, you may use binary characters in quoted fields, +including line feeds, carriage returns and C bytes. (The latter could +be escaped as C<"0>.) By default this feature is off. + +If a string is marked UTF8, C will be turned on automatically when +binary characters other than C and C are encountered. Note that a +simple string like C<"\x{00a0}"> might still be binary, but not marked UTF8, +so setting C<< { binary => 1 } >> is still a wise option. + +=head3 strict + + my $csv = Text::CSV->new ({ strict => 1 }); + $csv->strict (0); + my $f = $csv->strict; + +If this attribute is set to C<1>, any row that parses to a different number +of fields than the previous row will cause the parser to throw error 2014. + +=head3 formula_handling + +=head3 formula + + my $csv = Text::CSV->new ({ formula => "none" }); + $csv->formula ("none"); + my $f = $csv->formula; + +This defines the behavior of fields containing I. As formulas are +considered dangerous in spreadsheets, this attribute can define an optional +action to be taken if a field starts with an equal sign (C<=>). + +For purpose of code-readability, this can also be written as + + my $csv = Text::CSV->new ({ formula_handling => "none" }); + $csv->formula_handling ("none"); + my $f = $csv->formula_handling; + +Possible values for this attribute are + +=over 2 + +=item none + +Take no specific action. This is the default. + + $csv->formula ("none"); + +=item die + +Cause the process to C whenever a leading C<=> is encountered. + + $csv->formula ("die"); + +=item croak + +Cause the process to C whenever a leading C<=> is encountered. (See +L) + + $csv->formula ("croak"); + +=item diag + +Report position and content of the field whenever a leading C<=> is found. +The value of the field is unchanged. + + $csv->formula ("diag"); + +=item empty + +Replace the content of fields that start with a C<=> with the empty string. + + $csv->formula ("empty"); + $csv->formula (""); + +=item undef + +Replace the content of fields that start with a C<=> with C. + + $csv->formula ("undef"); + $csv->formula (undef); + +=back + +All other values will give a warning and then fallback to C. + +=head3 decode_utf8 + + my $csv = Text::CSV->new ({ decode_utf8 => 1 }); + $csv->decode_utf8 (0); + my $f = $csv->decode_utf8; + +This attributes defaults to TRUE. + +While I, fields that are valid UTF-8, are automatically set to be +UTF-8, so that + + $csv->parse ("\xC4\xA8\n"); + +results in + + PV("\304\250"\0) [UTF8 "\x{128}"] + +Sometimes it might not be a desired action. To prevent those upgrades, set +this attribute to false, and the result will be + + PV("\304\250"\0) + +=head3 auto_diag + + my $csv = Text::CSV->new ({ auto_diag => 1 }); + $csv->auto_diag (2); + my $l = $csv->auto_diag; + +Set this attribute to a number between C<1> and C<9> causes L +to be automatically called in void context upon errors. + +In case of error C<2012 - EOF>, this call will be void. + +If C is set to a numeric value greater than C<1>, it will C +on errors instead of C. If set to anything unrecognized, it will be +silently ignored. + +Future extensions to this feature will include more reliable auto-detection +of C being active in the scope of which the error occurred which +will increment the value of C with C<1> the moment the error is +detected. + +=head3 diag_verbose + + my $csv = Text::CSV->new ({ diag_verbose => 1 }); + $csv->diag_verbose (2); + my $l = $csv->diag_verbose; + +Set the verbosity of the output triggered by C. Currently only +adds the current input-record-number (if known) to the diagnostic output +with an indication of the position of the error. + +=head3 blank_is_undef + + my $csv = Text::CSV->new ({ blank_is_undef => 1 }); + $csv->blank_is_undef (0); + my $f = $csv->blank_is_undef; + +Under normal circumstances, C data makes no distinction between quoted- +and unquoted empty fields. These both end up in an empty string field once +read, thus + + 1,"",," ",2 + +is read as + + ("1", "", "", " ", "2") + +When I C files with either L|/always_quote> +or L|/quote_empty> set, the unquoted I field is the +result of an undefined value. To enable this distinction when I +C data, the C attribute will cause unquoted empty +fields to be set to C, causing the above to be parsed as + + ("1", "", undef, " ", "2") + +note that this is specifically important when loading C fields into a +database that allows C values, as the perl equivalent for C is +C in L land. + +=head3 empty_is_undef + + my $csv = Text::CSV->new ({ empty_is_undef => 1 }); + $csv->empty_is_undef (0); + my $f = $csv->empty_is_undef; + +Going one step further than L|/blank_is_undef>, this +attribute converts all empty fields to C, so + + 1,"",," ",2 + +is read as + + (1, undef, undef, " ", 2) + +Note that this effects only fields that are originally empty, not fields +that are empty after stripping allowed whitespace. YMMV. + +=head3 allow_whitespace + + my $csv = Text::CSV->new ({ allow_whitespace => 1 }); + $csv->allow_whitespace (0); + my $f = $csv->allow_whitespace; + +When this option is set to true, the whitespace (C's and C's) +surrounding the separation character is removed when parsing. If either +C or C is one of the three characters L|/sep_char>, +L|/quote_char>, or L|/escape_char> it will not +be considered whitespace. + +Now lines like: + + 1 , "foo" , bar , 3 , zapp + +are parsed as valid C, even though it violates the C specs. + +Note that B whitespace is stripped from both start and end of each +field. That would make it I than a I to enable parsing bad +C lines, as + + 1, 2.0, 3, ape , monkey + +will now be parsed as + + ("1", "2.0", "3", "ape", "monkey") + +even if the original line was perfectly acceptable C. + +=head3 allow_loose_quotes + + my $csv = Text::CSV->new ({ allow_loose_quotes => 1 }); + $csv->allow_loose_quotes (0); + my $f = $csv->allow_loose_quotes; + +By default, parsing unquoted fields containing L|/quote_char> +characters like + + 1,foo "bar" baz,42 + +would result in parse error 2034. Though it is still bad practice to allow +this format, we cannot help the fact that some vendors make their +applications spit out lines styled this way. + +If there is B bad C data, like + + 1,"foo "bar" baz",42 + +or + + 1,""foo bar baz"",42 + +there is a way to get this data-line parsed and leave the quotes inside the +quoted field as-is. This can be achieved by setting C +B making sure that the L|/escape_char> is I equal +to L|/quote_char>. + +=head3 allow_loose_escapes + + my $csv = Text::CSV->new ({ allow_loose_escapes => 1 }); + $csv->allow_loose_escapes (0); + my $f = $csv->allow_loose_escapes; + +Parsing fields that have L|/escape_char> characters that +escape characters that do not need to be escaped, like: + + my $csv = Text::CSV->new ({ escape_char => "\\" }); + $csv->parse (qq{1,"my bar\'s",baz,42}); + +would result in parse error 2025. Though it is bad practice to allow this +format, this attribute enables you to treat all escape character sequences +equal. + +=head3 allow_unquoted_escape + + my $csv = Text::CSV->new ({ allow_unquoted_escape => 1 }); + $csv->allow_unquoted_escape (0); + my $f = $csv->allow_unquoted_escape; + +A backward compatibility issue where L|/escape_char> differs +from L|/quote_char> prevents L|/escape_char> +to be in the first position of a field. If L|/quote_char> is +equal to the default C<"> and L|/escape_char> is set to C<\>, +this would be illegal: + + 1,\0,2 + +Setting this attribute to C<1> might help to overcome issues with backward +compatibility and allow this style. + +=head3 always_quote + + my $csv = Text::CSV->new ({ always_quote => 1 }); + $csv->always_quote (0); + my $f = $csv->always_quote; + +By default the generated fields are quoted only if they I to be. For +example, if they contain the separator character. If you set this attribute +to C<1> then I defined fields will be quoted. (C fields are not +quoted, see L). This makes it quite often easier to handle +exported data in external applications. + +=head3 quote_space + + my $csv = Text::CSV->new ({ quote_space => 1 }); + $csv->quote_space (0); + my $f = $csv->quote_space; + +By default, a space in a field would trigger quotation. As no rule exists +this to be forced in C, nor any for the opposite, the default is true +for safety. You can exclude the space from this trigger by setting this +attribute to 0. + +=head3 quote_empty + + my $csv = Text::CSV->new ({ quote_empty => 1 }); + $csv->quote_empty (0); + my $f = $csv->quote_empty; + +By default the generated fields are quoted only if they I to be. An +empty (defined) field does not need quotation. If you set this attribute to +C<1> then I defined fields will be quoted. (C fields are not +quoted, see L). See also L|/always_quote>. + +=head3 quote_binary + + my $csv = Text::CSV->new ({ quote_binary => 1 }); + $csv->quote_binary (0); + my $f = $csv->quote_binary; + +By default, all "unsafe" bytes inside a string cause the combined field to +be quoted. By setting this attribute to C<0>, you can disable that trigger +for bytes >= C<0x7F>. + +=head3 escape_null + + my $csv = Text::CSV->new ({ escape_null => 1 }); + $csv->escape_null (0); + my $f = $csv->escape_null; + +By default, a C byte in a field would be escaped. This option enables +you to treat the C byte as a simple binary character in binary mode +(the C<< { binary => 1 } >> is set). The default is true. You can prevent +C escapes by setting this attribute to C<0>. + +When the C attribute is set to undefined, this attribute will +be set to false. + +The default setting will encode "=\x00=" as + + "="0=" + +With C set, this will result in + + "=\x00=" + +The default when using the C function is C. + +For backward compatibility reasons, the deprecated old name C +is still recognized. + +=head3 keep_meta_info + + my $csv = Text::CSV->new ({ keep_meta_info => 1 }); + $csv->keep_meta_info (0); + my $f = $csv->keep_meta_info; + +By default, the parsing of input records is as simple and fast as possible. +However, some parsing information - like quotation of the original field - +is lost in that process. Setting this flag to true enables retrieving that +information after parsing with the methods L, L, +and L described below. Default is false for performance. + +If you set this attribute to a value greater than 9, than you can control +output quotation style like it was used in the input of the the last parsed +record (unless quotation was added because of other reasons). + + my $csv = Text::CSV->new ({ + binary => 1, + keep_meta_info => 1, + quote_space => 0, + }); + + my $row = $csv->parse (q{1,,"", ," ",f,"g","h""h",help,"help"}); + + $csv->print (*STDOUT, \@row); + # 1,,, , ,f,g,"h""h",help,help + $csv->keep_meta_info (11); + $csv->print (*STDOUT, \@row); + # 1,,"", ," ",f,"g","h""h",help,"help" + +=head3 undef_str + + my $csv = Text::CSV->new ({ undef_str => "\\N" }); + $csv->undef_str (undef); + my $s = $csv->undef_str; + +This attribute optionally defines the output of undefined fields. The value +passed is not changed at all, so if it needs quotation, the quotation needs +to be included in the value of the attribute. Use with caution, as passing +a value like C<",",,,,"""> will for sure mess up your output. The default +for this attribute is C, meaning no special treatment. + +This attribute is useful when exporting CSV data to be imported in custom +loaders, like for MySQL, that recognize special sequences for C data. + +=head3 verbatim + + my $csv = Text::CSV->new ({ verbatim => 1 }); + $csv->verbatim (0); + my $f = $csv->verbatim; + +This is a quite controversial attribute to set, but makes some hard things +possible. + +The rationale behind this attribute is to tell the parser that the normally +special characters newline (C) and Carriage Return (C) will not be +special when this flag is set, and be dealt with as being ordinary binary +characters. This will ease working with data with embedded newlines. + +When C is used with L, L auto-C's +every line. + +Imagine a file format like + + M^^Hans^Janssen^Klas 2\n2A^Ja^11-06-2007#\r\n + +where, the line ending is a very specific C<"#\r\n">, and the sep_char is a +C<^> (caret). None of the fields is quoted, but embedded binary data is +likely to be present. With the specific line ending, this should not be too +hard to detect. + +By default, Text::CSV' parse function is instructed to only know about +C<"\n"> and C<"\r"> to be legal line endings, and so has to deal with the +embedded newline as a real C, so it can scan the next line if +binary is true, and the newline is inside a quoted field. With this option, +we tell L to parse the line as if C<"\n"> is just nothing more than +a binary character. + +For L this means that the parser has no more idea about line ending +and L Cs line endings on reading. + +=head3 types + +A set of column types; the attribute is immediately passed to the L +method. + +=head3 callbacks + +See the L section below. + +=head3 accessors + +To sum it up, + + $csv = Text::CSV->new (); + +is equivalent to + + $csv = Text::CSV->new ({ + eol => undef, # \r, \n, or \r\n + sep_char => ',', + sep => undef, + quote_char => '"', + quote => undef, + escape_char => '"', + binary => 0, + decode_utf8 => 1, + auto_diag => 0, + diag_verbose => 0, + blank_is_undef => 0, + empty_is_undef => 0, + allow_whitespace => 0, + allow_loose_quotes => 0, + allow_loose_escapes => 0, + allow_unquoted_escape => 0, + always_quote => 0, + quote_empty => 0, + quote_space => 1, + escape_null => 1, + quote_binary => 1, + keep_meta_info => 0, + verbatim => 0, + undef_str => undef, + types => undef, + callbacks => undef, + }); + +For all of the above mentioned flags, an accessor method is available where +you can inquire the current value, or change the value + + my $quote = $csv->quote_char; + $csv->binary (1); + +It is not wise to change these settings halfway through writing C data +to a stream. If however you want to create a new stream using the available +C object, there is no harm in changing them. + +If the L constructor call fails, it returns C, and makes the +fail reason available through the L method. + + $csv = Text::CSV->new ({ ecs_char => 1 }) or + die "".Text::CSV->error_diag (); + +L will return a string like + + "INI - Unknown attribute 'ecs_char'" + +=head2 known_attributes + + @attr = Text::CSV->known_attributes; + @attr = Text::CSV::known_attributes; + @attr = $csv->known_attributes; + +This method will return an ordered list of all the supported attributes as +described above. This can be useful for knowing what attributes are valid +in classes that use or extend Text::CSV. + +=head2 print + + $status = $csv->print ($fh, $colref); + +Similar to L + L + L, but much more efficient. +It expects an array ref as input (not an array!) and the resulting string +is not really created, but immediately written to the C<$fh> object, +typically an IO handle or any other object that offers a L method. + +For performance reasons C does not create a result string, so all +L, L, L, and L methods will return +undefined information after executing this method. + +If C<$colref> is C (explicit, not through a variable argument) and +L was used to specify fields to be printed, it is possible +to make performance improvements, as otherwise data would have to be copied +as arguments to the method call: + + $csv->bind_columns (\($foo, $bar)); + $status = $csv->print ($fh, undef); + +A short benchmark + + my @data = ("aa" .. "zz"); + $csv->bind_columns (\(@data)); + + $csv->print ($fh, [ @data ]); # 11800 recs/sec + $csv->print ($fh, \@data ); # 57600 recs/sec + $csv->print ($fh, undef ); # 48500 recs/sec + +=head2 say + + $status = $csv->say ($fh, $colref); + +Like L|/print>, but L|/eol> defaults to C<$\>. + +=head2 print_hr + + $csv->print_hr ($fh, $ref); + +Provides an easy way to print a C<$ref> (as fetched with L) +provided the column names are set with L. + +It is just a wrapper method with basic parameter checks over + + $csv->print ($fh, [ map { $ref->{$_} } $csv->column_names ]); + +=head2 combine + + $status = $csv->combine (@fields); + +This method constructs a C record from C<@fields>, returning success +or failure. Failure can result from lack of arguments or an argument that +contains an invalid character. Upon success, L can be called to +retrieve the resultant C string. Upon failure, the value returned by +L is undefined and L could be called to retrieve the +invalid argument. + +=head2 string + + $line = $csv->string (); + +This method returns the input to L or the resultant C string +of L, whichever was called more recently. + +=head2 getline + + $colref = $csv->getline ($fh); + +This is the counterpart to L, as L is the counterpart to +L: it parses a row from the C<$fh> handle using the L +method associated with C<$fh> and parses this row into an array ref. This +array ref is returned by the function or C for failure. When C<$fh> +does not support C, you are likely to hit errors. + +When fields are bound with L the return value is a reference +to an empty list. + +The L, L, and L methods are meaningless again. + +=head2 getline_all + + $arrayref = $csv->getline_all ($fh); + $arrayref = $csv->getline_all ($fh, $offset); + $arrayref = $csv->getline_all ($fh, $offset, $length); + +This will return a reference to a list of L results. +In this call, C is disabled. If C<$offset> is negative, as +with C, only the last C records of C<$fh> are taken +into consideration. + +Given a CSV file with 10 lines: + + lines call + ----- --------------------------------------------------------- + 0..9 $csv->getline_all ($fh) # all + 0..9 $csv->getline_all ($fh, 0) # all + 8..9 $csv->getline_all ($fh, 8) # start at 8 + - $csv->getline_all ($fh, 0, 0) # start at 0 first 0 rows + 0..4 $csv->getline_all ($fh, 0, 5) # start at 0 first 5 rows + 4..5 $csv->getline_all ($fh, 4, 2) # start at 4 first 2 rows + 8..9 $csv->getline_all ($fh, -2) # last 2 rows + 6..7 $csv->getline_all ($fh, -4, 2) # first 2 of last 4 rows + +=head2 getline_hr + +The L and L methods work together to allow you +to have rows returned as hashrefs. You must call L first to +declare your column names. + + $csv->column_names (qw( code name price description )); + $hr = $csv->getline_hr ($fh); + print "Price for $hr->{name} is $hr->{price} EUR\n"; + +L will croak if called before L. + +Note that L creates a hashref for every row and will be much +slower than the combined use of L and L but still +offering the same ease of use hashref inside the loop: + + my @cols = @{$csv->getline ($fh)}; + $csv->column_names (@cols); + while (my $row = $csv->getline_hr ($fh)) { + print $row->{price}; + } + +Could easily be rewritten to the much faster: + + my @cols = @{$csv->getline ($fh)}; + my $row = {}; + $csv->bind_columns (\@{$row}{@cols}); + while ($csv->getline ($fh)) { + print $row->{price}; + } + +Your mileage may vary for the size of the data and the number of rows. With +perl-5.14.2 the comparison for a 100_000 line file with 14 rows: + + Rate hashrefs getlines + hashrefs 1.00/s -- -76% + getlines 4.15/s 313% -- + +=head2 getline_hr_all + + $arrayref = $csv->getline_hr_all ($fh); + $arrayref = $csv->getline_hr_all ($fh, $offset); + $arrayref = $csv->getline_hr_all ($fh, $offset, $length); + +This will return a reference to a list of L +results. In this call, L|/keep_meta_info> is disabled. + +=head2 parse + + $status = $csv->parse ($line); + +This method decomposes a C string into fields, returning success or +failure. Failure can result from a lack of argument or the given C +string is improperly formatted. Upon success, L can be called to +retrieve the decomposed fields. Upon failure calling L will return +undefined data and L can be called to retrieve the invalid +argument. + +You may use the L method for setting column types. See L' +description below. + +The C<$line> argument is supposed to be a simple scalar. Everything else is +supposed to croak and set error 1500. + +=head2 fragment + +This function tries to implement RFC7111 (URI Fragment Identifiers for the +text/csv Media Type) - http://tools.ietf.org/html/rfc7111 + + my $AoA = $csv->fragment ($fh, $spec); + +In specifications, C<*> is used to specify the I item, a dash (C<->) +to indicate a range. All indices are C<1>-based: the first row or column +has index C<1>. Selections can be combined with the semi-colon (C<;>). + +When using this method in combination with L, the returned +reference will point to a list of hashes instead of a list of lists. A +disjointed cell-based combined selection might return rows with different +number of columns making the use of hashes unpredictable. + + $csv->column_names ("Name", "Age"); + my $AoH = $csv->fragment ($fh, "col=3;8"); + +If the L callback is active, it is also called on every line +parsed and skipped before the fragment. + +=over 2 + +=item row + + row=4 + row=5-7 + row=6-* + row=1-2;4;6-* + +=item col + + col=2 + col=1-3 + col=4-* + col=1-2;4;7-* + +=item cell + +In cell-based selection, the comma (C<,>) is used to pair row and column + + cell=4,1 + +The range operator (C<->) using Cs can be used to define top-left and +bottom-right C location + + cell=3,1-4,6 + +The C<*> is only allowed in the second part of a pair + + cell=3,2-*,2 # row 3 till end, only column 2 + cell=3,2-3,* # column 2 till end, only row 3 + cell=3,2-*,* # strip row 1 and 2, and column 1 + +Cells and cell ranges may be combined with C<;>, possibly resulting in rows +with different number of columns + + cell=1,1-2,2;3,3-4,4;1,4;4,1 + +Disjointed selections will only return selected cells. The cells that are +not specified will not be included in the returned set, not even as +C. As an example given a C like + + 11,12,13,...19 + 21,22,...28,29 + : : + 91,...97,98,99 + +with C will return: + + 11,12,14 + 21,22 + 33,34 + 41,43,44 + +Overlapping cell-specs will return those cells only once, So +C will return: + + 11,12,13 + 21,22,23,24 + 31,32,33,34 + 42,43,44 + +=back + +L does B allow different +types of specs to be combined (either C I C I C). +Passing an invalid fragment specification will croak and set error 2013. + +=head2 column_names + +Set the "keys" that will be used in the L calls. If no keys +(column names) are passed, it will return the current setting as a list. + +L accepts a list of scalars (the column names) or a single +array_ref, so you can pass the return value from L too: + + $csv->column_names ($csv->getline ($fh)); + +L does B checking on duplicates at all, which might lead +to unexpected results. Undefined entries will be replaced with the string +C<"\cAUNDEF\cA">, so + + $csv->column_names (undef, "", "name", "name"); + $hr = $csv->getline_hr ($fh); + +Will set C<< $hr->{"\cAUNDEF\cA"} >> to the 1st field, C<< $hr->{""} >> to +the 2nd field, and C<< $hr->{name} >> to the 4th field, discarding the 3rd +field. + +L croaks on invalid arguments. + +=head2 header + +This method does NOT work in perl-5.6.x + +Parse the CSV header and set L|/sep>, column_names and encoding. + + my @hdr = $csv->header ($fh); + $csv->header ($fh, { sep_set => [ ";", ",", "|", "\t" ] }); + $csv->header ($fh, { detect_bom => 1, munge_column_names => "lc" }); + +The first argument should be a file handle. + +This method resets some object properties, as it is supposed to be invoked +only once per file or stream. It will leave attributes C and +C alone of setting column names is disabled. Reading headers +on previously process objects might fail on perl-5.8.0 and older. + +Assuming that the file opened for parsing has a header, and the header does +not contain problematic characters like embedded newlines, read the first +line from the open handle then auto-detect whether the header separates the +column names with a character from the allowed separator list. + +If any of the allowed separators matches, and none of the I allowed +separators match, set L|/sep> to that separator for the current +CSV instance and use it to parse the first line, map those to lowercase, +and use that to set the instance L: + + my $csv = Text::CSV->new ({ binary => 1, auto_diag => 1 }); + open my $fh, "<", "file.csv"; + binmode $fh; # for Windows + $csv->header ($fh); + while (my $row = $csv->getline_hr ($fh)) { + ... + } + +If the header is empty, contains more than one unique separator out of the +allowed set, contains empty fields, or contains identical fields (after +folding), it will croak with error 1010, 1011, 1012, or 1013 respectively. + +If the header contains embedded newlines or is not valid CSV in any other +way, this method will croak and leave the parse error untouched. + +A successful call to C
will always set the L|/sep> of the +C<$csv> object. This behavior can not be disabled. + +=head3 return value + +On error this method will croak. + +In list context, the headers will be returned whether they are used to set +L or not. + +In scalar context, the instance itself is returned. B: the values as +found in the header will effectively be B if C is +false. + +=head3 Options + +=over 2 + +=item sep_set + + $csv->header ($fh, { sep_set => [ ";", ",", "|", "\t" ] }); + +The list of legal separators defaults to C<[ ";", "," ]> and can be changed +by this option. As this is probably the most often used option, it can be +passed on its own as an unnamed argument: + + $csv->header ($fh, [ ";", ",", "|", "\t", "::", "\x{2063}" ]); + +Multi-byte sequences are allowed, both multi-character and Unicode. See +L|/sep>. + +=item detect_bom + + $csv->header ($fh, { detect_bom => 1 }); + +The default behavior is to detect if the header line starts with a BOM. If +the header has a BOM, use that to set the encoding of C<$fh>. This default +behavior can be disabled by passing a false value to C. + +Supported encodings from BOM are: UTF-8, UTF-16BE, UTF-16LE, UTF-32BE, and +UTF-32LE. BOM's also support UTF-1, UTF-EBCDIC, SCSU, BOCU-1, and GB-18030 +but L does not (yet). UTF-7 is not supported. + +If a supported BOM was detected as start of the stream, it is stored in the +abject attribute C. + + my $enc = $csv->{ENCODING}; + +The encoding is used with C on C<$fh>. + +If the handle was opened in a (correct) encoding, this method will B +alter the encoding, as it checks the leading B of the first line. In +case the stream starts with a decode BOM (C), C<{ENCODING}> will be +C<""> (empty) instead of the default C. + +=item munge_column_names + +This option offers the means to modify the column names into something that +is most useful to the application. The default is to map all column names +to lower case. + + $csv->header ($fh, { munge_column_names => "lc" }); + +The following values are available: + + lc - lower case + uc - upper case + none - do not change + \%hash - supply a mapping + \&cb - supply a callback + +Literal: + + $csv->header ($fh, { munge_column_names => "none" }); + +Hash: + + $csv->header ($fh, { munge_column_names => { foo => "sombrero" }); + +if a value does not exist, the original value is used unchanged + +Callback: + + $csv->header ($fh, { munge_column_names => sub { fc } }); + $csv->header ($fh, { munge_column_names => sub { "column_".$col++ } }); + $csv->header ($fh, { munge_column_names => sub { lc (s/\W+/_/gr) } }); + +As this callback is called in a C, you can use C<$_> directly. + +=item set_column_names + + $csv->header ($fh, { set_column_names => 1 }); + +The default is to set the instances column names using L if +the method is successful, so subsequent calls to L can return +a hash. Disable setting the header can be forced by using a false value for +this option. + +As described in L above, content is lost in scalar context. + +=back + +=head3 Validation + +When receiving CSV files from external sources, this method can be used to +protect against changes in the layout by restricting to known headers (and +typos in the header fields). + + my %known = ( + "record key" => "c_rec", + "rec id" => "c_rec", + "id_rec" => "c_rec", + "kode" => "code", + "code" => "code", + "vaule" => "value", + "value" => "value", + ); + my $csv = Text::CSV->new ({ binary => 1, auto_diag => 1 }); + open my $fh, "<", $source or die "$source: $!"; + $csv->header ($fh, { munge_column_names => sub { + s/\s+$//; + s/^\s+//; + $known{lc $_} or die "Unknown column '$_' in $source"; + }}); + while (my $row = $csv->getline_hr ($fh)) { + say join "\t", $row->{c_rec}, $row->{code}, $row->{value}; + } + +=head2 bind_columns + +Takes a list of scalar references to be used for output with L or +to store in the fields fetched by L. When you do not pass enough +references to store the fetched fields in, L will fail with error +C<3006>. If you pass more than there are fields to return, the content of +the remaining references is left untouched. + + $csv->bind_columns (\$code, \$name, \$price, \$description); + while ($csv->getline ($fh)) { + print "The price of a $name is \x{20ac} $price\n"; + } + +To reset or clear all column binding, call L with the single +argument C. This will also clear column names. + + $csv->bind_columns (undef); + +If no arguments are passed at all, L will return the list of +current bindings or C if no binds are active. + +Note that in parsing with C, the fields are set on the fly. +That implies that if the third field of a row causes an error (or this row +has just two fields where the previous row had more), the first two fields +already have been assigned the values of the current row, while the rest of +the fields will still hold the values of the previous row. If you want the +parser to fail in these cases, use the L|/strict> attribute. + +=head2 eof + + $eof = $csv->eof (); + +If L or L was used with an IO stream, this method will +return true (1) if the last call hit end of file, otherwise it will return +false (''). This is useful to see the difference between a failure and end +of file. + +Note that if the parsing of the last line caused an error, C is still +true. That means that if you are I using L, an idiom like + + while (my $row = $csv->getline ($fh)) { + # ... + } + $csv->eof or $csv->error_diag; + +will I report the error. You would have to change that to + + while (my $row = $csv->getline ($fh)) { + # ... + } + +$csv->error_diag and $csv->error_diag; + +=head2 types + + $csv->types (\@tref); + +This method is used to force that (all) columns are of a given type. For +example, if you have an integer column, two columns with doubles and a +string column, then you might do a + + $csv->types ([Text::CSV::IV (), + Text::CSV::NV (), + Text::CSV::NV (), + Text::CSV::PV ()]); + +Column types are used only for I columns while parsing, in other +words by the L and L methods. + +You can unset column types by doing a + + $csv->types (undef); + +or fetch the current type settings with + + $types = $csv->types (); + +=over 4 + +=item IV + +Set field type to integer. + +=item NV + +Set field type to numeric/float. + +=item PV + +Set field type to string. + +=back + +=head2 fields + + @columns = $csv->fields (); + +This method returns the input to L or the resultant decomposed +fields of a successful L, whichever was called more recently. + +Note that the return value is undefined after using L, which does +not fill the data structures returned by L. + +=head2 meta_info + + @flags = $csv->meta_info (); + +This method returns the "flags" of the input to L or the flags of +the resultant decomposed fields of L, whichever was called more +recently. + +For each field, a meta_info field will hold flags that inform something +about the field returned by the L method or passed to the +L method. The flags are bit-wise-C'd like: + +=over 2 + +=item C< >0x0001 + +The field was quoted. + +=item C< >0x0002 + +The field was binary. + +=back + +See the C methods below. + +=head2 is_quoted + + my $quoted = $csv->is_quoted ($column_idx); + +Where C<$column_idx> is the (zero-based) index of the column in the last +result of L. + +This returns a true value if the data in the indicated column was enclosed +in L|/quote_char> quotes. This might be important for fields +where content C<,20070108,> is to be treated as a numeric value, and where +C<,"20070108",> is explicitly marked as character string data. + +This method is only valid when L is set to a true value. + +=head2 is_binary + + my $binary = $csv->is_binary ($column_idx); + +Where C<$column_idx> is the (zero-based) index of the column in the last +result of L. + +This returns a true value if the data in the indicated column contained any +byte in the range C<[\x00-\x08,\x10-\x1F,\x7F-\xFF]>. + +This method is only valid when L is set to a true value. + +=head2 is_missing + + my $missing = $csv->is_missing ($column_idx); + +Where C<$column_idx> is the (zero-based) index of the column in the last +result of L. + + $csv->keep_meta_info (1); + while (my $hr = $csv->getline_hr ($fh)) { + $csv->is_missing (0) and next; # This was an empty line + } + +When using L, it is impossible to tell if the parsed fields +are C because they where not filled in the C stream or because +they were not read at all, as B the fields defined by L +are set in the hash-ref. If you still need to know if all fields in each +row are provided, you should enable L|/keep_meta_info> so +you can check the flags. + +If L|/keep_meta_info> is C, C will +always return C, regardless of C<$column_idx> being valid or not. If +this attribute is C it will return either C<0> (the field is present) +or C<1> (the field is missing). + +A special case is the empty line. If the line is completely empty - after +dealing with the flags - this is still a valid CSV line: it is a record of +just one single empty field. However, if C is set, invoking +C with index C<0> will now return true. + +=head2 status + + $status = $csv->status (); + +This method returns the status of the last invoked L or L +call. Status is success (true: C<1>) or failure (false: C or C<0>). + +=head2 error_input + + $bad_argument = $csv->error_input (); + +This method returns the erroneous argument (if it exists) of L or +L, whichever was called more recently. If the last invocation was +successful, C will return C. + +=head2 error_diag + + Text::CSV->error_diag (); + $csv->error_diag (); + $error_code = 0 + $csv->error_diag (); + $error_str = "" . $csv->error_diag (); + ($cde, $str, $pos, $rec, $fld) = $csv->error_diag (); + +If (and only if) an error occurred, this function returns the diagnostics +of that error. + +If called in void context, this will print the internal error code and the +associated error message to STDERR. + +If called in list context, this will return the error code and the error +message in that order. If the last error was from parsing, the rest of the +values returned are a best guess at the location within the line that was +being parsed. Their values are 1-based. The position currently is index of +the byte at which the parsing failed in the current record. It might change +to be the index of the current character in a later release. The records is +the index of the record parsed by the csv instance. The field number is the +index of the field the parser thinks it is currently trying to parse. See +F for how this can be used. + +If called in scalar context, it will return the diagnostics in a single +scalar, a-la C<$!>. It will contain the error code in numeric context, and +the diagnostics message in string context. + +When called as a class method or a direct function call, the diagnostics +are that of the last L call. + +=head2 record_number + + $recno = $csv->record_number (); + +Returns the records parsed by this csv instance. This value should be more +accurate than C<$.> when embedded newlines come in play. Records written by +this instance are not counted. + +=head2 SetDiag + + $csv->SetDiag (0); + +Use to reset the diagnostics if you are dealing with errors. + +=head1 ADDITIONAL METHODS + +=over + +=item backend + +Returns the backend module name called by Text::CSV. +C is an alias. + +=item is_xs + +Returns true value if Text::CSV uses an XS backend. + +=item is_pp + +Returns true value if Text::CSV uses a pure-Perl backend. + +=back + +=head1 FUNCTIONS + +This section is also taken from Text::CSV_XS. + +=head2 csv + +This function is not exported by default and should be explicitly requested: + + use Text::CSV qw( csv ); + +This is an high-level function that aims at simple (user) interfaces. This +can be used to read/parse a C file or stream (the default behavior) or +to produce a file or write to a stream (define the C attribute). It +returns an array- or hash-reference on parsing (or C on fail) or the +numeric value of L on writing. When this function fails you +can get to the error using the class call to L + + my $aoa = csv (in => "test.csv") or + die Text::CSV->error_diag; + +This function takes the arguments as key-value pairs. This can be passed as +a list or as an anonymous hash: + + my $aoa = csv ( in => "test.csv", sep_char => ";"); + my $aoh = csv ({ in => $fh, headers => "auto" }); + +The arguments passed consist of two parts: the arguments to L itself +and the optional attributes to the C object used inside the function +as enumerated and explained in L. + +If not overridden, the default option used for CSV is + + auto_diag => 1 + escape_null => 0 + +The option that is always set and cannot be altered is + + binary => 1 + +As this function will likely be used in one-liners, it allows C to +be abbreviated as C, and C to be abbreviated as C +or C. + +Alternative invocations: + + my $aoa = Text::CSV::csv (in => "file.csv"); + + my $csv = Text::CSV->new (); + my $aoa = $csv->csv (in => "file.csv"); + +In the latter case, the object attributes are used from the existing object +and the attribute arguments in the function call are ignored: + + my $csv = Text::CSV->new ({ sep_char => ";" }); + my $aoh = $csv->csv (in => "file.csv", bom => 1); + +will parse using C<;> as C, not C<,>. + +=head3 in + +Used to specify the source. C can be a file name (e.g. C<"file.csv">), +which will be opened for reading and closed when finished, a file handle +(e.g. C<$fh> or C), a reference to a glob (e.g. C<\*ARGV>), the glob +itself (e.g. C<*STDIN>), or a reference to a scalar (e.g. C<\q{1,2,"csv"}>). + +When used with L, C should be a reference to a CSV structure (AoA +or AoH) or a CODE-ref that returns an array-reference or a hash-reference. +The code-ref will be invoked with no arguments. + + my $aoa = csv (in => "file.csv"); + + open my $fh, "<", "file.csv"; + my $aoa = csv (in => $fh); + + my $csv = [ [qw( Foo Bar )], [ 1, 2 ], [ 2, 3 ]]; + my $err = csv (in => $csv, out => "file.csv"); + +If called in void context without the L attribute, the resulting ref +will be used as input to a subsequent call to csv: + + csv (in => "file.csv", filter => { 2 => sub { length > 2 }}) + +will be a shortcut to + + csv (in => csv (in => "file.csv", filter => { 2 => sub { length > 2 }})) + +where, in the absence of the C attribute, this is a shortcut to + + csv (in => csv (in => "file.csv", filter => { 2 => sub { length > 2 }}), + out => *STDOUT) + +=head3 out + + csv (in => $aoa, out => "file.csv"); + csv (in => $aoa, out => $fh); + csv (in => $aoa, out => STDOUT); + csv (in => $aoa, out => *STDOUT); + csv (in => $aoa, out => \*STDOUT); + csv (in => $aoa, out => \my $data); + csv (in => $aoa, out => undef); + csv (in => $aoa, out => \"skip"); + +In output mode, the default CSV options when producing CSV are + + eol => "\r\n" + +The L attribute is ignored in output mode. + +C can be a file name (e.g. C<"file.csv">), which will be opened for +writing and closed when finished, a file handle (e.g. C<$fh> or C), a +reference to a glob (e.g. C<\*STDOUT>), the glob itself (e.g. C<*STDOUT>), +or a reference to a scalar (e.g. C<\my $data>). + + csv (in => sub { $sth->fetch }, out => "dump.csv"); + csv (in => sub { $sth->fetchrow_hashref }, out => "dump.csv", + headers => $sth->{NAME_lc}); + +When a code-ref is used for C, the output is generated per invocation, +so no buffering is involved. This implies that there is no size restriction +on the number of records. The C function ends when the coderef returns +a false value. + +If C is set to a reference of the literal string C<"skip">, the output +will be suppressed completely, which might be useful in combination with a +filter for side effects only. + + my %cache; + csv (in => "dump.csv", + out => \"skip", + on_in => sub { $cache{$_[1][1]}++ }); + +Currently, setting C to any false value (C, C<"">, 0) will be +equivalent to C<\"skip">. + +=head3 encoding + +If passed, it should be an encoding accepted by the C<:encoding()> option +to C. There is no default value. This attribute does not work in perl +5.6.x. C can be abbreviated to C for ease of use in command +line invocations. + +If C is set to the literal value C<"auto">, the method L
+will be invoked on the opened stream to check if there is a BOM and set the +encoding accordingly. This is equal to passing a true value in the option +L|/detect_bom>. + +=head3 detect_bom + +If C is given, the method L will be invoked on the +opened stream to check if there is a BOM and set the encoding accordingly. + +C can be abbreviated to C. + +This is the same as setting L|/encoding> to C<"auto">. + +Note that as the method L is invoked, its default is to also set +the headers. + +=head3 headers + +If this attribute is not given, the default behavior is to produce an array +of arrays. + +If C is supplied, it should be an anonymous list of column names, +an anonymous hashref, a coderef, or a literal flag: C, C, C, +or C. + +=over 2 + +=item skip + +When C is used, the header will not be included in the output. + + my $aoa = csv (in => $fh, headers => "skip"); + +=item auto + +If C is used, the first line of the C source will be read as the +list of field headers and used to produce an array of hashes. + + my $aoh = csv (in => $fh, headers => "auto"); + +=item lc + +If C is used, the first line of the C source will be read as the +list of field headers mapped to lower case and used to produce an array of +hashes. This is a variation of C. + + my $aoh = csv (in => $fh, headers => "lc"); + +=item uc + +If C is used, the first line of the C source will be read as the +list of field headers mapped to upper case and used to produce an array of +hashes. This is a variation of C. + + my $aoh = csv (in => $fh, headers => "uc"); + +=item CODE + +If a coderef is used, the first line of the C source will be read as +the list of mangled field headers in which each field is passed as the only +argument to the coderef. This list is used to produce an array of hashes. + + my $aoh = csv (in => $fh, + headers => sub { lc ($_[0]) =~ s/kode/code/gr }); + +this example is a variation of using C where all occurrences of C +are replaced with C. + +=item ARRAY + +If C is an anonymous list, the entries in the list will be used +as field names. The first line is considered data instead of headers. + + my $aoh = csv (in => $fh, headers => [qw( Foo Bar )]); + csv (in => $aoa, out => $fh, headers => [qw( code description price )]); + +=item HASH + +If C is an hash reference, this implies C, but header fields +for that exist as key in the hashref will be replaced by the value for that +key. Given a CSV file like + + post-kode,city,name,id number,fubble + 1234AA,Duckstad,Donald,13,"X313DF" + +using + + csv (headers => { "post-kode" => "pc", "id number" => "ID" }, ... + +will return an entry like + + { pc => "1234AA", + city => "Duckstad", + name => "Donald", + ID => "13", + fubble => "X313DF", + } + +=back + +See also L|/munge_column_names> and +L|/set_column_names>. + +=head3 munge_column_names + +If C is set, the method L is invoked on the +opened stream with all matching arguments to detect and set the headers. + +C can be abbreviated to C. + +=head3 key + +If passed, will default L|/headers> to C<"auto"> and return a +hashref instead of an array of hashes. + + my $ref = csv (in => "test.csv", key => "code"); + +with test.csv like + + code,product,price,color + 1,pc,850,gray + 2,keyboard,12,white + 3,mouse,5,black + +will return + + { 1 => { + code => 1, + color => 'gray', + price => 850, + product => 'pc' + }, + 2 => { + code => 2, + color => 'white', + price => 12, + product => 'keyboard' + }, + 3 => { + code => 3, + color => 'black', + price => 5, + product => 'mouse' + } + } + +The C attribute can be combined with L|/headers> for C +date that has no header line, like + + my $ref = csv ( + in => "foo.csv", + headers => [qw( c_foo foo bar description stock )], + key => "c_foo", + ); + +=head3 keep_headers + +When using hashes, keep the column names into the arrayref passed, so all +headers are available after the call in the original order. + + my $aoh = csv (in => "file.csv", keep_headers => \my @hdr); + +This attribute can be abbreviated to C or passed as C. + +This attribute implies a default of C for the C attribute. + +=head3 fragment + +Only output the fragment as defined in the L method. This option +is ignored when I C. See L. + +Combining all of them could give something like + + use Text::CSV qw( csv ); + my $aoh = csv ( + in => "test.txt", + encoding => "utf-8", + headers => "auto", + sep_char => "|", + fragment => "row=3;6-9;15-*", + ); + say $aoh->[15]{Foo}; + +=head3 sep_set + +If C is set, the method L is invoked on the opened stream +to detect and set L|/sep_char> with the given set. + +C can be abbreviated to C. + +Note that as the L method is invoked, its default is to also set +the headers. + +=head3 set_column_names + +If C is passed, the method L is invoked on the +opened stream with all arguments meant for L. + +If C is passed as a false value, the content of the first +row is only preserved if the output is AoA: + +With an input-file like + + bAr,foo + 1,2 + 3,4,5 + +This call + + my $aoa = csv (in => $file, set_column_names => 0); + +will result in + + [[ "bar", "foo" ], + [ "1", "2" ], + [ "3", "4", "5" ]] + +and + + my $aoa = csv (in => $file, set_column_names => 0, munge => "none"); + +will result in + + [[ "bAr", "foo" ], + [ "1", "2" ], + [ "3", "4", "5" ]] + +=head2 Callbacks + +Callbacks enable actions triggered from the I of Text::CSV. + +While most of what this enables can easily be done in an unrolled loop as +described in the L callbacks can be used to meet special demands +or enhance the L function. + +=over 2 + +=item error + + $csv->callbacks (error => sub { $csv->SetDiag (0) }); + +the C callback is invoked when an error occurs, but I when +L is set to a true value. A callback is invoked with the values +returned by L: + + my ($c, $s); + + sub ignore3006 + { + my ($err, $msg, $pos, $recno, $fldno) = @_; + if ($err == 3006) { + # ignore this error + ($c, $s) = (undef, undef); + Text::CSV->SetDiag (0); + } + # Any other error + return; + } # ignore3006 + + $csv->callbacks (error => \&ignore3006); + $csv->bind_columns (\$c, \$s); + while ($csv->getline ($fh)) { + # Error 3006 will not stop the loop + } + +=item after_parse + + $csv->callbacks (after_parse => sub { push @{$_[1]}, "NEW" }); + while (my $row = $csv->getline ($fh)) { + $row->[-1] eq "NEW"; + } + +This callback is invoked after parsing with L only if no error +occurred. The callback is invoked with two arguments: the current C +parser object and an array reference to the fields parsed. + +The return code of the callback is ignored unless it is a reference to the +string "skip", in which case the record will be skipped in L. + + sub add_from_db + { + my ($csv, $row) = @_; + $sth->execute ($row->[4]); + push @$row, $sth->fetchrow_array; + } # add_from_db + + my $aoa = csv (in => "file.csv", callbacks => { + after_parse => \&add_from_db }); + +This hook can be used for validation: + +=over 2 + +=item FAIL + +Die if any of the records does not validate a rule: + + after_parse => sub { + $_[1][4] =~ m/^[0-9]{4}\s?[A-Z]{2}$/ or + die "5th field does not have a valid Dutch zipcode"; + } + +=item DEFAULT + +Replace invalid fields with a default value: + + after_parse => sub { $_[1][2] =~ m/^\d+$/ or $_[1][2] = 0 } + +=item SKIP + +Skip records that have invalid fields (only applies to L): + + after_parse => sub { $_[1][0] =~ m/^\d+$/ or return \"skip"; } + +=back + +=item before_print + + my $idx = 1; + $csv->callbacks (before_print => sub { $_[1][0] = $idx++ }); + $csv->print (*STDOUT, [ 0, $_ ]) for @members; + +This callback is invoked before printing with L only if no error +occurred. The callback is invoked with two arguments: the current C +parser object and an array reference to the fields passed. + +The return code of the callback is ignored. + + sub max_4_fields + { + my ($csv, $row) = @_; + @$row > 4 and splice @$row, 4; + } # max_4_fields + + csv (in => csv (in => "file.csv"), out => *STDOUT, + callbacks => { before print => \&max_4_fields }); + +This callback is not active for L. + +=back + +=head3 Callbacks for csv () + +The L allows for some callbacks that do not integrate in XS internals +but only feature the L function. + + csv (in => "file.csv", + callbacks => { + filter => { 6 => sub { $_ > 15 } }, # first + after_parse => sub { say "AFTER PARSE"; }, # first + after_in => sub { say "AFTER IN"; }, # second + on_in => sub { say "ON IN"; }, # third + }, + ); + + csv (in => $aoh, + out => "file.csv", + callbacks => { + on_in => sub { say "ON IN"; }, # first + before_out => sub { say "BEFORE OUT"; }, # second + before_print => sub { say "BEFORE PRINT"; }, # third + }, + ); + +=over 2 + +=item filter + +This callback can be used to filter records. It is called just after a new +record has been scanned. The callback accepts a: + +=over 2 + +=item hashref + +The keys are the index to the row (the field name or field number, 1-based) +and the values are subs to return a true or false value. + + csv (in => "file.csv", filter => { + 3 => sub { m/a/ }, # third field should contain an "a" + 5 => sub { length > 4 }, # length of the 5th field minimal 5 + }); + + csv (in => "file.csv", filter => { foo => sub { $_ > 4 }}); + +If the keys to the filter hash contain any character that is not a digit it +will also implicitly set L to C<"auto"> unless L was +already passed as argument. When headers are active, returning an array of +hashes, the filter is not applicable to the header itself. + +All sub results should match, as in AND. + +The context of the callback sets C<$_> localized to the field indicated by +the filter. The two arguments are as with all other callbacks, so the other +fields in the current row can be seen: + + filter => { 3 => sub { $_ > 100 ? $_[1][1] =~ m/A/ : $_[1][6] =~ m/B/ }} + +If the context is set to return a list of hashes (L is defined), +the current record will also be available in the localized C<%_>: + + filter => { 3 => sub { $_ > 100 && $_{foo} =~ m/A/ && $_{bar} < 1000 }} + +If the filter is used to I the content by changing C<$_>, make sure +that the sub returns true in order not to have that record skipped: + + filter => { 2 => sub { $_ = uc }} + +will upper-case the second field, and then skip it if the resulting content +evaluates to false. To always accept, end with truth: + + filter => { 2 => sub { $_ = uc; 1 }} + +=item coderef + + csv (in => "file.csv", filter => sub { $n++; 0; }); + +If the argument to C is a coderef, it is an alias or shortcut to a +filter on column 0: + + csv (filter => sub { $n++; 0 }); + +is equal to + + csv (filter => { 0 => sub { $n++; 0 }); + +=item filter-name + + csv (in => "file.csv", filter => "not_blank"); + csv (in => "file.csv", filter => "not_empty"); + csv (in => "file.csv", filter => "filled"); + +These are predefined filters + +Given a file like (line numbers prefixed for doc purpose only): + + 1:1,2,3 + 2: + 3:, + 4:"" + 5:,, + 6:, , + 7:"", + 8:" " + 9:4,5,6 + +=over 2 + +=item not_blank + +Filter out the blank lines + +This filter is a shortcut for + + filter => { 0 => sub { @{$_[1]} > 1 or + defined $_[1][0] && $_[1][0] ne "" } } + +Due to the implementation, it is currently impossible to also filter lines +that consists only of a quoted empty field. These lines are also considered +blank lines. + +With the given example, lines 2 and 4 will be skipped. + +=item not_empty + +Filter out lines where all the fields are empty. + +This filter is a shortcut for + + filter => { 0 => sub { grep { defined && $_ ne "" } @{$_[1]} } } + +A space is not regarded being empty, so given the example data, lines 2, 3, +4, 5, and 7 are skipped. + +=item filled + +Filter out lines that have no visible data + +This filter is a shortcut for + + filter => { 0 => sub { grep { defined && m/\S/ } @{$_[1]} } } + +This filter rejects all lines that I have at least one field that does +not evaluate to the empty string. + +With the given example data, this filter would skip lines 2 through 8. + +=back + +=back + +=item after_in + +This callback is invoked for each record after all records have been parsed +but before returning the reference to the caller. The hook is invoked with +two arguments: the current C parser object and a reference to the +record. The reference can be a reference to a HASH or a reference to an +ARRAY as determined by the arguments. + +This callback can also be passed as an attribute without the C +wrapper. + +=item before_out + +This callback is invoked for each record before the record is printed. The +hook is invoked with two arguments: the current C parser object and a +reference to the record. The reference can be a reference to a HASH or a +reference to an ARRAY as determined by the arguments. + +This callback can also be passed as an attribute without the C +wrapper. + +This callback makes the row available in C<%_> if the row is a hashref. In +this case C<%_> is writable and will change the original row. + +=item on_in + +This callback acts exactly as the L or the L hooks. + +This callback can also be passed as an attribute without the C +wrapper. + +This callback makes the row available in C<%_> if the row is a hashref. In +this case C<%_> is writable and will change the original row. So e.g. with + + my $aoh = csv ( + in => \"foo\n1\n2\n", + headers => "auto", + on_in => sub { $_{bar} = 2; }, + ); + +C<$aoh> will be: + + [ { foo => 1, + bar => 2, + } + { foo => 2, + bar => 2, + } + ] + +=item csv + +The I L can also be called as a method or with an existing +Text::CSV object. This could help if the function is to be invoked a lot +of times and the overhead of creating the object internally over and over +again would be prevented by passing an existing instance. + + my $csv = Text::CSV->new ({ binary => 1, auto_diag => 1 }); + + my $aoa = $csv->csv (in => $fh); + my $aoa = csv (in => $fh, csv => $csv); + +both act the same. Running this 20000 times on a 20 lines CSV file, showed +a 53% speedup. + +=back + +=head1 DIAGNOSTICS + +This section is also taken from Text::CSV_XS. + +Still under construction ... + +If an error occurs, C<< $csv->error_diag >> can be used to get information +on the cause of the failure. Note that for speed reasons the internal value +is never cleared on success, so using the value returned by L +in normal cases - when no error occurred - may cause unexpected results. + +If the constructor failed, the cause can be found using L as a +class method, like C<< Text::CSV->error_diag >>. + +The C<< $csv->error_diag >> method is automatically invoked upon error when +the contractor was called with L|/auto_diag> set to C<1> or +C<2>, or when L is in effect. When set to C<1>, this will cause a +C with the error message, when set to C<2>, it will C. C<2012 - +EOF> is excluded from L|/auto_diag> reports. + +Errors can be (individually) caught using the L callback. + +The errors as described below are available. I have tried to make the error +itself explanatory enough, but more descriptions will be added. For most of +these errors, the first three capitals describe the error category: + +=over 2 + +=item * +INI + +Initialization error or option conflict. + +=item * +ECR + +Carriage-Return related parse error. + +=item * +EOF + +End-Of-File related parse error. + +=item * +EIQ + +Parse error inside quotation. + +=item * +EIF + +Parse error inside field. + +=item * +ECB + +Combine error. + +=item * +EHR + +HashRef parse related error. + +=back + +And below should be the complete list of error codes that can be returned: + +=over 2 + +=item * +1001 "INI - sep_char is equal to quote_char or escape_char" + +The L cannot be equal to L or to L, as this +would invalidate all parsing rules. + +=item * +1002 "INI - allow_whitespace with escape_char or quote_char SP or TAB" + +Using the L|/allow_whitespace> attribute when either +L|/quote_char> or L|/escape_char> is equal to +C or C is too ambiguous to allow. + +=item * +1003 "INI - \r or \n in main attr not allowed" + +Using default L|/eol> characters in either L|/sep_char>, +L|/quote_char>, or L|/escape_char> is not +allowed. + +=item * +1004 "INI - callbacks should be undef or a hashref" + +The L|/Callbacks> attribute only allows one to be C or +a hash reference. + +=item * +1005 "INI - EOL too long" + +The value passed for EOL is exceeding its maximum length (16). + +=item * +1006 "INI - SEP too long" + +The value passed for SEP is exceeding its maximum length (16). + +=item * +1007 "INI - QUOTE too long" + +The value passed for QUOTE is exceeding its maximum length (16). + +=item * +1008 "INI - SEP undefined" + +The value passed for SEP should be defined and not empty. + +=item * +1010 "INI - the header is empty" + +The header line parsed in the L is empty. + +=item * +1011 "INI - the header contains more than one valid separator" + +The header line parsed in the L contains more than one (unique) +separator character out of the allowed set of separators. + +=item * +1012 "INI - the header contains an empty field" + +The header line parsed in the L is contains an empty field. + +=item * +1013 "INI - the header contains nun-unique fields" + +The header line parsed in the L contains at least two identical +fields. + +=item * +1014 "INI - header called on undefined stream" + +The header line cannot be parsed from an undefined sources. + +=item * +1500 "PRM - Invalid/unsupported argument(s)" + +Function or method called with invalid argument(s) or parameter(s). + +=item * +1501 "PRM - The key attribute is passed as an unsupported type" + +The C attribute is of an unsupported type. + +=item * +2010 "ECR - QUO char inside quotes followed by CR not part of EOL" + +When L|/eol> has been set to anything but the default, like +C<"\r\t\n">, and the C<"\r"> is following the B (closing) +L|/quote_char>, where the characters following the C<"\r"> do +not make up the L|/eol> sequence, this is an error. + +=item * +2011 "ECR - Characters after end of quoted field" + +Sequences like C<1,foo,"bar"baz,22,1> are not allowed. C<"bar"> is a quoted +field and after the closing double-quote, there should be either a new-line +sequence or a separation character. + +=item * +2012 "EOF - End of data in parsing input stream" + +Self-explaining. End-of-file while inside parsing a stream. Can happen only +when reading from streams with L, as using L is done on +strings that are not required to have a trailing L|/eol>. + +=item * +2013 "INI - Specification error for fragments RFC7111" + +Invalid specification for URI L specification. + +=item * +2014 "ENF - Inconsistent number of fields" + +Inconsistent number of fields under strict parsing. + +=item * +2021 "EIQ - NL char inside quotes, binary off" + +Sequences like C<1,"foo\nbar",22,1> are allowed only when the binary option +has been selected with the constructor. + +=item * +2022 "EIQ - CR char inside quotes, binary off" + +Sequences like C<1,"foo\rbar",22,1> are allowed only when the binary option +has been selected with the constructor. + +=item * +2023 "EIQ - QUO character not allowed" + +Sequences like C<"foo "bar" baz",qu> and C<2023,",2008-04-05,"Foo, Bar",\n> +will cause this error. + +=item * +2024 "EIQ - EOF cannot be escaped, not even inside quotes" + +The escape character is not allowed as last character in an input stream. + +=item * +2025 "EIQ - Loose unescaped escape" + +An escape character should escape only characters that need escaping. + +Allowing the escape for other characters is possible with the attribute +L. + +=item * +2026 "EIQ - Binary character inside quoted field, binary off" + +Binary characters are not allowed by default. Exceptions are fields that +contain valid UTF-8, that will automatically be upgraded if the content is +valid UTF-8. Set L|/binary> to C<1> to accept binary data. + +=item * +2027 "EIQ - Quoted field not terminated" + +When parsing a field that started with a quotation character, the field is +expected to be closed with a quotation character. When the parsed line is +exhausted before the quote is found, that field is not terminated. + +=item * +2030 "EIF - NL char inside unquoted verbatim, binary off" + +=item * +2031 "EIF - CR char is first char of field, not part of EOL" + +=item * +2032 "EIF - CR char inside unquoted, not part of EOL" + +=item * +2034 "EIF - Loose unescaped quote" + +=item * +2035 "EIF - Escaped EOF in unquoted field" + +=item * +2036 "EIF - ESC error" + +=item * +2037 "EIF - Binary character in unquoted field, binary off" + +=item * +2110 "ECB - Binary character in Combine, binary off" + +=item * +2200 "EIO - print to IO failed. See errno" + +=item * +3001 "EHR - Unsupported syntax for column_names ()" + +=item * +3002 "EHR - getline_hr () called before column_names ()" + +=item * +3003 "EHR - bind_columns () and column_names () fields count mismatch" + +=item * +3004 "EHR - bind_columns () only accepts refs to scalars" + +=item * +3006 "EHR - bind_columns () did not pass enough refs for parsed fields" + +=item * +3007 "EHR - bind_columns needs refs to writable scalars" + +=item * +3008 "EHR - unexpected error in bound fields" + +=item * +3009 "EHR - print_hr () called before column_names ()" + +=item * +3010 "EHR - print_hr () called with invalid arguments" + +=back + +=head1 SEE ALSO + +L, L and L. + + +=head1 AUTHORS and MAINTAINERS + +Alan Citterman Falan[at]mfgrtl.comE> wrote the original Perl +module. Please don't send mail concerning Text::CSV to Alan, as +he's not a present maintainer. + +Jochen Wiedmann Fjoe[at]ispsoft.deE> rewrote the encoding and +decoding in C by implementing a simple finite-state machine and added +the variable quote, escape and separator characters, the binary mode +and the print and getline methods. See ChangeLog releases 0.10 through +0.23. + +H.Merijn Brand Fh.m.brand[at]xs4all.nlE> cleaned up the code, +added the field flags methods, wrote the major part of the test suite, +completed the documentation, fixed some RT bugs. See ChangeLog releases +0.25 and on. + +Makamaka Hannyaharamitu, Emakamaka[at]cpan.orgE wrote Text::CSV_PP +which is the pure-Perl version of Text::CSV_XS. + +New Text::CSV (since 0.99) is maintained by Makamaka, and Kenichi Ishigaki +since 1.91. + + +=head1 COPYRIGHT AND LICENSE + +Text::CSV + +Copyright (C) 1997 Alan Citterman. All rights reserved. +Copyright (C) 2007-2015 Makamaka Hannyaharamitu. +Copyright (C) 2017- Kenichi Ishigaki +A large portion of the doc is taken from Text::CSV_XS. See below. + +Text::CSV_PP: + +Copyright (C) 2005-2015 Makamaka Hannyaharamitu. +Copyright (C) 2017- Kenichi Ishigaki +A large portion of the code/doc are also taken from Text::CSV_XS. See below. + +Text:CSV_XS: + +Copyright (C) 2007-2016 H.Merijn Brand for PROCURA B.V. +Copyright (C) 1998-2001 Jochen Wiedmann. All rights reserved. +Portions Copyright (C) 1997 Alan Citterman. All rights reserved. + + +This library is free software; you can redistribute it and/or modify +it under the same terms as Perl itself. + +=cut diff --git a/hw/vendor/hpdcache/rtl/tb/scripts/perl5/Text/CSV_PP.pm b/hw/vendor/hpdcache/rtl/tb/scripts/perl5/Text/CSV_PP.pm new file mode 100644 index 00000000..053c7882 --- /dev/null +++ b/hw/vendor/hpdcache/rtl/tb/scripts/perl5/Text/CSV_PP.pm @@ -0,0 +1,5354 @@ +package Text::CSV_PP; + +################################################################################ +# +# Text::CSV_PP - Text::CSV_XS compatible pure-Perl module +# +################################################################################ +require 5.006001; + +use strict; +use Exporter (); +use vars qw($VERSION @ISA @EXPORT_OK); +use Carp; + +$VERSION = '1.97'; +@ISA = qw(Exporter); +@EXPORT_OK = qw(csv); + +sub PV { 0 } +sub IV { 1 } +sub NV { 2 } + +sub IS_QUOTED () { 0x0001; } +sub IS_BINARY () { 0x0002; } +sub IS_ERROR () { 0x0004; } +sub IS_MISSING () { 0x0010; } + +sub HOOK_ERROR () { 0x0001; } +sub HOOK_AFTER_PARSE () { 0x0002; } +sub HOOK_BEFORE_PRINT () { 0x0004; } + +sub useIO_EOF () { 0x0010; } + +my $ERRORS = { + # Generic errors + 1000 => "INI - constructor failed", + 1001 => "INI - sep_char is equal to quote_char or escape_char", + 1002 => "INI - allow_whitespace with escape_char or quote_char SP or TAB", + 1003 => "INI - \\r or \\n in main attr not allowed", + 1004 => "INI - callbacks should be undef or a hashref", + 1005 => "INI - EOL too long", + 1006 => "INI - SEP too long", + 1007 => "INI - QUOTE too long", + 1008 => "INI - SEP undefined", + + 1010 => "INI - the header is empty", + 1011 => "INI - the header contains more than one valid separator", + 1012 => "INI - the header contains an empty field", + 1013 => "INI - the header contains nun-unique fields", + 1014 => "INI - header called on undefined stream", + + # Syntax errors + 1500 => "PRM - Invalid/unsupported arguments(s)", + 1501 => "PRM - The key attribute is passed as an unsupported type", + + # Parse errors + 2010 => "ECR - QUO char inside quotes followed by CR not part of EOL", + 2011 => "ECR - Characters after end of quoted field", + 2012 => "EOF - End of data in parsing input stream", + 2013 => "ESP - Specification error for fragments RFC7111", + 2014 => "ENF - Inconsistent number of fields", + + # EIQ - Error Inside Quotes + 2021 => "EIQ - NL char inside quotes, binary off", + 2022 => "EIQ - CR char inside quotes, binary off", + 2023 => "EIQ - QUO character not allowed", + 2024 => "EIQ - EOF cannot be escaped, not even inside quotes", + 2025 => "EIQ - Loose unescaped escape", + 2026 => "EIQ - Binary character inside quoted field, binary off", + 2027 => "EIQ - Quoted field not terminated", + + # EIF - Error Inside Field + 2030 => "EIF - NL char inside unquoted verbatim, binary off", + 2031 => "EIF - CR char is first char of field, not part of EOL", + 2032 => "EIF - CR char inside unquoted, not part of EOL", + 2034 => "EIF - Loose unescaped quote", + 2035 => "EIF - Escaped EOF in unquoted field", + 2036 => "EIF - ESC error", + 2037 => "EIF - Binary character in unquoted field, binary off", + + # Combine errors + 2110 => "ECB - Binary character in Combine, binary off", + + # IO errors + 2200 => "EIO - print to IO failed. See errno", + + # Hash-Ref errors + 3001 => "EHR - Unsupported syntax for column_names ()", + 3002 => "EHR - getline_hr () called before column_names ()", + 3003 => "EHR - bind_columns () and column_names () fields count mismatch", + 3004 => "EHR - bind_columns () only accepts refs to scalars", + 3006 => "EHR - bind_columns () did not pass enough refs for parsed fields", + 3007 => "EHR - bind_columns needs refs to writable scalars", + 3008 => "EHR - unexpected error in bound fields", + 3009 => "EHR - print_hr () called before column_names ()", + 3010 => "EHR - print_hr () called with invalid arguments", + + # PP Only Error + 4002 => "EIQ - Unescaped ESC in quoted field", + 4003 => "EIF - ESC CR", + 4004 => "EUF - Field is terminated by the escape character (escape_char)", + + 0 => "", +}; + +BEGIN { + if ( $] < 5.006 ) { + $INC{'bytes.pm'} = 1 unless $INC{'bytes.pm'}; # dummy + no strict 'refs'; + *{"utf8::is_utf8"} = sub { 0; }; + *{"utf8::decode"} = sub { }; + } + elsif ( $] < 5.008 ) { + no strict 'refs'; + *{"utf8::is_utf8"} = sub { 0; }; + *{"utf8::decode"} = sub { }; + *{"utf8::encode"} = sub { }; + } + elsif ( !defined &utf8::is_utf8 ) { + require Encode; + *utf8::is_utf8 = *Encode::is_utf8; + } + + eval q| require Scalar::Util |; + if ( $@ ) { + eval q| require B |; + if ( $@ ) { + Carp::croak $@; + } + else { + my %tmap = qw( + B::NULL SCALAR + B::HV HASH + B::AV ARRAY + B::CV CODE + B::IO IO + B::GV GLOB + B::REGEXP REGEXP + ); + *Scalar::Util::reftype = sub (\$) { + my $r = shift; + return undef unless length(ref($r)); + my $t = ref(B::svref_2object($r)); + return + exists $tmap{$t} ? $tmap{$t} + : length(ref($$r)) ? 'REF' + : 'SCALAR'; + }; + *Scalar::Util::readonly = sub (\$) { + my $b = B::svref_2object( $_[0] ); + $b->FLAGS & 0x00800000; # SVf_READONLY? + }; + } + } +} + +################################################################################ +# +# Common pure perl methods, taken almost directly from Text::CSV_XS. +# (These should be moved into a common class eventually, so that +# both XS and PP don't need to apply the same changes.) +# +################################################################################ + +################################################################################ +# version +################################################################################ + +sub version { + return $VERSION; +} + +################################################################################ +# new +################################################################################ + +my %def_attr = ( + eol => '', + sep_char => ',', + quote_char => '"', + escape_char => '"', + binary => 0, + decode_utf8 => 1, + auto_diag => 0, + diag_verbose => 0, + strict => 0, + blank_is_undef => 0, + empty_is_undef => 0, + allow_whitespace => 0, + allow_loose_quotes => 0, + allow_loose_escapes => 0, + allow_unquoted_escape => 0, + always_quote => 0, + quote_empty => 0, + quote_space => 1, + quote_binary => 1, + escape_null => 1, + keep_meta_info => 0, + verbatim => 0, + formula => 0, + undef_str => undef, + types => undef, + callbacks => undef, + + _EOF => 0, + _RECNO => 0, + _STATUS => undef, + _FIELDS => undef, + _FFLAGS => undef, + _STRING => undef, + _ERROR_INPUT => undef, + _COLUMN_NAMES => undef, + _BOUND_COLUMNS => undef, + _AHEAD => undef, + + ENCODING => undef, +); + +my %attr_alias = ( + quote_always => "always_quote", + verbose_diag => "diag_verbose", + quote_null => "escape_null", + escape => "escape_char", + ); + +my $last_new_error = Text::CSV_PP->SetDiag(0); +my $last_error; + +# NOT a method: is also used before bless +sub _unhealthy_whitespace { + my ($self, $aw) = @_; + $aw or return 0; # no checks needed without allow_whitespace + + my $quo = $self->{quote}; + defined $quo && length ($quo) or $quo = $self->{quote_char}; + my $esc = $self->{escape_char}; + + defined $quo && $quo =~ m/^[ \t]/ and return 1002; + defined $esc && $esc =~ m/^[ \t]/ and return 1002; + + return 0; + } + +sub _check_sanity { + my $self = shift; + + my $eol = $self->{eol}; + my $sep = $self->{sep}; + defined $sep && length ($sep) or $sep = $self->{sep_char}; + my $quo = $self->{quote}; + defined $quo && length ($quo) or $quo = $self->{quote_char}; + my $esc = $self->{escape_char}; + +# use DP;::diag ("SEP: '", DPeek ($sep), +# "', QUO: '", DPeek ($quo), +# "', ESC: '", DPeek ($esc),"'"); + + # sep_char should not be undefined + $sep ne "" or return 1008; + length ($sep) > 16 and return 1006; + $sep =~ m/[\r\n]/ and return 1003; + + if (defined $quo) { + $quo eq $sep and return 1001; + length ($quo) > 16 and return 1007; + $quo =~ m/[\r\n]/ and return 1003; + } + if (defined $esc) { + $esc eq $sep and return 1001; + $esc =~ m/[\r\n]/ and return 1003; + } + if (defined $eol) { + length ($eol) > 16 and return 1005; + } + + return _unhealthy_whitespace ($self, $self->{allow_whitespace}); + } + +sub known_attributes { + sort grep !m/^_/ => "sep", "quote", keys %def_attr; + } + +sub new { + $last_new_error = Text::CSV_PP->SetDiag(1000, + 'usage: my $csv = Text::CSV_PP->new ([{ option => value, ... }]);'); + + my $proto = shift; + my $class = ref ($proto) || $proto or return; + @_ > 0 && ref $_[0] ne "HASH" and return; + my $attr = shift || {}; + my %attr = map { + my $k = m/^[a-zA-Z]\w+$/ ? lc $_ : $_; + exists $attr_alias{$k} and $k = $attr_alias{$k}; + $k => $attr->{$_}; + } keys %$attr; + + my $sep_aliased = 0; + if (exists $attr{sep}) { + $attr{sep_char} = delete $attr{sep}; + $sep_aliased = 1; + } + my $quote_aliased = 0; + if (exists $attr{quote}) { + $attr{quote_char} = delete $attr{quote}; + $quote_aliased = 1; + } + exists $attr{formula_handling} and + $attr{formula} = delete $attr{formula_handling}; + exists $attr{formula} and + $attr{formula} = _supported_formula (undef, $attr{formula}); + for (keys %attr) { + if (m/^[a-z]/ && exists $def_attr{$_}) { + # uncoverable condition false + defined $attr{$_} && m/_char$/ and utf8::decode ($attr{$_}); + next; + } +# croak? + $last_new_error = Text::CSV_PP->SetDiag(1000, "INI - Unknown attribute '$_'"); + $attr{auto_diag} and error_diag (); + return; + } + if ($sep_aliased) { + my @b = unpack "U0C*", $attr{sep_char}; + if (@b > 1) { + $attr{sep} = $attr{sep_char}; + $attr{sep_char} = "\0"; + } + else { + $attr{sep} = undef; + } + } + if ($quote_aliased and defined $attr{quote_char}) { + my @b = unpack "U0C*", $attr{quote_char}; + if (@b > 1) { + $attr{quote} = $attr{quote_char}; + $attr{quote_char} = "\0"; + } + else { + $attr{quote} = undef; + } + } + + my $self = { %def_attr, %attr }; + if (my $ec = _check_sanity ($self)) { + $last_new_error = Text::CSV_PP->SetDiag($ec); + $attr{auto_diag} and error_diag (); + return; + } + if (defined $self->{callbacks} && ref $self->{callbacks} ne "HASH") { + Carp::carp "The 'callbacks' attribute is set but is not a hash: ignored\n"; + $self->{callbacks} = undef; + } + + $last_new_error = Text::CSV_PP->SetDiag(0); + defined $\ && !exists $attr{eol} and $self->{eol} = $\; + bless $self, $class; + defined $self->{types} and $self->types ($self->{types}); + $self; +} + +# Keep in sync with XS! +my %_cache_id = ( # Only expose what is accessed from within PM + quote_char => 0, + escape_char => 1, + sep_char => 2, + sep => 39, # 39 .. 55 + binary => 3, + keep_meta_info => 4, + always_quote => 5, + allow_loose_quotes => 6, + allow_loose_escapes => 7, + allow_unquoted_escape => 8, + allow_whitespace => 9, + blank_is_undef => 10, + eol => 11, + quote => 15, + verbatim => 22, + empty_is_undef => 23, + auto_diag => 24, + diag_verbose => 33, + quote_space => 25, + quote_empty => 37, + quote_binary => 32, + escape_null => 31, + decode_utf8 => 35, + _has_ahead => 30, + _has_hooks => 36, + _is_bound => 26, # 26 .. 29 + formula => 38, + strict => 42, + undef_str => 46, + ); + +my %_hidden_cache_id = qw( + sep_len 38 + eol_len 12 + eol_is_cr 13 + quo_len 16 + has_error_input 34 +); + +my %_reverse_cache_id = ( + map({$_cache_id{$_} => $_} keys %_cache_id), + map({$_hidden_cache_id{$_} => $_} keys %_hidden_cache_id), +); + +# A `character' +sub _set_attr_C { + my ($self, $name, $val, $ec) = @_; + defined $val or $val = 0; + utf8::decode ($val); + $self->{$name} = $val; + $ec = _check_sanity ($self) and croak ($self->SetDiag ($ec)); + $self->_cache_set ($_cache_id{$name}, $val); + } + +# A flag +sub _set_attr_X { + my ($self, $name, $val) = @_; + defined $val or $val = 0; + $self->{$name} = $val; + $self->_cache_set ($_cache_id{$name}, 0 + $val); + } + +# A number +sub _set_attr_N { + my ($self, $name, $val) = @_; + $self->{$name} = $val; + $self->_cache_set ($_cache_id{$name}, 0 + $val); + } + +# Accessor methods. +# It is unwise to change them halfway through a single file! +sub quote_char { + my $self = shift; + if (@_) { + $self->_set_attr_C ("quote_char", shift); + $self->_cache_set ($_cache_id{quote}, ""); + } + $self->{quote_char}; + } + +sub quote { + my $self = shift; + if (@_) { + my $quote = shift; + defined $quote or $quote = ""; + utf8::decode ($quote); + my @b = unpack "U0C*", $quote; + if (@b > 1) { + @b > 16 and croak ($self->SetDiag (1007)); + $self->quote_char ("\0"); + } + else { + $self->quote_char ($quote); + $quote = ""; + } + $self->{quote} = $quote; + + my $ec = _check_sanity ($self); + $ec and croak ($self->SetDiag ($ec)); + + $self->_cache_set ($_cache_id{quote}, $quote); + } + my $quote = $self->{quote}; + defined $quote && length ($quote) ? $quote : $self->{quote_char}; + } + +sub escape_char { + my $self = shift; + if (@_) { + my $ec = shift; + $self->_set_attr_C ("escape_char", $ec); + $ec or $self->_set_attr_X ("escape_null", 0); + } + $self->{escape_char}; + } + +sub sep_char { + my $self = shift; + if (@_) { + $self->_set_attr_C ("sep_char", shift); + $self->_cache_set ($_cache_id{sep}, ""); + } + $self->{sep_char}; +} + +sub sep { + my $self = shift; + if (@_) { + my $sep = shift; + defined $sep or $sep = ""; + utf8::decode ($sep); + my @b = unpack "U0C*", $sep; + if (@b > 1) { + @b > 16 and croak ($self->SetDiag (1006)); + $self->sep_char ("\0"); + } + else { + $self->sep_char ($sep); + $sep = ""; + } + $self->{sep} = $sep; + + my $ec = _check_sanity ($self); + $ec and croak ($self->SetDiag ($ec)); + + $self->_cache_set ($_cache_id{sep}, $sep); + } + my $sep = $self->{sep}; + defined $sep && length ($sep) ? $sep : $self->{sep_char}; + } + +sub eol { + my $self = shift; + if (@_) { + my $eol = shift; + defined $eol or $eol = ""; + length ($eol) > 16 and croak ($self->SetDiag (1005)); + $self->{eol} = $eol; + $self->_cache_set ($_cache_id{eol}, $eol); + } + $self->{eol}; + } + +sub always_quote { + my $self = shift; + @_ and $self->_set_attr_X ("always_quote", shift); + $self->{always_quote}; + } + +sub quote_space { + my $self = shift; + @_ and $self->_set_attr_X ("quote_space", shift); + $self->{quote_space}; + } + +sub quote_empty { + my $self = shift; + @_ and $self->_set_attr_X ("quote_empty", shift); + $self->{quote_empty}; + } + +sub escape_null { + my $self = shift; + @_ and $self->_set_attr_X ("escape_null", shift); + $self->{escape_null}; + } + +sub quote_null { goto &escape_null; } + +sub quote_binary { + my $self = shift; + @_ and $self->_set_attr_X ("quote_binary", shift); + $self->{quote_binary}; + } + +sub binary { + my $self = shift; + @_ and $self->_set_attr_X ("binary", shift); + $self->{binary}; + } + +sub strict { + my $self = shift; + @_ and $self->_set_attr_X ("strict", shift); + $self->{strict}; + } + +sub _supported_formula { + my ($self, $f) = @_; + defined $f or return 5; + $f =~ m/^(?: 0 | none )$/xi ? 0 : + $f =~ m/^(?: 1 | die )$/xi ? 1 : + $f =~ m/^(?: 2 | croak )$/xi ? 2 : + $f =~ m/^(?: 3 | diag )$/xi ? 3 : + $f =~ m/^(?: 4 | empty | )$/xi ? 4 : + $f =~ m/^(?: 5 | undef )$/xi ? 5 : do { + $self ||= "Text::CSV_PP"; + $self->SetDiag (1500); + croak "formula-handling '$f' is not supported\n"; + }; + } + +sub formula { + my $self = shift; + @_ and $self->_set_attr_N ("formula", _supported_formula ($self, shift)); + [qw( none die croak diag empty undef )]->[_supported_formula ($self, $self->{formula})]; + } +sub formula_handling { + my $self = shift; + $self->formula (@_); + } + +sub decode_utf8 { + my $self = shift; + @_ and $self->_set_attr_X ("decode_utf8", shift); + $self->{decode_utf8}; +} + +sub keep_meta_info { + my $self = shift; + if (@_) { + my $v = shift; + !defined $v || $v eq "" and $v = 0; + $v =~ m/^[0-9]/ or $v = lc $v eq "false" ? 0 : 1; # true/truth = 1 + $self->_set_attr_X ("keep_meta_info", $v); + } + $self->{keep_meta_info}; + } + +sub allow_loose_quotes { + my $self = shift; + @_ and $self->_set_attr_X ("allow_loose_quotes", shift); + $self->{allow_loose_quotes}; + } + +sub allow_loose_escapes { + my $self = shift; + @_ and $self->_set_attr_X ("allow_loose_escapes", shift); + $self->{allow_loose_escapes}; + } + +sub allow_whitespace { + my $self = shift; + if (@_) { + my $aw = shift; + _unhealthy_whitespace ($self, $aw) and + croak ($self->SetDiag (1002)); + $self->_set_attr_X ("allow_whitespace", $aw); + } + $self->{allow_whitespace}; + } + +sub allow_unquoted_escape { + my $self = shift; + @_ and $self->_set_attr_X ("allow_unquoted_escape", shift); + $self->{allow_unquoted_escape}; + } + +sub blank_is_undef { + my $self = shift; + @_ and $self->_set_attr_X ("blank_is_undef", shift); + $self->{blank_is_undef}; + } + +sub empty_is_undef { + my $self = shift; + @_ and $self->_set_attr_X ("empty_is_undef", shift); + $self->{empty_is_undef}; + } + +sub verbatim { + my $self = shift; + @_ and $self->_set_attr_X ("verbatim", shift); + $self->{verbatim}; + } + +sub undef_str { + my $self = shift; + if (@_) { + my $v = shift; + $self->{undef_str} = defined $v ? "$v" : undef; + $self->_cache_set ($_cache_id{undef_str}, $self->{undef_str}); + } + $self->{undef_str}; + } + +sub auto_diag { + my $self = shift; + if (@_) { + my $v = shift; + !defined $v || $v eq "" and $v = 0; + $v =~ m/^[0-9]/ or $v = lc $v eq "false" ? 0 : 1; # true/truth = 1 + $self->_set_attr_X ("auto_diag", $v); + } + $self->{auto_diag}; + } + +sub diag_verbose { + my $self = shift; + if (@_) { + my $v = shift; + !defined $v || $v eq "" and $v = 0; + $v =~ m/^[0-9]/ or $v = lc $v eq "false" ? 0 : 1; # true/truth = 1 + $self->_set_attr_X ("diag_verbose", $v); + } + $self->{diag_verbose}; + } + +################################################################################ +# status +################################################################################ + +sub status { + $_[0]->{_STATUS}; +} + +sub eof { + $_[0]->{_EOF}; +} + +sub types { + my $self = shift; + + if (@_) { + if (my $types = shift) { + $self->{'_types'} = join("", map{ chr($_) } @$types); + $self->{'types'} = $types; + } + else { + delete $self->{'types'}; + delete $self->{'_types'}; + undef; + } + } + else { + $self->{'types'}; + } +} + +sub callbacks { + my $self = shift; + if (@_) { + my $cb; + my $hf = 0x00; + if (defined $_[0]) { + grep { !defined } @_ and croak ($self->SetDiag (1004)); + $cb = @_ == 1 && ref $_[0] eq "HASH" ? shift + : @_ % 2 == 0 ? { @_ } + : croak ($self->SetDiag (1004)); + foreach my $cbk (keys %$cb) { + # A key cannot be a ref. That would be stored as the *string + # 'SCALAR(0x1f3e710)' or 'ARRAY(0x1a5ae18)' + $cbk =~ m/^[\w.]+$/ && ref $cb->{$cbk} eq "CODE" or + croak ($self->SetDiag (1004)); + } + exists $cb->{error} and $hf |= 0x01; + exists $cb->{after_parse} and $hf |= 0x02; + exists $cb->{before_print} and $hf |= 0x04; + } + elsif (@_ > 1) { + # (undef, whatever) + croak ($self->SetDiag (1004)); + } + $self->_set_attr_X ("_has_hooks", $hf); + $self->{callbacks} = $cb; + } + $self->{callbacks}; + } + +################################################################################ +# error_diag +################################################################################ + +sub error_diag { + my $self = shift; + my @diag = (0 + $last_new_error, $last_new_error, 0, 0, 0); + + # Docs state to NEVER use UNIVERSAL::isa, because it will *never* call an + # overridden isa method in any class. Well, that is exacly what I want here + if ($self && ref $self && # Not a class method or direct call + UNIVERSAL::isa ($self, __PACKAGE__) && exists $self->{_ERROR_DIAG}) { + $diag[0] = 0 + $self->{_ERROR_DIAG}; + $diag[1] = $self->{_ERROR_DIAG}; + $diag[2] = 1 + $self->{_ERROR_POS} if exists $self->{_ERROR_POS}; + $diag[3] = $self->{_RECNO}; + $diag[4] = $self->{_ERROR_FLD} if exists $self->{_ERROR_FLD}; + + $diag[0] && $self->{callbacks} && $self->{callbacks}{error} and + return $self->{callbacks}{error}->(@diag); + } + + my $context = wantarray; + + unless (defined $context) { # Void context, auto-diag + if ($diag[0] && $diag[0] != 2012) { + my $msg = "# CSV_PP ERROR: $diag[0] - $diag[1] \@ rec $diag[3] pos $diag[2]\n"; + $diag[4] and $msg =~ s/$/ field $diag[4]/; + + unless ($self && ref $self) { # auto_diag + # called without args in void context + warn $msg; + return; + } + + if ($self->{diag_verbose} and $self->{_ERROR_INPUT}) { + $msg .= "$self->{_ERROR_INPUT}'\n"; + $msg .= " " x ($diag[2] - 1); + $msg .= "^\n"; + } + + my $lvl = $self->{auto_diag}; + if ($lvl < 2) { + my @c = caller (2); + if (@c >= 11 && $c[10] && ref $c[10] eq "HASH") { + my $hints = $c[10]; + (exists $hints->{autodie} && $hints->{autodie} or + exists $hints->{"guard Fatal"} && + !exists $hints->{"no Fatal"}) and + $lvl++; + # Future releases of autodie will probably set $^H{autodie} + # to "autodie @args", like "autodie :all" or "autodie open" + # so we can/should check for "open" or "new" + } + } + $lvl > 1 ? die $msg : warn $msg; + } + return; + } + + return $context ? @diag : $diag[1]; +} + +sub record_number { + return shift->{_RECNO}; +} + +################################################################################ +# string +################################################################################ + +*string = \&_string; +sub _string { + defined $_[0]->{_STRING} ? ${ $_[0]->{_STRING} } : undef; +} + +################################################################################ +# fields +################################################################################ + +*fields = \&_fields; +sub _fields { + ref($_[0]->{_FIELDS}) ? @{$_[0]->{_FIELDS}} : undef; +} + +################################################################################ +# meta_info +################################################################################ + +sub meta_info { + $_[0]->{_FFLAGS} ? @{ $_[0]->{_FFLAGS} } : undef; +} + +sub is_quoted { + return unless (defined $_[0]->{_FFLAGS}); + return if( $_[1] =~ /\D/ or $_[1] < 0 or $_[1] > $#{ $_[0]->{_FFLAGS} } ); + + $_[0]->{_FFLAGS}->[$_[1]] & IS_QUOTED ? 1 : 0; +} + +sub is_binary { + return unless (defined $_[0]->{_FFLAGS}); + return if( $_[1] =~ /\D/ or $_[1] < 0 or $_[1] > $#{ $_[0]->{_FFLAGS} } ); + $_[0]->{_FFLAGS}->[$_[1]] & IS_BINARY ? 1 : 0; +} + +sub is_missing { + my ($self, $idx, $val) = @_; + return unless $self->{keep_meta_info}; # FIXME + $idx < 0 || !ref $self->{_FFLAGS} and return; + $idx >= @{$self->{_FFLAGS}} and return 1; + $self->{_FFLAGS}[$idx] & IS_MISSING ? 1 : 0; +} + +################################################################################ +# combine +################################################################################ +*combine = \&_combine; +sub _combine { + my ($self, @fields) = @_; + my $str = ""; + $self->{_FIELDS} = \@fields; + $self->{_STATUS} = (@fields > 0) && $self->__combine(\$str, \@fields, 0); + $self->{_STRING} = \$str; + $self->{_STATUS}; + } + +################################################################################ +# parse +################################################################################ +*parse = \&_parse; +sub _parse { + my ($self, $str) = @_; + + ref $str and croak ($self->SetDiag (1500)); + + my $fields = []; + my $fflags = []; + $self->{_STRING} = \$str; + if (defined $str && $self->__parse ($fields, $fflags, $str, 0)) { + $self->{_FIELDS} = $fields; + $self->{_FFLAGS} = $fflags; + $self->{_STATUS} = 1; + } + else { + $self->{_FIELDS} = undef; + $self->{_FFLAGS} = undef; + $self->{_STATUS} = 0; + } + $self->{_STATUS}; + } + +sub column_names { + my ( $self, @columns ) = @_; + + @columns or return defined $self->{_COLUMN_NAMES} ? @{$self->{_COLUMN_NAMES}} : (); + @columns == 1 && ! defined $columns[0] and return $self->{_COLUMN_NAMES} = undef; + + if ( @columns == 1 && ref $columns[0] eq "ARRAY" ) { + @columns = @{ $columns[0] }; + } + elsif ( join "", map { defined $_ ? ref $_ : "" } @columns ) { + croak $self->SetDiag( 3001 ); + } + + if ( $self->{_BOUND_COLUMNS} && @columns != @{$self->{_BOUND_COLUMNS}} ) { + croak $self->SetDiag( 3003 ); + } + + $self->{_COLUMN_NAMES} = [ map { defined $_ ? $_ : "\cAUNDEF\cA" } @columns ]; + @{ $self->{_COLUMN_NAMES} }; +} + +sub header { + my ($self, $fh, @args) = @_; + + $fh or croak ($self->SetDiag (1014)); + + my (@seps, %args); + for (@args) { + if (ref $_ eq "ARRAY") { + push @seps, @$_; + next; + } + if (ref $_ eq "HASH") { + %args = %$_; + next; + } + croak (q{usage: $csv->header ($fh, [ seps ], { options })}); + } + + defined $args{detect_bom} or $args{detect_bom} = 1; + defined $args{set_column_names} or $args{set_column_names} = 1; + defined $args{munge_column_names} or $args{munge_column_names} = "lc"; + + # Reset any previous leftovers + $self->{_RECNO} = 0; + $self->{_AHEAD} = undef; + $self->{_COLUMN_NAMES} = undef if $args{set_column_names}; + $self->{_BOUND_COLUMNS} = undef if $args{set_column_names}; + $self->_cache_set($_cache_id{'_has_ahead'}, 0); + + if (defined $args{sep_set}) { + ref $args{sep_set} eq "ARRAY" or + croak ($self->SetDiag (1500, "sep_set should be an array ref")); + @seps = @{$args{sep_set}}; + } + + $^O eq "MSWin32" and binmode $fh; + my $hdr = <$fh>; + # check if $hdr can be empty here, I don't think so + defined $hdr && $hdr ne "" or croak ($self->SetDiag (1010)); + + my %sep; + @seps or @seps = (",", ";"); + foreach my $sep (@seps) { + index ($hdr, $sep) >= 0 and $sep{$sep}++; + } + + keys %sep >= 2 and croak ($self->SetDiag (1011)); + + $self->sep (keys %sep); + my $enc = ""; + if ($args{detect_bom}) { # UTF-7 is not supported + if ($hdr =~ s/^\x00\x00\xfe\xff//) { $enc = "utf-32be" } + elsif ($hdr =~ s/^\xff\xfe\x00\x00//) { $enc = "utf-32le" } + elsif ($hdr =~ s/^\xfe\xff//) { $enc = "utf-16be" } + elsif ($hdr =~ s/^\xff\xfe//) { $enc = "utf-16le" } + elsif ($hdr =~ s/^\xef\xbb\xbf//) { $enc = "utf-8" } + elsif ($hdr =~ s/^\xf7\x64\x4c//) { $enc = "utf-1" } + elsif ($hdr =~ s/^\xdd\x73\x66\x73//) { $enc = "utf-ebcdic" } + elsif ($hdr =~ s/^\x0e\xfe\xff//) { $enc = "scsu" } + elsif ($hdr =~ s/^\xfb\xee\x28//) { $enc = "bocu-1" } + elsif ($hdr =~ s/^\x84\x31\x95\x33//) { $enc = "gb-18030" } + elsif ($hdr =~ s/^\x{feff}//) { $enc = "" } + + $self->{ENCODING} = uc $enc; + + $hdr eq "" and croak ($self->SetDiag (1010)); + + if ($enc) { + if ($enc =~ m/([13]).le$/) { + my $l = 0 + $1; + my $x; + $hdr .= "\0" x $l; + read $fh, $x, $l; + } + if ($enc ne "utf-8") { + require Encode; + $hdr = Encode::decode ($enc, $hdr); + } + binmode $fh, ":encoding($enc)"; + } + } + + my ($ahead, $eol); + if ($hdr =~ s/^([^\r\n]+)([\r\n]+)([^\r\n].+)\z/$1/s) { + $eol = $2; + $ahead = $3; + } + + $args{munge_column_names} eq "lc" and $hdr = lc $hdr; + $args{munge_column_names} eq "uc" and $hdr = uc $hdr; + + my $hr = \$hdr; # Will cause croak on perl-5.6.x + open my $h, "<", $hr or croak ($self->SetDiag (1010)); + + my $row = $self->getline ($h) or croak; + close $h; + + if ($ahead) { # Must be after getline, which creates the cache + $self->_cache_set ($_cache_id{_has_ahead}, 1); + $self->{_AHEAD} = $ahead; + $eol =~ m/^\r([^\n]|\z)/ and $self->eol ($eol); + } + + my @hdr = @$row; + ref $args{munge_column_names} eq "CODE" and + @hdr = map { $args{munge_column_names}->($_) } @hdr; + ref $args{munge_column_names} eq "HASH" and + @hdr = map { $args{munge_column_names}->{$_} || $_ } @hdr; + my %hdr = map { $_ => 1 } @hdr; + exists $hdr{""} and croak ($self->SetDiag (1012)); + keys %hdr == @hdr or croak ($self->SetDiag (1013)); + $args{set_column_names} and $self->column_names (@hdr); + wantarray ? @hdr : $self; + } + +sub bind_columns { + my ( $self, @refs ) = @_; + + @refs or return defined $self->{_BOUND_COLUMNS} ? @{$self->{_BOUND_COLUMNS}} : undef; + @refs == 1 && ! defined $refs[0] and return $self->{_BOUND_COLUMNS} = undef; + + if ( $self->{_COLUMN_NAMES} && @refs != @{$self->{_COLUMN_NAMES}} ) { + croak $self->SetDiag( 3003 ); + } + + if ( grep { ref $_ ne "SCALAR" } @refs ) { # why don't use grep? + croak $self->SetDiag( 3004 ); + } + + $self->_set_attr_N("_is_bound", scalar @refs); + $self->{_BOUND_COLUMNS} = [ @refs ]; + @refs; +} + +sub getline_hr { + my ($self, @args, %hr) = @_; + $self->{_COLUMN_NAMES} or croak ($self->SetDiag (3002)); + my $fr = $self->getline (@args) or return; + if (ref $self->{_FFLAGS}) { # missing + $self->{_FFLAGS}[$_] = IS_MISSING + for (@$fr ? $#{$fr} + 1 : 0) .. $#{$self->{_COLUMN_NAMES}}; + @$fr == 1 && (!defined $fr->[0] || $fr->[0] eq "") and + $self->{_FFLAGS}[0] ||= IS_MISSING; + } + @hr{@{$self->{_COLUMN_NAMES}}} = @$fr; + \%hr; +} + +sub getline_hr_all { + my ( $self, $io, @args ) = @_; + + unless ( $self->{_COLUMN_NAMES} ) { + croak $self->SetDiag( 3002 ); + } + + my @cn = @{$self->{_COLUMN_NAMES}}; + + return [ map { my %h; @h{ @cn } = @$_; \%h } @{ $self->getline_all( $io, @args ) } ]; +} + +sub say { + my ($self, $io, @f) = @_; + my $eol = $self->eol; + $eol eq "" and $self->eol ($\ || $/); + # say ($fh, undef) does not propage actual undef to print () + my $state = $self->print ($io, @f == 1 && !defined $f[0] ? undef : @f); + $self->eol ($eol); + return $state; + } + +sub print_hr { + my ($self, $io, $hr) = @_; + $self->{_COLUMN_NAMES} or croak($self->SetDiag(3009)); + ref $hr eq "HASH" or croak($self->SetDiag(3010)); + $self->print ($io, [ map { $hr->{$_} } $self->column_names ]); +} + +sub fragment { + my ($self, $io, $spec) = @_; + + my $qd = qr{\s* [0-9]+ \s* }x; # digit + my $qs = qr{\s* (?: [0-9]+ | \* ) \s*}x; # digit or star + my $qr = qr{$qd (?: - $qs )?}x; # range + my $qc = qr{$qr (?: ; $qr )*}x; # list + defined $spec && $spec =~ m{^ \s* + \x23 ? \s* # optional leading # + ( row | col | cell ) \s* = + ( $qc # for row and col + | $qd , $qd (?: - $qs , $qs)? # for cell (ranges) + (?: ; $qd , $qd (?: - $qs , $qs)? )* # and cell (range) lists + ) \s* $}xi or croak ($self->SetDiag (2013)); + my ($type, $range) = (lc $1, $2); + + my @h = $self->column_names (); + + my @c; + if ($type eq "cell") { + my @spec; + my $min_row; + my $max_row = 0; + for (split m/\s*;\s*/ => $range) { + my ($tlr, $tlc, $brr, $brc) = (m{ + ^ \s* ([0-9]+ ) \s* , \s* ([0-9]+ ) \s* + (?: - \s* ([0-9]+ | \*) \s* , \s* ([0-9]+ | \*) \s* )? + $}x) or croak ($self->SetDiag (2013)); + defined $brr or ($brr, $brc) = ($tlr, $tlc); + $tlr == 0 || $tlc == 0 || + ($brr ne "*" && ($brr == 0 || $brr < $tlr)) || + ($brc ne "*" && ($brc == 0 || $brc < $tlc)) + and croak ($self->SetDiag (2013)); + $tlc--; + $brc-- unless $brc eq "*"; + defined $min_row or $min_row = $tlr; + $tlr < $min_row and $min_row = $tlr; + $brr eq "*" || $brr > $max_row and + $max_row = $brr; + push @spec, [ $tlr, $tlc, $brr, $brc ]; + } + my $r = 0; + while (my $row = $self->getline ($io)) { + ++$r < $min_row and next; + my %row; + my $lc; + foreach my $s (@spec) { + my ($tlr, $tlc, $brr, $brc) = @$s; + $r < $tlr || ($brr ne "*" && $r > $brr) and next; + !defined $lc || $tlc < $lc and $lc = $tlc; + my $rr = $brc eq "*" ? $#$row : $brc; + $row{$_} = $row->[$_] for $tlc .. $rr; + } + push @c, [ @row{sort { $a <=> $b } keys %row } ]; + if (@h) { + my %h; @h{@h} = @{$c[-1]}; + $c[-1] = \%h; + } + $max_row ne "*" && $r == $max_row and last; + } + return \@c; + } + + # row or col + my @r; + my $eod = 0; + for (split m/\s*;\s*/ => $range) { + my ($from, $to) = m/^\s* ([0-9]+) (?: \s* - \s* ([0-9]+ | \* ))? \s* $/x + or croak ($self->SetDiag (2013)); + $to ||= $from; + $to eq "*" and ($to, $eod) = ($from, 1); + # $to cannot be <= 0 due to regex and ||= + $from <= 0 || $to < $from and croak ($self->SetDiag (2013)); + $r[$_] = 1 for $from .. $to; + } + + my $r = 0; + $type eq "col" and shift @r; + $_ ||= 0 for @r; + while (my $row = $self->getline ($io)) { + $r++; + if ($type eq "row") { + if (($r > $#r && $eod) || $r[$r]) { + push @c, $row; + if (@h) { + my %h; @h{@h} = @{$c[-1]}; + $c[-1] = \%h; + } + } + next; + } + push @c, [ map { ($_ > $#r && $eod) || $r[$_] ? $row->[$_] : () } 0..$#$row ]; + if (@h) { + my %h; @h{@h} = @{$c[-1]}; + $c[-1] = \%h; + } + } + + return \@c; + } + +my $csv_usage = q{usage: my $aoa = csv (in => $file);}; + +sub _csv_attr { + my %attr = (@_ == 1 && ref $_[0] eq "HASH" ? %{$_[0]} : @_) or croak; + + $attr{binary} = 1; + + my $enc = delete $attr{enc} || delete $attr{encoding} || ""; + $enc eq "auto" and ($attr{detect_bom}, $enc) = (1, ""); + $enc =~ m/^[-\w.]+$/ and $enc = ":encoding($enc)"; + + my $fh; + my $sink = 0; + my $cls = 0; # If I open a file, I have to close it + my $in = delete $attr{in} || delete $attr{file} or croak $csv_usage; + my $out = exists $attr{out} && !$attr{out} ? \"skip" + : delete $attr{out} || delete $attr{file}; + + ref $in eq "CODE" || ref $in eq "ARRAY" and $out ||= \*STDOUT; + + $in && $out && !ref $in && !ref $out and croak join "\n" => + qq{Cannot use a string for both in and out. Instead use:}, + qq{ csv (in => csv (in => "$in"), out => "$out");\n}; + + if ($out) { + if ((ref $out and "SCALAR" ne ref $out) or "GLOB" eq ref \$out) { + $fh = $out; + } + elsif (ref $out and "SCALAR" eq ref $out and defined $$out and $$out eq "skip") { + delete $attr{out}; + $sink = 1; + } + else { + open $fh, ">", $out or croak "$out: $!"; + $cls = 1; + } + if ($fh) { + $enc and binmode $fh, $enc; + unless (defined $attr{eol}) { + my @layers = eval { PerlIO::get_layers ($fh) }; + $attr{eol} = (grep m/crlf/ => @layers) ? "\n" : "\r\n"; + } + } + } + + if ( ref $in eq "CODE" or ref $in eq "ARRAY") { + # All done + } + elsif (ref $in eq "SCALAR") { + # Strings with code points over 0xFF may not be mapped into in-memory file handles + # "<$enc" does not change that :( + open $fh, "<", $in or croak "Cannot open from SCALAR using PerlIO"; + $cls = 1; + } + elsif (ref $in or "GLOB" eq ref \$in) { + if (!ref $in && $] < 5.008005) { + $fh = \*$in; # uncoverable statement ancient perl version required + } + else { + $fh = $in; + } + } + else { + open $fh, "<$enc", $in or croak "$in: $!"; + $cls = 1; + } + $fh || $sink or croak qq{No valid source passed. "in" is required}; + + my $hdrs = delete $attr{headers}; + my $frag = delete $attr{fragment}; + my $key = delete $attr{key}; + my $kh = delete $attr{keep_headers} || + delete $attr{keep_column_names} || + delete $attr{kh}; + + my $cbai = delete $attr{callbacks}{after_in} || + delete $attr{after_in} || + delete $attr{callbacks}{after_parse} || + delete $attr{after_parse}; + my $cbbo = delete $attr{callbacks}{before_out} || + delete $attr{before_out}; + my $cboi = delete $attr{callbacks}{on_in} || + delete $attr{on_in}; + + my $hd_s = delete $attr{sep_set} || + delete $attr{seps}; + my $hd_b = delete $attr{detect_bom} || + delete $attr{bom}; + my $hd_m = delete $attr{munge} || + delete $attr{munge_column_names}; + my $hd_c = delete $attr{set_column_names}; + + for ([ quo => "quote" ], + [ esc => "escape" ], + [ escape => "escape_char" ], + ) { + my ($f, $t) = @$_; + exists $attr{$f} and !exists $attr{$t} and $attr{$t} = delete $attr{$f}; + } + + my $fltr = delete $attr{filter}; + my %fltr = ( + not_blank => sub { @{$_[1]} > 1 or defined $_[1][0] && $_[1][0] ne "" }, + not_empty => sub { grep { defined && $_ ne "" } @{$_[1]} }, + filled => sub { grep { defined && m/\S/ } @{$_[1]} }, + ); + defined $fltr && !ref $fltr && exists $fltr{$fltr} and + $fltr = { 0 => $fltr{$fltr} }; + ref $fltr eq "CODE" and $fltr = { 0 => $fltr }; + ref $fltr eq "HASH" or $fltr = undef; + + exists $attr{formula} and + $attr{formula} = _supported_formula (undef, $attr{formula}); + + defined $attr{auto_diag} or $attr{auto_diag} = 1; + defined $attr{escape_null} or $attr{escape_null} = 0; + my $csv = delete $attr{csv} || Text::CSV_PP->new (\%attr) + or croak $last_new_error; + + return { + csv => $csv, + attr => { %attr }, + fh => $fh, + cls => $cls, + in => $in, + sink => $sink, + out => $out, + enc => $enc, + hdrs => $hdrs, + key => $key, + kh => $kh, + frag => $frag, + fltr => $fltr, + cbai => $cbai, + cbbo => $cbbo, + cboi => $cboi, + hd_s => $hd_s, + hd_b => $hd_b, + hd_m => $hd_m, + hd_c => $hd_c, + }; + } + +sub csv { + @_ && (ref $_[0] eq __PACKAGE__ or ref $_[0] eq 'Text::CSV') and splice @_, 0, 0, "csv"; + @_ or croak $csv_usage; + + my $c = _csv_attr (@_); + + my ($csv, $in, $fh, $hdrs) = @{$c}{"csv", "in", "fh", "hdrs"}; + my %hdr; + if (ref $hdrs eq "HASH") { + %hdr = %$hdrs; + $hdrs = "auto"; + } + + if ($c->{out} && !$c->{sink}) { + if (ref $in eq "CODE") { + my $hdr = 1; + while (my $row = $in->($csv)) { + if (ref $row eq "ARRAY") { + $csv->print ($fh, $row); + next; + } + if (ref $row eq "HASH") { + if ($hdr) { + $hdrs ||= [ map { $hdr{$_} || $_ } keys %$row ]; + $csv->print ($fh, $hdrs); + $hdr = 0; + } + $csv->print ($fh, [ @{$row}{@$hdrs} ]); + } + } + } + elsif (ref $in->[0] eq "ARRAY") { # aoa + ref $hdrs and $csv->print ($fh, $hdrs); + for (@{$in}) { + $c->{cboi} and $c->{cboi}->($csv, $_); + $c->{cbbo} and $c->{cbbo}->($csv, $_); + $csv->print ($fh, $_); + } + } + else { # aoh + my @hdrs = ref $hdrs ? @{$hdrs} : keys %{$in->[0]}; + defined $hdrs or $hdrs = "auto"; + ref $hdrs || $hdrs eq "auto" and + $csv->print ($fh, [ map { $hdr{$_} || $_ } @hdrs ]); + for (@{$in}) { + local %_; + *_ = $_; + $c->{cboi} and $c->{cboi}->($csv, $_); + $c->{cbbo} and $c->{cbbo}->($csv, $_); + $csv->print ($fh, [ @{$_}{@hdrs} ]); + } + } + + $c->{cls} and close $fh; + return 1; + } + + my @row1; + if (defined $c->{hd_s} || defined $c->{hd_b} || defined $c->{hd_m} || defined $c->{hd_c}) { + my %harg; + defined $c->{hd_s} and $harg{set_set} = $c->{hd_s}; + defined $c->{hd_d} and $harg{detect_bom} = $c->{hd_b}; + defined $c->{hd_m} and $harg{munge_column_names} = $hdrs ? "none" : $c->{hd_m}; + defined $c->{hd_c} and $harg{set_column_names} = $hdrs ? 0 : $c->{hd_c}; + @row1 = $csv->header ($fh, \%harg); + my @hdr = $csv->column_names; + @hdr and $hdrs ||= \@hdr; + } + + if ($c->{kh}) { + ref $c->{kh} eq "ARRAY" or croak ($csv->SetDiag (1501, "1501 - PRM")); + $hdrs ||= "auto"; + } + + my $key = $c->{key}; + if ($key) { + ref $key and croak ($csv->SetDiag (1501, "1501 - PRM")); + $hdrs ||= "auto"; + } + + $c->{fltr} && grep m/\D/ => keys %{$c->{fltr}} and $hdrs ||= "auto"; + if (defined $hdrs) { + if (!ref $hdrs) { + if ($hdrs eq "skip") { + $csv->getline ($fh); # discard; + } + elsif ($hdrs eq "auto") { + my $h = $csv->getline ($fh) or return; + $hdrs = [ map { $hdr{$_} || $_ } @$h ]; + } + elsif ($hdrs eq "lc") { + my $h = $csv->getline ($fh) or return; + $hdrs = [ map { lc ($hdr{$_} || $_) } @$h ]; + } + elsif ($hdrs eq "uc") { + my $h = $csv->getline ($fh) or return; + $hdrs = [ map { uc ($hdr{$_} || $_) } @$h ]; + } + } + elsif (ref $hdrs eq "CODE") { + my $h = $csv->getline ($fh) or return; + my $cr = $hdrs; + $hdrs = [ map { $cr->($hdr{$_} || $_) } @$h ]; + } + $c->{kh} and $hdrs and @{$c->{kh}} = @$hdrs; + } + + if ($c->{fltr}) { + my %f = %{$c->{fltr}}; + # convert headers to index + my @hdr; + if (ref $hdrs) { + @hdr = @{$hdrs}; + for (0 .. $#hdr) { + exists $f{$hdr[$_]} and $f{$_ + 1} = delete $f{$hdr[$_]}; + } + } + $csv->callbacks (after_parse => sub { + my ($CSV, $ROW) = @_; # lexical sub-variables in caps + foreach my $FLD (sort keys %f) { + local $_ = $ROW->[$FLD - 1]; + local %_; + @hdr and @_{@hdr} = @$ROW; + $f{$FLD}->($CSV, $ROW) or return \"skip"; + $ROW->[$FLD - 1] = $_; + } + }); + } + + my $frag = $c->{frag}; + my $ref = ref $hdrs + ? # aoh + do { + $csv->column_names ($hdrs); + $frag ? $csv->fragment ($fh, $frag) : + $key ? { map { $_->{$key} => $_ } @{$csv->getline_hr_all ($fh)} } + : $csv->getline_hr_all ($fh); + } + : # aoa + $frag ? $csv->fragment ($fh, $frag) + : $csv->getline_all ($fh); + if ($ref) { + @row1 && !$c->{hd_c} && !ref $hdrs and unshift @$ref, \@row1; + } + else { + Text::CSV_PP->auto_diag; + } + $c->{cls} and close $fh; + if ($ref and $c->{cbai} || $c->{cboi}) { + # Default is ARRAYref, but with key =>, you'll get a hashref + foreach my $r (ref $ref eq "ARRAY" ? @{$ref} : values %{$ref}) { + local %_; + ref $r eq "HASH" and *_ = $r; + $c->{cbai} and $c->{cbai}->($csv, $r); + $c->{cboi} and $c->{cboi}->($csv, $r); + } + } + + $c->{sink} and return; + + defined wantarray or + return csv (%{$c->{attr}}, in => $ref, headers => $hdrs, %{$c->{attr}}); + + return $ref; + } + +# The end of the common pure perl part. + +################################################################################ +# +# The following are methods implemented in XS in Text::CSV_XS or +# helper methods for Text::CSV_PP only +# +################################################################################ + +sub _setup_ctx { + my $self = shift; + + $last_error = undef; + + my $ctx; + if ($self->{_CACHE}) { + %$ctx = %{$self->{_CACHE}}; + } else { + $ctx->{sep} = ','; + if (defined $self->{sep_char}) { + $ctx->{sep} = $self->{sep_char}; + } + if (defined $self->{sep} and $self->{sep} ne '') { + use bytes; + $ctx->{sep} = $self->{sep}; + my $sep_len = length($ctx->{sep}); + $ctx->{sep_len} = $sep_len if $sep_len > 1; + } + + $ctx->{quo} = '"'; + if (exists $self->{quote_char}) { + my $quote_char = $self->{quote_char}; + if (defined $quote_char and length $quote_char) { + $ctx->{quo} = $quote_char; + } else { + $ctx->{quo} = "\0"; + } + } + if (defined $self->{quote} and $self->{quote} ne '') { + use bytes; + $ctx->{quo} = $self->{quote}; + my $quote_len = length($ctx->{quo}); + $ctx->{quo_len} = $quote_len if $quote_len > 1; + } + + $ctx->{escape_char} = '"'; + if (exists $self->{escape_char}) { + my $escape_char = $self->{escape_char}; + if (defined $escape_char and length $escape_char) { + $ctx->{escape_char} = $escape_char; + } else { + $ctx->{escape_char} = "\0"; + } + } + + if (defined $self->{eol}) { + my $eol = $self->{eol}; + my $eol_len = length($eol); + $ctx->{eol} = $eol; + $ctx->{eol_len} = $eol_len; + if ($eol_len == 1 and $eol eq "\015") { + $ctx->{eol_is_cr} = 1; + } + } + + $ctx->{undef_flg} = 0; + if (defined $self->{undef_str}) { + $ctx->{undef_str} = $self->{undef_str}; + $ctx->{undef_flg} = 3 if utf8::is_utf8($self->{undef_str}); + } else { + $ctx->{undef_str} = undef; + } + + if (defined $self->{_types}) { + $ctx->{types} = $self->{_types}; + $ctx->{types_len} = length($ctx->{types}); + } + + if (defined $self->{_is_bound}) { + $ctx->{is_bound} = $self->{_is_bound}; + } + + if (defined $self->{callbacks}) { + my $cb = $self->{callbacks}; + $ctx->{has_hooks} = 0; + if (defined $cb->{after_parse} and ref $cb->{after_parse} eq 'CODE') { + $ctx->{has_hooks} |= HOOK_AFTER_PARSE; + } + if (defined $cb->{before_print} and ref $cb->{before_print} eq 'CODE') { + $ctx->{has_hooks} |= HOOK_BEFORE_PRINT; + } + } + + for (qw/ + binary decode_utf8 always_quote strict quote_empty + allow_loose_quotes allow_loose_escapes + allow_unquoted_escape allow_whitespace blank_is_undef + empty_is_undef verbatim auto_diag diag_verbose + keep_meta_info formula + /) { + $ctx->{$_} = defined $self->{$_} ? $self->{$_} : 0; + } + for (qw/quote_space escape_null quote_binary/) { + $ctx->{$_} = defined $self->{$_} ? $self->{$_} : 1; + } + if ($ctx->{escape_char} eq "\0") { + $ctx->{escape_null} = 0; + } + + # FIXME: readonly + %{$self->{_CACHE}} = %$ctx; + } + + $ctx->{utf8} = 0; + $ctx->{size} = 0; + $ctx->{used} = 0; + + if ($ctx->{is_bound}) { + my $bound = $self->{_BOUND_COLUMNS}; + if ($bound and ref $bound eq 'ARRAY') { + $ctx->{bound} = $bound; + } else { + $ctx->{is_bound} = 0; + } + } + + $ctx->{eol_pos} = -1; + $ctx->{eolx} = $ctx->{eol_len} + ? $ctx->{verbatim} || $ctx->{eol_len} >= 2 + ? 1 + : $ctx->{eol} =~ /\A[\015|\012]/ ? 0 : 1 + : 0; + + if ($ctx->{sep_len} and $ctx->{sep_len} > 1 and _is_valid_utf8($ctx->{sep})) { + $ctx->{utf8} = 1; + } + if ($ctx->{quo_len} and $ctx->{quo_len} > 1 and _is_valid_utf8($ctx->{quo})) { + $ctx->{utf8} = 1; + } + + $ctx; +} + +sub _cache_set { + my ($self, $idx, $value) = @_; + return unless exists $self->{_CACHE}; + my $cache = $self->{_CACHE}; + + my $key = $_reverse_cache_id{$idx}; + if (!defined $key) { + warn (sprintf "Unknown cache index %d ignored\n", $idx); + } elsif ($key eq 'sep_char') { + $cache->{sep} = $value; + $cache->{sep_len} = 0; + } + elsif ($key eq 'quote_char') { + $cache->{quo} = $value; + $cache->{quo_len} = 0; + } + elsif ($key eq '_has_ahead') { + $cache->{has_ahead} = $value; + } + elsif ($key eq '_has_hooks') { + $cache->{has_hooks} = $value; + } + elsif ($key eq '_is_bound') { + $cache->{is_bound} = $value; + } + elsif ($key eq 'sep') { + use bytes; + my $len = bytes::length($value); + $cache->{sep} = $value if $len; + $cache->{sep_len} = $len == 1 ? 0 : $len; + } + elsif ($key eq 'quote') { + use bytes; + my $len = bytes::length($value); + $cache->{quo} = $value if $len; + $cache->{quo_len} = $len == 1 ? 0 : $len; + } + elsif ($key eq 'eol') { + $cache->{eol} = $value if defined($value); + $cache->{eol_is_cr} = $value eq "\015" ? 1 : 0; + } + elsif ($key eq 'undef_str') { + if (defined $value) { + $cache->{undef_str} = $value; + $cache->{undef_flg} = 3 if utf8::is_utf8($value); + } else { + $cache->{undef_str} = undef; + $cache->{undef_flg} = 0; + } + } + else { + $cache->{$key} = $value; + } + return 1; +} + +sub _cache_diag { + my $self = shift; + unless (exists $self->{_CACHE}) { + warn ("CACHE: invalid\n"); + return; + } + + my $cache = $self->{_CACHE}; + warn ("CACHE:\n"); + $self->__cache_show_char(quote_char => $cache->{quo}); + $self->__cache_show_char(escape_char => $cache->{escape_char}); + $self->__cache_show_char(sep_char => $cache->{sep}); + for (qw/ + binary decode_utf8 allow_loose_escapes allow_loose_quotes + allow_whitespace always_quote quote_empty quote_space + escape_null quote_binary auto_diag diag_verbose formula strict + has_error_input blank_is_undef empty_is_undef has_ahead + keep_meta_info verbatim has_hooks eol_is_cr eol_len + /) { + $self->__cache_show_byte($_ => $cache->{$_}); + } + $self->__cache_show_str(eol => $cache->{eol_len}, $cache->{eol}); + $self->__cache_show_byte(sep_len => $cache->{sep_len}); + if ($cache->{sep_len} and $cache->{sep_len} > 1) { + $self->__cache_show_str(sep => $cache->{sep_len}, $cache->{sep}); + } + $self->__cache_show_byte(quo_len => $cache->{quo_len}); + if ($cache->{quo_len} and $cache->{quo_len} > 1) { + $self->__cache_show_str(quote => $cache->{quo_len}, $cache->{quo}); + } +} + +sub __cache_show_byte { + my ($self, $key, $value) = @_; + warn (sprintf " %-21s %02x:%3d\n", $key, defined $value ? ord($value) : 0, defined $value ? $value : 0); +} + +sub __cache_show_char { + my ($self, $key, $value) = @_; + my $v = $value; + if (defined $value) { + my @b = unpack "U0C*", $value; + $v = pack "U*", $b[0]; + } + warn (sprintf " %-21s %02x:%s\n", $key, defined $v ? ord($v) : 0, $self->__pretty_str($v, 1)); +} + +sub __cache_show_str { + my ($self, $key, $len, $value) = @_; + warn (sprintf " %-21s %02d:%s\n", $key, $len, $self->__pretty_str($value, $len)); +} + +sub __pretty_str { # FIXME + my ($self, $str, $len) = @_; + return '' unless defined $str; + $str = substr($str, 0, $len); + $str =~ s/"/\\"/g; + $str =~ s/([^\x09\x20-\x7e])/sprintf '\\x{%x}', ord($1)/eg; + qq{"$str"}; +} + +sub _hook { + my ($self, $name, $fields) = @_; + return 0 unless $self->{callbacks}; + + my $cb = $self->{callbacks}{$name}; + return 0 unless $cb && ref $cb eq 'CODE'; + + my (@res) = $cb->($self, $fields); + if (@res) { + return 0 if ref $res[0] eq 'SCALAR' and ${$res[0]} eq "skip"; + } + scalar @res; +} + +################################################################################ +# methods for combine +################################################################################ + +sub __combine { + my ($self, $dst, $fields, $useIO) = @_; + + my $ctx = $self->_setup_ctx; + + my ($binary, $quot, $sep, $esc, $quote_space) = @{$ctx}{qw/binary quo sep escape_char quote_space/}; + + if(!defined $quot or $quot eq "\0"){ $quot = ''; } + + my $re_esc; + if ($esc ne '' and $esc ne "\0") { + if ($quot ne '') { + $re_esc = $self->{_re_comb_escape}->{$quot}->{$esc} ||= qr/(\Q$quot\E|\Q$esc\E)/; + } else { + $re_esc = $self->{_re_comb_escape}->{$quot}->{$esc} ||= qr/(\Q$esc\E)/; + } + } + + my $bound = 0; + my $n = @$fields - 1; + if ($n < 0 and $ctx->{is_bound}) { + $n = $ctx->{is_bound} - 1; + $bound = 1; + } + + my $check_meta = ($ctx->{keep_meta_info} >= 10 and @{$self->{_FFLAGS} || []} >= $n) ? 1 : 0; + + my $must_be_quoted; + my @results; + for(my $i = 0; $i <= $n; $i++) { + my $v_ref; + if ($bound) { + $v_ref = $self->__bound_field($ctx, $i, 1); + } else { + if (@$fields > $i) { + $v_ref = \($fields->[$i]); + } + } + next unless $v_ref; + + my $value = $$v_ref; + + if (!defined $value) { + if ($ctx->{undef_str}) { + if ($ctx->{undef_flg}) { + $ctx->{utf8} = 1; + $ctx->{binary} = 1; + } + push @results, $ctx->{undef_str}; + } else { + push @results, ''; + } + next; + } + + if ( substr($value, 0, 1) eq '=' && $ctx->{formula} ) { + $value = $self->_formula($ctx, $value, $i); + if (!defined $value) { + push @results, ''; + next; + } + } + + $must_be_quoted = $ctx->{always_quote} ? 1 : 0; + if ($value eq '') { + $must_be_quoted++ if $ctx->{quote_empty} or ($check_meta && $self->is_quoted($i)); + } + else { + + if (utf8::is_utf8 $value) { + $ctx->{utf8} = 1; + $ctx->{binary} = 1; + } + + $must_be_quoted++ if $check_meta && $self->is_quoted($i); + + if (!$must_be_quoted and $quot ne '') { + use bytes; + $must_be_quoted++ if + ($value =~ /\Q$quot\E/) || + ($sep ne '' and $sep ne "\0" and $value =~ /\Q$sep\E/) || + ($esc ne '' and $esc ne "\0" and $value =~ /\Q$esc\E/) || + ($ctx->{quote_binary} && $value =~ /[\x00-\x1f\x7f-\xa0]/) || + ($ctx->{quote_space} && $value =~ /[\x09\x20]/); + } + + if (!$ctx->{binary} and $value =~ /[^\x09\x20-\x7E]/) { + # an argument contained an invalid character... + $self->{_ERROR_INPUT} = $value; + $self->SetDiag(2110); + return 0; + } + + if ($re_esc) { + $value =~ s/($re_esc)/$esc$1/g; + } + if ($ctx->{escape_null}) { + $value =~ s/\0/${esc}0/g; + } + } + + if ($must_be_quoted) { + $value = $quot . $value . $quot; + } + push @results, $value; + } + + $$dst = join($sep, @results) . ( defined $ctx->{eol} ? $ctx->{eol} : '' ); + + return 1; +} + +sub _formula { + my ($self, $ctx, $value, $i) = @_; + + my $fa = $ctx->{formula} or return; + if ($fa == 1) { die "Formulas are forbidden\n" } + if ($fa == 2) { die "Formulas are forbidden\n" } # XS croak behaves like PP's "die" + + if ($fa == 3) { + my $rec = ''; + if ($ctx->{recno}) { + $rec = sprintf " in record %lu", $ctx->{recno} + 1; + } + my $field = ''; + my $column_names = $self->{_COLUMN_NAMES}; + if (ref $column_names eq 'ARRAY' and @$column_names >= $i - 1) { + my $column_name = $column_names->[$i - 1]; + $field = sprintf " (column: '%.100s')", $column_name if defined $column_name; + } + warn sprintf("Field %d%s%s contains formula '%s'\n", $i, $field, $rec, $value); + return $value; + } + + if ($fa == 4) { + return ''; + } + if ($fa == 5) { + return undef; + } + return; +} + +sub print { + my ($self, $io, $fields) = @_; + + require IO::Handle; + + if (!defined $fields) { + $fields = []; + } elsif(ref($fields) ne 'ARRAY'){ + Carp::croak("Expected fields to be an array ref"); + } + + $self->_hook(before_print => $fields); + + my $str = ""; + $self->__combine(\$str, $fields, 1) or return ''; + + local $\ = ''; + + $io->print( $str ) or $self->_set_error_diag(2200); +} + +################################################################################ +# methods for parse +################################################################################ + + +sub __parse { # cx_xsParse + my ($self, $fields, $fflags, $src, $useIO) = @_; + + my $ctx = $self->_setup_ctx; + + my $state = $self->___parse($ctx, $fields, $fflags, $src, $useIO); + if ($state and ($ctx->{has_hooks} || 0) & HOOK_AFTER_PARSE) { + $self->_hook(after_parse => $fields); + } + return $state || !$last_error; +} + +sub ___parse { # cx_c_xsParse + my ($self, $ctx, $fields, $fflags, $src, $useIO) = @_; + + local $/ = $ctx->{eol} if $ctx->{eolx} or $ctx->{eol_is_cr}; + + if ($ctx->{useIO} = $useIO) { + require IO::Handle; + + $ctx->{tmp} = undef; + if ($ctx->{has_ahead} and defined $self->{_AHEAD}) { + $ctx->{tmp} = $self->{_AHEAD}; + $ctx->{size} = length $ctx->{tmp}; + $ctx->{used} = 0; + } + } else { + $ctx->{tmp} = $src; + $ctx->{size} = length $src; + $ctx->{used} = 0; + $ctx->{utf8} = utf8::is_utf8($src); + } + if ($ctx->{has_error_input}) { + $self->{_ERROR_INPUT} = undef; + $ctx->{has_error_input} = 0; + } + + my $result = $self->____parse($ctx, $src, $fields, $fflags); + $self->{_RECNO} = ++($ctx->{recno}); + $self->{_EOF} = ''; + + if ($ctx->{strict}) { + $ctx->{strict_n} ||= $ctx->{fld_idx}; + if ($ctx->{strict_n} != $ctx->{fld_idx}) { + $self->__parse_error($ctx, 2014, $ctx->{used}); + return; + } + } + + if ($ctx->{useIO}) { + if (defined $ctx->{tmp} and $ctx->{used} < $ctx->{size} and $ctx->{has_ahead}) { + $self->{_AHEAD} = substr($ctx->{tmp}, $ctx->{used}, $ctx->{size} - $ctx->{used}); + } else { + $ctx->{has_ahead} = 0; + if ($ctx->{useIO} & useIO_EOF) { + $self->{_EOF} = 1; + } + } + %{$self->{_CACHE}} = %$ctx; + + if ($fflags) { + if ($ctx->{keep_meta_info}) { + $self->{_FFLAGS} = $fflags; + } else { + undef $fflags; + } + } + } else { + %{$self->{_CACHE}} = %$ctx; + } + + if ($result and $ctx->{types}) { + my $len = @$fields; + for(my $i = 0; $i <= $len && $i <= $ctx->{types_len}; $i++) { + my $value = $fields->[$i]; + next unless defined $value; + my $type = ord(substr($ctx->{types}, $i, 1)); + if ($type == IV) { + $fields->[$i] = int($value); + } elsif ($type == NV) { + $fields->[$i] = $value + 0.0; + } + } + } + + $result; +} + +sub ____parse { # cx_Parse + my ($self, $ctx, $src, $fields, $fflags) = @_; + + my ($quot, $sep, $esc, $eol) = @{$ctx}{qw/quo sep escape_char eol/}; + + utf8::encode($sep) if !$ctx->{utf8} and $ctx->{sep_len}; + utf8::encode($quot) if !$ctx->{utf8} and $ctx->{quo_len}; + utf8::encode($eol) if !$ctx->{utf8} and $ctx->{eol_len}; + + my $seenSomething = 0; + my $waitingForField = 1; + my ($value, $v_ref); + $ctx->{fld_idx} = my $fnum = 0; + $ctx->{flag} = 0; + + my $re_str = join '|', map({$_ eq "\0" ? '[\\0]' : quotemeta($_)} sort {length $b <=> length $a} grep {defined $_ and $_ ne ''} $sep, $quot, $esc, $eol), "\015", "\012", "\x09", " "; + $ctx->{_re} = qr/$re_str/; + my $re = qr/$re_str|[^\x09\x20-\x7E]|$/; + +LOOP: + while($self->__get_from_src($ctx, $src)) { + while($ctx->{tmp} =~ /\G(.*?)($re)/gs) { + my ($hit, $c) = ($1, $2); + $ctx->{used} = pos($ctx->{tmp}); + if (!$waitingForField and $c eq '' and $hit ne '' and $ctx->{useIO} and !($ctx->{useIO} & useIO_EOF)) { + $self->{_AHEAD} = $hit; + $ctx->{has_ahead} = 1; + $ctx->{has_leftover} = 1; + last; + } + last if $seenSomething and $hit eq '' and $c eq ''; # EOF + + # new field + if (!$v_ref) { + if ($ctx->{is_bound}) { + $v_ref = $self->__bound_field($ctx, $fnum, 0); + } else { + $value = ''; + $v_ref = \$value; + } + $fnum++; + return unless $v_ref; + $ctx->{flag} = 0; + $ctx->{fld_idx}++; + } + + $seenSomething = 1; + + if (defined $hit and $hit ne '') { + if ($waitingForField) { + $waitingForField = 0; + } + if ($hit =~ /[^\x09\x20-\x7E]/) { + $ctx->{flag} |= IS_BINARY; + } + $$v_ref .= $hit; + } + +RESTART: + if (defined $c and defined $sep and $c eq $sep) { + if ($waitingForField) { + # ,1,"foo, 3",,bar, + # ^ ^ + if ($ctx->{blank_is_undef} or $ctx->{empty_is_undef}) { + $$v_ref = undef; + } else { + $$v_ref = ""; + } + unless ($ctx->{is_bound}) { + push @$fields, $$v_ref; + } + $v_ref = undef; + if ($ctx->{keep_meta_info} and $fflags) { + push @$fflags, $ctx->{flag}; + } + } elsif ($ctx->{flag} & IS_QUOTED) { + # ,1,"foo, 3",,bar, + # ^ + $$v_ref .= $c; + } else { + # ,1,"foo, 3",,bar, + # ^ ^ ^ + $self->__push_value($ctx, $v_ref, $fields, $fflags, $ctx->{flag}, $fnum); + $v_ref = undef; + $waitingForField = 1; + } + } + elsif (defined $c and defined $quot and $quot ne "\0" and $c eq $quot) { + if ($waitingForField) { + # ,1,"foo, 3",,bar,\r\n + # ^ + $ctx->{flag} |= IS_QUOTED; + $waitingForField = 0; + next; + } + if ($ctx->{flag} & IS_QUOTED) { + # ,1,"foo, 3",,bar,\r\n + # ^ + my $quoesc = 0; + my $c2 = $self->__get($ctx); + + if ($ctx->{allow_whitespace}) { + # , 1 , "foo, 3" , , bar , \r\n + # ^ + while($self->__is_whitespace($ctx, $c2)) { + if ($ctx->{allow_loose_quotes} and !(defined $esc and $c2 eq $esc)) { + $$v_ref .= $c; + $c = $c2; + } + $c2 = $self->__get($ctx); + } + } + + if (!defined $c2) { # EOF + # ,1,"foo, 3" + # ^ + $self->__push_value($ctx, $v_ref, $fields, $fflags, $ctx->{flag}, $fnum); + return 1; + } + + if (defined $c2 and defined $sep and $c2 eq $sep) { + # ,1,"foo, 3",,bar,\r\n + # ^ + $self->__push_value($ctx, $v_ref, $fields, $fflags, $ctx->{flag}, $fnum); + $v_ref = undef; + $waitingForField = 1; + next; + } + if (defined $c2 and ($c2 eq "\012" or (defined $eol and $c2 eq $eol))) { # FIXME: EOLX + # ,1,"foo, 3",,"bar"\n + # ^ + $self->__push_value($ctx, $v_ref, $fields, $fflags, $ctx->{flag}, $fnum); + return 1; + } + + if (defined $esc and $c eq $esc) { + $quoesc = 1; + if (defined $c2 and $c2 eq '0') { + # ,1,"foo, 3"056",,bar,\r\n + # ^ + $$v_ref .= "\0"; + next; + } + if (defined $c2 and defined $quot and $c2 eq $quot) { + # ,1,"foo, 3""56",,bar,\r\n + # ^ + if ($ctx->{utf8}) { + $ctx->{flag} |= IS_BINARY; + } + $$v_ref .= $c2; + next; + } + if ($ctx->{allow_loose_escapes} and defined $c2 and $c2 ne "\015") { + # ,1,"foo, 3"56",,bar,\r\n + # ^ + $$v_ref .= $c; + $c = $c2; + goto RESTART; + } + } + if (defined $c2 and $c2 eq "\015") { + if ($ctx->{eol_is_cr}) { + # ,1,"foo, 3"\r + # ^ + $self->__push_value($ctx, $v_ref, $fields, $fflags, $ctx->{flag}, $fnum); + return 1; + } + + my $c3 = $self->__get($ctx); + if (defined $c3 and $c3 eq "\012") { + # ,1,"foo, 3"\r\n + # ^ + $self->__push_value($ctx, $v_ref, $fields, $fflags, $ctx->{flag}, $fnum); + return 1; + } + + if ($ctx->{useIO} and !$ctx->{eol_len} and $c3 !~ /[^\x09\x20-\x7E]/) { + # ,1,"foo\n 3",,"bar"\r + # baz,4 + # ^ + $self->__set_eol_is_cr($ctx); + $ctx->{used}--; + $ctx->{has_ahead} = 1; + $self->__push_value($ctx, $v_ref, $fields, $fflags, $ctx->{flag}, $fnum); + return 1; + } + + $self->__parse_error($ctx, $quoesc ? 2023 : 2010, $ctx->{used} - 2); + return; + } + + if ($ctx->{allow_loose_quotes} and !$quoesc) { + # ,1,"foo, 3"456",,bar,\r\n + # ^ + $$v_ref .= $c; + $c = $c2; + goto RESTART; + } + # 1,"foo" ",3 + # ^ + if ($quoesc) { + $ctx->{used}--; + $self->__error_inside_quotes($ctx, 2023); + return; + } + $self->__error_inside_quotes($ctx, 2011); + return; + } + # !waitingForField, !InsideQuotes + if ($ctx->{allow_loose_quotes}) { # 1,foo "boo" d'uh,1 + $ctx->{flag} |= IS_ERROR; + $$v_ref .= $c; + } else { + $self->__error_inside_field($ctx, 2034); + return; + } + } + elsif (defined $c and defined $esc and $esc ne "\0" and $c eq $esc) { + # This means quote_char != escape_char + if ($waitingForField) { + $waitingForField = 0; + if ($ctx->{allow_unquoted_escape}) { + # The escape character is the first character of an + # unquoted field + # ... get and store next character + my $c2 = $self->__get($ctx); + $$v_ref = ""; + + if (!defined $c2) { # EOF + $ctx->{used}--; + $self->__error_inside_field($ctx, 2035); + return; + } + if ($c2 eq '0') { + $$v_ref .= "\0"; + } + elsif ( + (defined $quot and $c2 eq $quot) or + (defined $sep and $c2 eq $sep) or + (defined $esc and $c2 eq $esc) or + $ctx->{allow_loose_escapes} + ) { + if ($ctx->{utf8}) { + $ctx->{flag} |= IS_BINARY; + } + $$v_ref .= $c2; + } else { + $self->__parse_inside_quotes($ctx, 2025); + return; + } + } + } + elsif ($ctx->{flag} & IS_QUOTED) { + my $c2 = $self->__get($ctx); + if (!defined $c2) { # EOF + $ctx->{used}--; + $self->__error_inside_quotes($ctx, 2024); + return; + } + if ($c2 eq '0') { + $$v_ref .= "\0"; + } + elsif ( + (defined $quot and $c2 eq $quot) or + (defined $sep and $c2 eq $sep) or + (defined $esc and $c2 eq $esc) or + $ctx->{allow_loose_escapes} + ) { + if ($ctx->{utf8}) { + $ctx->{flag} |= IS_BINARY; + } + $$v_ref .= $c2; + } else { + $ctx->{used}--; + $self->__error_inside_quotes($ctx, 2025); + return; + } + } + elsif ($v_ref) { + my $c2 = $self->__get($ctx); + if (!defined $c2) { # EOF + $ctx->{used}--; + $self->__error_inside_field($ctx, 2035); + return; + } + $$v_ref .= $c2; + } + else { + $self->__error_inside_field($ctx, 2036); + return; + } + } + elsif (defined $c and ($c eq "\012" or $c eq '' or (defined $eol and $c eq $eol and $eol ne "\015"))) { # EOL + EOLX: + if ($waitingForField) { + # ,1,"foo, 3",,bar, + # ^ + if ($ctx->{blank_is_undef} or $ctx->{empty_is_undef}) { + $$v_ref = undef; + } else { + $$v_ref = ""; + } + unless ($ctx->{is_bound}) { + push @$fields, $$v_ref; + } + if ($ctx->{keep_meta_info} and $fflags) { + push @$fflags, $ctx->{flag}; + } + return 1; + } + if ($ctx->{flag} & IS_QUOTED) { + # ,1,"foo\n 3",,bar, + # ^ + $ctx->{flag} |= IS_BINARY; + unless ($ctx->{binary}) { + $self->__error_inside_quotes($ctx, 2021); + return; + } + $$v_ref .= $c; + } + elsif ($ctx->{verbatim}) { + # ,1,foo\n 3,,bar, + # This feature should be deprecated + $ctx->{flag} |= IS_BINARY; + unless ($ctx->{binary}) { + $self->__error_inside_field($ctx, 2030); + return; + } + $$v_ref .= $c unless $ctx->{eol} eq $c and $ctx->{useIO}; + } + else { + # sep=, + # ^ + if (!$ctx->{recno} and $ctx->{fld_idx} == 1 and $ctx->{useIO} and $hit =~ /^sep=(.{1,16})$/i) { + $ctx->{sep} = $1; + use bytes; + my $len = length $ctx->{sep}; + if ($len <= 16) { + $ctx->{sep_len} = $len == 1 ? 0 : $len; + return $self->____parse($ctx, $src, $fields, $fflags); + } + } + + # ,1,"foo\n 3",,bar + # ^ + $self->__push_value($ctx, $v_ref, $fields, $fflags, $ctx->{flag}, $fnum); + return 1; + } + } + elsif (defined $c and $c eq "\015" and !$ctx->{verbatim}) { + if ($waitingForField) { + $waitingForField = 0; + if ($ctx->{eol_is_cr}) { + # ,1,"foo\n 3",,bar,\r + # ^ + $c = "\012"; + goto RESTART; + } + + my $c2 = $self->__get($ctx); + if (!defined $c2) { # EOF + # ,1,"foo\n 3",,bar,\r + # ^ + $c = undef; + goto RESTART; + } + if ($c2 eq "\012") { # \r is not optional before EOLX! + # ,1,"foo\n 3",,bar,\r\n + # ^ + $c = $c2; + goto RESTART; + } + + if ($ctx->{useIO} and !$ctx->{eol_len} and $c2 !~ /[^\x09\x20-\x7E]/) { + # ,1,"foo\n 3",,bar,\r + # baz,4 + # ^ + $self->__set_eol_is_cr($ctx); + $ctx->{used}--; + $ctx->{has_ahead} = 1; + $self->__push_value($ctx, $v_ref, $fields, $fflags, $ctx->{flag}, $fnum); + return 1; + } + + # ,1,"foo\n 3",,bar,\r\t + # ^ + $ctx->{used}--; + $self->__error_inside_field($ctx, 2031); + return; + } + if ($ctx->{flag} & IS_QUOTED) { + # ,1,"foo\r 3",,bar,\r\t + # ^ + $ctx->{flag} |= IS_BINARY; + unless ($ctx->{binary}) { + $self->__error_inside_quotes($ctx, 2022); + return; + } + $$v_ref .= $c; + } + else { + if ($ctx->{eol_is_cr}) { + # ,1,"foo\n 3",,bar\r + # ^ + $self->__push_value($ctx, $v_ref, $fields, $fflags, $ctx->{flag}, $fnum); + return 1; + } + + my $c2 = $self->__get($ctx); + if (defined $c2 and $c2 eq "\012") { # \r is not optional before EOLX! + # ,1,"foo\n 3",,bar\r\n + # ^ + $self->__push_value($ctx, $v_ref, $fields, $fflags, $ctx->{flag}, $fnum); + return 1; + } + + if ($ctx->{useIO} and !$ctx->{eol_len} and $c2 !~ /[^\x09\x20-\x7E]/) { + # ,1,"foo\n 3",,bar\r + # baz,4 + # ^ + $self->__set_eol_is_cr($ctx); + $ctx->{used}--; + $ctx->{has_ahead} = 1; + $self->__push_value($ctx, $v_ref, $fields, $fflags, $ctx->{flag}, $fnum); + return 1; + } + + # ,1,"foo\n 3",,bar\r\t + # ^ + $self->__error_inside_field($ctx, 2032); + return; + } + } + else { + if ($ctx->{eolx} and $c eq $eol) { + $c = ''; + goto EOLX; + } + + if ($waitingForField) { + if ($ctx->{allow_whitespace} and $self->__is_whitespace($ctx, $c)) { + do { + $c = $self->__get($ctx); + last if !defined $c; + } while $self->__is_whitespace($ctx, $c); + goto RESTART; + } + $waitingForField = 0; + goto RESTART; + } + if ($ctx->{flag} & IS_QUOTED) { + if (!defined $c or $c =~ /[^\x09\x20-\x7E]/) { + $ctx->{flag} |= IS_BINARY; + unless ($ctx->{binary} or $ctx->{utf8}) { + $self->__error_inside_quotes($ctx, 2026); + return; + } + } + $$v_ref .= $c; + } else { + if (!defined $c or $c =~ /[^\x09\x20-\x7E]/) { + $ctx->{flag} |= IS_BINARY; + unless ($ctx->{binary} or $ctx->{utf8}) { + $self->__error_inside_field($ctx, 2037); + return; + } + } + $$v_ref .= $c; + } + } + last LOOP if $ctx->{useIO} and $ctx->{verbatim} and $ctx->{used} == $ctx->{size}; + } + } + + if ($waitingForField) { + if ($seenSomething or !$ctx->{useIO}) { + # new field + if (!$v_ref) { + if ($ctx->{is_bound}) { + $v_ref = $self->__bound_field($ctx, $fnum, 0); + } else { + $value = ''; + $v_ref = \$value; + } + $fnum++; + return unless $v_ref; + $ctx->{flag} = 0; + $ctx->{fld_idx}++; + } + if ($ctx->{blank_is_undef} or $ctx->{empty_is_undef}) { + $$v_ref = undef; + } else { + $$v_ref = ""; + } + unless ($ctx->{is_bound}) { + push @$fields, $$v_ref; + } + if ($ctx->{keep_meta_info} and $fflags) { + push @$fflags, $ctx->{flag}; + } + return 1; + } + $self->SetDiag(2012); + return; + } + + if ($ctx->{flag} & IS_QUOTED) { + $self->__error_inside_quotes($ctx, 2027); + return; + } + + if ($v_ref) { + $self->__push_value($ctx, $v_ref, $fields, $fflags, $ctx->{flag}, $fnum); + } + return 1; +} + +sub __get_from_src { + my ($self, $ctx, $src) = @_; + return 1 if defined $ctx->{tmp} and $ctx->{used} <= 0; + return 1 if $ctx->{used} < $ctx->{size}; + return unless $ctx->{useIO}; + my $res = $src->getline; + if (defined $res) { + if ($ctx->{has_ahead}) { + $ctx->{tmp} = $self->{_AHEAD}; + $ctx->{tmp} .= $ctx->{eol} if $ctx->{eol_len}; + $ctx->{tmp} .= $res; + $ctx->{has_ahead} = 0; + } else { + $ctx->{tmp} = $res; + } + if ($ctx->{size} = length $ctx->{tmp}) { + $ctx->{used} = -1; + $ctx->{utf8} = 1 if utf8::is_utf8($ctx->{tmp}); + pos($ctx->{tmp}) = 0; + return 1; + } + } elsif (delete $ctx->{has_leftover}) { + $ctx->{tmp} = $self->{_AHEAD}; + $ctx->{has_ahead} = 0; + $ctx->{useIO} |= useIO_EOF; + if ($ctx->{size} = length $ctx->{tmp}) { + $ctx->{used} = -1; + $ctx->{utf8} = 1 if utf8::is_utf8($ctx->{tmp}); + pos($ctx->{tmp}) = 0; + return 1; + } + } + $ctx->{tmp} = '' unless defined $ctx->{tmp}; + $ctx->{useIO} |= useIO_EOF; + return; +} + +sub __set_eol_is_cr { + my ($self, $ctx) = @_; + $ctx->{eol} = "\015"; + $ctx->{eol_is_cr} = 1; + $ctx->{eol_len} = 1; + %{$self->{_CACHE}} = %$ctx; + + $self->{eol} = $ctx->{eol}; +} + +sub __bound_field { + my ($self, $ctx, $i, $keep) = @_; + if ($i >= $ctx->{is_bound}) { + $self->SetDiag(3006); + return; + } + if (ref $ctx->{bound} eq 'ARRAY') { + my $ref = $ctx->{bound}[$i]; + if (ref $ref) { + if ($keep) { + return $ref; + } + unless (Scalar::Util::readonly($$ref)) { + $$ref = ""; + return $ref; + } + } + } + $self->SetDiag(3008); + return; +} + +sub __get { + my ($self, $ctx) = @_; + return unless defined $ctx->{used}; + return if $ctx->{used} >= $ctx->{size}; + my $pos = pos($ctx->{tmp}); + if ($ctx->{tmp} =~ /\G($ctx->{_re}|.)/gs) { + my $c = $1; + if ($c =~ /[^\x09\x20-\x7e]/) { + $ctx->{flag} |= IS_BINARY; + } + $ctx->{used} = pos($ctx->{tmp}); + return $c; + } else { + pos($ctx->{tmp}) = $pos; + return; + } +} + +sub __error_inside_quotes { + my ($self, $ctx, $error) = @_; + $self->__parse_error($ctx, $error, $ctx->{used} - 1); +} + +sub __error_inside_field { + my ($self, $ctx, $error) = @_; + $self->__parse_error($ctx, $error, $ctx->{used} - 1); +} + +sub __parse_error { + my ($self, $ctx, $error, $pos) = @_; + $self->{_ERROR_POS} = $pos; + $self->{_ERROR_FLD} = $ctx->{fld_idx}; + $self->{_ERROR_INPUT} = $ctx->{tmp} if $ctx->{tmp}; + $self->SetDiag($error); + return; +} + +sub __is_whitespace { + my ($self, $ctx, $c) = @_; + return unless defined $c; + return ( + (!defined $ctx->{sep} or $c ne $ctx->{sep}) && + (!defined $ctx->{quo} or $c ne $ctx->{quo}) && + (!defined $ctx->{escape_char} or $c ne $ctx->{escape_char}) && + ($c eq " " or $c eq "\t") + ); +} + +sub __push_value { # AV_PUSH (part of) + my ($self, $ctx, $v_ref, $fields, $fflags, $flag, $fnum) = @_; + utf8::encode($$v_ref) if $ctx->{utf8}; + if ($ctx->{formula} && $$v_ref && substr($$v_ref, 0, 1) eq '=') { + my $value = $self->_formula($ctx, $$v_ref, $fnum); + push @$fields, defined $value ? $value : undef; + return; + } + if ( + (!defined $$v_ref or $$v_ref eq '') and + ($ctx->{empty_is_undef} or (!($flag & IS_QUOTED) and $ctx->{blank_is_undef})) + ) { + $$v_ref = undef; + } else { + if ($ctx->{allow_whitespace} && !($flag & IS_QUOTED)) { + $$v_ref =~ s/[ \t]+$//; + } + if ($flag & IS_BINARY and $ctx->{decode_utf8} and ($ctx->{utf8} || _is_valid_utf8($$v_ref))) { + utf8::decode($$v_ref); + } + } + unless ($ctx->{is_bound}) { + push @$fields, $$v_ref; + } + if ($ctx->{keep_meta_info} and $fflags) { + push @$fflags, $flag; + } +} + +sub getline { + my ($self, $io) = @_; + + my (@fields, @fflags); + my $res = $self->__parse(\@fields, \@fflags, $io, 1); + $res ? \@fields : undef; +} + +sub getline_all { + my ( $self, $io, $offset, $len ) = @_; + + my $ctx = $self->_setup_ctx; + + my $tail = 0; + my $n = 0; + $offset ||= 0; + + if ( $offset < 0 ) { + $tail = -$offset; + $offset = -1; + } + + my (@row, @list); + while ($self->___parse($ctx, \@row, undef, $io, 1)) { + $ctx = $self->_setup_ctx; + + if ($offset > 0) { + $offset--; + @row = (); + next; + } + if ($n++ >= $tail and $tail) { + shift @list; + $n--; + } + if (($ctx->{has_hooks} || 0) & HOOK_AFTER_PARSE) { + unless ($self->_hook(after_parse => \@row)) { + @row = (); + next; + } + } + push @list, [@row]; + @row = (); + + last if defined $len && $n >= $len and $offset >= 0; # exceeds limit size + } + + if ( defined $len && $n > $len ) { + @list = splice( @list, 0, $len); + } + + return \@list; +} + +sub _is_valid_utf8 { + return ( $_[0] =~ /^(?: + [\x00-\x7F] + |[\xC2-\xDF][\x80-\xBF] + |[\xE0][\xA0-\xBF][\x80-\xBF] + |[\xE1-\xEC][\x80-\xBF][\x80-\xBF] + |[\xED][\x80-\x9F][\x80-\xBF] + |[\xEE-\xEF][\x80-\xBF][\x80-\xBF] + |[\xF0][\x90-\xBF][\x80-\xBF][\x80-\xBF] + |[\xF1-\xF3][\x80-\xBF][\x80-\xBF][\x80-\xBF] + |[\xF4][\x80-\x8F][\x80-\xBF][\x80-\xBF] + )+$/x ) ? 1 : 0; +} + +################################################################################ +# methods for errors +################################################################################ + +sub _set_error_diag { + my ( $self, $error, $pos ) = @_; + + $self->SetDiag($error); + + if (defined $pos) { + $_[0]->{_ERROR_POS} = $pos; + } + + return; +} + +sub error_input { + my $self = shift; + if ($self and ((Scalar::Util::reftype($self) || '') eq 'HASH' or (ref $self) =~ /^Text::CSV/)) { + return $self->{_ERROR_INPUT}; + } + return; +} + +sub _sv_diag { + my ($self, $error) = @_; + bless [$error, $ERRORS->{$error}], 'Text::CSV::ErrorDiag'; +} + +sub _set_diag { + my ($self, $ctx, $error) = @_; + + $last_error = $self->_sv_diag($error); + $self->{_ERROR_DIAG} = $last_error; + if ($error == 0) { + $self->{_ERROR_POS} = 0; + $self->{_ERROR_FLD} = 0; + $self->{_ERROR_INPUT} = undef; + $ctx->{has_error_input} = 0; + } + if ($error == 2012) { # EOF + $self->{_EOF} = 1; + } + if ($ctx->{auto_diag}) { + $self->error_diag; + } + return $last_error; +} + +sub SetDiag { + my ($self, $error, $errstr) = @_; + my $res; + if (ref $self) { + my $ctx = $self->_setup_ctx; + $res = $self->_set_diag($ctx, $error); + + } else { + $res = $self->_sv_diag($error); + } + if (defined $errstr) { + $res->[1] = $errstr; + } + $res; +} + +################################################################################ +package Text::CSV::ErrorDiag; + +use strict; +use overload ( + '""' => \&stringify, + '+' => \&numeric, + '-' => \&numeric, + '*' => \&numeric, + '/' => \&numeric, + fallback => 1, +); + + +sub numeric { + my ($left, $right) = @_; + return ref $left ? $left->[0] : $right->[0]; +} + + +sub stringify { + $_[0]->[1]; +} +################################################################################ +1; +__END__ + +=head1 NAME + +Text::CSV_PP - Text::CSV_XS compatible pure-Perl module + + +=head1 SYNOPSIS + + use Text::CSV_PP; + + $csv = Text::CSV_PP->new(); # create a new object + # If you want to handle non-ascii char. + $csv = Text::CSV_PP->new({binary => 1}); + + $status = $csv->combine(@columns); # combine columns into a string + $line = $csv->string(); # get the combined string + + $status = $csv->parse($line); # parse a CSV string into fields + @columns = $csv->fields(); # get the parsed fields + + $status = $csv->status (); # get the most recent status + $bad_argument = $csv->error_input (); # get the most recent bad argument + $diag = $csv->error_diag (); # if an error occurred, explains WHY + + $status = $csv->print ($io, $colref); # Write an array of fields + # immediately to a file $io + $colref = $csv->getline ($io); # Read a line from file $io, + # parse it and return an array + # ref of fields + $csv->column_names (@names); # Set column names for getline_hr () + $ref = $csv->getline_hr ($io); # getline (), but returns a hashref + $eof = $csv->eof (); # Indicate if last parse or + # getline () hit End Of File + + $csv->types(\@t_array); # Set column types + +=head1 DESCRIPTION + +Text::CSV_PP is a pure-perl module that provides facilities for the +composition and decomposition of comma-separated values. This is +(almost) compatible with much faster L, and mainly +used as its fallback module when you use L module without +having installed Text::CSV_XS. If you don't have any reason to use +this module directly, use Text::CSV for speed boost and portability +(or maybe Text::CSV_XS when you write an one-off script and don't need +to care about portability). + +The following caveats are taken from the doc of Text::CSV_XS. + +=head2 Embedded newlines + +B: The default behavior is to accept only ASCII characters +in the range from C<0x20> (space) to C<0x7E> (tilde). This means that the +fields can not contain newlines. If your data contains newlines embedded in +fields, or characters above C<0x7E> (tilde), or binary data, you B> +set C<< binary => 1 >> in the call to L. To cover the widest range of +parsing options, you will always want to set binary. + +But you still have the problem that you have to pass a correct line to the +L method, which is more complicated from the usual point of usage: + + my $csv = Text::CSV_PP->new ({ binary => 1, eol => $/ }); + while (<>) { # WRONG! + $csv->parse ($_); + my @fields = $csv->fields (); + } + +this will break, as the C might read broken lines: it does not care +about the quoting. If you need to support embedded newlines, the way to go +is to B pass L|/eol> in the parser (it accepts C<\n>, C<\r>, +B C<\r\n> by default) and then + + my $csv = Text::CSV_PP->new ({ binary => 1 }); + open my $fh, "<", $file or die "$file: $!"; + while (my $row = $csv->getline ($fh)) { + my @fields = @$row; + } + +The old(er) way of using global file handles is still supported + + while (my $row = $csv->getline (*ARGV)) { ... } + +=head2 Unicode + +Unicode is only tested to work with perl-5.8.2 and up. + +See also L. + +The simplest way to ensure the correct encoding is used for in- and output +is by either setting layers on the filehandles, or setting the L +argument for L. + + open my $fh, "<:encoding(UTF-8)", "in.csv" or die "in.csv: $!"; +or + my $aoa = csv (in => "in.csv", encoding => "UTF-8"); + + open my $fh, ">:encoding(UTF-8)", "out.csv" or die "out.csv: $!"; +or + csv (in => $aoa, out => "out.csv", encoding => "UTF-8"); + +On parsing (both for L and L), if the source is marked +being UTF8, then all fields that are marked binary will also be marked UTF8. + +On combining (L and L): if any of the combining fields +was marked UTF8, the resulting string will be marked as UTF8. Note however +that all fields I the first field marked UTF8 and contained 8-bit +characters that were not upgraded to UTF8, these will be C in the +resulting string too, possibly causing unexpected errors. If you pass data +of different encoding, or you don't know if there is different encoding, +force it to be upgraded before you pass them on: + + $csv->print ($fh, [ map { utf8::upgrade (my $x = $_); $x } @data ]); + +For complete control over encoding, please use L: + + use Text::CSV::Encoded; + my $csv = Text::CSV::Encoded->new ({ + encoding_in => "iso-8859-1", # the encoding comes into Perl + encoding_out => "cp1252", # the encoding comes out of Perl + }); + + $csv = Text::CSV::Encoded->new ({ encoding => "utf8" }); + # combine () and print () accept *literally* utf8 encoded data + # parse () and getline () return *literally* utf8 encoded data + + $csv = Text::CSV::Encoded->new ({ encoding => undef }); # default + # combine () and print () accept UTF8 marked data + # parse () and getline () return UTF8 marked data + +=head2 BOM + +BOM (or Byte Order Mark) handling is available only inside the L +method. This method supports the following encodings: C, C, +C, C, C, C, C, C, +C, and C. See L. + +If a file has a BOM, the easiest way to deal with that is + + my $aoh = csv (in => $file, detect_bom => 1); + +All records will be encoded based on the detected BOM. + +This implies a call to the L method, which defaults to also set +the L. So this is B the same as + + my $aoh = csv (in => $file, headers => "auto"); + +which only reads the first record to set L but ignores any +meaning of possible present BOM. + +=head1 METHODS + +This section is taken from Text::CSV_XS. + +=head2 version + +(Class method) Returns the current module version. + +=head2 new + +(Class method) Returns a new instance of class Text::CSV_PP. The attributes +are described by the (optional) hash ref C<\%attr>. + + my $csv = Text::CSV_PP->new ({ attributes ... }); + +The following attributes are available: + +=head3 eol + + my $csv = Text::CSV_PP->new ({ eol => $/ }); + $csv->eol (undef); + my $eol = $csv->eol; + +The end-of-line string to add to rows for L or the record separator +for L. + +When not passed in a B instance, the default behavior is to accept +C<\n>, C<\r>, and C<\r\n>, so it is probably safer to not specify C at +all. Passing C or the empty string behave the same. + +When not passed in a B instance, records are not terminated at +all, so it is probably wise to pass something you expect. A safe choice for +C on output is either C<$/> or C<\r\n>. + +Common values for C are C<"\012"> (C<\n> or Line Feed), C<"\015\012"> +(C<\r\n> or Carriage Return, Line Feed), and C<"\015"> (C<\r> or Carriage +Return). The L|/eol> attribute cannot exceed 7 (ASCII) characters. + +If both C<$/> and L|/eol> equal C<"\015">, parsing lines that end on +only a Carriage Return without Line Feed, will be Ld correct. + +=head3 sep_char + + my $csv = Text::CSV_PP->new ({ sep_char => ";" }); + $csv->sep_char (";"); + my $c = $csv->sep_char; + +The char used to separate fields, by default a comma. (C<,>). Limited to a +single-byte character, usually in the range from C<0x20> (space) to C<0x7E> +(tilde). When longer sequences are required, use L|/sep>. + +The separation character can not be equal to the quote character or to the +escape character. + +=head3 sep + + my $csv = Text::CSV_PP->new ({ sep => "\N{FULLWIDTH COMMA}" }); + $csv->sep (";"); + my $sep = $csv->sep; + +The chars used to separate fields, by default undefined. Limited to 8 bytes. + +When set, overrules L|/sep_char>. If its length is one byte it +acts as an alias to L|/sep_char>. + +=head3 quote_char + + my $csv = Text::CSV_PP->new ({ quote_char => "'" }); + $csv->quote_char (undef); + my $c = $csv->quote_char; + +The character to quote fields containing blanks or binary data, by default +the double quote character (C<">). A value of undef suppresses quote chars +(for simple cases only). Limited to a single-byte character, usually in the +range from C<0x20> (space) to C<0x7E> (tilde). When longer sequences are +required, use L|/quote>. + +C can not be equal to L|/sep_char>. + +=head3 quote + + my $csv = Text::CSV_PP->new ({ quote => "\N{FULLWIDTH QUOTATION MARK}" }); + $csv->quote ("'"); + my $quote = $csv->quote; + +The chars used to quote fields, by default undefined. Limited to 8 bytes. + +When set, overrules L|/quote_char>. If its length is one byte +it acts as an alias to L|/quote_char>. + +=head3 escape_char + + my $csv = Text::CSV_PP->new ({ escape_char => "\\" }); + $csv->escape_char (":"); + my $c = $csv->escape_char; + +The character to escape certain characters inside quoted fields. This is +limited to a single-byte character, usually in the range from C<0x20> +(space) to C<0x7E> (tilde). + +The C defaults to being the double-quote mark (C<">). In other +words the same as the default L|/quote_char>. This means that +doubling the quote mark in a field escapes it: + + "foo","bar","Escape ""quote mark"" with two ""quote marks""","baz" + +If you change the L|/quote_char> without changing the +C, the C will still be the double-quote (C<">). +If instead you want to escape the L|/quote_char> by doubling +it you will need to also change the C to be the same as what +you have changed the L|/quote_char> to. + +Setting C to or C<""> will disable escaping completely +and is greatly discouraged. This will also disable C. + +The escape character can not be equal to the separation character. + +=head3 binary + + my $csv = Text::CSV_PP->new ({ binary => 1 }); + $csv->binary (0); + my $f = $csv->binary; + +If this attribute is C<1>, you may use binary characters in quoted fields, +including line feeds, carriage returns and C bytes. (The latter could +be escaped as C<"0>.) By default this feature is off. + +If a string is marked UTF8, C will be turned on automatically when +binary characters other than C and C are encountered. Note that a +simple string like C<"\x{00a0}"> might still be binary, but not marked UTF8, +so setting C<< { binary => 1 } >> is still a wise option. + +=head3 strict + + my $csv = Text::CSV_PP->new ({ strict => 1 }); + $csv->strict (0); + my $f = $csv->strict; + +If this attribute is set to C<1>, any row that parses to a different number +of fields than the previous row will cause the parser to throw error 2014. + +=head3 formula_handling + +=head3 formula + + my $csv = Text::CSV_PP->new ({ formula => "none" }); + $csv->formula ("none"); + my $f = $csv->formula; + +This defines the behavior of fields containing I. As formulas are +considered dangerous in spreadsheets, this attribute can define an optional +action to be taken if a field starts with an equal sign (C<=>). + +For purpose of code-readability, this can also be written as + + my $csv = Text::CSV_PP->new ({ formula_handling => "none" }); + $csv->formula_handling ("none"); + my $f = $csv->formula_handling; + +Possible values for this attribute are + +=over 2 + +=item none + +Take no specific action. This is the default. + + $csv->formula ("none"); + +=item die + +Cause the process to C whenever a leading C<=> is encountered. + + $csv->formula ("die"); + +=item croak + +Cause the process to C whenever a leading C<=> is encountered. (See +L) + + $csv->formula ("croak"); + +=item diag + +Report position and content of the field whenever a leading C<=> is found. +The value of the field is unchanged. + + $csv->formula ("diag"); + +=item empty + +Replace the content of fields that start with a C<=> with the empty string. + + $csv->formula ("empty"); + $csv->formula (""); + +=item undef + +Replace the content of fields that start with a C<=> with C. + + $csv->formula ("undef"); + $csv->formula (undef); + +=back + +All other values will give a warning and then fallback to C. + +=head3 decode_utf8 + + my $csv = Text::CSV_PP->new ({ decode_utf8 => 1 }); + $csv->decode_utf8 (0); + my $f = $csv->decode_utf8; + +This attributes defaults to TRUE. + +While I, fields that are valid UTF-8, are automatically set to be +UTF-8, so that + + $csv->parse ("\xC4\xA8\n"); + +results in + + PV("\304\250"\0) [UTF8 "\x{128}"] + +Sometimes it might not be a desired action. To prevent those upgrades, set +this attribute to false, and the result will be + + PV("\304\250"\0) + +=head3 auto_diag + + my $csv = Text::CSV_PP->new ({ auto_diag => 1 }); + $csv->auto_diag (2); + my $l = $csv->auto_diag; + +Set this attribute to a number between C<1> and C<9> causes L +to be automatically called in void context upon errors. + +In case of error C<2012 - EOF>, this call will be void. + +If C is set to a numeric value greater than C<1>, it will C +on errors instead of C. If set to anything unrecognized, it will be +silently ignored. + +Future extensions to this feature will include more reliable auto-detection +of C being active in the scope of which the error occurred which +will increment the value of C with C<1> the moment the error is +detected. + +=head3 diag_verbose + + my $csv = Text::CSV_PP->new ({ diag_verbose => 1 }); + $csv->diag_verbose (2); + my $l = $csv->diag_verbose; + +Set the verbosity of the output triggered by C. Currently only +adds the current input-record-number (if known) to the diagnostic output +with an indication of the position of the error. + +=head3 blank_is_undef + + my $csv = Text::CSV_PP->new ({ blank_is_undef => 1 }); + $csv->blank_is_undef (0); + my $f = $csv->blank_is_undef; + +Under normal circumstances, C data makes no distinction between quoted- +and unquoted empty fields. These both end up in an empty string field once +read, thus + + 1,"",," ",2 + +is read as + + ("1", "", "", " ", "2") + +When I C files with either L|/always_quote> +or L|/quote_empty> set, the unquoted I field is the +result of an undefined value. To enable this distinction when I +C data, the C attribute will cause unquoted empty +fields to be set to C, causing the above to be parsed as + + ("1", "", undef, " ", "2") + +note that this is specifically important when loading C fields into a +database that allows C values, as the perl equivalent for C is +C in L land. + +=head3 empty_is_undef + + my $csv = Text::CSV_PP->new ({ empty_is_undef => 1 }); + $csv->empty_is_undef (0); + my $f = $csv->empty_is_undef; + +Going one step further than L|/blank_is_undef>, this +attribute converts all empty fields to C, so + + 1,"",," ",2 + +is read as + + (1, undef, undef, " ", 2) + +Note that this effects only fields that are originally empty, not fields +that are empty after stripping allowed whitespace. YMMV. + +=head3 allow_whitespace + + my $csv = Text::CSV_PP->new ({ allow_whitespace => 1 }); + $csv->allow_whitespace (0); + my $f = $csv->allow_whitespace; + +When this option is set to true, the whitespace (C's and C's) +surrounding the separation character is removed when parsing. If either +C or C is one of the three characters L|/sep_char>, +L|/quote_char>, or L|/escape_char> it will not +be considered whitespace. + +Now lines like: + + 1 , "foo" , bar , 3 , zapp + +are parsed as valid C, even though it violates the C specs. + +Note that B whitespace is stripped from both start and end of each +field. That would make it I than a I to enable parsing bad +C lines, as + + 1, 2.0, 3, ape , monkey + +will now be parsed as + + ("1", "2.0", "3", "ape", "monkey") + +even if the original line was perfectly acceptable C. + +=head3 allow_loose_quotes + + my $csv = Text::CSV_PP->new ({ allow_loose_quotes => 1 }); + $csv->allow_loose_quotes (0); + my $f = $csv->allow_loose_quotes; + +By default, parsing unquoted fields containing L|/quote_char> +characters like + + 1,foo "bar" baz,42 + +would result in parse error 2034. Though it is still bad practice to allow +this format, we cannot help the fact that some vendors make their +applications spit out lines styled this way. + +If there is B bad C data, like + + 1,"foo "bar" baz",42 + +or + + 1,""foo bar baz"",42 + +there is a way to get this data-line parsed and leave the quotes inside the +quoted field as-is. This can be achieved by setting C +B making sure that the L|/escape_char> is I equal +to L|/quote_char>. + +=head3 allow_loose_escapes + + my $csv = Text::CSV_PP->new ({ allow_loose_escapes => 1 }); + $csv->allow_loose_escapes (0); + my $f = $csv->allow_loose_escapes; + +Parsing fields that have L|/escape_char> characters that +escape characters that do not need to be escaped, like: + + my $csv = Text::CSV_PP->new ({ escape_char => "\\" }); + $csv->parse (qq{1,"my bar\'s",baz,42}); + +would result in parse error 2025. Though it is bad practice to allow this +format, this attribute enables you to treat all escape character sequences +equal. + +=head3 allow_unquoted_escape + + my $csv = Text::CSV_PP->new ({ allow_unquoted_escape => 1 }); + $csv->allow_unquoted_escape (0); + my $f = $csv->allow_unquoted_escape; + +A backward compatibility issue where L|/escape_char> differs +from L|/quote_char> prevents L|/escape_char> +to be in the first position of a field. If L|/quote_char> is +equal to the default C<"> and L|/escape_char> is set to C<\>, +this would be illegal: + + 1,\0,2 + +Setting this attribute to C<1> might help to overcome issues with backward +compatibility and allow this style. + +=head3 always_quote + + my $csv = Text::CSV_PP->new ({ always_quote => 1 }); + $csv->always_quote (0); + my $f = $csv->always_quote; + +By default the generated fields are quoted only if they I to be. For +example, if they contain the separator character. If you set this attribute +to C<1> then I defined fields will be quoted. (C fields are not +quoted, see L). This makes it quite often easier to handle +exported data in external applications. + +=head3 quote_space + + my $csv = Text::CSV_PP->new ({ quote_space => 1 }); + $csv->quote_space (0); + my $f = $csv->quote_space; + +By default, a space in a field would trigger quotation. As no rule exists +this to be forced in C, nor any for the opposite, the default is true +for safety. You can exclude the space from this trigger by setting this +attribute to 0. + +=head3 quote_empty + + my $csv = Text::CSV_PP->new ({ quote_empty => 1 }); + $csv->quote_empty (0); + my $f = $csv->quote_empty; + +By default the generated fields are quoted only if they I to be. An +empty (defined) field does not need quotation. If you set this attribute to +C<1> then I defined fields will be quoted. (C fields are not +quoted, see L). See also L|/always_quote>. + +=head3 quote_binary + + my $csv = Text::CSV_PP->new ({ quote_binary => 1 }); + $csv->quote_binary (0); + my $f = $csv->quote_binary; + +By default, all "unsafe" bytes inside a string cause the combined field to +be quoted. By setting this attribute to C<0>, you can disable that trigger +for bytes >= C<0x7F>. + +=head3 escape_null + + my $csv = Text::CSV_PP->new ({ escape_null => 1 }); + $csv->escape_null (0); + my $f = $csv->escape_null; + +By default, a C byte in a field would be escaped. This option enables +you to treat the C byte as a simple binary character in binary mode +(the C<< { binary => 1 } >> is set). The default is true. You can prevent +C escapes by setting this attribute to C<0>. + +When the C attribute is set to undefined, this attribute will +be set to false. + +The default setting will encode "=\x00=" as + + "="0=" + +With C set, this will result in + + "=\x00=" + +The default when using the C function is C. + +For backward compatibility reasons, the deprecated old name C +is still recognized. + +=head3 keep_meta_info + + my $csv = Text::CSV_PP->new ({ keep_meta_info => 1 }); + $csv->keep_meta_info (0); + my $f = $csv->keep_meta_info; + +By default, the parsing of input records is as simple and fast as possible. +However, some parsing information - like quotation of the original field - +is lost in that process. Setting this flag to true enables retrieving that +information after parsing with the methods L, L, +and L described below. Default is false for performance. + +If you set this attribute to a value greater than 9, than you can control +output quotation style like it was used in the input of the the last parsed +record (unless quotation was added because of other reasons). + + my $csv = Text::CSV_PP->new ({ + binary => 1, + keep_meta_info => 1, + quote_space => 0, + }); + + my $row = $csv->parse (q{1,,"", ," ",f,"g","h""h",help,"help"}); + + $csv->print (*STDOUT, \@row); + # 1,,, , ,f,g,"h""h",help,help + $csv->keep_meta_info (11); + $csv->print (*STDOUT, \@row); + # 1,,"", ," ",f,"g","h""h",help,"help" + +=head3 undef_str + + my $csv = Text::CSV_PP->new ({ undef_str => "\\N" }); + $csv->undef_str (undef); + my $s = $csv->undef_str; + +This attribute optionally defines the output of undefined fields. The value +passed is not changed at all, so if it needs quotation, the quotation needs +to be included in the value of the attribute. Use with caution, as passing +a value like C<",",,,,"""> will for sure mess up your output. The default +for this attribute is C, meaning no special treatment. + +This attribute is useful when exporting CSV data to be imported in custom +loaders, like for MySQL, that recognize special sequences for C data. + +=head3 verbatim + + my $csv = Text::CSV_PP->new ({ verbatim => 1 }); + $csv->verbatim (0); + my $f = $csv->verbatim; + +This is a quite controversial attribute to set, but makes some hard things +possible. + +The rationale behind this attribute is to tell the parser that the normally +special characters newline (C) and Carriage Return (C) will not be +special when this flag is set, and be dealt with as being ordinary binary +characters. This will ease working with data with embedded newlines. + +When C is used with L, L auto-C's +every line. + +Imagine a file format like + + M^^Hans^Janssen^Klas 2\n2A^Ja^11-06-2007#\r\n + +where, the line ending is a very specific C<"#\r\n">, and the sep_char is a +C<^> (caret). None of the fields is quoted, but embedded binary data is +likely to be present. With the specific line ending, this should not be too +hard to detect. + +By default, Text::CSV_PP' parse function is instructed to only know about +C<"\n"> and C<"\r"> to be legal line endings, and so has to deal with the +embedded newline as a real C, so it can scan the next line if +binary is true, and the newline is inside a quoted field. With this option, +we tell L to parse the line as if C<"\n"> is just nothing more than +a binary character. + +For L this means that the parser has no more idea about line ending +and L Cs line endings on reading. + +=head3 types + +A set of column types; the attribute is immediately passed to the L +method. + +=head3 callbacks + +See the L section below. + +=head3 accessors + +To sum it up, + + $csv = Text::CSV_PP->new (); + +is equivalent to + + $csv = Text::CSV_PP->new ({ + eol => undef, # \r, \n, or \r\n + sep_char => ',', + sep => undef, + quote_char => '"', + quote => undef, + escape_char => '"', + binary => 0, + decode_utf8 => 1, + auto_diag => 0, + diag_verbose => 0, + blank_is_undef => 0, + empty_is_undef => 0, + allow_whitespace => 0, + allow_loose_quotes => 0, + allow_loose_escapes => 0, + allow_unquoted_escape => 0, + always_quote => 0, + quote_empty => 0, + quote_space => 1, + escape_null => 1, + quote_binary => 1, + keep_meta_info => 0, + verbatim => 0, + undef_str => undef, + types => undef, + callbacks => undef, + }); + +For all of the above mentioned flags, an accessor method is available where +you can inquire the current value, or change the value + + my $quote = $csv->quote_char; + $csv->binary (1); + +It is not wise to change these settings halfway through writing C data +to a stream. If however you want to create a new stream using the available +C object, there is no harm in changing them. + +If the L constructor call fails, it returns C, and makes the +fail reason available through the L method. + + $csv = Text::CSV_PP->new ({ ecs_char => 1 }) or + die "".Text::CSV_PP->error_diag (); + +L will return a string like + + "INI - Unknown attribute 'ecs_char'" + +=head2 known_attributes + + @attr = Text::CSV_PP->known_attributes; + @attr = Text::CSV_PP::known_attributes; + @attr = $csv->known_attributes; + +This method will return an ordered list of all the supported attributes as +described above. This can be useful for knowing what attributes are valid +in classes that use or extend Text::CSV_PP. + +=head2 print + + $status = $csv->print ($fh, $colref); + +Similar to L + L
+ L, but much more efficient. +It expects an array ref as input (not an array!) and the resulting string +is not really created, but immediately written to the C<$fh> object, +typically an IO handle or any other object that offers a L method. + +For performance reasons C does not create a result string, so all +L
, L, L, and L methods will return +undefined information after executing this method. + +If C<$colref> is C (explicit, not through a variable argument) and +L was used to specify fields to be printed, it is possible +to make performance improvements, as otherwise data would have to be copied +as arguments to the method call: + + $csv->bind_columns (\($foo, $bar)); + $status = $csv->print ($fh, undef); + +A short benchmark + + my @data = ("aa" .. "zz"); + $csv->bind_columns (\(@data)); + + $csv->print ($fh, [ @data ]); # 11800 recs/sec + $csv->print ($fh, \@data ); # 57600 recs/sec + $csv->print ($fh, undef ); # 48500 recs/sec + +=head2 say + + $status = $csv->say ($fh, $colref); + +Like L|/print>, but L|/eol> defaults to C<$\>. + +=head2 print_hr + + $csv->print_hr ($fh, $ref); + +Provides an easy way to print a C<$ref> (as fetched with L) +provided the column names are set with L. + +It is just a wrapper method with basic parameter checks over + + $csv->print ($fh, [ map { $ref->{$_} } $csv->column_names ]); + +=head2 combine + + $status = $csv->combine (@fields); + +This method constructs a C record from C<@fields>, returning success +or failure. Failure can result from lack of arguments or an argument that +contains an invalid character. Upon success, L
can be called to +retrieve the resultant C string. Upon failure, the value returned by +L is undefined and L could be called to retrieve the +invalid argument. + +=head2 string + + $line = $csv->string (); + +This method returns the input to L or the resultant C string +of L, whichever was called more recently. + +=head2 getline + + $colref = $csv->getline ($fh); + +This is the counterpart to L, as L is the counterpart to +L: it parses a row from the C<$fh> handle using the L +method associated with C<$fh> and parses this row into an array ref. This +array ref is returned by the function or C for failure. When C<$fh> +does not support C, you are likely to hit errors. + +When fields are bound with L the return value is a reference +to an empty list. + +The L, L, and L methods are meaningless again. + +=head2 getline_all + + $arrayref = $csv->getline_all ($fh); + $arrayref = $csv->getline_all ($fh, $offset); + $arrayref = $csv->getline_all ($fh, $offset, $length); + +This will return a reference to a list of L results. +In this call, C is disabled. If C<$offset> is negative, as +with C, only the last C records of C<$fh> are taken +into consideration. + +Given a CSV file with 10 lines: + + lines call + ----- --------------------------------------------------------- + 0..9 $csv->getline_all ($fh) # all + 0..9 $csv->getline_all ($fh, 0) # all + 8..9 $csv->getline_all ($fh, 8) # start at 8 + - $csv->getline_all ($fh, 0, 0) # start at 0 first 0 rows + 0..4 $csv->getline_all ($fh, 0, 5) # start at 0 first 5 rows + 4..5 $csv->getline_all ($fh, 4, 2) # start at 4 first 2 rows + 8..9 $csv->getline_all ($fh, -2) # last 2 rows + 6..7 $csv->getline_all ($fh, -4, 2) # first 2 of last 4 rows + +=head2 getline_hr + +The L and L methods work together to allow you +to have rows returned as hashrefs. You must call L first to +declare your column names. + + $csv->column_names (qw( code name price description )); + $hr = $csv->getline_hr ($fh); + print "Price for $hr->{name} is $hr->{price} EUR\n"; + +L will croak if called before L. + +Note that L creates a hashref for every row and will be much +slower than the combined use of L and L but still +offering the same ease of use hashref inside the loop: + + my @cols = @{$csv->getline ($fh)}; + $csv->column_names (@cols); + while (my $row = $csv->getline_hr ($fh)) { + print $row->{price}; + } + +Could easily be rewritten to the much faster: + + my @cols = @{$csv->getline ($fh)}; + my $row = {}; + $csv->bind_columns (\@{$row}{@cols}); + while ($csv->getline ($fh)) { + print $row->{price}; + } + +Your mileage may vary for the size of the data and the number of rows. With +perl-5.14.2 the comparison for a 100_000 line file with 14 rows: + + Rate hashrefs getlines + hashrefs 1.00/s -- -76% + getlines 4.15/s 313% -- + +=head2 getline_hr_all + + $arrayref = $csv->getline_hr_all ($fh); + $arrayref = $csv->getline_hr_all ($fh, $offset); + $arrayref = $csv->getline_hr_all ($fh, $offset, $length); + +This will return a reference to a list of L +results. In this call, L|/keep_meta_info> is disabled. + +=head2 parse + + $status = $csv->parse ($line); + +This method decomposes a C string into fields, returning success or +failure. Failure can result from a lack of argument or the given C +string is improperly formatted. Upon success, L can be called to +retrieve the decomposed fields. Upon failure calling L will return +undefined data and L can be called to retrieve the invalid +argument. + +You may use the L method for setting column types. See L' +description below. + +The C<$line> argument is supposed to be a simple scalar. Everything else is +supposed to croak and set error 1500. + +=head2 fragment + +This function tries to implement RFC7111 (URI Fragment Identifiers for the +text/csv Media Type) - http://tools.ietf.org/html/rfc7111 + + my $AoA = $csv->fragment ($fh, $spec); + +In specifications, C<*> is used to specify the I item, a dash (C<->) +to indicate a range. All indices are C<1>-based: the first row or column +has index C<1>. Selections can be combined with the semi-colon (C<;>). + +When using this method in combination with L, the returned +reference will point to a list of hashes instead of a list of lists. A +disjointed cell-based combined selection might return rows with different +number of columns making the use of hashes unpredictable. + + $csv->column_names ("Name", "Age"); + my $AoH = $csv->fragment ($fh, "col=3;8"); + +If the L callback is active, it is also called on every line +parsed and skipped before the fragment. + +=over 2 + +=item row + + row=4 + row=5-7 + row=6-* + row=1-2;4;6-* + +=item col + + col=2 + col=1-3 + col=4-* + col=1-2;4;7-* + +=item cell + +In cell-based selection, the comma (C<,>) is used to pair row and column + + cell=4,1 + +The range operator (C<->) using Cs can be used to define top-left and +bottom-right C location + + cell=3,1-4,6 + +The C<*> is only allowed in the second part of a pair + + cell=3,2-*,2 # row 3 till end, only column 2 + cell=3,2-3,* # column 2 till end, only row 3 + cell=3,2-*,* # strip row 1 and 2, and column 1 + +Cells and cell ranges may be combined with C<;>, possibly resulting in rows +with different number of columns + + cell=1,1-2,2;3,3-4,4;1,4;4,1 + +Disjointed selections will only return selected cells. The cells that are +not specified will not be included in the returned set, not even as +C. As an example given a C like + + 11,12,13,...19 + 21,22,...28,29 + : : + 91,...97,98,99 + +with C will return: + + 11,12,14 + 21,22 + 33,34 + 41,43,44 + +Overlapping cell-specs will return those cells only once, So +C will return: + + 11,12,13 + 21,22,23,24 + 31,32,33,34 + 42,43,44 + +=back + +L does B allow different +types of specs to be combined (either C I C I C). +Passing an invalid fragment specification will croak and set error 2013. + +=head2 column_names + +Set the "keys" that will be used in the L calls. If no keys +(column names) are passed, it will return the current setting as a list. + +L accepts a list of scalars (the column names) or a single +array_ref, so you can pass the return value from L too: + + $csv->column_names ($csv->getline ($fh)); + +L does B checking on duplicates at all, which might lead +to unexpected results. Undefined entries will be replaced with the string +C<"\cAUNDEF\cA">, so + + $csv->column_names (undef, "", "name", "name"); + $hr = $csv->getline_hr ($fh); + +Will set C<< $hr->{"\cAUNDEF\cA"} >> to the 1st field, C<< $hr->{""} >> to +the 2nd field, and C<< $hr->{name} >> to the 4th field, discarding the 3rd +field. + +L croaks on invalid arguments. + +=head2 header + +This method does NOT work in perl-5.6.x + +Parse the CSV header and set L|/sep>, column_names and encoding. + + my @hdr = $csv->header ($fh); + $csv->header ($fh, { sep_set => [ ";", ",", "|", "\t" ] }); + $csv->header ($fh, { detect_bom => 1, munge_column_names => "lc" }); + +The first argument should be a file handle. + +This method resets some object properties, as it is supposed to be invoked +only once per file or stream. It will leave attributes C and +C alone of setting column names is disabled. Reading headers +on previously process objects might fail on perl-5.8.0 and older. + +Assuming that the file opened for parsing has a header, and the header does +not contain problematic characters like embedded newlines, read the first +line from the open handle then auto-detect whether the header separates the +column names with a character from the allowed separator list. + +If any of the allowed separators matches, and none of the I allowed +separators match, set L|/sep> to that separator for the current +CSV_PP instance and use it to parse the first line, map those to lowercase, +and use that to set the instance L: + + my $csv = Text::CSV_PP->new ({ binary => 1, auto_diag => 1 }); + open my $fh, "<", "file.csv"; + binmode $fh; # for Windows + $csv->header ($fh); + while (my $row = $csv->getline_hr ($fh)) { + ... + } + +If the header is empty, contains more than one unique separator out of the +allowed set, contains empty fields, or contains identical fields (after +folding), it will croak with error 1010, 1011, 1012, or 1013 respectively. + +If the header contains embedded newlines or is not valid CSV in any other +way, this method will croak and leave the parse error untouched. + +A successful call to C
will always set the L|/sep> of the +C<$csv> object. This behavior can not be disabled. + +=head3 return value + +On error this method will croak. + +In list context, the headers will be returned whether they are used to set +L or not. + +In scalar context, the instance itself is returned. B: the values as +found in the header will effectively be B if C is +false. + +=head3 Options + +=over 2 + +=item sep_set + + $csv->header ($fh, { sep_set => [ ";", ",", "|", "\t" ] }); + +The list of legal separators defaults to C<[ ";", "," ]> and can be changed +by this option. As this is probably the most often used option, it can be +passed on its own as an unnamed argument: + + $csv->header ($fh, [ ";", ",", "|", "\t", "::", "\x{2063}" ]); + +Multi-byte sequences are allowed, both multi-character and Unicode. See +L|/sep>. + +=item detect_bom + + $csv->header ($fh, { detect_bom => 1 }); + +The default behavior is to detect if the header line starts with a BOM. If +the header has a BOM, use that to set the encoding of C<$fh>. This default +behavior can be disabled by passing a false value to C. + +Supported encodings from BOM are: UTF-8, UTF-16BE, UTF-16LE, UTF-32BE, and +UTF-32LE. BOM's also support UTF-1, UTF-EBCDIC, SCSU, BOCU-1, and GB-18030 +but L does not (yet). UTF-7 is not supported. + +If a supported BOM was detected as start of the stream, it is stored in the +abject attribute C. + + my $enc = $csv->{ENCODING}; + +The encoding is used with C on C<$fh>. + +If the handle was opened in a (correct) encoding, this method will B +alter the encoding, as it checks the leading B of the first line. In +case the stream starts with a decode BOM (C), C<{ENCODING}> will be +C<""> (empty) instead of the default C. + +=item munge_column_names + +This option offers the means to modify the column names into something that +is most useful to the application. The default is to map all column names +to lower case. + + $csv->header ($fh, { munge_column_names => "lc" }); + +The following values are available: + + lc - lower case + uc - upper case + none - do not change + \%hash - supply a mapping + \&cb - supply a callback + +Literal: + + $csv->header ($fh, { munge_column_names => "none" }); + +Hash: + + $csv->header ($fh, { munge_column_names => { foo => "sombrero" }); + +if a value does not exist, the original value is used unchanged + +Callback: + + $csv->header ($fh, { munge_column_names => sub { fc } }); + $csv->header ($fh, { munge_column_names => sub { "column_".$col++ } }); + $csv->header ($fh, { munge_column_names => sub { lc (s/\W+/_/gr) } }); + +As this callback is called in a C, you can use C<$_> directly. + +=item set_column_names + + $csv->header ($fh, { set_column_names => 1 }); + +The default is to set the instances column names using L if +the method is successful, so subsequent calls to L can return +a hash. Disable setting the header can be forced by using a false value for +this option. + +As described in L above, content is lost in scalar context. + +=back + +=head3 Validation + +When receiving CSV files from external sources, this method can be used to +protect against changes in the layout by restricting to known headers (and +typos in the header fields). + + my %known = ( + "record key" => "c_rec", + "rec id" => "c_rec", + "id_rec" => "c_rec", + "kode" => "code", + "code" => "code", + "vaule" => "value", + "value" => "value", + ); + my $csv = Text::CSV_PP->new ({ binary => 1, auto_diag => 1 }); + open my $fh, "<", $source or die "$source: $!"; + $csv->header ($fh, { munge_column_names => sub { + s/\s+$//; + s/^\s+//; + $known{lc $_} or die "Unknown column '$_' in $source"; + }}); + while (my $row = $csv->getline_hr ($fh)) { + say join "\t", $row->{c_rec}, $row->{code}, $row->{value}; + } + +=head2 bind_columns + +Takes a list of scalar references to be used for output with L or +to store in the fields fetched by L. When you do not pass enough +references to store the fetched fields in, L will fail with error +C<3006>. If you pass more than there are fields to return, the content of +the remaining references is left untouched. + + $csv->bind_columns (\$code, \$name, \$price, \$description); + while ($csv->getline ($fh)) { + print "The price of a $name is \x{20ac} $price\n"; + } + +To reset or clear all column binding, call L with the single +argument C. This will also clear column names. + + $csv->bind_columns (undef); + +If no arguments are passed at all, L will return the list of +current bindings or C if no binds are active. + +Note that in parsing with C, the fields are set on the fly. +That implies that if the third field of a row causes an error (or this row +has just two fields where the previous row had more), the first two fields +already have been assigned the values of the current row, while the rest of +the fields will still hold the values of the previous row. If you want the +parser to fail in these cases, use the L|/strict> attribute. + +=head2 eof + + $eof = $csv->eof (); + +If L or L was used with an IO stream, this method will +return true (1) if the last call hit end of file, otherwise it will return +false (''). This is useful to see the difference between a failure and end +of file. + +Note that if the parsing of the last line caused an error, C is still +true. That means that if you are I using L, an idiom like + + while (my $row = $csv->getline ($fh)) { + # ... + } + $csv->eof or $csv->error_diag; + +will I report the error. You would have to change that to + + while (my $row = $csv->getline ($fh)) { + # ... + } + +$csv->error_diag and $csv->error_diag; + +=head2 types + + $csv->types (\@tref); + +This method is used to force that (all) columns are of a given type. For +example, if you have an integer column, two columns with doubles and a +string column, then you might do a + + $csv->types ([Text::CSV_PP::IV (), + Text::CSV_PP::NV (), + Text::CSV_PP::NV (), + Text::CSV_PP::PV ()]); + +Column types are used only for I columns while parsing, in other +words by the L and L methods. + +You can unset column types by doing a + + $csv->types (undef); + +or fetch the current type settings with + + $types = $csv->types (); + +=over 4 + +=item IV + +Set field type to integer. + +=item NV + +Set field type to numeric/float. + +=item PV + +Set field type to string. + +=back + +=head2 fields + + @columns = $csv->fields (); + +This method returns the input to L or the resultant decomposed +fields of a successful L, whichever was called more recently. + +Note that the return value is undefined after using L, which does +not fill the data structures returned by L. + +=head2 meta_info + + @flags = $csv->meta_info (); + +This method returns the "flags" of the input to L or the flags of +the resultant decomposed fields of L, whichever was called more +recently. + +For each field, a meta_info field will hold flags that inform something +about the field returned by the L method or passed to the +L method. The flags are bit-wise-C'd like: + +=over 2 + +=item C< >0x0001 + +The field was quoted. + +=item C< >0x0002 + +The field was binary. + +=back + +See the C methods below. + +=head2 is_quoted + + my $quoted = $csv->is_quoted ($column_idx); + +Where C<$column_idx> is the (zero-based) index of the column in the last +result of L. + +This returns a true value if the data in the indicated column was enclosed +in L|/quote_char> quotes. This might be important for fields +where content C<,20070108,> is to be treated as a numeric value, and where +C<,"20070108",> is explicitly marked as character string data. + +This method is only valid when L is set to a true value. + +=head2 is_binary + + my $binary = $csv->is_binary ($column_idx); + +Where C<$column_idx> is the (zero-based) index of the column in the last +result of L. + +This returns a true value if the data in the indicated column contained any +byte in the range C<[\x00-\x08,\x10-\x1F,\x7F-\xFF]>. + +This method is only valid when L is set to a true value. + +=head2 is_missing + + my $missing = $csv->is_missing ($column_idx); + +Where C<$column_idx> is the (zero-based) index of the column in the last +result of L. + + $csv->keep_meta_info (1); + while (my $hr = $csv->getline_hr ($fh)) { + $csv->is_missing (0) and next; # This was an empty line + } + +When using L, it is impossible to tell if the parsed fields +are C because they where not filled in the C stream or because +they were not read at all, as B the fields defined by L +are set in the hash-ref. If you still need to know if all fields in each +row are provided, you should enable L|/keep_meta_info> so +you can check the flags. + +If L|/keep_meta_info> is C, C will +always return C, regardless of C<$column_idx> being valid or not. If +this attribute is C it will return either C<0> (the field is present) +or C<1> (the field is missing). + +A special case is the empty line. If the line is completely empty - after +dealing with the flags - this is still a valid CSV line: it is a record of +just one single empty field. However, if C is set, invoking +C with index C<0> will now return true. + +=head2 status + + $status = $csv->status (); + +This method returns the status of the last invoked L or L +call. Status is success (true: C<1>) or failure (false: C or C<0>). + +=head2 error_input + + $bad_argument = $csv->error_input (); + +This method returns the erroneous argument (if it exists) of L or +L, whichever was called more recently. If the last invocation was +successful, C will return C. + +=head2 error_diag + + Text::CSV_PP->error_diag (); + $csv->error_diag (); + $error_code = 0 + $csv->error_diag (); + $error_str = "" . $csv->error_diag (); + ($cde, $str, $pos, $rec, $fld) = $csv->error_diag (); + +If (and only if) an error occurred, this function returns the diagnostics +of that error. + +If called in void context, this will print the internal error code and the +associated error message to STDERR. + +If called in list context, this will return the error code and the error +message in that order. If the last error was from parsing, the rest of the +values returned are a best guess at the location within the line that was +being parsed. Their values are 1-based. The position currently is index of +the byte at which the parsing failed in the current record. It might change +to be the index of the current character in a later release. The records is +the index of the record parsed by the csv instance. The field number is the +index of the field the parser thinks it is currently trying to parse. See +F for how this can be used. + +If called in scalar context, it will return the diagnostics in a single +scalar, a-la C<$!>. It will contain the error code in numeric context, and +the diagnostics message in string context. + +When called as a class method or a direct function call, the diagnostics +are that of the last L call. + +=head2 record_number + + $recno = $csv->record_number (); + +Returns the records parsed by this csv instance. This value should be more +accurate than C<$.> when embedded newlines come in play. Records written by +this instance are not counted. + +=head2 SetDiag + + $csv->SetDiag (0); + +Use to reset the diagnostics if you are dealing with errors. + +=head1 FUNCTIONS + +This section is also taken from Text::CSV_XS. + +=head2 csv + +This function is not exported by default and should be explicitly requested: + + use Text::CSV_PP qw( csv ); + +This is an high-level function that aims at simple (user) interfaces. This +can be used to read/parse a C file or stream (the default behavior) or +to produce a file or write to a stream (define the C attribute). It +returns an array- or hash-reference on parsing (or C on fail) or the +numeric value of L on writing. When this function fails you +can get to the error using the class call to L + + my $aoa = csv (in => "test.csv") or + die Text::CSV_PP->error_diag; + +This function takes the arguments as key-value pairs. This can be passed as +a list or as an anonymous hash: + + my $aoa = csv ( in => "test.csv", sep_char => ";"); + my $aoh = csv ({ in => $fh, headers => "auto" }); + +The arguments passed consist of two parts: the arguments to L itself +and the optional attributes to the C object used inside the function +as enumerated and explained in L. + +If not overridden, the default option used for CSV is + + auto_diag => 1 + escape_null => 0 + +The option that is always set and cannot be altered is + + binary => 1 + +As this function will likely be used in one-liners, it allows C to +be abbreviated as C, and C to be abbreviated as C +or C. + +Alternative invocations: + + my $aoa = Text::CSV_PP::csv (in => "file.csv"); + + my $csv = Text::CSV_PP->new (); + my $aoa = $csv->csv (in => "file.csv"); + +In the latter case, the object attributes are used from the existing object +and the attribute arguments in the function call are ignored: + + my $csv = Text::CSV_PP->new ({ sep_char => ";" }); + my $aoh = $csv->csv (in => "file.csv", bom => 1); + +will parse using C<;> as C, not C<,>. + +=head3 in + +Used to specify the source. C can be a file name (e.g. C<"file.csv">), +which will be opened for reading and closed when finished, a file handle +(e.g. C<$fh> or C), a reference to a glob (e.g. C<\*ARGV>), the glob +itself (e.g. C<*STDIN>), or a reference to a scalar (e.g. C<\q{1,2,"csv"}>). + +When used with L, C should be a reference to a CSV structure (AoA +or AoH) or a CODE-ref that returns an array-reference or a hash-reference. +The code-ref will be invoked with no arguments. + + my $aoa = csv (in => "file.csv"); + + open my $fh, "<", "file.csv"; + my $aoa = csv (in => $fh); + + my $csv = [ [qw( Foo Bar )], [ 1, 2 ], [ 2, 3 ]]; + my $err = csv (in => $csv, out => "file.csv"); + +If called in void context without the L attribute, the resulting ref +will be used as input to a subsequent call to csv: + + csv (in => "file.csv", filter => { 2 => sub { length > 2 }}) + +will be a shortcut to + + csv (in => csv (in => "file.csv", filter => { 2 => sub { length > 2 }})) + +where, in the absence of the C attribute, this is a shortcut to + + csv (in => csv (in => "file.csv", filter => { 2 => sub { length > 2 }}), + out => *STDOUT) + +=head3 out + + csv (in => $aoa, out => "file.csv"); + csv (in => $aoa, out => $fh); + csv (in => $aoa, out => STDOUT); + csv (in => $aoa, out => *STDOUT); + csv (in => $aoa, out => \*STDOUT); + csv (in => $aoa, out => \my $data); + csv (in => $aoa, out => undef); + csv (in => $aoa, out => \"skip"); + +In output mode, the default CSV options when producing CSV are + + eol => "\r\n" + +The L attribute is ignored in output mode. + +C can be a file name (e.g. C<"file.csv">), which will be opened for +writing and closed when finished, a file handle (e.g. C<$fh> or C), a +reference to a glob (e.g. C<\*STDOUT>), the glob itself (e.g. C<*STDOUT>), +or a reference to a scalar (e.g. C<\my $data>). + + csv (in => sub { $sth->fetch }, out => "dump.csv"); + csv (in => sub { $sth->fetchrow_hashref }, out => "dump.csv", + headers => $sth->{NAME_lc}); + +When a code-ref is used for C, the output is generated per invocation, +so no buffering is involved. This implies that there is no size restriction +on the number of records. The C function ends when the coderef returns +a false value. + +If C is set to a reference of the literal string C<"skip">, the output +will be suppressed completely, which might be useful in combination with a +filter for side effects only. + + my %cache; + csv (in => "dump.csv", + out => \"skip", + on_in => sub { $cache{$_[1][1]}++ }); + +Currently, setting C to any false value (C, C<"">, 0) will be +equivalent to C<\"skip">. + +=head3 encoding + +If passed, it should be an encoding accepted by the C<:encoding()> option +to C. There is no default value. This attribute does not work in perl +5.6.x. C can be abbreviated to C for ease of use in command +line invocations. + +If C is set to the literal value C<"auto">, the method L
+will be invoked on the opened stream to check if there is a BOM and set the +encoding accordingly. This is equal to passing a true value in the option +L|/detect_bom>. + +=head3 detect_bom + +If C is given, the method L will be invoked on the +opened stream to check if there is a BOM and set the encoding accordingly. + +C can be abbreviated to C. + +This is the same as setting L|/encoding> to C<"auto">. + +Note that as the method L is invoked, its default is to also set +the headers. + +=head3 headers + +If this attribute is not given, the default behavior is to produce an array +of arrays. + +If C is supplied, it should be an anonymous list of column names, +an anonymous hashref, a coderef, or a literal flag: C, C, C, +or C. + +=over 2 + +=item skip + +When C is used, the header will not be included in the output. + + my $aoa = csv (in => $fh, headers => "skip"); + +=item auto + +If C is used, the first line of the C source will be read as the +list of field headers and used to produce an array of hashes. + + my $aoh = csv (in => $fh, headers => "auto"); + +=item lc + +If C is used, the first line of the C source will be read as the +list of field headers mapped to lower case and used to produce an array of +hashes. This is a variation of C. + + my $aoh = csv (in => $fh, headers => "lc"); + +=item uc + +If C is used, the first line of the C source will be read as the +list of field headers mapped to upper case and used to produce an array of +hashes. This is a variation of C. + + my $aoh = csv (in => $fh, headers => "uc"); + +=item CODE + +If a coderef is used, the first line of the C source will be read as +the list of mangled field headers in which each field is passed as the only +argument to the coderef. This list is used to produce an array of hashes. + + my $aoh = csv (in => $fh, + headers => sub { lc ($_[0]) =~ s/kode/code/gr }); + +this example is a variation of using C where all occurrences of C +are replaced with C. + +=item ARRAY + +If C is an anonymous list, the entries in the list will be used +as field names. The first line is considered data instead of headers. + + my $aoh = csv (in => $fh, headers => [qw( Foo Bar )]); + csv (in => $aoa, out => $fh, headers => [qw( code description price )]); + +=item HASH + +If C is an hash reference, this implies C, but header fields +for that exist as key in the hashref will be replaced by the value for that +key. Given a CSV file like + + post-kode,city,name,id number,fubble + 1234AA,Duckstad,Donald,13,"X313DF" + +using + + csv (headers => { "post-kode" => "pc", "id number" => "ID" }, ... + +will return an entry like + + { pc => "1234AA", + city => "Duckstad", + name => "Donald", + ID => "13", + fubble => "X313DF", + } + +=back + +See also L|/munge_column_names> and +L|/set_column_names>. + +=head3 munge_column_names + +If C is set, the method L is invoked on the +opened stream with all matching arguments to detect and set the headers. + +C can be abbreviated to C. + +=head3 key + +If passed, will default L|/headers> to C<"auto"> and return a +hashref instead of an array of hashes. + + my $ref = csv (in => "test.csv", key => "code"); + +with test.csv like + + code,product,price,color + 1,pc,850,gray + 2,keyboard,12,white + 3,mouse,5,black + +will return + + { 1 => { + code => 1, + color => 'gray', + price => 850, + product => 'pc' + }, + 2 => { + code => 2, + color => 'white', + price => 12, + product => 'keyboard' + }, + 3 => { + code => 3, + color => 'black', + price => 5, + product => 'mouse' + } + } + +The C attribute can be combined with L|/headers> for C +date that has no header line, like + + my $ref = csv ( + in => "foo.csv", + headers => [qw( c_foo foo bar description stock )], + key => "c_foo", + ); + +=head3 keep_headers + +When using hashes, keep the column names into the arrayref passed, so all +headers are available after the call in the original order. + + my $aoh = csv (in => "file.csv", keep_headers => \my @hdr); + +This attribute can be abbreviated to C or passed as C. + +This attribute implies a default of C for the C attribute. + +=head3 fragment + +Only output the fragment as defined in the L method. This option +is ignored when I C. See L. + +Combining all of them could give something like + + use Text::CSV_PP qw( csv ); + my $aoh = csv ( + in => "test.txt", + encoding => "utf-8", + headers => "auto", + sep_char => "|", + fragment => "row=3;6-9;15-*", + ); + say $aoh->[15]{Foo}; + +=head3 sep_set + +If C is set, the method L is invoked on the opened stream +to detect and set L|/sep_char> with the given set. + +C can be abbreviated to C. + +Note that as the L method is invoked, its default is to also set +the headers. + +=head3 set_column_names + +If C is passed, the method L is invoked on the +opened stream with all arguments meant for L. + +If C is passed as a false value, the content of the first +row is only preserved if the output is AoA: + +With an input-file like + + bAr,foo + 1,2 + 3,4,5 + +This call + + my $aoa = csv (in => $file, set_column_names => 0); + +will result in + + [[ "bar", "foo" ], + [ "1", "2" ], + [ "3", "4", "5" ]] + +and + + my $aoa = csv (in => $file, set_column_names => 0, munge => "none"); + +will result in + + [[ "bAr", "foo" ], + [ "1", "2" ], + [ "3", "4", "5" ]] + +=head2 Callbacks + +Callbacks enable actions triggered from the I of Text::CSV_PP. + +While most of what this enables can easily be done in an unrolled loop as +described in the L callbacks can be used to meet special demands +or enhance the L function. + +=over 2 + +=item error + + $csv->callbacks (error => sub { $csv->SetDiag (0) }); + +the C callback is invoked when an error occurs, but I when +L is set to a true value. A callback is invoked with the values +returned by L: + + my ($c, $s); + + sub ignore3006 + { + my ($err, $msg, $pos, $recno, $fldno) = @_; + if ($err == 3006) { + # ignore this error + ($c, $s) = (undef, undef); + Text::CSV_PP->SetDiag (0); + } + # Any other error + return; + } # ignore3006 + + $csv->callbacks (error => \&ignore3006); + $csv->bind_columns (\$c, \$s); + while ($csv->getline ($fh)) { + # Error 3006 will not stop the loop + } + +=item after_parse + + $csv->callbacks (after_parse => sub { push @{$_[1]}, "NEW" }); + while (my $row = $csv->getline ($fh)) { + $row->[-1] eq "NEW"; + } + +This callback is invoked after parsing with L only if no error +occurred. The callback is invoked with two arguments: the current C +parser object and an array reference to the fields parsed. + +The return code of the callback is ignored unless it is a reference to the +string "skip", in which case the record will be skipped in L. + + sub add_from_db + { + my ($csv, $row) = @_; + $sth->execute ($row->[4]); + push @$row, $sth->fetchrow_array; + } # add_from_db + + my $aoa = csv (in => "file.csv", callbacks => { + after_parse => \&add_from_db }); + +This hook can be used for validation: + +=over 2 + +=item FAIL + +Die if any of the records does not validate a rule: + + after_parse => sub { + $_[1][4] =~ m/^[0-9]{4}\s?[A-Z]{2}$/ or + die "5th field does not have a valid Dutch zipcode"; + } + +=item DEFAULT + +Replace invalid fields with a default value: + + after_parse => sub { $_[1][2] =~ m/^\d+$/ or $_[1][2] = 0 } + +=item SKIP + +Skip records that have invalid fields (only applies to L): + + after_parse => sub { $_[1][0] =~ m/^\d+$/ or return \"skip"; } + +=back + +=item before_print + + my $idx = 1; + $csv->callbacks (before_print => sub { $_[1][0] = $idx++ }); + $csv->print (*STDOUT, [ 0, $_ ]) for @members; + +This callback is invoked before printing with L only if no error +occurred. The callback is invoked with two arguments: the current C +parser object and an array reference to the fields passed. + +The return code of the callback is ignored. + + sub max_4_fields + { + my ($csv, $row) = @_; + @$row > 4 and splice @$row, 4; + } # max_4_fields + + csv (in => csv (in => "file.csv"), out => *STDOUT, + callbacks => { before print => \&max_4_fields }); + +This callback is not active for L. + +=back + +=head3 Callbacks for csv () + +The L allows for some callbacks that do not integrate in XS internals +but only feature the L function. + + csv (in => "file.csv", + callbacks => { + filter => { 6 => sub { $_ > 15 } }, # first + after_parse => sub { say "AFTER PARSE"; }, # first + after_in => sub { say "AFTER IN"; }, # second + on_in => sub { say "ON IN"; }, # third + }, + ); + + csv (in => $aoh, + out => "file.csv", + callbacks => { + on_in => sub { say "ON IN"; }, # first + before_out => sub { say "BEFORE OUT"; }, # second + before_print => sub { say "BEFORE PRINT"; }, # third + }, + ); + +=over 2 + +=item filter + +This callback can be used to filter records. It is called just after a new +record has been scanned. The callback accepts a: + +=over 2 + +=item hashref + +The keys are the index to the row (the field name or field number, 1-based) +and the values are subs to return a true or false value. + + csv (in => "file.csv", filter => { + 3 => sub { m/a/ }, # third field should contain an "a" + 5 => sub { length > 4 }, # length of the 5th field minimal 5 + }); + + csv (in => "file.csv", filter => { foo => sub { $_ > 4 }}); + +If the keys to the filter hash contain any character that is not a digit it +will also implicitly set L to C<"auto"> unless L was +already passed as argument. When headers are active, returning an array of +hashes, the filter is not applicable to the header itself. + +All sub results should match, as in AND. + +The context of the callback sets C<$_> localized to the field indicated by +the filter. The two arguments are as with all other callbacks, so the other +fields in the current row can be seen: + + filter => { 3 => sub { $_ > 100 ? $_[1][1] =~ m/A/ : $_[1][6] =~ m/B/ }} + +If the context is set to return a list of hashes (L is defined), +the current record will also be available in the localized C<%_>: + + filter => { 3 => sub { $_ > 100 && $_{foo} =~ m/A/ && $_{bar} < 1000 }} + +If the filter is used to I the content by changing C<$_>, make sure +that the sub returns true in order not to have that record skipped: + + filter => { 2 => sub { $_ = uc }} + +will upper-case the second field, and then skip it if the resulting content +evaluates to false. To always accept, end with truth: + + filter => { 2 => sub { $_ = uc; 1 }} + +=item coderef + + csv (in => "file.csv", filter => sub { $n++; 0; }); + +If the argument to C is a coderef, it is an alias or shortcut to a +filter on column 0: + + csv (filter => sub { $n++; 0 }); + +is equal to + + csv (filter => { 0 => sub { $n++; 0 }); + +=item filter-name + + csv (in => "file.csv", filter => "not_blank"); + csv (in => "file.csv", filter => "not_empty"); + csv (in => "file.csv", filter => "filled"); + +These are predefined filters + +Given a file like (line numbers prefixed for doc purpose only): + + 1:1,2,3 + 2: + 3:, + 4:"" + 5:,, + 6:, , + 7:"", + 8:" " + 9:4,5,6 + +=over 2 + +=item not_blank + +Filter out the blank lines + +This filter is a shortcut for + + filter => { 0 => sub { @{$_[1]} > 1 or + defined $_[1][0] && $_[1][0] ne "" } } + +Due to the implementation, it is currently impossible to also filter lines +that consists only of a quoted empty field. These lines are also considered +blank lines. + +With the given example, lines 2 and 4 will be skipped. + +=item not_empty + +Filter out lines where all the fields are empty. + +This filter is a shortcut for + + filter => { 0 => sub { grep { defined && $_ ne "" } @{$_[1]} } } + +A space is not regarded being empty, so given the example data, lines 2, 3, +4, 5, and 7 are skipped. + +=item filled + +Filter out lines that have no visible data + +This filter is a shortcut for + + filter => { 0 => sub { grep { defined && m/\S/ } @{$_[1]} } } + +This filter rejects all lines that I have at least one field that does +not evaluate to the empty string. + +With the given example data, this filter would skip lines 2 through 8. + +=back + +=back + +=item after_in + +This callback is invoked for each record after all records have been parsed +but before returning the reference to the caller. The hook is invoked with +two arguments: the current C parser object and a reference to the +record. The reference can be a reference to a HASH or a reference to an +ARRAY as determined by the arguments. + +This callback can also be passed as an attribute without the C +wrapper. + +=item before_out + +This callback is invoked for each record before the record is printed. The +hook is invoked with two arguments: the current C parser object and a +reference to the record. The reference can be a reference to a HASH or a +reference to an ARRAY as determined by the arguments. + +This callback can also be passed as an attribute without the C +wrapper. + +This callback makes the row available in C<%_> if the row is a hashref. In +this case C<%_> is writable and will change the original row. + +=item on_in + +This callback acts exactly as the L or the L hooks. + +This callback can also be passed as an attribute without the C +wrapper. + +This callback makes the row available in C<%_> if the row is a hashref. In +this case C<%_> is writable and will change the original row. So e.g. with + + my $aoh = csv ( + in => \"foo\n1\n2\n", + headers => "auto", + on_in => sub { $_{bar} = 2; }, + ); + +C<$aoh> will be: + + [ { foo => 1, + bar => 2, + } + { foo => 2, + bar => 2, + } + ] + +=item csv + +The I L can also be called as a method or with an existing +Text::CSV_PP object. This could help if the function is to be invoked a lot +of times and the overhead of creating the object internally over and over +again would be prevented by passing an existing instance. + + my $csv = Text::CSV_PP->new ({ binary => 1, auto_diag => 1 }); + + my $aoa = $csv->csv (in => $fh); + my $aoa = csv (in => $fh, csv => $csv); + +both act the same. Running this 20000 times on a 20 lines CSV file, showed +a 53% speedup. + +=back + +=head1 DIAGNOSTICS + +This section is also taken from Text::CSV_XS. + +Still under construction ... + +If an error occurs, C<< $csv->error_diag >> can be used to get information +on the cause of the failure. Note that for speed reasons the internal value +is never cleared on success, so using the value returned by L +in normal cases - when no error occurred - may cause unexpected results. + +If the constructor failed, the cause can be found using L as a +class method, like C<< Text::CSV_PP->error_diag >>. + +The C<< $csv->error_diag >> method is automatically invoked upon error when +the contractor was called with L|/auto_diag> set to C<1> or +C<2>, or when L is in effect. When set to C<1>, this will cause a +C with the error message, when set to C<2>, it will C. C<2012 - +EOF> is excluded from L|/auto_diag> reports. + +Errors can be (individually) caught using the L callback. + +The errors as described below are available. I have tried to make the error +itself explanatory enough, but more descriptions will be added. For most of +these errors, the first three capitals describe the error category: + +=over 2 + +=item * +INI + +Initialization error or option conflict. + +=item * +ECR + +Carriage-Return related parse error. + +=item * +EOF + +End-Of-File related parse error. + +=item * +EIQ + +Parse error inside quotation. + +=item * +EIF + +Parse error inside field. + +=item * +ECB + +Combine error. + +=item * +EHR + +HashRef parse related error. + +=back + +And below should be the complete list of error codes that can be returned: + +=over 2 + +=item * +1001 "INI - sep_char is equal to quote_char or escape_char" + +The L cannot be equal to L or to L, as this +would invalidate all parsing rules. + +=item * +1002 "INI - allow_whitespace with escape_char or quote_char SP or TAB" + +Using the L|/allow_whitespace> attribute when either +L|/quote_char> or L|/escape_char> is equal to +C or C is too ambiguous to allow. + +=item * +1003 "INI - \r or \n in main attr not allowed" + +Using default L|/eol> characters in either L|/sep_char>, +L|/quote_char>, or L|/escape_char> is not +allowed. + +=item * +1004 "INI - callbacks should be undef or a hashref" + +The L|/Callbacks> attribute only allows one to be C or +a hash reference. + +=item * +1005 "INI - EOL too long" + +The value passed for EOL is exceeding its maximum length (16). + +=item * +1006 "INI - SEP too long" + +The value passed for SEP is exceeding its maximum length (16). + +=item * +1007 "INI - QUOTE too long" + +The value passed for QUOTE is exceeding its maximum length (16). + +=item * +1008 "INI - SEP undefined" + +The value passed for SEP should be defined and not empty. + +=item * +1010 "INI - the header is empty" + +The header line parsed in the L is empty. + +=item * +1011 "INI - the header contains more than one valid separator" + +The header line parsed in the L contains more than one (unique) +separator character out of the allowed set of separators. + +=item * +1012 "INI - the header contains an empty field" + +The header line parsed in the L is contains an empty field. + +=item * +1013 "INI - the header contains nun-unique fields" + +The header line parsed in the L contains at least two identical +fields. + +=item * +1014 "INI - header called on undefined stream" + +The header line cannot be parsed from an undefined sources. + +=item * +1500 "PRM - Invalid/unsupported argument(s)" + +Function or method called with invalid argument(s) or parameter(s). + +=item * +1501 "PRM - The key attribute is passed as an unsupported type" + +The C attribute is of an unsupported type. + +=item * +2010 "ECR - QUO char inside quotes followed by CR not part of EOL" + +When L|/eol> has been set to anything but the default, like +C<"\r\t\n">, and the C<"\r"> is following the B (closing) +L|/quote_char>, where the characters following the C<"\r"> do +not make up the L|/eol> sequence, this is an error. + +=item * +2011 "ECR - Characters after end of quoted field" + +Sequences like C<1,foo,"bar"baz,22,1> are not allowed. C<"bar"> is a quoted +field and after the closing double-quote, there should be either a new-line +sequence or a separation character. + +=item * +2012 "EOF - End of data in parsing input stream" + +Self-explaining. End-of-file while inside parsing a stream. Can happen only +when reading from streams with L, as using L is done on +strings that are not required to have a trailing L|/eol>. + +=item * +2013 "INI - Specification error for fragments RFC7111" + +Invalid specification for URI L specification. + +=item * +2014 "ENF - Inconsistent number of fields" + +Inconsistent number of fields under strict parsing. + +=item * +2021 "EIQ - NL char inside quotes, binary off" + +Sequences like C<1,"foo\nbar",22,1> are allowed only when the binary option +has been selected with the constructor. + +=item * +2022 "EIQ - CR char inside quotes, binary off" + +Sequences like C<1,"foo\rbar",22,1> are allowed only when the binary option +has been selected with the constructor. + +=item * +2023 "EIQ - QUO character not allowed" + +Sequences like C<"foo "bar" baz",qu> and C<2023,",2008-04-05,"Foo, Bar",\n> +will cause this error. + +=item * +2024 "EIQ - EOF cannot be escaped, not even inside quotes" + +The escape character is not allowed as last character in an input stream. + +=item * +2025 "EIQ - Loose unescaped escape" + +An escape character should escape only characters that need escaping. + +Allowing the escape for other characters is possible with the attribute +L. + +=item * +2026 "EIQ - Binary character inside quoted field, binary off" + +Binary characters are not allowed by default. Exceptions are fields that +contain valid UTF-8, that will automatically be upgraded if the content is +valid UTF-8. Set L|/binary> to C<1> to accept binary data. + +=item * +2027 "EIQ - Quoted field not terminated" + +When parsing a field that started with a quotation character, the field is +expected to be closed with a quotation character. When the parsed line is +exhausted before the quote is found, that field is not terminated. + +=item * +2030 "EIF - NL char inside unquoted verbatim, binary off" + +=item * +2031 "EIF - CR char is first char of field, not part of EOL" + +=item * +2032 "EIF - CR char inside unquoted, not part of EOL" + +=item * +2034 "EIF - Loose unescaped quote" + +=item * +2035 "EIF - Escaped EOF in unquoted field" + +=item * +2036 "EIF - ESC error" + +=item * +2037 "EIF - Binary character in unquoted field, binary off" + +=item * +2110 "ECB - Binary character in Combine, binary off" + +=item * +2200 "EIO - print to IO failed. See errno" + +=item * +3001 "EHR - Unsupported syntax for column_names ()" + +=item * +3002 "EHR - getline_hr () called before column_names ()" + +=item * +3003 "EHR - bind_columns () and column_names () fields count mismatch" + +=item * +3004 "EHR - bind_columns () only accepts refs to scalars" + +=item * +3006 "EHR - bind_columns () did not pass enough refs for parsed fields" + +=item * +3007 "EHR - bind_columns needs refs to writable scalars" + +=item * +3008 "EHR - unexpected error in bound fields" + +=item * +3009 "EHR - print_hr () called before column_names ()" + +=item * +3010 "EHR - print_hr () called with invalid arguments" + +=back + +=head1 SEE ALSO + +L, L + +Older versions took many regexp from L + +=head1 AUTHOR + +Kenichi Ishigaki, Eishigaki[at]cpan.orgE +Makamaka Hannyaharamitu, Emakamaka[at]cpan.orgE + +Text::CSV_XS was written by Ejoe[at]ispsoft.deE +and maintained by Eh.m.brand[at]xs4all.nlE. + +Text::CSV was written by Ealan[at]mfgrtl.comE. + +=head1 COPYRIGHT AND LICENSE + +Copyright 2017- by Kenichi Ishigaki, Eishigaki[at]cpan.orgE +Copyright 2005-2015 by Makamaka Hannyaharamitu, Emakamaka[at]cpan.orgE + +Most of the code and doc is directly taken from the pure perl part of +Text::CSV_XS. + +Copyright (C) 2007-2016 H.Merijn Brand. All rights reserved. +Copyright (C) 1998-2001 Jochen Wiedmann. All rights reserved. +Copyright (C) 1997 Alan Citterman. All rights reserved. + +This library is free software; you can redistribute it and/or modify +it under the same terms as Perl itself. + +=cut diff --git a/hw/vendor/hpdcache/rtl/tb/scripts/perl5/Text/Table.pm b/hw/vendor/hpdcache/rtl/tb/scripts/perl5/Text/Table.pm new file mode 100644 index 00000000..b6fa6ea9 --- /dev/null +++ b/hw/vendor/hpdcache/rtl/tb/scripts/perl5/Text/Table.pm @@ -0,0 +1,1629 @@ +# Text::Table - Organize Data in Tables +package Text::Table; + +use strict; +use warnings; + +use 5.008; + +use List::Util qw(sum max); + +use Text::Aligner qw(align); + +our $VERSION = '1.133'; + +use overload ( + # Don't stringify when only doing boolean tests, since stringification can + # be expensive for large tables: + bool => sub { return 1; }, + # Stringify when Table instances are used in most other scalar cases: + '""' => 'stringify', +); + +### User interface: How to specify columns and column separators + +sub _is_sep { + my $datum = shift; + + return + ( + defined($datum) + and + ( + (ref($datum) eq 'SCALAR') + or + (ref($datum) eq 'HASH' and $datum->{is_sep}) + ) + ); +} + +sub _get_sep_title_body +{ + my $sep = shift; + + return + +( ref($sep) eq 'HASH' ) + ? @{ $sep }{qw(title body)} + : split( /\n/, ${$sep}, -1 ) ; +} + +sub _parse_sep { + my $sep = shift; + + if (!defined($sep)) + { + $sep = ''; + } + + my ($title, $body) = _get_sep_title_body($sep); + + if (!defined($body)) + { + $body = $title; + } + + ($title, $body) = align( 'left', $title, $body); + + return + { + is_sep => 1, + title => $title, + body => $body, + }; +} + +sub _default_if_empty +{ + my ($ref, $default) = @_; + + if (! (defined($$ref) && length($$ref))) + { + $$ref = $default; + } + + return; +} + +sub _is_align +{ + my $align = shift; + + return $align =~ /\A(?:left|center|right)/; +} + +sub _parse_spec { + my $spec = shift; + + if (!defined($spec)) + { + $spec = ''; + } + + my $alispec = qr/^ *(?:left|center|right|num|point|auto)/; + my ( $title, $align, $align_title, $align_title_lines, $sample); + if ( ref ($spec) eq 'HASH' ) { + ( $title, $align, $align_title, $align_title_lines, $sample) = + @{$spec}{qw( title align align_title align_title_lines sample )}; + } else { + my $alispec = qr/&(.*)/; + if ( $spec =~ $alispec ) { + ($title, $align, $sample) = ($spec =~ /(.*)^$alispec\n?(.*)/sm); + } else { + $title = $spec; + } + for my $s ($title, $sample) + { + if (defined($s)) + { + chomp($s); + } + } + } + + # Assign default values. + foreach my $x ($title, $sample) + { + if (!defined($x)) + { + $x = []; + } + elsif (ref($x) ne 'ARRAY') + { + $x = [ split /\n/, $x, -1]; + } + } + + _default_if_empty(\$align, 'auto'); + + unless ( + ref $align eq 'Regexp' or + $align =~ /^(?:left|center|right|num\(?|point\(?|auto)/ + ) { + _warn( "Invalid align specification: '$align', using 'auto'"); + $align = 'auto'; + } + + _default_if_empty(\$align_title, 'left'); + + if ( ! _is_align($align_title) ) { + _warn( "Invalid align_title specification: " . + "'$align_title', using 'left'", + ); + $align_title = 'left'; + } + + _default_if_empty(\$align_title_lines, $align_title); + + if ( ! _is_align($align_title_lines) ) { + _warn( "Invalid align_title_lines specification: " . + "'$align_title_lines', using 'left'", + ); + $align_title_lines = 'left'; + } + + return + { + title => $title, + align => $align, + align_title => $align_title, + align_title_lines => $align_title_lines, + sample => $sample, + }; +} + +### table creation + +sub new +{ + my $tb = bless {}, shift; + + return $tb->_entitle( [ @_ ] ); +} + +sub _blank +{ + my $self = shift; + + if (@_) + { + $self->{blank} = shift; + } + + return $self->{blank}; +} + +sub _cols +{ + my $self = shift; + + if (@_) + { + $self->{cols} = shift; + } + + return $self->{cols}; +} + +sub _forms +{ + my $self = shift; + + if (@_) + { + $self->{forms} = shift; + } + + return $self->{forms}; +} + +sub _lines +{ + my $self = shift; + + if (@_) + { + $self->{lines} = shift; + } + + return $self->{lines}; +} + +sub _spec +{ + my $self = shift; + + if (@_) + { + $self->{spec} = shift; + } + + return $self->{spec}; +} + +sub _titles +{ + my $self = shift; + + if (@_) + { + $self->{titles} = shift; + } + + return $self->{titles}; +} + +sub _entitle { + my ($tb, $sep_list) = @_; # will be completely overwritten + # find active separators and, well separate them from col specs. + # n+1 separators for n cols + my ( @seps, @spec); # separators and column specifications + my $sep; + foreach my $sep_item ( @{$sep_list} ) { + if ( _is_sep ($sep_item) ) { + $sep = _parse_sep($sep_item); + } else { + push @seps, $sep; + push @spec, _parse_spec($sep_item); + undef $sep; + } + } + push @seps, $sep; + # build sprintf formats from separators + my $title_form = _compile_field_format('title', \@seps); + my $body_form = _compile_field_format('body', \@seps); + + # pre_align titles + my @titles = map { [ @{ $_->{title} } ] } @spec; + + my $title_height = max(0, map { scalar(@$_) } @titles); + + foreach my $title (@titles) + { + push @{$title}, ( '') x ( $title_height - @{$title}); + } + + foreach my $t_idx (0 .. $#titles) + { + align($spec[$t_idx]->{align_title_lines}, @{$titles[$t_idx]}); + } + + # build data structure + $tb->_spec(\@spec); + $tb->_cols([ map [], 1 .. @spec]); + $tb->_forms([ $title_form, $body_form]); # separators condensed + $tb->_titles(\@titles); + + $tb->_clear_cache; + + return $tb; +} + +# sprintf-format for line assembly, using separators +sub _compile_format { + my $seps = shift; # mix of strings and undef (for default) + + for my $idx (0 .. $#$seps) + { + if (!defined($seps->[$idx])) + { + $seps->[$idx] = ($idx == 0 or $idx == $#$seps) ? '' : q{ }; + } + else + { + # protect against sprintf + $seps->[$idx] =~ s/%/%%/g; + } + } + return join '%s', @$seps; +} + +sub _compile_field_format +{ + my ($field, $seps) = @_; + + return _compile_format( + [map { defined($_) ? $_->{$field} : undef } @$seps] + ); +} + +# reverse format compilation (used by colrange()) +sub _recover_separators { + my $format = shift; + my @seps = split /(?_select_group( $_), @_; + # get column selection, checking indices (some have been checked by + # _select_group, but not all) + my @sel = map $tb->_check_index( $_), grep !_is_sep( $_), @args; + # replace indices with column spec to create subtable + for my $arg (@args) + { + if (! _is_sep($arg)) + { + $arg = $tb->_spec->[ $arg]; + } + } + my $sub = ref( $tb)->new( @args); + # sneak in data columns + @{ $sub->{ cols}} = map { [ @$_ ] } @{ $tb->_cols}[ @sel]; + $sub; +} + +# the first non-separator column in the group is tested if it has any data +# if so, the group is returned, else nothing +sub _select_group { + my ( $tb, $group) = @_; + return $group unless ref $group eq 'ARRAY'; + GROUP_LOOP: + for my $g ( @$group ) { + if (_is_sep($g)) + { + next GROUP_LOOP; + } + $tb->_check_index($g); + + if (grep { $_} @{ $tb->_cols->[$g] }) + { + return @$group; + } + return; # no more tries after non-sep was found + } + return; # no column index in group, no select +} + +# check index for validity, return arg if returns at all +sub _check_index { + my $tb = shift; + my ( $i) = @_; + my $n = $tb->n_cols; + my $ok = eval { + use warnings FATAL => 'numeric'; + -$n <= $i and $i < $n; # in range of column array? + }; + _warn( "Invalid column index '$_'") if $@ or not $ok; + shift; +} + +### data entry + +sub _clear_cache { + my ($tb) = @_; + + $tb->_blank(undef()); + $tb->_lines(undef()); + + return; +} + +# add one data line or split the line into follow-up lines +sub add { + my $tb = shift; + + if (! $tb->n_cols) { + $tb->_entitle( [ ('') x @_] ); + } + + foreach my $row ( + _transpose( + [ + map { [ defined() ? split( /\n/ ) : '' ] } @_ + ] + ) + ) + { + $tb->_add(@$row); + } + $tb->_clear_cache; + + return $tb; +} + +# add one data line +sub _add { + my $tb = shift; + + foreach my $col ( @{ $tb->_cols} ) { + push @{$col}, shift(@_); + } + + $tb->_clear_cache; + + return $tb; +} + +# add one or more data lines +sub load { + my $tb = shift; + foreach my $row ( @_ ) { + if (!defined($row)) { + $row = ''; + } + $tb->add( + (ref($row) eq 'ARRAY') ? (@$row) : (split ' ',$row) + ) + } + $tb; +} + +sub clear { + my $tb = shift; + + foreach my $col (@{ $tb->_cols} ) + { + $col = []; + } + + $tb->_clear_cache; + + return $tb; +} + +### access to output area + +## sizes + +# number of table columns +sub n_cols { scalar @{ $_[0]->{ spec}} } + +# number of title lines +sub title_height { $_[ 0]->n_cols and scalar @{ $_[ 0]->_titles->[ 0]} } + +# number of data lines +sub body_height +{ + my ($tb) = @_; + + return ($tb->n_cols && scalar @{ $tb->_cols->[0] }); +} + +# total height +sub table_height +{ + my $tb = shift; + return $tb->title_height + $tb->body_height; +} + +BEGIN { *height = \&table_height; } # alias + +# number of characters in each table line. need to build the table to know +sub width +{ + my ($tb) = @_; + + return $tb->height && (length( ($tb->table(0))[0] ) - 1); +} + +sub _normalize_col_index +{ + my ($tb, $col_index) = @_; + + $col_index ||= 0; + + if ($col_index < 0) + { + $col_index += $tb->n_cols; + } + + if ($col_index < 0) + { + $col_index = 0; + } + elsif ($col_index > $tb->n_cols) + { + $col_index = $tb->n_cols; + } + + return $col_index; +} + +# start and width of each column +sub colrange { + my ( $tb, $proto_col_index) = @_; + + my $col_index = $tb->_normalize_col_index($proto_col_index); + + return ( 0, 0) unless $tb->width; # width called, $tb->_blank() exists now + my @widths = map { length } @{ $tb->_blank}, ''; + @widths = @widths[ 0 .. $col_index]; + + my $width = pop @widths; + my $pos = sum(@widths) || 0; + + my $seps_aref = _recover_separators( $tb->_forms->[ 0]); + + my $sep_sum = 0; + foreach my $sep (@$seps_aref[ 0 .. $col_index]) + { + $sep_sum += length($sep); + } + + return ( $pos+$sep_sum, $width ) ; +} + +## printable output + +# whole table +sub table { + my $tb = shift; + + return $tb->_table_portion( $tb->height, 0, @_); +} + +# only titles +sub title { + my $tb = shift; + + return $tb->_table_portion( $tb->title_height, 0, @_); +} + +# only body +sub body { + my $tb = shift; + + return $tb->_table_portion( $tb->body_height, $tb->title_height, @_); +} + +sub stringify +{ + my ($tb) = @_; + + return (scalar ( $tb->table() )); +} + +### common internals + +# common representation of table(), title() and body() + +sub _table_portion_as_aref +{ + my $tb = shift; + + my $total = shift; + my $offset = shift; + + my ( $from, $n) = ( 0, $total); # if no parameters + + if ( @_ ) { + $from = shift; + $n = @_ ? shift : 1; # one line if not given + } + + ( $from, $n) = _limit_range( $total, $from, $n); + + my $limit = $tb->title_height; # title format below + $from += $offset; + + return + [ + map $tb->_assemble_line( $_ >= $limit, $tb->_table_line( $_), 0), + $from .. $from + $n - 1 + ]; +} + +sub _table_portion +{ + my $tb = shift; + + my $lines_aref = $tb->_table_portion_as_aref(@_); + + return (wantarray ? @$lines_aref : join('', @$lines_aref)); +} + +sub _limit_range +{ + my ( $total, $from, $n) = @_; + + $from ||= 0; + $from += $total if $from < 0; + $n = $total unless defined $n; + + return ( 0, 0) if $from + $n < 0 or $from >= $total; + + $from = 0 if $from < 0; + $n = $total - $from if $n > $total - $from; + + return( $from, $n); +} + +# get table line (formatted, including titles). fill cache if needed +sub _table_line { + my ($tb, $idx) = @_; + + if (! $tb->_lines) + { + $tb->_lines([ $tb->_build_table_lines ]); + } + + return $tb->_lines->[$idx]; +} + +# build array of lines of justified data items +sub _build_table_lines { + my $tb = shift; + + # copy data columns, replacing undef with '' + my @cols = + map + { [ map { defined($_) ? $_ : '' } @$_ ] } + @{ $tb->_cols() } ; + + # add set of empty strings for blank line (needed to build horizontal rules) + foreach my $col (@cols) + { + push @$col, ''; + } + + # add samples for minimum alignment + foreach my $col_idx (0 .. $#cols) + { + push @{$cols[$col_idx]}, @{$tb->_spec->[$col_idx]->{sample}}; + } + + # align to style + foreach my $col_idx (0 .. $#cols) + { + align($tb->_spec->[$col_idx]->{align}, @{$cols[$col_idx]}); + } + + # trim off samples, but leave blank line + foreach my $col (@cols) + { + splice( @{$col}, 1 + $tb->body_height ); + } + + # include titles + foreach my $col_idx (0 .. $#cols) + { + unshift @{$cols[$col_idx]}, @{$tb->_titles->[$col_idx]}; + } + + # align title and body portions of columns + # blank line will be there even with no data + foreach my $col_idx (0 .. $#cols) + { + align($tb->_spec->[$col_idx]->{align_title}, @{$cols[$col_idx]}); + } + + # deposit a blank line, pulling it off the columns. + # *_rule() knows this is done + my @blank; + + foreach my $col (@cols) + { + push @blank, pop(@$col); + } + + $tb->_blank(\@blank); + + return _transpose_n( $tb->height, \@cols); # bye-bye, @cols +} + +# destructively transpose a number of lines/cols from an array of arrayrefs +sub _transpose_n { + my ($n, $cols) = @_; + + return map { [ map { shift @$_ } @$cols] } 1 .. $n; +} + +# like _transpose_n, but find the number to transpose from max of given +sub _transpose +{ + my ($cols) = @_; + + my $m = max ( map { scalar(@$_) } @$cols, []); + + return _transpose_n( $m, $cols); +} + +# make a line from a number of formatted data elements +sub _assemble_line { + my ($tb, $in_body, $line_aref, $replace_spaces) = @_; + + my $format = $tb->_forms->[ !!$in_body]; + + if ($replace_spaces) + { + $format =~ s/\s/=/g; + } + + return sprintf($format, @$line_aref) . "\n"; +} + +sub _text_rule +{ + my ($tb, $rule, $char, $alt) = @_; + + # replace blanks with $char. If $alt is given, replace nonblanks + # with $alt + if ( defined $alt ) + { + $rule =~ s/(.)/$1 eq ' ' ? $char : $alt/ge; + } + else + { + $rule =~ s/ /$char/g if $char ne ' '; + } + + return $rule; +} + +# build a rule line +sub _rule { + my $tb = shift; + + return + (!$tb->width) ? '' : $tb->_positive_width_rule(@_); +} + +sub _positive_width_rule +{ + my ($tb, $in_body, $char, $alt) = @_; + + my $rule = $tb->_assemble_line( $in_body, $tb->_blank, + ((ref($char) eq "CODE") ? 1 : 0), + ); + + return $tb->_render_rule($rule, $char, $alt); +} + +sub _render_rule +{ + my ($tb, $rule, $char, $alt) = @_; + + if (ref($char) eq "CODE") + { + return $tb->_render_rule_with_callbacks($rule, $char, $alt); + } + else + { + _default_if_empty(\$char, ' '); + + return $tb->_text_rule($rule, $char, $alt); + } +} + +sub _get_fixed_len_string +{ + my ($s, $len) = @_; + + $s = substr($s, 0, $len); + $s .= ' ' x ($len - length($s)); + + return $s; +} + +sub _render_rule_with_callbacks +{ + my ($tb, $rule, $char, $alt) = @_; + + my %callbacks = + ( + 'char' => { cb => $char, idx => 0, }, + 'alt' => { cb => $alt, idx => 0, }, + ); + + my $calc_substitution = sub { + my $s = shift; + + my $len = length($s); + + my $which = (($s =~ /\A /) ? 'char' : 'alt'); + my $rec = $callbacks{$which}; + + return _get_fixed_len_string( + scalar ($rec->{cb}->($rec->{idx}++, $len)), + $len, + ); + }; + + $rule =~ s/((.)\2*)/$calc_substitution->($1)/ge; + + return $rule; +} + +sub rule { + my $tb = shift; + return $tb->_rule( 0, @_); +} + +sub body_rule { + my $tb = shift; + return $tb->_rule( 1, @_); +} + +### warning behavior +use Carp; + +{ + my ( $warn, $fatal) = ( 0, 0); + + sub warnings + { + # Ignore the class/object. + my (undef, $toggle) = @_; + + $toggle ||= 'on'; + if ( $toggle eq 'off' ) + { + ($warn, $fatal) = (0, 0); + } + elsif ( $toggle eq 'fatal' ) + { + ($warn, $fatal) = (1, 1); + } + else + { + ($warn, $fatal) = (1, 0); + } + return $fatal ? 'fatal' : $warn ? 'on' : 'off'; + } + + sub _warn + { + my $msg = shift; + + return unless $warn; + + if ($fatal) + { + croak( $msg) + } + + carp( $msg); + + return; + } +} + +=pod + +=encoding UTF-8 + +=head1 NAME + +Text::Table - Organize Data in Tables + +=head1 VERSION + +version 1.133 + +=head1 SYNOPSIS + + use Text::Table; + my $tb = Text::Table->new( + "Planet", "Radius\nkm", "Density\ng/cm^3" + ); + $tb->load( + [ "Mercury", 2360, 3.7 ], + [ "Venus", 6110, 5.1 ], + [ "Earth", 6378, 5.52 ], + [ "Jupiter", 71030, 1.3 ], + ); + print $tb; + +This prints a table from the given title and data like this: + + Planet Radius Density + km g/cm^3 + Mercury 2360 3.7 + Venus 6110 5.1 + Earth 6378 5.52 + Jupiter 71030 1.3 + +Note that two-line titles work, and that the planet names are aligned +differently than the numbers. + +=head1 DESCRIPTION + +Organization of data in table form is a time-honored and useful +method of data representation. While columns of data are trivially +generated by computer through formatted output, even simple tasks +like keeping titles aligned with the data columns are not trivial, +and the one-shot solutions one comes up with tend to be particularly +hard to maintain. Text::Table allows you to create and maintain +tables that adapt to alignment requirements as you use them. + +=head2 Overview + +The process is simple: you create a table (a Text::Table object) by +describing the columns the table is going to have. Then you load +lines of data into the table, and finally print the resulting output +lines. Alignment of data and column titles is handled dynamically +in dependence on the data present. + +=head2 Table Creation + +In the simplest case, if all you want is a number of (untitled) columns, +you create an unspecified table and start adding data to it. The number +of columns is taken from the first line of data. + +To specify a table you specify its columns. A column description +can contain a title and alignment requirements for the data, both +optional. Additionally, you can specify how the title is aligned with +the body of a column, and how the lines of a multiline title are +aligned among themselves. + +The columns are collected in the table in the +order they are given. On data entry, each column corresponds to +one data item, and in column selection columns are indexed left to +right, starting from 0. + +Each title can be a multiline string which will be blank-filled to +the length of the longest partial line. The largest number of title +lines in a column determines how many title lines the table has as a +whole, including the case that no column has any titles. + +On output, columns are separated by a single blank. You can control +what goes between columns by specifying separators between (or before, +or after) columns. Separators don't contain any data and don't count +in column indexing. They also don't accumulate: in a sequence of only +separators and no columns, only the last one counts. + +=head2 Status Information + +The width (in characters), height (in lines), number of columns, and +similar data about the table is available. + +=head2 Data Loading + +Table data is entered line-wise, each time specifying data entries +for all table columns. A bulk loader for many lines at once is also +available. You can clear the data from the table for re-use (though +you will more likely just create another table). + +Data can contain colorizing escape sequences (as provided by +C) without upsetting the alignment. + +=head2 Table Output + +The output area of a table is divided in the title and the body. + +The title contains the combined titles from the table columns, if +any. Its content never changes with a given table, but it may be +spread out differently on the page through alignment with the data. + +The body contains the data lines, aligned column-wise as specified, +and left-aligned with the column title. + +Each of these is arranged like a Perl array (counting from 0) and can +be accessed in portions by specifying a first line and the number +of following lines. Also like an array, giving a negative first line +counts from the end of the area. The whole table, the title followed +by the body, can also be accessed in this manner. + +The subdivisions are there so you can repeat the title (or parts of +it) along with parts of the body on output, whether for screen paging +or printout. + +A rule line is also available, which is the horizontal counterpart to +the separator columns you specify with the table. +It is basically a table line as it would appear if all data entries +in the line were empty, that is, a blank line except for where the +column separators have non-blank entries. If you print it between +data lines, it will not disrupt the vertical separator structure +as a plain blank line would. You can also request a solid rule +consisting of any character, and even one with the non-blank column +separators replaced by a character of your choice. This way you can +get the popular representation of line-crossings like so: + + | + ----+--- + | + +=head2 Warning Control + +On table creation, some parameters are checked and warnings issued +if you allow warnings. You can also turn warnings into fatal errors. + +=head1 SPECIFICATIONS + +=head2 Column Specification + +Each column specification is a single scalar. Columns can be either proper +data columns or column separators. Both can be specified either as +(possibly multi-line) strings, or in a more explicit form as hash-refs. +In the string form, proper columns are given as plain strings, and +separators are given as scalar references to strings. In hash form, +separators have a true value in the field C while proper columns +don't have this field. + +=over 4 + +=item Columns as strings + +A column is given as a column title (any number of lines), +optionally followed by alignment requirements. Alignment requirements +start with a line that begins with an ampersand "&". However, only the +last such line counts as such, so if you have title lines that begin +with "&", just append an ampersand on a line by itself as a dummy +alignment section if you don't have one anyway. + +What follows the ampersand on its line is the alignment style (like +I, I, ... as described in L<"Alignment">), you want for +the data in this column. If nothing follows, the general default I +is used. If you specify an invalid alignment style, it falls back to +left alignment. + +The lines that follow can contain sample data for this column. These +are considered for alignment in the column, but never actually appear +in the output. The effect is to guarantee a minimum width for the +column even if the current data doesn't require it. This helps dampen +the oscillations in the appearance of dynamically aligned tables. + +=item Columns as Hashes + +The format is + + { + title => $title, + align => $align, + sample => $sample, + align_title => $align_title, + align_title_lines => $align_title_lines, + } + +$title contains the title lines and $sample the sample data. Both can +be given as a string or as an array-ref to the list of lines. $align contains +the alignment style (without a leading ampersand), usually as a string. +You can also give a regular expression here, which specifies regex alignment. +A regex can only be specified in the hash form of a column specification. + +In hash form you can also specify how the title of a column is aligned +with its body. To do this, you specify the keyword C with +C, C or C
. Other alignment specifications are not +valid here. The default is C. + +C also specifies how the lines of a multiline title are +aligned among themselves. If you want a different alignment, you +can specify it with the key C. Again, only C, +C or C
are allowed. + +Do not put other keys than those mentioned above (I, I<align>, +I<align_title>, I<align_title_lines>, and I<sample>) into a hash that +specifies a column. Most would be ignored, but some would confuse the +interpreter (in particular, I<is_sep> has to be avoided). + +=item Separators as strings + +A separator must be given as a reference to a string (often a literal, +like C<\' | '>), any string that is given directly describes a column. + +It is usually just a (short) string that will be printed between +table columns on all table lines instead of the default single +blank. If you specify two separators (on two lines), the first one +will be used in the title and the other in the body of the table. + +=item Separators as Hashes + +The hash representation of a separator has the format + + { + is_sep => 1, + title => $title, + body => $body, + } + +$title is the separator to be used in the title area and $body +the one for the body. If only one is given, it will be used for +both. If none is given, a blank is used. If one is shorter than +the other, it is blank filled on the right. + +The value of C<is_sep> must be set to a true value, this is the +distinguishing feature of a separator. + +=back + +=head2 Alignment + +The original documentation to L<Text::Aligner> contains all the details +on alignment specification, but here is the rundown: + +The possible alignment specifications are I<left>, I<right>, I<center>, +I<num> and I<point> (which are synonyms), and I<auto>. The first +three explain themselves. + +I<num> (and I<point>) align the decimal point in the data, which is +assumed to the right if none is present. Strings that aren't +numbers are treated the same way, that is, they appear aligned +with the integers unless they contain a ".". Instead of the +decimal point ".", you can also specify any other string in +the form I<num(,)>, for instance. The string in parentheses +is aligned in the data. The synonym I<point> for I<num> may be +more appropriate in contexts that deal with arbitrary +strings, as in I<point(=E<gt>)> (which might be used to align certain +bits of Perl code). + +I<regex alignment> is a more sophisticated form of point alignment. +If you specify a regular expression, as delivered by C<qr//>, the start +of the match is used as the alignment point. If the regex contains +capturing parentheses, the last submatch counts. [The usefulness of +this feature is under consideration.] + +I<auto> alignment combines numeric alignment with left alignment. +Data items that look like numbers, and those that don't, form two +virtual columns and are aligned accordingly: C<num> for numbers and +C<left> for other strings. These columns are left-aligned with +each other (i.e. the narrower one is blank-filled) to form the +final alignment. + +This way, a column that happens to have only numbers in the data gets +I<num> alignment, a column with no numbers appears I<left>-aligned, +and mixed data is presented in a reasonable way. + +=head2 Column Selection + +Besides creating tables from scratch, they can be created by +selecting columns from an existing table. Tables created this +way contain the data from the columns they were built from. + +This is done by specifying the columns to select by their index +(where negative indices count backward from the last column). +The same column can be selected more than once and the sequence +of columns can be arbitrarily changed. Separators don't travel +with columns, but can be specified between the columns at selection +time. + +You can make the selection of one or more columns dependent on +the data content of one of them. If you specify some of the columns +in angle brackets [...], the whole group is only included in the +selection if the first column in the group contains any data that +evaluates to boolean true. That way you can de-select parts of a +table if it contains no interesting data. Any column separators +given in brackets are selected or deselected along with the rest +of it. + +=head1 PUBLIC METHODS + +=head2 Table Creation + +=over 4 + +=item new() + + my $tb = Text::Table->new( $column, ... ); + +creates a table with the columns specified. A column can be proper column +which contains and displays data, or a separator which tells how to fill +the space between columns. The format of the parameters is described under +L<"Column Specification">. Specifying an invalid alignment for a column +results in a warning if these are allowed. + +If no columns are specified, the number of columns is taken from the first +line of data added to the table. The effect is as if you had specified +C<Text::Table-E<gt>new( ( '') x $n)>, where C<$n> is the number of +columns. + +=item select() + + my $sub = $tb->select( $column, ...); + +creates a table from the listed columns of the table $tb, including +the data. Columns are specified as integer indices which refer to +the data columns of $tb. Columns can be repeated and specified in any +order. Negative indices count from the last column. If an invalid +index is specified, a warning is issued, if allowed. + +As with L<"new()">, separators can be interspersed among the column +indices and will be used between the columns of the new table. + +If you enclose some of the arguments (column indices or separators) in +angle brackets C<[...]> (technically, you specify them inside an +arrayref), they form a group for conditional selection. The group is +only included in the resulting table if the first actual column inside +the group contains any data that evaluate to a boolean true. This way +you can exclude groups of columns that wouldn't contribute anything +interesting. Note that separators are selected and de-selected with +their group. That way, more than one separator can appear between +adjacent columns. They don't add up, but only the rightmost separator +is used. A group that contains only separators is never selected. +[Another feature whose usefulness is under consideration.] + +=back + +=head2 Status Information + +=over 4 + +=item n_cols() + + $tb->n_cols + +returns the number of columns in the table. + +=item width() + + $tb->width + +returns the width (in characters) of the table. All table lines have +this length (not counting a final "\n" in the line), as well as the +separator lines returned by $tb->rule() and $b->body_rule(). +The width of a table can potentially be influenced by any data item +in it. + +=item height() + + $tb->height + +returns the total number of lines in a table, including title lines +and body lines. For orthogonality, the synonym table_height() also +exists. + +=item table_height() + +Same as C<< $table->height() >>. + +=item title_height() + + $tb->title_height + +returns the number of title lines in a table. + +=item body_height() + + $tb->body_height + +returns the number of lines in the table body. + +=item colrange() + + $tb->colrange( $i) + +returns the start position and width of the $i-th column (counting from 0) +of the table. If $i is negative, counts from the end of the table. If $i +is larger than the greatest column index, an imaginary column of width 0 +is assumed right of the table. + +=back + +=head2 Data Loading + +=over 4 + +=item add() + + $tb->add( $col1, ..., $colN) + +adds a data line to the table, returns the table. + +C<$col1>, ..., C<$colN> are scalars that +correspond to the table columns. Undefined entries are converted to '', +and extra data beyond the number of table columns is ignored. + +Data entries can be multi-line strings. The partial strings all go into +the same column. The corresponding fields of other columns remain empty +unless there is another multi-line entry in that column that fills the +fields. Adding a line with multi-line entries is equivalent to adding +multiple lines. + +Every call to C<add()> increases the body height of the table by the +number of effective lines, one in the absence of multiline entries. + +=item load() + + $tb->load( $line, ...) + +loads the data lines given into the table, returns the table. + +Every argument to C<load()> represents a data line to be added to the +table. The line can be given as an array(ref) containing the data +items, or as a string, which is split on whitespace to retrieve the +data. If an undefined argument is given, it is treated as an +empty line. + +=item clear() + + $tb->clear; + +deletes all data from the table and resets it to the state after +creation. Returns the table. The body height of a table is 0 after +C<clear()>. + +=back + +=head2 Table Output + +The three methods C<table()>, C<title()>, and C<body()> are very similar. +They access different parts of the printable output lines of a table with +similar methods. The details are described with the C<table()> method. + +=over 4 + +=item table() + +The C<table()> method returns lines from the entire table, starting +with the first title line and ending with the last body line. + +In array context, the lines are returned separately, in scalar context +they are joined together in a single string. + + my @lines = $tb->table; + my $line = $tb->table( $line_number); + my @lines = $tb->table( $line_number, $n); + +The first call returns all the lines in the table. The second call +returns one line given by $line_number. The third call returns $n +lines, starting with $line_number. If $line_number is negative, it +counts from the end of the array. Unlike the C<select()> method, +C<table()> (and its sister methods C<title()> and C<body()>) is +protected against large negative line numbers, it truncates the +range described by $line_number and $n to the existing lines. If +$n is 0 or negative, no lines are returned (an empty string in scalar +context). + +=item stringify() + +Returns a string representation of the table. This method is called for +stringification by overload. + + my @table_strings = map { $_->stringify() } @tables; + +=item title() + +Returns lines from the title area of a table, where the column titles +are rendered. Parameters and response to context are as with C<table()>, +but no lines are returned from outside the title area. + +=item body() + +Returns lines from the body area of a table, that is the part where +the data content is rendered, so that $tb->body( 0) is the first data +line. Parameters and response to context are as with C<table()>. + +=item rule() + + $tb->rule; + $tb->rule( $char); + $tb->rule( $char, $char1); + $tb->rule( sub { my ($index, $len) = @_; }, + sub { my ($index, $len) = @_; }, + ); + +Returns a rule for the table. + +A rule is a line of table width that can be used between table lines +to provide visual horizontal divisions, much like column separators +provide vertical visual divisions. In its basic form (returned by the +first call) it looks like a table line with no data, hence a blank +line except for the non-blank parts of any column-separators. If +one character is specified (the second call), it replaces the blanks +in the first form, but non-blank column separators are retained. If +a second character is specified, it replaces the non-blank parts of +the separators. So specifying the same character twice gives a solid +line of table width. Another useful combo is C<$tb-E<gt>rule( '-', '+')>, +together with separators that contain a single nonblank "|", for a +popular representation of line crossings. + +C<rule()> uses the column separators for the title section if there +is a difference. + +If callbacks are specified instead of the characters, then they receive the +index of the section of the rule they need to render and its desired length in +characters, and should return the string to put there. The indexes given +are 0 based (where 0 is either the left column separator or the leftmost +cell) and the strings will be trimmed or extended in the replacement. + +=item body_rule() + +C<body_rule()> works like <rule()>, except the rule is generated using +the column separators for the table body. + +=back + +=head2 Warning Control + +=over 4 + +=item warnings() + + Text::Table->warnings(); + Text::Table->warnings( 'on'); + Text::Table->warnings( 'off'): + Text::Table->warnings( 'fatal'): + +The C<warnings()> method is used to control the appearance of warning +messages while tables are manipulated. When Text::Table starts, warnings +are disabled. The default action of C<warnings()> is to turn warnings +on. The other possible arguments are self-explanatory. C<warnings()> +can also be called as an object method (C<$tb-E<gt>warnings( ...)>). + +=back + +=head1 VERSION + +This document pertains to Text::Table version 1.127 + +=head1 BUGS + +=over 4 + +=item o + +I<auto> alignment doesn't support alternative characters for the decimal +point. This is actually a bug in the underlying Text::Aligner by the +same author. + +=back + +=head1 AUTHOR + +=head2 MAINTAINER + +Shlomi Fish, L<http://www.shlomifish.org/> - CPAN ID: "SHLOMIF". + +=head2 ORIGINAL AUTHOR + + Anno Siegel + CPAN ID: ANNO + siegel@zrz.tu-berlin.de + http://www.tu-berlin.de/~siegel + +=head1 COPYRIGHT + +Copyright (c) 2002 Anno Siegel. All rights reserved. +This program is free software; you can redistribute +it and/or modify it under the terms of the ISC license. + +(This program had been licensed under the same terms as Perl itself up to +version 1.118 released on 2011, and was relicensed by permission of its +originator). + +The full text of the license can be found in the +LICENSE file included with this module. + +=head1 SEE ALSO + +L<Text::Aligner>, L<perl(1)> . + +=head1 AUTHOR + +Shlomi Fish <shlomif@cpan.org> + +=head1 COPYRIGHT AND LICENSE + +This software is Copyright (c) 2002 by Anno Siegel and others. + +This is free software, licensed under: + + The ISC License + +=head1 BUGS + +Please report any bugs or feature requests on the bugtracker website +L<http://rt.cpan.org/NoAuth/Bugs.html?Dist=Text-Table> or by email to +L<bug-text-table@rt.cpan.org|mailto:bug-text-table@rt.cpan.org>. + +When submitting a bug or request, please include a test-file or a +patch to an existing test-file that illustrates the bug or desired +feature. + +=for :stopwords cpan testmatrix url annocpan anno bugtracker rt cpants kwalitee diff irc mailto metadata placeholders metacpan + +=head1 SUPPORT + +=head2 Perldoc + +You can find documentation for this module with the perldoc command. + + perldoc Text::Table + +=head2 Websites + +The following websites have more information about this module, and may be of help to you. As always, +in addition to those websites please use your favorite search engine to discover more resources. + +=over 4 + +=item * + +MetaCPAN + +A modern, open-source CPAN search engine, useful to view POD in HTML format. + +L<http://metacpan.org/release/Text-Table> + +=item * + +Search CPAN + +The default CPAN search engine, useful to view POD in HTML format. + +L<http://search.cpan.org/dist/Text-Table> + +=item * + +RT: CPAN's Bug Tracker + +The RT ( Request Tracker ) website is the default bug/issue tracking system for CPAN. + +L<https://rt.cpan.org/Public/Dist/Display.html?Name=Text-Table> + +=item * + +AnnoCPAN + +The AnnoCPAN is a website that allows community annotations of Perl module documentation. + +L<http://annocpan.org/dist/Text-Table> + +=item * + +CPAN Ratings + +The CPAN Ratings is a website that allows community ratings and reviews of Perl modules. + +L<http://cpanratings.perl.org/d/Text-Table> + +=item * + +CPAN Forum + +The CPAN Forum is a web forum for discussing Perl modules. + +L<http://cpanforum.com/dist/Text-Table> + +=item * + +CPANTS + +The CPANTS is a website that analyzes the Kwalitee ( code metrics ) of a distribution. + +L<http://cpants.cpanauthors.org/dist/Text-Table> + +=item * + +CPAN Testers + +The CPAN Testers is a network of smokers who run automated tests on uploaded CPAN distributions. + +L<http://www.cpantesters.org/distro/T/Text-Table> + +=item * + +CPAN Testers Matrix + +The CPAN Testers Matrix is a website that provides a visual overview of the test results for a distribution on various Perls/platforms. + +L<http://matrix.cpantesters.org/?dist=Text-Table> + +=item * + +CPAN Testers Dependencies + +The CPAN Testers Dependencies is a website that shows a chart of the test results of all dependencies for a distribution. + +L<http://deps.cpantesters.org/?module=Text::Table> + +=back + +=head2 Bugs / Feature Requests + +Please report any bugs or feature requests by email to C<bug-text-table at rt.cpan.org>, or through +the web interface at L<https://rt.cpan.org/Public/Bug/Report.html?Queue=Text-Table>. You will be automatically notified of any +progress on the request by the system. + +=head2 Source Code + +The code is open to the world, and available for you to hack on. Please feel free to browse it and play +with it, or whatever. If you want to contribute patches, please send me a diff or prod me to pull +from your repository :) + +L<https://github.com/shlomif/Text-Table> + + git clone ssh://git@github.com:shlomif/Text-Table.git + +=cut + +__END__ +########################################### main pod documentation begin ## + + +1; diff --git a/hw/vendor/hpdcache/rtl/tb/scripts/random_numbers.dat b/hw/vendor/hpdcache/rtl/tb/scripts/random_numbers.dat new file mode 100644 index 00000000..2da62730 --- /dev/null +++ b/hw/vendor/hpdcache/rtl/tb/scripts/random_numbers.dat @@ -0,0 +1,9947 @@ +60 +94 +192 +236 +246 +295 +314 +351 +473 +678 +911 +951 +1108 +1288 +1396 +1578 +1610 +1741 +2073 +2225 +2353 +2491 +2629 +2633 +2654 +2693 +2709 +2885 +3049 +3084 +3206 +3247 +3253 +3454 +3541 +3651 +3699 +3797 +3877 +4106 +4336 +4448 +4474 +4595 +4774 +4938 +5116 +5411 +5478 +5685 +5753 +6073 +6085 +6130 +6264 +6265 +6322 +6381 +6434 +6439 +6475 +6572 +6662 +6811 +6895 +6968 +7255 +7339 +7435 +7463 +7495 +7615 +7845 +7849 +7853 +7883 +7886 +7989 +8004 +8098 +8137 +8328 +8399 +8645 +8654 +8761 +8781 +8890 +8949 +8985 +9027 +9118 +9218 +9406 +9440 +9494 +9618 +9750 +9839 +9913 +10132 +10186 +10437 +10549 +10598 +10758 +10819 +10820 +10847 +10967 +10978 +10989 +10997 +11064 +11101 +11189 +11228 +11396 +11453 +11596 +11638 +11698 +11703 +11822 +11976 +12072 +12177 +12541 +12616 +12807 +12828 +12862 +12926 +13150 +13517 +13532 +13875 +13897 +13953 +14039 +14158 +14635 +14796 +14865 +15007 +15010 +15017 +15159 +15293 +15390 +15418 +15493 +15615 +15665 +15687 +15700 +15717 +15730 +15870 +15892 +15897 +16005 +16137 +16279 +16338 +16403 +16656 +17068 +17528 +17684 +17817 +18116 +18229 +18255 +18345 +18368 +18398 +18547 +18588 +18746 +18877 +18974 +19028 +19133 +19286 +19379 +19456 +19893 +19922 +20009 +20068 +20116 +20152 +20344 +20761 +20886 +20928 +20995 +21122 +21216 +21344 +21411 +21758 +21834 +21848 +21926 +21982 +22077 +22134 +22228 +22390 +22398 +22692 +22718 +22726 +22861 +22905 +22912 +22950 +23042 +23056 +23181 +23427 +23514 +23723 +23749 +23836 +23876 +24066 +24071 +24111 +24132 +24183 +24293 +24566 +24918 +24924 +24937 +25099 +25104 +25159 +25194 +25424 +25590 +25732 +25815 +25914 +25935 +25969 +26037 +26209 +26223 +26238 +26290 +26308 +26494 +26507 +26578 +27080 +27119 +27207 +27309 +27418 +27432 +27434 +27578 +27586 +27719 +27797 +27811 +27968 +28012 +28025 +28050 +28091 +28113 +28136 +28330 +28342 +28464 +28499 +28500 +28554 +28574 +28738 +29197 +29533 +29668 +29703 +29943 +29977 +30040 +30125 +30144 +30225 +30321 +30650 +30938 +30990 +31145 +31284 +31314 +31363 +31502 +31742 +31877 +31985 +32073 +32168 +32321 +32428 +32450 +32535 +32569 +32575 +32783 +32935 +33148 +33255 +33514 +33524 +33939 +34041 +34070 +34242 +34325 +34442 +34570 +34617 +34886 +35231 +35391 +35442 +35450 +35559 +35616 +35778 +35839 +35865 +36084 +36306 +36571 +36913 +36944 +37055 +37114 +37195 +37562 +37814 +37871 +38088 +38114 +38130 +38230 +38296 +38475 +38582 +38590 +38597 +38618 +38645 +38663 +38690 +38724 +38782 +38964 +39120 +39124 +39200 +39234 +39367 +39374 +39467 +39470 +39770 +39773 +39898 +39920 +40140 +40181 +40317 +40440 +40521 +40582 +40732 +40771 +40881 +41056 +41077 +41137 +41226 +41373 +41455 +41483 +41575 +41742 +41805 +42240 +42305 +42376 +42395 +42398 +42633 +42670 +42758 +43059 +43170 +43407 +43453 +43739 +43842 +43965 +43999 +44002 +44179 +44224 +44270 +44292 +44433 +44543 +44963 +44987 +45119 +45172 +45179 +45211 +45272 +45407 +45448 +45604 +45810 +45842 +45850 +46179 +46472 +46474 +46479 +46505 +46691 +46939 +47091 +47200 +47211 +47243 +47254 +47472 +47788 +47791 +48019 +48046 +48080 +48361 +48431 +48742 +48750 +48969 +49004 +49206 +49307 +49411 +49445 +49534 +49562 +49571 +49651 +49763 +49937 +50170 +50366 +50446 +50459 +50556 +50564 +50599 +50855 +50874 +51178 +51777 +51842 +51921 +52115 +52218 +52547 +52585 +52715 +52725 +52842 +53089 +53123 +53139 +53459 +53465 +53715 +53741 +53764 +53784 +53800 +53904 +54025 +54097 +54153 +54195 +54333 +54532 +54656 +54732 +54806 +54928 +55174 +55182 +55227 +55241 +55252 +55351 +55403 +55450 +55460 +55467 +55548 +55652 +55693 +55717 +55767 +56368 +56480 +56775 +56863 +56871 +56929 +57202 +57265 +57452 +57513 +57522 +57635 +57643 +57806 +57880 +57904 +58154 +58324 +58349 +58354 +58362 +58369 +58538 +58625 +58636 +58697 +59048 +59094 +59278 +59323 +59390 +59561 +59609 +59617 +59716 +59799 +59943 +59999 +60035 +60036 +60300 +60317 +60358 +60570 +60687 +60695 +60778 +60785 +60911 +61166 +61209 +61321 +61412 +61560 +61722 +62035 +62096 +62123 +62239 +62245 +62289 +62339 +62346 +62357 +62526 +62683 +62698 +62767 +62787 +62883 +62913 +63100 +63322 +63346 +63359 +63447 +63720 +64036 +64099 +64364 +64432 +64489 +64541 +64681 +64750 +64882 +64959 +65144 +65187 +65491 +65835 +65864 +66138 +66200 +66244 +66312 +66338 +66346 +66405 +66617 +66994 +67052 +67078 +67081 +67083 +67407 +67427 +67460 +67482 +67576 +67810 +68088 +68178 +68222 +68256 +68529 +68537 +68576 +68685 +68875 +68901 +69127 +69191 +69281 +69498 +69506 +69516 +69708 +70026 +70080 +70318 +70502 +70523 +70609 +70790 +70861 +70871 +70900 +70911 +71130 +71136 +71213 +71237 +71351 +71423 +71475 +71744 +71761 +71794 +71824 +71845 +71883 +72434 +72545 +72559 +72779 +72922 +73068 +73097 +73153 +73242 +73285 +73396 +73967 +74009 +74130 +74267 +74538 +74578 +74622 +74760 +74792 +74796 +74807 +74905 +74911 +74913 +74939 +74958 +74981 +75055 +75383 +75414 +75457 +75580 +75633 +75950 +76103 +76184 +76268 +76480 +76496 +76722 +76838 +76842 +76909 +76975 +76991 +77096 +77194 +77331 +77528 +77631 +77893 +78058 +78068 +78176 +78179 +78242 +78376 +78536 +78688 +78815 +78904 +78978 +79002 +79171 +79229 +79314 +79436 +79524 +79556 +79641 +79702 +79888 +81032 +81038 +81179 +81184 +81189 +81205 +81333 +81350 +81412 +81418 +81457 +81513 +81606 +81613 +81622 +81709 +81821 +81918 +81997 +82009 +82181 +82185 +82212 +82445 +82658 +82696 +82810 +82844 +82910 +82973 +83047 +83127 +83199 +83202 +83338 +83382 +83416 +83577 +83737 +83812 +83833 +83836 +83857 +83884 +83907 +83979 +84066 +84078 +84195 +84489 +84505 +84575 +84838 +84851 +84965 +85087 +85385 +85529 +85650 +85784 +85817 +86046 +86101 +86113 +86158 +86206 +86405 +86466 +86478 +86566 +86677 +86815 +86834 +86935 +86978 +87002 +87003 +87005 +87042 +87092 +87220 +87270 +87399 +87575 +87763 +87885 +87928 +88039 +88068 +88250 +88309 +88323 +88378 +88388 +88391 +88419 +88911 +88976 +88996 +89026 +89036 +89438 +89721 +89726 +89734 +89790 +89905 +89980 +90001 +90021 +90225 +90339 +90733 +90787 +90816 +90824 +90935 +90957 +91094 +91140 +91244 +91360 +91554 +91660 +91905 +91910 +91954 +91974 +92120 +92128 +92198 +92259 +92288 +92448 +92568 +92634 +92899 +92972 +93010 +93015 +93147 +93191 +93388 +93615 +93640 +93659 +93988 +94100 +94182 +94251 +94462 +94707 +94917 +95030 +95072 +95142 +95233 +95339 +95357 +95409 +95624 +95635 +95806 +95878 +95926 +96027 +96072 +96262 +96280 +96364 +96372 +96431 +96557 +96591 +96622 +96651 +96801 +96846 +96913 +96931 +97274 +97386 +97489 +97525 +97529 +97801 +98137 +98186 +98252 +98273 +98338 +98390 +98540 +98573 +98717 +98830 +98841 +98864 +98953 +99067 +99200 +99204 +99364 +99383 +99568 +99706 +99737 +99960 +100031 +100035 +100129 +100144 +100158 +100170 +100218 +100265 +100358 +100377 +100389 +100423 +100545 +100741 +100833 +100839 +100974 +101176 +101361 +101477 +101481 +101496 +101576 +101689 +101708 +101750 +101842 +102152 +102187 +102389 +102408 +102476 +102550 +102581 +102714 +102748 +102846 +102964 +103241 +103292 +103332 +103341 +103422 +103462 +103583 +103688 +103828 +103864 +103978 +104176 +104226 +104279 +104420 +104510 +104514 +104531 +104579 +104754 +104778 +104813 +104978 +105196 +105553 +105698 +105880 +106003 +106022 +106239 +106285 +106399 +106412 +106456 +106498 +106562 +106609 +106747 +106825 +107221 +107441 +107615 +107971 +108063 +108111 +108168 +108176 +108198 +108358 +108379 +108535 +108651 +108682 +108792 +108909 +109131 +109168 +109197 +109281 +109404 +109527 +109582 +109641 +109743 +109760 +110096 +110136 +110255 +110311 +110375 +110404 +110635 +110746 +110772 +110891 +110918 +111033 +111089 +111093 +111410 +111411 +111538 +111727 +111729 +111744 +111943 +112007 +112224 +112318 +112442 +112552 +112606 +112694 +112724 +112728 +112740 +112962 +112969 +112975 +113082 +113646 +113656 +113689 +113705 +113748 +113806 +113878 +113896 +113951 +113991 +114131 +114197 +114261 +114550 +114627 +114687 +114721 +114791 +114991 +115046 +115539 +115589 +115694 +116025 +116264 +116561 +116581 +116636 +116766 +116772 +116886 +116901 +116955 +117065 +117080 +117110 +117128 +117154 +117168 +117561 +117569 +117579 +117668 +117737 +117933 +118064 +118170 +118251 +118261 +118313 +118516 +118620 +118778 +118920 +119107 +119310 +119433 +119611 +119625 +119729 +119905 +119998 +120094 +120108 +120197 +120510 +120524 +120668 +120818 +120824 +120826 +120949 +121001 +121078 +121548 +121596 +121995 +122243 +122246 +122436 +122511 +122557 +122588 +122612 +122622 +122662 +122744 +123006 +123031 +123117 +123119 +123160 +123168 +123181 +123251 +123579 +123596 +123640 +123847 +124031 +124054 +124097 +124168 +124519 +124586 +124609 +124665 +124668 +124784 +124947 +125005 +125119 +125327 +125490 +125511 +125526 +125610 +125885 +126109 +126135 +126264 +126272 +126295 +126476 +126538 +126544 +126821 +126870 +126967 +126977 +127028 +127082 +127112 +127189 +127311 +127405 +127436 +127554 +127708 +127896 +128068 +128078 +128123 +128334 +128380 +128675 +128716 +128898 +129226 +129250 +129254 +129328 +129386 +129421 +129452 +129529 +129606 +129653 +129898 +129962 +130071 +130135 +130167 +130210 +130313 +130320 +130477 +130598 +130710 +131155 +131263 +131512 +131639 +132147 +132272 +132349 +132651 +132666 +132690 +132720 +132790 +132926 +133065 +133187 +133219 +133241 +133267 +133333 +133389 +133746 +133793 +133816 +133858 +134046 +134104 +134225 +134361 +134371 +134433 +134438 +134516 +134613 +134666 +134891 +135069 +135192 +135217 +135265 +135309 +135662 +135722 +135974 +136096 +136280 +136480 +136520 +136693 +136710 +136789 +136963 +137013 +137113 +137171 +137264 +137531 +137755 +137803 +138041 +138237 +138337 +138551 +138573 +138626 +138676 +138723 +138818 +138902 +139107 +139143 +139259 +139290 +139347 +139466 +139556 +139621 +139634 +139724 +139909 +139935 +139936 +140097 +140156 +140163 +140241 +140341 +140370 +140385 +140398 +140543 +140621 +140701 +140744 +140905 +140975 +141040 +141073 +141099 +141223 +141417 +141441 +141448 +141576 +141610 +142086 +142553 +142717 +142813 +142943 +143097 +143525 +143538 +143582 +143644 +143740 +143905 +144049 +144078 +144111 +144364 +144399 +144402 +144457 +144513 +144520 +144559 +144581 +144663 +144680 +144715 +144772 +144842 +144863 +144921 +144945 +144981 +145023 +145034 +145076 +145193 +145252 +145374 +145388 +145643 +145704 +145710 +145724 +146066 +146188 +146307 +146454 +146455 +146463 +146559 +146612 +146630 +146694 +146704 +146806 +146977 +146999 +147167 +147214 +147460 +147470 +147482 +147641 +147754 +147907 +148162 +148360 +148401 +148462 +148566 +148595 +148683 +148798 +148811 +148869 +149073 +149098 +149132 +149181 +149559 +149701 +149895 +150151 +150224 +150225 +150245 +150298 +150310 +150314 +150382 +150384 +150395 +150463 +150493 +150572 +150693 +150726 +150835 +151019 +151065 +151452 +151596 +152013 +152102 +152249 +152277 +152400 +152466 +152535 +152740 +152776 +152846 +153094 +153108 +153267 +153330 +153476 +153483 +153510 +153566 +153844 +154094 +154116 +154152 +154276 +154426 +154436 +154538 +154620 +154624 +154633 +154893 +155014 +155052 +155069 +155102 +155195 +155222 +155257 +155315 +155347 +155350 +155375 +155413 +155430 +155457 +155590 +155600 +155620 +155722 +155748 +155890 +155892 +156062 +156104 +156198 +156477 +156502 +156645 +156684 +156804 +156854 +156867 +156911 +157004 +157023 +157028 +157265 +157563 +157585 +157611 +157615 +157667 +157909 +158042 +158059 +158203 +158245 +158336 +158346 +158562 +158634 +158706 +158770 +158778 +158786 +158878 +158933 +159010 +159159 +159162 +159201 +159456 +159495 +159614 +160246 +160282 +160318 +160515 +160522 +160551 +160641 +160805 +160972 +160992 +161159 +161310 +161425 +161446 +161533 +161654 +161773 +162020 +162021 +162045 +162726 +162838 +162935 +162999 +163009 +163114 +163223 +163233 +163319 +163329 +163390 +163590 +163597 +163606 +163633 +163702 +163740 +164088 +164107 +164198 +164206 +164240 +164393 +164437 +164537 +164650 +164755 +165067 +165074 +165375 +165467 +165669 +165846 +166093 +166246 +166280 +166353 +166484 +166524 +166606 +166629 +166748 +166759 +166878 +166929 +167101 +167158 +167267 +167339 +167516 +167518 +167619 +167711 +167851 +167903 +167932 +167955 +168098 +168320 +168329 +168568 +168639 +168700 +168704 +168730 +168741 +168784 +168841 +168943 +169223 +169384 +169390 +169415 +169468 +169563 +169611 +169619 +169808 +169951 +169995 +169997 +170180 +170231 +170325 +170480 +170553 +170673 +170713 +170732 +170797 +170866 +170957 +171321 +171394 +171475 +171705 +171779 +171859 +171957 +172351 +172456 +172545 +172767 +172774 +173021 +173396 +173447 +173459 +173691 +173699 +173838 +173957 +174059 +174222 +174669 +174684 +174700 +174883 +174918 +174939 +175154 +175167 +175175 +175269 +175358 +175587 +175601 +175762 +175935 +176219 +176482 +176653 +176717 +176746 +176771 +176800 +176805 +177102 +177110 +177134 +177343 +177344 +177430 +177472 +177698 +177964 +178034 +178149 +178268 +178296 +178356 +178600 +178700 +178997 +179006 +179442 +179669 +179708 +179749 +179768 +179815 +179991 +180142 +180166 +180167 +180388 +180463 +180657 +180857 +180996 +180999 +181138 +181178 +181245 +181349 +181369 +181489 +181740 +181757 +181844 +181856 +181974 +181982 +182127 +182185 +182200 +182231 +182242 +182251 +182556 +182630 +182668 +182676 +182793 +182835 +182948 +183204 +183322 +183403 +183491 +183512 +183551 +183561 +183610 +183838 +183963 +184008 +184217 +184228 +184327 +184556 +184595 +184627 +184947 +184952 +185003 +185165 +185347 +185411 +185419 +185661 +185665 +185707 +185786 +186371 +186480 +186557 +186732 +186816 +186945 +187047 +187298 +187311 +187449 +187651 +187721 +187948 +188029 +188050 +188103 +188592 +188711 +188766 +188767 +188770 +189024 +189288 +189345 +189391 +189476 +189778 +189829 +189896 +189918 +189942 +190117 +190181 +190311 +190393 +190501 +190580 +190592 +190640 +190733 +190825 +190932 +191223 +191324 +191330 +191528 +191536 +191698 +191854 +191914 +191945 +192095 +192207 +192269 +192297 +192423 +192547 +192570 +192662 +192680 +192762 +192791 +192932 +193144 +193244 +193291 +193308 +193375 +193492 +193502 +193683 +193724 +193742 +193800 +193847 +193912 +193965 +193982 +194002 +194023 +194144 +194231 +194274 +194441 +194501 +194509 +194542 +194581 +194595 +194606 +194624 +194809 +194865 +194934 +194975 +195200 +195211 +195396 +195439 +195527 +195588 +195618 +195670 +195739 +195815 +195819 +196166 +196339 +196362 +196389 +196427 +196452 +196510 +196513 +196572 +196784 +197040 +197058 +197068 +197106 +197173 +197253 +197344 +197470 +197594 +197651 +197653 +197742 +197755 +197825 +197869 +197919 +197946 +198047 +198258 +198680 +198783 +198792 +198979 +199124 +199419 +199452 +199511 +199693 +199881 +200018 +200045 +200112 +200270 +200384 +200440 +200501 +200530 +200662 +200781 +200822 +200849 +200868 +200916 +200929 +200985 +201139 +201165 +201306 +201370 +201411 +201467 +201574 +201770 +201820 +202070 +202090 +202125 +202325 +202371 +202373 +202417 +202434 +202631 +202670 +202692 +202746 +202930 +203071 +203096 +203172 +203258 +203270 +203538 +203672 +203792 +203826 +203915 +203941 +203962 +203963 +204161 +204223 +204342 +204444 +204531 +204560 +204658 +204716 +204942 +205232 +205314 +205330 +205342 +205436 +205491 +205508 +205536 +205584 +205634 +205733 +205786 +205877 +205981 +205989 +205998 +206006 +206101 +206102 +206125 +206135 +206183 +206201 +206219 +206458 +206460 +206508 +206661 +206755 +207043 +207051 +207275 +207690 +207854 +207859 +207928 +208162 +208405 +208737 +208776 +208895 +208966 +209037 +209250 +209289 +209333 +209351 +209378 +209403 +209502 +209544 +209696 +209723 +209759 +209834 +209899 +210390 +210434 +210533 +210700 +210815 +210858 +210883 +211100 +211149 +211291 +211321 +211336 +211394 +211509 +211608 +211762 +211772 +211867 +212065 +212230 +212527 +212573 +212685 +212865 +213215 +213236 +213309 +213388 +213514 +213833 +213931 +213935 +214011 +214050 +214617 +214741 +214911 +214988 +215012 +215082 +215093 +215099 +215212 +215396 +215511 +215556 +215621 +215698 +215782 +216009 +216083 +216387 +216425 +216516 +216619 +216733 +216759 +216841 +217006 +217009 +217062 +217146 +217584 +217614 +217641 +217893 +217920 +217974 +218012 +218112 +218222 +218241 +218318 +218360 +218589 +218621 +218641 +218671 +218687 +218887 +219007 +219096 +219234 +219243 +219477 +219489 +219510 +219696 +219807 +220276 +220522 +220539 +220721 +220755 +220797 +220813 +220939 +221150 +221226 +221319 +221341 +221527 +221688 +221727 +221824 +221891 +222147 +222272 +222501 +222590 +222758 +222765 +222950 +222966 +223122 +223251 +223268 +223326 +223408 +223670 +223971 +224100 +224114 +224128 +224461 +224522 +224578 +224894 +224948 +224993 +225019 +225079 +225088 +225246 +225434 +225461 +225582 +225587 +225696 +226014 +226127 +226343 +226495 +226562 +226633 +226672 +226776 +226867 +227350 +227501 +227516 +227541 +227551 +227652 +227681 +227881 +227915 +227986 +227993 +228018 +228073 +228096 +228515 +228555 +228579 +228595 +228720 +228785 +229201 +229283 +229298 +229502 +229518 +229826 +229869 +229938 +230188 +230234 +230474 +230533 +230661 +231086 +231142 +231355 +231394 +231719 +231750 +231752 +231953 +232018 +232032 +232145 +232148 +232180 +232247 +232393 +232568 +232598 +232639 +232658 +232937 +232966 +233099 +233127 +233635 +233801 +233845 +233869 +233980 +234048 +234055 +234072 +234199 +234255 +234303 +234317 +234342 +234373 +234415 +234500 +234596 +234684 +234685 +234836 +234888 +235094 +235230 +235269 +235339 +235394 +235397 +235411 +235421 +235502 +235590 +235635 +235835 +236019 +236174 +236527 +236670 +236715 +236806 +236864 +236928 +237005 +237056 +237109 +237410 +237536 +237607 +237626 +237706 +237769 +237868 +238077 +238119 +238174 +238195 +238199 +238229 +238239 +238341 +238381 +238448 +238457 +238462 +238520 +238536 +238607 +238640 +238684 +239104 +239150 +239195 +239267 +239286 +239337 +239667 +239771 +239784 +239926 +240035 +240056 +240259 +240278 +240363 +240579 +240584 +240616 +240630 +240784 +240869 +240925 +240953 +241113 +241185 +241264 +241930 +241958 +242055 +242074 +242087 +242168 +242205 +242230 +242556 +242720 +242732 +242935 +242986 +243081 +243200 +243945 +244029 +244038 +244267 +244284 +244308 +244325 +244505 +244789 +244832 +244869 +245123 +245291 +245308 +245452 +245579 +245608 +245866 +245906 +246116 +246180 +246277 +246337 +246340 +246488 +246507 +246540 +246675 +246763 +246789 +247091 +247123 +247132 +247153 +247265 +247402 +247407 +247783 +247924 +248502 +248554 +248572 +248598 +248737 +248833 +248919 +248992 +249009 +249157 +249165 +249298 +249358 +249367 +249610 +249611 +249858 +249867 +250009 +250081 +250147 +250284 +250309 +250497 +250618 +250712 +250798 +250806 +250823 +251055 +251103 +251154 +251197 +251248 +251277 +251312 +251362 +251409 +251526 +251537 +251683 +251762 +251794 +251800 +251813 +251916 +251925 +252055 +252150 +252256 +252353 +252361 +252498 +252578 +252605 +252810 +252881 +253034 +253103 +253144 +253146 +253218 +253244 +253292 +253349 +253386 +253628 +253650 +253685 +253712 +253753 +253780 +253798 +253855 +254012 +254121 +254433 +254465 +254654 +254685 +254766 +254834 +254851 +255082 +255086 +255154 +255355 +255397 +255496 +255566 +255604 +255667 +255768 +255809 +255824 +255979 +256077 +256286 +256572 +256577 +256591 +256708 +257230 +257264 +257569 +257570 +257595 +257596 +257612 +257947 +258091 +258152 +258192 +258455 +258497 +258733 +258745 +258800 +258997 +259009 +259073 +259202 +259309 +259325 +259399 +259416 +259420 +259451 +259556 +259716 +259835 +260176 +260186 +260233 +260330 +260392 +260439 +260467 +260480 +260519 +260526 +260629 +260700 +260725 +260726 +260785 +260801 +260840 +260879 +261086 +261089 +261106 +261210 +261228 +261391 +261445 +261465 +261467 +261523 +261553 +261576 +261737 +261761 +261810 +261925 +262028 +262109 +262306 +262395 +262400 +262488 +262500 +262532 +262598 +262666 +262847 +262905 +263029 +263042 +263348 +263459 +263821 +263851 +263921 +263947 +263990 +264030 +264187 +264204 +264332 +264337 +264348 +264566 +264634 +264747 +264765 +264858 +264915 +264993 +265029 +265059 +265235 +265385 +265621 +265767 +265874 +266030 +266085 +266279 +266430 +266446 +266679 +266933 +267098 +267130 +267253 +267424 +267448 +267613 +267619 +267672 +267707 +267895 +268048 +268055 +268187 +268227 +268265 +268266 +268318 +268511 +268923 +269031 +269360 +269373 +269551 +269662 +269783 +269962 +269999 +270024 +270135 +270238 +270240 +270427 +270469 +270486 +270602 +270808 +271308 +271467 +271519 +271824 +271838 +271888 +272073 +272155 +272222 +272325 +272454 +272457 +272654 +272718 +272816 +273077 +273151 +273278 +273306 +273360 +273450 +273688 +273733 +273741 +273811 +273901 +273983 +274003 +274016 +274206 +274241 +274247 +274337 +274431 +274491 +274558 +274731 +274929 +274938 +275029 +275110 +275173 +275483 +275670 +275896 +275908 +275957 +275981 +276135 +276192 +276225 +276406 +276427 +276472 +276736 +277012 +277192 +277235 +277259 +277355 +277437 +277484 +277599 +277606 +277751 +278045 +278107 +278121 +278124 +278165 +278329 +278486 +278534 +278831 +278940 +279125 +279141 +279166 +279273 +279346 +279441 +279467 +279506 +279846 +279883 +280042 +280119 +280123 +280255 +280711 +280953 +281215 +281216 +281233 +281362 +281395 +281474 +281672 +281711 +281858 +282034 +282045 +282057 +282090 +282161 +282275 +282348 +282420 +282491 +282537 +282620 +282640 +282709 +282885 +282931 +283216 +283263 +283275 +283338 +283388 +283602 +283670 +283828 +283879 +284154 +284339 +284429 +284581 +284737 +284964 +285001 +285243 +285344 +285492 +285717 +285778 +285810 +285915 +285975 +286067 +286103 +286430 +286456 +286504 +286722 +286786 +286798 +286801 +286846 +286864 +286866 +286872 +286908 +286946 +287098 +287167 +287386 +287474 +287475 +287551 +287861 +287883 +288119 +288287 +288417 +288654 +288709 +288812 +288936 +289027 +289075 +289256 +289263 +289489 +289502 +289646 +289879 +289893 +290208 +290240 +290309 +290328 +290372 +290382 +290576 +290582 +290646 +290817 +290902 +290914 +290931 +290991 +291259 +291574 +291579 +291631 +291686 +291847 +291852 +291990 +292091 +292103 +292347 +292348 +292351 +292613 +292708 +292768 +292790 +293251 +293313 +293353 +293419 +293492 +293506 +293604 +293641 +293828 +293877 +293931 +293939 +293992 +294086 +294253 +294342 +294461 +294713 +294773 +294847 +295198 +295375 +295801 +295802 +295922 +295930 +295991 +296024 +296155 +296173 +296215 +296356 +296387 +296415 +296448 +296481 +296487 +296528 +296600 +296779 +296960 +296964 +297120 +297169 +297283 +297293 +297354 +297411 +297495 +297801 +297887 +297950 +297978 +298012 +298057 +298100 +298271 +298315 +298370 +298533 +298640 +298706 +298755 +298781 +299119 +299168 +299232 +299328 +299422 +299435 +299501 +299648 +300103 +300151 +300154 +300213 +300428 +300442 +300671 +300898 +301107 +301155 +301188 +301242 +301388 +301442 +301537 +301595 +301612 +301641 +301951 +302128 +302154 +302284 +302443 +302483 +302767 +302816 +303032 +303046 +303168 +303247 +303295 +303427 +303604 +303939 +303981 +304004 +304138 +304185 +304311 +304418 +304431 +304458 +304504 +304540 +304785 +304824 +304902 +304936 +304962 +305093 +305131 +305155 +305207 +305464 +305501 +305588 +305646 +305866 +305904 +305981 +306114 +306119 +306186 +306230 +306345 +306365 +306377 +306386 +306388 +306529 +306633 +306654 +306723 +306800 +306846 +306915 +306983 +307043 +307258 +307282 +307330 +307470 +307551 +307605 +307639 +307721 +307920 +308042 +308248 +308274 +308379 +308410 +308542 +308628 +308740 +308873 +308916 +309206 +309415 +309532 +309692 +309798 +310414 +310457 +310529 +310653 +310697 +311040 +311052 +311187 +311280 +311371 +311381 +311482 +311500 +311774 +311843 +311866 +311928 +312100 +312224 +312292 +312464 +312541 +312586 +312808 +312854 +312890 +313250 +313292 +313294 +313325 +313329 +313334 +313466 +313688 +313767 +313935 +313950 +314015 +314241 +314344 +314481 +314560 +314775 +315108 +315274 +315275 +315454 +315469 +315577 +315743 +315925 +316121 +316130 +316153 +316264 +316266 +316272 +316875 +316906 +316916 +316926 +316988 +317128 +317160 +317369 +317475 +317729 +317834 +318100 +318115 +318121 +318133 +318172 +318469 +318487 +318516 +318609 +318757 +318761 +318782 +319361 +319457 +319460 +319462 +319515 +319590 +319724 +319990 +320060 +320108 +320134 +320174 +320232 +320262 +320269 +320319 +320401 +320430 +320543 +320585 +320699 +320870 +320899 +320917 +320923 +320971 +321009 +321012 +321096 +321155 +321359 +321407 +321500 +321530 +321573 +321698 +321702 +321749 +321891 +321929 +322026 +322880 +322881 +323122 +323138 +323282 +323286 +323291 +323432 +323514 +323771 +323817 +323906 +323928 +324006 +324106 +324115 +324150 +324313 +324342 +324408 +324456 +324694 +324695 +324906 +324954 +324967 +325061 +325081 +325084 +325092 +325267 +325380 +325512 +325613 +325657 +325660 +325751 +325795 +325813 +325967 +325998 +326005 +326120 +326403 +326430 +326553 +326756 +326925 +326932 +327065 +327102 +327123 +327197 +327237 +327325 +327379 +327474 +327566 +327644 +327681 +327693 +327936 +328066 +328230 +328247 +328333 +328383 +328486 +328535 +328636 +328646 +328673 +328922 +328933 +328948 +329135 +329257 +329356 +329524 +329602 +329656 +329671 +329747 +329814 +329999 +330077 +330440 +330664 +330833 +330839 +330919 +330937 +330988 +331113 +331180 +331294 +331444 +331487 +331500 +331596 +331684 +331813 +331837 +331841 +332006 +332079 +332093 +332127 +332169 +332194 +332231 +332508 +332510 +332568 +332570 +332708 +332791 +332912 +332971 +333021 +333117 +333130 +333134 +333457 +333567 +333622 +333650 +333682 +333787 +333892 +333965 +334074 +334137 +334439 +334687 +334794 +335028 +335116 +335206 +335209 +335246 +335474 +335483 +335788 +335789 +335903 +336039 +336097 +336300 +336326 +336344 +336348 +336557 +336835 +336866 +336952 +337020 +337026 +337046 +337173 +337174 +337264 +337283 +337309 +337325 +337354 +337414 +337508 +337572 +337589 +337599 +337603 +337615 +337827 +337880 +337994 +338039 +338176 +338321 +338551 +338631 +338666 +338712 +338843 +338863 +338949 +339013 +339143 +339345 +339623 +339836 +339988 +340022 +340065 +340072 +340216 +340267 +340269 +340281 +340339 +340409 +340414 +340464 +340471 +340667 +340692 +340703 +340738 +340984 +340998 +341023 +341160 +341304 +341536 +341733 +341821 +341925 +342187 +342196 +342367 +342536 +342718 +342733 +342855 +342871 +342905 +342923 +343108 +343273 +343305 +343509 +343527 +343910 +344105 +344196 +344727 +344734 +344748 +344755 +344757 +344813 +344886 +345001 +345139 +345180 +345238 +345259 +345416 +345518 +345563 +345864 +346074 +346118 +346125 +346386 +346478 +346549 +346601 +347017 +347073 +347324 +347600 +347885 +347983 +348025 +348177 +348325 +348339 +348418 +348468 +348483 +348685 +348777 +348830 +348846 +348983 +348995 +349031 +349320 +349332 +349334 +349342 +349459 +349742 +349812 +349857 +349910 +349934 +349938 +349958 +350110 +350398 +350420 +350620 +350801 +350897 +351027 +351039 +351103 +351335 +351449 +351510 +351523 +351713 +351817 +351902 +351930 +351961 +352197 +352291 +352352 +352391 +352455 +352474 +352563 +352752 +352814 +352876 +352891 +352894 +353033 +353043 +353155 +353316 +353378 +353464 +353549 +353579 +353780 +353967 +354060 +354132 +354180 +354190 +354315 +354447 +354504 +354765 +354930 +354947 +354956 +354981 +355141 +355287 +355385 +355510 +355551 +355631 +355700 +355761 +355875 +356129 +356414 +356700 +356958 +356989 +357027 +357043 +357048 +357063 +357098 +357372 +357413 +357423 +357489 +357568 +357631 +357758 +357780 +357785 +357850 +357851 +358104 +358255 +358363 +358364 +358515 +358586 +358709 +358713 +358906 +358993 +359036 +359043 +359086 +359182 +359212 +359299 +359306 +359424 +359444 +359467 +359522 +359838 +359862 +359952 +360283 +360397 +360479 +360495 +360757 +360909 +361016 +361027 +361164 +361230 +361236 +361513 +361533 +361717 +361742 +361759 +361821 +361835 +361839 +362114 +362144 +362160 +362419 +362426 +362475 +362483 +362496 +362618 +362998 +363119 +363176 +363315 +363406 +363602 +363821 +363898 +364027 +364044 +364078 +364133 +364355 +364577 +364579 +364604 +364669 +364722 +364870 +364896 +365051 +365286 +365291 +365329 +365337 +365396 +365427 +365618 +366087 +366191 +366361 +366516 +366609 +366892 +367050 +367064 +367276 +367404 +367521 +367610 +367757 +367821 +367945 +368284 +368336 +368370 +368407 +368517 +368685 +368947 +368989 +369145 +369244 +369283 +369348 +369412 +369423 +369790 +369863 +369936 +370022 +370129 +370179 +370279 +370318 +370332 +370471 +370549 +370556 +370709 +370902 +370919 +370931 +371036 +371058 +371079 +371286 +371291 +371367 +371424 +371529 +371658 +371660 +371735 +371773 +371853 +371918 +372106 +372122 +372142 +372226 +372332 +372428 +372519 +372679 +372756 +372897 +372917 +372964 +373042 +373376 +373830 +373868 +373938 +374031 +374127 +374438 +374530 +374532 +374534 +374568 +374902 +374997 +375285 +375336 +375369 +375383 +375438 +375445 +375481 +375579 +375654 +375799 +375967 +376016 +376699 +376707 +376792 +376899 +376980 +377026 +377095 +377105 +377127 +377244 +377299 +377435 +377486 +377684 +377773 +377796 +377797 +378097 +378098 +378192 +378202 +378252 +378353 +378430 +378489 +378563 +378709 +378712 +378832 +378835 +378892 +378902 +378946 +379022 +379252 +379297 +379357 +379398 +379421 +379449 +379546 +379738 +379834 +379835 +379980 +380026 +380257 +380302 +380569 +380573 +380771 +380875 +380940 +380966 +381027 +381308 +381394 +381416 +381528 +381538 +381576 +381614 +381833 +381898 +381971 +381979 +382032 +382036 +382072 +382503 +382559 +382662 +382867 +382902 +383091 +383209 +383222 +383602 +383615 +383806 +383828 +383839 +383921 +383994 +384031 +384036 +384067 +384494 +384527 +384696 +384802 +385014 +385287 +385492 +385506 +385683 +385880 +385938 +385977 +386177 +386244 +386276 +386356 +386425 +386513 +386636 +386803 +386890 +386983 +387077 +387191 +387389 +387426 +387630 +387659 +387702 +387770 +387804 +387819 +387834 +387848 +387950 +388145 +388314 +388476 +388580 +388683 +388805 +388862 +388946 +388978 +389065 +389165 +389233 +389307 +389327 +389359 +389429 +389485 +389800 +389856 +389858 +389975 +390182 +390271 +390315 +390354 +390366 +390369 +390446 +390625 +390799 +390842 +390849 +390861 +390915 +391009 +391080 +391082 +391131 +391133 +391174 +391378 +391402 +391407 +391413 +391556 +391698 +391735 +391741 +392027 +392170 +392240 +392463 +392508 +392533 +392547 +392655 +392752 +392896 +392916 +392983 +393084 +393324 +393482 +393604 +393768 +393780 +394332 +394347 +394478 +394493 +394625 +394681 +394795 +394888 +394962 +395157 +395268 +395281 +395425 +395434 +395521 +395535 +395575 +395774 +395909 +396001 +396115 +396128 +396742 +397236 +397278 +397335 +397344 +397379 +397681 +397816 +397855 +398008 +398071 +398254 +398258 +398261 +398539 +398562 +398585 +398672 +398693 +398745 +398792 +399130 +399131 +399271 +399472 +399491 +399604 +399609 +399620 +399646 +399707 +399775 +399913 +399954 +399959 +399997 +400203 +400316 +400502 +400708 +400857 +400886 +400888 +400909 +400955 +400980 +401572 +402009 +402144 +402316 +402332 +402335 +402501 +402548 +402633 +402652 +402739 +402746 +403069 +403076 +403161 +403219 +403236 +403392 +403425 +403478 +403491 +403551 +403583 +403588 +403596 +403674 +403705 +403804 +403805 +404037 +404049 +404055 +404408 +404509 +404945 +404961 +404962 +405041 +405303 +405317 +405349 +405372 +405430 +405474 +405586 +405672 +405752 +405763 +405825 +405948 +406472 +406882 +406892 +407018 +407264 +407266 +407321 +407980 +408147 +408295 +408402 +408456 +408613 +408631 +408678 +408692 +408783 +408997 +409003 +409203 +409222 +409235 +409337 +409422 +409568 +409617 +409832 +409987 +410184 +410252 +410264 +410317 +410494 +410863 +410928 +411003 +411144 +411166 +411204 +411450 +411573 +411629 +411749 +411752 +411777 +411916 +412050 +412271 +412297 +412631 +412634 +412736 +412821 +413076 +413093 +413206 +413210 +413418 +413634 +413718 +414040 +414059 +414060 +414075 +414282 +414462 +414489 +414578 +414766 +414868 +414976 +415022 +415094 +415095 +415258 +415384 +415465 +415688 +415739 +415976 +416014 +416025 +416034 +416050 +416055 +416389 +416488 +416543 +416610 +416615 +416644 +416704 +416800 +416840 +416856 +417011 +417119 +417135 +417296 +417455 +417513 +417606 +417743 +417800 +417877 +417911 +417946 +417973 +418061 +418127 +418320 +418409 +418446 +418566 +418713 +418933 +418961 +418994 +419003 +419073 +419162 +419171 +419430 +419461 +419564 +419892 +419974 +420174 +420287 +420363 +420373 +420511 +420549 +420646 +420665 +420666 +420670 +420683 +420725 +420758 +420937 +421116 +421134 +421253 +421288 +421342 +421362 +421502 +421640 +421706 +421741 +421847 +421955 +421977 +422091 +422139 +422166 +422389 +422547 +422553 +422778 +422785 +422843 +422858 +422867 +422945 +422993 +423332 +423618 +423645 +423649 +423712 +423897 +423953 +424009 +424035 +424277 +424300 +424304 +424443 +424447 +425138 +425188 +425306 +425310 +425409 +425439 +425641 +425651 +425776 +425990 +425992 +426158 +426226 +426252 +426280 +426344 +426373 +426384 +426390 +426408 +426475 +426525 +426533 +426618 +426683 +426712 +426807 +427073 +427120 +427177 +427210 +427221 +427367 +427593 +427709 +427772 +427793 +427986 +428031 +428210 +428290 +428338 +428498 +428751 +428814 +428817 +428951 +428996 +429182 +429211 +429544 +429622 +429701 +430026 +430053 +430095 +430155 +430499 +430556 +430643 +430693 +430849 +430868 +431064 +431080 +431139 +431383 +431498 +431669 +431689 +431694 +431960 +432001 +432040 +432069 +432092 +432106 +432181 +432192 +432238 +432299 +432340 +432530 +432662 +432690 +432866 +432893 +432895 +432931 +433032 +433125 +433284 +433339 +433437 +433471 +433617 +433710 +433711 +433843 +433855 +433944 +434007 +434057 +434107 +434108 +434168 +434191 +434277 +434298 +434310 +434360 +434362 +434507 +434528 +434534 +434587 +434604 +434616 +434727 +434774 +434837 +434973 +435262 +435330 +435351 +435639 +435654 +435666 +435672 +435774 +435946 +435956 +436049 +436052 +436119 +436199 +436287 +436442 +436769 +436917 +437155 +437420 +437535 +437537 +437596 +437772 +437852 +438166 +438187 +438262 +438321 +438346 +438391 +438582 +438658 +438796 +438968 +439059 +439132 +439211 +439249 +439277 +439283 +439362 +439465 +439483 +439490 +439696 +439772 +439807 +439812 +439978 +440035 +440175 +440191 +440280 +440304 +440341 +440410 +440413 +440454 +440497 +440573 +440601 +440615 +440655 +440677 +440763 +440859 +441018 +441063 +441127 +441219 +441221 +441281 +441398 +441412 +441810 +441953 +441988 +442118 +442243 +442552 +442640 +442652 +442696 +442699 +442805 +443038 +443135 +443339 +443359 +443545 +443802 +443817 +443910 +443917 +443978 +444161 +444202 +444223 +444254 +444289 +444332 +444971 +445050 +445113 +445123 +445135 +445170 +445248 +445281 +445294 +445412 +445466 +445808 +445933 +446063 +446286 +446348 +446415 +446508 +446535 +446579 +446750 +447015 +447366 +447509 +447537 +447833 +447838 +448139 +448150 +448213 +448258 +448292 +448355 +448435 +448448 +448503 +448581 +448627 +448756 +448952 +449070 +449439 +449459 +449610 +449670 +449864 +449891 +449998 +450080 +450249 +450399 +450403 +450440 +450671 +451076 +451220 +451329 +451622 +451784 +451943 +451956 +452040 +452056 +452702 +452763 +452768 +452770 +453022 +453157 +453223 +453237 +453256 +453366 +453442 +453447 +453646 +453801 +453816 +453884 +453910 +453924 +454095 +454111 +454315 +454368 +454443 +454462 +454515 +454564 +454615 +454619 +454743 +454757 +454762 +454838 +454860 +454881 +454898 +455047 +455212 +455434 +455515 +455656 +455709 +455963 +455992 +456057 +456155 +456439 +456544 +456567 +456573 +456615 +456710 +456760 +456809 +456868 +456887 +456895 +456946 +456992 +457147 +457155 +457174 +457372 +457414 +457572 +457875 +458031 +458035 +458127 +458190 +458308 +458415 +458666 +458788 +458860 +458962 +459050 +459229 +459374 +459603 +459640 +459740 +459808 +459957 +460006 +460033 +460050 +460156 +460181 +460279 +460310 +460418 +460464 +460548 +460691 +460886 +460928 +461068 +461098 +461202 +461269 +461320 +461334 +461363 +461674 +462061 +462282 +462325 +462335 +462360 +462361 +462647 +462733 +462759 +462882 +462970 +463025 +463054 +463158 +463162 +463267 +463294 +463318 +463395 +463407 +463697 +463732 +463762 +463833 +463971 +464196 +464210 +464371 +464555 +464841 +464900 +465006 +465077 +465133 +465332 +465489 +465507 +465581 +465687 +465715 +465720 +465747 +465778 +466369 +466417 +466616 +466618 +466622 +466789 +466792 +466845 +466884 +466905 +466982 +467128 +467282 +467441 +467602 +467638 +467817 +467890 +467992 +468141 +468284 +468324 +468340 +468446 +468802 +468978 +469000 +469080 +469152 +469156 +469385 +469441 +469544 +469607 +469626 +470007 +470054 +470088 +470133 +470144 +470248 +470325 +470410 +470606 +470761 +470914 +470926 +470931 +470996 +471031 +471034 +471040 +471189 +471468 +471513 +471659 +471924 +471939 +472009 +472113 +472152 +472369 +472437 +472699 +472803 +472857 +472947 +473076 +473137 +473831 +473958 +473967 +474099 +474187 +474306 +474507 +474627 +474902 +475100 +475381 +475561 +475603 +475626 +475721 +475865 +475878 +476015 +476278 +476409 +476640 +476948 +477004 +477086 +477319 +477437 +477547 +477656 +478096 +478200 +478262 +478315 +478497 +478757 +478859 +478894 +479049 +479106 +479170 +479202 +479304 +479318 +479359 +479388 +479538 +479781 +479835 +479839 +479887 +479941 +480039 +480052 +480108 +480157 +480195 +480332 +480339 +480347 +480368 +480399 +480477 +480614 +480694 +480712 +480840 +480877 +480891 +480918 +480944 +481006 +481157 +481170 +481347 +481720 +482072 +482079 +482209 +482375 +482397 +482426 +482437 +482438 +482479 +482520 +482558 +482598 +482803 +482832 +482848 +482856 +482876 +482882 +482989 +483000 +483001 +483048 +483104 +483106 +483225 +483358 +483485 +483745 +483774 +483909 +483918 +483974 +484088 +484309 +484310 +484332 +484412 +484429 +484493 +484572 +484674 +484675 +484710 +484940 +484979 +485197 +485250 +485402 +485441 +485487 +485503 +485748 +485762 +485929 +485956 +486005 +486039 +486052 +486086 +486220 +486241 +486351 +486412 +486642 +486660 +486693 +486812 +486873 +487148 +487206 +487287 +487305 +487387 +487526 +487648 +487697 +487877 +488059 +488199 +488233 +488243 +488263 +488415 +488574 +488682 +488795 +488800 +488840 +488879 +489028 +489035 +489172 +489334 +489447 +489689 +489819 +489859 +490018 +490049 +490255 +490289 +490466 +490528 +490564 +490663 +490668 +490716 +490736 +491048 +491056 +491184 +491338 +491348 +491398 +491481 +491553 +491598 +491866 +491873 +492230 +493222 +493247 +493563 +493666 +493794 +493856 +493867 +493954 +494037 +494066 +494069 +494129 +494193 +494271 +494381 +494476 +494547 +494674 +494694 +494750 +494876 +494963 +495066 +495094 +495152 +495268 +495348 +495424 +495596 +495625 +495728 +495750 +495765 +495772 +495970 +496152 +496182 +496200 +496203 +496276 +496434 +496486 +496539 +496556 +496566 +496823 +496916 +497006 +497132 +497236 +497252 +497256 +497307 +497354 +497532 +497541 +497582 +497595 +497618 +497624 +497641 +497744 +498052 +498267 +498413 +498494 +498517 +498527 +498674 +498801 +498841 +498902 +498926 +498949 +499101 +499126 +499175 +499192 +499211 +499527 +499593 +499612 +499682 +499711 +499735 +499737 +499812 +499815 +499890 +500000 +500185 +500271 +500332 +500517 +500541 +500590 +500722 +500773 +500796 +500813 +501013 +501016 +501132 +501152 +501153 +501214 +501496 +501637 +501730 +501743 +501835 +501922 +502098 +502206 +502322 +502354 +502419 +502487 +502504 +502665 +502921 +502964 +503076 +503102 +503429 +503608 +503643 +503731 +503809 +503969 +504046 +504056 +504061 +504075 +504295 +504357 +505139 +505378 +505392 +505407 +505577 +505742 +506062 +506274 +506323 +506345 +506549 +506611 +506690 +506780 +507163 +507255 +507391 +507555 +507566 +507607 +507664 +507853 +508055 +508177 +508211 +508385 +508420 +508463 +508607 +508667 +509006 +509053 +509086 +509266 +509356 +509430 +509526 +509550 +509555 +509574 +509697 +509723 +509784 +509811 +509854 +510159 +510226 +510254 +510479 +510643 +510671 +510768 +511284 +511397 +511554 +511716 +511884 +511889 +512021 +512022 +512059 +512181 +512398 +512413 +512455 +512641 +512659 +512720 +512721 +512850 +513031 +513081 +513243 +513285 +513298 +513388 +513394 +513399 +513401 +513637 +513676 +513727 +513785 +513994 +514010 +514073 +514158 +514165 +514293 +514413 +514783 +514823 +514997 +515067 +515325 +515569 +515706 +515765 +515930 +516001 +516127 +516531 +516576 +516608 +516646 +516696 +516801 +516943 +516962 +516976 +517049 +517146 +517184 +517214 +517231 +517390 +517421 +517432 +517452 +517455 +517703 +517713 +517834 +517891 +517974 +518008 +518121 +518177 +518349 +518450 +518732 +518765 +518850 +518933 +519016 +519167 +519196 +519339 +519439 +519523 +519618 +519801 +519827 +519941 +519975 +520046 +520117 +520173 +520211 +520219 +520240 +520291 +520546 +520688 +520709 +520764 +520897 +521007 +521097 +521098 +521132 +521318 +521330 +521432 +521449 +521478 +521506 +521774 +521862 +522004 +522147 +522182 +522211 +522378 +522428 +522440 +522450 +522626 +522756 +522868 +523034 +523480 +523694 +523826 +523857 +523916 +524150 +524190 +524276 +524449 +524489 +524606 +524716 +524738 +524808 +524835 +524849 +524960 +525120 +525248 +525323 +525434 +525530 +525586 +525606 +525934 +526044 +526140 +526152 +526677 +526742 +526847 +526872 +527196 +527395 +527519 +527652 +527713 +527770 +527824 +527990 +528032 +528173 +528451 +528474 +528526 +528590 +528625 +528631 +528641 +528654 +528836 +528848 +528855 +528951 +529107 +529283 +529309 +529414 +529698 +529770 +529880 +530032 +530087 +530116 +530164 +530327 +530342 +530441 +530480 +530768 +530827 +530875 +530948 +530983 +530986 +531042 +531044 +531052 +531109 +531363 +531499 +531505 +531566 +531736 +531842 +531975 +532039 +532117 +532392 +532407 +532651 +532896 +532906 +532946 +533045 +533067 +533083 +533211 +533394 +533706 +533744 +533906 +533960 +533970 +534032 +534117 +534373 +534400 +534457 +534487 +534615 +534655 +534791 +534913 +534939 +534941 +535058 +535119 +535199 +535292 +535293 +535406 +535946 +536161 +536197 +536198 +536226 +536404 +536477 +536544 +536616 +536718 +536896 +536936 +537080 +537261 +537403 +537430 +537736 +537779 +537840 +537938 +538016 +538085 +538184 +538246 +538330 +538339 +538497 +538797 +538851 +539015 +539035 +539138 +539205 +539289 +539454 +539475 +539668 +539697 +539726 +539851 +539926 +540006 +540185 +540336 +540375 +540654 +540870 +540886 +540938 +541026 +541038 +541044 +541116 +541198 +541427 +541441 +541720 +541970 +542016 +542209 +542474 +542536 +542636 +542821 +542881 +542895 +542955 +543144 +543196 +543277 +543391 +543478 +543509 +543519 +543537 +543563 +543898 +544121 +544125 +544177 +544182 +544341 +544368 +544476 +544531 +544600 +544664 +544698 +545017 +545084 +545087 +545131 +545152 +545160 +545260 +545297 +545336 +545413 +545791 +545792 +545801 +546072 +546079 +546128 +546220 +546493 +546726 +546745 +546756 +546768 +546895 +546934 +547035 +547147 +547209 +547249 +547271 +547286 +547471 +547587 +547620 +547717 +547792 +547806 +547914 +548132 +548158 +548184 +548235 +548284 +548311 +548357 +548440 +548509 +548579 +548691 +548925 +548935 +548938 +548971 +548984 +549005 +549017 +549174 +549188 +549193 +549212 +549289 +549303 +549307 +549318 +549329 +549352 +549386 +549801 +549887 +550021 +550193 +550210 +550212 +550216 +550435 +550466 +550642 +550718 +550734 +550991 +551322 +551520 +551552 +551711 +551880 +552032 +552096 +552433 +552449 +552625 +552722 +552768 +552793 +553011 +553047 +553089 +553099 +553155 +553233 +553298 +553318 +553396 +553429 +553558 +553612 +553626 +553663 +553705 +553738 +554389 +554393 +554549 +554614 +554627 +554630 +554663 +554691 +554792 +554822 +554879 +555204 +555288 +555317 +555348 +555350 +555368 +555534 +555938 +556122 +556323 +556478 +556515 +556595 +556927 +557195 +557257 +557621 +557687 +557728 +557883 +557950 +558075 +558435 +558542 +558543 +558607 +558926 +559319 +559407 +559472 +559518 +559606 +559925 +560128 +560374 +560581 +560693 +560911 +561166 +561212 +561218 +561240 +561259 +561297 +561448 +561620 +561635 +561679 +561948 +562274 +562346 +562433 +562453 +562472 +562555 +562599 +562665 +562696 +562727 +562837 +562848 +562855 +563015 +563165 +563318 +563399 +563432 +563441 +563477 +563493 +563568 +563588 +563658 +563865 +564037 +564145 +564196 +564305 +564396 +564411 +564474 +564494 +564616 +564842 +564865 +565003 +565011 +565149 +565204 +565364 +565396 +565430 +565436 +566090 +566242 +566406 +566461 +566523 +566537 +566580 +566756 +566840 +566850 +566857 +567108 +567360 +567416 +567778 +567786 +567819 +567826 +567872 +568252 +568344 +568608 +568671 +568788 +569047 +569103 +569112 +569114 +569144 +569275 +569340 +569441 +569664 +569798 +569824 +569850 +569932 +569944 +569949 +569961 +570045 +570143 +570235 +570382 +570596 +570662 +570715 +570865 +571040 +571067 +571174 +571220 +571308 +571352 +571445 +571485 +571590 +571664 +571675 +571811 +571818 +571882 +572016 +572249 +572338 +572356 +572635 +572714 +572726 +572767 +572860 +572911 +573064 +573065 +573211 +573339 +573388 +573486 +573509 +573646 +573666 +573750 +573767 +573803 +573924 +574133 +574200 +574346 +574369 +574502 +574690 +574715 +574962 +575235 +575303 +575355 +575394 +575516 +575561 +575781 +575819 +575827 +575870 +575932 +575988 +576016 +576028 +576219 +576221 +576236 +576479 +576522 +576549 +576569 +576659 +576687 +576721 +576764 +576815 +576846 +577224 +577377 +577388 +577948 +578115 +578159 +578175 +578323 +578392 +578410 +578450 +578506 +578621 +578637 +578719 +578766 +578798 +578815 +578908 +579021 +579071 +579210 +579236 +579328 +579456 +579471 +579619 +579738 +579764 +579767 +580154 +580409 +580437 +580506 +580515 +580533 +580811 +580984 +581001 +581013 +581030 +581184 +581189 +581292 +581313 +581388 +581409 +581593 +581740 +581768 +581783 +581821 +581830 +581874 +581934 +581972 +582103 +582217 +582235 +582502 +582555 +582760 +582781 +582953 +582976 +583258 +583369 +583371 +583405 +583424 +583433 +583473 +583475 +583501 +583680 +583690 +583813 +583923 +584190 +584261 +584478 +584532 +584695 +584744 +584842 +585033 +585044 +585109 +585212 +585236 +585259 +585300 +585489 +585590 +585721 +585909 +586243 +586560 +586574 +586586 +586631 +586661 +586934 +587206 +587274 +587329 +587535 +587556 +587590 +587623 +587826 +587871 +588038 +588244 +588675 +588678 +588835 +588941 +589344 +589474 +589708 +589831 +589912 +590073 +590294 +590428 +590456 +590756 +590770 +590775 +590782 +590785 +590911 +591030 +591219 +591304 +591425 +591516 +591578 +591623 +591732 +591781 +591922 +592004 +592023 +592075 +592280 +592287 +592335 +592361 +592467 +592498 +592585 +592730 +592848 +592927 +592955 +593008 +593085 +593111 +593138 +593397 +593446 +593494 +593609 +593715 +593719 +593935 +593981 +594114 +594118 +594371 +594577 +594581 +594608 +594692 +594813 +594874 +594979 +595072 +595095 +595151 +595232 +595325 +595582 +595616 +595785 +595916 +595959 +596326 +596362 +596469 +596481 +596779 +596903 +596911 +596924 +596972 +597103 +597203 +597219 +597350 +597382 +597419 +597517 +597603 +597657 +597737 +597833 +598128 +598241 +598280 +598317 +598359 +598361 +598377 +598452 +598479 +598485 +598805 +598826 +598900 +599075 +599163 +599293 +599390 +599524 +599592 +600066 +600110 +600249 +600279 +600539 +600556 +600627 +600757 +600764 +600797 +600833 +600839 +600861 +600980 +600991 +601033 +601099 +601218 +601268 +601290 +601329 +601418 +601625 +601948 +601973 +601983 +602017 +602065 +602252 +602334 +602383 +602502 +602615 +602625 +602727 +602743 +602757 +602844 +603247 +603248 +603285 +603446 +603458 +603554 +603654 +603718 +604113 +604270 +604290 +604393 +604426 +604605 +604716 +604818 +604871 +605027 +605083 +605248 +605375 +605409 +605481 +605893 +605895 +606150 +606526 +606699 +606707 +606783 +606825 +606966 +607009 +607055 +607075 +607082 +607121 +607148 +607249 +607298 +607356 +607405 +607452 +607593 +607772 +608049 +608055 +608149 +608230 +608276 +608319 +608528 +608534 +608607 +608687 +608706 +608774 +608893 +608911 +608960 +608978 +608990 +609060 +609082 +609144 +609166 +609182 +609423 +609456 +609562 +609614 +609628 +609664 +609718 +609765 +609802 +609821 +609908 +609982 +610021 +610077 +610115 +610120 +610243 +610314 +610395 +610536 +610607 +610626 +610784 +610796 +610983 +611142 +611150 +611238 +611317 +611421 +611577 +611587 +611731 +611776 +612217 +612303 +612516 +612532 +612554 +612633 +612670 +612691 +612825 +612828 +612880 +613011 +613189 +613192 +613256 +613269 +613291 +613382 +613530 +613576 +613893 +613989 +614144 +614253 +614267 +614507 +614576 +614595 +614605 +614615 +614759 +614865 +615055 +615104 +615180 +615243 +615290 +615364 +615422 +615500 +615520 +615717 +615735 +615797 +616023 +616066 +616079 +616128 +616163 +616228 +616249 +616253 +616333 +616350 +616441 +616550 +616738 +616740 +616773 +616874 +617041 +617113 +617232 +617337 +617407 +617447 +617599 +617621 +617681 +617742 +617935 +617965 +618114 +618240 +618444 +618526 +618653 +618692 +618731 +618916 +618947 +618955 +619029 +619258 +619336 +619478 +619602 +619771 +619993 +620106 +620222 +620346 +620361 +620829 +620911 +620913 +620978 +621184 +621208 +621236 +621324 +621332 +621335 +621341 +621546 +621608 +621749 +621764 +621793 +621810 +621842 +621908 +621968 +622199 +622383 +622406 +622494 +622509 +622843 +622935 +622948 +623030 +623099 +623504 +623512 +623586 +623834 +623855 +624069 +624485 +624496 +624529 +624550 +624577 +624771 +624829 +624849 +624881 +624906 +624910 +625136 +625378 +625443 +625660 +625951 +625963 +625970 +626109 +626168 +626263 +626306 +626417 +626548 +626615 +626635 +626944 +626972 +627028 +627055 +627096 +627241 +627266 +627286 +627325 +627415 +627542 +627607 +627616 +627796 +628086 +628151 +628325 +628602 +628702 +628872 +628951 +628989 +629214 +629248 +629557 +629700 +629701 +629718 +629889 +629893 +630046 +630064 +630200 +630429 +630488 +630533 +630629 +630651 +630715 +630796 +630907 +630961 +630986 +631036 +631189 +631253 +631333 +631715 +631835 +631866 +631927 +632073 +632134 +632147 +632150 +632247 +632305 +632346 +632596 +632772 +632833 +632846 +632964 +633017 +633025 +633158 +633347 +633414 +633647 +633787 +633801 +633893 +634026 +634101 +634118 +634185 +634203 +634354 +634355 +634508 +634511 +634753 +634862 +634868 +634871 +634970 +634993 +635078 +635174 +635297 +635389 +635420 +635463 +635491 +635514 +635661 +635763 +635909 +636068 +636146 +636268 +636327 +636411 +636421 +636936 +637357 +637467 +637494 +637942 +638371 +638729 +638792 +638851 +638870 +638932 +639038 +639136 +639152 +639205 +639350 +639403 +639455 +639524 +639715 +639816 +639885 +639887 +639954 +640080 +640249 +640335 +641133 +641264 +641282 +641342 +641485 +641486 +641526 +641615 +641785 +641870 +642005 +642051 +642086 +642087 +642171 +642185 +642233 +642315 +642484 +642610 +642611 +642644 +642692 +642966 +643008 +643026 +643214 +643219 +643322 +643414 +644110 +644112 +644342 +644352 +644353 +644388 +644476 +644533 +644615 +644724 +644745 +644835 +644908 +644924 +645036 +645193 +645227 +645393 +645580 +645747 +645793 +645805 +645839 +645959 +645983 +646655 +646677 +646982 +647068 +647154 +647465 +647494 +647716 +647751 +647790 +647806 +647836 +647860 +648135 +648240 +648256 +648305 +648346 +648356 +648435 +648664 +648665 +648732 +648743 +648804 +648921 +649036 +649106 +649238 +649352 +649396 +649510 +649596 +649752 +649814 +649874 +649915 +649985 +649988 +650000 +650077 +650108 +650327 +650371 +650386 +650877 +651163 +651376 +651453 +651598 +651785 +651958 +652218 +652219 +652234 +652256 +652278 +652354 +652411 +652428 +652447 +652499 +652686 +652710 +652727 +652849 +653084 +653098 +653108 +653292 +653396 +653472 +653493 +653544 +653657 +653813 +653815 +654112 +654192 +654211 +654286 +654354 +654411 +654446 +654597 +654643 +654673 +655004 +655241 +655266 +655279 +655338 +655377 +655453 +655455 +655472 +655921 +655994 +656026 +656036 +656065 +656090 +656120 +656145 +656323 +656544 +656547 +656861 +657047 +657182 +657331 +657383 +657395 +657423 +657507 +657726 +657892 +657899 +657909 +658000 +658033 +658139 +658465 +658492 +658853 +658971 +659017 +659063 +659096 +659141 +659150 +659209 +659213 +659225 +659298 +659704 +660053 +660320 +660336 +660356 +660492 +661192 +661297 +661319 +661474 +661556 +661571 +661644 +661657 +661673 +661693 +661786 +661819 +662056 +662085 +662179 +662238 +662376 +662679 +662890 +663184 +663231 +663246 +663292 +663461 +663562 +663594 +663612 +663688 +663690 +663732 +663766 +663770 +663844 +663945 +664165 +664354 +664682 +664719 +664825 +664845 +664910 +664950 +665033 +665150 +665223 +665351 +665383 +665424 +666002 +666012 +666031 +666101 +666196 +666351 +666366 +666395 +666590 +666623 +666676 +666698 +666735 +666850 +666898 +666933 +667100 +667503 +667515 +667721 +667791 +667912 +667971 +667977 +668103 +668251 +668285 +668287 +668304 +668325 +668501 +668648 +668664 +668836 +668841 +668847 +669027 +669099 +669106 +669116 +669146 +669448 +669601 +669618 +669743 +669746 +669793 +669802 +669821 +669942 +669967 +669984 +670073 +670145 +670187 +670346 +670349 +670621 +670850 +670927 +670960 +670996 +671327 +671405 +671476 +671584 +671633 +671653 +671656 +671834 +672116 +672128 +672206 +672595 +672946 +672971 +673626 +673662 +673700 +673880 +673956 +673968 +674043 +674065 +674069 +674107 +674197 +674242 +674477 +674510 +674554 +674626 +674634 +674652 +674938 +675014 +675018 +675101 +675160 +675255 +675311 +675500 +675567 +675612 +675880 +676005 +676056 +676060 +676069 +676157 +676163 +676252 +676272 +676330 +676386 +676471 +676590 +676591 +676612 +676648 +676802 +676873 +676938 +677112 +677123 +677161 +677163 +677175 +677203 +677264 +677288 +677365 +677628 +677631 +677806 +677897 +677952 +678020 +678101 +678218 +678335 +678420 +678560 +678627 +678653 +678688 +678757 +678804 +678843 +678870 +678933 +679007 +679266 +679428 +679463 +679501 +679552 +679582 +679656 +680006 +680160 +680205 +680298 +680444 +680472 +680649 +680709 +680745 +680885 +681029 +681127 +681195 +681290 +681308 +681335 +681416 +681481 +681657 +681759 +681969 +682021 +682124 +682210 +682333 +682342 +682478 +682622 +682726 +682965 +682967 +683019 +683080 +683288 +683307 +683400 +683404 +683482 +683641 +683731 +683734 +683750 +684002 +684019 +684022 +684400 +684560 +684611 +684679 +684773 +685002 +685196 +685215 +685460 +685486 +685552 +685634 +685690 +686004 +686176 +686248 +686264 +686363 +686527 +686630 +686849 +686868 +686875 +687013 +687064 +687189 +687257 +687416 +687434 +687634 +687669 +687718 +687783 +687810 +687813 +687820 +687918 +688238 +688582 +688632 +688736 +688811 +689005 +689103 +689162 +689361 +689454 +689484 +689498 +689728 +689807 +689969 +690233 +690399 +690573 +690646 +690733 +690817 +690836 +690902 +690911 +690916 +690972 +691020 +691038 +691247 +691331 +691368 +691378 +691420 +691495 +691572 +691659 +691785 +691860 +691975 +692008 +692167 +692266 +692323 +692347 +692447 +692610 +692719 +692801 +692906 +692911 +692998 +693063 +693144 +693285 +693616 +693791 +693877 +694335 +694579 +694646 +694670 +694945 +695088 +695197 +695293 +695408 +695556 +695639 +695725 +695734 +695832 +695849 +696010 +696113 +696138 +696143 +696183 +696289 +696393 +696511 +696604 +696673 +696737 +696779 +696829 +696988 +697091 +697354 +697445 +697712 +697732 +697744 +697996 +698216 +698287 +698306 +698377 +698396 +698526 +698551 +698686 +698876 +699141 +699269 +699353 +699448 +699615 +699698 +699744 +699782 +699846 +699871 +699955 +700083 +700084 +700187 +700232 +700426 +700485 +700591 +700753 +700794 +700914 +700967 +701151 +701152 +701207 +701270 +701346 +701471 +701475 +701494 +701528 +701535 +701558 +701586 +701617 +701660 +701706 +701751 +701756 +701932 +701999 +702162 +702209 +702520 +702538 +702741 +702899 +702984 +703002 +703280 +703451 +703522 +703795 +703947 +704223 +704259 +704272 +704446 +704934 +705094 +705178 +705222 +705279 +705355 +705454 +705654 +705675 +705919 +706030 +706395 +706454 +706709 +706712 +706850 +706900 +706916 +707045 +707159 +707162 +707196 +707349 +707430 +707467 +708243 +708248 +708265 +708352 +708354 +708372 +708398 +708450 +708563 +708642 +708789 +708957 +709014 +709046 +709229 +709258 +709521 +709798 +709842 +709846 +709880 +709980 +710494 +710541 +710739 +710772 +710793 +711328 +711417 +711448 +711690 +711910 +711920 +711950 +712039 +712089 +712227 +712239 +712250 +712288 +712331 +712399 +712409 +712425 +712573 +712672 +712744 +712986 +713107 +713145 +713168 +713188 +713301 +713404 +713493 +713564 +713770 +713869 +713960 +714108 +714378 +714379 +714459 +714474 +714559 +714609 +714611 +714631 +714635 +714705 +714723 +714765 +714895 +714938 +715071 +715165 +715379 +715533 +715578 +715885 +715893 +715896 +715962 +716058 +716073 +716265 +716278 +716400 +716450 +716452 +716523 +716792 +716961 +717096 +717122 +717306 +717346 +717369 +717460 +717535 +717546 +717718 +718083 +718150 +718204 +718556 +718734 +718987 +719009 +719112 +719128 +719180 +719189 +719665 +719780 +720017 +720144 +720449 +720489 +720624 +720741 +720826 +720843 +720890 +721147 +721331 +721335 +721580 +721647 +721710 +721843 +721897 +722139 +722195 +722247 +722343 +722383 +722591 +722722 +722739 +722746 +722834 +722869 +722952 +723089 +723113 +723163 +723190 +723223 +723420 +723615 +723749 +723843 +724025 +724281 +724489 +724515 +724615 +724628 +724634 +724745 +724926 +724997 +725007 +725017 +725031 +725060 +725190 +725207 +725236 +725475 +725591 +725620 +725706 +726045 +726098 +726302 +726507 +726616 +726635 +726801 +726917 +726961 +726973 +726994 +727185 +727291 +727299 +727562 +727610 +727696 +727774 +727790 +727964 +727984 +728213 +728266 +728320 +728624 +728651 +728720 +728782 +728823 +728841 +729285 +729319 +729348 +729453 +729563 +729599 +729739 +729770 +729772 +729865 +729936 +729970 +730030 +730040 +730100 +730120 +730135 +730155 +730313 +730320 +730500 +730513 +730602 +730649 +730682 +730748 +730814 +730873 +731105 +731172 +731185 +731227 +731232 +731605 +731640 +731788 +731901 +731977 +732275 +732498 +732777 +732792 +733093 +733334 +733376 +733483 +733488 +733991 +734037 +734068 +734160 +734204 +734265 +734336 +734398 +734508 +734582 +734783 +735092 +735168 +735281 +735384 +735427 +735514 +735547 +735842 +735940 +736044 +736194 +736203 +736221 +736324 +736325 +736383 +736518 +736636 +736824 +736842 +736854 +736878 +736926 +736949 +737151 +737504 +737593 +738077 +738078 +738086 +738340 +738517 +738546 +738578 +738593 +738605 +738654 +738738 +738757 +738847 +738937 +738997 +739234 +739405 +739686 +739729 +740409 +740926 +741077 +741081 +741129 +741135 +741194 +741231 +741357 +741488 +741537 +741812 +741903 +741935 +742000 +742192 +742281 +742435 +742480 +742601 +742602 +742735 +742796 +742842 +743039 +743156 +743184 +743220 +743244 +743382 +743553 +743698 +743710 +743714 +743723 +743846 +743959 +744022 +744192 +744242 +744359 +744789 +744828 +744881 +745060 +745270 +745274 +745347 +745402 +745563 +745677 +745787 +745842 +745934 +745958 +746053 +746184 +746231 +746249 +746431 +746501 +746565 +746690 +746729 +746739 +746840 +746919 +747035 +747145 +747320 +747347 +747414 +747550 +747581 +747656 +747701 +747724 +747946 +747997 +748188 +748250 +748390 +748602 +748628 +748737 +748770 +748816 +748860 +748976 +749060 +749114 +749272 +749546 +749882 +749997 +750186 +750366 +750411 +750474 +750566 +750571 +750592 +750667 +750669 +750730 +750768 +750819 +750867 +750872 +751059 +751179 +751262 +751268 +751327 +751331 +751355 +751613 +751646 +751768 +751811 +751861 +752003 +752030 +752158 +752251 +752255 +752335 +752474 +752493 +752532 +752549 +752603 +752611 +752711 +752788 +752836 +752850 +753139 +753265 +753344 +753404 +753432 +753495 +753530 +753798 +754283 +754367 +754520 +754568 +754597 +754944 +755026 +755407 +755637 +755642 +755650 +755691 +755927 +756086 +756125 +756438 +756533 +756769 +756925 +756953 +757024 +757073 +757077 +757143 +757161 +757447 +757468 +757503 +757735 +757813 +757885 +757935 +758094 +758260 +758444 +758474 +758502 +758508 +758518 +758581 +758585 +758625 +758629 +758748 +758756 +758770 +759077 +759131 +759187 +759198 +759235 +759297 +759336 +759352 +759399 +759441 +759452 +759543 +759601 +759865 +759924 +760092 +760388 +760394 +760481 +760635 +760773 +760969 +761146 +761504 +761536 +761785 +761821 +761851 +761874 +761995 +762426 +762621 +762868 +762918 +763094 +763146 +763213 +763577 +763597 +763678 +763842 +763862 +763992 +764130 +764325 +764393 +764417 +764442 +764561 +764581 +764589 +764673 +764718 +764827 +764845 +764931 +765108 +765143 +765170 +765221 +765286 +765412 +765509 +765624 +765629 +765706 +765777 +765849 +765900 +765914 +766123 +766178 +766617 +766728 +766902 +766992 +767012 +767121 +767191 +767224 +767261 +767289 +767310 +767421 +767465 +767626 +767739 +768065 +768095 +768130 +768143 +768170 +768244 +768247 +768281 +768373 +768387 +768401 +768435 +768668 +768684 +768788 +768829 +768907 +769029 +769033 +769034 +769243 +769290 +769329 +769395 +769504 +769557 +769596 +769603 +769616 +769629 +769680 +769765 +769809 +769904 +769925 +770128 +770413 +770444 +770517 +770607 +770723 +770760 +770826 +770837 +770921 +771013 +771050 +771276 +771296 +771344 +771430 +771446 +771721 +771723 +771836 +771952 +772216 +772264 +772365 +772410 +772435 +772437 +772492 +772499 +772550 +772666 +772679 +772976 +773042 +773065 +773105 +773206 +773221 +773338 +773365 +773393 +773566 +773572 +773575 +773770 +773783 +773898 +773951 +774140 +774190 +774195 +774429 +774438 +774475 +774480 +774553 +774562 +774573 +774588 +774592 +774778 +774983 +775060 +775061 +775064 +775140 +775294 +775424 +775514 +775713 +775797 +775807 +775850 +776071 +776077 +776316 +776372 +776395 +776711 +776909 +776943 +777047 +777092 +777429 +777574 +777581 +777655 +777671 +777820 +777910 +778023 +778066 +778092 +778266 +778509 +778543 +778724 +778769 +778819 +778890 +779004 +779419 +779439 +779486 +779620 +779734 +779896 +780029 +780153 +780308 +780422 +780432 +780898 +780932 +780993 +781016 +781066 +781234 +781350 +781513 +781582 +781608 +781629 +781907 +781947 +782000 +782435 +782475 +782604 +782614 +782617 +782624 +782631 +782637 +782668 +782944 +782967 +783044 +783235 +783367 +783389 +783436 +783493 +783544 +783651 +783935 +783948 +783966 +784035 +784042 +784294 +784464 +784753 +784855 +784893 +785104 +785137 +785224 +785239 +785350 +785457 +785682 +786106 +786198 +786250 +786442 +786529 +786559 +786583 +786635 +786814 +786830 +786902 +786935 +786987 +787024 +787050 +787052 +787112 +787184 +787350 +787392 +787411 +787765 +788231 +788296 +788423 +788484 +788507 +788743 +788750 +788835 +788882 +788922 +789057 +789133 +789160 +789189 +789314 +789443 +789632 +789834 +789952 +790693 +790824 +790943 +791013 +791038 +791240 +791352 +791435 +791477 +791613 +791722 +791731 +791759 +791820 +791822 +792062 +792145 +792179 +792605 +792728 +792760 +792805 +792813 +792901 +792909 +792934 +793045 +793223 +793246 +793250 +793431 +793456 +793540 +793575 +793663 +793696 +793846 +793869 +793870 +794013 +794041 +794074 +794122 +794262 +794402 +794426 +794490 +794515 +794602 +794607 +794634 +794702 +794844 +794846 +794888 +795036 +795106 +795110 +795144 +795442 +795516 +795549 +795557 +795594 +795832 +795879 +796332 +796381 +796977 +797010 +797011 +797140 +797371 +797398 +797528 +797622 +797628 +797983 +798197 +798348 +798355 +798421 +798704 +798734 +798790 +798797 +798851 +798878 +798887 +798927 +798944 +799012 +799129 +799141 +799192 +799407 +799607 +799721 +800139 +800253 +800533 +800587 +800692 +800766 +800773 +800785 +800911 +801146 +801247 +801318 +801323 +801342 +801353 +801496 +801513 +801526 +801637 +801685 +801748 +801930 +802005 +802034 +802040 +802101 +802217 +802233 +802322 +802331 +802348 +802362 +802368 +802389 +802571 +802641 +802699 +802758 +802900 +803076 +803118 +803341 +803503 +803580 +803637 +803654 +803741 +803899 +804053 +804115 +804149 +804298 +804392 +804453 +804517 +804624 +804725 +804924 +804929 +804986 +805142 +805188 +805226 +805246 +805420 +805568 +805576 +805587 +805596 +805702 +805839 +805853 +805875 +805932 +806050 +806102 +806234 +806569 +806600 +806650 +806665 +806842 +807043 +807052 +807084 +807182 +807287 +807369 +807388 +807399 +807463 +807499 +807547 +807615 +807628 +807652 +807747 +807799 +807808 +807820 +807988 +808019 +808254 +808339 +808456 +808490 +808600 +808719 +808839 +808899 +808921 +808923 +808972 +809131 +809276 +809532 +809575 +809690 +809728 +809752 +809786 +809862 +810068 +810074 +810107 +810228 +810243 +810354 +810525 +810622 +810682 +810923 +811048 +811066 +811069 +811071 +811105 +811160 +811229 +811353 +811777 +811832 +811878 +811979 +812009 +812130 +812302 +812317 +812433 +812613 +812630 +812662 +812692 +812709 +812739 +812795 +812909 +812957 +813113 +813157 +813277 +813278 +813402 +813403 +813718 +813981 +814006 +814072 +814104 +814201 +814208 +814227 +814319 +814721 +814970 +815245 +815495 +815505 +815513 +815557 +815747 +815793 +816078 +816219 +816298 +816391 +816633 +816668 +816938 +817112 +817381 +817708 +817769 +817945 +818062 +818064 +818139 +818301 +818334 +818340 +818367 +818456 +818465 +818668 +818809 +818849 +818887 +818889 +819230 +819312 +819448 +819574 +819669 +819726 +819969 +820036 +820060 +820307 +820484 +820605 +820641 +820683 +820744 +820808 +821097 +821172 +821267 +821366 +821395 +821640 +821911 +821931 +821944 +822151 +822331 +822637 +822848 +822867 +822906 +823076 +823096 +823147 +823218 +823399 +823491 +823623 +823913 +823994 +824181 +824189 +824446 +824529 +824578 +824710 +824876 +824914 +825139 +825150 +825159 +825369 +825426 +825443 +825456 +825528 +825620 +825641 +825676 +825689 +825692 +825725 +825784 +825824 +825963 +826007 +826256 +826279 +826333 +826374 +826390 +826452 +826706 +826822 +827015 +827217 +827638 +827688 +827703 +827711 +827896 +828610 +828611 +828614 +828615 +828638 +828643 +828748 +828843 +829161 +829195 +829267 +829506 +829538 +829594 +829694 +829724 +829941 +830099 +830177 +830338 +830422 +830620 +830732 +830733 +830763 +830803 +830829 +830888 +831056 +831163 +831194 +831420 +831434 +831483 +831665 +831901 +832074 +832202 +832296 +832461 +832648 +832667 +832685 +832748 +832797 +832831 +832852 +832899 +833152 +833206 +833424 +833472 +833571 +833655 +833686 +833841 +834145 +834341 +834406 +834486 +834766 +835008 +835034 +835050 +835098 +835148 +835182 +835286 +835592 +835671 +835679 +835708 +835791 +836053 +836151 +836279 +836372 +836473 +836649 +836674 +836833 +837093 +837143 +837492 +837716 +837853 +837952 +837989 +838079 +838201 +838309 +838399 +838594 +838919 +839033 +839149 +839403 +839501 +839542 +839545 +839744 +839805 +839836 +839843 +839851 +839864 +839939 +839968 +840038 +840044 +840099 +840114 +840175 +840247 +840482 +840618 +840803 +840816 +840844 +840999 +841082 +841089 +841124 +841265 +841382 +841660 +841928 +841998 +842123 +842306 +842565 +842607 +842636 +842722 +842776 +842817 +842865 +843021 +843155 +843202 +843294 +843399 +843407 +843413 +843562 +843693 +843696 +843708 +843895 +844044 +844168 +844238 +844266 +844385 +844499 +844650 +844904 +845079 +845207 +845208 +845390 +845405 +845471 +845605 +845706 +845809 +845819 +845996 +846179 +846287 +846379 +846402 +846752 +846923 +846960 +846965 +847025 +847262 +847337 +847596 +847869 +848029 +848142 +848211 +848227 +848307 +848593 +848621 +848747 +848950 +849079 +849129 +849158 +849245 +849296 +849379 +849410 +849483 +849525 +849949 +850239 +850264 +850283 +850363 +850413 +850437 +850462 +850519 +850601 +850692 +850705 +850841 +850885 +851156 +851862 +851900 +851988 +852215 +852227 +852229 +852252 +852512 +852547 +852592 +852628 +853028 +853089 +853276 +853424 +853652 +853657 +853674 +853689 +853716 +853738 +853868 +853917 +853931 +853963 +853993 +854004 +854033 +854161 +854343 +854500 +854522 +854564 +854816 +854833 +854872 +854896 +854908 +854954 +855033 +855243 +855279 +855321 +855364 +855394 +855408 +855553 +855571 +855754 +855766 +855908 +855957 +856426 +856543 +856655 +856775 +856821 +857063 +857096 +857158 +857312 +857372 +857478 +857601 +857782 +857839 +857865 +858019 +858087 +858167 +858234 +858461 +858651 +858848 +858859 +858865 +859229 +859308 +859593 +859633 +859964 +860094 +860240 +860420 +860461 +860642 +860725 +861049 +861178 +861211 +861327 +861488 +861718 +861730 +861834 +862244 +862330 +862361 +862445 +862592 +862616 +862660 +862773 +862789 +862812 +862847 +862878 +862921 +863017 +863022 +863163 +863178 +863203 +863283 +863332 +863428 +863458 +863586 +863639 +863697 +863760 +863833 +863900 +863923 +863957 +864421 +864627 +864738 +864799 +864917 +864921 +864939 +865042 +865313 +865382 +865399 +865493 +865536 +865557 +865757 +865891 +865987 +866093 +866207 +866269 +866507 +866543 +866767 +866958 +867220 +867222 +867244 +867352 +867440 +867448 +867452 +867561 +867692 +867791 +867811 +867925 +868277 +868291 +868446 +868551 +868554 +868601 +868648 +868767 +868790 +869033 +869066 +869086 +869113 +869158 +869228 +869255 +869415 +869416 +869507 +869811 +869971 +870097 +870421 +870463 +870613 +870735 +870777 +870841 +870902 +870907 +871061 +871101 +871279 +871298 +871384 +871554 +871629 +871742 +871884 +871925 +872044 +872117 +872141 +872170 +872198 +872348 +872435 +872585 +872789 +872916 +873145 +873221 +873353 +873552 +873572 +873893 +874220 +874268 +874520 +874576 +874621 +874766 +874811 +874837 +874921 +874959 +874965 +875061 +875166 +875227 +875244 +875272 +875273 +875548 +875581 +875665 +875739 +875806 +875881 +876090 +876363 +876401 +876418 +876434 +876444 +876459 +876471 +876692 +876699 +876780 +877066 +877081 +877180 +877365 +877716 +877800 +877805 +877907 +878117 +878153 +878206 +878449 +878505 +878676 +878689 +878816 +878865 +878920 +878944 +879211 +879237 +879243 +879402 +879433 +879508 +879540 +879921 +880005 +880102 +880330 +880334 +880471 +880614 +880620 +880631 +880674 +880841 +880955 +880969 +880987 +881194 +881208 +881364 +881553 +881623 +881928 +881933 +881951 +881954 +882147 +882149 +882312 +882314 +882363 +882664 +882805 +883070 +883155 +883209 +883260 +883321 +883366 +883578 +883694 +883695 +883751 +883797 +883947 +883949 +883979 +884294 +884407 +884505 +884707 +884851 +885003 +885104 +885172 +885205 +885295 +885331 +885384 +885496 +885604 +885646 +885829 +885838 +885888 +885931 +885933 +885970 +886029 +886088 +886094 +886237 +886569 +886778 +886795 +886976 +887320 +887340 +887359 +887464 +887575 +887742 +887786 +887912 +887943 +888140 +888414 +888570 +888698 +888756 +888797 +888816 +888822 +888863 +889132 +889152 +889373 +889400 +889404 +889636 +889943 +889946 +889966 +890025 +890217 +890503 +890546 +890549 +890601 +890660 +890876 +890885 +891062 +891099 +891170 +891433 +891592 +891628 +891739 +891812 +891838 +891935 +891992 +892039 +892183 +892195 +892260 +892423 +892460 +892500 +892568 +892636 +892814 +892850 +892949 +893099 +893348 +893581 +893646 +893979 +894207 +894293 +894377 +894411 +894414 +894546 +894691 +894695 +894703 +894891 +894915 +894994 +895103 +895120 +895144 +895267 +895317 +895439 +895442 +895553 +895744 +895756 +896211 +896229 +896474 +896488 +896493 +896496 +896645 +896978 +897112 +897174 +897393 +897460 +897718 +897813 +898345 +898454 +898648 +898734 +898752 +898812 +898849 +899020 +899128 +899151 +899342 +899371 +899380 +899499 +899512 +899618 +899753 +899813 +899824 +899983 +900129 +900135 +900275 +900309 +900440 +900450 +900480 +900572 +900644 +900678 +901062 +901118 +901225 +901243 +901283 +901351 +901370 +901619 +901687 +901917 +901947 +902167 +902197 +902234 +902247 +902404 +902430 +902638 +902695 +902789 +902790 +902973 +902978 +903149 +903209 +903581 +903807 +903964 +904298 +904412 +904735 +904761 +904954 +905032 +905060 +905080 +905082 +905089 +905223 +905241 +905303 +905511 +906014 +906071 +906103 +906168 +906253 +906315 +906433 +906542 +906664 +906752 +906824 +906893 +906981 +907013 +907194 +907460 +907474 +907523 +907550 +907780 +908465 +908695 +908717 +908770 +908839 +909003 +909070 +909135 +909233 +909285 +909342 +909364 +909705 +909746 +909775 +909940 +910059 +910125 +910151 +910160 +910233 +910253 +910303 +910389 +910509 +910690 +910743 +910784 +910841 +911058 +911159 +911204 +911215 +911271 +911378 +911573 +911586 +911654 +911722 +911834 +911937 +911941 +912139 +912182 +912446 +912500 +912568 +912715 +912779 +912871 +912936 +913010 +913216 +913466 +913505 +913735 +913816 +913907 +913986 +914106 +914197 +914224 +914360 +914583 +914895 +914905 +915057 +915128 +915214 +915315 +915371 +915439 +915520 +915584 +915590 +915599 +915670 +915942 +916010 +916054 +916142 +916207 +916529 +916559 +916587 +916674 +916704 +916746 +916867 +916946 +917255 +917360 +917478 +917485 +917590 +917625 +917690 +917720 +917757 +918075 +918187 +918379 +918399 +918455 +918523 +918553 +918836 +918910 +918986 +919157 +919270 +919380 +919458 +919532 +919994 +920220 +920274 +920487 +920720 +920750 +920794 +920821 +920840 +920898 +920987 +921045 +921120 +921150 +921431 +921593 +921650 +921718 +921741 +921760 +921870 +921899 +922005 +922288 +922487 +922589 +922671 +923010 +923196 +923238 +923294 +923403 +923407 +923416 +923436 +923598 +923614 +923767 +923898 +923949 +923958 +923977 +924198 +924234 +924290 +924352 +924514 +924576 +924679 +924761 +924808 +924841 +925001 +925210 +925315 +925397 +925418 +925487 +925545 +925663 +925665 +925779 +925862 +925961 +926075 +926088 +926096 +926136 +926213 +926268 +926290 +926685 +926760 +926810 +926812 +926861 +927005 +927048 +927116 +927501 +927588 +927717 +927833 +927882 +927915 +927918 +927976 +928180 +928210 +928311 +928361 +928394 +928546 +928706 +928746 +928780 +928794 +928817 +929238 +929249 +929276 +929289 +929340 +929355 +929424 +929494 +929560 +929669 +929956 +930035 +930106 +930316 +930578 +930588 +930648 +930755 +930768 +930786 +930826 +930861 +931553 +931587 +931600 +931625 +931809 +931842 +931897 +932165 +932376 +933103 +933115 +933579 +933586 +933654 +933720 +933847 +933889 +933974 +934336 +934476 +934509 +934884 +934894 +935077 +935092 +935179 +935211 +935261 +935294 +935364 +935387 +935410 +935420 +935430 +935533 +935667 +935875 +935916 +935990 +936064 +936160 +936186 +936250 +936420 +936426 +936429 +936571 +936704 +936717 +936824 +936903 +936958 +937091 +937277 +937580 +937764 +937769 +937809 +937891 +938216 +938406 +938493 +938759 +938774 +938777 +938846 +938966 +939112 +939115 +939262 +939281 +939600 +939738 +940200 +940217 +940363 +940593 +940681 +940708 +940803 +940827 +940874 +940915 +940989 +941019 +941058 +941186 +941257 +941562 +941630 +941881 +941898 +941911 +942037 +942352 +942389 +942392 +942534 +942625 +942655 +942683 +942766 +942996 +943241 +943352 +943579 +943631 +943981 +944017 +944096 +944097 +944189 +944198 +944201 +944320 +944623 +944717 +944778 +944818 +944939 +945341 +945347 +945377 +945500 +945513 +945541 +945553 +945557 +945679 +945723 +945758 +946146 +946329 +946469 +946502 +946552 +946553 +946625 +946973 +947030 +947084 +947204 +947250 +947313 +947362 +947408 +947431 +947474 +947494 +947727 +947772 +947776 +948036 +948049 +948072 +948203 +948221 +948249 +948578 +948616 +948844 +949010 +949176 +949660 +949710 +949823 +949914 +949925 +950042 +950113 +950116 +950161 +950191 +950529 +950559 +950577 +950622 +950650 +950667 +950767 +950786 +950932 +951037 +951063 +951104 +951160 +951404 +951482 +951671 +951709 +951806 +951878 +952122 +952135 +952137 +952182 +952207 +952241 +952243 +952403 +952518 +952630 +952755 +952814 +952924 +953041 +953197 +953349 +953820 +953861 +954371 +954493 +954533 +954567 +954570 +954576 +954614 +954694 +954796 +954899 +954944 +955185 +955353 +955401 +955504 +955571 +955581 +955872 +955898 +956086 +956135 +956178 +956431 +956442 +956484 +956680 +956740 +956741 +956763 +956773 +956777 +956967 +956993 +957015 +957049 +957071 +957105 +957245 +957363 +957449 +957644 +957779 +957784 +957796 +957838 +958095 +958127 +958146 +958149 +958242 +958302 +958324 +958552 +958662 +958728 +958822 +959108 +959229 +959370 +959394 +959400 +959552 +959655 +959909 +960111 +960211 +960263 +960350 +960353 +960442 +960518 +960650 +961045 +961064 +961080 +961116 +961270 +961285 +961483 +961594 +962127 +962524 +962700 +962723 +962801 +962814 +963224 +963332 +963434 +963479 +963750 +963775 +963844 +963874 +963929 +964122 +964196 +964223 +964372 +964535 +964540 +964565 +964674 +964773 +964860 +965154 +965353 +965457 +965583 +965937 +965948 +966028 +966058 +966104 +966447 +966823 +966869 +966871 +967006 +967279 +967323 +967390 +967417 +967493 +967511 +967552 +967851 +967987 +968167 +968231 +968234 +968341 +968353 +968483 +968601 +968647 +968736 +968849 +968922 +968968 +969115 +969150 +969166 +969240 +969422 +969458 +969487 +969489 +969686 +969765 +969770 +969881 +970006 +970007 +970483 +970633 +970840 +970859 +970878 +971028 +971079 +971172 +971297 +971358 +971376 +971380 +971523 +971602 +971617 +971698 +971897 +971956 +972132 +972207 +972397 +972936 +973028 +973069 +973182 +973338 +973399 +973444 +973470 +973571 +973725 +973939 +973977 +974002 +974110 +974197 +974210 +974341 +974653 +974832 +974902 +975074 +975125 +975182 +975333 +975334 +975447 +975607 +975732 +975746 +975756 +975866 +976002 +976141 +976219 +976405 +976495 +976569 +976613 +976615 +976641 +976680 +976817 +976979 +977016 +977086 +977151 +977291 +977480 +977555 +977745 +977773 +977928 +977956 +978098 +978107 +978431 +978497 +978525 +978803 +978886 +979337 +979522 +979576 +979582 +979615 +979753 +979842 +979858 +979896 +979919 +979936 +980013 +980016 +980112 +980157 +980162 +980188 +980236 +980328 +980539 +980555 +980562 +980576 +980709 +980726 +980765 +980943 +981056 +981113 +981124 +981308 +981402 +981421 +981518 +981592 +981844 +981869 +981965 +982037 +982139 +982152 +982264 +982290 +982299 +982402 +982448 +982692 +982701 +982782 +982888 +982891 +983050 +983093 +983174 +983277 +983294 +983388 +983697 +983706 +984022 +984160 +984271 +984318 +984393 +984428 +984603 +984738 +984764 +984812 +984870 +985238 +985256 +985444 +985455 +985506 +985585 +985644 +985654 +985787 +985797 +985897 +986050 +986418 +986468 +986547 +986734 +986739 +986745 +986789 +986800 +986936 +986941 +987227 +987258 +987360 +987586 +987621 +987624 +987984 +988029 +988048 +988112 +988294 +988341 +988372 +988658 +988671 +988748 +988830 +988868 +988871 +988962 +989021 +989188 +989200 +989281 +989349 +989800 +989807 +989843 +989930 +989977 +989985 +990023 +990110 +990365 +990438 +990472 +990497 +990615 +990643 +990660 +990741 +990771 +990799 +990901 +991156 +991260 +991317 +991652 +991880 +991927 +991956 +992117 +992161 +992197 +992203 +992229 +992291 +992377 +992429 +992432 +992996 +993022 +993034 +993228 +993317 +993497 +993502 +993513 +993712 +993721 +993752 +993756 +993939 +994111 +994210 +994267 +994283 +994309 +994438 +994588 +994700 +994734 +994765 +995015 +995034 +995079 +995247 +995293 +995330 +995583 +996234 +996254 +996283 +996425 +996466 +996561 +996594 +996702 +996956 +996985 +997074 +997312 +997429 +997520 +997669 +997837 +997937 +998075 +998078 +998222 +998240 +998349 +998383 +998475 +998667 +999278 +999445 +999547 +999756 +999813 +999861 +999989 diff --git a/hw/vendor/hpdcache/rtl/tb/scripts/scan_logs.pl b/hw/vendor/hpdcache/rtl/tb/scripts/scan_logs.pl new file mode 100755 index 00000000..4c56c847 --- /dev/null +++ b/hw/vendor/hpdcache/rtl/tb/scripts/scan_logs.pl @@ -0,0 +1,1363 @@ +#!/usr/bin/env perl +################################################################################ +# ---------------------------------------------------------------------------- +#Copyright 2024 CEA* +#*Commissariat a l'Energie Atomique et aux Energies Alternatives (CEA) +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. +#[END OF HEADER] +# ---------------------------------------------------------------------------- +# Description : Simulation log file scan script. +################################################################################ + +=encoding utf8 + +=head1 NAME + +scanlogs - parser for log files + +=head1 SYNOPSIS + + scanlogs [-pat pattern_file ] [-waiver waiver_file ] [ -format txt|xml|csv ] -out [outfile] [-nowarn ] [-nopreresetwarn] [-notime] [-noendcheck] [-listwarnings] [-listerrors] [-nogroup] [-suppresswaived] [-suppressprereset] logfile1 [ logfile2 ... ] + +=head1 DESCRIPTION + +=head2 Overview + +In a regressionable work environment, it is important that the pass/fail status +of tasks can be assessed in an automated fashion. In complex environments, +there may be many sources of errors or warnings and these may generate messages +with different formats (UVM messages, legacy messages, model specific messages, +timing violations, etc.). The purpose of this script is to be able to read one +(or multiple) log files and to extract the warning and error messages. The +script will return a pass (zero) status if no errors or warnings are detected. +Otherwise, it will return a non-zero status corresponding to the number of +error or warning messages. + +The script can also check for timing violations in the log file and fail +the test if one occurs. Often, one only wants to consider timing violations +that occur after reset has been de-asserted. If the user defines a message +to indicate the time of reset, then only those timing violations after reset +are considered. If no reset message is detected, then all timing violations +are considered. + +It is essential not to produce false positives. For this reason, this script +will always check for an explicit message indicating that the job has +completed. This helps ensure that a false positive is not produced if +a truncated (or potentially empty) log file is provided as input. + +The script has a set of default rules which are used to identify warnings, +errors and successful test completion. These rules can be customized using +configuration files. The user specifies the name of the pattern configuration +file with the '-pat' command line option. + +In some cases, it is helpful to provide a list of waivers, for known and +accepted warnings. An optional waiver file can be provided on the command line +with the '-waiver' option. + +The user can provide one or multiple log files. These files are parsed and +a summary is produced. By default, a text summary is output to stdout. +Alternatively, the summary can be output in a csv or XML form (not yet +implemented). Command line options can be abbreviated, as long as they are not +ambiguous. + +In a randomized simulation environment, it is common for the same test to be +run with different random seeds. The script can extract the seed (using +a defined pattern). If no SEED is found, the seed information is not displayed. + +=head1 OPTIONS + +=over + +=item B<-h> + +Displays man page. + +=item B<-pat pattern_file> + +Specify an input pattern file. Multiple input pattern files can be +specified. Details about the format of the pattern file are provided +later. + +=item B<-waiver waiver_file> + +Specify an input waiver file. Multiple input waiver files can be +specified. Details about the format of the waiver file are provided +later. + +=item B<-att attribute_file> + +Specify an input attribute file. Multiple attribute files can be +specified. Details about the format of the attribute file are provided +later. + +=item B<-format txt|xml|csv> + +Specify the output format for the report. Default is txt. + +=item B<-out outfile> + +Specify the name of an output file. Default is to output to stdout. + +=item B<-nowarn> + +By default, if there are any warnings, the return status is non-zero. If +this option is specified, warnings will not affect the output status. + +=item B<-nopreresetwarn> + +By default, if there are any warnings, the return status is non-zero. If +this option is specified, warnings that occur before the reset message +will not cause the test to fail. + +=item B<-notime> + +By default, if there are any timing violations, the return status is non-zero. If +this option is specified, timing violations will not affect the output status. + +=item B<-noendcheck> + +By default, a fail status is returned, if the the log does not have an +explicit end of test message. This is to avoid truncated logs producing a +pass status. Using this option, the check for the end message can be +surpressed. + +=item B<-silent> + +Even when an output file is specified, the script outputs some information +to the console. Using this option, all messages can be surpressed. + +=item B<-listwarnings> + +When this option is specified, the report will include a list of all the +warnings that were found, including in which file, the line number and +whether it was wavied. + +=item B<-listerrors> + +When this option is specified, the report will include a list of all the +errors that were found, including in which file, the line number and +whether it was wavied. + +=item B<-nogroup> + +By default, the warnings and error messages are grouped and a table is +output showing the number of messages corresponding to each group. This +table can be suppresseed using this option. + +=item B<-suppresswaived> + +This option applies when the -listerrors or -listwarnings is used. By +default these options will list all the errors/warnings, including those +that were waived. With the additional -suppresswaived option, only those +errors/warnings that were not waived are displayed. + +=item B<-suppressprereset> + +This option applies when the -listwarnings is used. By default this option will +list all the warnings, including those that were prior to reset. With the +additional -suppressprepreset option, only those warnings that are after reset +are displayed. + +=back + +=head1 EXIT STATUS + +An exit status of 0 indicates the log has passed. +A non-zero positive exit status indicates the log has failures and the value is +the total number of errors and warnings (plus 1 if the end of test +check has failed). If the script fails to actually read and process the +pattern or log files, it returns the exit value 255. + +=head1 PATTERN FILE FORMAT + +A pattern file is simply a CSV file with two columns. The first column +specifies the type of pattern +(ERROR|ERROR_NUM|WARNING|WARNING_NUM|EOT|SEED|GROUP). The second column +contains a perl regular expression. For the ERROR, WARNING and EOT types, the +script simply checks whether the line matches or not. For the ERROR_NUM, +WARNING_NUM, SEED and GROUP types, it is expected that the regular expression +contains a capture group that extracts an argument. For example the log might +contain a line such as "NUMER ERRORS : 3". The regular expression could extract +the numeric value '3'. When scanning a log, the total number of errors (or +warnings) is summed. Thus for a log file to pass, all the ERROR_NUM messages +must report zero errors. + +The GROUP pattern has a third argument which is the name of the grouping. For +example, if we group all the messages from a given tool (simulator...), this +could be the name of the tool. For example, a rule to group all messages from +vsim, might look like "GROUP,(vsim-[0-9]+),QUESTA_SIMULATE". + +=head1 WAIVER FILE FORMAT + +A waiver file is simply a CSV file with two columns. The first column +specifies the type of waiver (NOERROR|NOWARNING). The text after the column +serves as a reg-ex. If a given line in a log file first matches an +error or a warning pattern, then the script checks if it matches a waiver +pattern. If it matches a waiver pattern, then the warning or error is +not reported. At the end of a scan, the number of waivers is reported. + +=head1 ATTRIBUTE FILE FORMAT + +An attribute file is a CSV file with 5 columns. The first column contains +either the keyword 'ATT' or 'META'. Lines with 'ATT' specify an attribute, +which is a raw value that is extracted from a log file. Lines with 'META' +specify 'meta-attribute'. A meta-attribute is an expression based on +attributes. For example, a meta-atribute might the ratio between two attributes. + +The second column contains a name for the attribute. This is the name that +is displayed in the column header in the final report. Generally, it should +be kept relatively short, otherwise the resulting table can become too wide. + +The thid column contains a type (s=string,d=decimal, f=float,t=time h:m:s). +The fourth column contains a regular expression for detecting the attribute in +a log file. Finally, the fifth column contains a format string which, if +present, is used with sformatf to print the result. For example, a %3.1f +specifier might be used to indicate that only one decimal point should be +printed. + +=cut + +use Getopt::Long qw(:config no_auto_abbrev no_ignore_case); +use Pod::Usage; +use strict; +use Text::Table; +use warnings; +# use Switch; # not using switch, appears not to be installed + +$| = 1 ; # force flushing of stdout + +# ########################################################################### +# Subroutine prototypes +# ########################################################################### +sub text_report($ ); +sub csv_report( ); +sub string_repeat($$); +sub extract_common_prefix(@); +sub extract_common_suffix(@); + +# ########################################################################### +# # Declare Global Variables +# ########################################################################### + +# Command options +my @pat_files_A = ( ); +my @att_files_A = ( ); +my @waiver_files_A = ( ); +my $format = "TXT"; # outputf format +my $outfile = ""; # default to STDOUT +my $noendcheck = 0; # don't fail if missing EOT +my $nowarn = 0; # don't fail if there are warnings +my $list_warning = 0; # list all the warnings +my $list_error = 0; # list all the errors +my $nopreresetwarn = 0; # don't fail if there are warnings pre reset +my $notime = 0; # don't fail if there are timing violations +my $no_groups = 0; # flag to suppresss group by table report +my $suppresswaived = 0; # flag to prevent waived errors/warnings being reported +my $suppressprereset = 0; # flag to prevent waived errors/warnings being reported +my $help_flag = 0; +my @log_files_A = ( ); + +my $debug = 0; +my $silent = 0; + +# Internal data structures +my @pattern_DB_AoH = ( ); # Array containging patterns. Each entry contains a + # hash with keys REGEXP|TYPE. REGEXP is a regexp + # to match against. + # TYPE is "ERROR|ERROR_NUM|SEED|WARNING|WARNING_NUM|EOT|RESET|GROUP + +my @waiver_DB_AoH = ( ); # Array containging waiver patterns. Each entry contains a + # hash with keys REGEXP|TYPE. REGEXP is a regexp + # to match against. + # TYPE is "NOERROR|NOWARNING" + +my @attribute_DB_AoH = ( ); # Array containing patterns. Each entry contains a + # hash with keys NAME|TYPE|REGEXP + +my %present_attributes_H; # Hash which is indexed by attribute NAMEs. An + # entry is present, if that attribute has been + # seen. Used to avoid printing attributes that + # were not see in the current logs + +my %found_attributes_DB_HoH; # Holds the attributes that were found + # First index is the log file name + # Second Index is {ATT_NAME} + +my @valid_types_A = "GROUP|ERROR|ERROR_NUM|WARNING|SEED|WARNING_NUM|EOT|RESET|TIMING"; +my @valid_waiver_types_A = "NOERROR|NOWARNING"; +my @valid_attribute_types_A = "ATT|META"; +my @valid_attribute_formats_A = "s|d|f|t"; + +my %msg_DB_HoH; # Holds summary of results after parsing each log file + # + # First index is the filename + # Second index is {ERROR|SEED|WARNING|EOT} + +my %group_DB_HoH; # First Index is filename + # Second is Group Name ( eg. QUESTA_MSG) + # Third is Key (eg. vlog-1234) + # Content is the count + +# General behaviour +my $dot_interval = 1000; # One dot on the STD out after this many lines of log file +my $debug_prefix = "<<< DEBUG >>> "; +my $warning_prefix = "*** WARNING ***"; +my $numeric_RE = "[0-9]+"; +my $max_log_file_name_len = 0; +my $common_length_start_log_file_names = 0; # part at start of log file names + # that is common and thus not printed +my $common_length_end_log_file_names = 0; # same and end of file name + +# Summary variables +my $total_errors=0; # total errors in all logs +my $total_waived_errors=0; # total errors wavied in all logs +my $total_warnings=0; # total warnings in all logs +my $total_waived_warnings=0; # total warnings in all logs +my $total_timing=0; # total timing violations in all logs +my $total_seed = 0; # total # of seed messages +my $missing_eot=0; # total logs missing an EOT +my $final_ret_value = 255; # +my $num_passing_tests = 0; # Count how many tests passed +my %warnings_DB_HoHoH; # Stores all the warning lines. Indexed by (i) file (ii) line number (iii) WAIVED|TEXT-> Contains the warning text +my %errors_DB_HoHoH; # Stores all the error lines. Index by (i) file (ii) line number, (iii) WAIVED|TEXT + +# File handle for output +my $outfile_FH; + +# ########################################################################### +# Read Command Options +# ########################################################################### +GetOptions( + 'pat=s' => \@pat_files_A, + 'waiver=s' => \@waiver_files_A, + 'att=s' => \@att_files_A, + 'format=s' => \$format, + 'out=s' => \$outfile, + 'noendcheck' => \$noendcheck, + 'listwarnings' => \$list_warning, + 'listerrors' => \$list_error, + 'suppresswaived' => \$suppresswaived, + 'suppressprereset' => \$suppressprereset, + 'nowarn' => \$nowarn, + 'nopreresetwarn' => \$nopreresetwarn, + 'help' => \$help_flag, + 'debug' => \$debug, + 'nogroup' => \$no_groups, + 'silent' => \$silent, + ) or pod2usage( -verbose => 1, -exitval => 1); + if ($help_flag) { pod2usage( -verbose => 3, -exitval => 0); }; + +# Basic checks on arguments +$format = uc($format); +if ( ( $format ne "TXT" ) && ( $format ne "CSV" ) && ( $format ne "XML" ) ) { + proc_err("Invalid output file format : $format"); +} + +# Check there is at least one log file (no vacuous pass!) +if ( $#ARGV < 0 ) { + proc_err( "No log files specified." ); +} + +# ########################################################################### +# Read the pattern files +# ########################################################################### +foreach my $pat_file ( @pat_files_A ) { + my $l=0; # line counter for error messages + open (my $FH,"<", $pat_file ) or proc_err("Unable to open pattern file $pat_file"); + while (<$FH>) { + $l++; + next if (/^#/); + next if (/^$/); + my @csv_fields = split(","); + my $type = $csv_fields[0]; + my $regexp = $csv_fields[1]; + my $group_name; + if ( grep ( /$type/, @valid_types_A ) != 1 ) { + proc_err("Invalid pattern type ($type) at line $l of file $pat_file.\n$_" ); + } + chomp $regexp; + if ( $regexp eq "" ) { + proc_err( "Regular expression ($regexp) at line $l of file $pat_file is empty" ); + } + if ( $type eq "GROUP" ) { + $group_name = $csv_fields[2]; + chomp $group_name; + if ( !defined $group_name ) { + proc_err("Group pattern at line $l of file $pat_file is missing a group name.\n$_"); + } + } + my %rec; + $rec{TYPE} = $type; + $rec{REGEXP} = $regexp; + $rec{GROUPNAME}= $group_name; + $rec{FILE} = $pat_file; # for debug only + $rec{LINE_NUM} = $l; # for debug only + push @pattern_DB_AoH, { %rec }; + if ( $debug ) { + print $debug_prefix, "Reading pattern of type $type in $pat_file with this pattern : $regexp \n"; + } + } + close $FH; +} + +# ########################################################################### +# Read the waiver files +# ########################################################################### +foreach my $waiver_file ( @waiver_files_A ) { + my $l=0; # line counter for error messages + open (my $FH,"<", $waiver_file ) or proc_err("Unable to open pattern file $waiver_file"); + while (<$FH>) { + $l++; + next if (/^#/); + next if (/^$/); + my ( $type, $regexp ) = split(","); + if ( grep ( /$type/, @valid_waiver_types_A ) != 1 ) { + proc_err("Invalid pattern type ($type) at line $l of file $waiver_file.\n$_" ); + } + chomp $regexp; + if ( $regexp eq "" ) { + proc_err( "Regular expression ($regexp) at line $l of file $waiver_file is empty" ); + } + my %rec; + $rec{TYPE} = $type; + $rec{REGEXP} = $regexp; + $rec{FILE} = $waiver_file; # for debug only + $rec{LINE_NUM} = $l; # for debug only + push @waiver_DB_AoH, { %rec }; + if ( $debug ) { + print $debug_prefix, "Reading pattern of type $type in $waiver_file with this pattern : $regexp \n"; + } + } + close $FH; +} + +# ########################################################################### +# Read the attributes files +# ########################################################################### +foreach my $att_file ( @att_files_A ) { + my $l=0; # line counter for error messages + open (my $FH,"<", $att_file ) or proc_err("Unable to open pattern file $att_file"); + while (<$FH>) { + $l++; + next if (/^#/); + next if (/^$/); + my ( $att_type, $att_name, $type, $regexp, $format_string ) = split(","); + if ( grep ( /$att_type/, @valid_attribute_types_A ) != 1 ) { + proc_err("Invalid attribute type ($type) at line $l of file $att_file.\n$_" ); + } + if ( grep ( /$type/, @valid_attribute_formats_A ) != 1 ) { + proc_err("Invalid attribute format ($type) at line $l of file $att_file.\n$_" ); + } + chomp $regexp; + if ( $regexp eq "" ) { + proc_err( "Regular expression ($regexp) at line $l of file $att_file is empty" ); + } + if ( ! defined $format_string ) { $format_string = ""; } + my %rec; + $rec{ATT_TYPE} = $att_type; + $rec{ATT_NAME} = $att_name; + $rec{TYPE} = $type; + $rec{REGEXP} = $regexp; + $rec{FORMAT} = $format_string; + $rec{FILE} = $att_file; # for debug only + $rec{LINE_NUM} = $l; # for debug only + push @attribute_DB_AoH, { %rec }; + if ( $debug ) { + print $debug_prefix, "Reading attribute of type $type in $att_file with this pattern : $regexp \n"; + } + } + close $FH; +} + +# ########################################################################### +# Install some default patterns, if no pattern file provided. +# +# These patterns are very broad and might trigger on +# ########################################################################### +if ( $#pattern_DB_AoH eq -1 ) { + @pattern_DB_AoH = ( { + TYPE => "ERROR", + REGEXP => ".*[Ee][Rr][Rr][Oo][Rr]", + BUILTIN => 1, + }, + { + TYPE => "WARNING", + REGEXP => ".*[Ww][Aa][Rr][Nn]", + BUILTIN => 1, + }, + { + TYPE => "EOT", + REGEXP => ".*[Cc][Oo][Mm][Pp][Ll][Ee][Tt][Ee]", + BUILTIN => 1, + }, + { + TYPE => "SEED", + REGEXP => "SEED=([0-9]+)", + BUILTIN => 1, + }, + { + TYPE => "RESET", + REGEXP => "RESET_DONE", + BUILTIN => 1, + }, + { + TYPE => "TIMING", + REGEXP => "Timing Violation", + BUILTIN => 1, + }, + ); +} + +# ########################################################################### +# For debug - dump all the patterns +# ########################################################################### +if ( $debug ) { + my $n=0; + foreach my $pat_href ( @pattern_DB_AoH ) { + print "$debug_prefix Pattern #$n (",$pat_href->{TYPE},") = ", $pat_href->{REGEXP}; + if ( $pat_href->{BUILTIN} ) { + print " (default pattern)"; + } else { + print ". Found on line ",$pat_href->{LINE_NUM}, " of ", $pat_href->{FILE}; + } + print "\n"; + $n++; + } + $n=0; + foreach my $pat_href ( @waiver_DB_AoH ) { + print "$debug_prefix Waiver Pattern #$n (",$pat_href->{TYPE},") = ", $pat_href->{REGEXP}; + if ( $pat_href->{BUILTIN} ) { + print " (default pattern)"; + } else { + print ". Found on line ",$pat_href->{LINE_NUM}, " of ", $pat_href->{FILE}; + } + print "\n"; + $n++; + } +} + +# ########################################################################### +# Read and Parse the log files +# ########################################################################### +if ( $#ARGV > 0 ) { + $common_length_start_log_file_names = extract_common_prefix( @ARGV ); + $common_length_end_log_file_names = extract_common_suffix( @ARGV ); +} +foreach my $log ( @ARGV ) { + open (my $FH,"<", $log) or proc_err( "Unable to open $log for reading."); + if (!$silent) { + print "Scanning $log"; + if ( $debug ) { print "\n"; } + } + + my $l=0; # line counter + + my $num_errors = 0; # no. errors in current log + my $num_pre_reset_warnings = 0; # no. pre-reset warnings in current log + my $num_post_reset_warnings = 0; # no. post-reset warnings in current log + my $num_pre_reset_timing = 0; # no. pre-reset timing viol in current log + my $num_post_reset_timing = 0; # no. post-reset timing viol in current log + my $num_timing = 0; # either all timing violns or jus post-reset + my $num_warnings = 0; # either all warnings or jus post-reset + my $num_waived_warnings = 0; # number of warnings that were waived + my $num_waived_errors = 0; # number of errors that were waived + my $got_eot = 0; # check we got an EOT message + my $seed = ""; + + my $got_bad_msg = 0 ; # indicates badly formated ERROR_NUM, WARNING_NUM + my $reset_done_flag = 0; + my $bad_msg_line; + + while (<$FH>) { + + + $l++; + my $logit=0; # something matched ; print it in debug mode + + my $waive_error_on_this_line = 0; + my $waive_warning_on_this_line = 0; + + my %groups_H; + + # First check if the line matches a waiver + foreach my $pat_href ( @waiver_DB_AoH ) { + my $regex = $pat_href->{REGEXP} ; + my $type = $pat_href->{TYPE} ; + + if (/$regex/) { + if ( $type eq "NOWARNING" ) { $waive_warning_on_this_line = 1; } + if ( $type eq "NOERROR" ) { $waive_error_on_this_line = 1; } + } + } + + # Second - check for groups + foreach my $pat_href ( @pattern_DB_AoH ) { + my $regex = $pat_href->{REGEXP} ; + my $type = $pat_href->{TYPE} ; + + if ( $type eq "GROUP" ) { + my $group_name = $pat_href->{GROUPNAME}; + if (/$regex/) { + if ( !( defined $1 ) ) { + print "\n $warning_prefix At line $l of $log, found a group expression ($regex) which didn't yield a valid string.\n"; + } else { + $groups_H{$group_name} = $1; + } # endif + } # endif + } # endif + } # endforeach + + + # Now check if the line matches a warning/error/eot/seed pattern + foreach my $pat_href ( @pattern_DB_AoH ) { + my $regex = $pat_href->{REGEXP} ; + my $type = $pat_href->{TYPE} ; + + if ( $type ne "GROUP" ) { + if (/$regex/) { + + if ( $type eq "TIMING" ) { + $logit=1; # for debug only + if ( $reset_done_flag ) { + $num_post_reset_timing++; + } else { + $num_pre_reset_timing++; + } + } elsif ( $type eq "ERROR" ) { + $logit=1; # for debug only + $errors_DB_HoHoH{$log}{$l}{TEXT} = $_; + if ( $waive_error_on_this_line ) { + $errors_DB_HoHoH{$log}{$l}{WAIVED} = 1; + $num_waived_errors++; + } else { + $errors_DB_HoHoH{$log}{$l}{WAIVED} = 0; + $num_errors +=1; + # Record which groups this error belonged to + foreach my $group ( keys %groups_H ) { + if ( defined $group_DB_HoH{$log}{ $group }{ $groups_H{$group} } ) { + $group_DB_HoH{$log}{ $group }{ $groups_H{$group} }++; + } else { + $group_DB_HoH{$log}{ $group }{ $groups_H{$group} }=1; + } + } + } + } elsif ( $type eq "ERROR_NUM" ) { + $logit=1; # for debug only + my $num_err = $1; + if ( ( ! defined $1 ) || ! ( $1 =~ $numeric_RE ) ) { + $num_errors++; + $got_bad_msg = 1; + $bad_msg_line = $l; + } else { + $num_errors += $num_err; + } + } elsif ( $type eq "WARNING" ) { + $logit=1; # for debug only + $warnings_DB_HoHoH{$log}{$l}{TEXT} = $_; + $warnings_DB_HoHoH{$log}{$l}{POSTRESET} = $reset_done_flag; + if ( $waive_warning_on_this_line ) { + $warnings_DB_HoHoH{$log}{$l}{WAIVED} = 1; + $num_waived_warnings++; + } else { + $warnings_DB_HoHoH{$log}{$l}{WAIVED} = 0; + if ( $reset_done_flag ) { + $num_post_reset_warnings +=1; + } else { + $num_pre_reset_warnings +=1; + } + # Record which groups this warnings belonged to + if ( ($reset_done_flag) || ( !($nopreresetwarn) ) && (!$nowarn) ) { + foreach my $group ( keys %groups_H ) { + if ( defined $group_DB_HoH{$log}{ $group }{ $groups_H{$group} } ) { + $group_DB_HoH{$log}{ $group }{ $groups_H{$group} }++; + } else { + $group_DB_HoH{$log}{ $group }{ $groups_H{$group} }=1; + } + } + } + } + } elsif ( $type eq "WARNING_NUM" ) { + $logit=1; # for debug only + my $num_warn = $1; + if ( ( ! defined $1 ) || ! ( $1 =~ $numeric_RE ) ) { + $num_warnings++; + $got_bad_msg = 1; + $bad_msg_line = $l; + } else { + if ( $reset_done_flag ) { + $num_post_reset_warnings += $num_warn; + } else { + $num_pre_reset_warnings += $num_warn; + } + } + } elsif ( $type eq "EOT" ) { + $logit=1; # for debug only + $got_eot = 1; + } elsif ( $type eq "SEED" ) { + $logit=1; # for debug only + if ( defined $1 ) { + $seed = $1; + $total_seed++; + } + } elsif ( $type eq "RESET" ) { + $logit=1; # for debug only + $reset_done_flag = 1; + } + } # endif REGEXP + } # endif !GROUP + } # end foreach + + # Now check if the line matches an attribute + foreach my $att_href ( @attribute_DB_AoH ) { + my $att_type = $att_href->{ATT_TYPE} ; + my $regex = $att_href->{REGEXP} ; + my $type = $att_href->{TYPE} ; + my $att_name = $att_href->{ATT_NAME} ; + + # If it's an attribute - search for the regexp in this line + if ( $att_type eq "ATT" ) { + if (/$regex/) { + my $att_value = $1; + if (defined $att_value) { + if ( $debug ) { + print $debug_prefix, "Matched attribute $att_name = $att_value at line $l\n"; + } + } + $found_attributes_DB_HoH{$log}{$att_name} = $att_value; + $present_attributes_H{$att_name} = $type; + } + } + } + + # Timing violations + # Safety check - if there are timing violations but no reset indication - fail the test + if ($reset_done_flag ) { + $num_timing = $num_post_reset_timing; + } else { + $num_timing = $num_pre_reset_timing + $num_pre_reset_timing; + } + + # Safety check - if there are timing violations but no reset indication - fail the test + if ( ($reset_done_flag) && ( $nopreresetwarn ) ) { + $num_warnings = $num_post_reset_warnings; + } else { + $num_warnings = $num_pre_reset_warnings + $num_post_reset_warnings; + } + + if ( $debug ) { + if ( $logit ) { + print "$debug_prefix : Line $l in $log matched."; + print "ERRORS=$num_errors ( $num_waived_errors were wavied ).\n"; + print "PRE RESET WARNINGS=$num_pre_reset_warnings. POST RESET WARNINGS=$num_post_reset_warnings. $num_waived_warnings were waived.\n"; + print "EOT=$got_eot.\n"; + print "PRE RESET TIMING=$num_pre_reset_timing. POST RESET TIMING=$num_post_reset_timing\n"; + print "RESET_DONE=$reset_done_flag. NUM_TIMING=$num_timing\n"; + print "GOT BAD MSG=$got_bad_msg. SEED=$seed\n" + } + } else { + # Print dots to show progress for long log files + if ( ( $l % $dot_interval ) == 0 ) { + print "."; + } + } + } + close $FH; +if(!$silent) { + print "\n"; + } + + # If there was a message with a number of errors/warnings that could not + # be parsed cleanly - report it now + if ( $got_bad_msg ) { + print "$warning_prefix : On line $bad_msg_line of $log "; + print "unable to determine number of messages - assuming 1.\n"; + } + + # Store results for current file in the DB + $msg_DB_HoH{$log}{ERROR} = $num_errors; + $msg_DB_HoH{$log}{WAIVED_ERROR} = $num_waived_errors; + $msg_DB_HoH{$log}{WARNING} = $num_warnings; + $msg_DB_HoH{$log}{WAIVED_WARNING} = $num_waived_warnings; + $msg_DB_HoH{$log}{TIMING} = $num_timing; + $msg_DB_HoH{$log}{EOT} = $got_eot; + $msg_DB_HoH{$log}{SEED} = $seed; + +} + +# ########################################################################### +# Compute the META Attributes +# ########################################################################### +foreach my $log ( @ARGV ) { + foreach my $att_href ( @attribute_DB_AoH ) { + if ( $att_href->{ATT_TYPE} eq "META" ) { + my $expression = $att_href->{REGEXP} ; + my $type = $att_href->{TYPE} ; + my $att_name = $att_href->{ATT_NAME} ; + my $format = $att_href->{FORMAT} ; + + my $vars_defined = 1; + + # Perform variable substitution + while ( $expression =~ /\$([A-Za-z_]+)/ ) { + my $var_name = $1; + my $var_type = $present_attributes_H{$var_name}; + if ( !defined $var_type ) { $var_type = "d"; } + my $value = $found_attributes_DB_HoH{$log}{$var_name}; + if ( defined $value ) { + if ( $var_type eq "t" ) { + $value = time2sec($value); + } + $expression =~ s/\$$var_name/$value/g; + } else { + $expression =~ s/\$$var_name/0/g; + $vars_defined = 0; + } + } + # Only evaluate if all variables were defined + if ( $vars_defined ) { + my $raw_result = eval($expression); + my $formatted_result = $raw_result; + # If a format specified was defined, apply it now + if ( ( defined $raw_result ) && ( defined $format ) ) { + $formatted_result = sprintf($format,$raw_result); + } + $present_attributes_H{$att_name} = 1; + $found_attributes_DB_HoH{$log}{$att_name} = $formatted_result; + } + } + } +} + +# ########################################################################### +# Count total errors and check that each test got an EOT +# ########################################################################### +foreach my $log ( @ARGV ) { + my $status = "FAIL"; # start by assuming test failed + + # Track length of longest log file name to format report + if ( length ($log) > $max_log_file_name_len ) { + $max_log_file_name_len = length($log); + } + + if ( ( $msg_DB_HoH{$log}{ERROR} == 0 ) && + ( $nowarn || ( $msg_DB_HoH{$log}{WARNING} == 0 ) ) && + ( $notime || ( $msg_DB_HoH{$log}{TIMING} == 0 ) ) && + ( $noendcheck || ( $msg_DB_HoH{$log}{EOT} != 0 ) ) ) { + $status = "PASS"; + $num_passing_tests++; + } + $total_errors += $msg_DB_HoH{$log}{ERROR}; + $total_waived_errors += $msg_DB_HoH{$log}{WAIVED_ERROR}; + $total_timing += $msg_DB_HoH{$log}{TIMING}; + $total_warnings += $msg_DB_HoH{$log}{WARNING}; + $total_waived_warnings += $msg_DB_HoH{$log}{WAIVED_WARNING}; + $missing_eot++ unless ( $msg_DB_HoH{$log}{EOT} > 0 ) ; + $msg_DB_HoH{$log}{STATUS} = $status; +} + +# ########################################################################### +# Compute final status +# ########################################################################### +$final_ret_value = $total_errors; # always report errors +$final_ret_value += $total_warnings unless $nowarn; # normally include warnings +$final_ret_value += $total_timing unless $notime; # normally include warnings +$final_ret_value += $missing_eot unless $noendcheck; # and missing eots +if ( $final_ret_value > 255 ) { $final_ret_value = 255; } # don't try return values >255 + +# ########################################################################### +# Report Generation +# ########################################################################### + +# Open the outputfile ( or point to STDOUT) +if ( $outfile ) { + open ($outfile_FH,">", $outfile ) or + proc_err("Unable to open output file $outfile for writing"); +} else { + $outfile_FH = *STDOUT; +} + +# Create a summary +my $final_summary = "Scanned " . ($#ARGV+1) . " log file(s).\n" . + "Number passing logs : $num_passing_tests\n\n" . + + "Total number of errors : $total_errors" . + ( ( $total_waived_errors > 0 ) ? " ($total_waived_errors more were waived)\n" : "\n" ) . + "Total number of warnings : $total_warnings" . + ( ( $total_waived_warnings > 0 ) ? " ($total_waived_warnings more were waived)\n" : "\n" ) . + "Total number of timing violns : $total_timing\n" . + "Total missing end of test mgs : $missing_eot\n"; + +if ( $outfile ne "" ) { + $final_summary .= "Wrote detailed report to $outfile"; +} + +if ( $format eq "TXT" ) { + if (!$silent) { + text_report( $final_summary ); + } +} elsif ( $format eq "CSV" ) { + csv_report( ); +} else { + proc_error("XML report not yet implemented"); +} +print STDERR $final_summary unless $outfile eq ""; + +# ########################################################################### +# Banner Output +# ########################################################################### +if (!$silent) { + if ( $final_ret_value != 0 ) { + banner_fail(); + } else { + banner_pass(); + } +} +# ########################################################################### +# That's it +# ########################################################################### +exit($final_ret_value); + +# ########################################################################### +# ########################################################################### +# Subroutines +# ########################################################################### +# ########################################################################### + +# ########################################################################### +# sub proc_err +# +# Report a fatal error msg and exit. +# ########################################################################### +sub proc_err { + my $msg = shift; + + + print STDERR "*** ERROR scanlogs failed ***\n"; + print STDERR $msg,"\n"; + exit(255); +} + +# ########################################################################### +# sub banner_pass +# ########################################################################### +sub banner_pass { + print STDERR "\n\n"; + print STDERR " PPPPP AAAA SSSS SSSS \n"; + print STDERR " P P A A S S S S \n"; + print STDERR " P P A A S S \n"; + print STDERR " PPPPP AAAAAA SS SS \n"; + print STDERR " P A A S S \n"; + print STDERR " P A A S S S S \n"; + print STDERR " P A A SSSS SSSS \n"; +} + +# ########################################################################### +# sub banner_fail +# ########################################################################### +sub banner_fail { + print STDERR "\n\n"; + print STDERR " FFFFFF AAAA III L \n"; + print STDERR " F A A I L \n"; + print STDERR " F A A I L \n"; + print STDERR " FFFF AAAAAA I L \n"; + print STDERR " F A A I L \n"; + print STDERR " F A A I L \n"; + print STDERR " F A A III LLLLL \n"; +} + + +# ########################################################################### +# sub text_report +# ########################################################################### +sub text_report( $ ) { + my $final_summary = shift; + + # Figure out width of the column for the log file names + my $log_col_width = $max_log_file_name_len + - $common_length_start_log_file_names + - $common_length_end_log_file_names; + + if ( $log_col_width < 20 ) { $log_col_width = 20; } + + my $total_width = $log_col_width + 20; + + Text::Table->warnings('on'); + + my @headings_A = ( + \'|', + { title => "Log file", + align => "left", + align_title => "center" }, + \'|' ); + if ( $total_seed > 0 ) { + push @headings_A, ( + { title => "Seed", + align => "center", + align_title => "center" }, + \'|', + ) + }; + push @headings_A, ( + { title => "Errors", + align => "center", + align_title => "center" }, + \'|', + { title => "Warnings", + align => "center", + align_title => "center" }, + \'|' ); + + if ( $total_timing > 0 ) { + push @headings_A, ( + { title => "Timing\nViolations", + align => "center", + align_title => "center" }, + \'|', + ); + } + push @headings_A, ( + { title => "EOT", + align => "center", + align_title => "center" }, + \'|', + { title => "STATUS", + align => "center", + align_title => "center" }, + \'|' ); + foreach my $att ( sort keys %present_attributes_H ) { + push @headings_A, ( + { title => $att, + align => "center", + align_title => "center" }, + \'|' ); + } + + my $tb = Text::Table->new( @headings_A ); + + foreach my $log ( @ARGV ) { + my @record_A; + my $log_to_print = substr($log, $common_length_start_log_file_names ); + + $log_to_print = substr( $log_to_print, 0, $log_col_width ); + push @record_A, $log_to_print; + + if ( $total_seed > 0 ) { + if ( $msg_DB_HoH{$log}{SEED} ne "" ) { + push @record_A, $msg_DB_HoH{$log}{SEED}; + } else { + push @record_A, ""; + } + } + + # Print errors and possibly waived errors + my $err_string = $msg_DB_HoH{$log}{ERROR}; + if ( $msg_DB_HoH{$log}{WAIVED_ERROR} > 0 ) { + $err_string .= sprintf("(%dW)", $msg_DB_HoH{$log}{WAIVED_ERROR} ); + } + push @record_A, $err_string; + + # Print warnings and possibly waived warnings + my $warn_string = $msg_DB_HoH{$log}{WARNING}; + if ( $msg_DB_HoH{$log}{WAIVED_WARNING} > 0 ) { + $warn_string .= sprintf("%7s", sprintf("(%dW)", $msg_DB_HoH{$log}{WAIVED_WARNING} ) ) ; + } + push @record_A, $warn_string; + + if ($total_timing > 0 ) { + push @record_A, $msg_DB_HoH{$log}{TIMING}; + } + + push @record_A, ( $msg_DB_HoH{$log}{EOT} ? "YES" : "NO" ); + + push @record_A, $msg_DB_HoH{$log}{STATUS}; + + + foreach my $att ( sort keys %present_attributes_H ) { + my $att_value; + $att_value = $found_attributes_DB_HoH{$log}{$att}; + push @record_A, $att_value; + } + + $tb->add( @record_A ); + } + + # Output the table + print $outfile_FH "\n"; + print $outfile_FH $tb->rule( '-','+'); + print $outfile_FH $tb->title; + print $outfile_FH $tb->rule( '-','+'); + print $outfile_FH $tb->body; + print $outfile_FH $tb->rule( '-','+'); + print $outfile_FH "\n\n"; + + if ( $list_warning ) { + my %warning_report_H; # Temporarily hold the warning report - prior to sorting + + print $outfile_FH " START WARNING SUMMARY\n"; + foreach my $log_file ( keys %warnings_DB_HoHoH ) { + foreach my $line_num ( keys %{ $warnings_DB_HoHoH{$log_file} } ) { + + + my $t = $warnings_DB_HoHoH{$log_file}{$line_num}{TEXT}; + chomp $t; + my $w = $warnings_DB_HoHoH{$log_file}{$line_num}{WAIVED} ? "WAIVED" : "" ; + my $prereset = $warnings_DB_HoHoH{$log_file}{$line_num}{POSTRESET} ? "" : "PRE_RESET" ; + my $msg_prefix = " WARNING $w $prereset at $log_file ($line_num)"; + + # If suppresswaived flag is true - don't display waived messages + # If use doesn't want to see pre-reset warnings - skip them + if ( ( $w eq "" ) || ( !$suppresswaived ) ) { + if ( ( !$suppressprereset ) || ( $prereset ne "PRE_RESET" ) ) { + my $key = $log_file . sprintf("%07d", $line_num ); # sort report by file and then line numbe + $warning_report_H{$key} = sprintf("%-30s : %s\n", $msg_prefix, $t ); + } + } + } + } + # Sort and print the warning report + foreach my $k ( sort keys %warning_report_H ) { + print $outfile_FH $warning_report_H{$k}; + } + print $outfile_FH " END WARNING SUMMARY\n\n"; + } + + if ( $list_error ) { + my %error_report_H; # Temporarily hold the warning report - prior to sorting + + print $outfile_FH " START ERROR SUMMARY\n"; + foreach my $log_file ( keys %errors_DB_HoHoH ) { + foreach my $line_num ( keys %{ $errors_DB_HoHoH{$log_file} } ) { + my $t = $errors_DB_HoHoH{$log_file}{$line_num}{TEXT}; + chomp $t; + my $w = $errors_DB_HoHoH{$log_file}{$line_num}{WAIVED} ? "WAIVED" : "" ; + my $msg_prefix = " ERROR $w at $log_file ($line_num)"; + + # If suppresswaived flag is true - don't display waived messages + if ( ( $w eq "" ) || ( !$suppresswaived ) ) { + my $key = $log_file . sprintf("%07d", $line_num ); # sort report by file and then line numbe + $error_report_H{$key} = sprintf("%-30s : %s\n", $msg_prefix, $t ); + } + } + } + # Sort and print the error report + foreach my $k ( sort keys %error_report_H ) { + print $outfile_FH $error_report_H{$k}; + } + print $outfile_FH " END ERROR SUMMARY\n\n"; + } + + # Print report by group + if ( !$no_groups ) { + + my %unique_group_H; + my @unique_group_A; + my %unique_messages_group_HoH; # Indexed by group name, message + my @headings_A ; + + # Identify a unique list of groups and their messages + foreach my $log_i ( keys %group_DB_HoH ) { + foreach my $group_i ( keys %{ $group_DB_HoH{$log_i} } ) { + foreach my $msg ( keys %{ $group_DB_HoH{$log_i}{$group_i} } ) { + $unique_group_H{$group_i} = 1; + $unique_messages_group_HoH{$group_i}{ $msg } = 1; + } + } + } + + # Adding log file to headings + @headings_A = ( + \'|', + { title => "Log file", + align => "left", + align_title => "center" }, + \'|' ); + if ( $total_seed > 0 ) { + push @headings_A, ( + { title => "Seed", + align => "center", + align_title => "center" }, + \'|', + ) + }; + push @headings_A,\'|'; + + # Add List of groups to Headings + my $found_a_group_flag = 0; + @unique_group_A = sort keys %unique_group_H; + foreach my $group ( @unique_group_A ) { + foreach my $msg ( sort keys %{ $unique_messages_group_HoH{$group} } ) { + push @headings_A, { + title => ( $group . "\n" . $msg ), + align => "center", + align_title => "center" }; + push @headings_A,\'|'; + $found_a_group_flag = 1; + } + } + + if ( $found_a_group_flag ) { + my $group_tb = Text::Table->new( @headings_A ); + + # Add each row + foreach my $log ( @ARGV ) { + my @record_A; + my $log_to_print = substr($log, $common_length_start_log_file_names ); + my $num_groups = 0; # number of groups found for this row + + $log_to_print = substr( $log_to_print, 0, $log_col_width ); + push @record_A, $log_to_print; + + if ( $total_seed > 0 ) { + if ( $msg_DB_HoH{$log}{SEED} ne "" ) { + push @record_A, $msg_DB_HoH{$log}{SEED}; + } else { + push @record_A, ""; + } + } + + foreach my $group ( @unique_group_A ) { + foreach my $msg ( sort keys %{ $unique_messages_group_HoH{$group} } ) { + my $v = $group_DB_HoH{$log}{$group}{$msg}; + if ( !defined $v ) { $v = ""; } else { $num_groups++; } + push @record_A, $v; + } + } + + # Add the row to the table + if ($num_groups>0) { + $group_tb->add( @record_A ); + } + } + + # Output the table + print $outfile_FH "Summary by Message Group\n"; + print $outfile_FH "\n"; + print $outfile_FH $group_tb->rule( '-','+'); + print $outfile_FH $group_tb->title; + print $outfile_FH $group_tb->rule( '-','+'); + print $outfile_FH $group_tb->body; + print $outfile_FH $group_tb->rule( '-','+'); + print $outfile_FH "\n\n"; + } + + } + + print $outfile_FH $final_summary; +} + +# ########################################################################### +# sub csv_report +# +# Output a report in CSV format. +# ########################################################################### +sub csv_report( ) { + + print $outfile_FH "TEST NAME,SEED,NUM ERRORS,NUM WARNINGS,TIMING,MISSING EOT,STATUS\n"; + foreach my $log ( @ARGV ) { + print $outfile_FH $log,","; + print $outfile_FH $msg_DB_HoH{$log}{SEED},","; + print $outfile_FH $msg_DB_HoH{$log}{ERROR},","; + print $outfile_FH $msg_DB_HoH{$log}{WARNING},","; + print $outfile_FH $msg_DB_HoH{$log}{TIMING},","; + print $outfile_FH ( $msg_DB_HoH{$log}{EOT} ? "YES" : "NO" ),","; + print $outfile_FH $msg_DB_HoH{$log}{STATUS}; + print $outfile_FH "\n"; + } +} + +# ########################################################################### +# sub string_repeat +# +# Takes an input string and returns N copies concatenated together. +# ########################################################################### +sub string_repeat($$) { + my $s = shift; + my $n = shift; + my $r = ""; + while ($n>0) { $r .= $s; $n-- }; + return $r; +} + +# ########################################################################### +# sub extract_common prefix +# +# Takes in an array of strings. +# Returns the number of characters that are common at the start of all +# the strings - or -1 if there is no common prefix. +# ########################################################################### +sub extract_common_prefix(@) { + my @strings_A = @_; + + my $n=-1; + my $f=1; + + while ( $f ) { + $n++; + if ( length($strings_A[0]) <= $n ) { + $f=0; + } else { + my $c = substr($strings_A[0],$n,1); + for( my $i=1 ; ( ( $f ) && ( $i <= $#strings_A ) ) ; $i++ ) { + if ( substr($strings_A[$i], $n, 1 ) ne $c ) { $f=0; } + } + } + } + return $n; +} + + +# ########################################################################### +# sub extract_common suffix +# +# Takes in an array of strings. +# Returns the number of characters that are common at the start of all +# the strings - or -1 if there is no common prefix. +# ########################################################################### +sub extract_common_suffix(@) { + my @strings_A = @_; + + my $n=-1; + my $f=1; + + while ( $f ) { + $n++; + if ( length($strings_A[0]) <= $n ) { + $f=0; + } else { + my $c = substr($strings_A[0],-$n,1); + for( my $i=1 ; ( ( $f ) && ( $i <= $#strings_A ) ) ; $i++ ) { + if ( substr($strings_A[$i], -$n, 1 ) ne $c ) { $f=0; } + } + } + } + return $n-1; +} + +# ########################################################################### +# time2sec +# ########################################################################### +sub time2sec { + my $s = shift; + + $s =~ /([0-9]+):([0-9]+):([0-9]+)/; + my $hour = $1; + my $min = $2; + my $sec = $3; + if (!defined $hour) { $hour = 0; } + if (!defined $min ) { $hour = 0; } + if (!defined $sec ) { $hour = 0; } + return ($sec + 60*$min + 60*60*$hour); +} + + diff --git a/hw/vendor/hpdcache/rtl/tb/scripts/scan_patterns/build_patterns.pat b/hw/vendor/hpdcache/rtl/tb/scripts/scan_patterns/build_patterns.pat new file mode 100644 index 00000000..2fdc0a2e --- /dev/null +++ b/hw/vendor/hpdcache/rtl/tb/scripts/scan_patterns/build_patterns.pat @@ -0,0 +1,7 @@ +# ########################################################################### +# This file defines that patterns which are used to identify an error +# or a warning in a build log file. +# ########################################################################### +ERROR, error: +WARNING, warning: +EOT,BUILD FINISHED diff --git a/hw/vendor/hpdcache/rtl/tb/scripts/scan_patterns/run_attributes.pat b/hw/vendor/hpdcache/rtl/tb/scripts/scan_patterns/run_attributes.pat new file mode 100644 index 00000000..9e26b808 --- /dev/null +++ b/hw/vendor/hpdcache/rtl/tb/scripts/scan_patterns/run_attributes.pat @@ -0,0 +1,6 @@ +# ########################################################################### +# Simulation Attributes +# ########################################################################### + +ATT,WALL_TIME (sec),t,Simulation wall clock time (sec): ([0-9]+.[0-9]+) +ATT,SIM_SPEED (KHz),t,Simulation real frequency : ([0-9]+.[0-9]+) KHz diff --git a/hw/vendor/hpdcache/rtl/tb/scripts/scan_patterns/run_patterns.pat b/hw/vendor/hpdcache/rtl/tb/scripts/scan_patterns/run_patterns.pat new file mode 100644 index 00000000..58744063 --- /dev/null +++ b/hw/vendor/hpdcache/rtl/tb/scripts/scan_patterns/run_patterns.pat @@ -0,0 +1,13 @@ +# ########################################################################### +# This file defines that patterns which are used to identify an error +# or a warning in a build log file. +# ########################################################################### +ERROR,SB_ERROR +ERROR,SEQ_ERROR +ERROR,assertion +ERROR,Assertion +ERROR,Error: +ERROR,%Error: +ERROR,Fatal: +WARNING,SB_WARNING +EOT,^Finishing the simulation... diff --git a/hw/vendor/hpdcache/rtl/tb/scripts/scan_patterns/verilate_patterns.pat b/hw/vendor/hpdcache/rtl/tb/scripts/scan_patterns/verilate_patterns.pat new file mode 100644 index 00000000..8fdaa989 --- /dev/null +++ b/hw/vendor/hpdcache/rtl/tb/scripts/scan_patterns/verilate_patterns.pat @@ -0,0 +1,7 @@ +# ########################################################################### +# This file defines that patterns which are used to identify an error +# or a warning in a build log file. +# ########################################################################### +ERROR,%Error +WARNING,%Warning +EOT,VERILATE FINISHED diff --git a/hw/vendor/hpdcache/rtl/tb/scripts/vcd2fst.sh b/hw/vendor/hpdcache/rtl/tb/scripts/vcd2fst.sh new file mode 100755 index 00000000..76bcba70 --- /dev/null +++ b/hw/vendor/hpdcache/rtl/tb/scripts/vcd2fst.sh @@ -0,0 +1,49 @@ +#!/bin/bash + +## +# Copyright 2023,2024 CEA* +# *Commissariat a l'Energie Atomique et aux Energies Alternatives (CEA) +# Copyright 2025 Inria, Universite Grenoble-Alpes, TIMA +# +# SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 +# +# Licensed under the Solderpad Hardware License v 2.1 (the “License”); you +# may not use this file except in compliance with the License, or, at your +# option, the Apache License version 2.0. You may obtain a copy of the +# License at +# +# https://solderpad.org/licenses/SHL-2.1/ +# +# Unless required by applicable law or agreed to in writing, any work +# distributed under the License is distributed on an “AS IS” BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +## +## +# Author : Cesar Fuguet +# Date : October, 2024 +# Description: VCD to FST conversion wrapper +## +vcdfile=$1 + +# Check that the VCD file exists +if [[ "x" == "x${vcdfile}" ]] ; then + echo "usage: $(basename $0) <vcdfile>" ; exit 1 +fi + +# Check that the VCD file exists +if [[ ! -e ${vcdfile} ]] ; then + echo "error: file ${vcdfile} not found" ; exit 1 +fi + +# Convert the VCD file to FST +vcd2fst -c -v ${vcdfile} -f ${vcdfile}.fst + +# Check that the FST file was generated +if [[ ! -e ${vcdfile}.fst ]] ; then + echo "error: file ${vcdfile}.fst not generated" ; exit 1 +fi + +# Remove the VCD file if the FST one was generated +rm ${vcdfile} diff --git a/hw/vendor/hpdcache/rtl/tb/scripts/verilate_waivers.vlt b/hw/vendor/hpdcache/rtl/tb/scripts/verilate_waivers.vlt new file mode 100644 index 00000000..31259a60 --- /dev/null +++ b/hw/vendor/hpdcache/rtl/tb/scripts/verilate_waivers.vlt @@ -0,0 +1,12 @@ +// DESCRIPTION: Verilator output: Waivers generated with --waiver-output + +`verilator_config + +// Below you find suggested waivers. You have three options: +// 1. Fix the reason for the linter warning +// 2. Keep the waiver permanently if you are sure this is okay +// 3. Keep the waiver temporarily to suppress the output + +lint_off -file "axi_pkg.sv" +lint_off -rule UNUSEDSIGNAL -file "*" -match "Bits of signal are not used*" +lint_off -rule PINCONNECTEMPTY -file "*" -match "Cell pin connected by name with empty reference*" diff --git a/hw/vendor/hpdcache/rtl/tb/sequence_lib/hpdcache_test_random_seq.h b/hw/vendor/hpdcache/rtl/tb/sequence_lib/hpdcache_test_random_seq.h new file mode 100644 index 00000000..156bd1a1 --- /dev/null +++ b/hw/vendor/hpdcache/rtl/tb/sequence_lib/hpdcache_test_random_seq.h @@ -0,0 +1,336 @@ +/** + * Copyright 2023,2024 CEA* + * *Commissariat a l'Energie Atomique et aux Energies Alternatives (CEA) + * Copyright 2025 Inria, Universite Grenoble-Alpes, TIMA + * + * SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 + * + * Licensed under the Solderpad Hardware License v 2.1 (the “License”); you + * may not use this file except in compliance with the License, or, at your + * option, the Apache License version 2.0. You may obtain a copy of the + * License at + * + * https://solderpad.org/licenses/SHL-2.1/ + * + * Unless required by applicable law or agreed to in writing, any work + * distributed under the License is distributed on an “AS IS” BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +/** + * Author : Cesar Fuguet + * Date : October, 2024 + * Description: Class definition of the HPDCACHE test random sequence + */ +#ifndef __HPDCACHE_TEST_RANDOM_SEQ_H__ +#define __HPDCACHE_TEST_RANDOM_SEQ_H__ + +#include <systemc> +#include "scv.h" +#include "hpdcache_test_defs.h" +#include "hpdcache_test_sequence.h" + +#define HPDCACHE_TEST_SEQUENCE_ENABLE_ERROR_SEGMENTS 1 + +class hpdcache_test_random_seq : public hpdcache_test_sequence +{ +public: + + hpdcache_test_random_seq(sc_core::sc_module_name nm) : hpdcache_test_sequence(nm, "random_seq") + { + SC_THREAD(run); + sensitive << clk_i.pos(); + + seg[0].set_base (0x00000000ULL); + seg[0].set_length (0x00004000ULL); + seg[0].set_uncached (false); + seg[0].set_amo_supported (true); + seg[0].set_wr_policy_hint (hpdcache_test_memory_segment::WR_POLICY_RANDOM); + + seg[1].set_base (0x40004000ULL); + seg[1].set_length (0x00004000ULL); + seg[1].set_uncached (false); + seg[1].set_amo_supported (true); + seg[1].set_wr_policy_hint (hpdcache_test_memory_segment::WR_POLICY_WB); + + seg[2].set_base (0x80008000ULL); + seg[2].set_length (0x00004000ULL); + seg[2].set_uncached (false); + seg[2].set_amo_supported (true); + seg[2].set_wr_policy_hint (hpdcache_test_memory_segment::WR_POLICY_WT); + + seg[3].set_base (0xC000C000ULL); + seg[3].set_length (0x00004000ULL); + seg[3].set_uncached (true); + seg[3].set_amo_supported (true); + seg[3].set_wr_policy_hint (hpdcache_test_memory_segment::WR_POLICY_AUTO); + + hpdcache_test_sequence::seg_distribution.push(0, 33); + hpdcache_test_sequence::seg_distribution.push(1, 33); + hpdcache_test_sequence::seg_distribution.push(2, 33); + hpdcache_test_sequence::seg_distribution.push(3, 1); + hpdcache_test_sequence::segptr->set_mode(seg_distribution); + + hpdcache_test_sequence::delay_distribution.push(pair<int,int>(0, 0), 80); + hpdcache_test_sequence::delay_distribution.push(pair<int,int>(1, 4), 18); + hpdcache_test_sequence::delay_distribution.push(pair<int,int>(5, 20), 2); + hpdcache_test_sequence::delay->set_mode(delay_distribution); + + hpdcache_test_sequence::amo_sc_do_distribution.push(false, 25); + hpdcache_test_sequence::amo_sc_do_distribution.push(true, 75); + hpdcache_test_sequence::amo_sc_do->set_mode(amo_sc_do_distribution); + + hpdcache_test_sequence::wr_policy_distribution.push(hpdcache_test_transaction_req::HPDCACHE_WR_POLICY_AUTO, 80); + hpdcache_test_sequence::wr_policy_distribution.push(hpdcache_test_transaction_req::HPDCACHE_WR_POLICY_WB, 10); + hpdcache_test_sequence::wr_policy_distribution.push(hpdcache_test_transaction_req::HPDCACHE_WR_POLICY_WT, 10); + hpdcache_test_sequence::wr_policy->set_mode(wr_policy_distribution); + + hpdcache_test_sequence::op_distribution.push(hpdcache_test_transaction_req::HPDCACHE_REQ_LOAD, 400); + hpdcache_test_sequence::op_distribution.push(hpdcache_test_transaction_req::HPDCACHE_REQ_STORE, 350); + hpdcache_test_sequence::op_distribution.push(hpdcache_test_transaction_req::HPDCACHE_REQ_CMO_FENCE, 10); + hpdcache_test_sequence::op_distribution.push(hpdcache_test_transaction_req::HPDCACHE_REQ_CMO_PREFETCH, 10); +// hpdcache_test_sequence::op_distribution.push(hpdcache_test_transaction_req::HPDCACHE_REQ_CMO_INVAL_NLINE, 15); +// hpdcache_test_sequence::op_distribution.push(hpdcache_test_transaction_req::HPDCACHE_REQ_CMO_INVAL_ALL, 15); + hpdcache_test_sequence::op_distribution.push(hpdcache_test_transaction_req::HPDCACHE_REQ_CMO_FLUSH_NLINE, 10); + hpdcache_test_sequence::op_distribution.push(hpdcache_test_transaction_req::HPDCACHE_REQ_CMO_FLUSH_ALL, 1); + hpdcache_test_sequence::op_distribution.push(hpdcache_test_transaction_req::HPDCACHE_REQ_CMO_FLUSH_INVAL_NLINE, 10); + hpdcache_test_sequence::op_distribution.push(hpdcache_test_transaction_req::HPDCACHE_REQ_CMO_FLUSH_INVAL_ALL, 1); + hpdcache_test_sequence::op->set_mode(op_distribution); + + hpdcache_test_sequence::op_amo_distribution.push(hpdcache_test_transaction_req::HPDCACHE_REQ_LOAD, 400); + hpdcache_test_sequence::op_amo_distribution.push(hpdcache_test_transaction_req::HPDCACHE_REQ_STORE, 350); + hpdcache_test_sequence::op_amo_distribution.push(hpdcache_test_transaction_req::HPDCACHE_REQ_CMO_FENCE, 10); + hpdcache_test_sequence::op_amo_distribution.push(hpdcache_test_transaction_req::HPDCACHE_REQ_CMO_PREFETCH, 10); +// hpdcache_test_sequence::op_distribution.push(hpdcache_test_transaction_req::HPDCACHE_REQ_CMO_INVAL_NLINE, 15); +// hpdcache_test_sequence::op_distribution.push(hpdcache_test_transaction_req::HPDCACHE_REQ_CMO_INVAL_ALL, 15); + hpdcache_test_sequence::op_amo_distribution.push(hpdcache_test_transaction_req::HPDCACHE_REQ_CMO_FLUSH_NLINE, 10); + hpdcache_test_sequence::op_amo_distribution.push(hpdcache_test_transaction_req::HPDCACHE_REQ_CMO_FLUSH_ALL, 1); + hpdcache_test_sequence::op_amo_distribution.push(hpdcache_test_transaction_req::HPDCACHE_REQ_CMO_FLUSH_INVAL_NLINE, 10); + hpdcache_test_sequence::op_amo_distribution.push(hpdcache_test_transaction_req::HPDCACHE_REQ_CMO_FLUSH_INVAL_ALL, 1); + hpdcache_test_sequence::op_amo_distribution.push(hpdcache_test_transaction_req::HPDCACHE_REQ_AMO_LR, 4); + hpdcache_test_sequence::op_amo_distribution.push(hpdcache_test_transaction_req::HPDCACHE_REQ_AMO_SC, 4); + hpdcache_test_sequence::op_amo_distribution.push(hpdcache_test_transaction_req::HPDCACHE_REQ_AMO_SWAP, 4); + hpdcache_test_sequence::op_amo_distribution.push(hpdcache_test_transaction_req::HPDCACHE_REQ_AMO_ADD, 4); + hpdcache_test_sequence::op_amo_distribution.push(hpdcache_test_transaction_req::HPDCACHE_REQ_AMO_AND, 4); + hpdcache_test_sequence::op_amo_distribution.push(hpdcache_test_transaction_req::HPDCACHE_REQ_AMO_OR, 4); + hpdcache_test_sequence::op_amo_distribution.push(hpdcache_test_transaction_req::HPDCACHE_REQ_AMO_XOR, 4); + hpdcache_test_sequence::op_amo_distribution.push(hpdcache_test_transaction_req::HPDCACHE_REQ_AMO_MAX, 4); + hpdcache_test_sequence::op_amo_distribution.push(hpdcache_test_transaction_req::HPDCACHE_REQ_AMO_MAXU, 4); + hpdcache_test_sequence::op_amo_distribution.push(hpdcache_test_transaction_req::HPDCACHE_REQ_AMO_MIN, 4); + hpdcache_test_sequence::op_amo_distribution.push(hpdcache_test_transaction_req::HPDCACHE_REQ_AMO_MINU, 4); + hpdcache_test_sequence::op_amo->set_mode(op_amo_distribution); + + scv_bag<bool> need_rsp_distribution; + need_rsp_distribution.push(false, 5); + need_rsp_distribution.push(true, 95); + need_rsp_rnd->set_mode(need_rsp_distribution); + + size->keep_only(0, LOG2_REQ_DATA_BYTES); + } + +private: + hpdcache_test_sequence::hpdcache_test_memory_segment seg[4]; + scv_smart_ptr<sc_bv<HPDCACHE_REQ_DATA_WIDTH> > data; + scv_smart_ptr<sc_bv<HPDCACHE_REQ_DATA_WIDTH> > size; + scv_smart_ptr<bool> need_rsp_rnd; + static constexpr unsigned int REQ_DATA_BYTES = HPDCACHE_REQ_DATA_WIDTH/8; + static constexpr unsigned int LOG2_REQ_DATA_BYTES = HPDCACHE_TEST_DEFS_LOG2(REQ_DATA_BYTES); + +#if SC_VERSION_MAJOR < 3 + SC_HAS_PROCESS(hpdcache_test_random_seq); +#endif + + inline sc_bv<HPDCACHE_REQ_DATA_WIDTH> create_random_data() + { + data->next(); + return data->read(); + } + + inline uint32_t create_random_size(bool is_amo) + { + uint32_t ret; + size->next(); + ret = size->read().to_uint(); + if (is_amo) { + return (ret >= 3) ? 3 : 2; + } + return ret; + } + + std::shared_ptr<hpdcache_test_transaction_req> create_random_transaction() + { + std::shared_ptr<hpdcache_test_transaction_req> t; + + scv_smart_ptr<uint64_t> addr("addr"); + bool seg_amo_supported; + bool seg_wr_policy_random; + hpdcache_test_memory_segment::wr_policy_e seg_wr_policy; + int segn; + + while (!is_available_id()) wait(); + + segptr->next(); + segn = segptr->read(); + + seg_wr_policy = seg[segn].get_wr_policy_hint(); + seg_amo_supported = seg[segn].is_amo_supported(); + seg_wr_policy_random = (seg_wr_policy == hpdcache_test_memory_segment::WR_POLICY_RANDOM); + + addr->next(); + + t = acquire_transaction<hpdcache_test_transaction_req>(); + + // Select operation + if (seg_amo_supported) { + hpdcache_test_sequence::op_amo->next(); + t->req_op = op_amo->read(); + } else { + hpdcache_test_sequence::op->next(); + t->req_op = op->read(); + } + + t->req_wdata = create_random_data(); + t->req_sid = 0; + t->req_tid = allocate_id(); + t->req_abort = false; + t->req_phys_indexed = false; + + // Select write policy + if (seg_wr_policy_random) { + wr_policy->next(); + t->req_wr_policy_hint = + (hpdcache_test_transaction_req::hpdcache_wr_policy_hint_e)wr_policy->read(); + } else if (seg_wr_policy == hpdcache_test_memory_segment::WR_POLICY_WB) { + t->req_wr_policy_hint = hpdcache_test_transaction_req::HPDCACHE_WR_POLICY_WB; + } else if (seg_wr_policy == hpdcache_test_memory_segment::WR_POLICY_WT) { + t->req_wr_policy_hint = hpdcache_test_transaction_req::HPDCACHE_WR_POLICY_WT; + } else { + t->req_wr_policy_hint = hpdcache_test_transaction_req::HPDCACHE_WR_POLICY_AUTO; + } + + // Select address and size + bool req_is_amo = t->is_amo() || t->is_amo_sc() || t->is_amo_lr(); + uint32_t sz = create_random_size(req_is_amo); + uint64_t base = seg[segn].get_base(); + uint64_t length = seg[segn].get_length(); + uint32_t bytes = 1 << sz; + uint64_t address = ((base + (addr->read() % length)) / bytes) * bytes; + + t->req_addr = address; + if (t->is_cmo()) { + t->req_be = 0; + t->req_size = 0; + t->req_uncacheable = false; + + need_rsp_rnd->next(); + t->req_need_rsp = need_rsp_rnd->read(); + } else { + uint32_t offset = address % REQ_DATA_BYTES; + t->req_be = ((1UL << bytes) - 1) << offset; + t->req_size = sz; + t->req_uncacheable = seg[segptr->read()].is_uncached() ? 1 : 0; + if (req_is_amo) { + t->req_need_rsp = true; + } else { + need_rsp_rnd->next(); + t->req_need_rsp = need_rsp_rnd->read(); + } + } + + return t; + } + + std::shared_ptr<hpdcache_test_transaction_req> create_sc_transaction(uint64_t addr, bool uncacheable) + { + std::shared_ptr<hpdcache_test_transaction_req> t; + + while (!is_available_id()) wait(); + + segptr->next(); + + uint32_t sz = create_random_size(true); + uint32_t bytes = 1 << sz; + uint64_t address = (addr / bytes) * bytes; + uint32_t offset = address % REQ_DATA_BYTES; + + t = acquire_transaction<hpdcache_test_transaction_req>(); + t->req_op = hpdcache_test_transaction_req::HPDCACHE_REQ_AMO_SC; + t->req_wdata = create_random_data(); + t->req_sid = 0; + t->req_tid = allocate_id(); + t->req_addr = address; + t->req_be = ((1UL << bytes) - 1) << offset; + t->req_size = sz; + t->req_uncacheable = uncacheable; + t->req_need_rsp = true; + + return t; + } + + void run() + { + +#if HPDCACHE_TEST_SEQUENCE_ENABLE_ERROR_SEGMENTS + if (hpdcache_test_sequence::mem_resp_model) { + hpdcache_test_sequence::mem_resp_model->add_error_segment( + hpdcache_test_mem_resp_model_base::segment_t( + 0x00000000ULL, 0x00000200ULL, true + ) + ); + hpdcache_test_sequence::mem_resp_model->add_error_segment( + hpdcache_test_mem_resp_model_base::segment_t( + 0x40004000ULL, 0x40004200ULL, true + ) + ); + hpdcache_test_sequence::mem_resp_model->add_error_segment( + hpdcache_test_mem_resp_model_base::segment_t( + 0xC000C000ULL, 0xC000C200ULL, true + ) + ); + } +#endif + + while (rst_ni == 0) wait(); + + wait(); + + scv_smart_ptr<int> lrsc_inbetween_instrs; + lrsc_inbetween_instrs->keep_only(0, 10); + lrsc_inbetween_instrs->next(); + + delay->next(); + for (size_t n = 0; n < this->max_transactions; n++) { + std::shared_ptr<hpdcache_test_transaction_req> t; + t = create_random_transaction(); + + if (t->is_amo_lr()) { + hpdcache_test_transaction_req prev_lr = *t; + send_transaction(t, delay->read()); + delay->next(); + + amo_sc_do->next(); + if (amo_sc_do->read()) { + for (int i = 0; i < lrsc_inbetween_instrs.read(); i++) { + t = create_random_transaction(); + send_transaction(t, delay->read()); + delay->next(); + } + + t = create_sc_transaction(prev_lr.req_addr.to_uint64(), prev_lr.req_uncacheable); + send_transaction(t, delay->read()); + delay->next(); + } + + continue; + } + send_transaction(t, delay->read()); + delay->next(); + } + + // ask the driver to stop + transaction_fifo_o->write(nullptr); + } +}; + +#endif // __HPDCACHE_TEST_RANDOM_SEQ_H__ diff --git a/hw/vendor/hpdcache/rtl/tb/sequence_lib/hpdcache_test_read_seq.h b/hw/vendor/hpdcache/rtl/tb/sequence_lib/hpdcache_test_read_seq.h new file mode 100644 index 00000000..e3675f50 --- /dev/null +++ b/hw/vendor/hpdcache/rtl/tb/sequence_lib/hpdcache_test_read_seq.h @@ -0,0 +1,169 @@ +/** + * Copyright 2023,2024 CEA* + * *Commissariat a l'Energie Atomique et aux Energies Alternatives (CEA) + * Copyright 2025 Inria, Universite Grenoble-Alpes, TIMA + * + * SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 + * + * Licensed under the Solderpad Hardware License v 2.1 (the “License”); you + * may not use this file except in compliance with the License, or, at your + * option, the Apache License version 2.0. You may obtain a copy of the + * License at + * + * https://solderpad.org/licenses/SHL-2.1/ + * + * Unless required by applicable law or agreed to in writing, any work + * distributed under the License is distributed on an “AS IS” BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +/** + * Author : Cesar Fuguet + * Date : October, 2024 + * Description: Class definition of the HPDCACHE test read sequence + */ +#ifndef __HPDCACHE_TEST_READ_SEQ_H__ +#define __HPDCACHE_TEST_READ_SEQ_H__ + +#include <systemc> +#include "scv.h" +#include "hpdcache_test_defs.h" +#include "hpdcache_test_mem_resp_model_base.h" +#include "hpdcache_test_sequence.h" + +class hpdcache_test_read_seq : public hpdcache_test_sequence +{ +public: + + hpdcache_test_read_seq(sc_core::sc_module_name nm) : hpdcache_test_sequence(nm, "read_seq") + { + SC_THREAD(run); + sensitive << clk_i.pos(); + + seg.set_base(0x00000000ULL); + seg.set_length(0x00080000ULL); + seg.set_uncached(false); + + hpdcache_test_sequence::segptr->keep_only(0); + hpdcache_test_sequence::delay->keep_only(0); + hpdcache_test_sequence::op->keep_only(hpdcache_test_transaction_req::HPDCACHE_REQ_LOAD); + } + +private: + typedef sc_bv<HPDCACHE_REQ_DATA_WIDTH> req_data_t; + + hpdcache_test_sequence::hpdcache_test_memory_segment seg; + scv_smart_ptr<req_data_t> data; + scv_smart_ptr<req_data_t> size; + const unsigned int HPDCACHE_REQ_DATA_BYTES = HPDCACHE_REQ_DATA_WIDTH/8; + +#if SC_VERSION_MAJOR < 3 + SC_HAS_PROCESS(hpdcache_test_read_seq); +#endif + + inline uint32_t create_random_size(bool is_amo) + { + if (!is_amo) size->keep_only(0, 3); + else size->keep_only(2, 3); + + size->next(); + return size->read().to_uint(); + } + + void send_transaction(std::shared_ptr<hpdcache_test_transaction_req> t) + { + // send transaction to the driver + transaction_fifo_o->write(t); + + // wait and consume driver acknowledgement (this is blocking) + transaction_ret_i.read(); + + // release the previously used transaction object + release_transaction<hpdcache_test_transaction_req>(t); + + // add some random delay between two consecutive commands + for (int i = 0; i < delay->read(); i++) { + wait(); + } + } + + std::shared_ptr<hpdcache_test_transaction_req> create_random_transaction() + { + std::shared_ptr<hpdcache_test_transaction_req> t; + + scv_smart_ptr<uint64_t> addr("addr"); + + while (!is_available_id()) wait(); + + hpdcache_test_sequence::segptr->next(); + hpdcache_test_sequence::delay->next(); + hpdcache_test_sequence::op->next(); + + addr->next(); + + t = acquire_transaction<hpdcache_test_transaction_req>(); + t->req_op = op->read(); + t->req_wdata = 0; + t->req_sid = 0; + t->req_tid = allocate_id(); + t->req_abort = false; + t->req_phys_indexed = false; + + uint32_t sz = create_random_size( + t->is_amo() || + t->is_amo_sc() || + t->is_amo_lr()); + + uint64_t base = seg.get_base(); + uint64_t length = seg.get_length(); + uint32_t bytes = 1 << sz; + uint64_t address = ((base + (addr->read() % length)) / bytes) * bytes; + uint32_t offset = address % HPDCACHE_REQ_DATA_BYTES; + + t->req_addr = address; + t->req_be = ((1UL << bytes) - 1) << offset; + t->req_size = sz; + t->req_uncacheable = seg.is_uncached() ? 1 : 0; + t->req_need_rsp = true; + + return t; + } + + void run() + { + while (rst_ni == 0) wait(); + + std::shared_ptr<hpdcache_test_mem_resp_model_base> mem_resp_model = + this->get_mem_resp_model(); + + scv_bag<pair<int, int>> ra_delay_distribution; + scv_bag<pair<int, int>> rd_delay_distribution; + scv_bag<pair<int, int>> wa_delay_distribution; + scv_bag<pair<int, int>> wb_delay_distribution; + + ra_delay_distribution.push(pair<int,int>( 0, 0), 100); + rd_delay_distribution.push(pair<int,int>( 0, 0), 100); + wa_delay_distribution.push(pair<int,int>( 0, 0), 100); + wb_delay_distribution.push(pair<int,int>( 0, 0), 100); + + mem_resp_model->set_ra_ready_delay_distribution(ra_delay_distribution); + mem_resp_model->set_rd_valid_delay_distribution(rd_delay_distribution); + mem_resp_model->set_wa_ready_delay_distribution(wa_delay_distribution); + mem_resp_model->set_wd_ready_delay_distribution(wa_delay_distribution); + mem_resp_model->set_wb_valid_delay_distribution(wb_delay_distribution); + + wait(); + + for (size_t n = 0; n < this->max_transactions; n++) { + std::shared_ptr<hpdcache_test_transaction_req> t; + t = create_random_transaction(); + send_transaction(t); + } + + // ask the driver to stop + transaction_fifo_o->write(nullptr); + } +}; + +#endif // __HPDCACHE_TEST_READ_SEQ_H__ diff --git a/hw/vendor/hpdcache/rtl/tb/sequence_lib/hpdcache_test_unique_set_seq.h b/hw/vendor/hpdcache/rtl/tb/sequence_lib/hpdcache_test_unique_set_seq.h new file mode 100644 index 00000000..28d149e1 --- /dev/null +++ b/hw/vendor/hpdcache/rtl/tb/sequence_lib/hpdcache_test_unique_set_seq.h @@ -0,0 +1,315 @@ +/** + * Copyright 2023,2024 CEA* + * *Commissariat a l'Energie Atomique et aux Energies Alternatives (CEA) + * Copyright 2025 Inria, Universite Grenoble-Alpes, TIMA + * + * SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 + * + * Licensed under the Solderpad Hardware License v 2.1 (the “License”); you + * may not use this file except in compliance with the License, or, at your + * option, the Apache License version 2.0. You may obtain a copy of the + * License at + * + * https://solderpad.org/licenses/SHL-2.1/ + * + * Unless required by applicable law or agreed to in writing, any work + * distributed under the License is distributed on an “AS IS” BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +/** + * Author : Cesar Fuguet + * Date : May, 2025 + * Description: Class definition of the HPDCACHE test unique set sequence + */ +#ifndef __HPDCACHE_TEST_UNIQUE_SET_SEQ_H__ +#define __HPDCACHE_TEST_UNIQUE_SET_SEQ_H__ + +#include <systemc> +#include "scv.h" +#include "hpdcache_test_defs.h" +#include "hpdcache_test_sequence.h" + +class hpdcache_test_unique_set_seq : public hpdcache_test_sequence +{ +public: + + hpdcache_test_unique_set_seq(sc_core::sc_module_name nm) : hpdcache_test_sequence(nm, "random_seq") + { + SC_THREAD(run); + sensitive << clk_i.pos(); + + seg[0].set_base (0x00000000ULL); + seg[0].set_length (0x00004000ULL); + seg[0].set_uncached (false); + seg[0].set_amo_supported (true); + seg[0].set_wr_policy_hint (hpdcache_test_memory_segment::WR_POLICY_RANDOM); + + seg[1].set_base (0x40004000ULL); + seg[1].set_length (0x00004000ULL); + seg[1].set_uncached (false); + seg[1].set_amo_supported (true); + seg[1].set_wr_policy_hint (hpdcache_test_memory_segment::WR_POLICY_WB); + + seg[2].set_base (0x80008000ULL); + seg[2].set_length (0x00004000ULL); + seg[2].set_uncached (false); + seg[3].set_amo_supported (true); + seg[3].set_wr_policy_hint (hpdcache_test_memory_segment::WR_POLICY_WT); + + seg[3].set_base (0xC000C000ULL); + seg[3].set_length (0x00004000ULL); + seg[3].set_uncached (false); + seg[3].set_amo_supported (true); + seg[3].set_wr_policy_hint (hpdcache_test_memory_segment::WR_POLICY_AUTO); + + hpdcache_test_sequence::seg_distribution.push(0, 100/4); + hpdcache_test_sequence::seg_distribution.push(1, 100/4); + hpdcache_test_sequence::seg_distribution.push(2, 100/4); + hpdcache_test_sequence::seg_distribution.push(3, 100/4); + hpdcache_test_sequence::segptr->set_mode(seg_distribution); + + hpdcache_test_sequence::delay_distribution.push(pair<int,int>(0, 0), 80); + hpdcache_test_sequence::delay_distribution.push(pair<int,int>(1, 4), 18); + hpdcache_test_sequence::delay_distribution.push(pair<int,int>(5, 20), 2); + hpdcache_test_sequence::delay->set_mode(delay_distribution); + + hpdcache_test_sequence::amo_sc_do_distribution.push(false, 25); + hpdcache_test_sequence::amo_sc_do_distribution.push(true, 75); + hpdcache_test_sequence::amo_sc_do->set_mode(amo_sc_do_distribution); + + hpdcache_test_sequence::wr_policy_distribution.push(hpdcache_test_transaction_req::HPDCACHE_WR_POLICY_AUTO, 80); + hpdcache_test_sequence::wr_policy_distribution.push(hpdcache_test_transaction_req::HPDCACHE_WR_POLICY_WB, 10); + hpdcache_test_sequence::wr_policy_distribution.push(hpdcache_test_transaction_req::HPDCACHE_WR_POLICY_WT, 10); + hpdcache_test_sequence::wr_policy->set_mode(wr_policy_distribution); + + hpdcache_test_sequence::op_distribution.push(hpdcache_test_transaction_req::HPDCACHE_REQ_LOAD, 400); + hpdcache_test_sequence::op_distribution.push(hpdcache_test_transaction_req::HPDCACHE_REQ_STORE, 350); + hpdcache_test_sequence::op_distribution.push(hpdcache_test_transaction_req::HPDCACHE_REQ_CMO_FENCE, 10); + hpdcache_test_sequence::op_distribution.push(hpdcache_test_transaction_req::HPDCACHE_REQ_CMO_PREFETCH, 10); +// hpdcache_test_sequence::op_distribution.push(hpdcache_test_transaction_req::HPDCACHE_REQ_CMO_INVAL_NLINE, 15); +// hpdcache_test_sequence::op_distribution.push(hpdcache_test_transaction_req::HPDCACHE_REQ_CMO_INVAL_ALL, 15); + hpdcache_test_sequence::op_distribution.push(hpdcache_test_transaction_req::HPDCACHE_REQ_CMO_FLUSH_NLINE, 10); + hpdcache_test_sequence::op_distribution.push(hpdcache_test_transaction_req::HPDCACHE_REQ_CMO_FLUSH_ALL, 1); + hpdcache_test_sequence::op_distribution.push(hpdcache_test_transaction_req::HPDCACHE_REQ_CMO_FLUSH_INVAL_NLINE, 10); + hpdcache_test_sequence::op_distribution.push(hpdcache_test_transaction_req::HPDCACHE_REQ_CMO_FLUSH_INVAL_ALL, 1); + hpdcache_test_sequence::op->set_mode(op_distribution); + + hpdcache_test_sequence::op_amo_distribution.push(hpdcache_test_transaction_req::HPDCACHE_REQ_LOAD, 400); + hpdcache_test_sequence::op_amo_distribution.push(hpdcache_test_transaction_req::HPDCACHE_REQ_STORE, 350); + hpdcache_test_sequence::op_amo_distribution.push(hpdcache_test_transaction_req::HPDCACHE_REQ_CMO_FENCE, 10); + hpdcache_test_sequence::op_amo_distribution.push(hpdcache_test_transaction_req::HPDCACHE_REQ_CMO_PREFETCH, 10); +// hpdcache_test_sequence::op_distribution.push(hpdcache_test_transaction_req::HPDCACHE_REQ_CMO_INVAL_NLINE, 15); +// hpdcache_test_sequence::op_distribution.push(hpdcache_test_transaction_req::HPDCACHE_REQ_CMO_INVAL_ALL, 15); + hpdcache_test_sequence::op_amo_distribution.push(hpdcache_test_transaction_req::HPDCACHE_REQ_CMO_FLUSH_NLINE, 10); + hpdcache_test_sequence::op_amo_distribution.push(hpdcache_test_transaction_req::HPDCACHE_REQ_CMO_FLUSH_ALL, 1); + hpdcache_test_sequence::op_amo_distribution.push(hpdcache_test_transaction_req::HPDCACHE_REQ_CMO_FLUSH_INVAL_NLINE, 10); + hpdcache_test_sequence::op_amo_distribution.push(hpdcache_test_transaction_req::HPDCACHE_REQ_CMO_FLUSH_INVAL_ALL, 1); + hpdcache_test_sequence::op_amo_distribution.push(hpdcache_test_transaction_req::HPDCACHE_REQ_AMO_LR, 4); + hpdcache_test_sequence::op_amo_distribution.push(hpdcache_test_transaction_req::HPDCACHE_REQ_AMO_SC, 4); + hpdcache_test_sequence::op_amo_distribution.push(hpdcache_test_transaction_req::HPDCACHE_REQ_AMO_SWAP, 4); + hpdcache_test_sequence::op_amo_distribution.push(hpdcache_test_transaction_req::HPDCACHE_REQ_AMO_ADD, 4); + hpdcache_test_sequence::op_amo_distribution.push(hpdcache_test_transaction_req::HPDCACHE_REQ_AMO_AND, 4); + hpdcache_test_sequence::op_amo_distribution.push(hpdcache_test_transaction_req::HPDCACHE_REQ_AMO_OR, 4); + hpdcache_test_sequence::op_amo_distribution.push(hpdcache_test_transaction_req::HPDCACHE_REQ_AMO_XOR, 4); + hpdcache_test_sequence::op_amo_distribution.push(hpdcache_test_transaction_req::HPDCACHE_REQ_AMO_MAX, 4); + hpdcache_test_sequence::op_amo_distribution.push(hpdcache_test_transaction_req::HPDCACHE_REQ_AMO_MAXU, 4); + hpdcache_test_sequence::op_amo_distribution.push(hpdcache_test_transaction_req::HPDCACHE_REQ_AMO_MIN, 4); + hpdcache_test_sequence::op_amo_distribution.push(hpdcache_test_transaction_req::HPDCACHE_REQ_AMO_MINU, 4); + hpdcache_test_sequence::op_amo->set_mode(op_amo_distribution); + + scv_bag<bool> need_rsp_distribution; + need_rsp_distribution.push(false, 5); + need_rsp_distribution.push(true, 95); + need_rsp_rnd->set_mode(need_rsp_distribution); + + size->keep_only(0, LOG2_REQ_DATA_BYTES); + } + +private: + hpdcache_test_sequence::hpdcache_test_memory_segment seg[4]; + scv_smart_ptr<uint64_t> addr; + scv_smart_ptr<sc_bv<HPDCACHE_REQ_DATA_WIDTH> > data; + scv_smart_ptr<sc_bv<HPDCACHE_REQ_DATA_WIDTH> > size; + uint64_t set; + scv_smart_ptr<bool> need_rsp_rnd; + static constexpr unsigned int REQ_DATA_BYTES = HPDCACHE_REQ_DATA_WIDTH/8; + static constexpr unsigned int LOG2_REQ_DATA_BYTES = HPDCACHE_TEST_DEFS_LOG2(REQ_DATA_BYTES); + +#if SC_VERSION_MAJOR < 3 + SC_HAS_PROCESS(hpdcache_test_unique_set_seq); +#endif + + inline sc_bv<HPDCACHE_REQ_DATA_WIDTH> create_random_data() + { + data->next(); + return data->read(); + } + + inline uint32_t create_random_size(bool is_amo) + { + uint32_t ret; + size->next(); + ret = size->read().to_uint(); + if (is_amo) { + return (ret >= 3) ? 3 : 2; + } + return ret; + } + + inline uint64_t create_random_addr(uint64_t seg_base, uint64_t seg_length, uint32_t bytes) + { + static const uint64_t _set_mask = ((1 << HPDCACHE_SET_WIDTH) - 1); + static const uint64_t set_mask = _set_mask << HPDCACHE_CL_OFFSET_WIDTH; + uint64_t address; + addr->next(); + address = ((seg_base + (addr->read() % seg_length)) / bytes) * bytes; + return (address & ~set_mask) | (set << HPDCACHE_CL_OFFSET_WIDTH); + } + + inline hpdcache_test_transaction_req::hpdcache_wr_policy_hint_e + select_random_wr_policy(hpdcache_test_memory_segment::wr_policy_e seg_wr_policy) + { + if (seg_wr_policy == hpdcache_test_memory_segment::WR_POLICY_RANDOM) { + wr_policy->next(); + return static_cast<hpdcache_test_transaction_req::hpdcache_wr_policy_hint_e>( + wr_policy->read()); + } else if (seg_wr_policy == hpdcache_test_memory_segment::WR_POLICY_WB) { + return hpdcache_test_transaction_req::HPDCACHE_WR_POLICY_WB; + } else if (seg_wr_policy == hpdcache_test_memory_segment::WR_POLICY_WT) { + return hpdcache_test_transaction_req::HPDCACHE_WR_POLICY_WT; + } + return hpdcache_test_transaction_req::HPDCACHE_WR_POLICY_AUTO; + } + + std::shared_ptr<hpdcache_test_transaction_req> create_random_transaction() + { + std::shared_ptr<hpdcache_test_transaction_req> t; + + bool seg_amo_supported; + hpdcache_test_memory_segment::wr_policy_e seg_wr_policy; + int segn; + + while (!is_available_id()) wait(); + + segptr->next(); + segn = segptr->read(); + seg_wr_policy = seg[segn].get_wr_policy_hint(); + seg_amo_supported = seg[segn].is_amo_supported(); + + t = acquire_transaction<hpdcache_test_transaction_req>(); + + // Select operation + if (seg_amo_supported) { + hpdcache_test_sequence::op_amo->next(); + t->req_op = op_amo->read(); + } else { + hpdcache_test_sequence::op->next(); + t->req_op = op->read(); + } + + t->req_wdata = create_random_data(); + t->req_sid = 0; + t->req_tid = allocate_id(); + t->req_abort = false; + t->req_phys_indexed = false; + t->req_wr_policy_hint = select_random_wr_policy(seg_wr_policy); + + // Select address and size + bool req_is_amo = t->is_amo() || t->is_amo_sc() || t->is_amo_lr(); + uint32_t sz = create_random_size(req_is_amo); + uint32_t bytes = 1 << sz; + uint64_t address = create_random_addr(seg[segn].get_base(), seg[segn].get_length(), bytes); + t->req_addr = address; + need_rsp_rnd->next(); + t->req_need_rsp = req_is_amo || need_rsp_rnd->read(); + if (t->is_cmo()) { + t->req_be = 0; + t->req_size = 0; + t->req_uncacheable = false; + } else { + uint32_t offset = address % REQ_DATA_BYTES; + t->req_be = ((1UL << bytes) - 1) << offset; + t->req_size = sz; + t->req_uncacheable = seg[segptr->read()].is_uncached() ? 1 : 0; + } + return t; + } + + std::shared_ptr<hpdcache_test_transaction_req> create_sc_transaction(uint64_t addr, bool uncacheable) + { + std::shared_ptr<hpdcache_test_transaction_req> t; + + while (!is_available_id()) wait(); + + segptr->next(); + + uint32_t sz = create_random_size(true); + uint32_t bytes = 1 << sz; + uint64_t address = (addr / bytes) * bytes; + uint32_t offset = address % REQ_DATA_BYTES; + + t = acquire_transaction<hpdcache_test_transaction_req>(); + t->req_op = hpdcache_test_transaction_req::HPDCACHE_REQ_AMO_SC; + t->req_wdata = create_random_data(); + t->req_sid = 0; + t->req_tid = allocate_id(); + t->req_addr = address; + t->req_be = ((1UL << bytes) - 1) << offset; + t->req_size = sz; + t->req_uncacheable = uncacheable; + t->req_need_rsp = true; + + return t; + } + + void run() + { + while (rst_ni == 0) wait(); + + wait(); + + scv_smart_ptr<int> lrsc_inbetween_instrs; + lrsc_inbetween_instrs->keep_only(0, 10); + lrsc_inbetween_instrs->next(); + + delay->next(); + + // randomize the unique set for each execution + set = rand() % HPDCACHE_SETS; + + for (size_t n = 0; n < this->max_transactions; n++) { + std::shared_ptr<hpdcache_test_transaction_req> t; + t = create_random_transaction(); + + if (t->is_amo_lr()) { + hpdcache_test_transaction_req prev_lr = *t; + send_transaction(t, delay->read()); + delay->next(); + + amo_sc_do->next(); + if (amo_sc_do->read()) { + for (int i = 0; i < lrsc_inbetween_instrs.read(); i++) { + t = create_random_transaction(); + send_transaction(t, delay->read()); + delay->next(); + } + + t = create_sc_transaction(prev_lr.req_addr.to_uint64(), prev_lr.req_uncacheable); + send_transaction(t, delay->read()); + delay->next(); + } + + continue; + } + send_transaction(t, delay->read()); + delay->next(); + } + + // ask the driver to stop + transaction_fifo_o->write(nullptr); + } +}; + +#endif // __HPDCACHE_TEST_RANDOM_SEQ_H__ diff --git a/hw/vendor/hpdcache/rtl/tb/sequence_lib/hpdcache_test_write_seq.h b/hw/vendor/hpdcache/rtl/tb/sequence_lib/hpdcache_test_write_seq.h new file mode 100644 index 00000000..420dae67 --- /dev/null +++ b/hw/vendor/hpdcache/rtl/tb/sequence_lib/hpdcache_test_write_seq.h @@ -0,0 +1,171 @@ +/** + * Copyright 2023,2024 CEA* + * *Commissariat a l'Energie Atomique et aux Energies Alternatives (CEA) + * Copyright 2025 Inria, Universite Grenoble-Alpes, TIMA + * + * SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 + * + * Licensed under the Solderpad Hardware License v 2.1 (the “License”); you + * may not use this file except in compliance with the License, or, at your + * option, the Apache License version 2.0. You may obtain a copy of the + * License at + * + * https://solderpad.org/licenses/SHL-2.1/ + * + * Unless required by applicable law or agreed to in writing, any work + * distributed under the License is distributed on an “AS IS” BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +/** + * Author : Cesar Fuguet + * Date : October, 2024 + * Description: Class definition of the HPDCACHE test random sequence + */ +#ifndef __HPDCACHE_TEST_WRITE_SEQ_H__ +#define __HPDCACHE_TEST_WRITE_SEQ_H__ + +#include <systemc> +#include "scv.h" +#include "hpdcache_test_defs.h" +#include "hpdcache_test_mem_resp_model_base.h" +#include "hpdcache_test_sequence.h" + +class hpdcache_test_write_seq : public hpdcache_test_sequence +{ +public: + + hpdcache_test_write_seq(sc_core::sc_module_name nm) : hpdcache_test_sequence(nm, "write_seq") + { + SC_THREAD(run); + sensitive << clk_i.pos(); + + seg.set_base(0x00000000ULL); + seg.set_length(0x00004000ULL); + seg.set_uncached(false); + + hpdcache_test_sequence::segptr->keep_only(0); + hpdcache_test_sequence::delay->keep_only(0); + hpdcache_test_sequence::op->keep_only(hpdcache_test_transaction_req::HPDCACHE_REQ_STORE); + } + +private: + typedef sc_bv<HPDCACHE_REQ_DATA_WIDTH> req_data_t; + + hpdcache_test_sequence::hpdcache_test_memory_segment seg; + scv_smart_ptr<req_data_t> data; + scv_smart_ptr<req_data_t> size; + const unsigned int HPDCACHE_REQ_DATA_BYTES = HPDCACHE_REQ_DATA_WIDTH/8; + +#if SC_VERSION_MAJOR < 3 + SC_HAS_PROCESS(hpdcache_test_write_seq); +#endif + + inline req_data_t create_random_data() + { + data->next(); + return data->read(); + } + + inline uint32_t create_random_size(bool is_amo) + { + if (!is_amo) size->keep_only(0, 3); + else size->keep_only(2, 3); + + size->next(); + return size->read().to_uint(); + } + + void send_transaction(std::shared_ptr<hpdcache_test_transaction_req> t) + { + // send transaction to the driver + transaction_fifo_o->write(t); + + // wait and consume driver acknowledgement (this is blocking) + transaction_ret_i.read(); + + // release the previously used transaction object + release_transaction<hpdcache_test_transaction_req>(t); + + // add some random delay between two consecutive commands + for (int i = 0; i < delay->read(); i++) { + wait(); + } + } + + std::shared_ptr<hpdcache_test_transaction_req> create_random_transaction() + { + std::shared_ptr<hpdcache_test_transaction_req> t; + + scv_smart_ptr<uint64_t> addr("addr"); + + while (!is_available_id()) wait(); + + hpdcache_test_sequence::segptr->next(); + hpdcache_test_sequence::delay->next(); + hpdcache_test_sequence::op->next(); + + addr->next(); + + t = acquire_transaction<hpdcache_test_transaction_req>(); + t->req_op = op->read(); + t->req_wdata = create_random_data(); + t->req_sid = 0; + t->req_tid = allocate_id(); + t->req_abort = false; + t->req_phys_indexed = false; + + uint32_t sz = create_random_size( + t->is_amo() || + t->is_amo_sc() || + t->is_amo_lr()); + + uint64_t base = seg.get_base(); + uint64_t length = seg.get_length(); + uint32_t bytes = 1 << sz; + uint64_t address = ((base + (addr->read() % length)) / bytes) * bytes; + uint32_t offset = address % HPDCACHE_REQ_DATA_BYTES; + + t->req_addr = address; + t->req_be = ((1UL << bytes) - 1) << offset; + t->req_size = sz; + t->req_uncacheable = seg.is_uncached() ? 1 : 0; + t->req_need_rsp = true; + + return t; + } + + void run() + { + while (rst_ni == 0) wait(); + + std::shared_ptr<hpdcache_test_mem_resp_model_base> mem_resp_model = + this->get_mem_resp_model(); + + scv_bag<pair<int, int>> wa_delay_distribution; + scv_bag<pair<int, int>> wb_delay_distribution; + + wa_delay_distribution.push(pair<int,int>( 0, 0), 100); + wb_delay_distribution.push(pair<int,int>( 0, 0), 100); + + mem_resp_model->get_ra_ready_delay_distribution()->keep_only(0); + mem_resp_model->get_rd_valid_delay_distribution()->keep_only(16); + mem_resp_model->set_wa_ready_delay_distribution(wa_delay_distribution); + mem_resp_model->set_wd_ready_delay_distribution(wa_delay_distribution); + mem_resp_model->set_wb_valid_delay_distribution(wb_delay_distribution); + + wait(); + + for (size_t n = 0; n < this->max_transactions; n++) { + std::shared_ptr<hpdcache_test_transaction_req> t; + t = create_random_transaction(); + send_transaction(t); + } + + // ask the driver to stop + transaction_fifo_o->write(nullptr); + } +}; + +#endif // __HPDCACHE_TEST_WRITE_SEQ_H__ From 551a71ad5cf0b32261e868668df4f541ae0220d3 Mon Sep 17 00:00:00 2001 From: Marno van der Maas <mvdmaas+git@lowrisc.org> Date: Mon, 4 May 2026 16:30:46 +0100 Subject: [PATCH 05/10] Update cva6_cheri to lowRISC/cva6-cheri@3c596eb6 This is a big code change because there has been a rebase on upstream non-CHERI CVA6 as well as work to enable HPDCache. This vendor is based on the following release: https://github.com/Capabilities-Limited/cheri-cva6/releases/tag/cva6-april-2026 --- hw/vendor/cva6_cheri.lock.hjson | 2 +- hw/vendor/cva6_cheri/.gitlab-ci.yml | 11 +- hw/vendor/cva6_cheri/.gitmodules | 6 +- hw/vendor/cva6_cheri/Bender.yml | 80 ++- hw/vendor/cva6_cheri/Flist.ariane | 1 + hw/vendor/cva6_cheri/Makefile | 36 +- hw/vendor/cva6_cheri/README.md | 2 +- hw/vendor/cva6_cheri/ci/install-verilator.sh | 2 +- .../common/local/util/instr_trace_item.svh | 97 +++- .../common/local/util/instr_tracer.sv | 57 +- hw/vendor/cva6_cheri/core/Flist.cva6 | 31 +- hw/vendor/cva6_cheri/core/alu.sv | 33 +- hw/vendor/cva6_cheri/core/alu_wrapper.sv | 71 +++ hw/vendor/cva6_cheri/core/ariane_regfile.sv | 114 ---- .../cva6_cheri/core/ariane_regfile_ff.sv | 15 +- .../cva6_cheri/core/ariane_regfile_fpga.sv | 3 +- hw/vendor/cva6_cheri/core/branch_unit.sv | 30 +- .../core/cache_subsystem/cache_ctrl.sv | 1 + .../cva6_hpdcache_if_adapter.sv | 112 +++- .../cva6_hpdcache_subsystem.sv | 43 +- .../cva6_hpdcache_subsystem_axi_arbiter.sv | 41 +- .../cache_subsystem/cva6_hpdcache_wrapper.sv | 2 + .../core/cache_subsystem/wt_axi_adapter.sv | 36 +- .../cache_subsystem/wt_cache_subsystem.sv | 69 +-- .../core/cache_subsystem/wt_dcache_mem.sv | 2 +- hw/vendor/cva6_cheri/core/cheri_unit.sv | 47 +- hw/vendor/cva6_cheri/core/commit_stage.sv | 13 +- hw/vendor/cva6_cheri/core/csr_regfile.sv | 304 ++++++++--- hw/vendor/cva6_cheri/core/cva6.sv | 122 +++-- hw/vendor/cva6_cheri/core/cva6_fifo_v3.sv | 8 +- .../core/cva6_iti/instr_to_trace.sv | 95 ---- hw/vendor/cva6_cheri/core/cva6_iti/iti.sv | 187 ------- .../core/cva6_iti/itype_detector.sv | 92 ---- .../cva6_cheri/core/cva6_mmu/cva6_mmu.sv | 10 +- .../cva6_cheri/core/cva6_mmu/cva6_ptw.sv | 4 + .../core/cva6_mmu/cva6_shared_tlb.sv | 26 +- hw/vendor/cva6_cheri/core/cva6_rvfi.sv | 162 +++--- hw/vendor/cva6_cheri/core/decoder.sv | 9 +- hw/vendor/cva6_cheri/core/ex_stage.sv | 113 ++-- hw/vendor/cva6_cheri/core/fpu_wrap.sv | 6 +- .../cva6_cheri/core/frontend/frontend.sv | 1 - hw/vendor/cva6_cheri/core/id_stage.sv | 23 +- .../cva6_cheri/core/include/ariane_pkg.sv | 31 +- .../core/include/build_config_pkg.sv | 14 +- .../cva6_cheri/core/include/config_pkg.sv | 21 +- .../core/include/cv32a60x_config_pkg.sv | 10 +- .../core/include/cv32a65x_config_pkg.sv | 8 + .../cv32a6_ima_sv32_fpga_config_pkg.sv | 6 + .../include/cv32a6_imac_sv32_config_pkg.sv | 6 + .../core/include/cv64a60ax_config_pkg.sv | 14 +- .../include/cv64a6_imafdc_sv39_config_pkg.sv | 10 +- .../cv64a6_imafdc_sv39_hpdcache_config_pkg.sv | 6 + ...64a6_imafdc_sv39_hpdcache_wb_config_pkg.sv | 14 +- ...dc_sv39_hpdcache_wb_rvfi_dii_config_pkg.sv | 179 +++++++ ...cv64a6_imafdc_sv39_openpiton_config_pkg.sv | 6 + ...cv64a6_imafdc_sv39_rvfi_dii_config_pkg.sv} | 12 +- .../cv64a6_imafdc_sv39_wb_config_pkg.sv | 6 + .../include/cv64a6_imafdch_sv39_config_pkg.sv | 6 + .../cv64a6_imafdch_sv39_wb_config_pkg.sv | 6 + .../include/cv64a6_imafdcv_sv39_config_pkg.sv | 6 + ...=> cv64a6_imafdczcheri_sv39_config_pkg.sv} | 32 +- ...mafdczcheri_sv39_hpdcache_wb_config_pkg.sv | 181 +++++++ ...ri_sv39_hpdcache_wb_rvfi_dii_config_pkg.sv | 179 +++++++ ..._imafdczcheri_sv39_rvfi_dii_config_pkg.sv} | 12 +- .../cva6_cheri/core/include/cva6_cheri_pkg.sv | 149 +----- .../cva6_cheri/core/include/cvxif_types.svh | 2 +- .../cv32a6_embedded_config_pkg_deprecated.sv | 5 + .../cv32a6_imac_sv0_config_pkg.sv | 6 + .../cv32a6_imafc_sv32_config_pkg.sv | 6 + .../cv64a6_imadfcv_sv39_polara_config_pkg.sv | 6 + .../cv64a6_mmu_config_pkg.sv | 6 + hw/vendor/cva6_cheri/core/include/iti_pkg.sv | 36 -- .../cva6_cheri/core/include/iti_types.svh | 24 - .../cva6_cheri/core/include/riscv_pkg.sv | 2 + .../cva6_cheri/core/include/triggers_pkg.sv | 117 ++++ hw/vendor/cva6_cheri/core/instr_realign.sv | 8 +- .../cva6_cheri/core/issue_read_operands.sv | 235 ++++---- hw/vendor/cva6_cheri/core/issue_stage.sv | 10 +- hw/vendor/cva6_cheri/core/load_store_unit.sv | 209 +++++--- hw/vendor/cva6_cheri/core/load_unit.sv | 56 +- hw/vendor/cva6_cheri/core/mult.sv | 4 +- hw/vendor/cva6_cheri/core/multiplier.sv | 3 + .../cva6_cheri/core/pmp/src/pmp_data_if.sv | 31 +- hw/vendor/cva6_cheri/core/scoreboard.sv | 5 +- hw/vendor/cva6_cheri/core/store_unit.sv | 24 +- hw/vendor/cva6_cheri/core/trigger_module.sv | 501 ++++++++++++++++++ .../docs/01_cva6_user/Trigger_Module.rst | 323 +++++++++++ .../util/toolchain-builder/README.md | 7 + .../toolchain-builder/gcc-cva6-tune.patch | 178 +++++++ 89 files changed, 3354 insertions(+), 1483 deletions(-) create mode 100644 hw/vendor/cva6_cheri/core/alu_wrapper.sv delete mode 100644 hw/vendor/cva6_cheri/core/ariane_regfile.sv delete mode 100644 hw/vendor/cva6_cheri/core/cva6_iti/instr_to_trace.sv delete mode 100644 hw/vendor/cva6_cheri/core/cva6_iti/iti.sv delete mode 100755 hw/vendor/cva6_cheri/core/cva6_iti/itype_detector.sv create mode 100644 hw/vendor/cva6_cheri/core/include/cv64a6_imafdc_sv39_hpdcache_wb_rvfi_dii_config_pkg.sv rename hw/vendor/cva6_cheri/core/include/{cv64a6_imafdch_sv39_rvfi_dii_config_pkg.sv => cv64a6_imafdc_sv39_rvfi_dii_config_pkg.sv} (96%) rename hw/vendor/cva6_cheri/core/include/{cv64a6_imafdchzcheri_sv39_config_pkg.sv => cv64a6_imafdczcheri_sv39_config_pkg.sv} (88%) create mode 100644 hw/vendor/cva6_cheri/core/include/cv64a6_imafdczcheri_sv39_hpdcache_wb_config_pkg.sv create mode 100644 hw/vendor/cva6_cheri/core/include/cv64a6_imafdczcheri_sv39_hpdcache_wb_rvfi_dii_config_pkg.sv rename hw/vendor/cva6_cheri/core/include/{cv64a6_imafdchzcheri_sv39_rvfi_dii_config_pkg.sv => cv64a6_imafdczcheri_sv39_rvfi_dii_config_pkg.sv} (96%) rename hw/vendor/cva6_cheri/core/include/{ => deprecated_packages}/cv32a6_embedded_config_pkg_deprecated.sv (98%) rename hw/vendor/cva6_cheri/core/include/{ => deprecated_packages}/cv32a6_imac_sv0_config_pkg.sv (97%) rename hw/vendor/cva6_cheri/core/include/{ => deprecated_packages}/cv32a6_imafc_sv32_config_pkg.sv (97%) rename hw/vendor/cva6_cheri/core/include/{ => deprecated_packages}/cv64a6_imadfcv_sv39_polara_config_pkg.sv (97%) rename hw/vendor/cva6_cheri/core/include/{ => deprecated_packages}/cv64a6_mmu_config_pkg.sv (96%) delete mode 100644 hw/vendor/cva6_cheri/core/include/iti_pkg.sv delete mode 100644 hw/vendor/cva6_cheri/core/include/iti_types.svh create mode 100644 hw/vendor/cva6_cheri/core/include/triggers_pkg.sv create mode 100644 hw/vendor/cva6_cheri/core/trigger_module.sv create mode 100644 hw/vendor/cva6_cheri/docs/01_cva6_user/Trigger_Module.rst create mode 100644 hw/vendor/cva6_cheri/util/toolchain-builder/gcc-cva6-tune.patch diff --git a/hw/vendor/cva6_cheri.lock.hjson b/hw/vendor/cva6_cheri.lock.hjson index 14d0a948..6856e276 100644 --- a/hw/vendor/cva6_cheri.lock.hjson +++ b/hw/vendor/cva6_cheri.lock.hjson @@ -9,6 +9,6 @@ upstream: { url: https://github.com/lowRISC/cva6-cheri.git - rev: 94a9d6f4eb2f7d59d9e66db7c39b522224312768 + rev: 3c596eb647692e34df246401945804b5a5931041 } } diff --git a/hw/vendor/cva6_cheri/.gitlab-ci.yml b/hw/vendor/cva6_cheri/.gitlab-ci.yml index abdb74b8..6cce0f4c 100644 --- a/hw/vendor/cva6_cheri/.gitlab-ci.yml +++ b/hw/vendor/cva6_cheri/.gitlab-ci.yml @@ -238,19 +238,20 @@ hello-pk: - when: manual allow_failure: true -iti-test: +it-test: extends: - .synthesis_test variables: - DASHBOARD_JOB_TITLE: "ITI test" - DASHBOARD_JOB_DESCRIPTION: "Short test to challenge the Instruction Trace Interface" + DASHBOARD_JOB_TITLE: "Instruction Trace test" + DASHBOARD_JOB_DESCRIPTION: "Test to Challenge the Hardware flow of the Instruction Tracer" DASHBOARD_SORT_INDEX: 0 DASHBOARD_JOB_CATEGORY: "Basic" DV_SIMULATORS: "vcs-testharness" script: - - bash verif/regress/iti_test.sh - - diff .gitlab-ci/iti_reference.trace .gitlab-ci/iti.trace + - python3 .gitlab-ci/scripts/report_fail.py + - bash verif/regress/Instr_tracing_test.sh ../tests/custom/ITI/test_iti_asm.o - python3 .gitlab-ci/scripts/report_pass.py + - cp -r verif/sim/Instr_tracing_artifact artifacts/ spyglass: extends: diff --git a/hw/vendor/cva6_cheri/.gitmodules b/hw/vendor/cva6_cheri/.gitmodules index 4ff4b7c7..153db3b2 100644 --- a/hw/vendor/cva6_cheri/.gitmodules +++ b/hw/vendor/cva6_cheri/.gitmodules @@ -21,7 +21,7 @@ url = https://github.com/lowRISC/ariane-ethernet.git [submodule "corev_apu/src/axi_riscv_atomics"] path = corev_apu/src/axi_riscv_atomics - url = https://github.com/pulp-platform/axi_riscv_atomics.git + url = https://github.com/ninolomata/axi_riscv_atomics.git [submodule "corev_apu/riscv-dbg"] path = corev_apu/riscv-dbg url = https://github.com/pulp-platform/riscv-dbg.git @@ -42,7 +42,7 @@ url = https://github.com/openhwgroup/cvfpu.git [submodule "core/cache_subsystem/hpdcache"] path = core/cache_subsystem/hpdcache - url = https://github.com/openhwgroup/cv-hpdcache.git + url = https://github.com/Capabilities-Limited/cv-hpdcache.git [submodule "verif/sim/dv"] path = verif/sim/dv url = https://github.com/google/riscv-dv.git @@ -57,7 +57,7 @@ url = https://github.com/pulp-platform/gpio.git [submodule "vendor/zero-day/axi_tagcontroller"] path = vendor/zero-day/axi_tagcontroller - url = https://github.com/ninolomata/axi_cheri_tagcontroller.git + url = https://github.com/Capabilities-Limited/axi_cheri_tagcontroller.git [submodule "corev_apu/tb/tb_testRig_cheri/src/RVFI-DII-utils"] path = corev_apu/tb/tb_testRig_cheri/src/RVFI-DII-utils url = https://github.com/CTSRD-CHERI/RVFI-DII-utils.git diff --git a/hw/vendor/cva6_cheri/Bender.yml b/hw/vendor/cva6_cheri/Bender.yml index 1bb70ab6..c725beb5 100644 --- a/hw/vendor/cva6_cheri/Bender.yml +++ b/hw/vendor/cva6_cheri/Bender.yml @@ -10,12 +10,13 @@ dependencies: axi: { git: "https://github.com/pulp-platform/axi.git", version: 0.31.0 } common_cells: { git: "https://github.com/pulp-platform/common_cells", version: 1.23.0 } - fpnew: { git: "https://github.com/openhwgroup/cvfpu.git", version: 0.7.0 } + fpnew: { git: "https://github.com/openhwgroup/cvfpu.git", rev: 58ca3c3 } # branch: develop tech_cells_generic: { git: "https://github.com/pulp-platform/tech_cells_generic.git", version: 0.2.13 } export_include_dirs: - core/include + - core/cache_subsystem/hpdcache/rtl/include sources: - include_dirs: @@ -35,6 +36,14 @@ sources: files: - core/include/cv64a6_imafdc_sv39_wb_config_pkg.sv + - target: cv64a6_imafdc_sv39_hpdcache + files: + - core/include/cv64a6_imafdc_sv39_hpdcache_config_pkg.sv + + - target: cv64a6_imafdc_sv39_hpdcache_wb + files: + - core/include/cv64a6_imafdc_sv39_hpdcache_wb_config_pkg.sv + - target: cv64a6_imafdch_sv39 files: - core/include/cv64a6_imafdch_sv39_config_pkg.sv @@ -55,6 +64,14 @@ sources: files: - core/include/cv32a6_imafc_sv32_config_pkg.sv + - target: cv32a60x + files: + - core/include/cv32a60x_config_pkg.sv + + - target: cv32a65x + files: + - core/include/cv32a65x_config_pkg.sv + # General config infrastructure - core/include/riscv_pkg.sv - core/include/ariane_pkg.sv @@ -65,7 +82,7 @@ sources: - core/cva6_accel_first_pass_decoder_stub.sv # MMU - - target: any(cv64a6_imafdcv_sv39, cv64a6_imafdc_sv39, cv64a6_imafdc_sv39_wb, cv64a6_imafdch_sv39, cv64a6_imafdch_sv39_wb, cv32a6_imac_sv0, cv32a6_imac_sv32, cv32a6_imafc_sv32) + - target: any(cv64a6_imafdcv_sv39, cv64a6_imafdc_sv39, cv64a6_imafdc_sv39_wb, cv64a6_imafdch_sv39, cv64a6_imafdc_sv39_hpdcache, cv64a6_imafdc_sv39_hpdcache_wb, cv64a6_imafdch_sv39_wb, cv32a6_imac_sv0, cv32a6_imac_sv32, cv32a6_imafc_sv32) files: - core/cva6_mmu/cva6_tlb.sv - core/cva6_mmu/cva6_shared_tlb.sv @@ -75,6 +92,7 @@ sources: # Packages - core/include/wt_cache_pkg.sv - core/include/std_cache_pkg.sv + - core/include/aes_pkg.sv # Extension Interface - core/cvxif_example/include/cvxif_instr_pkg.sv @@ -89,7 +107,9 @@ sources: - core/cva6_fifo_v3.sv # Top-level source files (not necessarily instantiated at the top of the cva6). - core/cva6.sv + - core/aes.sv - core/alu.sv + - core/alu_wrapper.sv - core/fpu_wrap.sv # depends on fpnew_pkg, above - core/branch_unit.sv - core/compressed_decoder.sv @@ -144,6 +164,61 @@ sources: - core/cache_subsystem/cache_ctrl.sv - core/cache_subsystem/std_nbdcache.sv - core/cache_subsystem/std_cache_subsystem.sv + # HPDCache sources + - core/cache_subsystem/hpdcache/rtl/src/hpdcache_pkg.sv + - core/cache_subsystem/hpdcache/rtl/src/utils/hpdcache_mem_resp_demux.sv + - core/cache_subsystem/hpdcache/rtl/src/utils/hpdcache_mem_to_axi_read.sv + - core/cache_subsystem/hpdcache/rtl/src/utils/hpdcache_mem_to_axi_write.sv + - core/cache_subsystem/hpdcache/rtl/src/utils/hpdcache_mem_req_read_arbiter.sv + - core/cache_subsystem/hpdcache/rtl/src/utils/hpdcache_mem_req_write_arbiter.sv + - core/cache_subsystem/hpdcache/rtl/src/common/hpdcache_demux.sv + - core/cache_subsystem/hpdcache/rtl/src/common/hpdcache_lfsr.sv + - core/cache_subsystem/hpdcache/rtl/src/common/hpdcache_sync_buffer.sv + - core/cache_subsystem/hpdcache/rtl/src/common/hpdcache_fifo_reg.sv + - core/cache_subsystem/hpdcache/rtl/src/common/hpdcache_fifo_reg_initialized.sv + - core/cache_subsystem/hpdcache/rtl/src/common/hpdcache_fxarb.sv + - core/cache_subsystem/hpdcache/rtl/src/common/hpdcache_rrarb.sv + - core/cache_subsystem/hpdcache/rtl/src/common/hpdcache_mux.sv + - core/cache_subsystem/hpdcache/rtl/src/common/hpdcache_decoder.sv + - core/cache_subsystem/hpdcache/rtl/src/common/hpdcache_1hot_to_binary.sv + - core/cache_subsystem/hpdcache/rtl/src/common/hpdcache_prio_1hot_encoder.sv + - core/cache_subsystem/hpdcache/rtl/src/common/hpdcache_sram.sv + - core/cache_subsystem/hpdcache/rtl/src/common/hpdcache_sram_wbyteenable.sv + - core/cache_subsystem/hpdcache/rtl/src/common/hpdcache_sram_wmask.sv + - core/cache_subsystem/hpdcache/rtl/src/common/hpdcache_regbank_wbyteenable_1rw.sv + - core/cache_subsystem/hpdcache/rtl/src/common/hpdcache_regbank_wmask_1rw.sv + - core/cache_subsystem/hpdcache/rtl/src/common/hpdcache_data_downsize.sv + - core/cache_subsystem/hpdcache/rtl/src/common/hpdcache_data_upsize.sv + - core/cache_subsystem/hpdcache/rtl/src/common/hpdcache_data_resize.sv + - core/cache_subsystem/hpdcache/rtl/src/hwpf_stride/hwpf_stride_pkg.sv + - core/cache_subsystem/hpdcache/rtl/src/hwpf_stride/hwpf_stride.sv + - core/cache_subsystem/hpdcache/rtl/src/hwpf_stride/hwpf_stride_arb.sv + - core/cache_subsystem/hpdcache/rtl/src/hwpf_stride/hwpf_stride_wrapper.sv + - core/cache_subsystem/hpdcache/rtl/src/hpdcache.sv + - core/cache_subsystem/hpdcache/rtl/src/hpdcache_amo.sv + - core/cache_subsystem/hpdcache/rtl/src/hpdcache_cmo.sv + - core/cache_subsystem/hpdcache/rtl/src/hpdcache_core_arbiter.sv + - core/cache_subsystem/hpdcache/rtl/src/hpdcache_ctrl.sv + - core/cache_subsystem/hpdcache/rtl/src/hpdcache_ctrl_pe.sv + - core/cache_subsystem/hpdcache/rtl/src/hpdcache_memctrl.sv + - core/cache_subsystem/hpdcache/rtl/src/hpdcache_miss_handler.sv + - core/cache_subsystem/hpdcache/rtl/src/hpdcache_mshr.sv + - core/cache_subsystem/hpdcache/rtl/src/hpdcache_rtab.sv + - core/cache_subsystem/hpdcache/rtl/src/hpdcache_uncached.sv + - core/cache_subsystem/hpdcache/rtl/src/hpdcache_victim_plru.sv + - core/cache_subsystem/hpdcache/rtl/src/hpdcache_victim_random.sv + - core/cache_subsystem/hpdcache/rtl/src/hpdcache_victim_sel.sv + - core/cache_subsystem/hpdcache/rtl/src/hpdcache_wbuf.sv + - core/cache_subsystem/hpdcache/rtl/src/hpdcache_flush.sv + # HPDCache integration + - core/cache_subsystem/cva6_hpdcache_if_adapter.sv + - core/cache_subsystem/cva6_hpdcache_subsystem_axi_arbiter.sv + - core/cache_subsystem/cva6_hpdcache_subsystem.sv + - core/cache_subsystem/cva6_hpdcache_wrapper.sv + # HPDCache SRAM models + - core/cache_subsystem/hpdcache/rtl/src/common/macros/behav/hpdcache_sram_1rw.sv + - core/cache_subsystem/hpdcache/rtl/src/common/macros/behav/hpdcache_sram_wbyteenable_1rw.sv + - core/cache_subsystem/hpdcache/rtl/src/common/macros/behav/hpdcache_sram_wmask_1rw.sv # Physical Memory Protection - core/pmp/src/pmp.sv @@ -161,6 +236,7 @@ sources: files: - common/local/util/tc_sram_wrapper.sv - common/local/util/sram_cache.sv + - common/local/util/tc_sram_wrapper_cache_techno.sv - target: all(fpga, xilinx) include_dirs: diff --git a/hw/vendor/cva6_cheri/Flist.ariane b/hw/vendor/cva6_cheri/Flist.ariane index 103cd41d..0f4e2d64 100644 --- a/hw/vendor/cva6_cheri/Flist.ariane +++ b/hw/vendor/cva6_cheri/Flist.ariane @@ -60,6 +60,7 @@ vendor/pulp-platform/tech_cells_generic/src/rtl/tc_sram.sv vendor/pulp-platform/tech_cells_generic/src/rtl/tc_clk.sv core/cache_subsystem/axi_adapter.sv core/alu.sv +core/alu_wrapper.sv core/fpu_wrap.sv corev_apu/src/ariane.sv core/cva6.sv diff --git a/hw/vendor/cva6_cheri/Makefile b/hw/vendor/cva6_cheri/Makefile index 276a67e6..a602927b 100644 --- a/hw/vendor/cva6_cheri/Makefile +++ b/hw/vendor/cva6_cheri/Makefile @@ -104,7 +104,7 @@ endif # target takes one of the following cva6 hardware configuration: # cv64a6_imafdc_sv39, cv32a6_imac_sv0, cv32a6_imac_sv32, cv32a6_imafc_sv32, cv32a6_ima_sv32_fpga # Changing the default target to cv32a60x for Step1 verification -target ?= cv64a6_imafdchzcheri_sv39 +target ?= cv64a6_imafdczcheri_sv39_hpdcache_wb ifneq (,$(findstring cv64,$(target))) XLEN ?= 64 else @@ -213,7 +213,9 @@ src := $(if $(spike-tandem),verif/tb/core/uvma_core_cntrl_pkg.sv) vendor/pulp-platform/tech_cells_generic/src/deprecated/cluster_clk_cells.sv \ vendor/pulp-platform/tech_cells_generic/src/deprecated/pulp_clk_cells.sv \ vendor/pulp-platform/tech_cells_generic/src/rtl/tc_clk.sv \ - core/include/iti_pkg.sv \ + corev_apu/instr_tracing/ITI/include/iti_pkg.sv \ + corev_apu/instr_tracing/rv_tracer-main/include/te_pkg.sv \ + corev_apu/instr_tracing/rv_encapsulator-main/src/include/encap_pkg.sv \ vendor/zero-day/axi_tagcontroller/src/axi_llc/src/axi_llc_burst_cutter.sv \ vendor/zero-day/axi_tagcontroller/src/axi_llc/src/axi_llc_data_way.sv \ vendor/zero-day/axi_tagcontroller/src/axi_llc/src/axi_llc_merge_unit.sv \ @@ -251,10 +253,25 @@ src := $(if $(spike-tandem),verif/tb/core/uvma_core_cntrl_pkg.sv) corev_apu/tb/common/uart.sv \ corev_apu/tb/common/SimDTM.sv \ corev_apu/tb/common/SimJTAG.sv \ - core/cva6_iti/instr_to_trace.sv \ - core/cva6_iti/iti.sv \ - core/cva6_iti/itype_detector.sv - + corev_apu/instr_tracing/ITI/cva6_iti/iti.sv \ + corev_apu/instr_tracing/ITI/cva6_iti/block_retirement.sv \ + corev_apu/instr_tracing/ITI/cva6_iti/single_retirement.sv \ + corev_apu/instr_tracing/ITI/cva6_iti/itype_detector.sv \ + vendor/pulp-platform/common_cells/src/counter.sv \ + vendor/pulp-platform/common_cells/src/sync.sv \ + vendor/pulp-platform/common_cells/src/sync_wedge.sv \ + vendor/pulp-platform/common_cells/src/edge_detect.sv \ + corev_apu/instr_tracing/rv_tracer-main/rtl/lzc.sv \ + corev_apu/instr_tracing/rv_tracer-main/rtl/te_branch_map.sv \ + corev_apu/instr_tracing/rv_tracer-main/rtl/te_filter.sv \ + corev_apu/instr_tracing/rv_tracer-main/rtl/te_packet_emitter.sv \ + corev_apu/instr_tracing/rv_tracer-main/rtl/te_priority.sv \ + corev_apu/instr_tracing/rv_tracer-main/rtl/te_reg.sv \ + corev_apu/instr_tracing/rv_tracer-main/rtl/te_resync_counter.sv \ + corev_apu/instr_tracing/rv_tracer-main/rtl/rv_tracer.sv \ + vendor/pulp-platform/common_cells/src/fifo_v3.sv \ + corev_apu/instr_tracing/DPTI/slicer_DPTI.sv \ + corev_apu/instr_tracing/rv_encapsulator-main/src/rtl/encapsulator.sv src := $(addprefix $(root-dir), $(src)) copro_src := core/cvxif_example/include/cvxif_instr_pkg.sv \ @@ -264,6 +281,9 @@ copro_src := $(addprefix $(root-dir), $(copro_src)) uart_src := $(wildcard corev_apu/fpga/src/apb_uart/src/vhdl_orig/*.vhd) uart_src := $(addprefix $(root-dir), $(uart_src)) +dpti_src := $(wildcard corev_apu/instr_tracing/DPTI/*.vhd) +dpti_src := $(addprefix $(root-dir), $(dpti_src)) + uart_src_sv:= corev_apu/fpga/src/apb_uart/src/slib_clock_div.sv \ corev_apu/fpga/src/apb_uart/src/slib_counter.sv \ corev_apu/fpga/src/apb_uart/src/slib_edge_detect.sv \ @@ -361,6 +381,7 @@ incdir := $(CVA6_REPO_DIR)/vendor/pulp-platform/common_cells/include/ $(CVA6_REP $(CVA6_REPO_DIR)/verif/core-v-verif/lib/uvm_agents/uvma_core_cntrl/ \ $(CVA6_REPO_DIR)/verif/tb/core/ \ $(CVA6_REPO_DIR)/core/include/ \ + $(CVA6_REPO_DIR)/corev_apu/instr_tracing/ITI/include \ $(SPIKE_INSTALL_DIR)/include/disasm/ # Compile and sim flags @@ -833,9 +854,10 @@ fpga_filter += $(addprefix $(root-dir), core/cache_subsystem/hpdcache/rtl/src/co $(addprefix $(root-dir), corev_apu/fpga/src/bootrom/bootrom_$(XLEN).sv): $(MAKE) -C corev_apu/fpga/src/bootrom BOARD=$(BOARD) XLEN=$(XLEN) PLATFORM=$(PLATFORM) bootrom_$(XLEN).sv -fpga: $(ariane_pkg) $(src) $(fpga_src) $(uart_src) $(src_flist) +fpga: $(ariane_pkg) $(src) $(fpga_src) $(uart_src) $(dpti_src) $(src_flist) @echo "[FPGA] Generate sources" @echo read_vhdl {$(uart_src)} > corev_apu/fpga/scripts/add_sources.tcl + @echo read_vhdl {$(dpti_src)} >> corev_apu/fpga/scripts/add_sources.tcl @echo read_verilog -sv {$(ariane_pkg)} >> corev_apu/fpga/scripts/add_sources.tcl @echo read_verilog -sv {$(filter-out $(fpga_filter), $(src_flist))} >> corev_apu/fpga/scripts/add_sources.tcl @echo read_verilog -sv {$(filter-out $(fpga_filter), $(src))} >> corev_apu/fpga/scripts/add_sources.tcl diff --git a/hw/vendor/cva6_cheri/README.md b/hw/vendor/cva6_cheri/README.md index 3adca597..c7e4ec03 100644 --- a/hw/vendor/cva6_cheri/README.md +++ b/hw/vendor/cva6_cheri/README.md @@ -67,7 +67,7 @@ bash verif/regress/smoke-tests.sh * **[Running Simulations](tutorials/running_sim.md)** * **[ASIC Implementation](tutorials/asic.md)** * **[FPGA Implementation and running an OS](tutorials/fpga.md)** - +* **[Instruction Tracing](corev_apu/instr_tracing/README.md)** # Directory Structure diff --git a/hw/vendor/cva6_cheri/ci/install-verilator.sh b/hw/vendor/cva6_cheri/ci/install-verilator.sh index b100a656..a02a4755 100755 --- a/hw/vendor/cva6_cheri/ci/install-verilator.sh +++ b/hw/vendor/cva6_cheri/ci/install-verilator.sh @@ -10,7 +10,7 @@ fi VERILATOR_REPO="https://github.com/verilator/verilator.git" VERILATOR_BRANCH="master" # Use the release tag instead of a full SHA1 hash. -VERILATOR_HASH="v5.008" +VERILATOR_HASH="v5.038" VERILATOR_PATCH="$ROOT/verif/regress/verilator-v5.patch" VERILATOR_BUILD_DIR=$PWD/verilator-$VERILATOR_HASH/verilator diff --git a/hw/vendor/cva6_cheri/common/local/util/instr_trace_item.svh b/hw/vendor/cva6_cheri/common/local/util/instr_trace_item.svh index c1ea36e2..7bcbdc3d 100644 --- a/hw/vendor/cva6_cheri/common/local/util/instr_trace_item.svh +++ b/hw/vendor/cva6_cheri/common/local/util/instr_trace_item.svh @@ -41,6 +41,8 @@ class instr_trace_item #( logic result_fpr [$]; logic [63:0] imm; logic [63:0] result; + logic dest_we_valid; + logic dest_is_fp; logic [CVA6Cfg.PLEN-1:0] paddr; string priv_lvl; bp_resolve_t bp; @@ -49,7 +51,7 @@ class instr_trace_item #( // constructor creating a new instruction trace item, e.g.: a single instruction with all relevant information function new (time simtime, longint unsigned cycle, scoreboard_entry_t sbe, logic [31:0] instr, logic [63:0] gp_reg_file [32], - logic [63:0] fp_reg_file [32], logic [63:0] result, logic [CVA6Cfg.PLEN-1:0] paddr, riscv::priv_lvl_t priv_lvl, logic debug_mode, bp_resolve_t bp); + logic [63:0] fp_reg_file [32], logic [63:0] result, logic dest_we_valid, logic dest_is_fp, logic [CVA6Cfg.PLEN-1:0] paddr, riscv::priv_lvl_t priv_lvl, logic debug_mode, bp_resolve_t bp); this.simtime = simtime; this.cycle = cycle; this.pc = sbe.pc; @@ -57,6 +59,8 @@ class instr_trace_item #( this.gp_reg_file = gp_reg_file; this.fp_reg_file = fp_reg_file; this.result = result; + this.dest_we_valid = dest_we_valid; + this.dest_is_fp = dest_is_fp; this.paddr = paddr; this.bp = bp; this.priv_lvl = (debug_mode) ? "D" : getPrivLevel(priv_lvl); @@ -195,6 +199,68 @@ class instr_trace_item #( endcase endfunction + function logic scoreboardDestIsFp(); + if (sbe.fu inside {ariane_pkg::FPU, ariane_pkg::FPU_VEC}) begin + return ariane_pkg::fd_changes_rd_state(sbe.op); + end + return ariane_pkg::is_rd_fpr(sbe.op); + endfunction + + function logic rawDestIsFp(); + if (dest_we_valid) begin + return dest_is_fp; + end + return scoreboardDestIsFp(); + endfunction + + function logic fpInstrForcesGprDest(); + logic [6:0] opcode = instr[6:0]; + logic [4:0] funct5 = instr[31:27]; + logic [2:0] rm = instr[14:12]; + + if (opcode == riscv::OpcodeOpFp) begin + if (funct5 == 5'b11000) begin + // fcvt.*.* instructions that target an integer register + return 1'b1; + end + + if (funct5 == 5'b10100) begin + // fle.*, flt.*, feq.* comparisons produce integer results. + return 1'b1; + end + + if (funct5 == 5'b11100) begin + // fmv.x.* (rm == 000) and fclass (rm == 001) always write GPRs. + if ((rm == 3'b000) || (rm == 3'b001)) begin + return 1'b1; + end + // Alternate encodings when FP16ALT is enabled mirror rm encodings. + if (CVA6Cfg.XF16ALT && ((rm == 3'b100) || (rm == 3'b101))) begin + return 1'b1; + end + end + end + + return 1'b0; + endfunction + + function logic effectiveDestIsFp(); + logic dest_is_fp_raw = rawDestIsFp(); + + if (fpInstrForcesGprDest()) begin + return 1'b0; + end + + return dest_is_fp_raw; + endfunction + + function void adjustResultRegKinds(); + logic final_dest_is_fp = effectiveDestIsFp(); + foreach (result_fpr[i]) begin + result_fpr[i] = final_dest_is_fp; + end + endfunction + function string printInstr(); string s; @@ -379,6 +445,8 @@ class instr_trace_item #( // instr, // s); + adjustResultRegKinds(); + foreach (result_regs[i]) begin if (result_fpr[i]) s = $sformatf("%s %-4s:%16x", s, fpRegAddrToStr(result_regs[i]), this.result); @@ -466,45 +534,48 @@ class instr_trace_item #( function string printRFBCInstr(input string mnemonic, input bit use_rnd); + logic dest_fp = effectiveDestIsFp(); result_regs.push_back(rd); - result_fpr.push_back(ariane_pkg::is_rd_fpr(sbe.op)); + result_fpr.push_back(dest_fp); read_regs.push_back(rs2); read_fpr.push_back(ariane_pkg::is_rs2_fpr(sbe.op)); read_regs.push_back(sbe.result[4:0]); read_fpr.push_back(ariane_pkg::is_imm_fpr(sbe.op)); if (use_rnd && instr[14:12]!=3'b111) - return $sformatf("%-12s %4s, %s, %s, %s", $sformatf("%s.%s",mnemonic, fpFmtToStr(instr[26:25])), ariane_pkg::is_rd_fpr(sbe.op)?fpRegAddrToStr(rd):regAddrToStr(rd), ariane_pkg::is_rs2_fpr(sbe.op)?fpRegAddrToStr(rs2):regAddrToStr(rs2), ariane_pkg::is_imm_fpr(sbe.op)?fpRegAddrToStr(sbe.result[4:0]):regAddrToStr(sbe.result[4:0]), fpRmToStr(instr[14:12])); + return $sformatf("%-12s %4s, %s, %s, %s", $sformatf("%s.%s",mnemonic, fpFmtToStr(instr[26:25])), dest_fp?fpRegAddrToStr(rd):regAddrToStr(rd), ariane_pkg::is_rs2_fpr(sbe.op)?fpRegAddrToStr(rs2):regAddrToStr(rs2), ariane_pkg::is_imm_fpr(sbe.op)?fpRegAddrToStr(sbe.result[4:0]):regAddrToStr(sbe.result[4:0]), fpRmToStr(instr[14:12])); else - return $sformatf("%-12s %4s, %s, %s", $sformatf("%s.%s",mnemonic, fpFmtToStr(instr[26:25])), ariane_pkg::is_rd_fpr(sbe.op)?fpRegAddrToStr(rd):regAddrToStr(rd), ariane_pkg::is_rs2_fpr(sbe.op)?fpRegAddrToStr(rs2):regAddrToStr(rs2), ariane_pkg::is_imm_fpr(sbe.op)?fpRegAddrToStr(sbe.result[4:0]):regAddrToStr(sbe.result[4:0])); + return $sformatf("%-12s %4s, %s, %s", $sformatf("%s.%s",mnemonic, fpFmtToStr(instr[26:25])), dest_fp?fpRegAddrToStr(rd):regAddrToStr(rd), ariane_pkg::is_rs2_fpr(sbe.op)?fpRegAddrToStr(rs2):regAddrToStr(rs2), ariane_pkg::is_imm_fpr(sbe.op)?fpRegAddrToStr(sbe.result[4:0]):regAddrToStr(sbe.result[4:0])); endfunction // printRFInstr function string printRFInstr(input string mnemonic, input bit use_rnd); + logic dest_fp = effectiveDestIsFp(); result_regs.push_back(rd); - result_fpr.push_back(ariane_pkg::is_rd_fpr(sbe.op)); + result_fpr.push_back(dest_fp); read_regs.push_back(rs1); read_fpr.push_back(ariane_pkg::is_rs1_fpr(sbe.op)); read_regs.push_back(rs2); read_fpr.push_back(ariane_pkg::is_rs2_fpr(sbe.op)); if (use_rnd && instr[14:12]!=3'b111) - return $sformatf("%-12s %4s, %s, %s, %s", $sformatf("%s.%s",mnemonic, fpFmtToStr(instr[26:25])), ariane_pkg::is_rd_fpr(sbe.op)?fpRegAddrToStr(rd):regAddrToStr(rd), ariane_pkg::is_rs1_fpr(sbe.op)?fpRegAddrToStr(rs1):regAddrToStr(rs1), ariane_pkg::is_rs2_fpr(sbe.op)?fpRegAddrToStr(rs2):regAddrToStr(rs2), fpRmToStr(instr[14:12])); + return $sformatf("%-12s %4s, %s, %s, %s", $sformatf("%s.%s",mnemonic, fpFmtToStr(instr[26:25])), dest_fp?fpRegAddrToStr(rd):regAddrToStr(rd), ariane_pkg::is_rs1_fpr(sbe.op)?fpRegAddrToStr(rs1):regAddrToStr(rs1), ariane_pkg::is_rs2_fpr(sbe.op)?fpRegAddrToStr(rs2):regAddrToStr(rs2), fpRmToStr(instr[14:12])); else - return $sformatf("%-12s %4s, %s, %s", $sformatf("%s.%s",mnemonic, fpFmtToStr(instr[26:25])), ariane_pkg::is_rd_fpr(sbe.op)?fpRegAddrToStr(rd):regAddrToStr(rd), ariane_pkg::is_rs1_fpr(sbe.op)?fpRegAddrToStr(rs1):regAddrToStr(rs1), ariane_pkg::is_rs2_fpr(sbe.op)?fpRegAddrToStr(rs2):regAddrToStr(rs2)); + return $sformatf("%-12s %4s, %s, %s", $sformatf("%s.%s",mnemonic, fpFmtToStr(instr[26:25])), dest_fp?fpRegAddrToStr(rd):regAddrToStr(rd), ariane_pkg::is_rs1_fpr(sbe.op)?fpRegAddrToStr(rs1):regAddrToStr(rs1), ariane_pkg::is_rs2_fpr(sbe.op)?fpRegAddrToStr(rs2):regAddrToStr(rs2)); endfunction // printRFInstr function string printRFInstr1Op(input string mnemonic, input bit use_rnd); + logic dest_fp = effectiveDestIsFp(); result_regs.push_back(rd); - result_fpr.push_back(ariane_pkg::is_rd_fpr(sbe.op)); + result_fpr.push_back(dest_fp); read_regs.push_back(rs1); read_fpr.push_back(ariane_pkg::is_rs1_fpr(sbe.op)); if (use_rnd && instr[14:12]!=3'b111) - return $sformatf("%-12s %4s, %s, %s", $sformatf("%s.%s",mnemonic, fpFmtToStr(instr[26:25])), ariane_pkg::is_rd_fpr(sbe.op)?fpRegAddrToStr(rd):regAddrToStr(rd), ariane_pkg::is_rs1_fpr(sbe.op)?fpRegAddrToStr(rs1):regAddrToStr(rs1), fpRmToStr(instr[14:12])); + return $sformatf("%-12s %4s, %s, %s", $sformatf("%s.%s",mnemonic, fpFmtToStr(instr[26:25])), dest_fp?fpRegAddrToStr(rd):regAddrToStr(rd), ariane_pkg::is_rs1_fpr(sbe.op)?fpRegAddrToStr(rs1):regAddrToStr(rs1), fpRmToStr(instr[14:12])); else - return $sformatf("%-12s %4s, %s", $sformatf("%s.%s",mnemonic, fpFmtToStr(instr[26:25])), ariane_pkg::is_rd_fpr(sbe.op)?fpRegAddrToStr(rd):regAddrToStr(rd), ariane_pkg::is_rs1_fpr(sbe.op)?fpRegAddrToStr(rs1):regAddrToStr(rs1)); + return $sformatf("%-12s %4s, %s", $sformatf("%s.%s",mnemonic, fpFmtToStr(instr[26:25])), dest_fp?fpRegAddrToStr(rd):regAddrToStr(rd), ariane_pkg::is_rs1_fpr(sbe.op)?fpRegAddrToStr(rs1):regAddrToStr(rs1)); endfunction // printRFInstr1Op function string printR4Instr(input string mnemonic); @@ -523,8 +594,9 @@ class instr_trace_item #( function string printFpSpecialInstr(); + logic dest_fp = effectiveDestIsFp(); result_regs.push_back(rd); - result_fpr.push_back(ariane_pkg::is_rd_fpr(sbe.op)); + result_fpr.push_back(dest_fp); read_regs.push_back(rs1); read_fpr.push_back(ariane_pkg::is_rs1_fpr(sbe.op)); @@ -634,8 +706,9 @@ class instr_trace_item #( endfunction // printCSRInstr function string printLoadInstr(input string mnemonic); + logic dest_fp = effectiveDestIsFp(); result_regs.push_back(rd); - result_fpr.push_back(ariane_pkg::is_rd_fpr(sbe.op)); + result_fpr.push_back(dest_fp); read_regs.push_back(rs1); read_fpr.push_back(1'b0); // save the immediate for calculating the virtual address diff --git a/hw/vendor/cva6_cheri/common/local/util/instr_tracer.sv b/hw/vendor/cva6_cheri/common/local/util/instr_tracer.sv index 5376d5c1..dad2f29c 100644 --- a/hw/vendor/cva6_cheri/common/local/util/instr_tracer.sv +++ b/hw/vendor/cva6_cheri/common/local/util/instr_tracer.sv @@ -168,11 +168,41 @@ module instr_tracer #( // check if the write back is valid, if not we need to source the result from the register file // as the most recent version of this register will be there. if (we_gpr[i] || we_fpr[i]) begin - printInstr(issue_sbe_item, issue_commit_instruction, wdata[i], address_mapping, priv_lvl, debug_mode, bp_instruction); + printInstr( + issue_sbe_item, + issue_commit_instruction, + wdata[i], + we_gpr[i] || we_fpr[i], + we_fpr[i], + address_mapping, + priv_lvl, + debug_mode, + bp_instruction + ); end else if (ariane_pkg::is_rd_fpr(commit_instruction.op)) begin - printInstr(issue_sbe_item, issue_commit_instruction, fp_reg_file[commit_instruction.rd], address_mapping, priv_lvl, debug_mode, bp_instruction); + printInstr( + issue_sbe_item, + issue_commit_instruction, + fp_reg_file[commit_instruction.rd], + 1'b0, + 1'b1, + address_mapping, + priv_lvl, + debug_mode, + bp_instruction + ); end else begin - printInstr(issue_sbe_item, issue_commit_instruction, gp_reg_file[commit_instruction.rd], address_mapping, priv_lvl, debug_mode, bp_instruction); + printInstr( + issue_sbe_item, + issue_commit_instruction, + gp_reg_file[commit_instruction.rd], + 1'b0, + 1'b0, + address_mapping, + priv_lvl, + debug_mode, + bp_instruction + ); end end // -------------- @@ -224,16 +254,31 @@ module instr_tracer #( bp = {}; endfunction - function void printInstr(scoreboard_entry_t sbe, logic [31:0] instr, logic [63:0] result, logic [CVA6Cfg.PLEN-1:0] paddr, riscv::priv_lvl_t priv_lvl, logic debug_mode, bp_resolve_t bp); + function void printInstr(scoreboard_entry_t sbe, logic [31:0] instr, logic [63:0] result, logic dest_we_valid, logic dest_is_fp, logic [CVA6Cfg.PLEN-1:0] paddr, riscv::priv_lvl_t priv_lvl, logic debug_mode, bp_resolve_t bp); automatic instr_trace_item #( .CVA6Cfg(CVA6Cfg), .bp_resolve_t(bp_resolve_t), .scoreboard_entry_t(scoreboard_entry_t) - ) iti = new ($time, clk_ticks, sbe, instr, gp_reg_file, fp_reg_file, result, paddr, priv_lvl, debug_mode, bp); + ) iti = new ( + $time, + clk_ticks, + sbe, + instr, + gp_reg_file, + fp_reg_file, + result, + dest_we_valid, + dest_is_fp, + paddr, + priv_lvl, + debug_mode, + bp + ); // print instruction to console automatic string print_instr = iti.printInstr(); + automatic logic commit_is_fp = dest_we_valid ? dest_is_fp : ariane_pkg::is_rd_fpr(sbe.op); if (ariane_pkg::ENABLE_SPIKE_COMMIT_LOG && !debug_mode) begin - $fwrite(commit_log, riscv::spikeCommitLog(sbe.pc, priv_lvl, instr, sbe.rd, result, ariane_pkg::is_rd_fpr(sbe.op))); + $fwrite(commit_log, riscv::spikeCommitLog(sbe.pc, priv_lvl, instr, sbe.rd, result, commit_is_fp)); end $fwrite(f, {print_instr, "\n"}); endfunction diff --git a/hw/vendor/cva6_cheri/core/Flist.cva6 b/hw/vendor/cva6_cheri/core/Flist.cva6 index d6a91ecf..c5c46353 100644 --- a/hw/vendor/cva6_cheri/core/Flist.cva6 +++ b/hw/vendor/cva6_cheri/core/Flist.cva6 @@ -42,6 +42,31 @@ ${CVA6_REPO_DIR}/vendor/pulp-platform/fpga-support/rtl/SyncDpRam_ind_r_w.sv ${CVA6_REPO_DIR}/core/cvfpu/src/fpnew_pkg.sv ${CVA6_REPO_DIR}/core/cvfpu/src/fpnew_cast_multi.sv ${CVA6_REPO_DIR}/core/cvfpu/src/fpnew_classifier.sv +${CVA6_REPO_DIR}/core/cvfpu/vendor/opene906/E906_RTL_FACTORY/gen_rtl/clk/rtl/gated_clk_cell.v +${CVA6_REPO_DIR}/core/cvfpu/vendor/opene906/E906_RTL_FACTORY/gen_rtl/fdsu/rtl/pa_fdsu_ctrl.v +${CVA6_REPO_DIR}/core/cvfpu/vendor/opene906/E906_RTL_FACTORY/gen_rtl/fdsu/rtl/pa_fdsu_ff1.v +${CVA6_REPO_DIR}/core/cvfpu/vendor/opene906/E906_RTL_FACTORY/gen_rtl/fdsu/rtl/pa_fdsu_pack_single.v +${CVA6_REPO_DIR}/core/cvfpu/vendor/opene906/E906_RTL_FACTORY/gen_rtl/fdsu/rtl/pa_fdsu_prepare.v +${CVA6_REPO_DIR}/core/cvfpu/vendor/opene906/E906_RTL_FACTORY/gen_rtl/fdsu/rtl/pa_fdsu_round_single.v +${CVA6_REPO_DIR}/core/cvfpu/vendor/opene906/E906_RTL_FACTORY/gen_rtl/fdsu/rtl/pa_fdsu_special.v +${CVA6_REPO_DIR}/core/cvfpu/vendor/opene906/E906_RTL_FACTORY/gen_rtl/fdsu/rtl/pa_fdsu_srt_single.v +${CVA6_REPO_DIR}/core/cvfpu/vendor/opene906/E906_RTL_FACTORY/gen_rtl/fdsu/rtl/pa_fdsu_top.v +${CVA6_REPO_DIR}/core/cvfpu/vendor/opene906/E906_RTL_FACTORY/gen_rtl/fpu/rtl/pa_fpu_dp.v +${CVA6_REPO_DIR}/core/cvfpu/vendor/opene906/E906_RTL_FACTORY/gen_rtl/fpu/rtl/pa_fpu_frbus.v +${CVA6_REPO_DIR}/core/cvfpu/vendor/opene906/E906_RTL_FACTORY/gen_rtl/fpu/rtl/pa_fpu_src_type.v +${CVA6_REPO_DIR}/core/cvfpu/vendor/openc910/C910_RTL_FACTORY/gen_rtl/vfdsu/rtl/ct_vfdsu_ctrl.v +${CVA6_REPO_DIR}/core/cvfpu/vendor/openc910/C910_RTL_FACTORY/gen_rtl/vfdsu/rtl/ct_vfdsu_double.v +${CVA6_REPO_DIR}/core/cvfpu/vendor/openc910/C910_RTL_FACTORY/gen_rtl/vfdsu/rtl/ct_vfdsu_ff1.v +${CVA6_REPO_DIR}/core/cvfpu/vendor/openc910/C910_RTL_FACTORY/gen_rtl/vfdsu/rtl/ct_vfdsu_pack.v +${CVA6_REPO_DIR}/core/cvfpu/vendor/openc910/C910_RTL_FACTORY/gen_rtl/vfdsu/rtl/ct_vfdsu_prepare.v +${CVA6_REPO_DIR}/core/cvfpu/vendor/openc910/C910_RTL_FACTORY/gen_rtl/vfdsu/rtl/ct_vfdsu_round.v +${CVA6_REPO_DIR}/core/cvfpu/vendor/openc910/C910_RTL_FACTORY/gen_rtl/vfdsu/rtl/ct_vfdsu_scalar_dp.v +${CVA6_REPO_DIR}/core/cvfpu/vendor/openc910/C910_RTL_FACTORY/gen_rtl/vfdsu/rtl/ct_vfdsu_srt_radix16_bound_table.v +${CVA6_REPO_DIR}/core/cvfpu/vendor/openc910/C910_RTL_FACTORY/gen_rtl/vfdsu/rtl/ct_vfdsu_srt_radix16_with_sqrt.v +${CVA6_REPO_DIR}/core/cvfpu/vendor/openc910/C910_RTL_FACTORY/gen_rtl/vfdsu/rtl/ct_vfdsu_srt.v +${CVA6_REPO_DIR}/core/cvfpu/vendor/openc910/C910_RTL_FACTORY/gen_rtl/vfdsu/rtl/ct_vfdsu_top.v +${CVA6_REPO_DIR}/core/cvfpu/src/fpnew_divsqrt_th_32.sv +${CVA6_REPO_DIR}/core/cvfpu/src/fpnew_divsqrt_th_64_multi.sv ${CVA6_REPO_DIR}/core/cvfpu/src/fpnew_divsqrt_multi.sv ${CVA6_REPO_DIR}/core/cvfpu/src/fpnew_fma_multi.sv ${CVA6_REPO_DIR}/core/cvfpu/src/fpnew_fma.sv @@ -73,6 +98,7 @@ ${CVA6_REPO_DIR}/core/include/std_cache_pkg.sv ${CVA6_REPO_DIR}/core/include/instr_tracer_pkg.sv ${CVA6_REPO_DIR}/core/include/build_config_pkg.sv ${CVA6_REPO_DIR}/core/include/aes_pkg.sv +${CVA6_REPO_DIR}/core/include/triggers_pkg.sv //CVXIF ${CVA6_REPO_DIR}/core/cvxif_compressed_if_driver.sv @@ -111,6 +137,7 @@ ${CVA6_REPO_DIR}/vendor/pulp-platform/common_cells/src/delta_counter.sv ${CVA6_REPO_DIR}/core/cva6.sv ${CVA6_REPO_DIR}/core/cva6_rvfi_probes.sv ${CVA6_REPO_DIR}/core/alu.sv +${CVA6_REPO_DIR}/core/alu_wrapper.sv ${CVA6_REPO_DIR}/core/aes.sv // Note: depends on fpnew_pkg, above ${CVA6_REPO_DIR}/core/fpu_wrap.sv @@ -121,6 +148,7 @@ ${CVA6_REPO_DIR}/core/controller.sv ${CVA6_REPO_DIR}/core/zcmt_decoder.sv ${CVA6_REPO_DIR}/core/csr_buffer.sv ${CVA6_REPO_DIR}/core/csr_regfile.sv +${CVA6_REPO_DIR}/core/trigger_module.sv ${CVA6_REPO_DIR}/core/decoder.sv ${CVA6_REPO_DIR}/core/ex_stage.sv ${CVA6_REPO_DIR}/core/instr_realign.sv @@ -184,6 +212,7 @@ ${CVA6_REPO_DIR}/core/cache_subsystem/cva6_hpdcache_if_adapter.sv ${CVA6_REPO_DIR}/core/cache_subsystem/cva6_hpdcache_wrapper.sv ${HPDCACHE_DIR}/rtl/src/common/macros/behav/hpdcache_sram_1rw.sv ${HPDCACHE_DIR}/rtl/src/common/macros/behav/hpdcache_sram_wbyteenable_1rw.sv +${HPDCACHE_DIR}/rtl/src/common/macros/behav/hpdcache_sram_watomenable_1rw.sv ${HPDCACHE_DIR}/rtl/src/common/macros/behav/hpdcache_sram_wmask_1rw.sv // Physical Memory Protection @@ -200,7 +229,7 @@ ${CVA6_REPO_DIR}/vendor/pulp-platform/tech_cells_generic/src/rtl/tc_sram.sv ${CVA6_REPO_DIR}/common/local/util/sram.sv ${CVA6_REPO_DIR}/common/local/util/sram_cache.sv -// MMU +// MMU ${CVA6_REPO_DIR}/core/cva6_mmu/cva6_mmu.sv ${CVA6_REPO_DIR}/core/cva6_mmu/cva6_ptw.sv ${CVA6_REPO_DIR}/core/cva6_mmu/cva6_tlb.sv diff --git a/hw/vendor/cva6_cheri/core/alu.sv b/hw/vendor/cva6_cheri/core/alu.sv index 4cea1c5c..f3c087ac 100644 --- a/hw/vendor/cva6_cheri/core/alu.sv +++ b/hw/vendor/cva6_cheri/core/alu.sv @@ -31,16 +31,18 @@ module alu input logic rst_ni, // FU data needed to execute instruction - ISSUE_STAGE input fu_data_t fu_data_i, + // FU data needed to execute CPOP/CPOPW - ISSUE_STAGE + input fu_data_t fu_data_cpop_i, // ALU result - ISSUE_STAGE output logic [CVA6Cfg.XLEN-1:0] result_o, // ALU branch compare result - branch_unit output logic alu_branch_res_o ); - logic [ CVA6Cfg.XLEN:0] operand_a; + logic [CVA6Cfg.XLEN-1:0] operand_a; logic [CVA6Cfg.XLEN-1:0] operand_a_rev; logic [ 31:0] operand_a_rev32; - logic [ CVA6Cfg.XLEN:0] operand_b; + logic [CVA6Cfg.XLEN-1:0] operand_b; logic [ CVA6Cfg.XLEN:0] operand_b_neg; logic [CVA6Cfg.XLEN+1:0] adder_result_ext_o; logic less; // handles both signed and unsigned forms @@ -60,8 +62,8 @@ module alu logic [CVA6Cfg.XLEN-1:0] xperm4_result; // Fetch the capability address in case CHERI extension is enabled - assign operand_a = fu_data_i.operand_a[CVA6Cfg.XLEN-1:0]; - assign operand_b = fu_data_i.operand_b[CVA6Cfg.XLEN-1:0]; + assign operand_a = reg_to_x(fu_data_i.operand_a); + assign operand_b = reg_to_x(fu_data_i.operand_b); // bit reverse operand_a for left shifts and bit counting generate @@ -79,21 +81,24 @@ module alu logic [CVA6Cfg.XLEN:0] adder_in_a, adder_in_b; logic [CVA6Cfg.XLEN-1:0] adder_result; logic [CVA6Cfg.XLEN-1:0] operand_a_bitmanip, bit_indx; + logic [CVA6Cfg.XLEN-1:0] operand_a_cpop; assign adder_op_b_negate = fu_data_i.operation inside {EQ, NE, SUB, SUBW, ANDN, ORN, XNOR}; always_comb begin + operand_a_cpop = reg_to_x(fu_data_cpop_i.operand_a); operand_a_bitmanip = operand_a; if (CVA6Cfg.RVB) begin if (CVA6Cfg.IS_XLEN64) begin unique case (fu_data_i.operation) - SH1ADDUW: operand_a_bitmanip = operand_a[31:0] << 1; - SH2ADDUW: operand_a_bitmanip = operand_a[31:0] << 2; - SH3ADDUW: operand_a_bitmanip = operand_a[31:0] << 3; - CTZW: operand_a_bitmanip = operand_a_rev32; - ADDUW, CPOPW, CLZW: operand_a_bitmanip = operand_a[31:0]; - default: ; + SH1ADDUW: operand_a_bitmanip = operand_a[31:0] << 1; + SH2ADDUW: operand_a_bitmanip = operand_a[31:0] << 2; + SH3ADDUW: operand_a_bitmanip = operand_a[31:0] << 3; + CTZW: operand_a_bitmanip = operand_a_rev32; + ADDUW, CLZW: operand_a_bitmanip = operand_a[31:0]; + CPOPW: operand_a_cpop = fu_data_cpop_i.operand_a[31:0]; + default: ; endcase end unique case (fu_data_i.operation) @@ -213,7 +218,7 @@ module alu popcount #( .INPUT_WIDTH(CVA6Cfg.XLEN) ) i_cpop_count ( - .data_i (operand_a_bitmanip), + .data_i (operand_a_cpop), .popcount_o(cpop) ); @@ -408,8 +413,10 @@ module alu endcase if (fu_data_i.operation == PACK_W && CVA6Cfg.IS_XLEN64) result_o = {{32{operand_b[15]}}, {operand_b[15:0]}, {operand_a[15:0]}}; - if (fu_data_i.operation == UNZIP && CVA6Cfg.IS_XLEN32) result_o = unzip_gen; - if (fu_data_i.operation == ZIP && CVA6Cfg.IS_XLEN32) result_o = zip_gen; + if (CVA6Cfg.IS_XLEN32) begin + if (fu_data_i.operation == UNZIP) result_o = unzip_gen; + if (fu_data_i.operation == ZIP) result_o = zip_gen; + end end end endmodule diff --git a/hw/vendor/cva6_cheri/core/alu_wrapper.sv b/hw/vendor/cva6_cheri/core/alu_wrapper.sv new file mode 100644 index 00000000..461b7d08 --- /dev/null +++ b/hw/vendor/cva6_cheri/core/alu_wrapper.sv @@ -0,0 +1,71 @@ +// Copyright 2025 ETH Zurich and University of Bologna. +// Copyright and related rights are licensed under the Solderpad Hardware +// License, Version 0.51 (the "License"); you may not use this file except in +// compliance with the License. You may obtain a copy of the License at +// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law +// or agreed to in writing, software, hardware and materials distributed under +// this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. +// +// Original authors: Gianmarco Ottavi, University of Bologna +// Riccardo Tedeschi, University of Bologna +// Description: ALU(s) wrapper + +module alu_wrapper + import ariane_pkg::*; +#( + parameter config_pkg::cva6_cfg_t CVA6Cfg = config_pkg::cva6_cfg_empty, + parameter type fu_data_t = logic +) ( + input logic clk_i, + input logic rst_ni, + input alu_bypass_t alu_bypass_i, + input fu_data_t [CVA6Cfg.NrALUs-1:0] fu_data_i, + output logic [CVA6Cfg.NrALUs-1:0][CVA6Cfg.XLEN-1:0] result_o, + output logic alu_branch_res_o +); + + alu #( + .CVA6Cfg (CVA6Cfg), + .HasBranch(1'b1), + .fu_data_t(fu_data_t) + ) alu_i ( + .clk_i (clk_i), + .rst_ni (rst_ni), + .fu_data_i (fu_data_i[0]), + .fu_data_cpop_i (fu_data_i[0]), + .result_o (result_o[0]), + .alu_branch_res_o(alu_branch_res_o) + ); + + if (CVA6Cfg.SuperscalarEn) begin : gen_alu2 + + fu_data_t fu_data_bypass; + + always_comb begin + fu_data_bypass = fu_data_i[1]; + + if (alu_bypass_i.rs1_from_rd) begin + fu_data_bypass.operand_a = result_o[0]; + end + if (alu_bypass_i.rs2_from_rd) begin + fu_data_bypass.operand_b = result_o[0]; + end + end + + alu #( + .CVA6Cfg (CVA6Cfg), + .HasBranch(1'b0), + .fu_data_t(fu_data_t) + ) alu2_i ( + .clk_i (clk_i), + .rst_ni (rst_ni), + .fu_data_i (fu_data_bypass), + .fu_data_cpop_i (fu_data_i[1]), + .result_o (result_o[1]), + .alu_branch_res_o( /* Unconnected */) + ); + end + +endmodule diff --git a/hw/vendor/cva6_cheri/core/ariane_regfile.sv b/hw/vendor/cva6_cheri/core/ariane_regfile.sv deleted file mode 100644 index 1663cc70..00000000 --- a/hw/vendor/cva6_cheri/core/ariane_regfile.sv +++ /dev/null @@ -1,114 +0,0 @@ -// Copyright 2018 ETH Zurich and University of Bologna. -// Copyright and related rights are licensed under the Solderpad Hardware -// License, Version 0.51 (the "License"); you may not use this file except in -// compliance with the License. You may obtain a copy of the License at -// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law -// or agreed to in writing, software, hardware and materials distributed under -// this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR -// CONDITIONS OF ANY KIND, either express or implied. See the License for the -// specific language governing permissions and limitations under the License. -// -// Author: Antonio Pullini - pullinia@iis.ee.ethz.ch -// -// Additional contributions by: -// Sven Stucki - svstucki@student.ethz.ch -// Markus Wegmann - markus.wegmann@technokrat.ch -// -// Design Name: RISC-V register file -// Project Name: zero-riscy -// Language: SystemVerilog -// -// Description: Register file with 31 or 15x 32 bit wide registers. -// Register 0 is fixed to 0. This register file is based on -// latches and is thus smaller than the flip-flop based RF. -// - -module ariane_regfile_lol #( - parameter config_pkg::cva6_cfg_t CVA6Cfg = config_pkg::cva6_cfg_empty, - parameter int unsigned DATA_WIDTH = 32, - parameter int unsigned NR_READ_PORTS = 2, - parameter bit ZERO_REG_ZERO = 0 -) ( - // clock and reset - input logic clk_i, - input logic rst_ni, - // disable clock gates for testing - input logic test_en_i, - // read port - input logic [ NR_READ_PORTS-1:0][ 4:0] raddr_i, - output logic [ NR_READ_PORTS-1:0][DATA_WIDTH-1:0] rdata_o, - // write port - input logic [CVA6Cfg.NrCommitPorts-1:0][ 4:0] waddr_i, - input logic [CVA6Cfg.NrCommitPorts-1:0][DATA_WIDTH-1:0] wdata_i, - input logic [CVA6Cfg.NrCommitPorts-1:0] we_i -); - - localparam ADDR_WIDTH = 5; - localparam NUM_WORDS = 2 ** ADDR_WIDTH; - - logic [NUM_WORDS-1:ZERO_REG_ZERO] mem_clocks; - - logic [ DATA_WIDTH-1:0] mem [NUM_WORDS]; - logic [CVA6Cfg.NrCommitPorts-1:0][NUM_WORDS-1:1] waddr_onehot, waddr_onehot_q; - logic [CVA6Cfg.NrCommitPorts-1:0][DATA_WIDTH-1:0] wdata_q; - - - // decode addresses - for (genvar i = 0; i < NR_READ_PORTS; i++) assign rdata_o[i] = mem[raddr_i[i][ADDR_WIDTH-1:0]]; - - always_ff @(posedge clk_i, negedge rst_ni) begin : sample_waddr - if (~rst_ni) begin - wdata_q <= '0; - end else begin - for (int unsigned i = 0; i < CVA6Cfg.NrCommitPorts; i++) - // enable flipflop will most probably infer clock gating - if (we_i[i]) begin - wdata_q[i] <= wdata_i[i]; - end - waddr_onehot_q <= waddr_onehot; - end - end - - // WRITE : Write Address Decoder (WAD), combinatorial process - always_comb begin : decode_write_address - for (int unsigned i = 0; i < CVA6Cfg.NrCommitPorts; i++) begin - for (int unsigned j = 1; j < NUM_WORDS; j++) begin - if (we_i[i] && (waddr_i[i] == j)) waddr_onehot[i][j] = 1'b1; - else waddr_onehot[i][j] = 1'b0; - end - end - end - - // WRITE : Clock gating (if integrated clock-gating cells are available) - for (genvar x = ZERO_REG_ZERO; x < NUM_WORDS; x++) begin - - logic [CVA6Cfg.NrCommitPorts-1:0] waddr_ored; - - for (genvar i = 0; i < CVA6Cfg.NrCommitPorts; i++) assign waddr_ored[i] = waddr_onehot[i][x]; - - cluster_clock_gating i_cg ( - .clk_i (clk_i), - .en_i (|waddr_ored), - .test_en_i(test_en_i), - .clk_o (mem_clocks[x]) - ); - end - - // Generate M = WORDS sequential processes, each of which describes one - // word of the memory. The processes are synchronized with the clocks - // ClocksxC(i), i = 0, 1, ..., M-1 - // Use active low, i.e. transparent on low latches as storage elements - // Data is sampled on rising clock edge - - // Integer registers - always_latch begin : latch_wdata - // Note: The assignment has to be done inside this process or Modelsim complains about it - if (ZERO_REG_ZERO) mem[0] = '0; - - for (int unsigned i = 0; i < CVA6Cfg.NrCommitPorts; i++) begin - for (int unsigned k = ZERO_REG_ZERO; k < NUM_WORDS; k++) begin - if (mem_clocks[k] && waddr_onehot_q[i][k]) mem[k] = wdata_q[i]; - end - end - end -endmodule diff --git a/hw/vendor/cva6_cheri/core/ariane_regfile_ff.sv b/hw/vendor/cva6_cheri/core/ariane_regfile_ff.sv index fe919baa..eaaa0561 100644 --- a/hw/vendor/cva6_cheri/core/ariane_regfile_ff.sv +++ b/hw/vendor/cva6_cheri/core/ariane_regfile_ff.sv @@ -23,11 +23,12 @@ // module ariane_regfile #( - parameter config_pkg::cva6_cfg_t CVA6Cfg = config_pkg::cva6_cfg_empty, - parameter int unsigned DATA_WIDTH = 32, - parameter int unsigned NR_READ_PORTS = 2, - parameter bit ZERO_REG_ZERO = 0, - parameter bit EN_CHERI_CAP = 0 + parameter config_pkg::cva6_cfg_t CVA6Cfg = config_pkg::cva6_cfg_empty, + parameter int unsigned DATA_WIDTH = 32, + parameter int unsigned NR_READ_PORTS = 2, + parameter logic [DATA_WIDTH-1:0] INIT_VAL = '0, + parameter bit ZERO_REG_ZERO = 0, + parameter logic [DATA_WIDTH-1:0] ZERO_VAL = '0 ) ( // clock and reset input logic clk_i, @@ -63,7 +64,7 @@ module ariane_regfile #( always_ff @(posedge clk_i, negedge rst_ni) begin : register_write_behavioral if (~rst_ni) begin for (int unsigned i = 0; i < NUM_WORDS; i++) begin - mem[i] <= (EN_CHERI_CAP) ? (CVA6Cfg.RVFI_DII ? cva6_cheri_pkg::REG_ROOT_CAP : cva6_cheri_pkg::REG_NULL_CAP) : '0; + mem[i] <= INIT_VAL; end end else begin for (int unsigned j = 0; j < CVA6Cfg.NrCommitPorts; j++) begin @@ -73,7 +74,7 @@ module ariane_regfile #( end end if (ZERO_REG_ZERO) begin - mem[0] <= (EN_CHERI_CAP) ? cva6_cheri_pkg::REG_NULL_CAP : '0; + mem[0] <= ZERO_VAL; end end end diff --git a/hw/vendor/cva6_cheri/core/ariane_regfile_fpga.sv b/hw/vendor/cva6_cheri/core/ariane_regfile_fpga.sv index 3601d707..0b55a649 100644 --- a/hw/vendor/cva6_cheri/core/ariane_regfile_fpga.sv +++ b/hw/vendor/cva6_cheri/core/ariane_regfile_fpga.sv @@ -30,8 +30,7 @@ module ariane_regfile_fpga #( parameter config_pkg::cva6_cfg_t CVA6Cfg = config_pkg::cva6_cfg_empty, parameter int unsigned DATA_WIDTH = 32, parameter int unsigned NR_READ_PORTS = 2, - parameter bit ZERO_REG_ZERO = 0, - parameter bit EN_CHERI_CAP = 0 + parameter bit ZERO_REG_ZERO = 0 ) ( // clock and reset input logic clk_i, diff --git a/hw/vendor/cva6_cheri/core/branch_unit.sv b/hw/vendor/cva6_cheri/core/branch_unit.sv index c8695af7..7ee90506 100644 --- a/hw/vendor/cva6_cheri/core/branch_unit.sv +++ b/hw/vendor/cva6_cheri/core/branch_unit.sv @@ -60,7 +60,7 @@ module branch_unit #( logic [CVA6Cfg.PCLEN-1:0] next_pc; // Decode input capability operand a and pcc - cva6_cheri_pkg::cap_pcc_t operand_a; + cva6_cheri_pkg::cap_reg_t operand_a; cva6_cheri_pkg::cap_reg_t pcc; // Signals for CHERI exception handling @@ -73,9 +73,7 @@ module branch_unit #( cva6_cheri_pkg::addrwe_t min_instr_off; logic target_pcc_is_sealed; assign pcc = CVA6Cfg.CheriPresent ? cva6_cheri_pkg::cap_reg_t'(pc_i) : pc_i; - assign operand_a = CVA6Cfg.CheriPresent ? cva6_cheri_pkg::cap_reg_to_cap_pcc( - fu_data_i.operand_a - ) : fu_data_i.operand_a; + assign operand_a = fu_data_i.operand_a; assign target_pcc = CVA6Cfg.CheriPresent ? cva6_cheri_pkg::cap_reg_t'(target_address) : target_address; assign target_pcc_meta = cva6_cheri_pkg::get_cap_reg_meta_data(target_pcc); assign target_pcc_base = cva6_cheri_pkg::get_cap_reg_base(target_pcc, target_pcc_meta); @@ -95,17 +93,18 @@ module branch_unit #( always_comb begin : mispredict_handler // set the jump base, for JALR we need to look at the register, for all other control flow instructions we can take the current PC automatic logic [CVA6Cfg.VLEN-1:0] jump_base; - automatic cva6_cheri_pkg::cap_pcc_t jump_base_cap; + automatic cva6_cheri_pkg::cap_reg_t jump_base_cap; // TODO(zarubaf): The ALU can be used to calculate the branch target jump_base = (fu_data_i.operation inside {ariane_pkg::JALR, ariane_pkg::CJALR}) ? fu_data_i.operand_a[CVA6Cfg.VLEN-1:0] : pc_i[CVA6Cfg.VLEN-1:0]; jump_base_cap = CVA6Cfg.CheriPresent ? ((fu_data_i.operation inside {ariane_pkg::CJALR}) ? operand_a : pc_i) : '0; - branch_result_o = CVA6Cfg.CheriPresent ? cva6_cheri_pkg::REG_NULL_CAP : '0; + branch_result_o = ariane_pkg::REG_NULL; resolve_branch_o = 1'b0; resolved_branch_o.target_address = '0; resolved_branch_o.is_taken = 1'b0; resolved_branch_o.valid = branch_valid_i; resolved_branch_o.is_mispredict = 1'b0; + resolved_branch_o.is_pcc_change = 1'b0; resolved_branch_o.cf_type = branch_predict_i.cf; // calculate target address simple 64 bit addition target_address = $unsigned($signed(jump_base) + $signed(fu_data_i.imm[CVA6Cfg.VLEN-1:0])); @@ -121,18 +120,29 @@ module branch_unit #( // on a JALR we are supposed to reset the LSB to 0 (according to the specification) if (CVA6Cfg.CheriPresent) begin if (fu_data_i.operation inside {ariane_pkg::CJAL, ariane_pkg::CJALR}) begin - branch_result_o = cva6_cheri_pkg::set_cap_reg_otype( - cva6_cheri_pkg::cap_pcc_to_cap_reg(next_pc), cva6_cheri_pkg::SENTRY_CAP); + branch_result_o = cva6_cheri_pkg::set_cap_reg_otype(next_pc, cva6_cheri_pkg::SENTRY_CAP); if (fu_data_i.operation inside {ariane_pkg::CJALR}) begin + automatic cva6_cheri_pkg::cap_reg_t compare_pcc; + automatic cva6_cheri_pkg::cap_reg_t compare_target_cap; + compare_pcc = cva6_cheri_pkg::set_cap_reg_flags(pcc, 0); + compare_target_cap = cva6_cheri_pkg::set_cap_reg_flags( + cva6_cheri_pkg::set_cap_reg_otype(operand_a, cva6_cheri_pkg::UNSEALED_CAP), 0); target_address = cva6_cheri_pkg::set_cap_reg_otype(target_address, cva6_cheri_pkg::UNSEALED_CAP); + if (compare_target_cap != cva6_cheri_pkg::set_cap_reg_address( + compare_pcc, + compare_target_cap[CVA6Cfg.XLEN-1:0], + cva6_cheri_pkg::get_cap_reg_meta_data( + pcc) + )) begin + resolved_branch_o.is_pcc_change = 1'b1; + end // If jumping into intmode, we must have been in capmode, so always mispredict if (cva6_cheri_pkg::get_cap_reg_flags(target_address) == 1'b1) resolved_branch_o.is_mispredict = branch_valid_i; end end else begin - branch_result_o = cva6_cheri_pkg::set_cap_reg_addr(cva6_cheri_pkg::REG_NULL_CAP, - next_pc[CVA6Cfg.VLEN-1:0]); + branch_result_o = ariane_pkg::x_to_reg(next_pc[CVA6Cfg.VLEN-1:0]); end end else begin // we need to put the branch target address into rd, this is the result of this unit diff --git a/hw/vendor/cva6_cheri/core/cache_subsystem/cache_ctrl.sv b/hw/vendor/cva6_cheri/core/cache_subsystem/cache_ctrl.sv index 8091e6a8..a76c62e0 100644 --- a/hw/vendor/cva6_cheri/core/cache_subsystem/cache_ctrl.sv +++ b/hw/vendor/cva6_cheri/core/cache_subsystem/cache_ctrl.sv @@ -128,6 +128,7 @@ module cache_ctrl req_port_o.data_rvalid = 1'b0; req_port_o.data_rdata = '0; req_port_o.data_rid = mem_req_q.id; + req_port_o.data_ruser = '0; miss_req_o = '0; mshr_addr_o = '0; // Memory array communication diff --git a/hw/vendor/cva6_cheri/core/cache_subsystem/cva6_hpdcache_if_adapter.sv b/hw/vendor/cva6_cheri/core/cache_subsystem/cva6_hpdcache_if_adapter.sv index d9e9f233..1c9e5dbb 100644 --- a/hw/vendor/cva6_cheri/core/cache_subsystem/cva6_hpdcache_if_adapter.sv +++ b/hw/vendor/cva6_cheri/core/cache_subsystem/cva6_hpdcache_if_adapter.sv @@ -90,6 +90,7 @@ module cva6_hpdcache_if_adapter // Request forwarding assign hpdcache_req_valid_o = cva6_req_i.data_req; assign hpdcache_req.addr_offset = cva6_req_i.address_index; + assign hpdcache_req.wuser = '0; assign hpdcache_req.wdata = '0; assign hpdcache_req.op = hpdcache_pkg::HPDCACHE_REQ_LOAD; assign hpdcache_req.be = cva6_req_i.data_be; @@ -111,6 +112,7 @@ module cva6_hpdcache_if_adapter // Response forwarding assign cva6_req_o.data_rvalid = hpdcache_rsp_valid_i; + assign cva6_req_o.data_ruser = hpdcache_rsp_i.ruser; assign cva6_req_o.data_rdata = hpdcache_rsp_i.rdata; assign cva6_req_o.data_rid = hpdcache_rsp_i.tid; assign cva6_req_o.data_gnt = hpdcache_req_ready_i; @@ -128,19 +130,20 @@ module cva6_hpdcache_if_adapter // {{{ else begin : store_amo_gen // STORE/AMO request - logic [63:0] amo_addr; - hpdcache_req_offset_t amo_addr_offset; - hpdcache_tag_t amo_tag; - logic amo_is_word, amo_is_word_hi; - logic [63:0] amo_data; - logic [ 7:0] amo_data_be; - hpdcache_pkg::hpdcache_req_op_t amo_op; - logic [31:0] amo_resp_word; - logic amo_pending_q; - - hpdcache_req_t hpdcache_req_amo; - hpdcache_req_t hpdcache_req_store; - hpdcache_req_t hpdcache_req_flush; + logic [ 63:0] amo_addr; + hpdcache_req_offset_t amo_addr_offset; + hpdcache_tag_t amo_tag; + logic [127:0] amo_data; + logic amo_user; + logic [ 15:0] amo_data_be; + hpdcache_pkg::hpdcache_req_op_t amo_op; + logic [127:0] amo_resp; + logic amo_resp_cap_vld; + logic amo_pending_q; + + hpdcache_req_t hpdcache_req_amo; + hpdcache_req_t hpdcache_req_store; + hpdcache_req_t hpdcache_req_flush; flush_fsm_t flush_fsm_q, flush_fsm_d; @@ -219,19 +222,45 @@ module cva6_hpdcache_if_adapter } ); - assign amo_is_word = (cva6_amo_req_i.size == 2'b10); - assign amo_is_word_hi = cva6_amo_req_i.operand_a[2]; - if (CVA6Cfg.XLEN == 64) begin : amo_data_64_gen - assign amo_data = amo_is_word ? {2{cva6_amo_req_i.operand_b[0+:32]}} : cva6_amo_req_i.operand_b; - assign amo_data_be = amo_is_word_hi ? 8'hf0 : amo_is_word ? 8'h0f : 8'hff; - end else begin : amo_data_32_gen - assign amo_data = {32'b0, cva6_amo_req_i.operand_b}; - assign amo_data_be = 8'h0f; + always_comb begin : generate_amo_inputs + amo_data = '0; + amo_data_be = '0; + if (cva6_amo_req_i.size == 3'b000) begin + amo_data = {{120{1'b0}}, cva6_amo_req_i.operand_b[0+:8]}; + amo_data_be = 16'h0001; + end + if (cva6_amo_req_i.size == 3'b001) begin + amo_data = {{112{1'b0}}, cva6_amo_req_i.operand_b[0+:16]}; + amo_data_be = 16'h0003; + end else begin + amo_data = {{112{1'b0}}, {2{amo_data[0+:8]}}}; + end + if (cva6_amo_req_i.size == 3'b010) begin + amo_data = {{96{1'b0}}, cva6_amo_req_i.operand_b[0+:32]}; + amo_data_be = 16'h000f; + end else begin + amo_data = {{96{1'b0}}, {2{amo_data[0+:16]}}}; + end + if (cva6_amo_req_i.size == 3'b011) begin + amo_data = {{64{1'b0}}, cva6_amo_req_i.operand_b[0+:64]}; + amo_data_be = 16'h00ff; + end else begin + amo_data = {{64{1'b0}}, {2{amo_data[0+:32]}}}; + end + if (cva6_amo_req_i.size == 3'b100) begin + amo_data = cva6_amo_req_i.operand_b[0+:128]; + amo_data_be = 16'hffff; + end else begin + amo_data = {{2{amo_data[0+:64]}}}; + end + amo_data_be = amo_data_be << (CVA6Cfg.CheriPresent ? cva6_amo_req_i.operand_a[3:0] : cva6_amo_req_i.operand_a[2:0]); + amo_user = cva6_amo_req_i.size == 3'b100 && cva6_amo_req_i.cap_vld; end assign hpdcache_req_amo = '{ addr_offset: amo_addr_offset, wdata: amo_data, + wuser: amo_user, op: amo_op, be: amo_data_be, size: cva6_amo_req_i.size, @@ -250,6 +279,7 @@ module cva6_hpdcache_if_adapter assign hpdcache_req_store = '{ addr_offset: cva6_req_i.address_index, wdata: cva6_req_i.data_wdata, + wuser: cva6_req_i.data_wuser, op: hpdcache_pkg::HPDCACHE_REQ_STORE, be: cva6_req_i.data_be, size: cva6_req_i.data_size, @@ -269,6 +299,7 @@ module cva6_hpdcache_if_adapter addr_offset: '0, addr_tag: '0, wdata: '0, + wuser: '0, op: InvalidateOnFlush ? @@ -305,22 +336,45 @@ module cva6_hpdcache_if_adapter // Response forwarding // {{{ - if (CVA6Cfg.XLEN == 64) begin : amo_resp_64_gen - assign amo_resp_word = amo_is_word_hi - ? hpdcache_rsp_i.rdata[0][32 +: 32] - : hpdcache_rsp_i.rdata[0][0 +: 32]; - end else begin : amo_resp_32_gen - assign amo_resp_word = hpdcache_rsp_i.rdata[0]; + always_comb begin + amo_resp = hpdcache_rsp_i.rdata[0]; + amo_resp_cap_vld = hpdcache_rsp_i.ruser; + if (cva6_amo_req_i.size <= 3'b011) begin + if (CVA6Cfg.CheriPresent && amo_addr[3] == 1'b1) begin + amo_resp[63:0] = amo_resp[127:64]; + end + amo_resp[127:64] = 64'b0; + amo_resp_cap_vld = 1'b0; + end + if (cva6_amo_req_i.size <= 3'b010) begin + if (amo_addr[2] == 1'b1) begin + amo_resp[31:0] = amo_resp[63:32]; + end + amo_resp[63:32] = {32{amo_resp[31]}}; + end + if (cva6_amo_req_i.size <= 3'b001) begin + if (amo_addr[1] == 1'b1) begin + amo_resp[15:0] = amo_resp[31:16]; + end + amo_resp[63:16] = {48{amo_resp[15]}}; + end + if (cva6_amo_req_i.size == 3'b000) begin + if (amo_addr[0] == 1'b1) begin + amo_resp[7:0] = amo_resp[15:8]; + end + amo_resp[63:8] = {56{amo_resp[7]}}; + end end assign cva6_req_o.data_rvalid = hpdcache_rsp_valid_i && (hpdcache_rsp_i.tid != '1); assign cva6_req_o.data_rdata = hpdcache_rsp_i.rdata; + assign cva6_req_o.data_ruser = hpdcache_rsp_i.ruser; assign cva6_req_o.data_rid = hpdcache_rsp_i.tid; assign cva6_req_o.data_gnt = hpdcache_req_ready_i; assign cva6_amo_resp_o.ack = hpdcache_rsp_valid_i && (hpdcache_rsp_i.tid == '1); - assign cva6_amo_resp_o.result = amo_is_word ? {{32{amo_resp_word[31]}}, amo_resp_word} - : hpdcache_rsp_i.rdata[0]; + assign cva6_amo_resp_o.result = amo_resp; + assign cva6_amo_resp_o.cap_vld = amo_resp_cap_vld; // }}} always_ff @(posedge clk_i or negedge rst_ni) begin : amo_pending_ff diff --git a/hw/vendor/cva6_cheri/core/cache_subsystem/cva6_hpdcache_subsystem.sv b/hw/vendor/cva6_cheri/core/cache_subsystem/cva6_hpdcache_subsystem.sv index a78f6039..c7fc775d 100644 --- a/hw/vendor/cva6_cheri/core/cache_subsystem/cva6_hpdcache_subsystem.sv +++ b/hw/vendor/cva6_cheri/core/cache_subsystem/cva6_hpdcache_subsystem.sv @@ -193,24 +193,26 @@ module cva6_hpdcache_subsystem hpdcache_pkg::hpdcache_user_cfg_t userCfg; userCfg.nRequesters = HPDCACHE_NREQUESTERS; userCfg.paWidth = CVA6Cfg.PLEN; - userCfg.wordWidth = CVA6Cfg.XLEN; + userCfg.wordWidth = CVA6Cfg.CLEN; + userCfg.wordUserWidth = 1; userCfg.sets = CVA6Cfg.DCACHE_NUM_WORDS; userCfg.ways = CVA6Cfg.DCACHE_SET_ASSOC; - userCfg.clWords = CVA6Cfg.DCACHE_LINE_WIDTH / CVA6Cfg.XLEN; - userCfg.reqWords = 1; + userCfg.clWords = CVA6Cfg.DCACHE_LINE_WIDTH / userCfg.wordWidth; + userCfg.reqWords = CVA6Cfg.CLEN / userCfg.wordWidth; userCfg.reqTransIdWidth = CVA6Cfg.DcacheIdWidth; userCfg.reqSrcIdWidth = 3; // Up to 8 requesters userCfg.victimSel = hpdcache_pkg::HPDCACHE_VICTIM_RANDOM; - userCfg.dataWaysPerRamWord = __minu(CVA6Cfg.DCACHE_SET_ASSOC, 128 / CVA6Cfg.XLEN); + userCfg.dataWaysPerRamWord = __minu(CVA6Cfg.DCACHE_SET_ASSOC, 128 / userCfg.wordWidth); userCfg.dataSetsPerRam = CVA6Cfg.DCACHE_NUM_WORDS; userCfg.dataRamByteEnable = 1'b1; - userCfg.accessWords = __maxu(CVA6Cfg.AxiDataWidth / CVA6Cfg.XLEN, userCfg.reqWords); + userCfg.accessWords = __maxu(CVA6Cfg.AxiDataWidth / userCfg.wordWidth, userCfg.reqWords); userCfg.mshrSets = CVA6Cfg.NrLoadBufEntries < 16 ? 1 : CVA6Cfg.NrLoadBufEntries / 2; userCfg.mshrWays = CVA6Cfg.NrLoadBufEntries < 16 ? CVA6Cfg.NrLoadBufEntries : 2; userCfg.mshrWaysPerRamWord = CVA6Cfg.NrLoadBufEntries < 16 ? CVA6Cfg.NrLoadBufEntries : 2; userCfg.mshrSetsPerRam = CVA6Cfg.NrLoadBufEntries < 16 ? 1 : CVA6Cfg.NrLoadBufEntries / 2; userCfg.mshrRamByteEnable = 1'b1; userCfg.mshrUseRegbank = (CVA6Cfg.NrLoadBufEntries < 16); + userCfg.cbufEntries = 8; userCfg.refillCoreRspFeedthrough = 1'b1; userCfg.refillFifoDepth = 2 * (CVA6Cfg.DCACHE_LINE_WIDTH / CVA6Cfg.AxiDataWidth); userCfg.wbufDirEntries = CVA6Cfg.WtDcacheWbufDepth; @@ -225,12 +227,15 @@ module cva6_hpdcache_subsystem userCfg.memAddrWidth = CVA6Cfg.AxiAddrWidth; userCfg.memIdWidth = CVA6Cfg.MEM_TID_WIDTH; userCfg.memDataWidth = CVA6Cfg.AxiDataWidth; + userCfg.lowLatency = 1'b1; userCfg.wtEn = (CVA6Cfg.DCacheType == config_pkg::HPDCACHE_WT) || (CVA6Cfg.DCacheType == config_pkg::HPDCACHE_WT_WB); userCfg.wbEn = (CVA6Cfg.DCacheType == config_pkg::HPDCACHE_WB) || (CVA6Cfg.DCacheType == config_pkg::HPDCACHE_WT_WB); + userCfg.userEn = CVA6Cfg.CheriPresent; + userCfg.capAmoEn = CVA6Cfg.CheriPresent; return userCfg; endfunction @@ -239,21 +244,24 @@ module cva6_hpdcache_subsystem HPDcacheUserCfg ); - `HPDCACHE_TYPEDEF_MEM_ATTR_T(hpdcache_mem_addr_t, hpdcache_mem_id_t, hpdcache_mem_data_t, - hpdcache_mem_be_t, HPDcacheCfg); + `HPDCACHE_USEREN_TYPEDEF_MEM_ATTR_T(hpdcache_mem_addr_t, hpdcache_mem_id_t, hpdcache_mem_data_t, + hpdcache_mem_be_t, hpdcache_mem_user_t, HPDcacheCfg); `HPDCACHE_TYPEDEF_MEM_REQ_T(hpdcache_mem_req_t, hpdcache_mem_addr_t, hpdcache_mem_id_t); - `HPDCACHE_TYPEDEF_MEM_RESP_R_T(hpdcache_mem_resp_r_t, hpdcache_mem_id_t, hpdcache_mem_data_t); - `HPDCACHE_TYPEDEF_MEM_REQ_W_T(hpdcache_mem_req_w_t, hpdcache_mem_data_t, hpdcache_mem_be_t); + `HPDCACHE_USEREN_TYPEDEF_MEM_RESP_R_T(hpdcache_mem_resp_r_t, hpdcache_mem_id_t, + hpdcache_mem_data_t, hpdcache_mem_user_t); + `HPDCACHE_USEREN_TYPEDEF_MEM_REQ_W_T(hpdcache_mem_req_w_t, hpdcache_mem_data_t, + hpdcache_mem_be_t, hpdcache_mem_user_t); `HPDCACHE_TYPEDEF_MEM_RESP_W_T(hpdcache_mem_resp_w_t, hpdcache_mem_id_t); - `HPDCACHE_TYPEDEF_REQ_ATTR_T(hpdcache_req_offset_t, hpdcache_data_word_t, hpdcache_data_be_t, - hpdcache_req_data_t, hpdcache_req_be_t, hpdcache_req_sid_t, - hpdcache_req_tid_t, hpdcache_tag_t, HPDcacheCfg); - `HPDCACHE_TYPEDEF_REQ_T(hpdcache_req_t, hpdcache_req_offset_t, hpdcache_req_data_t, - hpdcache_req_be_t, hpdcache_req_sid_t, hpdcache_req_tid_t, - hpdcache_tag_t); - `HPDCACHE_TYPEDEF_RSP_T(hpdcache_rsp_t, hpdcache_req_data_t, hpdcache_req_sid_t, - hpdcache_req_tid_t); + `HPDCACHE_USEREN_TYPEDEF_REQ_ATTR_T(hpdcache_req_offset_t, hpdcache_data_word_t, + hpdcache_data_be_t, hpdcache_req_data_t, hpdcache_req_be_t, + hpdcache_req_sid_t, hpdcache_req_tid_t, hpdcache_tag_t, + hpdcache_req_user_t, HPDcacheCfg); + `HPDCACHE_USEREN_TYPEDEF_REQ_T(hpdcache_req_t, hpdcache_req_offset_t, hpdcache_req_data_t, + hpdcache_req_be_t, hpdcache_req_sid_t, hpdcache_req_tid_t, + hpdcache_tag_t, hpdcache_req_user_t); + `HPDCACHE_USEREN_TYPEDEF_RSP_T(hpdcache_rsp_t, hpdcache_req_data_t, hpdcache_req_sid_t, + hpdcache_req_tid_t, hpdcache_req_user_t); typedef logic [HPDcacheCfg.u.wbufTimecntWidth-1:0] hpdcache_wbuf_timecnt_t; @@ -296,6 +304,7 @@ module cva6_hpdcache_subsystem .hpdcache_mem_resp_w_t(hpdcache_mem_resp_w_t), .hpdcache_req_offset_t(hpdcache_req_offset_t), .hpdcache_data_word_t(hpdcache_data_word_t), + .hpdcache_req_user_t(hpdcache_req_user_t), .hpdcache_req_data_t(hpdcache_req_data_t), .hpdcache_req_be_t(hpdcache_req_be_t), .hpdcache_req_sid_t(hpdcache_req_sid_t), diff --git a/hw/vendor/cva6_cheri/core/cache_subsystem/cva6_hpdcache_subsystem_axi_arbiter.sv b/hw/vendor/cva6_cheri/core/cache_subsystem/cva6_hpdcache_subsystem_axi_arbiter.sv index f082dc5c..61b7b779 100644 --- a/hw/vendor/cva6_cheri/core/cache_subsystem/cva6_hpdcache_subsystem_axi_arbiter.sv +++ b/hw/vendor/cva6_cheri/core/cache_subsystem/cva6_hpdcache_subsystem_axi_arbiter.sv @@ -99,8 +99,9 @@ module cva6_hpdcache_subsystem_axi_arbiter // Adapt the I$ interface to the HPDcache memory interface // {{{ - localparam int ICACHE_CL_WORDS = CVA6Cfg.ICACHE_LINE_WIDTH / 64; - localparam int ICACHE_CL_WORD_INDEX = $clog2(ICACHE_CL_WORDS); + localparam int ICACHE_UC_WORD_INDEX = CVA6Cfg.AxiDataWidth > 64 ? $clog2( + CVA6Cfg.AxiDataWidth / 64 + ) : 1; localparam int ICACHE_CL_SIZE = $clog2(CVA6Cfg.ICACHE_LINE_WIDTH / 8); localparam int ICACHE_WORD_SIZE = 3; localparam int ICACHE_MEM_REQ_CL_LEN = @@ -211,29 +212,31 @@ module cva6_hpdcache_subsystem_axi_arbiter assign icache_miss_resp_wok = icache_miss_resp_data_wok & ( icache_miss_resp_meta_wok | ~icache_miss_resp_wdata.mem_resp_r_last); - assign icache_miss_rdata = icache_miss_resp_data_rdata; - end else begin assign icache_miss_resp_data_rok = icache_miss_resp_w; assign icache_miss_resp_meta_rok = icache_miss_resp_w; assign icache_miss_resp_wok = 1'b1; assign icache_miss_resp_meta_id = icache_miss_resp_wdata.mem_resp_r_id; assign icache_miss_resp_data_rdata = icache_miss_resp_wdata.mem_resp_r_data; + end + endgenerate - // In the case of uncacheable accesses, the Icache expects the data to be right-aligned - always_comb begin : icache_miss_resp_data_comb - if (!icache_miss_req_rdata.mem_req_cacheable) begin - automatic logic [ICACHE_CL_WORD_INDEX - 1:0] icache_miss_word_index; - automatic logic [63:0] icache_miss_word; - icache_miss_word_index = icache_miss_req_rdata.mem_req_addr[3+:ICACHE_CL_WORD_INDEX]; - icache_miss_word = icache_miss_resp_data_rdata[icache_miss_word_index*64+:64]; - icache_miss_rdata = {{CVA6Cfg.ICACHE_LINE_WIDTH - 64{1'b0}}, icache_miss_word}; - end else begin - icache_miss_rdata = icache_miss_resp_data_rdata; - end + // In the case of uncacheable accesses, the Icache expects the data to be right-aligned + always_comb begin : icache_miss_resp_data_comb + if (!icache_miss_req_rdata.mem_req_cacheable) begin + automatic logic [ICACHE_UC_WORD_INDEX - 1:0] icache_miss_word_index; + automatic logic [63:0] icache_miss_word; + if (CVA6Cfg.AxiDataWidth > 64) begin + icache_miss_word_index = icache_miss_req_rdata.mem_req_addr[3+:ICACHE_UC_WORD_INDEX]; + end else begin + icache_miss_word_index = 0; end + icache_miss_word = icache_miss_resp_data_rdata[icache_miss_word_index*64+:64]; + icache_miss_rdata = {{CVA6Cfg.ICACHE_LINE_WIDTH - 64{1'b0}}, icache_miss_word}; + end else begin + icache_miss_rdata = icache_miss_resp_data_rdata; end - endgenerate + end assign icache_miss_resp_valid_o = icache_miss_resp_meta_rok; assign icache_miss_resp_o.rtype = wt_cache_pkg::ICACHE_IFILL_ACK; @@ -350,7 +353,8 @@ module cva6_hpdcache_subsystem_axi_arbiter .hpdcache_mem_resp_w_t(hpdcache_mem_resp_w_t), .aw_chan_t (axi_aw_chan_t), .w_chan_t (axi_w_chan_t), - .b_chan_t (axi_b_chan_t) + .b_chan_t (axi_b_chan_t), + .userEn (CVA6Cfg.CheriPresent) ) i_hpdcache_mem_to_axi_write ( .req_ready_o(dcache_write_ready_o), .req_valid_i(dcache_write_valid_i), @@ -381,7 +385,8 @@ module cva6_hpdcache_subsystem_axi_arbiter .hpdcache_mem_req_t (hpdcache_mem_req_t), .hpdcache_mem_resp_r_t(hpdcache_mem_resp_r_t), .ar_chan_t (axi_ar_chan_t), - .r_chan_t (axi_r_chan_t) + .r_chan_t (axi_r_chan_t), + .userEn (CVA6Cfg.CheriPresent) ) i_hpdcache_mem_to_axi_read ( .req_ready_o(mem_req_read_ready_arb), .req_valid_i(mem_req_read_valid_arb), diff --git a/hw/vendor/cva6_cheri/core/cache_subsystem/cva6_hpdcache_wrapper.sv b/hw/vendor/cva6_cheri/core/cache_subsystem/cva6_hpdcache_wrapper.sv index 7727dc70..3ec440af 100644 --- a/hw/vendor/cva6_cheri/core/cache_subsystem/cva6_hpdcache_wrapper.sv +++ b/hw/vendor/cva6_cheri/core/cache_subsystem/cva6_hpdcache_wrapper.sv @@ -35,6 +35,7 @@ module cva6_hpdcache_wrapper parameter type hpdcache_mem_resp_w_t = logic, parameter type hpdcache_req_offset_t = logic, parameter type hpdcache_data_word_t = logic, + parameter type hpdcache_req_user_t = logic, parameter type hpdcache_req_data_t = logic, parameter type hpdcache_req_be_t = logic, parameter type hpdcache_req_sid_t = logic, @@ -345,6 +346,7 @@ module cva6_hpdcache_wrapper .hpdcache_data_word_t (hpdcache_data_word_t), .hpdcache_data_be_t (hpdcache_data_be_t), .hpdcache_req_offset_t(hpdcache_req_offset_t), + .hpdcache_req_user_t (hpdcache_req_user_t), .hpdcache_req_data_t (hpdcache_req_data_t), .hpdcache_req_be_t (hpdcache_req_be_t), .hpdcache_req_sid_t (hpdcache_req_sid_t), diff --git a/hw/vendor/cva6_cheri/core/cache_subsystem/wt_axi_adapter.sv b/hw/vendor/cva6_cheri/core/cache_subsystem/wt_axi_adapter.sv index 759ab0fa..51195889 100644 --- a/hw/vendor/cva6_cheri/core/cache_subsystem/wt_axi_adapter.sv +++ b/hw/vendor/cva6_cheri/core/cache_subsystem/wt_axi_adapter.sv @@ -541,38 +541,22 @@ module wt_axi_adapter }; // if this is a single word transaction, we need to make sure that word is placed at offset 0 if (dcache_first_q) begin - dcache_rd_shift_d[0] = axi_rd_data; - dcache_rd_shift_user_d[0] = axi_rd_user; - // replicate also the second word - if (CVA6Cfg.CheriPresent) begin - for (int i = 1; i < CVA6Cfg.DCACHE_LINE_WIDTH / CVA6Cfg.AxiDataWidth; i++) begin - dcache_rd_shift_d[i] = axi_rd_data; - dcache_rd_shift_user_d[i] = axi_rd_user; - end - end - end - // Fixup for this shift register which assumes all bursted reads are data cache line width. - // This is false when the data cache is disabled and CHERI capabilities are 128 bit, bus is 64 bit and data cahce line is 256 bit. - if (CVA6Cfg.CheriPresent && axi_rd_last) begin - for (int i = 1; i < CVA6Cfg.DCACHE_LINE_WIDTH / CVA6Cfg.AxiDataWidth; i+=2) begin + automatic int limit; + limit = CVA6Cfg.CheriPresent ? CVA6Cfg.DCACHE_LINE_WIDTH / CVA6Cfg.AxiDataWidth : 1; + for (int i = 0; i < limit; i++) begin dcache_rd_shift_d[i] = axi_rd_data; dcache_rd_shift_user_d[i] = axi_rd_user; end end end else if (CVA6Cfg.RVA && dcache_sc_rtrn) begin + automatic int limit; + limit = CVA6Cfg.CheriPresent ? CVA6Cfg.DCACHE_LINE_WIDTH / CVA6Cfg.AxiDataWidth : 1; // encode lr/sc success - dcache_rd_shift_d[0] = '0; - dcache_rd_shift_user_d[0] = '0; - dcache_rd_shift_d[0][amo_off_q*8] = (wr_exokay) ? '0 : 1'b1; - dcache_rd_shift_user_d[0][amo_off_q*8] = (wr_exokay) ? '0 : 1'b1; - // replicate also the second word - if (CVA6Cfg.CheriPresent) begin - for (int i = 1; i < CVA6Cfg.DCACHE_LINE_WIDTH / CVA6Cfg.AxiDataWidth; i++) begin - dcache_rd_shift_d[i] = '0; - dcache_rd_shift_user_d[i] = '0; - dcache_rd_shift_d[i][amo_off_q*8] = (wr_exokay) ? '0 : 1'b1; - dcache_rd_shift_user_d[i][amo_off_q*8] = (wr_exokay) ? '0 : 1'b1; - end + for (int i = 0; i < limit; i++) begin + dcache_rd_shift_d[i] = '0; + dcache_rd_shift_user_d[i] = '0; + dcache_rd_shift_d[i][amo_off_q*8] = (wr_exokay) ? '0 : 1'b1; + dcache_rd_shift_user_d[i][amo_off_q*8] = (wr_exokay) ? '0 : 1'b1; end end end diff --git a/hw/vendor/cva6_cheri/core/cache_subsystem/wt_cache_subsystem.sv b/hw/vendor/cva6_cheri/core/cache_subsystem/wt_cache_subsystem.sv index 1ff8686b..4aefea66 100644 --- a/hw/vendor/cva6_cheri/core/cache_subsystem/wt_cache_subsystem.sv +++ b/hw/vendor/cva6_cheri/core/cache_subsystem/wt_cache_subsystem.sv @@ -113,50 +113,33 @@ module wt_cache_subsystem logic dcache_adapter_data_req, adapter_dcache_data_ack, adapter_dcache_rtrn_vld; dcache_req_t dcache_adapter; dcache_rtrn_t adapter_dcache; - if (CVA6Cfg.RVFI_DII) begin : gen_rvfi_dii_generator - rvfi_dii_generator #( - .CVA6Cfg(CVA6Cfg), - .icache_dreq_t(icache_dreq_t), - .icache_drsp_t(icache_drsp_t), - .exception_t(exception_t) - ) i_cva6_rvfi_dii_generator ( - .clk_i (clk_i), - .rst_ni(rst_ni), - .dreq_i(icache_dreq_i), - .dreq_o(icache_dreq_o) - ); - assign icache_areq_o = '0; - assign icache_adapter_data_req = '0; - assign icache_adapter = '0; - end else begin : gen_icache - cva6_icache #( - // use ID 0 for icache reads - .CVA6Cfg(CVA6Cfg), - .icache_areq_t(icache_areq_t), - .icache_arsp_t(icache_arsp_t), - .icache_dreq_t(icache_dreq_t), - .icache_drsp_t(icache_drsp_t), - .icache_req_t(icache_req_t), - .icache_rtrn_t(icache_rtrn_t), - .RdTxId(0) - ) i_cva6_icache ( - .clk_i (clk_i), - .rst_ni (rst_ni), - .flush_i (icache_flush_i), - .en_i (icache_en_i), - .miss_o (icache_miss_o), - .areq_i (icache_areq_i), - .areq_o (icache_areq_o), - .dreq_i (icache_dreq_i), - .dreq_o (icache_dreq_o), - .mem_rtrn_vld_i(adapter_icache_rtrn_vld), - .mem_rtrn_i (adapter_icache), - .mem_data_req_o(icache_adapter_data_req), - .mem_data_ack_i(adapter_icache_data_ack), - .mem_data_o (icache_adapter) - ); - end + cva6_icache #( + // use ID 0 for icache reads + .CVA6Cfg(CVA6Cfg), + .icache_areq_t(icache_areq_t), + .icache_arsp_t(icache_arsp_t), + .icache_dreq_t(icache_dreq_t), + .icache_drsp_t(icache_drsp_t), + .icache_req_t(icache_req_t), + .icache_rtrn_t(icache_rtrn_t), + .RdTxId(0) + ) i_cva6_icache ( + .clk_i (clk_i), + .rst_ni (rst_ni), + .flush_i (icache_flush_i), + .en_i (icache_en_i), + .miss_o (icache_miss_o), + .areq_i (icache_areq_i), + .areq_o (icache_areq_o), + .dreq_i (icache_dreq_i), + .dreq_o (icache_dreq_o), + .mem_rtrn_vld_i(adapter_icache_rtrn_vld), + .mem_rtrn_i (adapter_icache), + .mem_data_req_o(icache_adapter_data_req), + .mem_data_ack_i(adapter_icache_data_ack), + .mem_data_o (icache_adapter) + ); // Note: // Ports 0/1 for PTW and LD unit are read only. diff --git a/hw/vendor/cva6_cheri/core/cache_subsystem/wt_dcache_mem.sv b/hw/vendor/cva6_cheri/core/cache_subsystem/wt_dcache_mem.sv index af18ed6a..ad93dd2b 100644 --- a/hw/vendor/cva6_cheri/core/cache_subsystem/wt_dcache_mem.sv +++ b/hw/vendor/cva6_cheri/core/cache_subsystem/wt_dcache_mem.sv @@ -283,7 +283,7 @@ module wt_dcache_mem // In case of an uncached read, return the desired CVA6Cfg.XLEN-bit segment of the most recent AXI read assign wr_cl_off = (wr_cl_nc_i) ? (CVA6Cfg.AxiDataWidth == CVA6Cfg.CLEN) ? '0 : (CVA6Cfg.CheriPresent) ? {{CVA6Cfg.DCACHE_OFFSET_WIDTH-CVA6Cfg.CLEN_ALIGN_BYTES{1'b0}}, wr_cl_off_i[CVA6Cfg.CLEN_ALIGN_BYTES]} : - {{CVA6Cfg.DCACHE_OFFSET_WIDTH-AXI_OFFSET_WIDTH{1'b0}}, wr_cl_off_i[AXI_OFFSET_WIDTH-1-:CVA6Cfg.CLEN_ALIGN_BYTES]} : + {{CVA6Cfg.DCACHE_OFFSET_WIDTH-AXI_OFFSET_WIDTH{1'b0}}, wr_cl_off_i[AXI_OFFSET_WIDTH-1:CVA6Cfg.CLEN_ALIGN_BYTES]} : wr_cl_off_i[CVA6Cfg.DCACHE_OFFSET_WIDTH-1:CVA6Cfg.CLEN_ALIGN_BYTES]; end else begin : gen_piton_offset assign wr_cl_off = wr_cl_off_i[CVA6Cfg.DCACHE_OFFSET_WIDTH-1:3]; diff --git a/hw/vendor/cva6_cheri/core/cheri_unit.sv b/hw/vendor/cva6_cheri/core/cheri_unit.sv index 7f9cea82..1caad7cf 100644 --- a/hw/vendor/cva6_cheri/core/cheri_unit.sv +++ b/hw/vendor/cva6_cheri/core/cheri_unit.sv @@ -22,7 +22,6 @@ typedef struct packed { } cap_check_select_t; module cheri_unit - import ariane_pkg::*; import cva6_cheri_pkg::*; #( parameter config_pkg::cva6_cfg_t CVA6Cfg = config_pkg::cva6_cfg_empty, @@ -52,9 +51,7 @@ module cheri_unit cap_reg_t operand_b; addrw_t operand_b_base; addrwe_t operand_b_top; - //addrw_t operand_b_length; addrw_t operand_b_address; - //addrw_t operand_b_offset; logic operand_b_is_sealed; cap_meta_data_t op_b_meta_info; logic operand_b_hperms_malformed; @@ -79,7 +76,6 @@ module cheri_unit cap_mem_t cap_mem_null; cap_reg_t tmp_cap, req_cap; addrwe_t tmp_length; - cap_reg_set_bounds_ret_t res_set_bounds; always_comb begin // exceptions signals reset check_operand_a_violations = {1'b0, 1'b0, 1'b0}; @@ -104,11 +100,14 @@ module cheri_unit end // CAndPerm ariane_pkg::ACPERM: begin - automatic cap_report_perms_t mask = cap_report_perms_t'(operand_b_address); + automatic cap_report_perms_t new_perms; check_operand_a_violations.seal = 1'b1; tmp_cap = operand_a; - tmp_cap.uperms = (tmp_cap.uperms & mask.uperms); - tmp_cap.hperms = cap_hperms_t'(tmp_cap.hperms & report_perms_to_hperms(mask)); + new_perms = hperms_and_uperms_to_report_perms(operand_a.hperms, operand_a.uperms, + operand_a_hperms_malformed); + new_perms &= cap_report_perms_t'(operand_b_address); + tmp_cap.uperms = new_perms.uperms; + tmp_cap.hperms = legalize_arch_perms(report_perms_to_hperms(new_perms)); if (!tmp_cap.hperms.permit_execute) tmp_cap.flags.int_mode = 1'b0; clu_result = tmp_cap; end @@ -152,47 +151,43 @@ module cheri_unit if (fu_data_i.operation == ariane_pkg::CBLD) clu_result = tmp_cap; // fu_data_i.operation == ariane_pkg::SCSS else - clu_result = set_cap_reg_addr(REG_NULL_CAP, {{CVA6Cfg.XLEN - 1{1'b0}}, tmp_cap.tag}); + clu_result = ariane_pkg::x_to_reg({{CVA6Cfg.XLEN - 1{1'b0}}, tmp_cap.tag}); end // CGetBase ariane_pkg::GCBASE: begin - clu_result = - set_cap_reg_addr(REG_NULL_CAP, operand_a_bounds_malformed ? '0 : operand_a_base); + clu_result = ariane_pkg::x_to_reg(operand_a_bounds_malformed ? '0 : operand_a_base); end // CGetFlags ariane_pkg::GCMODE: begin - clu_result = set_cap_reg_addr(REG_NULL_CAP, - {{CVA6Cfg.XLEN - 1{1'b0}}, get_cap_reg_flags(operand_a)}); + clu_result = ariane_pkg::x_to_reg({{CVA6Cfg.XLEN - 1{1'b0}}, get_cap_reg_flags(operand_a)}); end // CGetLength ariane_pkg::GCLEN: begin - clu_result = - set_cap_reg_addr(REG_NULL_CAP, operand_a_bounds_malformed ? '0 : operand_a_length); + clu_result = ariane_pkg::x_to_reg(operand_a_bounds_malformed ? '0 : operand_a_length); end // CGetHigh ariane_pkg::GCHI: begin cap_mem = cap_reg_to_cap_mem(operand_a); - clu_result = set_cap_reg_addr(REG_NULL_CAP, cap_mem[((CVA6Cfg.XLEN*2)-1):CVA6Cfg.XLEN]); + clu_result = ariane_pkg::x_to_reg(cap_mem[((CVA6Cfg.XLEN*2)-1):CVA6Cfg.XLEN]); end // CGetPerm ariane_pkg::GCPERM: begin - clu_result = set_cap_reg_addr( - REG_NULL_CAP, + clu_result = ariane_pkg::x_to_reg( { {CVA6Cfg.XLEN - $bits(cap_report_perms_t) {1'b0}}, hperms_and_uperms_to_report_perms( - operand_a.hperms, operand_a.uperms, operand_a.flags.int_mode + operand_a.hperms, operand_a.uperms, operand_a_hperms_malformed ) } ); end // CGetTag ariane_pkg::GCTAG: begin - clu_result = set_cap_reg_addr(REG_NULL_CAP, {{CVA6Cfg.XLEN - 1{1'b0}}, operand_a.tag}); + clu_result = ariane_pkg::x_to_reg({{CVA6Cfg.XLEN - 1{1'b0}}, operand_a.tag}); end // CGetType ariane_pkg::GCTYPE: begin - clu_result = set_cap_reg_addr(REG_NULL_CAP, {{CVA6Cfg.XLEN - 1{1'b0}}, operand_a.otype}); + clu_result = ariane_pkg::x_to_reg({{CVA6Cfg.XLEN - 1{1'b0}}, operand_a.otype}); end // CIncOffset and CIncOffsetImm // TODO-cheri(ninolomata): use ALU to calculate address @@ -220,13 +215,14 @@ module cheri_unit // CSetBounds, CSetBoundsExact, CSetBoundsImm, // CRepresentableAlignmentMask ariane_pkg::SCBNDSR, ariane_pkg::SCBNDS, ariane_pkg::CRAM: begin + automatic cap_reg_set_bounds_ret_t res_set_bounds; + res_set_bounds = + set_cap_reg_bounds(operand_a, operand_a_address, {1'b0, operand_b_address}); check_operand_a_violations.tag = 1'b1; check_operand_a_violations.seal = 1'b1; check_operand_a_violations.bounds = 1'b1; - res_set_bounds = - set_cap_reg_bounds(operand_a, operand_a_address, {1'b0, operand_b_address}); if (fu_data_i.operation == ariane_pkg::CRAM) begin - clu_result = set_cap_reg_addr(REG_NULL_CAP, res_set_bounds.mask); + clu_result = ariane_pkg::x_to_reg(res_set_bounds.mask); end else begin clu_result = res_set_bounds.cap; end @@ -237,8 +233,7 @@ module cheri_unit end // CSetEqualExact ariane_pkg::SCEQ: begin - clu_result = set_cap_reg_addr( - REG_NULL_CAP, + clu_result = ariane_pkg::x_to_reg( { {CVA6Cfg.XLEN - 1{1'b0}}, (cap_reg_to_cap_mem(operand_a) == cap_reg_to_cap_mem(operand_b)) ? 1'b1 : 1'b0 @@ -287,8 +282,6 @@ module cheri_unit operand_b_base = get_cap_reg_base(operand_b, op_b_meta_info); operand_b_top = get_cap_reg_top(operand_b, op_b_meta_info); operand_b_bounds_malformed = !are_cap_reg_bounds_valid(operand_b, op_b_meta_info); - //operand_b_length = get_cap_reg_length(operand_b, op_b_meta_info}; - //operand_b_offset = get_cap_reg_offset(operand_b, op_b_meta_info); operand_b_is_sealed = (operand_b.otype != UNSEALED_CAP); operand_b_hperms_malformed = (operand_b.hperms != legalize_arch_perms(operand_b.hperms)) | (!operand_b.hperms.permit_execute & operand_b.flags.int_mode); diff --git a/hw/vendor/cva6_cheri/core/commit_stage.sv b/hw/vendor/cva6_cheri/core/commit_stage.sv index 3edd0d1b..9d5e1b5b 100644 --- a/hw/vendor/cva6_cheri/core/commit_stage.sv +++ b/hw/vendor/cva6_cheri/core/commit_stage.sv @@ -99,7 +99,9 @@ module commit_stage // TO_BE_COMPLETED - CONTROLLER output logic hfence_vvma_o, // TO_BE_COMPLETED - CONTROLLER - output logic hfence_gvma_o + output logic hfence_gvma_o, + // Breakpoint exception from trigger module + input logic break_from_trigger_i ); // ila_0 i_ila_commit ( @@ -123,7 +125,7 @@ module commit_stage always_comb begin : prepare_pc_o // Recalculate the PCC with correct address. Representability check not required because this was in-bounds at issue. automatic - cva6_cheri_pkg::cap_pcc_t + cva6_cheri_pkg::cap_reg_t pcc_o = cva6_cheri_pkg::set_cap_reg_addr( pcc_i, commit_instr_i[0].pc ); @@ -184,7 +186,7 @@ module commit_stage // we do not commit the instruction yet if we requested a halt if (commit_instr_i[0].valid && !halt_i && !halt_for_single_step_i) begin // we will not commit the instruction if we took an exception - if (commit_instr_i[0].ex.valid) begin + if (commit_instr_i[0].ex.valid || break_from_trigger_i) begin // However we can drop it (with its exception) if (commit_drop_i[0]) begin commit_ack_o[0] = 1'b1; @@ -442,5 +444,10 @@ module commit_stage if (halt_i || halt_for_single_step_i) begin exception_o.valid = 1'b0; end + + if (CVA6Cfg.SDTRIG && !CVA6Cfg.DebugEn && break_from_trigger_i) begin + exception_o.valid = 1'b1; + exception_o.cause = 32'h00000003; + end end endmodule diff --git a/hw/vendor/cva6_cheri/core/csr_regfile.sv b/hw/vendor/cva6_cheri/core/csr_regfile.sv index d969dbc5..67e9d276 100644 --- a/hw/vendor/cva6_cheri/core/csr_regfile.sv +++ b/hw/vendor/cva6_cheri/core/csr_regfile.sv @@ -25,7 +25,8 @@ module csr_regfile parameter type irq_ctrl_t = logic, parameter type scoreboard_entry_t = logic, parameter type rvfi_probes_csr_t = logic, - parameter int VmidWidth = 1 + parameter int VmidWidth = 1, + parameter int unsigned N_Triggers = 4 ) ( // Subsystem Clock - SUBSYSTEM input logic clk_i, @@ -182,7 +183,13 @@ module csr_regfile // RVFI output rvfi_probes_csr_t rvfi_csr_o, //jvt output - output jvt_t jvt_o + output jvt_t jvt_o, + // trigger module signals + output logic debug_from_trigger_o, + input logic [CVA6Cfg.VLEN-1:0] vaddr_from_lsu_i, + input logic [CVA6Cfg.NrIssuePorts-1:0][31:0] orig_instr_i, + input logic [CVA6Cfg.XLEN-1:0] store_result_i, + output logic break_from_trigger_o ); localparam logic [63:0] SMODE_STATUS_READ_MASK = ariane_pkg::smode_status_read_mask(CVA6Cfg); @@ -218,8 +225,7 @@ module csr_regfile logic csr_write_cap; logic csr_read_cap; logic csr_rcap_null; - cva6_cheri_pkg::cap_pcc_t pcc; - cva6_cheri_pkg::cap_reg_t pcc_reg; + cva6_cheri_pkg::cap_reg_t pcc; riscv::priv_lvl_t trap_to_priv_lvl; logic trap_to_v; // register for enabling load store address translation, this is critical, hence the register @@ -322,6 +328,22 @@ module csr_regfile logic [63:0][CVA6Cfg.PLEN-3:0] pmpaddr_q, pmpaddr_d, pmpaddr_next; logic [MHPMCounterNum+3-1:0] mcountinhibit_d, mcountinhibit_q; + // Trigger Module Registers + logic [CVA6Cfg.XLEN-1:0] scontext_d, scontext_q; + logic [CVA6Cfg.XLEN-1:0] tdata1_from_tm; + logic [CVA6Cfg.XLEN-1:0] tdata1_to_tm; + logic [CVA6Cfg.XLEN-1:0] tdata2_from_tm; + logic [CVA6Cfg.XLEN-1:0] tdata2_to_tm; + logic [CVA6Cfg.XLEN-1:0] tdata3_from_tm; + logic [CVA6Cfg.XLEN-1:0] tdata3_to_tm; + logic [CVA6Cfg.XLEN-1:0] tselect_to_tm; + logic [CVA6Cfg.XLEN-1:0] tselect_from_tm; + logic tselect_we, tdata1_we, tdata2_we, tdata3_we; + logic flush_from_tm; + logic break_from_trigger; + logic debug_from_trigger; + logic debug_from_mcontrol; + localparam logic [CVA6Cfg.XLEN-1:0] IsaCode = (CVA6Cfg.XLEN'(CVA6Cfg.RVA) << 0) // A - Atomic Instructions extension | (CVA6Cfg.XLEN'(CVA6Cfg.RVB) << 1) // B - Bitmanip extension | (CVA6Cfg.XLEN'(CVA6Cfg.RVC) << 2) // C - Compressed extension @@ -340,8 +362,7 @@ module csr_regfile assign pmpcfg_o = pmpcfg_q[(CVA6Cfg.NrPMPEntries>0?CVA6Cfg.NrPMPEntries-1 : 0):0]; assign pmpaddr_o = pmpaddr_q[(CVA6Cfg.NrPMPEntries>0?CVA6Cfg.NrPMPEntries-1 : 0):0]; - assign pcc = cva6_cheri_pkg::cap_pcc_t'(pc_i); - assign pcc_reg = cap_pcc_to_cap_reg(pcc); + assign pcc = cva6_cheri_pkg::cap_reg_t'(pc_i); riscv::fcsr_t fcsr_q, fcsr_d; jvt_t jvt_q, jvt_d; @@ -430,7 +451,7 @@ module csr_regfile csr_rcap = dscratch0_q; csr_rcap_null = 1'b0; end - csr_rdata = dscratch0_q[CVA6Cfg.XLEN-1:0]; + csr_rdata = reg_to_x(dscratch0_q); end else read_access_exception = 1'b1; riscv::CSR_DSCRATCH1: if (CVA6Cfg.DebugEn) begin @@ -438,7 +459,7 @@ module csr_regfile csr_rcap = dscratch1_q; csr_rcap_null = 1'b0; end - csr_rdata = dscratch1_q[CVA6Cfg.XLEN-1:0]; + csr_rdata = reg_to_x(dscratch1_q); end else read_access_exception = 1'b1; riscv::CSR_DSCRATCH2: if (CVA6Cfg.DebugEn & CVA6Cfg.CheriPresent) begin @@ -446,13 +467,33 @@ module csr_regfile csr_rcap = dscratch2_q; csr_rcap_null = 1'b0; end - csr_rdata = dscratch2_q[CVA6Cfg.XLEN-1:0]; + csr_rdata = reg_to_x(dscratch2_q); end else read_access_exception = 1'b1; - // trigger module registers - riscv::CSR_TSELECT: read_access_exception = 1'b1; // not implemented - riscv::CSR_TDATA1: read_access_exception = 1'b1; // not implemented - riscv::CSR_TDATA2: read_access_exception = 1'b1; // not implemented - riscv::CSR_TDATA3: read_access_exception = 1'b1; // not implemented + // Trigger module registers + riscv::CSR_TSELECT: + if (CVA6Cfg.SDTRIG) csr_rdata = tselect_from_tm; + else read_access_exception = 1'b1; + riscv::CSR_TDATA1: + if (CVA6Cfg.SDTRIG) csr_rdata = tdata1_from_tm; + else read_access_exception = 1'b1; + riscv::CSR_TDATA2: + if (CVA6Cfg.SDTRIG) csr_rdata = tdata2_from_tm; + else read_access_exception = 1'b1; + riscv::CSR_TDATA3: + if (CVA6Cfg.SDTRIG) csr_rdata = tdata3_from_tm; + else read_access_exception = 1'b1; + riscv::CSR_TINFO: begin + if (CVA6Cfg.SDTRIG) begin + csr_rdata = { + {CVA6Cfg.XLEN - 32{1'b0}}, 8'h1, 8'h0, 16'b1000_0000_0111_1000 + }; // trigger info:icount = 3, itrigger = 4, etrigger = 5, mcontrol6 = 6, disable = 15 + end else begin + read_access_exception = 1'b1; + end + end + riscv::CSR_SCONTEXT: + if (CVA6Cfg.SDTRIG) csr_rdata = scontext_q; + else read_access_exception = 1'b1; riscv::CSR_VSSTATUS: if (CVA6Cfg.RVH) csr_rdata = vsstatus_extended; else read_access_exception = 1'b1; @@ -470,7 +511,7 @@ module csr_regfile csr_rcap = vstvec_q; csr_rcap_null = 1'b0; end - csr_rdata = vstvec_q[CVA6Cfg.XLEN-1:0]; + csr_rdata = reg_to_x(vstvec_q); end else read_access_exception = 1'b1; riscv::CSR_VSSCRATCH: if (CVA6Cfg.RVH) begin @@ -478,7 +519,7 @@ module csr_regfile csr_rcap = vsscratch_q; csr_rcap_null = 1'b0; end - csr_rdata = vsscratch_q[CVA6Cfg.XLEN-1:0]; + csr_rdata = reg_to_x(vsscratch_q); end else read_access_exception = 1'b1; riscv::CSR_VSTID: if (CVA6Cfg.RVH && CVA6Cfg.CheriPresent) begin @@ -486,7 +527,7 @@ module csr_regfile csr_rcap = vstid_q; csr_rcap_null = 1'b0; end - csr_rdata = vstid_q[CVA6Cfg.XLEN-1:0]; + csr_rdata = reg_to_x(vstid_q); end else read_access_exception = 1'b1; riscv::CSR_VSEPC: if (CVA6Cfg.RVH) begin @@ -494,7 +535,7 @@ module csr_regfile csr_rcap = vsepc_q; csr_rcap_null = 1'b0; end - csr_rdata = vsepc_q[CVA6Cfg.XLEN-1:0]; + csr_rdata = reg_to_x(vsepc_q); end else read_access_exception = 1'b1; riscv::CSR_VSCAUSE: if (CVA6Cfg.RVH) csr_rdata = vscause_q; @@ -530,7 +571,7 @@ module csr_regfile csr_rcap = stvec_q; csr_rcap_null = 1'b0; end - csr_rdata = stvec_q[CVA6Cfg.XLEN-1:0]; + csr_rdata = reg_to_x(stvec_q); end else read_access_exception = 1'b1; riscv::CSR_SCOUNTEREN: if (CVA6Cfg.RVS) csr_rdata = scounteren_q; @@ -541,7 +582,7 @@ module csr_regfile csr_rcap = sscratch_q; csr_rcap_null = 1'b0; end - csr_rdata = sscratch_q[CVA6Cfg.XLEN-1:0]; + csr_rdata = reg_to_x(sscratch_q); end else read_access_exception = 1'b1; riscv::CSR_STID: if (CVA6Cfg.RVS && CVA6Cfg.CheriPresent) begin @@ -549,7 +590,7 @@ module csr_regfile csr_rcap = stid_q; csr_rcap_null = 1'b0; end - csr_rdata = stid_q[CVA6Cfg.XLEN-1:0]; + csr_rdata = reg_to_x(stid_q); end else read_access_exception = 1'b1; riscv::CSR_SEPC: if (CVA6Cfg.RVS) begin @@ -557,7 +598,7 @@ module csr_regfile csr_rcap = sepc_q; csr_rcap_null = 1'b0; end - csr_rdata = sepc_q[XLEN-1:0]; + csr_rdata = reg_to_x(sepc_q); end else read_access_exception = 1'b1; riscv::CSR_SCAUSE: if (CVA6Cfg.RVS) csr_rdata = scause_q; @@ -651,7 +692,7 @@ module csr_regfile csr_rcap = mtvec_q; csr_rcap_null = 1'b0; end - csr_rdata = mtvec_q[CVA6Cfg.XLEN-1:0]; + csr_rdata = reg_to_x(mtvec_q); end riscv::CSR_MCOUNTEREN: if (CVA6Cfg.RVU) csr_rdata = mcounteren_q; @@ -661,7 +702,7 @@ module csr_regfile csr_rcap = mscratch_q; csr_rcap_null = 1'b0; end - csr_rdata = mscratch_q[CVA6Cfg.XLEN-1:0]; + csr_rdata = reg_to_x(mscratch_q); end riscv::CSR_MTID: if (CVA6Cfg.CheriPresent) begin @@ -669,14 +710,14 @@ module csr_regfile csr_rcap = mtid_q; csr_rcap_null = 1'b0; end - csr_rdata = mtid_q[CVA6Cfg.XLEN-1:0]; + csr_rdata = reg_to_x(mtid_q); end else read_access_exception = 1'b1; riscv::CSR_MEPC: begin if (CVA6Cfg.CheriPresent && csr_read_cap) begin csr_rcap = mepc_q; csr_rcap_null = 1'b0; end - csr_rdata = mepc_q[CVA6Cfg.XLEN-1:0]; + csr_rdata = reg_to_x(mepc_q); end riscv::CSR_MCAUSE: csr_rdata = mcause_q; riscv::CSR_MTVAL: @@ -701,7 +742,7 @@ module csr_regfile riscv::CSR_MSECCFGH: csr_rdata = '0; riscv::CSR_MVENDORID: csr_rdata = {{CVA6Cfg.XLEN - 32{1'b0}}, OPENHWGROUP_MVENDORID}; riscv::CSR_MARCHID: csr_rdata = {{CVA6Cfg.XLEN - 32{1'b0}}, ARIANE_MARCHID}; - riscv::CSR_MIMPID: csr_rdata = '0; // not implemented + riscv::CSR_MIMPID: csr_rdata = {{CVA6Cfg.XLEN - 32{1'b0}}, ARIANE_MIMPID}; riscv::CSR_MHARTID: csr_rdata = hart_id_i; riscv::CSR_MCONFIGPTR: csr_rdata = '0; // not implemented riscv::CSR_MCOUNTINHIBIT: @@ -1018,7 +1059,7 @@ module csr_regfile csr_rcap = ddc_q; csr_rcap_null = 1'b0; end - csr_rdata = ddc_q[CVA6Cfg.XLEN-1:0]; + csr_rdata = reg_to_x(ddc_q); end else read_access_exception = 1'b1; riscv::CSR_UTID: if (CVA6Cfg.CheriPresent) begin @@ -1026,7 +1067,7 @@ module csr_regfile csr_rcap = utid_q; csr_rcap_null = 1'b0; end - csr_rdata = utid_q[CVA6Cfg.XLEN-1:0]; + csr_rdata = reg_to_x(utid_q); end else read_access_exception = 1'b1; default: read_access_exception = 1'b1; endcase @@ -1069,7 +1110,7 @@ module csr_regfile csr_update_cap_prelegal = CVA6Cfg.CheriPresent ? csr_wdata_i : '0; csr_update_allow_sealed = 1'b0; - csr_wdata_legalised = csr_wdata_i[CVA6Cfg.XLEN-1:0]; + csr_wdata_legalised = reg_to_x(csr_wdata_i); if (CVA6Cfg.RVS) begin satp = satp_q; @@ -1203,6 +1244,16 @@ module csr_regfile pmpcfg_d = pmpcfg_q; pmpaddr_d = pmpaddr_q; + // trigger module defaults + tdata1_to_tm = '0; + tdata2_to_tm = '0; + tdata3_to_tm = '0; + tselect_to_tm = '0; + tselect_we = 1'b0; + tdata1_we = 1'b0; + tdata2_we = 1'b0; + tdata3_we = 1'b0; + if (CVA6Cfg.CheriPresent) begin ddc_d = ddc_q; utid_d = utid_q; @@ -1210,10 +1261,9 @@ module csr_regfile if (mtvec_rst_load_q) begin if (CVA6Cfg.RVFI_DII) begin - mtvec_d = cva6_cheri_pkg::REG_ROOT_CAP; + mtvec_d = REG_ROOT; end else begin - mtvec_d = - set_cap_reg_addr(cap_pcc_to_cap_reg(boot_addr_i), boot_addr_i[CVA6Cfg.XLEN-1:0] + 'h40); + mtvec_d = set_cap_reg_addr(boot_addr_i, reg_to_x(boot_addr_i) + 'h40); end end else begin mtvec_d = mtvec_q; @@ -1281,7 +1331,7 @@ module csr_regfile if (CVA6Cfg.DebugEn) begin if (CVA6Cfg.CheriPresent) begin // XXX debugger injects infinite cap on PCC changes - csr_update_cap_prelegal = cva6_cheri_pkg::REG_ROOT_CAP; + csr_update_cap_prelegal = REG_ROOT; dpc_d = csr_update_cap_postlegal; end else begin dpc_d = csr_wdata; @@ -1304,11 +1354,47 @@ module csr_regfile update_access_exception = 1'b1; end end - // trigger module CSRs - riscv::CSR_TSELECT: update_access_exception = 1'b1; // not implemented - riscv::CSR_TDATA1: update_access_exception = 1'b1; // not implemented - riscv::CSR_TDATA2: update_access_exception = 1'b1; // not implemented - riscv::CSR_TDATA3: update_access_exception = 1'b1; // not implemented + // Trigger module CSRs + riscv::CSR_TSELECT: begin + if (CVA6Cfg.SDTRIG) begin + tselect_to_tm = csr_wdata; + tselect_we = csr_we; + end else begin + update_access_exception = 1'b1; + end + end + riscv::CSR_TDATA1: begin + if (CVA6Cfg.SDTRIG) begin + tdata1_to_tm = csr_wdata; + tdata1_we = csr_we; + flush_o = flush_from_tm; + end else begin + update_access_exception = 1'b1; + end + end + riscv::CSR_TDATA2: begin + if (CVA6Cfg.SDTRIG) begin + tdata2_to_tm = csr_wdata; + tdata2_we = csr_we; + end else begin + update_access_exception = 1'b1; + end + end + riscv::CSR_TDATA3: begin + if (CVA6Cfg.SDTRIG) begin + tdata3_to_tm = csr_wdata; + tdata3_we = csr_we; + end else begin + update_access_exception = 1'b1; + end + end + riscv::CSR_SCONTEXT: begin + if (CVA6Cfg.SDTRIG) begin + scontext_d = {{CVA6Cfg.XLEN - 32{1'b0}}, csr_wdata[31:0]}; + end else begin + update_access_exception = 1'b1; + end + end // virtual supervisor registers riscv::CSR_VSSTATUS: begin if (CVA6Cfg.RVH) begin @@ -2162,7 +2248,7 @@ module csr_regfile trap_to_v = 1'b0; // Exception is taken and we are not in debug mode // exceptions in debug mode don't update any fields - if ((CVA6Cfg.DebugEn && !debug_mode_q && ex_i.cause != riscv::DEBUG_REQUEST && ex_i.valid) || (!CVA6Cfg.DebugEn && ex_i.valid)) begin + if ((CVA6Cfg.DebugEn && !debug_mode_q && ex_i.cause != riscv::DEBUG_REQUEST && ex_i.valid) || (!CVA6Cfg.DebugEn && ex_i.valid) || (!CVA6Cfg.DebugEn && CVA6Cfg.SDTRIG && break_from_trigger)) begin // do not flush, flush is reserved for CSR writes with side effects flush_o = 1'b0; // figure out where to trap to @@ -2203,7 +2289,7 @@ module csr_regfile vscause_d = ex_i.cause[CVA6Cfg.XLEN-1] ? {ex_i.cause[CVA6Cfg.XLEN-1:2], 2'b01} : ex_i.cause; // set epc if (CVA6Cfg.CheriPresent) begin - vsepc_d = pcc_reg; + vsepc_d = pcc; end else begin vsepc_d = {{CVA6Cfg.XLEN - CVA6Cfg.VLEN{pc_i[CVA6Cfg.VLEN-1]}}, pc_i}; end @@ -2224,7 +2310,7 @@ module csr_regfile scause_d = ex_i.cause; // set epc if (CVA6Cfg.CheriPresent) begin - sepc_d = pcc_reg; + sepc_d = pcc; end else begin sepc_d = {{CVA6Cfg.XLEN - CVA6Cfg.VLEN{pc_i[CVA6Cfg.VLEN-1]}}, pc_i}; end @@ -2251,12 +2337,12 @@ module csr_regfile riscv::VIRTUAL_INSTRUCTION } || ex_i.cause[CVA6Cfg.XLEN-1])) ? '0 : {{CVA6Cfg.XLEN - 32 {1'b0}}, ex_i.tinst}; hstatus_d.spvp = v_q ? priv_lvl_q[0] : hstatus_d.spvp; - htval_d = {{CVA6Cfg.XLEN - CVA6Cfg.GPLEN{1'b0}}, ex_i.tval2[CVA6Cfg.GPLEN-1:0]}; + htval_d = {{CVA6Cfg.XLEN - CVA6Cfg.GPLEN + 2{1'b0}}, ex_i.tval2[CVA6Cfg.GPLEN-1:2]}; hstatus_d.gva = ex_i.gva; hstatus_d.spv = v_q; end if (CVA6Cfg.CheriPresent) begin - stval2_d = {{CVA6Cfg.XLEN - CVA6Cfg.GPLEN{1'b0}}, ex_i.tval2[CVA6Cfg.GPLEN-1:0]}; + stval2_d = {{CVA6Cfg.XLEN - CVA6Cfg.GPLEN + 2{1'b0}}, ex_i.tval2[CVA6Cfg.GPLEN-1:2]}; end end // trap to machine mode @@ -2266,10 +2352,10 @@ module csr_regfile mstatus_d.mpie = mstatus_q.mie; // save the previous privilege mode mstatus_d.mpp = priv_lvl_q; - mcause_d = ex_i.cause; + mcause_d = (break_from_trigger) ? 32'h00000003 : ex_i.cause; // set epc if (CVA6Cfg.CheriPresent) begin - mepc_d = pcc_reg; + mepc_d = pcc; end else begin mepc_d = {{CVA6Cfg.XLEN - CVA6Cfg.VLEN{pc_i[CVA6Cfg.VLEN-1]}}, pc_i}; end @@ -2306,7 +2392,7 @@ module csr_regfile mstatus_d.gva = ex_i.gva; end if (CVA6Cfg.CheriPresent || CVA6Cfg.RVH) begin - mtval2_d = {{CVA6Cfg.XLEN - CVA6Cfg.GPLEN{1'b0}}, ex_i.tval2[CVA6Cfg.GPLEN-1:0]}; + mtval2_d = {{CVA6Cfg.XLEN - CVA6Cfg.GPLEN + 2{1'b0}}, ex_i.tval2[CVA6Cfg.GPLEN-1:2]}; end end @@ -2330,7 +2416,16 @@ module csr_regfile dcsr_d.prv = priv_lvl_o; // save virtualization mode bit dcsr_d.v = (!CVA6Cfg.RVH) ? 1'b0 : v_q; + // trigger module fired + if (CVA6Cfg.SDTRIG && debug_from_trigger) begin + dcsr_d.prv = priv_lvl_o; + dcsr_d.v = (!CVA6Cfg.RVH) ? 1'b0 : v_q; + dpc_d = {{CVA6Cfg.XLEN - CVA6Cfg.VLEN{pc_i[CVA6Cfg.VLEN-1]}}, pc_i}; + debug_mode_d = 1'b1; + set_debug_pc_o = 1'b1; + dcsr_d.cause = ariane_pkg::CauseTrigger; + end // caused by a breakpoint if (ex_i.valid && ex_i.cause == riscv::BREAKPOINT) begin @@ -2359,7 +2454,7 @@ module csr_regfile endcase // save PC of next this instruction e.g.: the next one to be executed if (CVA6Cfg.CheriPresent) begin - dpc_d = pcc_reg; + dpc_d = pcc; end else begin dpc_d = {{CVA6Cfg.XLEN - CVA6Cfg.VLEN{pc_i[CVA6Cfg.VLEN-1]}}, pc_i[CVA6Cfg.VLEN-1:0]}; end @@ -2372,7 +2467,7 @@ module csr_regfile dcsr_d.v = (!CVA6Cfg.RVH) ? 1'b0 : v_q; // save the PC if (CVA6Cfg.CheriPresent) begin - dpc_d = pcc_reg; + dpc_d = pcc; end else begin dpc_d = {{CVA6Cfg.XLEN - CVA6Cfg.VLEN{pc_i[CVA6Cfg.VLEN-1]}}, pc_i[CVA6Cfg.VLEN-1:0]}; end @@ -2395,7 +2490,7 @@ module csr_regfile dcsr_d.v = (!CVA6Cfg.RVH) ? 1'b0 : v_q; // save the PC if (CVA6Cfg.CheriPresent) begin - dpc_d = pcc_reg; + dpc_d = pcc; end else begin dpc_d = {{CVA6Cfg.XLEN - CVA6Cfg.VLEN{pc_i[CVA6Cfg.VLEN-1]}}, pc_i[CVA6Cfg.VLEN-1:0]}; end @@ -2545,7 +2640,7 @@ module csr_regfile // CSR OP Select Logic // --------------------------- always_comb begin : csr_op_logic - csr_wdata = csr_wdata_i[CVA6Cfg.XLEN-1:0]; + csr_wdata = reg_to_x(csr_wdata_i); csr_we = 1'b1; csr_write_cap = (CVA6Cfg.CheriPresent && !commit_instr_i.int_mode && csr_op_i == CSR_WRITE && !csr_op_is_imm_i) ? 1'b1 : 1'b0; ; @@ -2556,9 +2651,9 @@ module csr_regfile dret = 1'b0; unique case (csr_op_i) - CSR_WRITE: csr_wdata = csr_wdata_i[CVA6Cfg.XLEN-1:0]; - CSR_SET: csr_wdata = csr_wdata_i[CVA6Cfg.XLEN-1:0] | csr_rdata; - CSR_CLEAR: csr_wdata = (~csr_wdata_i[CVA6Cfg.XLEN-1:0]) & csr_rdata; + CSR_WRITE: csr_wdata = reg_to_x(csr_wdata_i); + CSR_SET: csr_wdata = reg_to_x(csr_wdata_i) | csr_rdata; + CSR_CLEAR: csr_wdata = (~reg_to_x(csr_wdata_i)) & csr_rdata; CSR_READ: csr_we = 1'b0; MRET: begin // the return should not have any write or read side-effects @@ -2830,7 +2925,7 @@ module csr_regfile if (CVA6Cfg.DebugEn && debug_mode_q) begin if (CVA6Cfg.CheriPresent) begin trap_vector_base_o = cva6_cheri_pkg::set_cap_reg_addr( - cva6_cheri_pkg::REG_ROOT_CAP, + REG_ROOT, CVA6Cfg.DmBaseAddress[CVA6Cfg.VLEN-1:0] + CVA6Cfg.ExceptionAddress[CVA6Cfg.VLEN-1:0] ); end else begin @@ -2896,10 +2991,8 @@ module csr_regfile default: ; endcase - if (CVA6Cfg.CheriPresent) begin - if (csr_rcap_null) csr_rdata_o = set_cap_reg_addr(REG_NULL_CAP, csr_rdata_tmp); - else csr_rdata_o = csr_rcap; - end else csr_rdata_o = csr_rdata_tmp; + csr_rdata_o = x_to_reg(csr_rdata_tmp); + if (CVA6Cfg.CheriPresent && !csr_rcap_null) csr_rdata_o = csr_rcap; end // in debug mode we execute with privilege level M @@ -2994,7 +3087,7 @@ module csr_regfile if (CVA6Cfg.DebugEn) begin debug_mode_q <= 1'b0; dcsr_q <= '{xdebugver: 4'h4, prv: riscv::PRIV_LVL_M, default: '0}; - dpc_q <= CVA6Cfg.CheriPresent ? cva6_cheri_pkg::REG_ROOT_CAP : '0; + dpc_q <= REG_ROOT; dscratch0_q <= {CVA6Cfg.XLEN{1'b0}}; dscratch1_q <= {CVA6Cfg.XLEN{1'b0}}; dscratch2_q <= {CVA6Cfg.XLEN{1'b0}}; @@ -3004,14 +3097,14 @@ module csr_regfile mstatus_q <= 64'b0; // set to boot address + direct mode + 4 byte offset which is the initial trap mtvec_rst_load_q <= 1'b1; - mtvec_q <= (CVA6Cfg.CheriPresent) ? cva6_cheri_pkg::REG_ROOT_CAP : 0; + mtvec_q <= REG_ROOT; mip_q <= {CVA6Cfg.XLEN{1'b0}}; mie_q <= {CVA6Cfg.XLEN{1'b0}}; - mepc_q <= (CVA6Cfg.CheriPresent) ? cva6_cheri_pkg::REG_ROOT_CAP : {CVA6Cfg.XLEN{1'b0}}; + mepc_q <= REG_ROOT; mcause_q <= {CVA6Cfg.XLEN{1'b0}}; mcounteren_q <= {CVA6Cfg.XLEN{1'b0}}; - mscratch_q <= (CVA6Cfg.CheriPresent) ? cva6_cheri_pkg::REG_NULL_CAP : {CVA6Cfg.XLEN{1'b0}}; - if (CVA6Cfg.CheriPresent) mtid_q <= cva6_cheri_pkg::REG_NULL_CAP; + mscratch_q <= REG_NULL; + if (CVA6Cfg.CheriPresent) mtid_q <= REG_NULL; if (CVA6Cfg.TvalEn) mtval_q <= {CVA6Cfg.XLEN{1'b0}}; mfiom_q <= '0; dcache_q <= {{CVA6Cfg.XLEN - 1{1'b0}}, 1'b1}; @@ -3022,12 +3115,12 @@ module csr_regfile if (CVA6Cfg.RVS) begin medeleg_q <= {CVA6Cfg.XLEN{1'b0}}; mideleg_q <= {CVA6Cfg.XLEN{1'b0}}; - sepc_q <= (CVA6Cfg.CheriPresent) ? cva6_cheri_pkg::REG_ROOT_CAP : {CVA6Cfg.XLEN{1'b0}}; + sepc_q <= REG_ROOT; scause_q <= {CVA6Cfg.XLEN{1'b0}}; - stvec_q <= (CVA6Cfg.CheriPresent) ? cva6_cheri_pkg::REG_ROOT_CAP : {CVA6Cfg.XLEN{1'b0}}; + stvec_q <= REG_ROOT; scounteren_q <= {CVA6Cfg.XLEN{1'b0}}; - sscratch_q <= (CVA6Cfg.CheriPresent) ? cva6_cheri_pkg::REG_NULL_CAP : {CVA6Cfg.XLEN{1'b0}}; - if (CVA6Cfg.CheriPresent) stid_q <= cva6_cheri_pkg::REG_NULL_CAP; + sscratch_q <= REG_NULL; + if (CVA6Cfg.CheriPresent) stid_q <= REG_NULL; stval_q <= {CVA6Cfg.XLEN{1'b0}}; stval2_q <= {CVA6Cfg.XLEN{1'b0}}; sfiom_q <= '0; @@ -3051,15 +3144,18 @@ module csr_regfile htinst_q <= {CVA6Cfg.XLEN{1'b0}}; // virtual supervisor mode registers vsstatus_q <= 64'b0; - vsepc_q <= (CVA6Cfg.CheriPresent) ? cva6_cheri_pkg::REG_ROOT_CAP : {CVA6Cfg.XLEN{1'b0}}; + vsepc_q <= REG_ROOT; vscause_q <= {CVA6Cfg.XLEN{1'b0}}; - vstvec_q <= (CVA6Cfg.CheriPresent) ? cva6_cheri_pkg::REG_ROOT_CAP : {CVA6Cfg.XLEN{1'b0}}; - vsscratch_q <= (CVA6Cfg.CheriPresent) ? cva6_cheri_pkg::REG_NULL_CAP : {CVA6Cfg.XLEN{1'b0}}; - if (CVA6Cfg.CheriPresent) vstid_q <= cva6_cheri_pkg::REG_NULL_CAP; + vstvec_q <= REG_ROOT; + vsscratch_q <= REG_NULL; + if (CVA6Cfg.CheriPresent) vstid_q <= REG_NULL; vstval_q <= {CVA6Cfg.XLEN{1'b0}}; vsatp_q <= {CVA6Cfg.XLEN{1'b0}}; en_ld_st_g_translation_q <= 1'b0; end + if (CVA6Cfg.SDTRIG) begin + scontext_q <= '0; + end // timer and counters cycle_q <= 64'b0; instret_q <= 64'b0; @@ -3078,8 +3174,8 @@ module csr_regfile end end if (CVA6Cfg.CheriPresent) begin - ddc_q <= cva6_cheri_pkg::REG_ROOT_CAP; - utid_q <= cva6_cheri_pkg::REG_NULL_CAP; + ddc_q <= REG_ROOT; + utid_q <= REG_NULL; end end else begin priv_lvl_q <= priv_lvl_d; @@ -3157,6 +3253,9 @@ module csr_regfile vsatp_q <= vsatp_d; en_ld_st_g_translation_q <= en_ld_st_g_translation_d; end + if (CVA6Cfg.SDTRIG) begin + scontext_q <= scontext_d; + end // timer and counters cycle_q <= cycle_d; instret_q <= instret_d; @@ -3174,6 +3273,9 @@ module csr_regfile end end + assign debug_from_trigger_o = debug_from_mcontrol; // from trigger module to id stage + assign break_from_trigger_o = break_from_trigger; // from trigger module to commit stage + // write logic pmp always_comb begin : write for (int i = 0; i < 64; i++) begin @@ -3204,6 +3306,60 @@ module csr_regfile end end + //------------- + // Trigger Module + //------------- + generate + if (CVA6Cfg.SDTRIG) begin : tm_gen + trigger_module #( + .CVA6Cfg (CVA6Cfg), + .exception_t (exception_t), + .scoreboard_entry_t(scoreboard_entry_t), + .N_Triggers (N_Triggers) + ) trigger_module_i ( + .clk_i, + .rst_ni, + .commit_instr_i (commit_instr_i), + .commit_ack_i (commit_ack_i), + .ex_i (ex_i), + .vaddr_from_lsu_i (vaddr_from_lsu_i), + .orig_instr_i (orig_instr_i), + .store_result_i (store_result_i), + .priv_lvl_i (priv_lvl_o), + .debug_mode_i (debug_mode_q), + .mret_i (mret), + .sret_i (sret), + .scontext_i (scontext_q), + .tselect_i (tselect_to_tm), + .tdata1_i (tdata1_to_tm), + .tdata2_i (tdata2_to_tm), + .tdata3_i (tdata3_to_tm), + .tselect_we (tselect_we), + .tdata1_we (tdata1_we), + .tdata2_we (tdata2_we), + .tdata3_we (tdata3_we), + .tselect_o (tselect_from_tm), + .tdata1_o (tdata1_from_tm), + .tdata2_o (tdata2_from_tm), + .tdata3_o (tdata3_from_tm), + .flush_o (flush_from_tm), + // debug/break request from TM + .debug_from_trigger_o (debug_from_trigger), + .break_from_trigger_o (break_from_trigger), + .debug_from_mcontrol_o(debug_from_mcontrol) + ); + end else begin + assign tdata1_from_tm = '0; + assign tdata2_from_tm = '0; + assign tdata3_from_tm = '0; + assign tselect_from_tm = '0; + assign flush_from_tm = 0; + assign debug_from_trigger = 0; + assign break_from_trigger = 0; + assign debug_from_mcontrol = 0; + end + endgenerate + //------------- // Assertions //------------- diff --git a/hw/vendor/cva6_cheri/core/cva6.sv b/hw/vendor/cva6_cheri/core/cva6.sv index 19dc0697..78bd15a2 100644 --- a/hw/vendor/cva6_cheri/core/cva6.sv +++ b/hw/vendor/cva6_cheri/core/cva6.sv @@ -142,13 +142,14 @@ module cva6 // all the necessary data structures // bp_resolve_t localparam type bp_resolve_t = struct packed { - logic valid; // prediction with all its values is valid - logic [CVA6Cfg.VLEN-1:0] pc; // PC of predict or mis-predict - logic [CVA6Cfg.DIIIDLEN-1:0] dii_id; // dii id of branch - logic [CVA6Cfg.PCLEN-1:0] target_address; // target address at which to jump, or not - logic is_mispredict; // set if this was a mis-predict - logic is_taken; // branch is taken - cf_t cf_type; // Type of control flow change + logic valid; // prediction with all its values is valid + logic [CVA6Cfg.VLEN-1:0] pc; // PC of predict or mis-predict + logic [CVA6Cfg.DIIIDLEN-1:0] dii_id; // dii id of branch + logic [CVA6Cfg.PCLEN-1:0] target_address; // target address at which to jump, or not + logic is_mispredict; // set if this was a mis-predict + logic is_taken; // branch is taken + logic is_pcc_change; // with CHERI: set if the branch changes PCC metadata + cf_t cf_type; // Type of control flow change }, // All information needed to determine whether we need to associate an interrupt @@ -329,7 +330,7 @@ module cva6 parameter type x_commit_t = `X_COMMIT_T(CVA6Cfg, hartid_t, id_t), parameter type x_result_t = `X_RESULT_T(CVA6Cfg, hartid_t, id_t, writeregflags_t), parameter type cvxif_req_t = - `CVXIF_REQ_T(CVA6Cfg, x_compressed_req_t, x_issue_req_t, x_register_req_t, x_commit_t), + `CVXIF_REQ_T(CVA6Cfg, x_compressed_req_t, x_issue_req_t, x_register_t, x_commit_t), parameter type cvxif_resp_t = `CVXIF_RESP_T(CVA6Cfg, x_compressed_resp_t, x_issue_resp_t, x_result_t) ) ( @@ -375,16 +376,16 @@ module cva6 }; localparam interrupts_t INTERRUPTS = '{ - S_SW: (1 << (CVA6Cfg.XLEN - 1)) | CVA6Cfg.XLEN'(riscv::IRQ_S_SOFT), - VS_SW: (1 << (CVA6Cfg.XLEN - 1)) | CVA6Cfg.XLEN'(riscv::IRQ_VS_SOFT), - M_SW: (1 << (CVA6Cfg.XLEN - 1)) | CVA6Cfg.XLEN'(riscv::IRQ_M_SOFT), - S_TIMER: (1 << (CVA6Cfg.XLEN - 1)) | CVA6Cfg.XLEN'(riscv::IRQ_S_TIMER), - VS_TIMER: (1 << (CVA6Cfg.XLEN - 1)) | CVA6Cfg.XLEN'(riscv::IRQ_VS_TIMER), - M_TIMER: (1 << (CVA6Cfg.XLEN - 1)) | CVA6Cfg.XLEN'(riscv::IRQ_M_TIMER), - S_EXT: (1 << (CVA6Cfg.XLEN - 1)) | CVA6Cfg.XLEN'(riscv::IRQ_S_EXT), - VS_EXT: (1 << (CVA6Cfg.XLEN - 1)) | CVA6Cfg.XLEN'(riscv::IRQ_VS_EXT), - M_EXT: (1 << (CVA6Cfg.XLEN - 1)) | CVA6Cfg.XLEN'(riscv::IRQ_M_EXT), - HS_EXT: (1 << (CVA6Cfg.XLEN - 1)) | CVA6Cfg.XLEN'(riscv::IRQ_HS_EXT) + S_SW: (CVA6Cfg.XLEN'(1) << (CVA6Cfg.XLEN - 1)) | CVA6Cfg.XLEN'(riscv::IRQ_S_SOFT), + VS_SW: (CVA6Cfg.XLEN'(1) << (CVA6Cfg.XLEN - 1)) | CVA6Cfg.XLEN'(riscv::IRQ_VS_SOFT), + M_SW: (CVA6Cfg.XLEN'(1) << (CVA6Cfg.XLEN - 1)) | CVA6Cfg.XLEN'(riscv::IRQ_M_SOFT), + S_TIMER: (CVA6Cfg.XLEN'(1) << (CVA6Cfg.XLEN - 1)) | CVA6Cfg.XLEN'(riscv::IRQ_S_TIMER), + VS_TIMER: (CVA6Cfg.XLEN'(1) << (CVA6Cfg.XLEN - 1)) | CVA6Cfg.XLEN'(riscv::IRQ_VS_TIMER), + M_TIMER: (CVA6Cfg.XLEN'(1) << (CVA6Cfg.XLEN - 1)) | CVA6Cfg.XLEN'(riscv::IRQ_M_TIMER), + S_EXT: (CVA6Cfg.XLEN'(1) << (CVA6Cfg.XLEN - 1)) | CVA6Cfg.XLEN'(riscv::IRQ_S_EXT), + VS_EXT: (CVA6Cfg.XLEN'(1) << (CVA6Cfg.XLEN - 1)) | CVA6Cfg.XLEN'(riscv::IRQ_VS_EXT), + M_EXT: (CVA6Cfg.XLEN'(1) << (CVA6Cfg.XLEN - 1)) | CVA6Cfg.XLEN'(riscv::IRQ_M_EXT), + HS_EXT: (CVA6Cfg.XLEN'(1) << (CVA6Cfg.XLEN - 1)) | CVA6Cfg.XLEN'(riscv::IRQ_HS_EXT) }; // ------------------------------------------ @@ -454,6 +455,7 @@ module cva6 logic [CVA6Cfg.NrIssuePorts-1:0][CVA6Cfg.XLEN-1:0] rvfi_rs2; fu_data_t [CVA6Cfg.NrIssuePorts-1:0] fu_data_id_ex; + alu_bypass_t alu_bypass_id_ex; logic [CVA6Cfg.PCLEN-1:0] pc_id_ex; logic [CVA6Cfg.DIIIDLEN-1:0] dii_id_id_ex; logic zcmt_id_ex; @@ -478,7 +480,7 @@ module cva6 logic [CVA6Cfg.NrIssuePorts-1:0] lsu_valid_id_ex; logic lsu_ready_ex_id; // CLU - logic clu_valid_id_ex; + logic [CVA6Cfg.NrIssuePorts-1:0] clu_valid_id_ex; logic [CVA6Cfg.TRANS_ID_BITS-1:0] load_trans_id_ex_id; logic [CVA6Cfg.REGLEN-1:0] load_result_ex_id; @@ -500,6 +502,7 @@ module cva6 logic [CVA6Cfg.REGLEN-1:0] fpu_result_ex_id; logic fpu_valid_ex_id; exception_t fpu_exception_ex_id; + logic fpu_early_valid_ex_id; // ALU2 logic [CVA6Cfg.NrIssuePorts-1:0] alu2_valid_id_ex; // Accelerator @@ -625,6 +628,9 @@ module cva6 logic [31:0] mcountinhibit_csr_perf; //jvt jvt_t jvt; + // trigger module + logic debug_from_trigger; + logic break_from_trigger; // ---------------------------- // Performance Counters <-> * // ---------------------------- @@ -637,6 +643,7 @@ module cva6 logic dtlb_miss_ex_perf; logic dcache_miss_cache_perf; logic icache_miss_cache_perf; + logic icache_miss_cache_perf_real; logic [NumPorts-1:0][CVA6Cfg.DCACHE_SET_ASSOC-1:0] miss_vld_bits; logic stall_issue; // -------------- @@ -669,8 +676,10 @@ module cva6 icache_areq_t icache_areq_ex_cache; icache_arsp_t icache_areq_cache_ex; + icache_arsp_t icache_areq_cache_ex_real; icache_dreq_t icache_dreq_if_cache; icache_drsp_t icache_dreq_cache_if; + icache_drsp_t icache_dreq_cache_if_real; amo_req_t amo_req; amo_resp_t amo_resp; @@ -789,6 +798,7 @@ module cva6 .compressed_valid_o(x_compressed_valid), .compressed_req_o(x_compressed_req), .jvt_i(jvt), + .debug_from_trigger_i(debug_from_trigger), // DCACHE interfaces .dcache_req_ports_i(dcache_req_ports_cache_id), .dcache_req_ports_o(dcache_req_ports_id_cache), @@ -859,6 +869,13 @@ module cva6 assign wt_valid_ex_id[ACC_WB] = acc_valid_ex_id; end else begin assign cvxif_req = '0; + assign x_compressed_ready = '0; + assign x_compressed_resp = '0; + assign x_issue_ready = '0; + assign x_issue_resp = '0; + assign x_register_ready = '0; + assign x_result_valid = '0; + assign x_result = '0; end if (CVA6Cfg.CvxifEn && CVA6Cfg.EnableAccelerator) begin : gen_err_xif_and_acc @@ -900,6 +917,7 @@ module cva6 .rs1_forwarding_o (rs1_forwarding_id_ex), .rs2_forwarding_o (rs2_forwarding_id_ex), .fu_data_o (fu_data_id_ex), + .alu_bypass_o (alu_bypass_id_ex), .pc_o (pc_id_ex), .commit_pcc_o (commit_pcc), .dii_id_o (dii_id_id_ex), @@ -927,6 +945,7 @@ module cva6 .fpu_valid_o (fpu_valid_id_ex), .fpu_fmt_o (fpu_fmt_id_ex), .fpu_rm_o (fpu_rm_id_ex), + .fpu_early_valid_i (fpu_early_valid_ex_id), // ALU2 .alu2_valid_o (alu2_valid_id_ex), // CSR @@ -1010,6 +1029,7 @@ module cva6 .rs1_forwarding_i(rs1_forwarding_id_ex), .rs2_forwarding_i(rs2_forwarding_id_ex), .fu_data_i(fu_data_id_ex), + .alu_bypass_i(alu_bypass_id_ex), .pc_i(pc_id_ex), .dii_id_i(dii_id_id_ex), .is_zcmt_i(zcmt_id_ex), @@ -1070,6 +1090,7 @@ module cva6 .fpu_result_o (fpu_result_ex_id), .fpu_valid_o (fpu_valid_ex_id), .fpu_exception_o (fpu_exception_ex_id), + .fpu_early_valid_o (fpu_early_valid_ex_id), // ALU2 .alu2_valid_i (alu2_valid_id_ex), .amo_valid_commit_i (amo_valid_commit), @@ -1186,7 +1207,8 @@ module cva6 .flush_commit_o (flush_commit), .sfence_vma_o (sfence_vma_commit_controller), .hfence_vvma_o (hfence_vvma_commit_controller), - .hfence_gvma_o (hfence_gvma_commit_controller) + .hfence_gvma_o (hfence_gvma_commit_controller), + .break_from_trigger_i (break_from_trigger) ); assign commit_ack = commit_macro_ack & ~commit_drop_id_commit; @@ -1280,7 +1302,13 @@ module cva6 .mcountinhibit_o (mcountinhibit_csr_perf), .jvt_o (jvt), //RVFI - .rvfi_csr_o (rvfi_csr) + .rvfi_csr_o (rvfi_csr), + // Trigger Signals + .debug_from_trigger_o (debug_from_trigger), + .vaddr_from_lsu_i (rvfi_lsu_ctrl.vaddr), + .orig_instr_i (orig_instr_id_issue), + .store_result_i (reg_to_x(store_result_ex_id)), + .break_from_trigger_o (break_from_trigger) ); // ------------------------ @@ -1438,15 +1466,15 @@ module cva6 .clk_i (clk_i), .rst_ni (rst_ni), // I$ - .icache_en_i (1'b0), + .icache_en_i (icache_en_csr), .icache_flush_i (icache_flush_ctrl_cache), - .icache_miss_o (icache_miss_cache_perf), + .icache_miss_o (icache_miss_cache_perf_real), .icache_areq_i (icache_areq_ex_cache), - .icache_areq_o (icache_areq_cache_ex), + .icache_areq_o (icache_areq_cache_ex_real), .icache_dreq_i (icache_dreq_if_cache), - .icache_dreq_o (icache_dreq_cache_if), + .icache_dreq_o (icache_dreq_cache_if_real), // D$ - .dcache_enable_i (1'b0), + .dcache_enable_i (dcache_en_csr_nbdcache), .dcache_flush_i (dcache_flush_ctrl_cache), .dcache_flush_ack_o(dcache_flush_ack_cache_ctrl), // to commit stage @@ -1497,15 +1525,15 @@ module cva6 .clk_i (clk_i), .rst_ni(rst_ni), - .icache_en_i (icache_en_csr), + .icache_en_i (1'b0), .icache_flush_i(icache_flush_ctrl_cache), - .icache_miss_o (icache_miss_cache_perf), + .icache_miss_o (icache_miss_cache_perf_real), .icache_areq_i (icache_areq_ex_cache), - .icache_areq_o (icache_areq_cache_ex), + .icache_areq_o (icache_areq_cache_ex_real), .icache_dreq_i (icache_dreq_if_cache), - .icache_dreq_o (icache_dreq_cache_if), + .icache_dreq_o (icache_dreq_cache_if_real), - .dcache_enable_i (dcache_en_csr_nbdcache), + .dcache_enable_i (1'b0), .dcache_flush_i (dcache_flush_ctrl_cache), .dcache_flush_ack_o(dcache_flush_ack_cache_ctrl), .dcache_miss_o (dcache_miss_cache_perf), @@ -1536,7 +1564,8 @@ module cva6 .noc_req_o (noc_req_o), .noc_resp_i(noc_resp_i) ); - assign inval_ready = 1'b1; + assign inval_ready = 1'b1; + assign miss_vld_bits = '0; end else begin : gen_cache_wb std_cache_subsystem #( // note: this only works with one cacheable region @@ -1566,11 +1595,11 @@ module cva6 // I$ .icache_en_i (icache_en_csr), .icache_flush_i (icache_flush_ctrl_cache), - .icache_miss_o (icache_miss_cache_perf), + .icache_miss_o (icache_miss_cache_perf_real), .icache_areq_i (icache_areq_ex_cache), - .icache_areq_o (icache_areq_cache_ex), + .icache_areq_o (icache_areq_cache_ex_real), .icache_dreq_i (icache_dreq_if_cache), - .icache_dreq_o (icache_dreq_cache_if), + .icache_dreq_o (icache_dreq_cache_if_real), // D$ .dcache_enable_i (dcache_en_csr_nbdcache), .dcache_flush_i (dcache_flush_ctrl_cache), @@ -1590,6 +1619,27 @@ module cva6 ); assign dcache_commit_wbuffer_not_ni = 1'b1; assign inval_ready = 1'b1; + assign miss_vld_bits = '0; + end + + if (CVA6Cfg.RVFI_DII) begin + rvfi_dii_generator #( + .CVA6Cfg(CVA6Cfg), + .icache_dreq_t(icache_dreq_t), + .icache_drsp_t(icache_drsp_t), + .exception_t(exception_t) + ) i_cva6_rvfi_dii_generator ( + .clk_i (clk_i), + .rst_ni(rst_ni), + .dreq_i(icache_dreq_if_cache), + .dreq_o(icache_dreq_cache_if) + ); + assign icache_miss_cache_perf = '0; + assign icache_areq_cache_ex = '0; + end else begin + assign icache_dreq_cache_if = icache_dreq_cache_if_real; + assign icache_miss_cache_perf = icache_miss_cache_perf_real; + assign icache_areq_cache_ex = icache_areq_cache_ex_real; end // ---------------- @@ -1861,7 +1911,7 @@ module cva6 logic [CVA6Cfg.NrCommitPorts-1:0][CVA6Cfg.XLEN-1:0] wdata_commit_rvfi; for (genvar i = 0; i < CVA6Cfg.NrCommitPorts; i++) begin - assign wdata_commit_rvfi[i] = wdata_commit_id[i][CVA6Cfg.XLEN-1:0]; + assign wdata_commit_rvfi[i] = reg_to_x(wdata_commit_id[i]); end cva6_rvfi_probes #( diff --git a/hw/vendor/cva6_cheri/core/cva6_fifo_v3.sv b/hw/vendor/cva6_cheri/core/cva6_fifo_v3.sv index 631ce226..434d272e 100644 --- a/hw/vendor/cva6_cheri/core/cva6_fifo_v3.sv +++ b/hw/vendor/cva6_cheri/core/cva6_fifo_v3.sv @@ -117,8 +117,10 @@ module cva6_fifo_v3 #( end if (pop_i && ~empty_o) begin - data_ft_n = data_i; - if (FPGA_EN && FPGA_ALTERA) first_word_n = first_word_q && push_i; + if (FPGA_EN && FPGA_ALTERA) begin + data_ft_n = data_i; + first_word_n = first_word_q && push_i; + end // read from the queue is a default assignment // but increment the read pointer... if (read_pointer_n == FifoDepth[ADDR_DEPTH-1:0] - 1) read_pointer_n = '0; @@ -138,7 +140,7 @@ module cva6_fifo_v3 #( first_word_n = '1; end if (pop_i) begin - first_word_n = '0; + if (FPGA_EN && FPGA_ALTERA) first_word_n = '0; status_cnt_n = status_cnt_q; read_pointer_n = read_pointer_q; write_pointer_n = write_pointer_q; diff --git a/hw/vendor/cva6_cheri/core/cva6_iti/instr_to_trace.sv b/hw/vendor/cva6_cheri/core/cva6_iti/instr_to_trace.sv deleted file mode 100644 index 6542c060..00000000 --- a/hw/vendor/cva6_cheri/core/cva6_iti/instr_to_trace.sv +++ /dev/null @@ -1,95 +0,0 @@ -// Copyright (c) 2025 Thales DIS design services SAS -// SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 -// Author: Maxime Colson - Thales -// Date: 20/03/2025 -// Contributors: -// Darshak Sheladiya, SYSGO GmbH -// Umberto Laghi, UNIBO - -//Systolic module used to determines the iaddr, ilastsize, iretire for Encoder Module - - -module instr_to_trace #( - parameter config_pkg::cva6_cfg_t CVA6Cfg = config_pkg::cva6_cfg_empty, - parameter type uop_entry_t = logic, - parameter type itt_out_t = logic, - parameter CAUSE_LEN = 5, //Size is ecause_width_p in the E-Trace SPEC - parameter ITYPE_LEN = 3, //Size is itype_width_p in the E-Trace SPEC (3 or 4) - parameter IRETIRE_LEN = 32 //Size is iretire_width_p in the E-Trace SPEC -) ( - input uop_entry_t uop_entry_i, - input logic [ CAUSE_LEN-1:0] cause_i, - input logic [CVA6Cfg.XLEN-1:0] tval_i, - input logic [ IRETIRE_LEN-1:0] counter_i, - input logic [CVA6Cfg.XLEN-1:0] iaddr_i, - input logic was_special_i, - - output itt_out_t itt_out_o, - output logic [ IRETIRE_LEN-1:0] counter_o, - output logic [CVA6Cfg.XLEN-1:0] iaddr_o, - output logic is_special_o -); - - logic special_inst; - logic exception; - logic interrupt; - assign special_inst = !(uop_entry_i.itype inside {iti_pkg::INT, iti_pkg::EXC, iti_pkg::STANDARD}) && uop_entry_i.valid ; - assign exception = (uop_entry_i.itype == iti_pkg::EXC) ? 1'b1 : 1'b0; - assign interrupt = (uop_entry_i.itype == iti_pkg::INT) ? 1'b1 : 1'b0; - - always_comb begin - counter_o = counter_i; - is_special_o = was_special_i; - iaddr_o = iaddr_i; - itt_out_o = '0; - - if (uop_entry_i.valid) begin - counter_o = uop_entry_i.compressed ? counter_i + 1 : counter_i + 2; - - if (was_special_i) begin - counter_o = 0; - iaddr_o = uop_entry_i.pc; - is_special_o = 1'b0; - end - - if (special_inst) begin - itt_out_o.valid = 1'b1; - itt_out_o.iretire = uop_entry_i.compressed ? counter_o + 1 : counter_o + 2; - itt_out_o.itype = uop_entry_i.itype; - itt_out_o.ilastsize = ~uop_entry_i.compressed; - itt_out_o.iaddr = iaddr_o; - itt_out_o.priv = uop_entry_i.priv; - itt_out_o.cycles = uop_entry_i.cycles; - itt_out_o.cause = '0; - itt_out_o.tval = '0; - is_special_o = 1'b1; - end - - if (interrupt) begin - itt_out_o.valid = 1'b1; - itt_out_o.iretire = uop_entry_i.compressed ? 1 : 2; - itt_out_o.itype = uop_entry_i.itype; - itt_out_o.ilastsize = ~uop_entry_i.compressed; - itt_out_o.iaddr = uop_entry_i.pc; - itt_out_o.priv = uop_entry_i.priv; - itt_out_o.cycles = uop_entry_i.cycles; - itt_out_o.cause = cause_i; - itt_out_o.tval = '0; - is_special_o = 1'b1; - end - - if (exception) begin - itt_out_o.valid = 1'b1; - itt_out_o.iretire = uop_entry_i.compressed ? 1 : 2; - itt_out_o.itype = uop_entry_i.itype; - itt_out_o.ilastsize = ~uop_entry_i.compressed; - itt_out_o.iaddr = uop_entry_i.pc; - itt_out_o.priv = uop_entry_i.priv; - itt_out_o.cycles = uop_entry_i.cycles; - itt_out_o.cause = cause_i; - itt_out_o.tval = tval_i; - is_special_o = 1'b1; - end - end - end -endmodule diff --git a/hw/vendor/cva6_cheri/core/cva6_iti/iti.sv b/hw/vendor/cva6_cheri/core/cva6_iti/iti.sv deleted file mode 100644 index ce45c619..00000000 --- a/hw/vendor/cva6_cheri/core/cva6_iti/iti.sv +++ /dev/null @@ -1,187 +0,0 @@ -// Copyright (c) 2025 Thales DIS design services SAS -// SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 -// Author: Maxime Colson - Thales -// Date: 20/03/2025 -// Contributors: -// Darshak Sheladiya, SYSGO GmbH -// Umberto Laghi, UNIBO - -// For reference : See Section 4.2 Instruction Trace Interface from Efficient Trace for RISC-V v2.0 (may 5 2022) -// iti stand for Instruction Trace Interface, changing because tip (Trace Ingress Port) and "type" are too similar creating confusion - -module cva6_iti #( - parameter config_pkg::cva6_cfg_t CVA6Cfg = config_pkg::cva6_cfg_empty, - parameter CAUSE_LEN = 5, //Size is ecause_width_p in the E-Trace SPEC - parameter ITYPE_LEN = 3, //Size is itype_width_p in the E-Trace SPEC (3 or 4) - parameter IRETIRE_LEN = 32, //Size is iretire_width_p in the E-Trace SPEC - parameter type rvfi_to_iti_t = logic, - parameter type iti_to_encoder_t = logic -) ( - input logic clk_i, - input logic rst_ni, - - input logic [CVA6Cfg.NrCommitPorts-1:0] valid_i, - input rvfi_to_iti_t rvfi_to_iti_i, - - output logic [CVA6Cfg.NrCommitPorts-1:0] valid_o, - output iti_to_encoder_t iti_to_encoder_o -); - - // pragma translate_off - int f; - initial begin - f = $fopen("iti.trace", "w"); - end - final $fclose(f); - // pragma translate_on - - /* Structure used for each instr*/ - localparam type uop_entry_t = struct packed { - logic valid; - logic [CVA6Cfg.XLEN-1:0] pc; - iti_pkg::itype_t itype; - logic compressed; - riscv::priv_lvl_t priv; - logic [63:0] cycles; - }; - - /* Structure used to output trace_signals if special instr */ - localparam type itt_out_t = struct packed { - logic valid; - logic [IRETIRE_LEN-1:0] iretire; - iti_pkg::itype_t itype; - logic ilastsize; - logic [CVA6Cfg.XLEN-1:0] iaddr; - riscv::priv_lvl_t priv; - logic [CAUSE_LEN-1:0] cause; - logic [CVA6Cfg.XLEN-1:0] tval; - logic [63:0] cycles; - }; - - logic interrupt; - iti_pkg::itype_t [CVA6Cfg.NrCommitPorts-1:0] itype; - - logic [IRETIRE_LEN-1:0] counter_d, counter_q; - logic [CVA6Cfg.XLEN-1:0] addr_d, addr_q; - logic special_d, special_q; - - uop_entry_t [CVA6Cfg.NrCommitPorts-1:0] uop_entry; - itt_out_t [CVA6Cfg.NrCommitPorts-1:0] itt_out; - - logic [CVA6Cfg.NrCommitPorts-1:0][IRETIRE_LEN-1:0] counter_itt; - logic [CVA6Cfg.NrCommitPorts-1:0][CVA6Cfg.XLEN-1:0] addr_itt; - logic [CVA6Cfg.NrCommitPorts-1:0] special_itt; - logic [CVA6Cfg.NrCommitPorts-1:0][CAUSE_LEN-1:0] cause_itt; - logic [CVA6Cfg.NrCommitPorts-1:0][CVA6Cfg.XLEN-1:0] tval_itt; - - logic [CVA6Cfg.NrCommitPorts-1:0][IRETIRE_LEN-1:0] counter; - logic [CVA6Cfg.NrCommitPorts-1:0][CVA6Cfg.XLEN-1:0] addr; - logic [CVA6Cfg.NrCommitPorts-1:0] special; - - - assign interrupt = rvfi_to_iti_i.cause[CVA6Cfg.XLEN-1]; // determined based on the MSB of cause - - for (genvar i = 0; i < CVA6Cfg.NrCommitPorts; i++) begin - itype_detector #( - .ITYPE_LEN(ITYPE_LEN) - ) i_itype_detector ( - .valid_i (valid_i[i]), - .exception_i (rvfi_to_iti_i.ex_valid), - .interrupt_i (interrupt), - .op_i (rvfi_to_iti_i.op[i]), - .branch_taken_i(rvfi_to_iti_i.is_taken[i]), - .itype_o (itype[i]) - ); - - // Adding this to ensure that interrupt/exception happen only in commit port 0 of cva6 - assign cause_itt[i] = i == 0 ? rvfi_to_iti_i.cause[CAUSE_LEN-1:0] : '0; - assign tval_itt[i] = i == 0 ? rvfi_to_iti_i.tval : '0; - // Systolic logic (First itt is connected to D Flip-Flop to continue computation if needed) - assign counter_itt[i] = i == 0 ? counter_q : counter[i-1]; - assign addr_itt[i] = i == 0 ? addr_q : addr[i-1]; - assign special_itt[i] = i == 0 ? special_q : special[i-1]; - - instr_to_trace #( - .CVA6Cfg(CVA6Cfg), - .uop_entry_t(uop_entry_t), - .itt_out_t(itt_out_t), - .CAUSE_LEN(CAUSE_LEN), - .ITYPE_LEN(ITYPE_LEN), - .IRETIRE_LEN(IRETIRE_LEN) - ) i_instr_to_trace ( - .uop_entry_i(uop_entry[i]), - .cause_i(cause_itt[i]), - .tval_i(tval_itt[i]), - .counter_i(counter_itt[i]), - .iaddr_i(addr_itt[i]), - .was_special_i(special_itt[i]), - .itt_out_o(itt_out[i]), - .counter_o(counter[i]), - .iaddr_o(addr[i]), - .is_special_o(special[i]) - ); - end - - - always_comb begin - iti_to_encoder_o.cause = '0; - iti_to_encoder_o.tval = '0; - for (int i = 0; i < CVA6Cfg.NrCommitPorts; i++) begin - uop_entry[i].valid = valid_i[i]; - uop_entry[i].pc = rvfi_to_iti_i.pc[i]; - uop_entry[i].itype = itype[i]; - uop_entry[i].compressed = rvfi_to_iti_i.is_compressed[i]; - uop_entry[i].priv = rvfi_to_iti_i.priv_lvl; - uop_entry[i].cycles = rvfi_to_iti_i.cycles; - - iti_to_encoder_o.valid[i] = 1'b0; - iti_to_encoder_o.iretire[i] = '0; - iti_to_encoder_o.ilastsize[i] = '0; - iti_to_encoder_o.itype[i] = '0; - iti_to_encoder_o.iaddr[i] = '0; - if (itt_out[i].valid) begin - valid_o[i] = itt_out[i].valid; - iti_to_encoder_o.valid[i] = itt_out[i].valid; - iti_to_encoder_o.iretire[i] = itt_out[i].iretire; - iti_to_encoder_o.ilastsize[i] = itt_out[i].ilastsize; - iti_to_encoder_o.itype[i] = itt_out[i].itype; - iti_to_encoder_o.iaddr[i] = itt_out[i].iaddr; - iti_to_encoder_o.priv = itt_out[i].priv; // privilege don't change between 2 instr committed in the same cycle - iti_to_encoder_o.cycles = itt_out[i].cycles; // Same here (same time at same cycle) - end - end - if (itt_out[0].valid) begin // interrupt & exception only in port 0 - iti_to_encoder_o.cause = itt_out[0].cause; - iti_to_encoder_o.tval = itt_out[0].tval; - end - end - - assign counter_d = counter[CVA6Cfg.NrCommitPorts-1]; - assign addr_d = addr[CVA6Cfg.NrCommitPorts-1]; - assign special_d = special[CVA6Cfg.NrCommitPorts-1]; - - always_ff @(posedge clk_i, negedge rst_ni) begin - if (!rst_ni) begin - counter_q <= '0; - addr_q <= '0; - special_q <= 1'b1; - end else begin - counter_q <= counter_d; - addr_q <= addr_d; - special_q <= special_d; - end - //pragma translate_off - for (int i = 0; i < CVA6Cfg.NrCommitPorts; i++) begin - if (itt_out[i].valid) begin - $fwrite( - f, - "i :%d , val = %d , iret = %d, ilast = 0x%d , itype = %d , cause = 0x%h , tval= 0x%h , priv = 0x%d , iadd= 0x%h \n", - i, itt_out[i].valid, itt_out[i].iretire, itt_out[i].ilastsize, itt_out[i].itype, - itt_out[i].cause, itt_out[i].tval, itt_out[i].priv, itt_out[i].iaddr); - end - end - //pragma translate_on - end - - -endmodule diff --git a/hw/vendor/cva6_cheri/core/cva6_iti/itype_detector.sv b/hw/vendor/cva6_cheri/core/cva6_iti/itype_detector.sv deleted file mode 100755 index 5c503ab2..00000000 --- a/hw/vendor/cva6_cheri/core/cva6_iti/itype_detector.sv +++ /dev/null @@ -1,92 +0,0 @@ -// Licensed under the Solderpad Hardware License v 2.1 (the “License”); you may not use this file -// except in compliance with the License, or, at your option, the Apache License version 2.0. You -// may obtain a copy of the License at - -// https://solderpad.org/licenses/SHL-2.1/ - -// Unless required by applicable law or agreed to in writing, any work distributed under the -// License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, -// either express or implied. See the License for the specific language governing permissions and -// limitations under the License. - -// Author: Umberto Laghi -// Contact: umberto.laghi@studio.unibo.it -// Github: @ubolakes -// Contributors: -// Darshak Sheladiya, SYSGO GmbH -// Maxime COLSON, Thales CDI France - -/* ITYPE DETECTOR */ -/* -it produces the type of the instruction -*/ - -module itype_detector #( - parameter ITYPE_LEN = 3 //Size is itype_width_p in the E-Trace SPEC (3 or 4) -) ( - input logic valid_i, - input logic exception_i, - input logic interrupt_i, - input ariane_pkg::fu_op op_i, - input logic branch_taken_i, - output iti_pkg::itype_t itype_o -); - - // internal signals - logic exception; - logic interrupt; - logic eret; - logic nontaken_branch; - logic taken_branch; - logic updiscon; - - // assignments - assign exception = exception_i; - assign interrupt = interrupt_i; // no need to have an inst committed - - - - - assign eret = op_i inside {ariane_pkg::MRET, ariane_pkg::SRET, ariane_pkg::DRET}; - - assign nontaken_branch = ( op_i == ariane_pkg::EQ || - op_i == ariane_pkg::NE || - op_i == ariane_pkg::LTS || - op_i == ariane_pkg::GES || - op_i == ariane_pkg::LTU || - op_i == ariane_pkg::GEU) && - ~branch_taken_i; - - assign taken_branch = ( op_i == ariane_pkg::EQ || - op_i == ariane_pkg::NE || - op_i == ariane_pkg::LTS || - op_i == ariane_pkg::GES || - op_i == ariane_pkg::LTU || - op_i == ariane_pkg::GEU) && - branch_taken_i; - - assign updiscon = op_i == ariane_pkg::JALR; - - // assigning the itype - always_comb begin - // initialization - itype_o = iti_pkg::STANDARD; - - if (exception) begin // exception - itype_o = iti_pkg::EXC; - end else if (interrupt) begin // interrupt - itype_o = iti_pkg::INT; - end else if (valid_i) begin - if (eret) begin // exception or interrupt return - itype_o = iti_pkg::ERET; - end else if (nontaken_branch) begin // nontaken branch - itype_o = iti_pkg::NON_TAKEN_BR; - end else if (taken_branch) begin // taken branch - itype_o = iti_pkg::TAKEN_BR; - end else if (ITYPE_LEN == 3 && updiscon) begin // uninferable discontinuity - itype_o = iti_pkg::UNINF_JMP; - end - end - end - -endmodule diff --git a/hw/vendor/cva6_cheri/core/cva6_mmu/cva6_mmu.sv b/hw/vendor/cva6_cheri/core/cva6_mmu/cva6_mmu.sv index ac300124..5243acdc 100644 --- a/hw/vendor/cva6_cheri/core/cva6_mmu/cva6_mmu.sv +++ b/hw/vendor/cva6_cheri/core/cva6_mmu/cva6_mmu.sv @@ -639,7 +639,8 @@ module cva6_mmu lsu_exception_o.gva = ld_st_v_i; end if (CVA6Cfg.CheriPresent) begin - if (cheri_cap_err) lsu_exception_o.tval2 = daccess_err ? 'd2 : 'd1; + if (cheri_cap_err) + lsu_exception_o.tval2 = CVA6Cfg.GPLEN'({daccess_err ? 2'd2 : 2'd1, 2'b0}); else lsu_exception_o.tval2 = '0; end end @@ -663,7 +664,8 @@ module cva6_mmu lsu_exception_o.gva = ld_st_v_i; end if (CVA6Cfg.CheriPresent) begin - if (cheri_cap_err) lsu_exception_o.tval2 = daccess_err ? 'd2 : 'd1; + if (cheri_cap_err) + lsu_exception_o.tval2 = CVA6Cfg.GPLEN'({daccess_err ? 2'd2 : 2'd1, 2'b0}); else lsu_exception_o.tval2 = '0; end end @@ -702,7 +704,7 @@ module cva6_mmu lsu_exception_o.gva = ld_st_v_i; end if (CVA6Cfg.CheriPresent) begin - lsu_exception_o.tval2 = {{CVA6Cfg.GPLEN - 2{1'b0}}, ptw_cheri_error}; + lsu_exception_o.tval2 = {{CVA6Cfg.GPLEN - 4{1'b0}}, ptw_cheri_error, 2'b0}; end end end else begin @@ -723,7 +725,7 @@ module cva6_mmu lsu_exception_o.gva = ld_st_v_i; end if (CVA6Cfg.CheriPresent) begin - lsu_exception_o.tval2 = {{CVA6Cfg.GPLEN - 2{1'b0}}, ptw_cheri_error}; + lsu_exception_o.tval2 = {{CVA6Cfg.GPLEN - 4{1'b0}}, ptw_cheri_error, 2'b0}; end end end diff --git a/hw/vendor/cva6_cheri/core/cva6_mmu/cva6_ptw.sv b/hw/vendor/cva6_cheri/core/cva6_mmu/cva6_ptw.sv index 1bc52011..c59a554f 100644 --- a/hw/vendor/cva6_cheri/core/cva6_mmu/cva6_ptw.sv +++ b/hw/vendor/cva6_cheri/core/cva6_mmu/cva6_ptw.sv @@ -162,6 +162,8 @@ module cva6_ptw assign req_port_o.data_wdata = '0; // we only issue one single request at a time assign req_port_o.data_id = '0; + // user field not used + assign req_port_o.data_wuser = '0; // ----------- // TLB Update @@ -305,6 +307,7 @@ module cva6_ptw ptw_lvl_n = ptw_lvl_q; ptw_pptr_n = ptw_pptr_q; state_d = state_q; + gpte_d = '0; ptw_stage_d = ptw_stage_q; global_mapping_n = global_mapping_q; // input registers @@ -575,6 +578,7 @@ module cva6_ptw pte.ppn, vaddr_lvl[HYP_EXT*2][ptw_lvl_q[0]], (CVA6Cfg.PtLevels)'(0) }; end + default: ; endcase end else ptw_pptr_n = {pte.ppn, vaddr_lvl[0][ptw_lvl_q[0]], (CVA6Cfg.PtLevels)'(0)}; diff --git a/hw/vendor/cva6_cheri/core/cva6_mmu/cva6_shared_tlb.sv b/hw/vendor/cva6_cheri/core/cva6_mmu/cva6_shared_tlb.sv index ca0f02fe..11913129 100644 --- a/hw/vendor/cva6_cheri/core/cva6_mmu/cva6_shared_tlb.sv +++ b/hw/vendor/cva6_cheri/core/cva6_mmu/cva6_shared_tlb.sv @@ -201,10 +201,11 @@ module cva6_shared_tlb #( endgenerate if (CVA6Cfg.RVH) //THIS UPDATES THE EXTRA BITS OF VPN IN SV39x4 - assign vpn_d[CVA6Cfg.PtLevels][(CVA6Cfg.VpnLen%CVA6Cfg.PtLevels)-1:0] = ((|v_st_enbl[1][HYP_EXT:0]) && itlb_access_i && ~itlb_hit_i && ~dtlb_access_i) ? // - itlb_vaddr_i[CVA6Cfg.VpnLen-1:CVA6Cfg.VpnLen-(CVA6Cfg.VpnLen%CVA6Cfg.PtLevels)] : // + assign vpn_d[CVA6Cfg.PtLevels] = ((|v_st_enbl[1][HYP_EXT:0]) && itlb_access_i && ~itlb_hit_i && ~dtlb_access_i) ? // + {{(((CVA6Cfg.VpnLen/CVA6Cfg.PtLevels)-(CVA6Cfg.VpnLen%CVA6Cfg.PtLevels))){1'b0}}, itlb_vaddr_i[CVA6Cfg.VpnLen-1:CVA6Cfg.VpnLen-(CVA6Cfg.VpnLen%CVA6Cfg.PtLevels)]} : // (((|v_st_enbl[0][HYP_EXT:0]) && dtlb_access_i && ~dtlb_hit_i) ? // - dtlb_vaddr_i[CVA6Cfg.VpnLen-1: CVA6Cfg.VpnLen-(CVA6Cfg.VpnLen%CVA6Cfg.PtLevels)] : vpn_q[CVA6Cfg.PtLevels][(CVA6Cfg.VpnLen%CVA6Cfg.PtLevels)-1:0]); + {{(((CVA6Cfg.VpnLen/CVA6Cfg.PtLevels)-(CVA6Cfg.VpnLen%CVA6Cfg.PtLevels))){1'b0}}, dtlb_vaddr_i[CVA6Cfg.VpnLen-1: CVA6Cfg.VpnLen-(CVA6Cfg.VpnLen%CVA6Cfg.PtLevels)]} : // + {{(((CVA6Cfg.VpnLen/CVA6Cfg.PtLevels)-(CVA6Cfg.VpnLen%CVA6Cfg.PtLevels))){1'b0}}, vpn_q[CVA6Cfg.PtLevels][(CVA6Cfg.VpnLen%CVA6Cfg.PtLevels)-1:0]}); /////////////////////////////////////////////////////// // tag comparison, hit generation @@ -344,7 +345,6 @@ module cva6_shared_tlb #( itlb_vpn_q <= '0; dtlb_vpn_q <= '0; tlb_update_asid_q <= '{default: 0}; - tlb_update_vmid_q <= '{default: 0}; shared_tlb_access_q <= '0; shared_tlb_vaddr_q <= '0; shared_tag_valid_q <= '0; @@ -366,7 +366,16 @@ module cva6_shared_tlb #( i_req_q <= i_req_d; shared_tag_valid <= shared_tag_valid_q[tag_rd_addr]; - if (CVA6Cfg.RVH) tlb_update_vmid_q <= tlb_update_vmid_d; + end + end + + if (CVA6Cfg.RVH) begin + always_ff @(posedge clk_i or negedge rst_ni) begin + if (~rst_ni) begin + tlb_update_vmid_q <= '{default: 0}; + end else begin + tlb_update_vmid_q <= tlb_update_vmid_d; + end end end @@ -403,7 +412,10 @@ module cva6_shared_tlb #( end if (CVA6Cfg.RVH) begin : gen_shared_tag_hyp //THIS UPDATES THE EXTRA BITS OF VPN IN SV39x4 - assign shared_tag_wr.vpn[CVA6Cfg.PtLevels][(CVA6Cfg.VpnLen%CVA6Cfg.PtLevels)-1:0] = shared_tlb_update_i.vpn[CVA6Cfg.VpnLen-1: CVA6Cfg.VpnLen-(CVA6Cfg.VpnLen%CVA6Cfg.PtLevels)]; + assign shared_tag_wr.vpn[CVA6Cfg.PtLevels] = { + {(((CVA6Cfg.VpnLen / CVA6Cfg.PtLevels) - (CVA6Cfg.VpnLen % CVA6Cfg.PtLevels))) {1'b0}}, + shared_tlb_update_i.vpn[CVA6Cfg.VpnLen-1:CVA6Cfg.VpnLen-(CVA6Cfg.VpnLen%CVA6Cfg.PtLevels)] + }; end endgenerate @@ -500,6 +512,8 @@ module cva6_shared_tlb #( ); assign pte[i][a] = pte_cva6_t'(pte_rd_data[i][a]); end + end else begin + assign shared_tag_rd[i] = '0; end end endmodule diff --git a/hw/vendor/cva6_cheri/core/cva6_rvfi.sv b/hw/vendor/cva6_cheri/core/cva6_rvfi.sv index 148a4664..3fc397fd 100644 --- a/hw/vendor/cva6_cheri/core/cva6_rvfi.sv +++ b/hw/vendor/cva6_cheri/core/cva6_rvfi.sv @@ -168,7 +168,7 @@ module cva6_rvfi assign branch_valid_iti = instr.branch_valid; assign is_taken_iti = instr.is_taken; assign tval_iti = instr.tval; - assign time_iti = rvfi_probes_i.csr.cycle_q; + assign time_iti = csr.cycle_q; assign priv_lvl = instr.priv_lvl; @@ -178,7 +178,7 @@ module cva6_rvfi assign lsu_addr = instr.lsu_ctrl_vaddr; assign lsu_rmask = (instr.lsu_ctrl_fu == LOAD || (((mem_q[lsu_addr_trans_id].instr & 32'hF800703F) == 32'h1000402F)) && instr.lsu_ctrl_fu == STORE) ? instr.lsu_ctrl_be : '0; - assign lsu_wmask = (instr.lsu_ctrl_fu == STORE && !((mem_q[lsu_addr_trans_id].instr & 32'hF800703F) == 32'h1000402F)) ? instr.lsu_ctrl_be : '0; + assign lsu_wmask = instr.lsu_ctrl_fu == STORE ? instr.lsu_ctrl_be : '0; assign lsu_addr_trans_id = instr.lsu_ctrl_trans_id; assign branch_trans_id = instr.branch_trans_id; @@ -366,14 +366,19 @@ module cva6_rvfi rvfi_instr_o[i].mem_addr <= mem_q[commit_pointer[i]].lsu_addr; // So far, only write paddr is reported. TODO: read paddr rvfi_instr_o[i].mem_paddr <= mem_paddr; - rvfi_instr_o[i].mem_wmask <= /* (mem_q[commit_pointer[i]].lsu_wmask == 16'hFFFF) ? '0 : */ (is_amo_sc( + rvfi_instr_o[i].mem_wmask <= (is_amo_sc( commit_instr_op[i] ) && wdata[i] == 1) ? '0 : - mem_q[commit_pointer[i]].lsu_wmask >> mem_q[commit_pointer[i]].lsu_addr[3:0]; + mem_q[commit_pointer[i]].lsu_wmask >> mem_q[commit_pointer[i]].lsu_addr[$clog2( + CVA6Cfg.CLEN/8 + )-1:0]; rvfi_instr_o[i].mem_wdata <= (is_amo_sc( commit_instr_op[i] ) && wdata[i] == 1) ? '0 : mem_q[commit_pointer[i]].lsu_wdata; - rvfi_instr_o[i].mem_rmask <= /* (mem_q[commit_pointer[i]].lsu_wmask == 16'hFFFF) ? '0 : */mem_q[commit_pointer[i]].lsu_rmask >> mem_q[commit_pointer[i]].lsu_addr[3:0]; + rvfi_instr_o[i].mem_rmask <= mem_q[commit_pointer[i]].lsu_rmask >> + mem_q[commit_pointer[i]].lsu_addr[$clog2( + CVA6Cfg.CLEN/8 + )-1:0]; rvfi_instr_o[i].mem_rdata <= commit_instr_result[i]; rvfi_instr_o[i].rs1_rdata <= mem_q[commit_pointer[i]].rs1_rdata; rvfi_instr_o[i].rs2_rdata <= (rs2_addr == 0) ? '0 : mem_q[commit_pointer[i]].rs2_rdata; @@ -395,113 +400,110 @@ module cva6_rvfi //---------------------------------------------------------------------------------------------------------- // CSR //---------------------------------------------------------------------------------------------------------- - - `define CONNECT_RVFI_FULL(CSR_ENABLE_COND, CSR_NAME, - CSR_SOURCE_NAME) \ - always_ff @(posedge clk_i) begin \ - if (CSR_ENABLE_COND) begin \ - rvfi_csr_o.``CSR_NAME``.rdata <= {{CVA6Cfg.XLEN - $bits(CSR_SOURCE_NAME)}, CSR_SOURCE_NAME}; \ - end \ - end \ - assign rvfi_csr_o.``CSR_NAME``.wdata = CSR_ENABLE_COND ? { {{CVA6Cfg.XLEN-$bits(CSR_SOURCE_NAME)}, CSR_SOURCE_NAME} } : 0; \ - assign rvfi_csr_o.``CSR_NAME``.rmask = CSR_ENABLE_COND ? 1 : 0; \ - assign rvfi_csr_o.``CSR_NAME``.wmask = (rvfi_csr_o.``CSR_NAME``.rdata != {{CVA6Cfg.XLEN - $bits(CSR_SOURCE_NAME)}, CSR_SOURCE_NAME}) && CSR_ENABLE_COND; + // Changing verible formating to fix vivado synthesis errors and warnings + // verilog_format: off + `define CONNECT_RVFI_FULL(CSR_ENABLE_COND, CSR_NAME, CSR_SOURCE_NAME) \ + always_ff @(posedge clk_i) begin \ + if (CSR_ENABLE_COND) begin \ + rvfi_csr_o.``CSR_NAME``.rdata <= {{CVA6Cfg.XLEN - $bits(CSR_SOURCE_NAME)}, CSR_SOURCE_NAME}; \ + end \ + end \ + assign rvfi_csr_o.``CSR_NAME``.wdata = CSR_ENABLE_COND ? { {{CVA6Cfg.XLEN-$bits(CSR_SOURCE_NAME)}, CSR_SOURCE_NAME} } : 0; \ + assign rvfi_csr_o.``CSR_NAME``.rmask = CSR_ENABLE_COND ? 1 : 0; \ + assign rvfi_csr_o.``CSR_NAME``.wmask = (rvfi_csr_o.``CSR_NAME``.rdata != {{CVA6Cfg.XLEN - $bits(CSR_SOURCE_NAME)}, CSR_SOURCE_NAME}) && CSR_ENABLE_COND; `define CONNECT_RVFI_SAME(CSR_ENABLE_COND, CSR_NAME) \ - `CONNECT_RVFI_FULL(CSR_ENABLE_COND, CSR_NAME, csr.``CSR_NAME``_q) + `CONNECT_RVFI_FULL(CSR_ENABLE_COND, CSR_NAME, csr.``CSR_NAME``_q) - `CONNECT_RVFI_FULL(CVA6Cfg.FpPresent, fflags, csr.fcsr_q.fflags) - `CONNECT_RVFI_FULL(CVA6Cfg.FpPresent, frm, csr.fcsr_q.frm) - `CONNECT_RVFI_FULL(CVA6Cfg.FpPresent, fcsr, {csr.fcsr_q.frm, csr.fcsr_q.fflags}) + if ($bits(rvfi_csr_o) != 1) begin + `CONNECT_RVFI_FULL(CVA6Cfg.FpPresent, fflags, csr.fcsr_q.fflags) + `CONNECT_RVFI_FULL(CVA6Cfg.FpPresent, frm, csr.fcsr_q.frm) + `CONNECT_RVFI_FULL(CVA6Cfg.FpPresent, fcsr, {csr.fcsr_q.frm, csr.fcsr_q.fflags}) - `CONNECT_RVFI_FULL(CVA6Cfg.FpPresent, ftran, csr.fcsr_q.fprec) - `CONNECT_RVFI_SAME(CVA6Cfg.FpPresent, dcsr) + `CONNECT_RVFI_FULL(CVA6Cfg.FpPresent, ftran, csr.fcsr_q.fprec) + `CONNECT_RVFI_SAME(CVA6Cfg.FpPresent, dcsr) - `CONNECT_RVFI_SAME(CVA6Cfg.DebugEn, dpc) + `CONNECT_RVFI_SAME(CVA6Cfg.DebugEn, dpc) - `CONNECT_RVFI_SAME(CVA6Cfg.DebugEn, dscratch0) - `CONNECT_RVFI_SAME(CVA6Cfg.DebugEn, dscratch1) + `CONNECT_RVFI_SAME(CVA6Cfg.DebugEn, dscratch0) + `CONNECT_RVFI_SAME(CVA6Cfg.DebugEn, dscratch1) - `CONNECT_RVFI_FULL(CVA6Cfg.RVS, sstatus, - csr.mstatus_extended & SMODE_STATUS_READ_MASK[CVA6Cfg.XLEN-1:0]) + `CONNECT_RVFI_FULL(CVA6Cfg.RVS, sstatus, csr.mstatus_extended & SMODE_STATUS_READ_MASK[CVA6Cfg.XLEN-1:0]) - `CONNECT_RVFI_FULL(CVA6Cfg.RVS, sie, csr.mie_q & csr.mideleg_q) - `CONNECT_RVFI_FULL(CVA6Cfg.RVS, sip, csr.mip_q & csr.mideleg_q) + `CONNECT_RVFI_FULL(CVA6Cfg.RVS, sie, csr.mie_q & csr.mideleg_q) + `CONNECT_RVFI_FULL(CVA6Cfg.RVS, sip, csr.mip_q & csr.mideleg_q) - `CONNECT_RVFI_SAME(CVA6Cfg.RVS, stvec) + `CONNECT_RVFI_SAME(CVA6Cfg.RVS, stvec) - `CONNECT_RVFI_SAME(CVA6Cfg.RVS, scounteren) + `CONNECT_RVFI_SAME(CVA6Cfg.RVS, scounteren) - `CONNECT_RVFI_SAME(CVA6Cfg.RVS, sscratch) - `CONNECT_RVFI_SAME(CVA6Cfg.RVS, sepc) + `CONNECT_RVFI_SAME(CVA6Cfg.RVS, sscratch) + `CONNECT_RVFI_SAME(CVA6Cfg.RVS, sepc) - `CONNECT_RVFI_SAME(CVA6Cfg.RVS, scause) + `CONNECT_RVFI_SAME(CVA6Cfg.RVS, scause) - `CONNECT_RVFI_SAME(CVA6Cfg.RVS, stval) - `CONNECT_RVFI_SAME(CVA6Cfg.RVS, satp) + `CONNECT_RVFI_SAME(CVA6Cfg.RVS, stval) + `CONNECT_RVFI_SAME(CVA6Cfg.RVS, satp) - `CONNECT_RVFI_FULL(1'b1, mstatus, csr.mstatus_extended) + `CONNECT_RVFI_FULL(1'b1, mstatus, csr.mstatus_extended) - bit [31:0] mstatush_q; - `CONNECT_RVFI_FULL(1'b1, mstatush, mstatush_q) + bit [31:0] mstatush_q; + `CONNECT_RVFI_FULL(1'b1, mstatush, mstatush_q) - `CONNECT_RVFI_FULL(1'b1, misa, IsaCode) + `CONNECT_RVFI_FULL(1'b1, misa, IsaCode) - `CONNECT_RVFI_SAME(CVA6Cfg.RVS, medeleg) - `CONNECT_RVFI_SAME(CVA6Cfg.RVS, mideleg) + `CONNECT_RVFI_SAME(CVA6Cfg.RVS, medeleg) + `CONNECT_RVFI_SAME(CVA6Cfg.RVS, mideleg) - `CONNECT_RVFI_SAME(1'b1, mie) - `CONNECT_RVFI_SAME(1'b1, mtvec) - `CONNECT_RVFI_SAME(1'b1, mcounteren) + `CONNECT_RVFI_SAME(1'b1, mie) + `CONNECT_RVFI_SAME(1'b1, mtvec) + `CONNECT_RVFI_SAME(1'b1, mcounteren) - `CONNECT_RVFI_SAME(1'b1, mscratch) + `CONNECT_RVFI_SAME(1'b1, mscratch) - `CONNECT_RVFI_SAME(1'b1, mepc) - `CONNECT_RVFI_SAME(1'b1, mcause) - `CONNECT_RVFI_SAME(1'b1, mtval) - `CONNECT_RVFI_SAME(1'b1, mip) + `CONNECT_RVFI_SAME(1'b1, mepc) + `CONNECT_RVFI_SAME(1'b1, mcause) + `CONNECT_RVFI_SAME(1'b1, mtval) + `CONNECT_RVFI_SAME(1'b1, mip) - `CONNECT_RVFI_FULL(1'b1, menvcfg, csr.fiom_q) + `CONNECT_RVFI_FULL(1'b1, menvcfg, csr.fiom_q) - `CONNECT_RVFI_FULL(CVA6Cfg.XLEN == 32, menvcfgh, 32'h0) + `CONNECT_RVFI_FULL(CVA6Cfg.XLEN == 32, menvcfgh, 32'h0) - `CONNECT_RVFI_FULL(1'b1, mvendorid, OPENHWGROUP_MVENDORID) - `CONNECT_RVFI_FULL(1'b1, marchid, ARIANE_MARCHID) - `CONNECT_RVFI_FULL(1'b1, mhartid, hart_id_i) + `CONNECT_RVFI_FULL(1'b1, mvendorid, OPENHWGROUP_MVENDORID) + `CONNECT_RVFI_FULL(1'b1, marchid, ARIANE_MARCHID) + `CONNECT_RVFI_FULL(1'b1, mhartid, hart_id_i) - `CONNECT_RVFI_SAME(1'b1, mcountinhibit) + `CONNECT_RVFI_SAME(1'b1, mcountinhibit) - `CONNECT_RVFI_FULL(1'b1, mcycle, csr.cycle_q[CVA6Cfg.XLEN-1:0]) - `CONNECT_RVFI_FULL(CVA6Cfg.XLEN == 32, mcycleh, csr.cycle_q[63:32]) + `CONNECT_RVFI_FULL(1'b1, mcycle, csr.cycle_q[CVA6Cfg.XLEN-1:0]) + `CONNECT_RVFI_FULL(CVA6Cfg.XLEN == 32, mcycleh, csr.cycle_q[63:32]) - `CONNECT_RVFI_FULL(1'b1, minstret, csr.instret_q[CVA6Cfg.XLEN-1:0]) - `CONNECT_RVFI_FULL(CVA6Cfg.XLEN == 32, minstreth, csr.instret_q[63:32]) + `CONNECT_RVFI_FULL(1'b1, minstret, csr.instret_q[CVA6Cfg.XLEN-1:0]) + `CONNECT_RVFI_FULL(CVA6Cfg.XLEN == 32, minstreth, csr.instret_q[63:32]) - `CONNECT_RVFI_FULL(1'b1, cycle, csr.cycle_q[CVA6Cfg.XLEN-1:0]) - `CONNECT_RVFI_FULL(CVA6Cfg.XLEN == 32, cycleh, csr.cycle_q[63:32]) + `CONNECT_RVFI_FULL(1'b1, cycle, csr.cycle_q[CVA6Cfg.XLEN-1:0]) + `CONNECT_RVFI_FULL(CVA6Cfg.XLEN == 32, cycleh, csr.cycle_q[63:32]) - `CONNECT_RVFI_FULL(1'b1, instret, csr.instret_q[CVA6Cfg.XLEN-1:0]) - `CONNECT_RVFI_FULL(CVA6Cfg.XLEN == 32, instreth, csr.instret_q[63:32]) + `CONNECT_RVFI_FULL(1'b1, instret, csr.instret_q[CVA6Cfg.XLEN-1:0]) + `CONNECT_RVFI_FULL(CVA6Cfg.XLEN == 32, instreth, csr.instret_q[63:32]) - `CONNECT_RVFI_SAME(1'b1, dcache) - `CONNECT_RVFI_SAME(1'b1, icache) + `CONNECT_RVFI_SAME(1'b1, dcache) + `CONNECT_RVFI_SAME(1'b1, icache) - `CONNECT_RVFI_SAME(CVA6Cfg.EnableAccelerator, acc_cons) - `CONNECT_RVFI_SAME(CVA6Cfg.RVZCMT, jvt) - `CONNECT_RVFI_FULL(1'b1, pmpcfg0, csr.pmpcfg_q[CVA6Cfg.XLEN/8-1:0]) - `CONNECT_RVFI_FULL(CVA6Cfg.XLEN == 32, pmpcfg1, csr.pmpcfg_q[7:4]) + `CONNECT_RVFI_SAME(CVA6Cfg.EnableAccelerator, acc_cons) + `CONNECT_RVFI_SAME(CVA6Cfg.RVZCMT, jvt) + `CONNECT_RVFI_FULL(1'b1, pmpcfg0, csr.pmpcfg_q[CVA6Cfg.XLEN/8-1:0]) + `CONNECT_RVFI_FULL(CVA6Cfg.XLEN == 32, pmpcfg1, csr.pmpcfg_q[7:4]) - `CONNECT_RVFI_FULL(1'b1, pmpcfg2, csr.pmpcfg_q[8+:CVA6Cfg.XLEN/8]) - `CONNECT_RVFI_FULL(CVA6Cfg.XLEN == 32, pmpcfg3, csr.pmpcfg_q[15:12]) + `CONNECT_RVFI_FULL(1'b1, pmpcfg2, csr.pmpcfg_q[8+:CVA6Cfg.XLEN/8]) + `CONNECT_RVFI_FULL(CVA6Cfg.XLEN == 32, pmpcfg3, csr.pmpcfg_q[15:12]) - bit [CVA6Cfg.XLEN-1:0] pmpaddr_q; - genvar i; - generate + bit [CVA6Cfg.XLEN-1:0] pmpaddr_q; + genvar i; for (i = 0; i < 16; i++) begin - `CONNECT_RVFI_FULL(1'b1, pmpaddr[i], { - csr.pmpaddr_q[i][CVA6Cfg.PLEN-3:1], pmpcfg_q[i].addr_mode[1]}) + `CONNECT_RVFI_FULL(1'b1, pmpaddr[i], {csr.pmpaddr_q[i][CVA6Cfg.PLEN-3:1], pmpcfg_q[i].addr_mode[1]}) end - endgenerate - ; - + end + // verilog_format: on endmodule diff --git a/hw/vendor/cva6_cheri/core/decoder.sv b/hw/vendor/cva6_cheri/core/decoder.sv index bc8d58c0..6e172ef1 100644 --- a/hw/vendor/cva6_cheri/core/decoder.sv +++ b/hw/vendor/cva6_cheri/core/decoder.sv @@ -96,6 +96,7 @@ module decoder output logic [31:0] orig_instr_o, // Is a control flow instruction - ISSUE_STAGE output logic is_control_flow_instr_o, + input debug_from_trigger_i, // The int_mode for the next instruction - FRONTEND output logic int_mode_o ); @@ -1313,7 +1314,7 @@ module decoder endcase if (!illegal_instr_cheri) instruction_o.fu = CLU; end - illegal_instr = illegal_instr_non_bm & illegal_instr_bm & illegal_instr_cheri; + illegal_instr = illegal_instr_non_bm & illegal_instr_bm & (!CVA6Cfg.CheriPresent || illegal_instr_cheri); end // -------------------------------- @@ -1367,7 +1368,7 @@ module decoder endcase if (!illegal_instr_cheri) instruction_o.fu = CLU; end - illegal_instr = illegal_instr_non_bm & illegal_instr_bm & illegal_instr_cheri; + illegal_instr = illegal_instr_non_bm & illegal_instr_bm & (!CVA6Cfg.CheriPresent || illegal_instr_cheri); end else illegal_instr = 1'b1; end // -------------------------------- @@ -2155,8 +2156,8 @@ module decoder end end - // a debug request has precedence over everything else - if (CVA6Cfg.DebugEn && debug_req_i && !debug_mode_i) begin + // a debug request has precendece over everything else + if ((CVA6Cfg.DebugEn && debug_req_i && !debug_mode_i) || (CVA6Cfg.SDTRIG && CVA6Cfg.Mcontrol6 && CVA6Cfg.DebugEn && !debug_mode_i && debug_from_trigger_i)) begin instruction_o.ex.valid = 1'b1; instruction_o.ex.cause = riscv::DEBUG_REQUEST; end diff --git a/hw/vendor/cva6_cheri/core/ex_stage.sv b/hw/vendor/cva6_cheri/core/ex_stage.sv index 10f96ada..e28699ef 100644 --- a/hw/vendor/cva6_cheri/core/ex_stage.sv +++ b/hw/vendor/cva6_cheri/core/ex_stage.sv @@ -49,6 +49,8 @@ module ex_stage input logic [CVA6Cfg.NrIssuePorts-1:0][CVA6Cfg.REGLEN-1:0] rs2_forwarding_i, // FU data useful to execute instruction - ISSUE_STAGE input fu_data_t [CVA6Cfg.NrIssuePorts-1:0] fu_data_i, + // ALU to ALU bypass control - ISSUE_STAGE + input alu_bypass_t alu_bypass_i, // PC of the current instruction - ISSUE_STAGE input logic [CVA6Cfg.PCLEN-1:0] pc_i, // DII ID of the current instruction - ISSUE_STAGE @@ -143,10 +145,12 @@ module ex_stage output logic fpu_valid_o, // FPU exception - ISSUE_STAGE output exception_t fpu_exception_o, + // FPU early valid - ISSUE_STAGE + output logic fpu_early_valid_o, // ALU2 instruction is valid - ISSUE_STAGE input logic [CVA6Cfg.NrIssuePorts-1:0] alu2_valid_i, // CLU instruction is ready - input logic clu_valid_i, + input logic [CVA6Cfg.NrIssuePorts-1:0] clu_valid_i, // CVXIF instruction is valid - ISSUE_STAGE input logic [CVA6Cfg.NrIssuePorts-1:0] x_valid_i, // CVXIF is ready - ISSUE_STAGE @@ -285,7 +289,8 @@ module ex_stage // from ALU to branch unit logic alu_branch_res; // branch comparison result - logic [CVA6Cfg.XLEN-1:0] alu_result, mult_result, aes_result; + logic [CVA6Cfg.NrALUs-1:0][CVA6Cfg.XLEN-1:0] alu_result; + logic [CVA6Cfg.XLEN-1:0] mult_result, aes_result; logic [CVA6Cfg.REGLEN-1:0] clu_result, csr_result; logic [CVA6Cfg.REGLEN-1:0] branch_result; logic lsu_ready, csr_ready, mult_ready; @@ -295,6 +300,8 @@ module ex_stage // Conditioning this on the result of the exception is likely to have timing problems. assign flu_exception_o = branch_exception; + fu_data_t [CVA6Cfg.NrALUs-1:0] alu_data; + logic [CVA6Cfg.NrIssuePorts-1:0] one_cycle_select; assign one_cycle_select = alu_valid_i | branch_valid_i | csr_valid_i | aes_valid_i | clu_valid_i; @@ -316,15 +323,27 @@ module ex_stage end end - // 1. ALU (combinatorial) - alu #( + // 1. ALU(s) (combinatorial) + assign alu_data[0] = one_cycle_data; + + if (CVA6Cfg.SuperscalarEn) begin : gen_alu2_data_sel + always_comb begin + unique case (1'b1) + alu2_valid_i[1]: alu_data[1] = fu_data_i[1]; + alu2_valid_i[0]: alu_data[1] = fu_data_i[0]; + default: alu_data[1] = '0; + endcase + end + end + + alu_wrapper #( .CVA6Cfg (CVA6Cfg), - .HasBranch(1'b1), .fu_data_t(fu_data_t) - ) alu_i ( + ) alu_wrapper_i ( .clk_i, .rst_ni, - .fu_data_i (one_cycle_data), + .alu_bypass_i (alu_bypass_i), + .fu_data_i (alu_data), .result_o (alu_result), .alu_branch_res_o(alu_branch_res) ); @@ -380,14 +399,14 @@ module ex_stage // Branch result as default case flu_result_o = branch_result; flu_trans_id_o = one_cycle_data.trans_id; - result = cva6_cheri_pkg::REG_NULL_CAP; + result = REG_NULL; // ALU result if (|alu_valid_i) begin if (CVA6Cfg.CheriPresent) begin - result = cva6_cheri_pkg::set_cap_reg_addr(result, alu_result); + result = cva6_cheri_pkg::set_cap_reg_addr(result, alu_result[0]); flu_result_o = result; end else begin - flu_result_o = {{(CVA6Cfg.REGLEN - CVA6Cfg.XLEN) {1'b0}}, alu_result}; + flu_result_o = {{(CVA6Cfg.REGLEN - CVA6Cfg.XLEN) {1'b0}}, alu_result[0]}; end // CSR result end else if (|csr_valid_i) begin @@ -402,7 +421,7 @@ module ex_stage flu_trans_id_o = mult_trans_id; end else if (|aes_valid_i) begin flu_result_o = aes_result; - end else if (clu_valid_i) begin + end else if (|clu_valid_i) begin flu_result_o = clu_result; end end @@ -447,8 +466,6 @@ module ex_stage logic [CVA6Cfg.TRANS_ID_BITS-1:0] fpu_trans_id; logic [CVA6Cfg.XLEN-1:0] fpu_result; logic [CVA6Cfg.XLEN-1:0] fpu_result_muxed; - logic alu2_valid; - logic [CVA6Cfg.XLEN-1:0] alu2_result; generate if (CVA6Cfg.FpPresent) begin : fpu_gen @@ -480,56 +497,30 @@ module ex_stage .fpu_trans_id_o(fpu_trans_id), .result_o(fpu_result), .fpu_valid_o(fpu_valid), - .fpu_exception_o + .fpu_exception_o, + .fpu_early_valid_o ); end else begin : no_fpu_gen - assign fpu_ready_o = '0; - assign fpu_trans_id = '0; - assign fpu_result = '0; - assign fpu_valid = '0; - assign fpu_exception_o = '0; + assign fpu_ready_o = '0; + assign fpu_trans_id = '0; + assign fpu_result = '0; + assign fpu_valid = '0; + assign fpu_exception_o = '0; + assign fpu_early_valid_o = '0; end endgenerate - // ---------------- - // ALU2 - // ---------------- - fu_data_t alu2_data; - if (CVA6Cfg.SuperscalarEn) begin : alu2_gen - always_comb begin - alu2_data = alu2_valid_i[0] ? fu_data_i[0] : '0; - if (alu2_valid_i[1]) begin - alu2_data = fu_data_i[1]; - end - end - - alu #( - .CVA6Cfg (CVA6Cfg), - .HasBranch(1'b0), - .fu_data_t(fu_data_t) - ) alu2_i ( - .clk_i, - .rst_ni, - .fu_data_i (alu2_data), - .result_o (alu2_result), - .alu_branch_res_o( /* this ALU does not handle branching */) - ); - end else begin - assign alu2_data = '0; - assign alu2_result = '0; - end - // result MUX // This is really explicit so that synthesis tools can elide unused signals if (CVA6Cfg.SuperscalarEn) begin if (CVA6Cfg.FpPresent) begin assign fpu_valid_o = fpu_valid || |alu2_valid_i; - assign fpu_result_muxed = fpu_valid ? fpu_result : alu2_result; - assign fpu_trans_id_o = fpu_valid ? fpu_trans_id : alu2_data.trans_id; + assign fpu_result_muxed = fpu_valid ? fpu_result : alu_result[1]; + assign fpu_trans_id_o = fpu_valid ? fpu_trans_id : alu_data[1].trans_id; end else begin assign fpu_valid_o = |alu2_valid_i; - assign fpu_result_muxed = alu2_result; - assign fpu_trans_id_o = alu2_data.trans_id; + assign fpu_result_muxed = alu_result[1]; + assign fpu_trans_id_o = alu_data[1].trans_id; end end else begin if (CVA6Cfg.FpPresent) begin @@ -542,13 +533,7 @@ module ex_stage assign fpu_trans_id_o = '0; end end - if (CVA6Cfg.CheriPresent) begin - assign fpu_result_o = cva6_cheri_pkg::set_cap_reg_addr( - cva6_cheri_pkg::REG_NULL_CAP, fpu_result_muxed - ); - end else begin - assign fpu_result_o = fpu_result_muxed; - end + assign fpu_result_o = x_to_reg(fpu_result_muxed); // ---------------- // Load-Store Unit @@ -689,16 +674,14 @@ module ex_stage assign x_exception_o = '0; assign x_result_o = '0; assign x_valid_o = '0; + assign x_we_o = '0; + assign x_rd_o = '0; end // ---------------- // CHERI Logic Unit // ---------------- if (CVA6Cfg.CheriPresent) begin : gen_cheri_unit - fu_data_t clu_data; - - assign clu_data = (clu_valid_i | branch_valid_i) ? fu_data_i : '0; - cheri_unit #( .CVA6Cfg(CVA6Cfg), .exception_t(exception_t), @@ -707,9 +690,9 @@ module ex_stage .clk_i, .rst_ni, .v_i, - .fu_data_i (clu_data), - .clu_valid_i (clu_valid_i), - .alu_result_i(alu_result), + .fu_data_i (one_cycle_data), + .clu_valid_i (|clu_valid_i), + .alu_result_i(alu_result[0]), .clu_result_o(clu_result) ); end diff --git a/hw/vendor/cva6_cheri/core/fpu_wrap.sv b/hw/vendor/cva6_cheri/core/fpu_wrap.sv index 75cab2b9..bae24129 100644 --- a/hw/vendor/cva6_cheri/core/fpu_wrap.sv +++ b/hw/vendor/cva6_cheri/core/fpu_wrap.sv @@ -34,7 +34,8 @@ module fpu_wrap output logic [CVA6Cfg.TRANS_ID_BITS-1:0] fpu_trans_id_o, output logic [ CVA6Cfg.FLen-1:0] result_o, output logic fpu_valid_o, - output exception_t fpu_exception_o + output exception_t fpu_exception_o, + output logic fpu_early_valid_o ); // this is a workaround @@ -553,7 +554,8 @@ module fpu_wrap .tag_o (fpu_trans_id_o), .out_valid_o (fpu_out_valid), .out_ready_i (fpu_out_ready), - .busy_o ( /* unused */) + .busy_o ( /* unused */), + .early_valid_o (fpu_early_valid_o) ); // Pack status flag into exception cause, tval ignored in wb, exception is always invalid diff --git a/hw/vendor/cva6_cheri/core/frontend/frontend.sv b/hw/vendor/cva6_cheri/core/frontend/frontend.sv index 665e2106..0c1bfe7d 100644 --- a/hw/vendor/cva6_cheri/core/frontend/frontend.sv +++ b/hw/vendor/cva6_cheri/core/frontend/frontend.sv @@ -205,7 +205,6 @@ module frontend assign bht_prediction_shifted[0] = (serving_unaligned) ? bht_q : bht_prediction[addr[0][1]]; assign btb_prediction_shifted[0] = (serving_unaligned) ? btb_q : btb_prediction[addr[0][1]]; end - ; // for the return address stack it doesn't matter as we have the // address of the call/return already diff --git a/hw/vendor/cva6_cheri/core/id_stage.sv b/hw/vendor/cva6_cheri/core/id_stage.sv index 409e903b..a38215c9 100644 --- a/hw/vendor/cva6_cheri/core/id_stage.sv +++ b/hw/vendor/cva6_cheri/core/id_stage.sv @@ -92,7 +92,9 @@ module id_stage #( input x_compressed_resp_t compressed_resp_i, output logic compressed_valid_o, output x_compressed_req_t compressed_req_o, - // Data cache request output - CACHE + // breakpoint request from trigger module + input debug_from_trigger_i, + // Data cache request ouput - CACHE input dcache_req_o_t dcache_req_ports_i, // Data cache request input - CACHE output dcache_req_i_t dcache_req_ports_o, @@ -159,15 +161,15 @@ module id_stage #( logic [CVA6Cfg.NrIssuePorts-1:0][31:0] instruction_deco; logic [CVA6Cfg.NrIssuePorts-1:0] is_compressed_deco; - logic [CVA6Cfg.NrIssuePorts-1:0] int_mode_decode_o; - logic [CVA6Cfg.NrIssuePorts-1:0] int_mode_decode; + logic [CVA6Cfg.NrIssuePorts-1:0] int_mode_prev; + logic [CVA6Cfg.NrIssuePorts-1:0] int_mode_next; logic int_mode_d; logic int_mode_q; logic commit_redirect_q; - assign int_mode_decode[0] = int_mode_q; + assign int_mode_prev[0] = int_mode_q; for (genvar i = 1; i < CVA6Cfg.NrIssuePorts; i++) begin - assign int_mode_decode[i] = int_mode_decode_o[i-1]; + assign int_mode_prev[i] = int_mode_next[i-1]; end if (CVA6Cfg.RVC) begin @@ -179,7 +181,7 @@ module id_stage #( .CVA6Cfg(CVA6Cfg) ) compressed_decoder_i ( .instr_i (fetch_entry_i[i].instruction), - .int_mode_i ((CVA6Cfg.CheriPresent) ? int_mode_decode[i] : 1'b0), + .int_mode_i ((CVA6Cfg.CheriPresent) ? int_mode_prev[i] : 1'b0), .instr_o (instruction_rvc[i]), .illegal_instr_o (is_illegal_rvc[i]), .is_compressed_o (is_compressed_rvc[i]), @@ -335,7 +337,7 @@ module id_stage #( .irq_i, .pc_i (fetch_entry_i[i].address), .dii_id_i (fetch_entry_i[i].dii_id), - .int_mode_i ((CVA6Cfg.CheriPresent) ? int_mode_decode[i] : 1'b0), + .int_mode_i ((CVA6Cfg.CheriPresent) ? int_mode_prev[i] : 1'b0), .is_compressed_i (is_compressed_deco[i]), .is_macro_instr_i (is_macro_instr[i]), .is_zcmt_i (is_zcmt_instr[i]), @@ -362,7 +364,8 @@ module id_stage #( .instruction_o (decoded_instruction[i]), .orig_instr_o (orig_instr[i]), .is_control_flow_instr_o (is_control_flow_instr[i]), - .int_mode_o (int_mode_decode_o[i]) + .debug_from_trigger_i (debug_from_trigger_i), + .int_mode_o (int_mode_next[i]) ); end @@ -473,8 +476,8 @@ module id_stage #( else if (mispredict_redirect_i) int_mode_d = int_mode_resolved_branch_i; else begin for (int i = 0; i < CVA6Cfg.NrIssuePorts; i++) begin - if (fetch_entry_ready_o[i]) begin - int_mode_d = int_mode_decode_o[i]; + if (fetch_entry_valid_i[i] && fetch_entry_ready_o[i]) begin + int_mode_d = int_mode_next[i]; end end end diff --git a/hw/vendor/cva6_cheri/core/include/ariane_pkg.sv b/hw/vendor/cva6_cheri/core/include/ariane_pkg.sv index a2ddc6e9..f903e383 100644 --- a/hw/vendor/cva6_cheri/core/include/ariane_pkg.sv +++ b/hw/vendor/cva6_cheri/core/include/ariane_pkg.sv @@ -29,7 +29,12 @@ /// moved out to favour a fully parameterizable core. package ariane_pkg; - localparam CLEN = cva6_config_pkg::CVA6ConfigRVZcheripurecap || cva6_config_pkg::CVA6ConfigRVZcherihybrid ? 2*cva6_config_pkg::CVA6ConfigXlen : cva6_config_pkg::CVA6ConfigXlen; + localparam XLEN = cva6_config_pkg::CVA6ConfigXlen; + localparam CheriPresent = cva6_config_pkg::CVA6ConfigRVZcheripurecap; + localparam CLEN = CheriPresent ? 2 * XLEN : XLEN; + localparam REGLEN = CheriPresent ? $bits(cva6_cheri_pkg::cap_reg_t) : XLEN; + localparam logic [REGLEN-1:0] REG_NULL = CheriPresent ? cva6_cheri_pkg::REG_NULL_CAP : '0; + localparam logic [REGLEN-1:0] REG_ROOT = CheriPresent ? cva6_cheri_pkg::REG_ROOT_CAP : '0; // TODO: Slowly move those parameters to the new system. localparam BITS_SATURATION_COUNTER = 2; @@ -56,6 +61,7 @@ package ariane_pkg; localparam logic [31:0] OPENHWGROUP_MVENDORID = 32'h0602; localparam logic [31:0] ARIANE_MARCHID = 32'd3; + localparam logic [31:0] ARIANE_MIMPID = 32'd0; // 32 registers localparam REG_ADDR_SIZE = 5; @@ -582,6 +588,11 @@ package ariane_pkg; SHA512SUM1 } fu_op; + typedef struct packed { + logic rs1_from_rd; + logic rs2_from_rd; + } alu_bypass_t; + function automatic logic op_is_branch(input fu_op op); unique case (op) inside EQ, NE, LTS, GES, LTU, GEU: return 1'b1; @@ -1029,4 +1040,22 @@ package ariane_pkg; return (n < 0) ? 0 : n; endfunction : avoid_neg + // Convert XLENs to registers (which are capabilities with CHERI) + function automatic logic [REGLEN-1:0] x_to_reg(logic [XLEN-1:0] x_val); + if (CheriPresent) begin + return cva6_cheri_pkg::set_cap_reg_addr(REG_NULL, x_val); + end else begin + return x_val; + end + endfunction + + // Convert registers (which are capabilities with CHERI) to XLENs + function automatic logic [XLEN-1:0] reg_to_x(logic [REGLEN-1:0] reg_val); + if (CheriPresent) begin + return reg_val[XLEN-1:0]; + end else begin + return reg_val; + end + endfunction + endpackage diff --git a/hw/vendor/cva6_cheri/core/include/build_config_pkg.sv b/hw/vendor/cva6_cheri/core/include/build_config_pkg.sv index 463ea59c..beb54439 100644 --- a/hw/vendor/cva6_cheri/core/include/build_config_pkg.sv +++ b/hw/vendor/cva6_cheri/core/include/build_config_pkg.sv @@ -4,7 +4,7 @@ package build_config_pkg; bit IS_XLEN32 = (CVA6Cfg.XLEN == 32) ? 1'b1 : 1'b0; bit IS_XLEN64 = (CVA6Cfg.XLEN == 32) ? 1'b0 : 1'b1; bit FpPresent = CVA6Cfg.RVF | CVA6Cfg.RVD | CVA6Cfg.XF16 | CVA6Cfg.XF16ALT | CVA6Cfg.XF8; - bit CheriPresent = CVA6Cfg.RVZcheripurecap | CVA6Cfg.RVZcherihybrid; + bit CheriPresent = CVA6Cfg.RVZcheripurecap; bit NSX = CVA6Cfg.XF16 | CVA6Cfg.XF16ALT | CVA6Cfg.XF8 | CVA6Cfg.XFVec; // Are non-standard extensions present? int unsigned FLen = CVA6Cfg.RVD ? 64 : // D ext. CVA6Cfg.RVF ? 32 : // F ext. @@ -38,7 +38,7 @@ package build_config_pkg; cfg.PLEN = (CVA6Cfg.XLEN == 32) ? 34 : 56; cfg.GPLEN = (CVA6Cfg.XLEN == 32) ? 34 : 41; cfg.REGLEN = CheriPresent ? $bits(cva6_cheri_pkg::cap_reg_t) : cfg.XLEN; - cfg.PCLEN = CheriPresent ? $bits(cva6_cheri_pkg::cap_pcc_t) : cfg.VLEN; + cfg.PCLEN = CheriPresent ? $bits(cva6_cheri_pkg::cap_reg_t) : cfg.VLEN; cfg.IS_XLEN32 = IS_XLEN32; cfg.IS_XLEN64 = IS_XLEN64; cfg.XLEN_ALIGN_BYTES = $clog2(CVA6Cfg.XLEN / 8); @@ -55,6 +55,9 @@ package build_config_pkg; cfg.NrIssuePorts = unsigned'(CVA6Cfg.SuperscalarEn ? 2 : 1); cfg.SpeculativeSb = CVA6Cfg.SuperscalarEn; + cfg.NrALUs = CVA6Cfg.SuperscalarEn ? unsigned'(2) : unsigned'(1); + cfg.ALUBypass = CVA6Cfg.SuperscalarEn ? bit'(CVA6Cfg.ALUBypass) : bit'(0); + cfg.NrLoadPipeRegs = CVA6Cfg.NrLoadPipeRegs; cfg.NrStorePipeRegs = CVA6Cfg.NrStorePipeRegs; cfg.AxiAddrWidth = CVA6Cfg.AxiAddrWidth; @@ -134,6 +137,11 @@ package build_config_pkg; cfg.CachedRegionLength = CVA6Cfg.CachedRegionLength; cfg.MaxOutstandingStores = CVA6Cfg.MaxOutstandingStores; cfg.DebugEn = CVA6Cfg.DebugEn; + cfg.SDTRIG = CVA6Cfg.SDTRIG; + cfg.Mcontrol6 = CVA6Cfg.Mcontrol6; + cfg.Icount = CVA6Cfg.Icount; + cfg.Etrigger = CVA6Cfg.Etrigger; + cfg.Itrigger = CVA6Cfg.Itrigger; cfg.NonIdemPotenceEn = (CVA6Cfg.NrNonIdempotentRules > 0) && (CVA6Cfg.NonIdempotentLength > 0); cfg.AxiBurstWriteEn = CVA6Cfg.AxiBurstWriteEn; @@ -165,7 +173,7 @@ package build_config_pkg; cfg.CheriCapTagWidth = CVA6Cfg.CheriCapTagWidth; cfg.RVFI_DII = bit'(CVA6Cfg.RVFI_DII); - cfg.DIIIDLEN = CVA6Cfg.DIIIDLEN; + cfg.DIIIDLEN = 6; cfg.DATA_USER_EN = CVA6Cfg.DataUserEn; cfg.WtDcacheWbufDepth = CVA6Cfg.WtDcacheWbufDepth; diff --git a/hw/vendor/cva6_cheri/core/include/config_pkg.sv b/hw/vendor/cva6_cheri/core/include/config_pkg.sv index 17299549..bc26cbfa 100644 --- a/hw/vendor/cva6_cheri/core/include/config_pkg.sv +++ b/hw/vendor/cva6_cheri/core/include/config_pkg.sv @@ -122,6 +122,12 @@ package config_pkg; logic [63:0] HaltAddress; // Address to jump when exception logic [63:0] ExceptionAddress; + // Trigger Module Sdtrig Extension + bit SDTRIG; + bit Mcontrol6; + bit Icount; + bit Etrigger; + bit Itrigger; // Tval Support Enable bit TvalEn; // MTVEC CSR supports only direct mode @@ -208,6 +214,8 @@ package config_pkg; bit TechnoCut; // Enable superscalar* with 2 issue ports and 2 commit ports. bit SuperscalarEn; + // Enable ALU-ALU bypass (superscalar mode only) + bit ALUBypass; // Number of commit ports. Forced to 2 if SuperscalarEn. int unsigned NrCommitPorts; // Load cycle latency number @@ -242,8 +250,6 @@ package config_pkg; int unsigned CheriCapTagWidth; // Enable RVDI_DII interface int unsigned RVFI_DII; - // DII ID Length - int unsigned DIIIDLEN; } cva6_user_cfg_t; typedef struct packed { @@ -270,6 +276,9 @@ package config_pkg; int unsigned NrIssuePorts; bit SpeculativeSb; + int unsigned NrALUs; + bit ALUBypass; + int unsigned NrLoadPipeRegs; int unsigned NrStorePipeRegs; /// AXI parameters. @@ -356,6 +365,11 @@ package config_pkg; logic [NrMaxRules-1:0][63:0] CachedRegionLength; int unsigned MaxOutstandingStores; bit DebugEn; + bit SDTRIG; + bit Mcontrol6; + bit Icount; + bit Etrigger; + bit Itrigger; bit NonIdemPotenceEn; // Currently only used by V extension (Ara) bit AxiBurstWriteEn; @@ -434,7 +448,6 @@ package config_pkg; assert (Cfg.NrExecuteRegionRules <= NrMaxRules); assert (Cfg.NrCachedRegionRules <= NrMaxRules); assert (Cfg.NrPMPEntries <= 64); - assert (!(Cfg.SuperscalarEn && Cfg.RVF)); assert (Cfg.FETCH_WIDTH == 32 || Cfg.FETCH_WIDTH == 64) else $fatal(1, "[frontend] fetch width != not supported"); // Support for disabling MIP.MSIP and MIE.MSIE in Hypervisor and Supervisor mode is not supported @@ -442,6 +455,8 @@ package config_pkg; assert (!(Cfg.RVS && !Cfg.SoftwareInterruptEn)); assert (!(Cfg.RVH && !Cfg.SoftwareInterruptEn)); assert (!(Cfg.RVZCMT && ~Cfg.MmuPresent)); + // Can only have CHERI hybrid support if CHERI purecap is supported + assert (!(!Cfg.RVZcheripurecap && Cfg.RVZcherihybrid)); // pragma translate_on endfunction diff --git a/hw/vendor/cva6_cheri/core/include/cv32a60x_config_pkg.sv b/hw/vendor/cva6_cheri/core/include/cv32a60x_config_pkg.sv index 75898373..6bc06746 100644 --- a/hw/vendor/cva6_cheri/core/include/cv32a60x_config_pkg.sv +++ b/hw/vendor/cva6_cheri/core/include/cv32a60x_config_pkg.sv @@ -25,6 +25,7 @@ package cva6_config_pkg; FpgaAlteraEn: bit'(0), TechnoCut: bit'(1), SuperscalarEn: bit'(0), + ALUBypass: bit'(0), NrCommitPorts: unsigned'(1), AxiAddrWidth: unsigned'(CVA6ConfigAxiAddrWidth), AxiDataWidth: unsigned'(CVA6ConfigAxiDataWidth), @@ -50,10 +51,10 @@ package cva6_config_pkg; CvxifEn: bit'(1), CoproType: config_pkg::COPRO_EXAMPLE, RVZiCond: bit'(0), - RVZcheripurecap: bit'(0), - RVZcherihybrid: bit'(0), RVZicntr: bit'(0), RVZihpm: bit'(0), + RVZcheripurecap: bit'(0), + RVZcherihybrid: bit'(0), NrScoreboardEntries: unsigned'(4), PerfCounterEn: bit'(0), MmuPresent: bit'(0), @@ -87,6 +88,11 @@ package cva6_config_pkg; CachedRegionLength: 1024'({64'h40000000}), MaxOutstandingStores: unsigned'(7), DebugEn: bit'(0), + SDTRIG: bit'(0), + Mcontrol6: bit'(0), + Icount: bit'(0), + Etrigger: bit'(0), + Itrigger: bit'(0), AxiBurstWriteEn: bit'(0), IcacheByteSize: unsigned'(2048), IcacheSetAssoc: unsigned'(2), diff --git a/hw/vendor/cva6_cheri/core/include/cv32a65x_config_pkg.sv b/hw/vendor/cva6_cheri/core/include/cv32a65x_config_pkg.sv index a42295ff..417c24d2 100644 --- a/hw/vendor/cva6_cheri/core/include/cv32a65x_config_pkg.sv +++ b/hw/vendor/cva6_cheri/core/include/cv32a65x_config_pkg.sv @@ -28,6 +28,7 @@ package cva6_config_pkg; FpgaAlteraEn: bit'(0), TechnoCut: bit'(1), SuperscalarEn: bit'(1), + ALUBypass: bit'(1), NrCommitPorts: unsigned'(1), AxiAddrWidth: unsigned'(CVA6ConfigAxiAddrWidth), AxiDataWidth: unsigned'(CVA6ConfigAxiDataWidth), @@ -55,6 +56,8 @@ package cva6_config_pkg; RVZiCond: bit'(0), RVZicntr: bit'(0), RVZihpm: bit'(0), + RVZcheripurecap: bit'(0), + RVZcherihybrid: bit'(0), NrScoreboardEntries: unsigned'(8), PerfCounterEn: bit'(0), MmuPresent: bit'(0), @@ -88,6 +91,11 @@ package cva6_config_pkg; CachedRegionLength: 1024'({64'h40000000}), MaxOutstandingStores: unsigned'(7), DebugEn: bit'(0), + SDTRIG: bit'(0), + Mcontrol6: bit'(0), + Icount: bit'(0), + Etrigger: bit'(0), + Itrigger: bit'(0), AxiBurstWriteEn: bit'(0), IcacheByteSize: unsigned'(2048), IcacheSetAssoc: unsigned'(2), diff --git a/hw/vendor/cva6_cheri/core/include/cv32a6_ima_sv32_fpga_config_pkg.sv b/hw/vendor/cva6_cheri/core/include/cv32a6_ima_sv32_fpga_config_pkg.sv index 25d1082f..b178557e 100644 --- a/hw/vendor/cva6_cheri/core/include/cv32a6_ima_sv32_fpga_config_pkg.sv +++ b/hw/vendor/cva6_cheri/core/include/cv32a6_ima_sv32_fpga_config_pkg.sv @@ -80,6 +80,7 @@ package cva6_config_pkg; FpgaAlteraEn: bit'(0), // for Altera (only) TechnoCut: bit'(0), SuperscalarEn: bit'(0), + ALUBypass: bit'(0), NrCommitPorts: unsigned'(1), AxiAddrWidth: unsigned'(CVA6ConfigAxiAddrWidth), AxiDataWidth: unsigned'(CVA6ConfigAxiDataWidth), @@ -142,6 +143,11 @@ package cva6_config_pkg; CachedRegionLength: 1024'({64'h40000000}), MaxOutstandingStores: unsigned'(7), DebugEn: bit'(1), + SDTRIG: bit'(0), + Mcontrol6: bit'(0), + Icount: bit'(0), + Etrigger: bit'(0), + Itrigger: bit'(0), AxiBurstWriteEn: bit'(0), IcacheByteSize: unsigned'(CVA6ConfigIcacheByteSize), IcacheSetAssoc: unsigned'(CVA6ConfigIcacheSetAssoc), diff --git a/hw/vendor/cva6_cheri/core/include/cv32a6_imac_sv32_config_pkg.sv b/hw/vendor/cva6_cheri/core/include/cv32a6_imac_sv32_config_pkg.sv index 39695fbf..8b8be8d5 100644 --- a/hw/vendor/cva6_cheri/core/include/cv32a6_imac_sv32_config_pkg.sv +++ b/hw/vendor/cva6_cheri/core/include/cv32a6_imac_sv32_config_pkg.sv @@ -79,6 +79,7 @@ package cva6_config_pkg; FpgaAlteraEn: bit'(0), // for Altera (only) TechnoCut: bit'(0), SuperscalarEn: bit'(0), + ALUBypass: bit'(0), NrCommitPorts: unsigned'(2), AxiAddrWidth: unsigned'(CVA6ConfigAxiAddrWidth), AxiDataWidth: unsigned'(CVA6ConfigAxiDataWidth), @@ -141,6 +142,11 @@ package cva6_config_pkg; CachedRegionLength: 1024'({64'h40000000}), MaxOutstandingStores: unsigned'(7), DebugEn: bit'(1), + SDTRIG: bit'(0), + Mcontrol6: bit'(0), + Icount: bit'(0), + Etrigger: bit'(0), + Itrigger: bit'(0), AxiBurstWriteEn: bit'(0), IcacheByteSize: unsigned'(CVA6ConfigIcacheByteSize), IcacheSetAssoc: unsigned'(CVA6ConfigIcacheSetAssoc), diff --git a/hw/vendor/cva6_cheri/core/include/cv64a60ax_config_pkg.sv b/hw/vendor/cva6_cheri/core/include/cv64a60ax_config_pkg.sv index dc14b0f6..7c419307 100644 --- a/hw/vendor/cva6_cheri/core/include/cv64a60ax_config_pkg.sv +++ b/hw/vendor/cva6_cheri/core/include/cv64a60ax_config_pkg.sv @@ -55,6 +55,7 @@ package cva6_config_pkg; FpgaAlteraEn: bit'(0), // for Altera (only) TechnoCut: bit'(0), SuperscalarEn: bit'(0), + ALUBypass: bit'(0), NrCommitPorts: unsigned'(2), AxiAddrWidth: unsigned'(CVA6ConfigAxiAddrWidth), AxiDataWidth: unsigned'(CVA6ConfigAxiDataWidth), @@ -82,6 +83,8 @@ package cva6_config_pkg; RVZiCond: bit'(1), RVZicntr: bit'(1), RVZihpm: bit'(1), + RVZcheripurecap: bit'(0), + RVZcherihybrid: bit'(0), NrScoreboardEntries: unsigned'(8), PerfCounterEn: bit'(1), MmuPresent: bit'(1), @@ -121,7 +124,12 @@ package cva6_config_pkg; CachedRegionLength: 1024'({64'h2_0000_0000, 64'h1_0000}), MaxOutstandingStores: unsigned'(7), DebugEn: bit'(1), - AxiBurstWriteEn: bit'(1), + SDTRIG: bit'(0), + Mcontrol6: bit'(0), + Icount: bit'(0), + Etrigger: bit'(0), + Itrigger: bit'(0), + AxiBurstWriteEn: bit'(0), IcacheByteSize: unsigned'(32768), IcacheSetAssoc: unsigned'(8), IcacheLineWidth: unsigned'(512), @@ -141,7 +149,9 @@ package cva6_config_pkg; SharedTlbDepth: int'(64), NrLoadPipeRegs: int'(0), NrStorePipeRegs: int'(0), - DcacheIdWidth: int'(3) + DcacheIdWidth: int'(3), + CheriCapTagWidth : int'(1), + RVFI_DII: int'(0) }; endpackage diff --git a/hw/vendor/cva6_cheri/core/include/cv64a6_imafdc_sv39_config_pkg.sv b/hw/vendor/cva6_cheri/core/include/cv64a6_imafdc_sv39_config_pkg.sv index 8ff7146b..3cb2ac1f 100644 --- a/hw/vendor/cva6_cheri/core/include/cv64a6_imafdc_sv39_config_pkg.sv +++ b/hw/vendor/cva6_cheri/core/include/cv64a6_imafdc_sv39_config_pkg.sv @@ -28,8 +28,8 @@ package cva6_config_pkg; localparam CVA6ConfigBExtEn = 1; localparam CVA6ConfigVExtEn = 0; localparam CVA6ConfigRVZiCond = 1; - localparam CVA6ConfigRVZcheripurecap = 1; - localparam CVA6ConfigRVZcherihybrid = 1; + localparam CVA6ConfigRVZcheripurecap = 0; + localparam CVA6ConfigRVZcherihybrid = 0; localparam CVA6ConfigCheriCapTagWidth = 1; localparam CVA6ConfigRVFI_DII = 0; @@ -86,6 +86,7 @@ package cva6_config_pkg; FpgaAlteraEn: bit'(0), // for Altera (only) TechnoCut: bit'(0), SuperscalarEn: bit'(0), + ALUBypass: bit'(0), NrCommitPorts: unsigned'(2), AxiAddrWidth: unsigned'(CVA6ConfigAxiAddrWidth), AxiDataWidth: unsigned'(CVA6ConfigAxiDataWidth), @@ -148,6 +149,11 @@ package cva6_config_pkg; CachedRegionLength: 1024'({64'h40000000}), MaxOutstandingStores: unsigned'(7), DebugEn: bit'(1), + SDTRIG: bit'(0), + Mcontrol6: bit'(0), + Icount: bit'(0), + Etrigger: bit'(0), + Itrigger: bit'(0), AxiBurstWriteEn: bit'(1), IcacheByteSize: unsigned'(CVA6ConfigIcacheByteSize), IcacheSetAssoc: unsigned'(CVA6ConfigIcacheSetAssoc), diff --git a/hw/vendor/cva6_cheri/core/include/cv64a6_imafdc_sv39_hpdcache_config_pkg.sv b/hw/vendor/cva6_cheri/core/include/cv64a6_imafdc_sv39_hpdcache_config_pkg.sv index b6516830..f951007e 100644 --- a/hw/vendor/cva6_cheri/core/include/cv64a6_imafdc_sv39_hpdcache_config_pkg.sv +++ b/hw/vendor/cva6_cheri/core/include/cv64a6_imafdc_sv39_hpdcache_config_pkg.sv @@ -90,6 +90,7 @@ package cva6_config_pkg; FpgaAlteraEn: bit'(0), // for Altera (only) TechnoCut: bit'(0), SuperscalarEn: bit'(0), + ALUBypass: bit'(0), NrCommitPorts: unsigned'(2), AxiAddrWidth: unsigned'(CVA6ConfigAxiAddrWidth), AxiDataWidth: unsigned'(CVA6ConfigAxiDataWidth), @@ -152,6 +153,11 @@ package cva6_config_pkg; CachedRegionLength: 1024'({64'h40000000}), MaxOutstandingStores: unsigned'(7), DebugEn: bit'(1), + SDTRIG: bit'(0), + Mcontrol6: bit'(0), + Icount: bit'(0), + Etrigger: bit'(0), + Itrigger: bit'(0), AxiBurstWriteEn: bit'(0), IcacheByteSize: unsigned'(CVA6ConfigIcacheByteSize), IcacheSetAssoc: unsigned'(CVA6ConfigIcacheSetAssoc), diff --git a/hw/vendor/cva6_cheri/core/include/cv64a6_imafdc_sv39_hpdcache_wb_config_pkg.sv b/hw/vendor/cva6_cheri/core/include/cv64a6_imafdc_sv39_hpdcache_wb_config_pkg.sv index 385ef904..9eaf6d49 100644 --- a/hw/vendor/cva6_cheri/core/include/cv64a6_imafdc_sv39_hpdcache_wb_config_pkg.sv +++ b/hw/vendor/cva6_cheri/core/include/cv64a6_imafdc_sv39_hpdcache_wb_config_pkg.sv @@ -35,6 +35,8 @@ package cva6_config_pkg; localparam CVA6ConfigVExtEn = 0; localparam CVA6ConfigHExtEn = 0; localparam CVA6ConfigRVZiCond = 1; + localparam CVA6ConfigRVZcheripurecap = 0; + localparam CVA6ConfigRVZcherihybrid = 0; localparam CVA6ConfigAxiIdWidth = 4; localparam CVA6ConfigAxiAddrWidth = 64; @@ -88,6 +90,7 @@ package cva6_config_pkg; FpgaAlteraEn: bit'(0), // for Altera (only) TechnoCut: bit'(0), SuperscalarEn: bit'(0), + ALUBypass: bit'(0), NrCommitPorts: unsigned'(2), AxiAddrWidth: unsigned'(CVA6ConfigAxiAddrWidth), AxiDataWidth: unsigned'(CVA6ConfigAxiDataWidth), @@ -115,6 +118,8 @@ package cva6_config_pkg; RVZiCond: bit'(CVA6ConfigRVZiCond), RVZicntr: bit'(1), RVZihpm: bit'(1), + RVZcheripurecap: bit'(0), + RVZcherihybrid: bit'(0), NrScoreboardEntries: unsigned'(CVA6ConfigNrScoreboardEntries), PerfCounterEn: bit'(CVA6ConfigPerfCounterEn), MmuPresent: bit'(CVA6ConfigMmuPresent), @@ -148,6 +153,11 @@ package cva6_config_pkg; CachedRegionLength: 1024'({64'h40000000}), MaxOutstandingStores: unsigned'(7), DebugEn: bit'(1), + SDTRIG: bit'(0), + Mcontrol6: bit'(0), + Icount: bit'(0), + Etrigger: bit'(0), + Itrigger: bit'(0), AxiBurstWriteEn: bit'(0), IcacheByteSize: unsigned'(CVA6ConfigIcacheByteSize), IcacheSetAssoc: unsigned'(CVA6ConfigIcacheSetAssoc), @@ -168,7 +178,9 @@ package cva6_config_pkg; SharedTlbDepth: int'(64), NrLoadPipeRegs: int'(CVA6ConfigNrLoadPipeRegs), NrStorePipeRegs: int'(CVA6ConfigNrStorePipeRegs), - DcacheIdWidth: int'(CVA6ConfigDcacheIdWidth) + DcacheIdWidth: int'(CVA6ConfigDcacheIdWidth), + CheriCapTagWidth: int'(1), + RVFI_DII: int'(0) }; endpackage diff --git a/hw/vendor/cva6_cheri/core/include/cv64a6_imafdc_sv39_hpdcache_wb_rvfi_dii_config_pkg.sv b/hw/vendor/cva6_cheri/core/include/cv64a6_imafdc_sv39_hpdcache_wb_rvfi_dii_config_pkg.sv new file mode 100644 index 00000000..800d8835 --- /dev/null +++ b/hw/vendor/cva6_cheri/core/include/cv64a6_imafdc_sv39_hpdcache_wb_rvfi_dii_config_pkg.sv @@ -0,0 +1,179 @@ +// Copyright 2025 Bruno Sá and Zero-Day Labs. +// +// Licensed under the Solderpad Hardware Licence, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// SPDX-License-Identifier: Apache-2.0 WITH SHL-2.0 +// You may obtain a copy of the License at https://solderpad.org/licenses/ + + +package cva6_config_pkg; + + localparam CVA6ConfigXlen = 64; + + localparam CVA6ConfigRVF = 1; + localparam CVA6ConfigRVD = 1; + localparam CVA6ConfigF16En = 0; + localparam CVA6ConfigF16AltEn = 0; + localparam CVA6ConfigF8En = 0; + localparam CVA6ConfigFVecEn = 0; + + localparam CVA6ConfigCvxifEn = 0; + localparam CVA6ConfigCExtEn = 1; + localparam CVA6ConfigZcbExtEn = 0; + localparam CVA6ConfigZcmpExtEn = 0; + localparam CVA6ConfigAExtEn = 1; + localparam CVA6ConfigHExtEn = 0; + localparam CVA6ConfigBExtEn = 1; + localparam CVA6ConfigVExtEn = 0; + localparam CVA6ConfigRVZiCond = 1; + localparam CVA6ConfigRVZcheripurecap = 0; + localparam CVA6ConfigRVZcherihybrid = 0; + localparam CVA6ConfigCheriCapTagWidth = 1; + localparam CVA6ConfigRVFI_DII = 1; + + localparam CVA6ConfigAxiIdWidth = 4; + localparam CVA6ConfigAxiAddrWidth = 64; + localparam CVA6ConfigAxiDataWidth = 64; + localparam CVA6ConfigFetchUserEn = 0; + localparam CVA6ConfigFetchUserWidth = CVA6ConfigXlen; + localparam CVA6ConfigDataUserEn = 1; + localparam CVA6ConfigDataUserWidth = CVA6ConfigCheriCapTagWidth; + + localparam CVA6ConfigIcacheByteSize = 16384; + localparam CVA6ConfigIcacheSetAssoc = 4; + localparam CVA6ConfigIcacheLineWidth = 512; + localparam CVA6ConfigDcacheByteSize = 32768; + localparam CVA6ConfigDcacheSetAssoc = 8; + localparam CVA6ConfigDcacheLineWidth = 512; + + localparam CVA6ConfigDcacheFlushOnFence = 1'b1; + localparam CVA6ConfigDcacheInvalidateOnFlush = 1'b0; + + localparam CVA6ConfigDcacheIdWidth = 3; + localparam CVA6ConfigMemTidWidth = CVA6ConfigAxiIdWidth; + + localparam CVA6ConfigWtDcacheWbufDepth = 7; + + localparam CVA6ConfigNrScoreboardEntries = 8; + + localparam CVA6ConfigNrLoadPipeRegs = 1; + localparam CVA6ConfigNrStorePipeRegs = 0; + localparam CVA6ConfigNrLoadBufEntries = 8; + + localparam CVA6ConfigRASDepth = 2; + localparam CVA6ConfigBTBEntries = 32; + localparam CVA6ConfigBHTEntries = 128; + + localparam CVA6ConfigTvalEn = 1; + + localparam CVA6ConfigNrPMPEntries = 8; + + localparam CVA6ConfigPerfCounterEn = 1; + + localparam config_pkg::cache_type_t CVA6ConfigDcacheType = config_pkg::HPDCACHE_WB; + + localparam CVA6ConfigMmuPresent = 1; + + localparam CVA6ConfigRvfiTrace = 1; + + localparam config_pkg::cva6_user_cfg_t cva6_cfg = '{ + XLEN: unsigned'(CVA6ConfigXlen), + VLEN: unsigned'(64), + FpgaEn: bit'(0), // for Xilinx and Altera + FpgaAlteraEn: bit'(0), // for Altera (only) + TechnoCut: bit'(0), + SuperscalarEn: bit'(1), + ALUBypass: bit'(1), + NrCommitPorts: unsigned'(2), + AxiAddrWidth: unsigned'(CVA6ConfigAxiAddrWidth), + AxiDataWidth: unsigned'(CVA6ConfigAxiDataWidth), + AxiIdWidth: unsigned'(CVA6ConfigAxiIdWidth), + AxiUserWidth: unsigned'(CVA6ConfigDataUserWidth), + MemTidWidth: unsigned'(CVA6ConfigMemTidWidth), + NrLoadBufEntries: unsigned'(CVA6ConfigNrLoadBufEntries), + RVF: bit'(CVA6ConfigRVF), + RVD: bit'(CVA6ConfigRVD), + XF16: bit'(CVA6ConfigF16En), + XF16ALT: bit'(CVA6ConfigF16AltEn), + XF8: bit'(CVA6ConfigF8En), + RVA: bit'(CVA6ConfigAExtEn), + RVB: bit'(CVA6ConfigBExtEn), + ZKN: bit'(0), + RVV: bit'(CVA6ConfigVExtEn), + RVC: bit'(CVA6ConfigCExtEn), + RVH: bit'(CVA6ConfigHExtEn), + RVZCB: bit'(CVA6ConfigZcbExtEn), + RVZCMT: bit'(0), + RVZCMP: bit'(CVA6ConfigZcmpExtEn), + XFVec: bit'(CVA6ConfigFVecEn), + CvxifEn: bit'(CVA6ConfigCvxifEn), + CoproType: config_pkg::COPRO_NONE, + RVZiCond: bit'(CVA6ConfigRVZiCond), + RVZicntr: bit'(1), + RVZihpm: bit'(1), + RVZcheripurecap: bit'(CVA6ConfigRVZcheripurecap), + RVZcherihybrid: bit'(CVA6ConfigRVZcherihybrid), + NrScoreboardEntries: unsigned'(CVA6ConfigNrScoreboardEntries), + PerfCounterEn: bit'(CVA6ConfigPerfCounterEn), + MmuPresent: bit'(CVA6ConfigMmuPresent), + RVS: bit'(1), + RVU: bit'(1), + SoftwareInterruptEn: bit'(1), + HaltAddress: 64'h800, + ExceptionAddress: 64'h808, + RASDepth: unsigned'(CVA6ConfigRASDepth), + BTBEntries: unsigned'(CVA6ConfigBTBEntries), + BPType: config_pkg::BHT, + BHTEntries: unsigned'(CVA6ConfigBHTEntries), + BHTHist: unsigned'(3), + DmBaseAddress: 64'h0, + TvalEn: bit'(CVA6ConfigTvalEn), + DirectVecOnly: bit'(0), + NrPMPEntries: unsigned'(CVA6ConfigNrPMPEntries), + PMPCfgRstVal: {64{64'h0}}, + PMPAddrRstVal: {64{64'h0}}, + PMPEntryReadOnly: 64'd0, + PMPNapotEn: bit'(1), + NOCType: config_pkg::NOC_TYPE_AXI4_ATOP, + NrNonIdempotentRules: unsigned'(2), + NonIdempotentAddrBase: 1024'({64'b0, 64'b0}), + NonIdempotentLength: 1024'({64'b0, 64'b0}), + NrExecuteRegionRules: unsigned'(3), + ExecuteRegionAddrBase: 1024'({64'h8000_0000, 64'h1_0000, 64'h0}), + ExecuteRegionLength: 1024'({64'h40000000, 64'h10000, 64'h1000}), + NrCachedRegionRules: unsigned'(1), + CachedRegionAddrBase: 1024'({64'h8000_0000}), + CachedRegionLength: 1024'({64'h40000000}), + MaxOutstandingStores: unsigned'(7), + DebugEn: bit'(1), + SDTRIG: bit'(0), + Mcontrol6: bit'(0), + Icount: bit'(0), + Etrigger: bit'(0), + Itrigger: bit'(0), + AxiBurstWriteEn: bit'(1), + IcacheByteSize: unsigned'(CVA6ConfigIcacheByteSize), + IcacheSetAssoc: unsigned'(CVA6ConfigIcacheSetAssoc), + IcacheLineWidth: unsigned'(CVA6ConfigIcacheLineWidth), + DCacheType: CVA6ConfigDcacheType, + DcacheByteSize: unsigned'(CVA6ConfigDcacheByteSize), + DcacheSetAssoc: unsigned'(CVA6ConfigDcacheSetAssoc), + DcacheLineWidth: unsigned'(CVA6ConfigDcacheLineWidth), + DcacheFlushOnFence: unsigned'(CVA6ConfigDcacheFlushOnFence), + DcacheInvalidateOnFlush: unsigned'(CVA6ConfigDcacheInvalidateOnFlush), + DataUserEn: unsigned'(CVA6ConfigDataUserEn), + WtDcacheWbufDepth: int'(CVA6ConfigWtDcacheWbufDepth), + FetchUserWidth: unsigned'(CVA6ConfigFetchUserWidth), + FetchUserEn: unsigned'(CVA6ConfigFetchUserEn), + InstrTlbEntries: int'(16), + DataTlbEntries: int'(16), + UseSharedTlb: bit'(0), + SharedTlbDepth: int'(64), + NrLoadPipeRegs: int'(CVA6ConfigNrLoadPipeRegs), + NrStorePipeRegs: int'(CVA6ConfigNrStorePipeRegs), + DcacheIdWidth: int'(CVA6ConfigDcacheIdWidth), + CheriCapTagWidth : int'(CVA6ConfigCheriCapTagWidth), + RVFI_DII : int'(CVA6ConfigRVFI_DII) + }; + +endpackage diff --git a/hw/vendor/cva6_cheri/core/include/cv64a6_imafdc_sv39_openpiton_config_pkg.sv b/hw/vendor/cva6_cheri/core/include/cv64a6_imafdc_sv39_openpiton_config_pkg.sv index 806158fd..fdf4390f 100644 --- a/hw/vendor/cva6_cheri/core/include/cv64a6_imafdc_sv39_openpiton_config_pkg.sv +++ b/hw/vendor/cva6_cheri/core/include/cv64a6_imafdc_sv39_openpiton_config_pkg.sv @@ -83,6 +83,7 @@ package cva6_config_pkg; FpgaAlteraEn: bit'(0), // for Altera (only) TechnoCut: bit'(0), SuperscalarEn: bit'(0), + ALUBypass: bit'(0), NrCommitPorts: unsigned'(2), AxiAddrWidth: unsigned'(CVA6ConfigAxiAddrWidth), AxiDataWidth: unsigned'(CVA6ConfigAxiDataWidth), @@ -145,6 +146,11 @@ package cva6_config_pkg; CachedRegionLength: 1024'({64'h40000000}), MaxOutstandingStores: unsigned'(7), DebugEn: bit'(1), + SDTRIG: bit'(0), + Mcontrol6: bit'(0), + Icount: bit'(0), + Etrigger: bit'(0), + Itrigger: bit'(0), AxiBurstWriteEn: bit'(0), IcacheByteSize: unsigned'(CVA6ConfigIcacheByteSize), IcacheSetAssoc: unsigned'(CVA6ConfigIcacheSetAssoc), diff --git a/hw/vendor/cva6_cheri/core/include/cv64a6_imafdch_sv39_rvfi_dii_config_pkg.sv b/hw/vendor/cva6_cheri/core/include/cv64a6_imafdc_sv39_rvfi_dii_config_pkg.sv similarity index 96% rename from hw/vendor/cva6_cheri/core/include/cv64a6_imafdch_sv39_rvfi_dii_config_pkg.sv rename to hw/vendor/cva6_cheri/core/include/cv64a6_imafdc_sv39_rvfi_dii_config_pkg.sv index b9208840..61f89bd8 100644 --- a/hw/vendor/cva6_cheri/core/include/cv64a6_imafdch_sv39_rvfi_dii_config_pkg.sv +++ b/hw/vendor/cva6_cheri/core/include/cv64a6_imafdc_sv39_rvfi_dii_config_pkg.sv @@ -30,7 +30,6 @@ package cva6_config_pkg; localparam CVA6ConfigRVZcherihybrid = 0; localparam CVA6ConfigCheriCapTagWidth = 1; localparam CVA6ConfigRVFI_DII = 1; - localparam CVA6ConfigDIIIDLEN = 6; localparam CVA6ConfigAxiIdWidth = 4; localparam CVA6ConfigAxiAddrWidth = 64; @@ -83,7 +82,8 @@ package cva6_config_pkg; FpgaEn: bit'(0), // for Xilinx and Altera FpgaAlteraEn: bit'(0), // for Altera (only) TechnoCut: bit'(0), - SuperscalarEn: bit'(0), + SuperscalarEn: bit'(1), + ALUBypass: bit'(1), NrCommitPorts: unsigned'(2), AxiAddrWidth: unsigned'(CVA6ConfigAxiAddrWidth), AxiDataWidth: unsigned'(CVA6ConfigAxiDataWidth), @@ -146,6 +146,11 @@ package cva6_config_pkg; CachedRegionLength: 1024'({64'h40000000}), MaxOutstandingStores: unsigned'(7), DebugEn: bit'(1), + SDTRIG: bit'(0), + Mcontrol6: bit'(0), + Icount: bit'(0), + Etrigger: bit'(0), + Itrigger: bit'(0), AxiBurstWriteEn: bit'(1), IcacheByteSize: unsigned'(CVA6ConfigIcacheByteSize), IcacheSetAssoc: unsigned'(CVA6ConfigIcacheSetAssoc), @@ -168,8 +173,7 @@ package cva6_config_pkg; NrStorePipeRegs: int'(CVA6ConfigNrStorePipeRegs), DcacheIdWidth: int'(CVA6ConfigDcacheIdWidth), CheriCapTagWidth : int'(CVA6ConfigCheriCapTagWidth), - RVFI_DII : int'(CVA6ConfigRVFI_DII), - DIIIDLEN : int'(CVA6ConfigDIIIDLEN) + RVFI_DII : int'(CVA6ConfigRVFI_DII) }; endpackage diff --git a/hw/vendor/cva6_cheri/core/include/cv64a6_imafdc_sv39_wb_config_pkg.sv b/hw/vendor/cva6_cheri/core/include/cv64a6_imafdc_sv39_wb_config_pkg.sv index 62ee5387..aa825bdc 100644 --- a/hw/vendor/cva6_cheri/core/include/cv64a6_imafdc_sv39_wb_config_pkg.sv +++ b/hw/vendor/cva6_cheri/core/include/cv64a6_imafdc_sv39_wb_config_pkg.sv @@ -83,6 +83,7 @@ package cva6_config_pkg; FpgaAlteraEn: bit'(0), // for Altera (only) TechnoCut: bit'(0), SuperscalarEn: bit'(0), + ALUBypass: bit'(0), NrCommitPorts: unsigned'(2), AxiAddrWidth: unsigned'(CVA6ConfigAxiAddrWidth), AxiDataWidth: unsigned'(CVA6ConfigAxiDataWidth), @@ -145,6 +146,11 @@ package cva6_config_pkg; CachedRegionLength: 1024'({64'h40000000}), MaxOutstandingStores: unsigned'(7), DebugEn: bit'(1), + SDTRIG: bit'(0), + Mcontrol6: bit'(0), + Icount: bit'(0), + Etrigger: bit'(0), + Itrigger: bit'(0), AxiBurstWriteEn: bit'(0), IcacheByteSize: unsigned'(CVA6ConfigIcacheByteSize), IcacheSetAssoc: unsigned'(CVA6ConfigIcacheSetAssoc), diff --git a/hw/vendor/cva6_cheri/core/include/cv64a6_imafdch_sv39_config_pkg.sv b/hw/vendor/cva6_cheri/core/include/cv64a6_imafdch_sv39_config_pkg.sv index 81b7aa7f..f954db0c 100644 --- a/hw/vendor/cva6_cheri/core/include/cv64a6_imafdch_sv39_config_pkg.sv +++ b/hw/vendor/cva6_cheri/core/include/cv64a6_imafdch_sv39_config_pkg.sv @@ -83,6 +83,7 @@ package cva6_config_pkg; FpgaAlteraEn: bit'(0), // for Altera (only) TechnoCut: bit'(0), SuperscalarEn: bit'(0), + ALUBypass: bit'(0), NrCommitPorts: unsigned'(2), AxiAddrWidth: unsigned'(CVA6ConfigAxiAddrWidth), AxiDataWidth: unsigned'(CVA6ConfigAxiDataWidth), @@ -145,6 +146,11 @@ package cva6_config_pkg; CachedRegionLength: 1024'({64'h40000000}), MaxOutstandingStores: unsigned'(7), DebugEn: bit'(1), + SDTRIG: bit'(0), + Mcontrol6: bit'(0), + Icount: bit'(0), + Etrigger: bit'(0), + Itrigger: bit'(0), AxiBurstWriteEn: bit'(0), IcacheByteSize: unsigned'(CVA6ConfigIcacheByteSize), IcacheSetAssoc: unsigned'(CVA6ConfigIcacheSetAssoc), diff --git a/hw/vendor/cva6_cheri/core/include/cv64a6_imafdch_sv39_wb_config_pkg.sv b/hw/vendor/cva6_cheri/core/include/cv64a6_imafdch_sv39_wb_config_pkg.sv index 96ecab07..0500f6d5 100644 --- a/hw/vendor/cva6_cheri/core/include/cv64a6_imafdch_sv39_wb_config_pkg.sv +++ b/hw/vendor/cva6_cheri/core/include/cv64a6_imafdch_sv39_wb_config_pkg.sv @@ -83,6 +83,7 @@ package cva6_config_pkg; FpgaAlteraEn: bit'(0), // for Altera (only) TechnoCut: bit'(0), SuperscalarEn: bit'(0), + ALUBypass: bit'(0), NrCommitPorts: unsigned'(2), AxiAddrWidth: unsigned'(CVA6ConfigAxiAddrWidth), AxiDataWidth: unsigned'(CVA6ConfigAxiDataWidth), @@ -145,6 +146,11 @@ package cva6_config_pkg; CachedRegionLength: 1024'({64'h40000000}), MaxOutstandingStores: unsigned'(7), DebugEn: bit'(1), + SDTRIG: bit'(0), + Mcontrol6: bit'(0), + Icount: bit'(0), + Etrigger: bit'(0), + Itrigger: bit'(0), AxiBurstWriteEn: bit'(0), IcacheByteSize: unsigned'(CVA6ConfigIcacheByteSize), IcacheSetAssoc: unsigned'(CVA6ConfigIcacheSetAssoc), diff --git a/hw/vendor/cva6_cheri/core/include/cv64a6_imafdcv_sv39_config_pkg.sv b/hw/vendor/cva6_cheri/core/include/cv64a6_imafdcv_sv39_config_pkg.sv index 9941243a..96b1781b 100644 --- a/hw/vendor/cva6_cheri/core/include/cv64a6_imafdcv_sv39_config_pkg.sv +++ b/hw/vendor/cva6_cheri/core/include/cv64a6_imafdcv_sv39_config_pkg.sv @@ -85,6 +85,7 @@ package cva6_config_pkg; FpgaAlteraEn: bit'(0), // for Altera (only) TechnoCut: bit'(0), SuperscalarEn: bit'(0), + ALUBypass: bit'(0), NrCommitPorts: unsigned'(CVA6ConfigNrCommitPorts), AxiAddrWidth: unsigned'(CVA6ConfigAxiAddrWidth), AxiDataWidth: unsigned'(CVA6ConfigAxiDataWidth), @@ -147,6 +148,11 @@ package cva6_config_pkg; CachedRegionLength: 1024'({64'h40000000}), MaxOutstandingStores: unsigned'(7), DebugEn: bit'(1), + SDTRIG: bit'(0), + Mcontrol6: bit'(0), + Icount: bit'(0), + Etrigger: bit'(0), + Itrigger: bit'(0), AxiBurstWriteEn: bit'(0), IcacheByteSize: unsigned'(CVA6ConfigIcacheByteSize), IcacheSetAssoc: unsigned'(CVA6ConfigIcacheSetAssoc), diff --git a/hw/vendor/cva6_cheri/core/include/cv64a6_imafdchzcheri_sv39_config_pkg.sv b/hw/vendor/cva6_cheri/core/include/cv64a6_imafdczcheri_sv39_config_pkg.sv similarity index 88% rename from hw/vendor/cva6_cheri/core/include/cv64a6_imafdchzcheri_sv39_config_pkg.sv rename to hw/vendor/cva6_cheri/core/include/cv64a6_imafdczcheri_sv39_config_pkg.sv index dfe479a6..cf5174e8 100644 --- a/hw/vendor/cva6_cheri/core/include/cv64a6_imafdchzcheri_sv39_config_pkg.sv +++ b/hw/vendor/cva6_cheri/core/include/cv64a6_imafdczcheri_sv39_config_pkg.sv @@ -12,8 +12,8 @@ package cva6_config_pkg; localparam CVA6ConfigXlen = 64; - localparam CVA6ConfigRVF = 0; - localparam CVA6ConfigRVD = 0; + localparam CVA6ConfigRVF = 1; + localparam CVA6ConfigRVD = 1; localparam CVA6ConfigF16En = 0; localparam CVA6ConfigF16AltEn = 0; localparam CVA6ConfigF8En = 0; @@ -32,7 +32,6 @@ package cva6_config_pkg; localparam CVA6ConfigRVZcherihybrid = 1; localparam CVA6ConfigCheriCapTagWidth = 1; localparam CVA6ConfigRVFI_DII = 0; - localparam CVA6ConfigDIIIDLEN = 1; localparam CVA6ConfigAxiIdWidth = 4; localparam CVA6ConfigAxiAddrWidth = 64; @@ -77,7 +76,7 @@ package cva6_config_pkg; localparam CVA6ConfigMmuPresent = 1; - localparam CVA6ConfigRvfiTrace = 1; + localparam CVA6ConfigRvfiTrace = 0; localparam config_pkg::cva6_user_cfg_t cva6_cfg = '{ XLEN: unsigned'(CVA6ConfigXlen), @@ -85,7 +84,8 @@ package cva6_config_pkg; FpgaEn: bit'(0), // for Xilinx and Altera FpgaAlteraEn: bit'(0), // for Altera (only) TechnoCut: bit'(0), - SuperscalarEn: bit'(0), + SuperscalarEn: bit'(1), + ALUBypass: bit'(1), NrCommitPorts: unsigned'(2), AxiAddrWidth: unsigned'(CVA6ConfigAxiAddrWidth), AxiDataWidth: unsigned'(CVA6ConfigAxiDataWidth), @@ -138,16 +138,21 @@ package cva6_config_pkg; PMPNapotEn: bit'(1), NOCType: config_pkg::NOC_TYPE_AXI4_ATOP, NrNonIdempotentRules: unsigned'(2), - NonIdempotentAddrBase: 1024'({64'b0, 64'b0}), - NonIdempotentLength: 1024'({64'b0, 64'b0}), + NonIdempotentAddrBase: 1024'({64'h0, 64'h2_0000}), + NonIdempotentLength: 1024'({64'h1_0000, 64'h8000_0000 - 64'h2_0000}), NrExecuteRegionRules: unsigned'(3), - ExecuteRegionAddrBase: 1024'({64'h8000_0000, 64'h1000_0000, 64'h2000_0000}), - ExecuteRegionLength: 1024'({64'h4000_0000, 64'h2_0000, 64'h1000}), - NrCachedRegionRules: unsigned'(1), - CachedRegionAddrBase: 1024'({64'h8000_0000}), - CachedRegionLength: 1024'({64'h4000_0000}), + ExecuteRegionAddrBase: 1024'({64'h8000_0000, 64'h1_0000, 64'h0}), + ExecuteRegionLength: 1024'({64'h40000000, 64'h10000, 64'h1000}), + NrCachedRegionRules: unsigned'(2), + CachedRegionAddrBase: 1024'({64'h8000_0000, 64'h1_0000}), + CachedRegionLength: 1024'({64'h40000000, 64'h1_0000}), MaxOutstandingStores: unsigned'(7), DebugEn: bit'(1), + SDTRIG: bit'(0), + Mcontrol6: bit'(0), + Icount: bit'(0), + Etrigger: bit'(0), + Itrigger: bit'(0), AxiBurstWriteEn: bit'(1), IcacheByteSize: unsigned'(CVA6ConfigIcacheByteSize), IcacheSetAssoc: unsigned'(CVA6ConfigIcacheSetAssoc), @@ -170,8 +175,7 @@ package cva6_config_pkg; NrStorePipeRegs: int'(CVA6ConfigNrStorePipeRegs), DcacheIdWidth: int'(CVA6ConfigDcacheIdWidth), CheriCapTagWidth : int'(CVA6ConfigCheriCapTagWidth), - RVFI_DII : int'(CVA6ConfigRVFI_DII), - DIIIDLEN : int'(CVA6ConfigDIIIDLEN) + RVFI_DII : int'(CVA6ConfigRVFI_DII) }; endpackage diff --git a/hw/vendor/cva6_cheri/core/include/cv64a6_imafdczcheri_sv39_hpdcache_wb_config_pkg.sv b/hw/vendor/cva6_cheri/core/include/cv64a6_imafdczcheri_sv39_hpdcache_wb_config_pkg.sv new file mode 100644 index 00000000..890eefa4 --- /dev/null +++ b/hw/vendor/cva6_cheri/core/include/cv64a6_imafdczcheri_sv39_hpdcache_wb_config_pkg.sv @@ -0,0 +1,181 @@ +// Copyright 2021 Thales DIS design services SAS +// +// Licensed under the Solderpad Hardware Licence, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// SPDX-License-Identifier: Apache-2.0 WITH SHL-2.0 +// You may obtain a copy of the License at https://solderpad.org/licenses/ +// +// Original Author: Jean-Roch COULON - Thales + + +package cva6_config_pkg; + + localparam CVA6ConfigXlen = 64; + + localparam CVA6ConfigRVF = 1; + localparam CVA6ConfigRVD = 1; + localparam CVA6ConfigF16En = 0; + localparam CVA6ConfigF16AltEn = 0; + localparam CVA6ConfigF8En = 0; + localparam CVA6ConfigFVecEn = 0; + + localparam CVA6ConfigCvxifEn = 0; + localparam CVA6ConfigCExtEn = 1; + localparam CVA6ConfigZcbExtEn = 0; + localparam CVA6ConfigZcmpExtEn = 0; + localparam CVA6ConfigAExtEn = 1; + localparam CVA6ConfigBExtEn = 1; + localparam CVA6ConfigVExtEn = 0; + localparam CVA6ConfigHExtEn = 0; + localparam CVA6ConfigRVZiCond = 1; + localparam CVA6ConfigRVZcheripurecap = 1; + localparam CVA6ConfigRVZcherihybrid = 1; + localparam CVA6ConfigCheriCapTagWidth = 1; + localparam CVA6ConfigRVFI_DII = 0; + + localparam CVA6ConfigAxiIdWidth = 4; + localparam CVA6ConfigAxiAddrWidth = 64; + localparam CVA6ConfigAxiDataWidth = CVA6ConfigXlen; + localparam CVA6ConfigFetchUserEn = 0; + localparam CVA6ConfigFetchUserWidth = 1; + localparam CVA6ConfigDataUserEn = 1; + localparam CVA6ConfigDataUserWidth = CVA6ConfigCheriCapTagWidth; + + localparam CVA6ConfigIcacheByteSize = 16384; + localparam CVA6ConfigIcacheSetAssoc = 4; + localparam CVA6ConfigIcacheLineWidth = 128; + localparam CVA6ConfigDcacheByteSize = 32768; + localparam CVA6ConfigDcacheSetAssoc = 8; + localparam CVA6ConfigDcacheLineWidth = 512; + + localparam CVA6ConfigDcacheFlushOnFence = 1'b1; + localparam CVA6ConfigDcacheInvalidateOnFlush = 1'b0; + + localparam CVA6ConfigDcacheIdWidth = 3; + localparam CVA6ConfigMemTidWidth = CVA6ConfigAxiIdWidth; + + localparam CVA6ConfigWtDcacheWbufDepth = 7; + + localparam CVA6ConfigNrScoreboardEntries = 8; + + localparam CVA6ConfigNrLoadPipeRegs = 1; + localparam CVA6ConfigNrStorePipeRegs = 0; + localparam CVA6ConfigNrLoadBufEntries = 8; + + localparam CVA6ConfigRASDepth = 2; + localparam CVA6ConfigBTBEntries = 32; + localparam CVA6ConfigBHTEntries = 128; + + localparam CVA6ConfigTvalEn = 1; + + localparam CVA6ConfigNrPMPEntries = 8; + + localparam CVA6ConfigPerfCounterEn = 1; + + localparam config_pkg::cache_type_t CVA6ConfigDcacheType = config_pkg::HPDCACHE_WB; + + localparam CVA6ConfigMmuPresent = 1; + + localparam CVA6ConfigRvfiTrace = 0; + + localparam config_pkg::cva6_user_cfg_t cva6_cfg = '{ + XLEN: unsigned'(CVA6ConfigXlen), + VLEN: unsigned'(64), + FpgaEn: bit'(0), // for Xilinx and Altera + FpgaAlteraEn: bit'(0), // for Altera (only) + TechnoCut: bit'(0), + SuperscalarEn: bit'(1), + ALUBypass: bit'(1), + NrCommitPorts: unsigned'(2), + AxiAddrWidth: unsigned'(CVA6ConfigAxiAddrWidth), + AxiDataWidth: unsigned'(CVA6ConfigAxiDataWidth), + AxiIdWidth: unsigned'(CVA6ConfigAxiIdWidth), + AxiUserWidth: unsigned'(CVA6ConfigDataUserWidth), + MemTidWidth: unsigned'(CVA6ConfigMemTidWidth), + NrLoadBufEntries: unsigned'(CVA6ConfigNrLoadBufEntries), + RVF: bit'(CVA6ConfigRVF), + RVD: bit'(CVA6ConfigRVD), + XF16: bit'(CVA6ConfigF16En), + XF16ALT: bit'(CVA6ConfigF16AltEn), + XF8: bit'(CVA6ConfigF8En), + RVA: bit'(CVA6ConfigAExtEn), + RVB: bit'(CVA6ConfigBExtEn), + ZKN: bit'(0), + RVV: bit'(CVA6ConfigVExtEn), + RVC: bit'(CVA6ConfigCExtEn), + RVH: bit'(CVA6ConfigHExtEn), + RVZCB: bit'(CVA6ConfigZcbExtEn), + RVZCMT: bit'(0), + RVZCMP: bit'(CVA6ConfigZcmpExtEn), + XFVec: bit'(CVA6ConfigFVecEn), + CvxifEn: bit'(CVA6ConfigCvxifEn), + CoproType: config_pkg::COPRO_NONE, + RVZiCond: bit'(CVA6ConfigRVZiCond), + RVZicntr: bit'(1), + RVZihpm: bit'(1), + RVZcheripurecap: bit'(CVA6ConfigRVZcheripurecap), + RVZcherihybrid: bit'(CVA6ConfigRVZcherihybrid), + NrScoreboardEntries: unsigned'(CVA6ConfigNrScoreboardEntries), + PerfCounterEn: bit'(CVA6ConfigPerfCounterEn), + MmuPresent: bit'(CVA6ConfigMmuPresent), + RVS: bit'(1), + RVU: bit'(1), + SoftwareInterruptEn: bit'(1), + HaltAddress: 64'h800, + ExceptionAddress: 64'h808, + RASDepth: unsigned'(CVA6ConfigRASDepth), + BTBEntries: unsigned'(CVA6ConfigBTBEntries), + BPType: config_pkg::BHT, + BHTEntries: unsigned'(CVA6ConfigBHTEntries), + BHTHist: unsigned'(3), + DmBaseAddress: 64'h0, + TvalEn: bit'(CVA6ConfigTvalEn), + DirectVecOnly: bit'(0), + NrPMPEntries: unsigned'(CVA6ConfigNrPMPEntries), + PMPCfgRstVal: {64{64'h0}}, + PMPAddrRstVal: {64{64'h0}}, + PMPEntryReadOnly: 64'd0, + PMPNapotEn: bit'(1), + NOCType: config_pkg::NOC_TYPE_AXI4_ATOP, + NrNonIdempotentRules: unsigned'(2), + NonIdempotentAddrBase: 1024'({64'h0, 64'h2_0000}), + NonIdempotentLength: 1024'({64'h1_0000, 64'h8000_0000 - 64'h2_0000}), + NrExecuteRegionRules: unsigned'(3), + ExecuteRegionAddrBase: 1024'({64'h8000_0000, 64'h1_0000, 64'h0}), + ExecuteRegionLength: 1024'({64'h40000000, 64'h10000, 64'h1000}), + NrCachedRegionRules: unsigned'(2), + CachedRegionAddrBase: 1024'({64'h8000_0000, 64'h1_0000}), + CachedRegionLength: 1024'({64'h40000000, 64'h1_0000}), + MaxOutstandingStores: unsigned'(7), + DebugEn: bit'(1), + SDTRIG: bit'(0), + Mcontrol6: bit'(0), + Icount: bit'(0), + Etrigger: bit'(0), + Itrigger: bit'(0), + AxiBurstWriteEn: bit'(1), + IcacheByteSize: unsigned'(CVA6ConfigIcacheByteSize), + IcacheSetAssoc: unsigned'(CVA6ConfigIcacheSetAssoc), + IcacheLineWidth: unsigned'(CVA6ConfigIcacheLineWidth), + DCacheType: CVA6ConfigDcacheType, + DcacheByteSize: unsigned'(CVA6ConfigDcacheByteSize), + DcacheSetAssoc: unsigned'(CVA6ConfigDcacheSetAssoc), + DcacheLineWidth: unsigned'(CVA6ConfigDcacheLineWidth), + DcacheFlushOnFence: unsigned'(CVA6ConfigDcacheFlushOnFence), + DcacheInvalidateOnFlush: unsigned'(CVA6ConfigDcacheInvalidateOnFlush), + DataUserEn: unsigned'(CVA6ConfigDataUserEn), + WtDcacheWbufDepth: int'(CVA6ConfigWtDcacheWbufDepth), + FetchUserWidth: unsigned'(CVA6ConfigFetchUserWidth), + FetchUserEn: unsigned'(CVA6ConfigFetchUserEn), + InstrTlbEntries: int'(16), + DataTlbEntries: int'(16), + UseSharedTlb: bit'(0), + SharedTlbDepth: int'(64), + NrLoadPipeRegs: int'(CVA6ConfigNrLoadPipeRegs), + NrStorePipeRegs: int'(CVA6ConfigNrStorePipeRegs), + DcacheIdWidth: int'(CVA6ConfigDcacheIdWidth), + CheriCapTagWidth : int'(CVA6ConfigCheriCapTagWidth), + RVFI_DII : int'(CVA6ConfigRVFI_DII) + }; + +endpackage diff --git a/hw/vendor/cva6_cheri/core/include/cv64a6_imafdczcheri_sv39_hpdcache_wb_rvfi_dii_config_pkg.sv b/hw/vendor/cva6_cheri/core/include/cv64a6_imafdczcheri_sv39_hpdcache_wb_rvfi_dii_config_pkg.sv new file mode 100644 index 00000000..81eac149 --- /dev/null +++ b/hw/vendor/cva6_cheri/core/include/cv64a6_imafdczcheri_sv39_hpdcache_wb_rvfi_dii_config_pkg.sv @@ -0,0 +1,179 @@ +// Copyright 2025 Bruno Sá and Zero-Day Labs. +// +// Licensed under the Solderpad Hardware Licence, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// SPDX-License-Identifier: Apache-2.0 WITH SHL-2.0 +// You may obtain a copy of the License at https://solderpad.org/licenses/ + + +package cva6_config_pkg; + + localparam CVA6ConfigXlen = 64; + + localparam CVA6ConfigRVF = 1; + localparam CVA6ConfigRVD = 1; + localparam CVA6ConfigF16En = 0; + localparam CVA6ConfigF16AltEn = 0; + localparam CVA6ConfigF8En = 0; + localparam CVA6ConfigFVecEn = 0; + + localparam CVA6ConfigCvxifEn = 0; + localparam CVA6ConfigCExtEn = 1; + localparam CVA6ConfigZcbExtEn = 0; + localparam CVA6ConfigZcmpExtEn = 0; + localparam CVA6ConfigAExtEn = 1; + localparam CVA6ConfigHExtEn = 0; + localparam CVA6ConfigBExtEn = 1; + localparam CVA6ConfigVExtEn = 0; + localparam CVA6ConfigRVZiCond = 1; + localparam CVA6ConfigRVZcheripurecap = 1; + localparam CVA6ConfigRVZcherihybrid = 1; + localparam CVA6ConfigCheriCapTagWidth = 1; + localparam CVA6ConfigRVFI_DII = 1; + + localparam CVA6ConfigAxiIdWidth = 4; + localparam CVA6ConfigAxiAddrWidth = 64; + localparam CVA6ConfigAxiDataWidth = CVA6ConfigXlen; + localparam CVA6ConfigFetchUserEn = 0; + localparam CVA6ConfigFetchUserWidth = 1; + localparam CVA6ConfigDataUserEn = 1; + localparam CVA6ConfigDataUserWidth = CVA6ConfigCheriCapTagWidth; + + localparam CVA6ConfigIcacheByteSize = 16384; + localparam CVA6ConfigIcacheSetAssoc = 4; + localparam CVA6ConfigIcacheLineWidth = 128; + localparam CVA6ConfigDcacheByteSize = 32768; + localparam CVA6ConfigDcacheSetAssoc = 8; + localparam CVA6ConfigDcacheLineWidth = 512; + + localparam CVA6ConfigDcacheFlushOnFence = 1'b1; + localparam CVA6ConfigDcacheInvalidateOnFlush = 1'b0; + + localparam CVA6ConfigDcacheIdWidth = 3; + localparam CVA6ConfigMemTidWidth = CVA6ConfigAxiIdWidth; + + localparam CVA6ConfigWtDcacheWbufDepth = 7; + + localparam CVA6ConfigNrScoreboardEntries = 8; + + localparam CVA6ConfigNrLoadPipeRegs = 1; + localparam CVA6ConfigNrStorePipeRegs = 0; + localparam CVA6ConfigNrLoadBufEntries = 8; + + localparam CVA6ConfigRASDepth = 2; + localparam CVA6ConfigBTBEntries = 32; + localparam CVA6ConfigBHTEntries = 128; + + localparam CVA6ConfigTvalEn = 1; + + localparam CVA6ConfigNrPMPEntries = 8; + + localparam CVA6ConfigPerfCounterEn = 1; + + localparam config_pkg::cache_type_t CVA6ConfigDcacheType = config_pkg::HPDCACHE_WB; + + localparam CVA6ConfigMmuPresent = 1; + + localparam CVA6ConfigRvfiTrace = 1; + + localparam config_pkg::cva6_user_cfg_t cva6_cfg = '{ + XLEN: unsigned'(CVA6ConfigXlen), + VLEN: unsigned'(64), + FpgaEn: bit'(0), // for Xilinx and Altera + FpgaAlteraEn: bit'(0), // for Altera (only) + TechnoCut: bit'(0), + SuperscalarEn: bit'(1), + ALUBypass: bit'(1), + NrCommitPorts: unsigned'(2), + AxiAddrWidth: unsigned'(CVA6ConfigAxiAddrWidth), + AxiDataWidth: unsigned'(CVA6ConfigAxiDataWidth), + AxiIdWidth: unsigned'(CVA6ConfigAxiIdWidth), + AxiUserWidth: unsigned'(CVA6ConfigDataUserWidth), + MemTidWidth: unsigned'(CVA6ConfigMemTidWidth), + NrLoadBufEntries: unsigned'(CVA6ConfigNrLoadBufEntries), + RVF: bit'(CVA6ConfigRVF), + RVD: bit'(CVA6ConfigRVD), + XF16: bit'(CVA6ConfigF16En), + XF16ALT: bit'(CVA6ConfigF16AltEn), + XF8: bit'(CVA6ConfigF8En), + RVA: bit'(CVA6ConfigAExtEn), + RVB: bit'(CVA6ConfigBExtEn), + ZKN: bit'(0), + RVV: bit'(CVA6ConfigVExtEn), + RVC: bit'(CVA6ConfigCExtEn), + RVH: bit'(CVA6ConfigHExtEn), + RVZCB: bit'(CVA6ConfigZcbExtEn), + RVZCMT: bit'(0), + RVZCMP: bit'(CVA6ConfigZcmpExtEn), + XFVec: bit'(CVA6ConfigFVecEn), + CvxifEn: bit'(CVA6ConfigCvxifEn), + CoproType: config_pkg::COPRO_NONE, + RVZiCond: bit'(CVA6ConfigRVZiCond), + RVZicntr: bit'(1), + RVZihpm: bit'(1), + RVZcheripurecap: bit'(CVA6ConfigRVZcheripurecap), + RVZcherihybrid: bit'(CVA6ConfigRVZcherihybrid), + NrScoreboardEntries: unsigned'(CVA6ConfigNrScoreboardEntries), + PerfCounterEn: bit'(CVA6ConfigPerfCounterEn), + MmuPresent: bit'(CVA6ConfigMmuPresent), + RVS: bit'(1), + RVU: bit'(1), + SoftwareInterruptEn: bit'(1), + HaltAddress: 64'h800, + ExceptionAddress: 64'h808, + RASDepth: unsigned'(CVA6ConfigRASDepth), + BTBEntries: unsigned'(CVA6ConfigBTBEntries), + BPType: config_pkg::BHT, + BHTEntries: unsigned'(CVA6ConfigBHTEntries), + BHTHist: unsigned'(3), + DmBaseAddress: 64'h0, + TvalEn: bit'(CVA6ConfigTvalEn), + DirectVecOnly: bit'(0), + NrPMPEntries: unsigned'(CVA6ConfigNrPMPEntries), + PMPCfgRstVal: {64{64'h0}}, + PMPAddrRstVal: {64{64'h0}}, + PMPEntryReadOnly: 64'd0, + PMPNapotEn: bit'(1), + NOCType: config_pkg::NOC_TYPE_AXI4_ATOP, + NrNonIdempotentRules: unsigned'(2), + NonIdempotentAddrBase: 1024'({64'b0, 64'b0}), + NonIdempotentLength: 1024'({64'b0, 64'b0}), + NrExecuteRegionRules: unsigned'(3), + ExecuteRegionAddrBase: 1024'({64'h8000_0000, 64'h1_0000, 64'h0}), + ExecuteRegionLength: 1024'({64'h40000000, 64'h10000, 64'h1000}), + NrCachedRegionRules: unsigned'(1), + CachedRegionAddrBase: 1024'({64'h8000_0000}), + CachedRegionLength: 1024'({64'h40000000}), + MaxOutstandingStores: unsigned'(7), + DebugEn: bit'(1), + SDTRIG: bit'(0), + Mcontrol6: bit'(0), + Icount: bit'(0), + Etrigger: bit'(0), + Itrigger: bit'(0), + AxiBurstWriteEn: bit'(1), + IcacheByteSize: unsigned'(CVA6ConfigIcacheByteSize), + IcacheSetAssoc: unsigned'(CVA6ConfigIcacheSetAssoc), + IcacheLineWidth: unsigned'(CVA6ConfigIcacheLineWidth), + DCacheType: CVA6ConfigDcacheType, + DcacheByteSize: unsigned'(CVA6ConfigDcacheByteSize), + DcacheSetAssoc: unsigned'(CVA6ConfigDcacheSetAssoc), + DcacheLineWidth: unsigned'(CVA6ConfigDcacheLineWidth), + DcacheFlushOnFence: unsigned'(CVA6ConfigDcacheFlushOnFence), + DcacheInvalidateOnFlush: unsigned'(CVA6ConfigDcacheInvalidateOnFlush), + DataUserEn: unsigned'(CVA6ConfigDataUserEn), + WtDcacheWbufDepth: int'(CVA6ConfigWtDcacheWbufDepth), + FetchUserWidth: unsigned'(CVA6ConfigFetchUserWidth), + FetchUserEn: unsigned'(CVA6ConfigFetchUserEn), + InstrTlbEntries: int'(16), + DataTlbEntries: int'(16), + UseSharedTlb: bit'(0), + SharedTlbDepth: int'(64), + NrLoadPipeRegs: int'(CVA6ConfigNrLoadPipeRegs), + NrStorePipeRegs: int'(CVA6ConfigNrStorePipeRegs), + DcacheIdWidth: int'(CVA6ConfigDcacheIdWidth), + CheriCapTagWidth : int'(CVA6ConfigCheriCapTagWidth), + RVFI_DII : int'(CVA6ConfigRVFI_DII) + }; + +endpackage diff --git a/hw/vendor/cva6_cheri/core/include/cv64a6_imafdchzcheri_sv39_rvfi_dii_config_pkg.sv b/hw/vendor/cva6_cheri/core/include/cv64a6_imafdczcheri_sv39_rvfi_dii_config_pkg.sv similarity index 96% rename from hw/vendor/cva6_cheri/core/include/cv64a6_imafdchzcheri_sv39_rvfi_dii_config_pkg.sv rename to hw/vendor/cva6_cheri/core/include/cv64a6_imafdczcheri_sv39_rvfi_dii_config_pkg.sv index 1f028f8f..42329691 100644 --- a/hw/vendor/cva6_cheri/core/include/cv64a6_imafdchzcheri_sv39_rvfi_dii_config_pkg.sv +++ b/hw/vendor/cva6_cheri/core/include/cv64a6_imafdczcheri_sv39_rvfi_dii_config_pkg.sv @@ -30,7 +30,6 @@ package cva6_config_pkg; localparam CVA6ConfigRVZcherihybrid = 1; localparam CVA6ConfigCheriCapTagWidth = 1; localparam CVA6ConfigRVFI_DII = 1; - localparam CVA6ConfigDIIIDLEN = 6; localparam CVA6ConfigAxiIdWidth = 4; localparam CVA6ConfigAxiAddrWidth = 64; @@ -83,7 +82,8 @@ package cva6_config_pkg; FpgaEn: bit'(0), // for Xilinx and Altera FpgaAlteraEn: bit'(0), // for Altera (only) TechnoCut: bit'(0), - SuperscalarEn: bit'(0), + SuperscalarEn: bit'(1), + ALUBypass: bit'(1), NrCommitPorts: unsigned'(2), AxiAddrWidth: unsigned'(CVA6ConfigAxiAddrWidth), AxiDataWidth: unsigned'(CVA6ConfigAxiDataWidth), @@ -146,6 +146,11 @@ package cva6_config_pkg; CachedRegionLength: 1024'({64'h40000000}), MaxOutstandingStores: unsigned'(7), DebugEn: bit'(1), + SDTRIG: bit'(0), + Mcontrol6: bit'(0), + Icount: bit'(0), + Etrigger: bit'(0), + Itrigger: bit'(0), AxiBurstWriteEn: bit'(1), IcacheByteSize: unsigned'(CVA6ConfigIcacheByteSize), IcacheSetAssoc: unsigned'(CVA6ConfigIcacheSetAssoc), @@ -168,8 +173,7 @@ package cva6_config_pkg; NrStorePipeRegs: int'(CVA6ConfigNrStorePipeRegs), DcacheIdWidth: int'(CVA6ConfigDcacheIdWidth), CheriCapTagWidth : int'(CVA6ConfigCheriCapTagWidth), - RVFI_DII : int'(CVA6ConfigRVFI_DII), - DIIIDLEN : int'(CVA6ConfigDIIIDLEN) + RVFI_DII : int'(CVA6ConfigRVFI_DII) }; endpackage diff --git a/hw/vendor/cva6_cheri/core/include/cva6_cheri_pkg.sv b/hw/vendor/cva6_cheri/core/include/cva6_cheri_pkg.sv index 659cf1f0..e0884d40 100644 --- a/hw/vendor/cva6_cheri/core/include/cva6_cheri_pkg.sv +++ b/hw/vendor/cva6_cheri/core/include/cva6_cheri_pkg.sv @@ -88,6 +88,7 @@ package cva6_cheri_pkg; logic [3:0] fault_type; /* Type of check being performed */ logic [11:0] wpri; logic [3:0] fault_cause; /* Reason for failed check */ + logic [1:0] ignored; /* Bottom two bits are sliced off */ } cap_tval2_t; /** @@ -218,7 +219,6 @@ package cva6_cheri_pkg; /* Capability definition in register */ typedef struct packed { bool_t tag; - mw_t addr_mid; resw_hi_t res_hi; upermsw_t uperms; cap_flags_t flags; @@ -230,9 +230,6 @@ package cva6_cheri_pkg; addrw_t addr; } cap_reg_t; - /* Full PCC Capability definition */ - typedef cap_reg_t cap_pcc_t; - /* Capability set bounds return */ typedef struct packed { cap_reg_t cap; @@ -251,7 +248,6 @@ package cva6_cheri_pkg; localparam cap_reg_t REG_ROOT_CAP = '{ tag : 1'b1, addr : '{default: 0}, - addr_mid : '{default: 0}, uperms : '{default: '1}, hperms : '{default: '1}, flags : 1'b1, @@ -265,7 +261,6 @@ package cva6_cheri_pkg; localparam cap_reg_t REG_NULL_CAP = '{ tag : 1'b0, addr : '{default: 0}, - addr_mid : '{default: 0}, uperms : '{default: 0}, hperms : '{default: 0}, flags : 1'b0, @@ -276,10 +271,6 @@ package cva6_cheri_pkg; bounds : DEFAULT_BOUNDS_CAP }; - localparam cap_pcc_t PCC_ROOT_CAP = REG_ROOT_CAP; - - localparam cap_pcc_t PCC_NULL_CAP = REG_NULL_CAP; - localparam cap_mem_t MEM_NULL_CAP = '{ tag : 1'b0, res_hi : '0, @@ -458,7 +449,7 @@ package cva6_cheri_pkg; function automatic cap_flags_t get_cap_reg_flags(cap_reg_t cap); cap_hperms_t perms = legalize_arch_perms(cap.hperms); - return (perms.permit_execute) ? cap.flags : 1'b0; + return (perms == cap.hperms && perms.permit_execute) ? cap.flags : 1'b0; endfunction function automatic cap_reg_t set_cap_reg_flags(cap_reg_t cap, cap_flags_t flags); @@ -502,23 +493,6 @@ package cva6_cheri_pkg; return (cap.bounds.exp == CAP_RESET_EXP) ? ~0 : length; endfunction - /** - * @brief Function to compute the capability offset address. - * @param cap capability in register format. - * @param dec_bounds decounds bounds meta data. - * @returns the capability offset with size [CAP_ADDR_WIDTH-1:0]. - */ - function automatic addrw_t get_cap_reg_offset(cap_reg_t cap, cap_meta_data_t cap_meta_data); - ew_t exp = cap.bounds.exp; - mwe2_t base = {cap_meta_data.cb, cap.bounds.base_bits}; - mwe2_t offset_bits = {2'b0, cap.addr_mid} - base; - addrw_t addr_lsb = cap.addr & (~0 >> (CAP_M_WIDTH - 2 + exp)); - addrw_t offset = {{CAP_ADDR_WIDTH - CAP_M_WIDTH - 2{offset_bits[CAP_M_WIDTH+1]}}, offset_bits}; - offset = offset << (CAP_MAX_EXP - exp); - offset = offset | addr_lsb; - return offset; - endfunction - /** * @brief Function sets the capability address and check if is representable. * @param cap capability in register format. @@ -545,7 +519,6 @@ package cva6_cheri_pkg; bool_t is_rep = deltaAddrHi == deltaAddrUpper; ret.addr = address; - ret.addr_mid = newAddrMid; // The exp out-of-range check is required for formal bsv equivalence. if (!is_rep || e > CAP_MAX_EXP) ret.tag = 1'b0; return ret; @@ -561,90 +534,9 @@ package cva6_cheri_pkg; cap_reg_t ret = cap; ew_t exp = (cap.bounds.exp > CAP_MAX_EXP) ? CAP_MAX_EXP : cap.bounds.exp; ret.addr = address; - ret.addr_mid = extract_addr_mid(address, exp); - return ret; - endfunction - - function automatic cap_reg_t cap_reg_inc_offset( - cap_reg_t cap, addrw_t cursor - , addrw_t offset // this is the increment in inc offset, and the offset in set offset - , cap_meta_data_t cap_meta_data, bool_t set_offset); - cap_reg_t ret = cap; - ew_t exp = cap.bounds.exp; - addrw_t offset_addr = offset; - mw_t offset_bits = extract_addr_mid(offset_addr, exp); - - // ---------------- - // In Range test - - localparam T_W = CAP_ADDR_WIDTH - CAP_M_WIDTH; - logic [T_W-1:0] sgn_bits = T_W'($signed(offset[CAP_ADDR_WIDTH-1])); - logic [T_W-1:0] og_hi_off_bits = offset_addr[CAP_ADDR_WIDTH-1:CAP_M_WIDTH]; - logic [T_W-1:0] hi_filt_bits = T_W'(~({T_W + 2{1'b1}} >> exp)); - logic [T_W-1:0] hi_off_bits = (og_hi_off_bits ^ sgn_bits) & hi_filt_bits; - bool_t in_range = hi_off_bits == 0; - - // The sign of the increment - bool_t pos_inc = 1'(offset_addr[CAP_ADDR_WIDTH-1]) == 1'b0; - mw_t to_bounds_a = {3'b110, (CAP_M_WIDTH - 3)'(0)}; - mw_t to_bounds_m1_a = to_bounds_a - CAP_M_WIDTH'(1); - mw_t rep_bound_bits = cap_meta_data.r; - mw_t to_bounds_b = rep_bound_bits - cap.addr_mid; - mw_t to_bounds_m1_b = rep_bound_bits + ~cap.addr_mid; - - // Select the appropriate toBounds value - mw_t to_bounds = set_offset ? to_bounds_a : to_bounds_b; - mw_t to_bounds_m1 = set_offset ? to_bounds_m1_a : to_bounds_m1_b; - bool_t addr_at_rep_bound = !set_offset && (rep_bound_bits == cap.addr_mid); - - // Implement the in_limits test - bool_t in_limits = pos_inc ? (set_offset ? offset_bits <= to_bounds_m1 - : offset_bits < to_bounds_m1) - : ((offset_bits >= to_bounds) && !addr_at_rep_bound); - - // Complete representable bounds check - // ----------------------------------- - bool_t in_bounds = (in_range && in_limits) || (exp <= 2); - - // Updating the return capability - // ------------------------------ - mw_t new_addr_bits = cap.bounds.base_bits + offset_bits; - logic [CAP_M_WIDTH-3:0] mask_lo = ~0; - logic [1:0] mask_hi = (exp == 0) ? 2'b00 : (exp == 1) ? 2'b01 : 2'b11; - mw_t mask = {mask_hi, mask_lo}; - if (set_offset) begin - ret.addr = get_cap_reg_base(cap, cap_meta_data) + offset_addr; - ret.addr_mid = new_addr_bits & mask; - end else begin - ret.addr = cursor; - ret.addr_mid = extract_addr_mid(cursor, exp); - end - // Nullify the capability if the representable bounds check has failed - if (!in_bounds) ret.tag = 1'b0; - - // return updated / invalid capability return ret; endfunction - /** - * @brief Function to check if capabiliy is within bounds. - * @param cap capability in register format. - * @param cap_meta_data capability decounds bounds meta data. - * @param inclusive 0 - includes top in the in bounds check. - * 1 - excludes top in the inbounds check. - * @returns 0 if not in bounds and 1 if in bounds. - */ - function automatic bool_t is_cap_reg_inbounds(cap_reg_t cap, cap_meta_data_t meta_data, - bool_t inclusive); - mw_t addr_mid = cap.addr_mid; - bool_t check_addr = inclusive ? addr_mid <= cap.bounds.top_bits - : addr_mid < cap.bounds.top_bits; - bool_t check_top = (meta_data.top_hi_r == meta_data.addr_hi_r) ? check_addr : meta_data.top_hi_r; - bool_t check_base = (meta_data.base_hi_r == meta_data.addr_hi_r) ? addr_mid >= cap.bounds.base_bits - : meta_data.addr_hi_r; - return check_top && check_base; - endfunction - /** * @brief Function to compute the in limit check * @param offset capability offset. @@ -752,10 +644,8 @@ package cva6_cheri_pkg; ) : add_b1000( lost_sig_top, new_top_bits[CAP_M_WIDTH-1:0]) ); - mw_t final_base_bits = bot3z( - int_exp - , (len_ovflw && int_exp) ? new_base_bits[CAP_M_WIDTH:1] : new_base_bits[CAP_M_WIDTH-1:0] - ); + mw_t base_bits = (len_ovflw && int_exp) ? new_base_bits[CAP_M_WIDTH:1] : new_base_bits[CAP_M_WIDTH-1:0]; + mw_t final_base_bits = bot3z(int_exp, base_bits); bool_t exact = !(lost_sig_base || lost_sig_top); cap_fmt_t fmt = (int_exp) ? EMBEDDED_EXP : IMPLIED_EXP; @@ -768,13 +658,18 @@ package cva6_cheri_pkg; : ~lmask_lo ) : {CAP_ADDR_WIDTH + 2{1'b1}}; + // perform well-formed checks on the result cap + //////////////////////////////////////////////////////////////////////// + if (final_exp == 6'd0 && base[CAP_ADDR_WIDTH-1-:CAP_M_WIDTH-2] != '0) begin + ret.cap.tag = 1'b0; + end + // fold in return values and return //////////////////////////////////////////////////////////////////////// ret.cap.EF = fmt; ret.cap.bounds.exp = final_exp; ret.cap.bounds.top_bits = final_top_bits; ret.cap.bounds.base_bits = final_base_bits; - ret.cap.addr_mid = final_base_bits; ret.exact = exact; ret.mask = base_mask[CAP_ADDR_WIDTH-1:0]; return ret; @@ -835,20 +730,11 @@ package cva6_cheri_pkg; otype: cap.otype, EF: cap.EF, bounds: bounds, - addr: cap.addr, - addr_mid: extract_addr_mid(cap.addr, exp) + addr: cap.addr }; return ret; endfunction - function automatic cap_pcc_t cap_reg_to_cap_pcc(cap_reg_t cap); - return cap; - endfunction - - function automatic cap_reg_t cap_pcc_to_cap_reg(cap_pcc_t cap); - return cap; - endfunction - /** * @brief Function to convert from encoded hperms and uperms field to format for reporting with gcperm. * @param hardware permissions in encoded format. @@ -856,10 +742,8 @@ package cva6_cheri_pkg; * @returns permissions in the report format for gcperms. */ function automatic cap_report_perms_t hperms_and_uperms_to_report_perms( - cap_hperms_t hp_raw, upermsw_t up, bool_t int_mode); - cap_hperms_t hp = ((legalize_arch_perms( - hp_raw - ) != hp_raw) || (int_mode && !hp_raw.permit_execute)) ? '0 : hp_raw; + cap_hperms_t hp_raw, upermsw_t up, logic perms_are_malformed); + cap_hperms_t hp = perms_are_malformed ? '0 : hp_raw; cap_report_perms_t rp = '{ reserved_hi : 0, //Newer spec:'1, permit_load : hp.permit_load, @@ -868,8 +752,7 @@ package cva6_cheri_pkg; reserved_lo : 0, //Newer spec:'1, uperms : up, permit_cap : hp.permit_cap, - cap_level : - hp_raw.cap_level, // Not a permission, so not subject to the same legalisation + cap_level : hp_raw.cap_level, // Not a permission, so not legalised permit_store_level : hp.permit_store_level, permit_elevate_level : hp.permit_elevate_level, permit_load_mutable : hp.permit_load_mutable, @@ -881,7 +764,7 @@ package cva6_cheri_pkg; /** * @brief Function to convert from reported permissions format for andperms to encoded hardware permissions. * @param permissions in the report format for ACPERM. - * @returns hardware permissions in encoded format. + * @returns hardware permissions in encoded format. Note that legalisation must be performed outside. */ function automatic cap_hperms_t report_perms_to_hperms(cap_report_perms_t rp); cap_hperms_t hp = '{ @@ -895,7 +778,7 @@ package cva6_cheri_pkg; permit_cap : rp.permit_cap, cap_level : rp.cap_level }; - return legalize_arch_perms(hp); + return hp; endfunction /** diff --git a/hw/vendor/cva6_cheri/core/include/cvxif_types.svh b/hw/vendor/cva6_cheri/core/include/cvxif_types.svh index 5902bd99..d1cdf35c 100644 --- a/hw/vendor/cva6_cheri/core/include/cvxif_types.svh +++ b/hw/vendor/cva6_cheri/core/include/cvxif_types.svh @@ -48,7 +48,7 @@ writeregflags_t we; /*Register file write enable(s)*/ \ } -`define CVXIF_REQ_T(Cfg, x_compressed_req_t, x_issue_req_t, x_register_req_t, x_commit_t) struct packed { \ +`define CVXIF_REQ_T(Cfg, x_compressed_req_t, x_issue_req_t, x_register_t, x_commit_t) struct packed { \ logic compressed_valid; \ x_compressed_req_t compressed_req; \ logic issue_valid; \ diff --git a/hw/vendor/cva6_cheri/core/include/cv32a6_embedded_config_pkg_deprecated.sv b/hw/vendor/cva6_cheri/core/include/deprecated_packages/cv32a6_embedded_config_pkg_deprecated.sv similarity index 98% rename from hw/vendor/cva6_cheri/core/include/cv32a6_embedded_config_pkg_deprecated.sv rename to hw/vendor/cva6_cheri/core/include/deprecated_packages/cv32a6_embedded_config_pkg_deprecated.sv index 804ebcc3..223318fa 100644 --- a/hw/vendor/cva6_cheri/core/include/cv32a6_embedded_config_pkg_deprecated.sv +++ b/hw/vendor/cva6_cheri/core/include/deprecated_packages/cv32a6_embedded_config_pkg_deprecated.sv @@ -144,6 +144,11 @@ package cva6_config_pkg; CachedRegionLength: 1024'({64'h40000000}), MaxOutstandingStores: unsigned'(7), DebugEn: bit'(0), + SDTRIG: bit'(0), + Mcontrol6: bit'(0), + Icount: bit'(0), + Etrigger: bit'(0), + Itrigger: bit'(0), AxiBurstWriteEn: bit'(0), IcacheByteSize: unsigned'(CVA6ConfigIcacheByteSize), IcacheSetAssoc: unsigned'(CVA6ConfigIcacheSetAssoc), diff --git a/hw/vendor/cva6_cheri/core/include/cv32a6_imac_sv0_config_pkg.sv b/hw/vendor/cva6_cheri/core/include/deprecated_packages/cv32a6_imac_sv0_config_pkg.sv similarity index 97% rename from hw/vendor/cva6_cheri/core/include/cv32a6_imac_sv0_config_pkg.sv rename to hw/vendor/cva6_cheri/core/include/deprecated_packages/cv32a6_imac_sv0_config_pkg.sv index 8062394e..19ee3a52 100644 --- a/hw/vendor/cva6_cheri/core/include/cv32a6_imac_sv0_config_pkg.sv +++ b/hw/vendor/cva6_cheri/core/include/deprecated_packages/cv32a6_imac_sv0_config_pkg.sv @@ -80,6 +80,7 @@ package cva6_config_pkg; FpgaAlteraEn: bit'(0), // for Altera (only) TechnoCut: bit'(0), SuperscalarEn: bit'(0), + ALUBypass: bit'(0), NrCommitPorts: unsigned'(2), AxiAddrWidth: unsigned'(CVA6ConfigAxiAddrWidth), AxiDataWidth: unsigned'(CVA6ConfigAxiDataWidth), @@ -142,6 +143,11 @@ package cva6_config_pkg; CachedRegionLength: 1024'({64'h40000000}), MaxOutstandingStores: unsigned'(7), DebugEn: bit'(1), + SDTRIG: bit'(0), + Mcontrol6: bit'(0), + Icount: bit'(0), + Etrigger: bit'(0), + Itrigger: bit'(0), AxiBurstWriteEn: bit'(0), IcacheByteSize: unsigned'(CVA6ConfigIcacheByteSize), IcacheSetAssoc: unsigned'(CVA6ConfigIcacheSetAssoc), diff --git a/hw/vendor/cva6_cheri/core/include/cv32a6_imafc_sv32_config_pkg.sv b/hw/vendor/cva6_cheri/core/include/deprecated_packages/cv32a6_imafc_sv32_config_pkg.sv similarity index 97% rename from hw/vendor/cva6_cheri/core/include/cv32a6_imafc_sv32_config_pkg.sv rename to hw/vendor/cva6_cheri/core/include/deprecated_packages/cv32a6_imafc_sv32_config_pkg.sv index dcec2a5a..fe721203 100644 --- a/hw/vendor/cva6_cheri/core/include/cv32a6_imafc_sv32_config_pkg.sv +++ b/hw/vendor/cva6_cheri/core/include/deprecated_packages/cv32a6_imafc_sv32_config_pkg.sv @@ -80,6 +80,7 @@ package cva6_config_pkg; FpgaAlteraEn: bit'(0), // for Altera (only) TechnoCut: bit'(0), SuperscalarEn: bit'(0), + ALUBypass: bit'(0), NrCommitPorts: unsigned'(2), AxiAddrWidth: unsigned'(CVA6ConfigAxiAddrWidth), AxiDataWidth: unsigned'(CVA6ConfigAxiDataWidth), @@ -142,6 +143,11 @@ package cva6_config_pkg; CachedRegionLength: 1024'({64'h40000000}), MaxOutstandingStores: unsigned'(7), DebugEn: bit'(1), + SDTRIG: bit'(0), + Mcontrol6: bit'(0), + Icount: bit'(0), + Etrigger: bit'(0), + Itrigger: bit'(0), AxiBurstWriteEn: bit'(0), IcacheByteSize: unsigned'(CVA6ConfigIcacheByteSize), IcacheSetAssoc: unsigned'(CVA6ConfigIcacheSetAssoc), diff --git a/hw/vendor/cva6_cheri/core/include/cv64a6_imadfcv_sv39_polara_config_pkg.sv b/hw/vendor/cva6_cheri/core/include/deprecated_packages/cv64a6_imadfcv_sv39_polara_config_pkg.sv similarity index 97% rename from hw/vendor/cva6_cheri/core/include/cv64a6_imadfcv_sv39_polara_config_pkg.sv rename to hw/vendor/cva6_cheri/core/include/deprecated_packages/cv64a6_imadfcv_sv39_polara_config_pkg.sv index b5a966dc..c9aabe4f 100644 --- a/hw/vendor/cva6_cheri/core/include/cv64a6_imadfcv_sv39_polara_config_pkg.sv +++ b/hw/vendor/cva6_cheri/core/include/deprecated_packages/cv64a6_imadfcv_sv39_polara_config_pkg.sv @@ -83,6 +83,7 @@ package cva6_config_pkg; FpgaAlteraEn: bit'(0), // for Altera (only) TechnoCut: bit'(0), SuperscalarEn: bit'(0), + ALUBypass: bit'(0), NrCommitPorts: unsigned'(2), AxiAddrWidth: unsigned'(CVA6ConfigAxiAddrWidth), AxiDataWidth: unsigned'(CVA6ConfigAxiDataWidth), @@ -145,6 +146,11 @@ package cva6_config_pkg; CachedRegionLength: 1024'({64'h40000000}), MaxOutstandingStores: unsigned'(7), DebugEn: bit'(1), + SDTRIG: bit'(0), + Mcontrol6: bit'(0), + Icount: bit'(0), + Etrigger: bit'(0), + Itrigger: bit'(0), AxiBurstWriteEn: bit'(0), IcacheByteSize: unsigned'(CVA6ConfigIcacheByteSize), IcacheSetAssoc: unsigned'(CVA6ConfigIcacheSetAssoc), diff --git a/hw/vendor/cva6_cheri/core/include/cv64a6_mmu_config_pkg.sv b/hw/vendor/cva6_cheri/core/include/deprecated_packages/cv64a6_mmu_config_pkg.sv similarity index 96% rename from hw/vendor/cva6_cheri/core/include/cv64a6_mmu_config_pkg.sv rename to hw/vendor/cva6_cheri/core/include/deprecated_packages/cv64a6_mmu_config_pkg.sv index c52f8f82..176063ca 100644 --- a/hw/vendor/cva6_cheri/core/include/cv64a6_mmu_config_pkg.sv +++ b/hw/vendor/cva6_cheri/core/include/deprecated_packages/cv64a6_mmu_config_pkg.sv @@ -32,6 +32,7 @@ package cva6_config_pkg; FpgaAlteraEn: bit'(0), TechnoCut: bit'(0), SuperscalarEn: bit'(0), + ALUBypass: bit'(0), NrCommitPorts: unsigned'(1), AxiAddrWidth: unsigned'(CVA6ConfigAxiAddrWidth), AxiDataWidth: unsigned'(CVA6ConfigAxiDataWidth), @@ -92,6 +93,11 @@ package cva6_config_pkg; CachedRegionLength: 1024'({64'h40000000}), MaxOutstandingStores: unsigned'(7), DebugEn: bit'(1), + SDTRIG: bit'(0), + Mcontrol6: bit'(0), + Icount: bit'(0), + Etrigger: bit'(0), + Itrigger: bit'(0), AxiBurstWriteEn: bit'(0), IcacheByteSize: unsigned'(2048), IcacheSetAssoc: unsigned'(2), diff --git a/hw/vendor/cva6_cheri/core/include/iti_pkg.sv b/hw/vendor/cva6_cheri/core/include/iti_pkg.sv deleted file mode 100644 index 0099f9e5..00000000 --- a/hw/vendor/cva6_cheri/core/include/iti_pkg.sv +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright (c) 2025 Thales DIS design services SAS -// SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 -// Author: Maxime Colson - Thales -// Date: 20/03/2025 -// Contributors: -// Darshak Sheladiya, SYSGO GmbH -// Umberto Laghi, UNIBO - -// This package is temporary, the idea is to have it directly in the encoder later - -package iti_pkg; - - localparam CAUSE_LEN = 5; //Size is ecause_width_p in the E-Trace SPEC - localparam ITYPE_LEN = 3; //Size is itype_width_p in the E-Trace SPEC (3 or 4) - localparam IRETIRE_LEN = 32; //Size is iretire_width_p in the E-Trace SPEC - - typedef enum logic [ITYPE_LEN-1:0] { - STANDARD = 0, // none of the other named itype codes - EXC = 1, // exception - INT = 2, // interrupt - ERET = 3, // exception or interrupt return - NON_TAKEN_BR = 4, // nontaken branch - TAKEN_BR = 5, // taken branch - UNINF_JMP = 6, // uninferable jump if ITYPE_LEN == 3, otherwise reserved - RES = 7 /*, // reserved - UC = 8, // uninferable call - IC = 9, // inferable call - UIJ = 10, // uninferable jump - IJ = 11, // inferable jump - CRS = 12, // co-routine swap - RET = 13, // return - OUIJ = 14, // other uninferable jump - OIJ = 15*/ // other inferable jump - } itype_t; - -endpackage diff --git a/hw/vendor/cva6_cheri/core/include/iti_types.svh b/hw/vendor/cva6_cheri/core/include/iti_types.svh deleted file mode 100644 index d18a604e..00000000 --- a/hw/vendor/cva6_cheri/core/include/iti_types.svh +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright (c) 2025 Thales DIS design services SAS -// SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 -// Author: Maxime Colson - Thales -// Date: 20/03/2025 -// Contributors: -// Darshak Sheladiya, SYSGO GmbH -// Umberto Laghi, UNIBO - -`ifndef ITI_TYPES_SVH -`define ITI_TYPES_SVH - -`define ITI_TO_ENCODER_T(Cfg) struct packed { \ - logic [Cfg.NrCommitPorts-1:0] valid; \ - logic [Cfg.NrCommitPorts-1:0][iti_pkg::IRETIRE_LEN-1:0] iretire; \ - logic [Cfg.NrCommitPorts-1:0] ilastsize; \ - iti_pkg::itype_t [Cfg.NrCommitPorts-1:0][iti_pkg::ITYPE_LEN-1:0] itype; \ - logic [iti_pkg::CAUSE_LEN-1:0] cause; \ - logic [Cfg.XLEN-1:0] tval; \ - riscv::priv_lvl_t priv; \ - logic [Cfg.NrCommitPorts-1:0][Cfg.XLEN-1:0] iaddr; \ - logic [63:0] cycles; \ - } - -`endif // ITI_TYPES_SVH diff --git a/hw/vendor/cva6_cheri/core/include/riscv_pkg.sv b/hw/vendor/cva6_cheri/core/include/riscv_pkg.sv index 723ab29c..d464b14d 100644 --- a/hw/vendor/cva6_cheri/core/include/riscv_pkg.sv +++ b/hw/vendor/cva6_cheri/core/include/riscv_pkg.sv @@ -703,6 +703,8 @@ package riscv; CSR_TDATA2 = 12'h7A2, CSR_TDATA3 = 12'h7A3, CSR_TINFO = 12'h7A4, + CSR_MCONTEXT = 12'h7A8, + CSR_SCONTEXT = 12'h5A8, // Debug CSR CSR_DCSR = 12'h7b0, CSR_DPC = 12'h7b1, diff --git a/hw/vendor/cva6_cheri/core/include/triggers_pkg.sv b/hw/vendor/cva6_cheri/core/include/triggers_pkg.sv new file mode 100644 index 00000000..57adffc3 --- /dev/null +++ b/hw/vendor/cva6_cheri/core/include/triggers_pkg.sv @@ -0,0 +1,117 @@ +package triggers_pkg; + // Triggers + typedef struct packed { + logic [31:28] t_type; // type 3 for icount + logic dmode; + logic vs; + logic vu; + logic hit; + logic [23:10] count; + logic m; + logic pending; + logic s; + logic u; + logic [5:0] action; + } icount32_tdata1_t; + + typedef struct packed { + logic [31:26] mhvalue; + logic [25:23] mhselect; + logic [22:20] zeroes; + logic [19:18] sbytemask; + logic [17:2] svalue; + logic [1:0] sselect; + } textra32_tdata3_t; + + typedef struct packed { + logic [63:51] mhvalue; + logic [50:48] mhselect; + logic [47:40] zeroes; + logic [39:36] sbytemask; + logic [35:34] zero_field; + logic [33:2] svalue; + logic [1:0] sselect; + } textra64_tdata3_t; + + typedef struct packed { + logic [31:28] t_type; // type 6 for mcontrol6 + logic dmode; + logic uncertain; + logic hit1; + logic vs; + logic vu; + logic hit0; + logic select; + logic [20:19] zeroes; + logic [18:16] size; + logic [15:12] action; + logic chain; + logic [10:7] match; + logic m; + logic uncertainen; + logic s; + logic u; + logic execute; + logic store; + logic load; + } mcontrol6_32_tdata1_t; + + typedef struct packed { + logic [31:28] t_type; // type 4 for etrigger + logic dmode; + logic hit; + logic [25:13] zeroed; + logic vs; + logic vu; + logic nmi; + logic m; + logic zero; + logic s; + logic u; + logic [5:0] action; + } itrigger32_tdata1_t; + + typedef struct packed { + logic [31:28] t_type; // type 5 for etrigger + logic dmode; + logic hit; + logic [25:13] zeroes; + logic vs; + logic vu; + logic zeroed; + logic m; + logic zero; + logic s; + logic u; + logic [5:0] action; + } etrigger32_tdata1_t; + + function automatic logic napot_match(logic [63:0] base, logic [63:0] value); + logic [63:0] mask; + logic is_valid_napot; + + is_valid_napot = ((base & (base + 1)) == 0); + if (!is_valid_napot) return 0; + + mask = ~(base & ~(base + 1)); + return (value & mask) == (base & mask); + endfunction + + function automatic logic match_scontext(input logic [31:0] scontext, input logic [1:0] sselect, + input logic [3:0] sbytemask, input logic [31:0] svalue, + input bit flag); + logic match; + match = 1'b1; + if (sselect == 2'd1) begin + int max_bytes = flag ? 4 : 2; + for (int b = 0; b < max_bytes; b++) begin + if (!sbytemask[b]) begin + if (scontext[8*b+:8] != svalue[8*b+:8]) match = 1'b0; + end + end + end else match = 1'b0; + return match; + endfunction + + +endpackage diff --git a/hw/vendor/cva6_cheri/core/instr_realign.sv b/hw/vendor/cva6_cheri/core/instr_realign.sv index 187cae74..c57ce896 100644 --- a/hw/vendor/cva6_cheri/core/instr_realign.sv +++ b/hw/vendor/cva6_cheri/core/instr_realign.sv @@ -204,7 +204,7 @@ module instr_realign end else begin unaligned_instr_d = instr_o[3]; unaligned_address_d = addr_o[3]; - if (CVA6Cfg.RVFI_DII) unaligned_dii_id_d = dii_id_o[3]; + if (CVA6Cfg.RVFI_DII) unaligned_dii_id_d = dii_id_o[2]; end end end else begin @@ -248,7 +248,7 @@ module instr_realign unaligned_d = 1'b1; unaligned_instr_d = instr_o[3]; unaligned_address_d = addr_o[3]; - if (CVA6Cfg.RVFI_DII) unaligned_dii_id_d = dii_id_o[3]; + if (CVA6Cfg.RVFI_DII) unaligned_dii_id_d = dii_id_o[2]; end end end else begin @@ -270,7 +270,7 @@ module instr_realign unaligned_d = 1'b1; unaligned_instr_d = instr_o[3]; unaligned_address_d = addr_o[3]; - if (CVA6Cfg.RVFI_DII) unaligned_dii_id_d = dii_id_o[3]; + if (CVA6Cfg.RVFI_DII) unaligned_dii_id_d = dii_id_o[2]; end end end @@ -321,7 +321,7 @@ module instr_realign unaligned_d = 1'b1; unaligned_instr_d = instr_o[2]; unaligned_address_d = addr_o[2]; - if (CVA6Cfg.RVFI_DII) unaligned_dii_id_d = dii_id_o[2]; + if (CVA6Cfg.RVFI_DII) unaligned_dii_id_d = dii_id_o[1]; end end end diff --git a/hw/vendor/cva6_cheri/core/issue_read_operands.sv b/hw/vendor/cva6_cheri/core/issue_read_operands.sv index 670d2c69..d4c27f8c 100644 --- a/hw/vendor/cva6_cheri/core/issue_read_operands.sv +++ b/hw/vendor/cva6_cheri/core/issue_read_operands.sv @@ -55,13 +55,15 @@ module issue_read_operands // Int mode flag in PCC register - ID_STAGE output logic int_mode_o, // PCC exception - Execute - output exception_t issue_pcc_ex_o, + output exception_t [CVA6Cfg.NrIssuePorts-1:0] issue_pcc_ex_o, // Backend Empty - scoreboard input logic backend_empty_i, // Forwarding - SCOREBOARD input forwarding_t fwd_i, // FU data useful to execute instruction - EX_STAGE output fu_data_t [CVA6Cfg.NrIssuePorts-1:0] fu_data_o, + // ALU to ALU bypass control - EX_STAGE + output alu_bypass_t alu_bypass_o, // Unregistered version of fu_data_o.operanda - EX_STAGE output logic [CVA6Cfg.NrIssuePorts-1:0][CVA6Cfg.REGLEN-1:0] rs1_forwarding_o, // Unregistered version of fu_data_o.operandb - EX_STAGE @@ -96,6 +98,8 @@ module issue_read_operands output logic [CVA6Cfg.NrIssuePorts-1:0] mult_valid_o, // FPU FU is ready - EX_STAGE input logic fpu_ready_i, + // FPU FU will perform a writeback in the next cycle - EX_STAGE + input logic fpu_early_valid_i, // FPU FU is valid - EX_STAGE output logic [CVA6Cfg.NrIssuePorts-1:0] fpu_valid_o, // FPU fmt field - EX_STAGE @@ -109,7 +113,7 @@ module issue_read_operands // CVXIF FU is valid - EX_STAGE output logic [CVA6Cfg.NrIssuePorts-1:0] cvxif_valid_o, // CLU result is valid - EX_STAGE - output logic clu_valid_o, + output logic [CVA6Cfg.NrIssuePorts-1:0] clu_valid_o, // CVXIF is FU ready - EX_STAGE input logic cvxif_ready_i, // CVXIF offloader instruction value - EX_STAGE @@ -182,10 +186,10 @@ module issue_read_operands rs3_len_t operand_c_fpr; // output flipflop (ID <-> EX) fu_data_t [CVA6Cfg.NrIssuePorts-1:0] fu_data_n, fu_data_q; - logic [CVA6Cfg.VLEN-1:0] pc_n; - logic is_compressed_instr_n; - branchpredict_sbe_t branch_predict_n; - logic [CVA6Cfg.XLEN-1:0] imm_forward_rs3; + logic [ CVA6Cfg.VLEN-1:0] pc_n; + logic is_compressed_instr_n; + branchpredict_sbe_t branch_predict_n; + logic [CVA6Cfg.NrIssuePorts-1:0][CVA6Cfg.XLEN-1:0] imm_forward_rs3; logic [CVA6Cfg.NrIssuePorts-1:0] alu_valid_n, alu_valid_q; logic [CVA6Cfg.NrIssuePorts-1:0] aes_valid_n, aes_valid_q; @@ -246,15 +250,22 @@ module issue_read_operands logic [CVA6Cfg.PCLEN-1:0] pcc_n, pcc_q; logic pcc_jump_change_valid_n, pcc_jump_change_valid_q; logic [CVA6Cfg.PCLEN-1:0] pcc_jump_change_n, pcc_jump_change_q; - cva6_cheri_pkg::cap_pcc_t pcc[CVA6Cfg.NrIssuePorts-1:0]; + cva6_cheri_pkg::cap_reg_t pcc[CVA6Cfg.NrIssuePorts-1:0]; cva6_cheri_pkg::cap_meta_data_t pcc_meta; // forwarding signals logic [CVA6Cfg.NrIssuePorts-1:0] forward_rs1, forward_rs2, forward_rs3; // original instruction - riscv::instruction_t orig_instr; - assign orig_instr = riscv::instruction_t'(orig_instr_i[0]); + riscv::instruction_t [CVA6Cfg.NrIssuePorts-1:0] orig_instr; + for (genvar i = 0; i < CVA6Cfg.NrIssuePorts; i++) begin + assign orig_instr[i] = riscv::instruction_t'(orig_instr_i[i]); + end + + // ALU-ALU bypass signals + alu_bypass_t alu_bypass, alu_bypass_n, alu_bypass_q; + logic is_alu_bypass; + logic [1:0] use_alu2; // CVXIF Signals logic cvxif_req_allowed; @@ -314,6 +325,7 @@ module issue_read_operands assign rvfi_rs2_o[i] = fu_data_n[i].operand_b; end + assign alu_bypass_o = alu_bypass_q; assign fu_data_o = fu_data_q; assign alu_valid_o = alu_valid_q; assign aes_valid_o = aes_valid_q; @@ -330,7 +342,29 @@ module issue_read_operands assign cvxif_off_instr_o = CVA6Cfg.CvxifEn ? cvxif_off_instr_q : '0; assign stall_issue_o = stall_raw[0]; assign tinst_o = CVA6Cfg.RVH ? tinst_q : '0; - assign int_mode_o = cva6_cheri_pkg::get_cap_reg_flags(pcc_q); + + // ALU bypass signals + if (CVA6Cfg.ALUBypass) begin + // If it is a ALU -> ALU, we can fuse all operation beside CPOP (maybe can be optimized OP -> CPOP, to explore) + assign is_alu_bypass = + (issue_instr_i[0].fu == ALU && issue_instr_i[1].fu == ALU) && + !((issue_instr_i[0].op inside {CPOP, CPOPW}) || (issue_instr_i[1].op inside {CPOP, CPOPW})); + end else begin + assign is_alu_bypass = 1'b0; + end + + if (CVA6Cfg.SuperscalarEn) begin + // When a bypass is possible or an FPU instruction is present on the second issue port, + // an instruction uses `alu2` only when `alu` is already busy + // In all other scenarios `alu2` is preferred over `alu`, unless it is busy + for (genvar i = 0; i < 2; i++) begin + assign use_alu2[i] = is_alu_bypass || (issue_instr_i[1].fu inside {FPU, FPU_VEC}) ? fus_busy[i].alu : !fus_busy[i].alu2; + end + end else begin + assign use_alu2 = '0; + end + + assign int_mode_o = cva6_cheri_pkg::get_cap_reg_flags(pcc_q); assign commit_pcc_o = pcc_q; // --------------- // Issue Stage @@ -363,7 +397,6 @@ module issue_read_operands if (CVA6Cfg.FpPresent && !fpu_ready_i) begin fus_busy[0].fpu = 1'b1; fus_busy[0].fpu_vec = 1'b1; - if (CVA6Cfg.SuperscalarEn) fus_busy[0].alu2 = 1'b1; end if (!lsu_ready_i) begin @@ -372,6 +405,11 @@ module issue_read_operands end if (CVA6Cfg.SuperscalarEn) begin + + if (fpu_early_valid_i) begin + fus_busy[0].alu2 = 1'b1; + end + fus_busy[1] = fus_busy[0]; // Never issue CSR instruction on second issue port. @@ -406,15 +444,12 @@ module issue_read_operands end end ALU: begin - if (CVA6Cfg.SuperscalarEn && !fus_busy[0].alu2) begin + if (use_alu2[0]) begin fus_busy[1].alu2 = 1'b1; - // TODO is there a minimum float execution time? - // If so we could issue FPU & ALU2 the same cycle - fus_busy[1].fpu = 1'b1; - fus_busy[1].fpu_vec = 1'b1; end else begin fus_busy[1].alu = 1'b1; fus_busy[1].ctrl_flow = 1'b1; + fus_busy[1].clu = 1'b1; fus_busy[1].csr = 1'b1; end end @@ -426,13 +461,24 @@ module issue_read_operands FPU, FPU_VEC: begin fus_busy[1].fpu = 1'b1; fus_busy[1].fpu_vec = 1'b1; + if (issue_instr_i[1].op inside {[FLD : FSB]}) begin + fus_busy[1].load = 1'b1; + fus_busy[1].store = 1'b1; + end end LOAD, STORE: begin fus_busy[1].load = 1'b1; fus_busy[1].store = 1'b1; + if (issue_instr_i[0].op inside {[FLD : FSB]}) begin + fus_busy[1].fpu = 1'b1; + fus_busy[1].fpu_vec = 1'b1; + end end CLU: begin + fus_busy[1].alu = 1'b1; fus_busy[1].clu = 1'b1; + fus_busy[1].ctrl_flow = 1'b1; + fus_busy[1].csr = 1'b1; end CVXIF: ; default: ; @@ -457,7 +503,7 @@ module issue_read_operands pcc_base = cva6_cheri_pkg::get_cap_reg_base(pcc_q, pcc_meta); pcc_top = cva6_cheri_pkg::get_cap_reg_top(pcc_q, pcc_meta); pcc_bounds_root = cva6_cheri_pkg::are_cap_reg_bounds_root(pcc_q, pcc_meta); - issue_pcc_ex_o = 0; + issue_pcc_ex_o = '0; // Check PCC bounds every instruction for (int unsigned i = 0; i < CVA6Cfg.NrIssuePorts; i++) begin automatic logic [CVA6Cfg.VLEN-1:0] next_pc_off; @@ -468,37 +514,37 @@ module issue_read_operands next_pc_off = ((issue_instr_i[i].is_compressed) ? {{CVA6Cfg.VLEN-2{1'b0}}, 2'h2} : {{CVA6Cfg.VLEN-3{1'b0}}, 3'h4}); {next_pc_carry, next_pc_addr} = {1'b0, issue_instr_i[i].pc} + {1'b0, next_pc_off}; if((cva6_cheri_pkg::addrw_t'(signed'(issue_instr_i[i].pc)) < pcc_base) || ({1'b0,cva6_cheri_pkg::addrw_t'(signed'(next_pc_addr))} > pcc_top) || (next_pc_carry && !pcc_bounds_root)) begin - issue_pcc_ex_o.cause = cva6_cheri_pkg::CAP_EXCEPTION; + issue_pcc_ex_o[i].cause = cva6_cheri_pkg::CAP_EXCEPTION; cheri_tval2.fault_cause = cva6_cheri_pkg::CAP_BOUNDS_VIOLATION; - issue_pcc_ex_o.tval2 = cheri_tval2; - issue_pcc_ex_o.valid = 1'b1; + issue_pcc_ex_o[i].tval2 = cheri_tval2; + issue_pcc_ex_o[i].valid = 1'b1; end if (issue_instr_i[i].needs_asr && !pcc[i].hperms.access_sys_regs) begin - issue_pcc_ex_o.cause = cva6_cheri_pkg::CAP_EXCEPTION; + issue_pcc_ex_o[i].cause = cva6_cheri_pkg::CAP_EXCEPTION; cheri_tval2.fault_cause = cva6_cheri_pkg::CAP_PERM_VIOLATION; - issue_pcc_ex_o.tval2 = cheri_tval2; - issue_pcc_ex_o.valid = 1'b1; + issue_pcc_ex_o[i].tval2 = cheri_tval2; + issue_pcc_ex_o[i].valid = 1'b1; end if (!pcc[i].hperms.permit_execute) begin - issue_pcc_ex_o.cause = cva6_cheri_pkg::CAP_EXCEPTION; + issue_pcc_ex_o[i].cause = cva6_cheri_pkg::CAP_EXCEPTION; cheri_tval2.fault_cause = cva6_cheri_pkg::CAP_PERM_VIOLATION; - issue_pcc_ex_o.tval2 = cheri_tval2; - issue_pcc_ex_o.valid = 1'b1; + issue_pcc_ex_o[i].tval2 = cheri_tval2; + issue_pcc_ex_o[i].valid = 1'b1; end if ((pcc[i].otype != cva6_cheri_pkg::UNSEALED_CAP) && pcc[i].tag) begin - issue_pcc_ex_o.cause = cva6_cheri_pkg::CAP_EXCEPTION; + issue_pcc_ex_o[i].cause = cva6_cheri_pkg::CAP_EXCEPTION; cheri_tval2.fault_cause = cva6_cheri_pkg::CAP_SEAL_VIOLATION; - issue_pcc_ex_o.tval2 = cheri_tval2; - issue_pcc_ex_o.valid = 1'b1; + issue_pcc_ex_o[i].tval2 = cheri_tval2; + issue_pcc_ex_o[i].valid = 1'b1; end if (!pcc[i].tag) begin - issue_pcc_ex_o.cause = cva6_cheri_pkg::CAP_EXCEPTION; + issue_pcc_ex_o[i].cause = cva6_cheri_pkg::CAP_EXCEPTION; cheri_tval2.fault_cause = cva6_cheri_pkg::CAP_TAG_VIOLATION; - issue_pcc_ex_o.tval2 = cheri_tval2; - issue_pcc_ex_o.valid = 1'b1; + issue_pcc_ex_o[i].tval2 = cheri_tval2; + issue_pcc_ex_o[i].valid = 1'b1; end + if (!issue_instr_valid_i[i] || debug_mode_i) issue_pcc_ex_o[i].valid = 1'b0; end - if (!issue_instr_valid_i || debug_mode_i) issue_pcc_ex_o.valid = 1'b0; end end @@ -509,7 +555,7 @@ module issue_read_operands unique case (issue_instr_i[i].fu) NONE: fu_busy[i] = fus_busy[i].none; ALU: begin - if (CVA6Cfg.SuperscalarEn && !fus_busy[i].alu2) begin + if (CVA6Cfg.SuperscalarEn && use_alu2[i]) begin fu_busy[i] = fus_busy[i].alu2; end else begin fu_busy[i] = fus_busy[i].alu; @@ -632,9 +678,9 @@ module issue_read_operands assign rs3_valid[i] = fwd_res_valid[idx_hzd_rs3[i]]; if (CVA6Cfg.NrRgprPorts == 3) begin - assign rs3_res[i] = rs3[i][CVA6Cfg.XLEN-1:0]; + assign rs3_res[i] = x_to_reg(rs3[i][CVA6Cfg.XLEN-1:0]); end else begin - assign rs3_res[i] = rs3[i][CVA6Cfg.FLen-1:0]; + assign rs3_res[i] = x_to_reg(rs3[i][CVA6Cfg.FLen-1:0]); end end @@ -644,6 +690,8 @@ module issue_read_operands // check that all operands are available, otherwise stall // forward corresponding register always_comb begin : operands_available + alu_bypass = '0; + stall_raw = '{default: stall_i}; stall_rs1 = '{default: stall_i}; stall_rs2 = '{default: stall_i}; @@ -709,7 +757,11 @@ module issue_read_operands ) == is_rd_fpr( issue_instr_i[0].op ))) && issue_instr_i[1].rs1 == issue_instr_i[0].rd && issue_instr_i[1].rs1 != '0) begin - stall_raw[1] = 1'b1; + if (is_alu_bypass) begin + alu_bypass.rs1_from_rd = 1'b1; + end else begin + stall_raw[1] = 1'b1; // RS1[1] NEEDS RD[0] + end end if ((!CVA6Cfg.FpPresent || (is_rs2_fpr( @@ -717,7 +769,11 @@ module issue_read_operands ) == is_rd_fpr( issue_instr_i[0].op ))) && issue_instr_i[1].rs2 == issue_instr_i[0].rd && issue_instr_i[1].rs2 != '0) begin - stall_raw[1] = 1'b1; + if (is_alu_bypass) begin + alu_bypass.rs2_from_rd = 1'b1; + end else begin + stall_raw[1] = 1'b1; // RS2[1] NEEDS RD[0] + end end // Only check clobbered gpr for OFFLOADED instruction @@ -730,6 +786,9 @@ module issue_read_operands issue_instr_i[0].rd == issue_instr_i[1].result[REG_ADDR_SIZE-1:0] : 1'b0) begin stall_raw[1] = 1'b1; end + + // Stall the second instruction if the first one could change bounds. + if (CVA6Cfg.CheriPresent && (issue_instr_i[0].op == ariane_pkg::CJALR)) stall_raw[1] = 1'b1; end // Stall while there is an outstanding change to bounds. @@ -738,10 +797,38 @@ module issue_read_operands end // third operand from fp regfile or gp regfile if NR_RGPR_PORTS == 3 - if (OPERANDS_PER_INSTR == 3) begin : gen_gp_rs3 - assign imm_forward_rs3 = rs3_res[0]; - end else begin : gen_fp_rs3 - assign imm_forward_rs3 = {{CVA6Cfg.XLEN - CVA6Cfg.FLen{1'b0}}, rs3_res[0]}; + for (genvar i = 0; i < CVA6Cfg.NrIssuePorts; i++) begin + if (OPERANDS_PER_INSTR == 3) begin : gen_gp_rs3 + assign imm_forward_rs3[i] = rs3_res[i]; + end else begin : gen_fp_rs3 + assign imm_forward_rs3[i] = {{CVA6Cfg.XLEN - CVA6Cfg.FLen{1'b0}}, rs3_res[i]}; + end + end + + // PCC logic + if (CVA6Cfg.CheriPresent) begin + always_comb begin : pcc_select + pcc_jump_change_valid_n = pcc_jump_change_valid_q; + pcc_jump_change_n = pcc_jump_change_q; + pcc_n = pcc_q; + + if (eret_i) begin + pcc_jump_change_valid_n = 1'b0; + pcc_n = epc_i; + end else if (set_pc_commit_i) begin + pcc_jump_change_valid_n = 1'b0; + pcc_n = pcc_commit_i; + end else if (ex_valid_i) begin + pcc_jump_change_valid_n = 1'b0; + pcc_n = trap_vector_base_i; + end else if (pcc_jump_change_valid_q && backend_empty_i) begin + pcc_jump_change_valid_n = 1'b0; + pcc_n = pcc_jump_change_q; + end else if (resolved_branch_i.valid && resolved_branch_i.is_pcc_change) begin + pcc_jump_change_valid_n = 1'b1; + pcc_jump_change_n = resolved_branch_i.target_address; + end + end end // Forwarding/Output MUX @@ -751,8 +838,6 @@ module issue_read_operands fu_data_n[i].operand_a = operand_a_regfile[i]; fu_data_n[i].operand_b = operand_b_regfile[i]; - pcc_jump_change_n = pcc_jump_change_q; - // immediates are the third operands in the store case // for FP operations, the imm field can also be the third operand from the regfile if (OPERANDS_PER_INSTR == 3) begin @@ -770,29 +855,6 @@ module issue_read_operands fu_data_n[i].rs2 = CVA6Cfg.CheriPresent ? issue_instr_i[i].rs2 : '0; fu_data_n[i].use_ddc = CVA6Cfg.CheriPresent ? issue_instr_i[i].use_ddc : '0; - if (CVA6Cfg.CheriPresent) begin - if (eret_i) pcc_n = epc_i; - else if (set_pc_commit_i) pcc_n = pcc_commit_i; - else if (ex_valid_i) pcc_n = trap_vector_base_i; - else if (pcc_jump_change_valid_q && backend_empty_i) pcc_n = pcc_jump_change_q; - else pcc_n = pcc_q; - - if (eret_i || set_pc_commit_i || ex_valid_i) pcc_jump_change_valid_n = 1'b0; - else if (resolved_branch_i.valid && (cva6_cheri_pkg::set_cap_reg_flags( - resolved_branch_i.target_address, 0 - ) != cva6_cheri_pkg::set_cap_reg_address( - cva6_cheri_pkg::set_cap_reg_flags( - pcc_q, 0 - ), - resolved_branch_i.target_address[CVA6Cfg.XLEN-1:0], - pcc_meta - ))) begin - pcc_jump_change_valid_n = 1'b1; - end else if (backend_empty_i) pcc_jump_change_valid_n = 1'b0; - else pcc_jump_change_valid_n = pcc_jump_change_valid_q; - - if (resolved_branch_i.valid) pcc_jump_change_n = resolved_branch_i.target_address; - end if (CVA6Cfg.RVH) begin tinst_n[i] = issue_instr_i[i].ex.tinst; end @@ -805,7 +867,7 @@ module issue_read_operands fu_data_n[i].operand_b = rs2_res[i]; end if ((CVA6Cfg.FpPresent || (CVA6Cfg.CvxifEn && OPERANDS_PER_INSTR == 3)) && forward_rs3[i]) begin - fu_data_n[i].imm = imm_forward_rs3; + fu_data_n[i].imm = imm_forward_rs3[i]; end // use the PC as operand a @@ -852,7 +914,7 @@ module issue_read_operands if (!issue_instr_i[i].ex.valid && issue_instr_valid_i[i] && issue_ack_o[i]) begin case (issue_instr_i[i].fu) ALU: begin - if (CVA6Cfg.SuperscalarEn && !fus_busy[i].alu2) begin + if (CVA6Cfg.SuperscalarEn && use_alu2[i]) begin alu2_valid_n[i] = 1'b1; end else begin alu_valid_n[i] = 1'b1; @@ -876,12 +938,12 @@ module issue_read_operands default: begin if (issue_instr_i[i].fu == FPU && CVA6Cfg.FpPresent) begin fpu_valid_n[i] = 1'b1; - fpu_fmt_n = orig_instr.rftype.fmt; // fmt bits from instruction - fpu_rm_n = orig_instr.rftype.rm; // rm bits from instruction + fpu_fmt_n = orig_instr[i].rftype.fmt; // fmt bits from instruction + fpu_rm_n = orig_instr[i].rftype.rm; // rm bits from instruction end else if (issue_instr_i[i].fu == FPU_VEC && CVA6Cfg.FpPresent) begin fpu_valid_n[i] = 1'b1; - fpu_fmt_n = orig_instr.rvftype.vfmt; // vfmt bits from instruction - fpu_rm_n = {2'b0, orig_instr.rvftype.repl}; // repl bit from instruction + fpu_fmt_n = orig_instr[i].rvftype.vfmt; // vfmt bits from instruction + fpu_rm_n = {2'b0, orig_instr[i].rvftype.repl}; // repl bit from instruction end else if (issue_instr_i[i].fu == CLU && CVA6Cfg.CheriPresent) begin clu_valid_n[i] = 1'b1; end @@ -1022,8 +1084,7 @@ module issue_read_operands .CVA6Cfg (CVA6Cfg), .DATA_WIDTH (CVA6Cfg.REGLEN), .NR_READ_PORTS(CVA6Cfg.NrRgprPorts), - .ZERO_REG_ZERO(1), - .EN_CHERI_CAP (CVA6Cfg.CheriPresent) + .ZERO_REG_ZERO(1) ) i_ariane_regfile_fpga ( .clk_i, .rst_ni, @@ -1040,7 +1101,8 @@ module issue_read_operands .DATA_WIDTH (CVA6Cfg.REGLEN), .NR_READ_PORTS(CVA6Cfg.NrRgprPorts), .ZERO_REG_ZERO(1), - .EN_CHERI_CAP (CVA6Cfg.CheriPresent) + .ZERO_VAL (ariane_pkg::REG_NULL), + .INIT_VAL (CVA6Cfg.RVFI_DII ? ariane_pkg::REG_ROOT : ariane_pkg::REG_NULL) ) i_ariane_regfile ( .clk_i, .rst_ni, @@ -1068,7 +1130,7 @@ module issue_read_operands }; if (CVA6Cfg.SuperscalarEn) begin - if (!(issue_instr_i[0].fu inside {FPU, FPU_VEC})) begin + if (!(issue_instr_i[0].fu inside {FPU, FPU_VEC} || issue_instr_i[0].op inside {[FLD:FSB]})) begin fp_raddr_pack = { issue_instr_i[1].result[4:0], issue_instr_i[1].rs2[4:0], issue_instr_i[1].rs1[4:0] }; @@ -1167,6 +1229,7 @@ module issue_read_operands end end + assign alu_bypass_n = &issue_ack_o ? alu_bypass : '0; always_ff @(posedge clk_i or negedge rst_ni) begin if (!rst_ni) begin @@ -1176,16 +1239,18 @@ module issue_read_operands end pc_o <= '0; if (CVA6Cfg.CheriPresent) begin - pcc_q <= cva6_cheri_pkg::PCC_ROOT_CAP; + pcc_q <= REG_ROOT; pcc_jump_change_valid_q <= '0; end is_zcmt_o <= '0; is_compressed_instr_o <= 1'b0; branch_predict_o <= {cf_t'(0), {CVA6Cfg.VLEN{1'b0}}}; x_transaction_rejected_o <= 1'b0; + alu_bypass_q <= '0; if (CVA6Cfg.RVFI_DII) dii_id_o <= '0; end else begin fu_data_q <= fu_data_n; + alu_bypass_q <= alu_bypass_n; if (CVA6Cfg.ZKN) begin orig_instr_aes_bits <= {orig_instr_i[0][31:30], orig_instr_i[0][23:20]}; end @@ -1230,20 +1295,6 @@ module issue_read_operands ); end - // FPU does not declare that it will return a result the subsequent cycle so - // it is not possible for issue stage to know when ALU2 can be used if there - // is an FPU. As there are discussions to change the FPU, I did not explore - // its architecture to create this "FPU returns next cycle" signal. Also, a - // "lookahead" optimization should be added to be performant with FPU: when - // issue port 2 is issuing to FPU, issue port 1 should issue to ALU1 instead - // of ALU2 so that FPU is not busy. However, if FPU has a minimum execution - // time of 2 cycles, it is possible to simply not raise fus_busy[1].alu2. - initial begin - assert (!(CVA6Cfg.SuperscalarEn && CVA6Cfg.FpPresent)) - else - $fatal(1, "FPU is not yet supported in superscalar CVA6, see comments above this assertion."); - end - for (genvar i = 0; i < CVA6Cfg.NrIssuePorts; i++) begin assert property (@(posedge clk_i) (branch_valid_q) |-> (!$isunknown( fu_data_q[i].operand_a diff --git a/hw/vendor/cva6_cheri/core/issue_stage.sv b/hw/vendor/cva6_cheri/core/issue_stage.sv index 73f95deb..2b2248e0 100644 --- a/hw/vendor/cva6_cheri/core/issue_stage.sv +++ b/hw/vendor/cva6_cheri/core/issue_stage.sv @@ -63,6 +63,8 @@ module issue_stage output [CVA6Cfg.NrIssuePorts-1:0][CVA6Cfg.REGLEN-1:0] rs2_forwarding_o, // FU data useful to execute instruction - EX_STAGE output fu_data_t [CVA6Cfg.NrIssuePorts-1:0] fu_data_o, + // ALU to ALU bypass control - EX_STAGE + output alu_bypass_t alu_bypass_o, // Program Counter - EX_STAGE output logic [CVA6Cfg.REGLEN-1:0] pc_o, // Program Counter Capability - EX_STAGE @@ -101,12 +103,14 @@ module issue_stage output logic [1:0] fpu_fmt_o, // FPU rm field - EX_STAGE output logic [2:0] fpu_rm_o, + // FPU early valid - EX_STAGE + input logic fpu_early_valid_i, // ALU2 FU is valid - EX_STAGE output logic [CVA6Cfg.NrIssuePorts-1:0] alu2_valid_o, // CSR is valid - EX_STAGE output logic [CVA6Cfg.NrIssuePorts-1:0] csr_valid_o, // CLU is valid - EX_STAGE - output logic clu_valid_o, + output logic [CVA6Cfg.NrIssuePorts-1:0] clu_valid_o, // CVXIF FU is valid - EX_STAGE output logic [CVA6Cfg.NrIssuePorts-1:0] xfu_valid_o, // CVXIF is FU ready - EX_STAGE @@ -211,7 +215,7 @@ module issue_stage logic backend_empty; - exception_t issue_pcc_ex; + exception_t [CVA6Cfg.NrIssuePorts-1:0] issue_pcc_ex; assign issue_instr_hs_o = issue_instr_valid_sb_iro[0] & issue_ack_iro_sb[0]; assign issue_instr_o = issue_instr_sb_iro; @@ -297,6 +301,7 @@ module issue_stage .issue_pcc_ex_o (issue_pcc_ex), .backend_empty_i (backend_empty), .fu_data_o (fu_data_o), + .alu_bypass_o (alu_bypass_o), .rs1_forwarding_o (rs1_forwarding_o), .rs2_forwarding_o (rs2_forwarding_o), .pc_o, @@ -317,6 +322,7 @@ module issue_stage .fpu_valid_o, .fpu_fmt_o, .fpu_rm_o, + .fpu_early_valid_i, .alu2_valid_o, .csr_valid_o, .cvxif_valid_o (xfu_valid_o), diff --git a/hw/vendor/cva6_cheri/core/load_store_unit.sv b/hw/vendor/cva6_cheri/core/load_store_unit.sv index 5083a009..512d3998 100644 --- a/hw/vendor/cva6_cheri/core/load_store_unit.sv +++ b/hw/vendor/cva6_cheri/core/load_store_unit.sv @@ -97,55 +97,55 @@ module load_store_unit output icache_areq_t icache_areq_o, // Current privilege mode - CSR_REGFILE - input riscv::priv_lvl_t priv_lvl_i, + input riscv::priv_lvl_t priv_lvl_i, // Current virtualization mode - CSR_REGFILE - input logic v_i, + input logic v_i, // Privilege level at which load and stores should happen - CSR_REGFILE - input riscv::priv_lvl_t ld_st_priv_lvl_i, + input riscv::priv_lvl_t ld_st_priv_lvl_i, // Virtualization mode at which load and stores should happen - CSR_REGFILE - input logic ld_st_v_i, + input logic ld_st_v_i, // Instruction is a hyp load/store - CSR_REGFILE - output logic csr_hs_ld_st_inst_o, + output logic csr_hs_ld_st_inst_o, // Supervisor User Memory - CSR_REGFILE - input logic sum_i, + input logic sum_i, // Virtual Supervisor User Memory - CSR_REGFILE - input logic vs_sum_i, + input logic vs_sum_i, // Make Executable Readable - CSR_REGFILE - input logic mxr_i, + input logic mxr_i, // Make Executable Readable Virtual Supervisor - CSR_REGFILE - input logic vmxr_i, + input logic vmxr_i, // TO_BE_COMPLETED - TO_BE_COMPLETED - input logic [ CVA6Cfg.PPNW-1:0] satp_ppn_i, + input logic [ CVA6Cfg.PPNW-1:0] satp_ppn_i, // TO_BE_COMPLETED - TO_BE_COMPLETED - input logic [CVA6Cfg.ASID_WIDTH-1:0] asid_i, + input logic [CVA6Cfg.ASID_WIDTH-1:0] asid_i, // TO_BE_COMPLETED - TO_BE_COMPLETED - input logic [ CVA6Cfg.PPNW-1:0] vsatp_ppn_i, + input logic [ CVA6Cfg.PPNW-1:0] vsatp_ppn_i, // TO_BE_COMPLETED - TO_BE_COMPLETED - input logic [CVA6Cfg.ASID_WIDTH-1:0] vs_asid_i, + input logic [CVA6Cfg.ASID_WIDTH-1:0] vs_asid_i, // TO_BE_COMPLETED - TO_BE_COMPLETED - input logic [ CVA6Cfg.PPNW-1:0] hgatp_ppn_i, + input logic [ CVA6Cfg.PPNW-1:0] hgatp_ppn_i, // TO_BE_COMPLETED - TO_BE_COMPLETED - input logic [CVA6Cfg.VMID_WIDTH-1:0] vmid_i, + input logic [CVA6Cfg.VMID_WIDTH-1:0] vmid_i, // Capability load barrier generation - EX_STAGE - input logic cap_ucrg_i, + input logic cap_ucrg_i, // Default Data Capability - CSR_REGFILE - input cva6_cheri_pkg::cap_reg_t ddc_i, + input logic [ CVA6Cfg.REGLEN-1:0] ddc_i, // TO_BE_COMPLETED - TO_BE_COMPLETED - input logic [CVA6Cfg.ASID_WIDTH-1:0] asid_to_be_flushed_i, + input logic [CVA6Cfg.ASID_WIDTH-1:0] asid_to_be_flushed_i, // TO_BE_COMPLETED - TO_BE_COMPLETED - input logic [CVA6Cfg.VMID_WIDTH-1:0] vmid_to_be_flushed_i, + input logic [CVA6Cfg.VMID_WIDTH-1:0] vmid_to_be_flushed_i, // TO_BE_COMPLETED - TO_BE_COMPLETED - input logic [ CVA6Cfg.VLEN-1:0] vaddr_to_be_flushed_i, + input logic [ CVA6Cfg.VLEN-1:0] vaddr_to_be_flushed_i, // TO_BE_COMPLETED - TO_BE_COMPLETED - input logic [ CVA6Cfg.GPLEN-1:0] gpaddr_to_be_flushed_i, + input logic [ CVA6Cfg.GPLEN-1:0] gpaddr_to_be_flushed_i, // TLB flush - CONTROLLER - input logic flush_tlb_i, - input logic flush_tlb_vvma_i, - input logic flush_tlb_gvma_i, + input logic flush_tlb_i, + input logic flush_tlb_vvma_i, + input logic flush_tlb_gvma_i, // Instruction TLB miss - PERF_COUNTERS - output logic itlb_miss_o, + output logic itlb_miss_o, // Data TLB miss - PERF_COUNTERS - output logic dtlb_miss_o, + output logic dtlb_miss_o, // Data cache request output - CACHES input dcache_req_o_t [2:0] dcache_req_ports_i, @@ -193,7 +193,7 @@ module load_store_unit logic g_overflow; logic [(CVA6Cfg.CLEN/8)-1:0] be_i; - assign vaddr_xlen = $unsigned($signed(fu_data_i.imm) + $signed(fu_data_i.operand_a)); + assign vaddr_xlen = $unsigned($signed(fu_data_i.imm) + $signed(reg_to_x(fu_data_i.operand_a))); assign vaddr_i = vaddr_xlen[CVA6Cfg.VLEN-1:0]; // we work with SV39 or SV32, so if VM is enabled, check that all bits [XLEN-1:38] or [XLEN-1:31] are equal assign overflow = (CVA6Cfg.IS_XLEN64 && (!((&vaddr_xlen[CVA6Cfg.XLEN-1:CVA6Cfg.SV-1]) == 1'b1 || (|vaddr_xlen[CVA6Cfg.XLEN-1:CVA6Cfg.SV-1]) == 1'b0))); @@ -207,6 +207,7 @@ module load_store_unit logic ld_valid_i; logic ld_translation_req; logic st_translation_req, cva6_st_translation_req, acc_st_translation_req; + logic st_translation_req_is_actually_lr, cva6_st_translation_req_is_actually_lr; logic ld_translation_req_is_cap; logic st_translation_req_is_cap, cva6_st_translation_req_is_cap; logic [CVA6Cfg.VLEN-1:0] ld_vaddr; @@ -239,6 +240,7 @@ module load_store_unit logic [CVA6Cfg.TRANS_ID_BITS-1:0] ld_trans_id; logic [ CVA6Cfg.REGLEN-1:0] ld_result; logic st_valid; + logic st_is_actually_lr; logic [CVA6Cfg.TRANS_ID_BITS-1:0] st_trans_id; logic [ CVA6Cfg.REGLEN-1:0] st_result; @@ -257,8 +259,6 @@ module load_store_unit cva6_cheri_pkg::cap_meta_data_t check_cap_meta_data; cva6_cheri_pkg::addrw_t check_cap_base; cva6_cheri_pkg::addrwe_t check_cap_top; - cva6_cheri_pkg::addrwe_t check_cap_length; - cva6_cheri_pkg::addrw_t check_cap_offset; cva6_cheri_pkg::addrw_t check_cap_address; logic check_cap_is_sealed; logic check_cap_bounds_root; @@ -301,7 +301,7 @@ module load_store_unit .lsu_req_i(translation_req), .lsu_vaddr_i(mmu_vaddr), .lsu_tinst_i(mmu_tinst), - .lsu_is_store_i(st_translation_req), + .lsu_is_store_i(st_translation_req && !st_translation_req_is_actually_lr), .lsu_is_cap_i(cva6_translation_req_is_cap), .csr_hs_ld_st_inst_o(csr_hs_ld_st_inst_o), .lsu_dtlb_hit_o(dtlb_hit), // send in the same cycle as the request @@ -387,6 +387,7 @@ module load_store_unit assign dcache_req_ports_o[0].data_be = '1; assign dcache_req_ports_o[0].data_size = 2'b11; assign dcache_req_ports_o[0].data_we = 1'b0; + assign dcache_req_ports_o[0].data_wuser = '0; assign dcache_req_ports_o[0].kill_req = '0; assign dcache_req_ports_o[0].tag_valid = 1'b0; assign dcache_req_ports_o[0].strip_tag = 1'b0; @@ -415,7 +416,7 @@ module load_store_unit .lsu_paddr_i (lsu_paddr), .lsu_vaddr_i (mmu_vaddr), .lsu_exception_i (mmu_exception), - .lsu_is_store_i (st_translation_req), + .lsu_is_store_i (st_translation_req && !st_translation_req_is_actually_lr), .lsu_valid_o (translation_valid), .lsu_paddr_o (mmu_paddr), .lsu_exception_o (pmp_exception), @@ -440,7 +441,7 @@ module load_store_unit 64'h8000_0000, 64'h000800000, check_address ) && tval_vaddr[CVA6Cfg.XLEN-1:CVA6Cfg.PLEN] == '0; automatic exception_t rvfi_exception = '0; - rvfi_exception.cause = st_valid ? riscv::ST_ACCESS_FAULT : riscv::LD_ACCESS_FAULT; + rvfi_exception.cause = (st_valid && !st_is_actually_lr) ? riscv::ST_ACCESS_FAULT : riscv::LD_ACCESS_FAULT; rvfi_exception.valid = pmp_translation_valid; if (CVA6Cfg.TvalEn) rvfi_exception.tval = tval_vaddr; lsu_exception = (rvfi_addr_allowed || mmu_exception.valid) ? mmu_exception : rvfi_exception; @@ -471,11 +472,12 @@ module load_store_unit // This logic can be optimized to reduce answer latency and contention always_comb begin // Maintain state - mmu_state_d = mmu_state_q; + mmu_state_d = mmu_state_q; // Serve CVA6 and gate the accelerator by default // MMU input - misaligned_exception = cva6_misaligned_exception; - st_translation_req = cva6_st_translation_req; + misaligned_exception = cva6_misaligned_exception; + st_translation_req = cva6_st_translation_req; + st_translation_req_is_actually_lr = cva6_st_translation_req_is_actually_lr; if (CVA6Cfg.CheriPresent) begin st_translation_req_is_cap = cva6_st_translation_req_is_cap; end @@ -510,7 +512,8 @@ module load_store_unit ACC: begin // MMU input misaligned_exception = acc_mmu_req_i.acc_mmu_misaligned_ex; - st_translation_req = acc_mmu_req_i.acc_mmu_is_store; + st_translation_req = acc_mmu_req_i.acc_mmu_is_store; + st_translation_req_is_actually_lr = 1'b0; if (CVA6Cfg.CheriPresent) begin st_translation_req_is_cap = 1'b0; end @@ -547,18 +550,19 @@ module load_store_unit if (CVA6Cfg.CheriPresent) begin assign st_translation_req_is_cap = cva6_st_translation_req_is_cap; end - assign translation_req = cva6_translation_req; - assign mmu_vaddr = cva6_mmu_vaddr; + assign st_translation_req_is_actually_lr = cva6_st_translation_req_is_actually_lr; + assign translation_req = cva6_translation_req; + assign mmu_vaddr = cva6_mmu_vaddr; // MMU output - assign cva6_translation_valid = translation_valid; - assign cva6_mmu_paddr = mmu_paddr; - assign cva6_mmu_exception = lsu_exception; - assign cva6_dtlb_hit = dtlb_hit; - assign cva6_dtlb_ppn = dtlb_ppn; + assign cva6_translation_valid = translation_valid; + assign cva6_mmu_paddr = mmu_paddr; + assign cva6_mmu_exception = lsu_exception; + assign cva6_dtlb_hit = dtlb_hit; + assign cva6_dtlb_ppn = dtlb_ppn; // No accelerator - assign acc_mmu_resp_o = '0; + assign acc_mmu_resp_o = '0; // Feed forward the lsu_ctrl bypass - assign lsu_ctrl = lsu_ctrl_byp; + assign lsu_ctrl = lsu_ctrl_byp; end logic store_buffer_empty; @@ -586,30 +590,32 @@ module load_store_unit .commit_ready_o, .amo_valid_commit_i, - .valid_o (st_valid), - .trans_id_o (st_trans_id), - .result_o (st_result), - .ex_o (st_ex), + .valid_o (st_valid), + .st_is_actually_lr_o (st_is_actually_lr), + .trans_id_o (st_trans_id), + .result_o (st_result), + .ex_o (st_ex), // MMU port - .translation_req_o (cva6_st_translation_req), - .translation_req_is_cap_o(cva6_st_translation_req_is_cap), - .vaddr_o (st_vaddr), - .rvfi_mem_paddr_o (rvfi_mem_paddr_o), - .tinst_o (st_tinst), - .hs_ld_st_inst_o (st_hs_ld_st_inst), - .hlvx_inst_o (st_hlvx_inst), - .paddr_i (cva6_mmu_paddr), - .ex_i (cva6_mmu_exception), - .dtlb_hit_i (cva6_dtlb_hit), + .translation_req_o (cva6_st_translation_req), + .translation_req_is_cap_o (cva6_st_translation_req_is_cap), + .translation_req_is_actually_lr_o(cva6_st_translation_req_is_actually_lr), + .vaddr_o (st_vaddr), + .rvfi_mem_paddr_o (rvfi_mem_paddr_o), + .tinst_o (st_tinst), + .hs_ld_st_inst_o (st_hs_ld_st_inst), + .hlvx_inst_o (st_hlvx_inst), + .paddr_i (cva6_mmu_paddr), + .ex_i (cva6_mmu_exception), + .dtlb_hit_i (cva6_dtlb_hit), // Load Unit - .page_offset_i (page_offset), - .page_offset_matches_o (page_offset_matches), + .page_offset_i (page_offset), + .page_offset_matches_o (page_offset_matches), // AMOs .amo_req_o, .amo_resp_i, // to memory arbiter - .req_port_i (dcache_req_ports_i[2]), - .req_port_o (dcache_req_ports_o[2]) + .req_port_i (dcache_req_ports_i[2]), + .req_port_o (dcache_req_ports_o[2]) ); logic ld_strip_tag, ld_strip_tag_o; @@ -847,7 +853,7 @@ module load_store_unit end STORE: begin - cva6_misaligned_exception.cause = riscv::ST_ADDR_MISALIGNED; + cva6_misaligned_exception.cause = lsu_ctrl.operation inside {AMO_LRB, AMO_LRH, AMO_LRW, AMO_LRD, AMO_LRC} ? riscv::LD_ADDR_MISALIGNED : riscv::ST_ADDR_MISALIGNED; cva6_misaligned_exception.valid = 1'b1; if (CVA6Cfg.TvalEn) cva6_misaligned_exception.tval = {{CVA6Cfg.XLEN - CVA6Cfg.VLEN{1'b0}}, lsu_ctrl.vaddr}; @@ -876,7 +882,7 @@ module load_store_unit end end STORE: begin - cva6_misaligned_exception.cause = riscv::STORE_PAGE_FAULT; + cva6_misaligned_exception.cause = lsu_ctrl.operation inside {AMO_LRB, AMO_LRH, AMO_LRW, AMO_LRD, AMO_LRC} ? riscv::LOAD_PAGE_FAULT : riscv::STORE_PAGE_FAULT; cva6_misaligned_exception.valid = 1'b1; if (CVA6Cfg.TvalEn) cva6_misaligned_exception.tval = {{CVA6Cfg.XLEN - CVA6Cfg.VLEN{1'b0}}, lsu_ctrl.vaddr}; @@ -905,7 +911,7 @@ module load_store_unit end end STORE: begin - cva6_misaligned_exception.cause = riscv::STORE_GUEST_PAGE_FAULT; + cva6_misaligned_exception.cause = lsu_ctrl.operation inside {AMO_LRB, AMO_LRH, AMO_LRW, AMO_LRD, AMO_LRC} ? riscv:: LOAD_GUEST_PAGE_FAULT : riscv::STORE_GUEST_PAGE_FAULT; cva6_misaligned_exception.valid = 1'b1; if (CVA6Cfg.TvalEn) cva6_misaligned_exception.tval = {{CVA6Cfg.XLEN - CVA6Cfg.VLEN{1'b0}}, lsu_ctrl.vaddr}; @@ -932,8 +938,6 @@ module load_store_unit check_cap_address = lsu_ctrl.vaddr; check_cap_base = cva6_cheri_pkg::get_cap_reg_base(check_cap, check_cap_meta_data); check_cap_top = cva6_cheri_pkg::get_cap_reg_top(check_cap, check_cap_meta_data); - check_cap_length = cva6_cheri_pkg::get_cap_reg_length(check_cap, check_cap_meta_data); - check_cap_offset = cva6_cheri_pkg::get_cap_reg_offset(check_cap, check_cap_meta_data); check_cap_is_sealed = (check_cap.otype != cva6_cheri_pkg::UNSEALED_CAP); check_cap_bounds_root = cva6_cheri_pkg::are_cap_reg_bounds_root(check_cap, check_cap_meta_data); @@ -944,7 +948,10 @@ module load_store_unit always_comb begin : data_cheri_exception automatic cva6_cheri_pkg::cap_tval2_t cheri_tval2; automatic cva6_cheri_pkg::cap_reg_t operand_b; - logic [CVA6Cfg.XLEN-1:0] size; + // "Safe" defaults for the below in case we miss a decoding case + automatic logic [CVA6Cfg.XLEN-1:0] size = cva6_cheri_pkg::CLEN / 8; + automatic logic does_load = 1'b1; + automatic logic does_store = 1'b1; cheri_tval2 = '0; cheri_tval2.fault_type = cva6_cheri_pkg::CAP_DATA_ACCESS_FAULT; @@ -958,25 +965,61 @@ module load_store_unit operand_b = lsu_ctrl.data; unique case (lsu_ctrl.operation) - ariane_pkg::LW, ariane_pkg::SW, - ariane_pkg::LWU, ariane_pkg::FLW, - ariane_pkg::HLV_W, ariane_pkg::HLV_WU, - ariane_pkg::HLVX_WU: begin + LW, LWU, FLW, HLV_W, HLV_WU, HLVX_WU, AMO_LRW: begin + size = 4; + does_store = 1'b0; + end + SW, FSW, HSV_W, AMO_SCW: begin size = 4; + does_load = 1'b0; end - ariane_pkg::LH, ariane_pkg::SH, - ariane_pkg::LHU, ariane_pkg::FLH, - ariane_pkg::HLV_H, ariane_pkg::HLV_HU, - ariane_pkg::HLVX_HU: begin + AMO_SWAPW, AMO_ADDW, + AMO_ANDW, AMO_ORW, AMO_XORW, AMO_MAXW, + AMO_MAXWU, AMO_MINW, AMO_MINWU: begin + size = 4; + end + LH, LHU, FLH, HLV_H, HLV_HU, HLVX_HU, AMO_LRH: begin size = 2; + does_store = 1'b0; end - ariane_pkg::LD, ariane_pkg::SD, ariane_pkg::FLD, ariane_pkg::HSV_D, ariane_pkg::HLV_D: begin + SH, FSH, HSV_H, AMO_SCH: begin + size = 2; + does_load = 1'b0; + end + LD, FLD, HLV_D, AMO_LRD: begin size = 8; + does_store = 1'b0; end - ariane_pkg::LC, ariane_pkg::SC, ariane_pkg::AMO_LRC, ariane_pkg::AMO_SCC: begin + SD, FSD, HSV_D, AMO_SCD: begin + size = 8; + does_load = 1'b0; + end + AMO_SWAPD, AMO_ADDD, + AMO_ANDD, AMO_ORD, AMO_XORD, AMO_MAXD, + AMO_MAXDU, AMO_MIND, AMO_MINDU: begin + size = 8; + end + LC, AMO_LRC: begin + size = cva6_cheri_pkg::CLEN / 8; + does_store = 1'b0; + end + SC, AMO_SCC: begin size = cva6_cheri_pkg::CLEN / 8; + does_load = 1'b0; + end + AMO_SWAPC: begin + size = cva6_cheri_pkg::CLEN / 8; + end + LB, LBU, AMO_LRB, FLB, HLV_B, HLV_BU: begin + size = 1; + does_store = 1'b0; + end + SB, AMO_SCB, FSB, HSV_B: begin + size = 1; + does_load = 1'b0; + end + default: begin end - default: size = 1; endcase if (lsu_ctrl.valid && !debug_mode_i) begin @@ -985,12 +1028,12 @@ module load_store_unit cheri_exception.valid = 1'b1; end - if (!check_cap.hperms.permit_load && (lsu_ctrl.fu == LOAD)) begin + if (!check_cap.hperms.permit_load && does_load) begin cheri_tval2.fault_cause = cva6_cheri_pkg::CAP_PERM_VIOLATION; cheri_exception.valid = 1'b1; end - if (!check_cap.hperms.permit_store && (lsu_ctrl.fu == STORE)) begin + if (!check_cap.hperms.permit_store && does_store) begin cheri_tval2.fault_cause = cva6_cheri_pkg::CAP_PERM_VIOLATION; cheri_exception.valid = 1'b1; end @@ -1004,7 +1047,7 @@ module load_store_unit cheri_tval2.fault_cause = cva6_cheri_pkg::CAP_TAG_VIOLATION; cheri_exception.valid = 1'b1; end - cheri_exception.tval2 = cheri_tval2; + cheri_exception.tval2 = {{CVA6Cfg.GPLEN - 22{1'b0}}, cheri_tval2}; end end end diff --git a/hw/vendor/cva6_cheri/core/load_unit.sv b/hw/vendor/cva6_cheri/core/load_unit.sv index 4cb31b5d..30ba145c 100644 --- a/hw/vendor/cva6_cheri/core/load_unit.sv +++ b/hw/vendor/cva6_cheri/core/load_unit.sv @@ -242,6 +242,8 @@ module load_unit CVA6Cfg.DCACHE_INDEX_WIDTH]; // request id = index of the load buffer's entry assign req_port_o.data_id = ldbuf_windex; + // user field not used + assign req_port_o.data_wuser = '0; // strip tag request assign req_port_o.strip_tag = CVA6Cfg.CheriPresent ? strip_tag_i : 1'b0; // directly forward exception fields (valid bit is set below) @@ -540,7 +542,7 @@ module load_unit // realign as needed assign shifted_data_wide = req_port_i.data_rdata >> {ldbuf_rdata.address_offset, 3'b000}; - assign shifted_data = shifted_data_wide[CVA6Cfg.XLEN-1:0]; // If not using cap, we need at most XLEN bits. + assign shifted_data = shifted_data_wide[CVA6Cfg.XLEN-1:0]; if (CVA6Cfg.CheriPresent) begin : gen_cheri_load_data assign data = {req_port_i.data_ruser, req_port_i.data_rdata}; end @@ -584,66 +586,38 @@ module load_unit // result mux always_comb begin - result_o = (CVA6Cfg.CheriPresent) ? cva6_cheri_pkg::REG_NULL_CAP : '{default: 0}; + result_o = REG_NULL; unique case (ldbuf_rdata.operation) ariane_pkg::LW, ariane_pkg::LWU, ariane_pkg::HLV_W, ariane_pkg::HLV_WU, ariane_pkg::HLVX_WU: - result_o = cva6_cheri_pkg::set_cap_reg_addr( - cva6_cheri_pkg::REG_NULL_CAP, {{CVA6Cfg.XLEN - 32{rdata_sign_bit}}, shifted_data[31:0]}); + result_o = x_to_reg({{CVA6Cfg.XLEN - 32{rdata_sign_bit}}, shifted_data[31:0]}); ariane_pkg::LH, ariane_pkg::LHU, ariane_pkg::HLV_H, ariane_pkg::HLV_HU, ariane_pkg::HLVX_HU: - result_o = cva6_cheri_pkg::set_cap_reg_addr( - cva6_cheri_pkg::REG_NULL_CAP, - {{CVA6Cfg.XLEN - 32 + 16{rdata_sign_bit}}, shifted_data[15:0]}); + result_o = x_to_reg({{CVA6Cfg.XLEN - 32 + 16{rdata_sign_bit}}, shifted_data[15:0]}); ariane_pkg::LB, ariane_pkg::LBU, ariane_pkg::HLV_B, ariane_pkg::HLV_BU: - result_o = cva6_cheri_pkg::set_cap_reg_addr( - cva6_cheri_pkg::REG_NULL_CAP, - {{CVA6Cfg.XLEN - 32 + 24{rdata_sign_bit}}, shifted_data[7:0]}); + result_o = x_to_reg({{CVA6Cfg.XLEN - 32 + 24{rdata_sign_bit}}, shifted_data[7:0]}); default: begin // FLW, FLH and FLB have been defined here in default case to improve Code Coverage if (CVA6Cfg.FpPresent) begin unique case (ldbuf_rdata.operation) ariane_pkg::FLW: begin - result_o = cva6_cheri_pkg::set_cap_reg_addr( - cva6_cheri_pkg::REG_NULL_CAP, - { - {CVA6Cfg.XLEN - 32{rdata_sign_bit}}, shifted_data[31:0] - } - ); + result_o = x_to_reg({{CVA6Cfg.XLEN - 32{rdata_sign_bit}}, shifted_data[31:0]}); end ariane_pkg::FLH: begin - result_o = cva6_cheri_pkg::set_cap_reg_addr( - cva6_cheri_pkg::REG_NULL_CAP, - { - {CVA6Cfg.XLEN - 32 + 16{rdata_sign_bit}}, shifted_data[15:0] - } - ); + result_o = x_to_reg({{CVA6Cfg.XLEN - 32 + 16{rdata_sign_bit}}, shifted_data[15:0]}); end ariane_pkg::FLB: begin - result_o = cva6_cheri_pkg::set_cap_reg_addr( - cva6_cheri_pkg::REG_NULL_CAP, - { - {CVA6Cfg.XLEN - 32 + 24{rdata_sign_bit}}, shifted_data[7:0] - } - ); + result_o = x_to_reg({{CVA6Cfg.XLEN - 32 + 24{rdata_sign_bit}}, shifted_data[7:0]}); end default: begin - result_o = cva6_cheri_pkg::set_cap_reg_addr(cva6_cheri_pkg::REG_NULL_CAP, - shifted_data[CVA6Cfg.XLEN-1:0]); + result_o = x_to_reg(shifted_data); end endcase end else begin - result_o = cva6_cheri_pkg::set_cap_reg_addr(cva6_cheri_pkg::REG_NULL_CAP, - shifted_data[CVA6Cfg.XLEN-1:0]); + result_o = x_to_reg(shifted_data); end - if (CVA6Cfg.CheriPresent) begin - if (ldbuf_rdata.operation == ariane_pkg::LC) begin - // Convert memory capability to register capability - result_o = cva6_cheri_pkg::cap_mem_to_cap_reg(data); - end else begin - result_o = cva6_cheri_pkg::set_cap_reg_addr(cva6_cheri_pkg::REG_NULL_CAP, shifted_data); - end - end else begin - result_o = shifted_data[CVA6Cfg.XLEN-1:0]; + if (CVA6Cfg.CheriPresent && ldbuf_rdata.operation == ariane_pkg::LC) begin + // Convert memory capability to register capability + result_o = cva6_cheri_pkg::cap_mem_to_cap_reg(data); end end endcase diff --git a/hw/vendor/cva6_cheri/core/mult.sv b/hw/vendor/cva6_cheri/core/mult.sv index 8ab59762..4e930088 100644 --- a/hw/vendor/cva6_cheri/core/mult.sv +++ b/hw/vendor/cva6_cheri/core/mult.sv @@ -62,8 +62,8 @@ module mult .rst_ni, .trans_id_i (fu_data_i.trans_id), .operation_i (fu_data_i.operation), - .operand_a_i (fu_data_i.operand_a[CVA6Cfg.XLEN-1:0]), - .operand_b_i (fu_data_i.operand_b[CVA6Cfg.XLEN-1:0]), + .operand_a_i (reg_to_x(fu_data_i.operand_a)), + .operand_b_i (reg_to_x(fu_data_i.operand_b)), .result_o (mul_result), .mult_valid_i (mul_valid_op), .mult_valid_o (mul_valid), diff --git a/hw/vendor/cva6_cheri/core/multiplier.sv b/hw/vendor/cva6_cheri/core/multiplier.sv index b6c44f7f..7ae4b50a 100644 --- a/hw/vendor/cva6_cheri/core/multiplier.sv +++ b/hw/vendor/cva6_cheri/core/multiplier.sv @@ -144,6 +144,9 @@ module multiplier clmulr_q <= clmulr_d; end end + end else begin + assign clmul_q = '0; + assign clmulr_q = '0; end // ----------------------- // Output pipeline register diff --git a/hw/vendor/cva6_cheri/core/pmp/src/pmp_data_if.sv b/hw/vendor/cva6_cheri/core/pmp/src/pmp_data_if.sv index fc325db2..7304a6f6 100644 --- a/hw/vendor/cva6_cheri/core/pmp/src/pmp_data_if.sv +++ b/hw/vendor/cva6_cheri/core/pmp/src/pmp_data_if.sv @@ -78,18 +78,25 @@ module pmp_data_if // if it didn't match any execute region throw an `Instruction Access Fault` (PMA) // or if PMP reject the access - if (!match_any_execute_region || !pmp_if_allow) begin - icache_areq_o.fetch_exception.cause = riscv::INSTR_ACCESS_FAULT; - icache_areq_o.fetch_exception.valid = 1'b1; - // For exception, the virtual address is required for tval, if no MMU is - // instantiated then it will be equal to physical address - if (CVA6Cfg.TvalEn) begin - icache_areq_o.fetch_exception.tval = fetch_vaddr_xlen; - end - if (CVA6Cfg.RVH) begin - icache_areq_o.fetch_exception.tval2 = '0; - icache_areq_o.fetch_exception.tinst = '0; - icache_areq_o.fetch_exception.gva = v_i; + // Per RISCV privilege spec, a page fault has higher priority than access + // fault, therefore do not change the exception type in case of double + // exception + if (icache_areq_i.fetch_valid) begin + if (icache_areq_o.fetch_exception.cause != riscv::INSTR_PAGE_FAULT) begin + if (!match_any_execute_region || !pmp_if_allow) begin + icache_areq_o.fetch_exception.cause = riscv::INSTR_ACCESS_FAULT; + icache_areq_o.fetch_exception.valid = 1'b1; + // For exception, the virtual address is required for tval, if no MMU is + // instantiated then it will be equal to physical address + if (CVA6Cfg.TvalEn) begin + icache_areq_o.fetch_exception.tval = fetch_vaddr_xlen; + end + if (CVA6Cfg.RVH) begin + icache_areq_o.fetch_exception.tval2 = '0; + icache_areq_o.fetch_exception.tinst = '0; + icache_areq_o.fetch_exception.gva = v_i; + end + end end end end diff --git a/hw/vendor/cva6_cheri/core/scoreboard.sv b/hw/vendor/cva6_cheri/core/scoreboard.sv index a59c490c..8e5e283e 100644 --- a/hw/vendor/cva6_cheri/core/scoreboard.sv +++ b/hw/vendor/cva6_cheri/core/scoreboard.sv @@ -220,7 +220,8 @@ module scoreboard #( mem_n[trans_id_i[i]].sbe.result = wbdata_i[i]; // save the target address of a branch (needed for debug in commit stage) if (CVA6Cfg.DebugEn || CVA6Cfg.RVFI_DII) begin - mem_n[trans_id_i[i]].sbe.bp.predict_address = resolved_branch_i.target_address[CVA6Cfg.XLEN-1:0]; + mem_n[trans_id_i[i]].sbe.bp.predict_address = + ariane_pkg::reg_to_x(resolved_branch_i.target_address); end if (mem_n[trans_id_i[i]].sbe.fu == ariane_pkg::CVXIF) begin if (x_we_i) mem_n[trans_id_i[i]].sbe.rd = x_rd_i; @@ -307,7 +308,7 @@ module scoreboard #( end assign fwd_o.still_issued = still_issued; - assign fwd_o.issue_pointer = issue_pointer; + assign fwd_o.issue_pointer = issue_pointer[0]; assign fwd_o.wb = wb; for (genvar i = 0; i < CVA6Cfg.NR_SB_ENTRIES; i++) begin assign fwd_o.sbe[i] = mem_q[i].sbe; diff --git a/hw/vendor/cva6_cheri/core/store_unit.sv b/hw/vendor/cva6_cheri/core/store_unit.sv index e55b925f..51e407a3 100644 --- a/hw/vendor/cva6_cheri/core/store_unit.sv +++ b/hw/vendor/cva6_cheri/core/store_unit.sv @@ -49,6 +49,8 @@ module store_unit input logic amo_valid_commit_i, // Store result is valid - ISSUE_STAGE output logic valid_o, + // Store result is actually for an lr - TO_BE_COMPLETED + output logic st_is_actually_lr_o, // Transaction ID - ISSUE_STAGE output logic [CVA6Cfg.TRANS_ID_BITS-1:0] trans_id_o, // Store result - ISSUE_STAGE @@ -59,6 +61,8 @@ module store_unit output logic translation_req_o, // Addredd translation request is tagged data - TO_BE_COMPLETED output logic translation_req_is_cap_o, + // Translation req is actually for an lr - TO_BE_COMPLETED + output logic translation_req_is_actually_lr_o, // Virtual address - TO_BE_COMPLETED output logic [CVA6Cfg.VLEN-1:0] vaddr_o, // RVFI information - RVFI @@ -184,16 +188,22 @@ module store_unit assign trans_id_o = trans_id_q; // transaction id from previous cycle always_comb begin : store_control - translation_req_o = 1'b0; - valid_o = 1'b0; - st_valid = 1'b0; - st_valid_without_flush = 1'b0; - pop_st_o = 1'b0; - ex_o = ex_i; - cap_translation_req_d = cap_translation_req_q; + translation_req_o = 1'b0; + translation_req_is_actually_lr_o = 1'b0; + valid_o = 1'b0; + st_is_actually_lr_o = 1'b0; + st_valid = 1'b0; + st_valid_without_flush = 1'b0; + pop_st_o = 1'b0; + ex_o = ex_i; + cap_translation_req_d = cap_translation_req_q; if (CVA6Cfg.RVFI_DII && amo_op_q == AMO_SC && ex_i.cause == riscv::ST_ACCESS_FAULT) begin ex_o.valid = 1'b0; end + if (amo_op_q == AMO_LR) begin + st_is_actually_lr_o = 1'b1; + translation_req_is_actually_lr_o = 1'b1; + end trans_id_n = lsu_ctrl_i.trans_id; state_d = state_q; diff --git a/hw/vendor/cva6_cheri/core/trigger_module.sv b/hw/vendor/cva6_cheri/core/trigger_module.sv new file mode 100644 index 00000000..f900ae85 --- /dev/null +++ b/hw/vendor/cva6_cheri/core/trigger_module.sv @@ -0,0 +1,501 @@ +module trigger_module + import ariane_pkg::*; + import triggers_pkg::*; +#( + parameter config_pkg::cva6_cfg_t CVA6Cfg = config_pkg::cva6_cfg_empty, + parameter type exception_t = logic, + parameter type scoreboard_entry_t = logic, + parameter int unsigned N_Triggers = 4 +) ( + input logic clk_i, + input logic rst_ni, + input scoreboard_entry_t commit_instr_i, + input logic [CVA6Cfg.NrCommitPorts-1:0] commit_ack_i, + input exception_t ex_i, + input riscv::priv_lvl_t priv_lvl_i, + input logic debug_mode_i, + input logic mret_i, + input logic sret_i, + input logic [CVA6Cfg.VLEN-1:0] vaddr_from_lsu_i, + input logic [CVA6Cfg.NrIssuePorts-1:0][31:0] orig_instr_i, + input logic [CVA6Cfg.XLEN-1:0] store_result_i, + + input logic [CVA6Cfg.XLEN-1:0] scontext_i, + + input logic [CVA6Cfg.XLEN-1:0] tdata1_i, + input logic [CVA6Cfg.XLEN-1:0] tdata2_i, + input logic [CVA6Cfg.XLEN-1:0] tdata3_i, + input logic [CVA6Cfg.XLEN-1:0] tselect_i, + input logic tselect_we, + input logic tdata1_we, + input logic tdata2_we, + input logic tdata3_we, + + output logic [CVA6Cfg.XLEN-1:0] tselect_o, + output logic [CVA6Cfg.XLEN-1:0] tdata1_o, + output logic [CVA6Cfg.XLEN-1:0] tdata2_o, + output logic [CVA6Cfg.XLEN-1:0] tdata3_o, + output logic flush_o, + + output logic break_from_trigger_o, + output logic debug_from_trigger_o, + output logic debug_from_mcontrol_o +); + + // Trigger Module Helpers + logic [N_Triggers-1:0] tselect_q, tselect_d; + logic [3:0] trigger_type_q[N_Triggers], trigger_type_d[N_Triggers]; + logic [N_Triggers-1:0] priv_match, scontext_match; + textra32_tdata3_t textra32_tdata3_q[N_Triggers], textra32_tdata3_d[N_Triggers]; + textra64_tdata3_t textra64_tdata3_q[N_Triggers], textra64_tdata3_d[N_Triggers]; + logic [CVA6Cfg.XLEN-1:0] tdata2_q[N_Triggers], tdata2_d[N_Triggers]; + logic mcontrol6_debug_d, mcontrol6_debug_q, matched; + logic e_matched_q, e_matched_d; + logic mret_reg_q, mret_reg_d; + logic break_from_trigger_q, break_from_trigger_d; + logic in_trap_handler_q, in_trap_handler_d; + logic debug_from_trigger_q, debug_from_trigger_d; + icount32_tdata1_t icount32_tdata1_q[N_Triggers], icount32_tdata1_d[N_Triggers]; + mcontrol6_32_tdata1_t mcontrol6_32_tdata1_q[N_Triggers], mcontrol6_32_tdata1_d[N_Triggers]; + etrigger32_tdata1_t etrigger32_tdata1_q[N_Triggers], etrigger32_tdata1_d[N_Triggers]; + itrigger32_tdata1_t itrigger32_tdata1_q[N_Triggers], itrigger32_tdata1_d[N_Triggers]; + + + // Trigger CSRs write/update logic + always_comb begin : write_path + // Trigger module CSRs + if (tselect_we) begin + tselect_d = (tselect_i < N_Triggers) ? tselect_i[$clog2(N_Triggers)-1:0] : tselect_q; + end + if (tdata1_we) begin + if ((tdata1_i[31:28] == 4'd3 && CVA6Cfg.IS_XLEN32) || (CVA6Cfg.IS_XLEN64 && tdata1_i[63:60] == 4'd3)) begin + trigger_type_d[tselect_q] = (CVA6Cfg.IS_XLEN32) ? tdata1_i[31:28] : tdata1_i[63:60]; + icount32_tdata1_d[tselect_q].t_type = (CVA6Cfg.IS_XLEN32) ? ((tdata1_i[31:28] == 4'd3 || tdata1_i[31:28] == 4'd15) ? tdata1_i[31:28] : trigger_type_q[tselect_q]) : ((tdata1_i[63:60] == 4'd3 || tdata1_i[63:60] == 4'd15) ? tdata1_i[63:60] : trigger_type_q[tselect_q]); + icount32_tdata1_d[tselect_q].dmode = (CVA6Cfg.IS_XLEN32) ? tdata1_i[27] : tdata1_i[59]; + icount32_tdata1_d[tselect_q].vs = 0; + icount32_tdata1_d[tselect_q].vu = 0; + icount32_tdata1_d[tselect_q].hit = tdata1_i[24]; + icount32_tdata1_d[tselect_q].count = tdata1_i[23:10]; + icount32_tdata1_d[tselect_q].m = tdata1_i[9]; + icount32_tdata1_d[tselect_q].pending = tdata1_i[8]; + icount32_tdata1_d[tselect_q].s = tdata1_i[7]; + icount32_tdata1_d[tselect_q].u = tdata1_i[6]; + icount32_tdata1_d[tselect_q].action = tdata1_i[5:0]; + flush_o = 1'b1; + end else if ((CVA6Cfg.IS_XLEN32 && tdata1_i[31:28] == 4'd6) || (CVA6Cfg.IS_XLEN64 && tdata1_i[63:60] == 4'd6)) begin + trigger_type_d[tselect_q] = (CVA6Cfg.IS_XLEN32) ? tdata1_i[31:28] : tdata1_i[63:60]; + mcontrol6_32_tdata1_d[tselect_q].t_type = (CVA6Cfg.IS_XLEN32) ? ((tdata1_i[31:28] == 4'd6 || tdata1_i[31:28] == 4'd15) ? tdata1_i[31:28] : trigger_type_q[tselect_q]) : ((tdata1_i[63:60] == 4'd6 || tdata1_i[63:60] == 4'd15) ? tdata1_i[63:60] : trigger_type_q[tselect_q]); + mcontrol6_32_tdata1_d[tselect_q].dmode = (CVA6Cfg.IS_XLEN32) ? tdata1_i[27] : tdata1_i[59]; + mcontrol6_32_tdata1_d[tselect_q].uncertain = 0; + mcontrol6_32_tdata1_d[tselect_q].hit1 = tdata1_i[25]; + mcontrol6_32_tdata1_d[tselect_q].vs = 0; + mcontrol6_32_tdata1_d[tselect_q].vu = 0; + mcontrol6_32_tdata1_d[tselect_q].hit0 = tdata1_i[22]; + mcontrol6_32_tdata1_d[tselect_q].select = tdata1_i[21]; + mcontrol6_32_tdata1_d[tselect_q].zeroes = '0; + mcontrol6_32_tdata1_d[tselect_q].size = tdata1_i[18:16]; + mcontrol6_32_tdata1_d[tselect_q].action = tdata1_i[15:12]; + mcontrol6_32_tdata1_d[tselect_q].chain = 0; + mcontrol6_32_tdata1_d[tselect_q].match = tdata1_i[10:7]; + mcontrol6_32_tdata1_d[tselect_q].m = tdata1_i[6]; + mcontrol6_32_tdata1_d[tselect_q].uncertainen = 0; + mcontrol6_32_tdata1_d[tselect_q].s = tdata1_i[4]; + mcontrol6_32_tdata1_d[tselect_q].u = tdata1_i[3]; + mcontrol6_32_tdata1_d[tselect_q].execute = tdata1_i[2]; + mcontrol6_32_tdata1_d[tselect_q].store = tdata1_i[1]; + mcontrol6_32_tdata1_d[tselect_q].load = tdata1_i[0]; + flush_o = 1'b1; + end else if ((tdata1_i[31:28] == 4'd5 && CVA6Cfg.IS_XLEN32) || (tdata1_i[63:60] == 4'd5 && CVA6Cfg.IS_XLEN64)) begin + trigger_type_d[tselect_q] = (CVA6Cfg.IS_XLEN32) ? tdata1_i[31:28] : tdata1_i[63:60]; + etrigger32_tdata1_d[tselect_q].t_type = (CVA6Cfg.IS_XLEN32) ? ((tdata1_i[31:28] == 4'd5 || tdata1_i[31:28] == 4'd15) ? tdata1_i[31:28] : trigger_type_q[tselect_q]) : ((tdata1_i[63:60] == 4'd5 || tdata1_i[63:60] == 4'd15) ? tdata1_i[63:60] : trigger_type_q[tselect_q]); + etrigger32_tdata1_d[tselect_q].dmode = (CVA6Cfg.IS_XLEN32) ? tdata1_i[27] : tdata1_i[59]; + etrigger32_tdata1_d[tselect_q].hit = (CVA6Cfg.IS_XLEN32) ? tdata1_i[26] : tdata1_i[58]; + etrigger32_tdata1_d[tselect_q].zeroes = '0; + etrigger32_tdata1_d[tselect_q].vs = 0; + etrigger32_tdata1_d[tselect_q].vu = 0; + etrigger32_tdata1_d[tselect_q].zeroed = 0; + etrigger32_tdata1_d[tselect_q].m = tdata1_i[9]; + etrigger32_tdata1_d[tselect_q].zero = 0; + etrigger32_tdata1_d[tselect_q].s = tdata1_i[7]; + etrigger32_tdata1_d[tselect_q].u = tdata1_i[6]; + etrigger32_tdata1_d[tselect_q].action = tdata1_i[5:0]; + end else if ((tdata1_i[31:28] == 4'd4 && CVA6Cfg.IS_XLEN32) || (tdata1_i[63:60] == 4'd4 && CVA6Cfg.IS_XLEN64)) begin + trigger_type_d[tselect_q] = (CVA6Cfg.IS_XLEN32) ? tdata1_i[31:28] : tdata1_i[63:60]; + itrigger32_tdata1_d[tselect_q].t_type = (CVA6Cfg.IS_XLEN32) ? ((tdata1_i[31:28] == 4'd4 || tdata1_i[31:28] == 4'd15) ? tdata1_i[31:28] : trigger_type_q[tselect_q]) : ((tdata1_i[63:60] == 4'd4 || tdata1_i[63:60] == 4'd15) ? tdata1_i[63:60] : trigger_type_q[tselect_q]); + itrigger32_tdata1_d[tselect_q].dmode = (CVA6Cfg.IS_XLEN32) ? tdata1_i[27] : tdata1_i[59]; + itrigger32_tdata1_d[tselect_q].hit = (CVA6Cfg.IS_XLEN32) ? tdata1_i[26] : tdata1_i[58]; + itrigger32_tdata1_d[tselect_q].zeroed = '0; + itrigger32_tdata1_d[tselect_q].vs = 0; + itrigger32_tdata1_d[tselect_q].vu = 0; + itrigger32_tdata1_d[tselect_q].nmi = 0; + itrigger32_tdata1_d[tselect_q].m = tdata1_i[9]; + itrigger32_tdata1_d[tselect_q].zero = 0; + itrigger32_tdata1_d[tselect_q].s = tdata1_i[7]; + itrigger32_tdata1_d[tselect_q].u = tdata1_i[6]; + itrigger32_tdata1_d[tselect_q].action = tdata1_i[5:0]; + end + end + if (tdata2_we) begin + tdata2_d[tselect_q] = tdata2_i; + end + if (tdata3_we) begin + if (CVA6Cfg.XLEN == 32) begin // textra32 + textra32_tdata3_d[tselect_q].mhvalue = '0; + textra32_tdata3_d[tselect_q].mhselect = '0; + textra32_tdata3_d[tselect_q].zeroes = '0; + textra32_tdata3_d[tselect_q].sbytemask = tdata3_i[19:18]; + textra32_tdata3_d[tselect_q].svalue = tdata3_i[17:2]; + textra32_tdata3_d[tselect_q].sselect = tdata3_i[1:0]; + end + if (CVA6Cfg.XLEN == 64) begin // textra64 + textra64_tdata3_d[tselect_q].mhvalue = '0; + textra64_tdata3_d[tselect_q].mhselect = '0; + textra64_tdata3_d[tselect_q].zeroes = '0; + textra64_tdata3_d[tselect_q].sbytemask = tdata3_i[39:36]; + textra64_tdata3_d[tselect_q].zero_field = '0; + textra64_tdata3_d[tselect_q].svalue = tdata3_i[33:2]; + textra64_tdata3_d[tselect_q].sselect = tdata3_i[1:0]; + end + end + + + // Triggers Match Logic + if (CVA6Cfg.SDTRIG) begin + for (int i = 0; i < N_Triggers; i++) begin + priv_match[i] = 1'b0; + matched = 1'b0; + // icount match logic + if (trigger_type_d[i] == 4'd3 && CVA6Cfg.Icount) begin + break_from_trigger_d = 1'b0; + case(priv_lvl_i) // trigger will only fire if current priv lvl is same as the trigger configuration + riscv::PRIV_LVL_M: if (icount32_tdata1_d[i].m) priv_match[i] = 1'b1; + riscv::PRIV_LVL_S: if (icount32_tdata1_d[i].s) priv_match[i] = 1'b1; + riscv::PRIV_LVL_U: if (icount32_tdata1_d[i].u) priv_match[i] = 1'b1; + default: priv_match[i] = 1'b0; + endcase + // S_MODE context match check + if (priv_lvl_i == riscv::PRIV_LVL_S && icount32_tdata1_d[i].s) begin + if (CVA6Cfg.IS_XLEN32) begin + scontext_match[i] = match_scontext( + scontext_i, + textra32_tdata3_d[i].sselect, + textra32_tdata3_d[i].sbytemask, + textra32_tdata3_d[i].svalue, + 1'b0 + ); + end else begin + scontext_match[i] = match_scontext( + scontext_i, + textra64_tdata3_d[i].sselect, + textra64_tdata3_d[i].sbytemask, + textra64_tdata3_d[i].svalue, + 1'b1 + ); + end + priv_match[i] &= scontext_match[i]; + end + if (ex_i.valid) begin + in_trap_handler_d = 1'b1; + icount32_tdata1_d[i].count = icount32_tdata1_q[i].count - 1; + end + if (commit_ack_i && (mret_i || sret_i)) in_trap_handler_d = 1'b0; + if (commit_ack_i && !in_trap_handler_d && (icount32_tdata1_q[i].count != 0)) begin + icount32_tdata1_d[i].count = icount32_tdata1_q[i].count - 1; + end + if ((icount32_tdata1_d[i].count == 0) && priv_match[i] && !icount32_tdata1_q[i].pending && !icount32_tdata1_q[i].hit) begin + icount32_tdata1_d[i].pending = 1'b1; + case (icount32_tdata1_d[i].action) + 6'd0: break_from_trigger_d = 1'b1; //breakpoint + 6'd1: debug_from_trigger_d = 1'b1; //into debug mode + default: ; + endcase + end + if (break_from_trigger_q) begin + icount32_tdata1_d[i].hit = 1'b1; + icount32_tdata1_d[i].pending = 1'b0; + end + if (debug_mode_i && icount32_tdata1_d[i].pending) begin + icount32_tdata1_d[i].pending = 1'b0; + icount32_tdata1_d[i].hit = 1'b1; + debug_from_trigger_d = 1'b0; + end + end + // mcontrol6 match logic + if (trigger_type_d[i] == 4'd6 && CVA6Cfg.Mcontrol6) begin + case(priv_lvl_i) // trigger will only fire if current priv lvl is same as the trigger configuration + riscv::PRIV_LVL_M: if (mcontrol6_32_tdata1_d[i].m) priv_match[i] = 1'b1; + riscv::PRIV_LVL_S: if (mcontrol6_32_tdata1_d[i].s) priv_match[i] = 1'b1; + riscv::PRIV_LVL_U: if (mcontrol6_32_tdata1_d[i].u) priv_match[i] = 1'b1; + default: priv_match[i] = 1'b0; + endcase + // S_MODE context match check + if (priv_lvl_i == riscv::PRIV_LVL_S && mcontrol6_32_tdata1_d[i].s) begin + if (CVA6Cfg.IS_XLEN32) begin + scontext_match[i] = match_scontext( + scontext_i, + textra32_tdata3_d[i].sselect, + textra32_tdata3_d[i].sbytemask, + textra32_tdata3_d[i].svalue, + 1'b0 + ); + end else begin + scontext_match[i] = match_scontext( + scontext_i, + textra64_tdata3_d[i].sselect, + textra64_tdata3_d[i].sbytemask, + textra64_tdata3_d[i].svalue, + 1'b1 + ); + end + priv_match[i] &= scontext_match[i]; + end + // execute with address + if (mcontrol6_32_tdata1_d[i].execute && !mcontrol6_32_tdata1_d[i].select) begin + case (mcontrol6_32_tdata1_d[i].match) + 4'd0: matched = (tdata2_d[i] == commit_instr_i.pc && commit_ack_i); + 4'd1: matched = (napot_match(tdata2_d[i], commit_instr_i.pc) && commit_ack_i); + 4'd8: matched = (tdata2_d[i] != commit_instr_i.pc && commit_ack_i); + endcase + end + // execute with instruction + if (mcontrol6_32_tdata1_d[i].execute && mcontrol6_32_tdata1_d[i].select) begin + case (mcontrol6_32_tdata1_d[i].match) + 4'd0: matched = (tdata2_d[i] == orig_instr_i); + 4'd1: matched = (napot_match(tdata2_d[i], orig_instr_i)); + 4'd8: matched = (tdata2_d[i] != orig_instr_i); + endcase + end + // store with data + if (mcontrol6_32_tdata1_d[i].store && mcontrol6_32_tdata1_d[i].select) begin + case (mcontrol6_32_tdata1_d[i].match) + 4'd0: matched = (tdata2_d[i] == store_result_i); + 4'd1: matched = (napot_match(tdata2_d[i], store_result_i)); + 4'd8: matched = (tdata2_d[i] != store_result_i); + endcase + end + // store with address + if (mcontrol6_32_tdata1_d[i].store && !mcontrol6_32_tdata1_d[i].select) begin + case (mcontrol6_32_tdata1_d[i].match) + 4'd0: matched = (tdata2_d[i] == vaddr_from_lsu_i); + 4'd1: matched = (napot_match(tdata2_d[i], vaddr_from_lsu_i)); + 4'd8: matched = (tdata2_d[i] != vaddr_from_lsu_i); + endcase + end + // load with data + if (mcontrol6_32_tdata1_d[i].load && mcontrol6_32_tdata1_d[i].select) begin + case (mcontrol6_32_tdata1_d[i].match) + 4'd0: matched = (tdata2_d[i] == commit_instr_i.result && commit_instr_i.op == 8'h27); + 4'd1: + matched = (napot_match(tdata2_d[i], commit_instr_i.result) && + commit_instr_i.op == 8'h27); + 4'd8: matched = (tdata2_d[i] != commit_instr_i.result && commit_instr_i.op == 8'h27); + endcase + end + // load with address + if (mcontrol6_32_tdata1_d[i].load && !mcontrol6_32_tdata1_d[i].select) begin + case (mcontrol6_32_tdata1_d[i].match) + 4'd0: matched = (tdata2_d[i] == vaddr_from_lsu_i); + 4'd1: matched = (napot_match(tdata2_d[i], vaddr_from_lsu_i)); + 4'd8: matched = (tdata2_d[i] != vaddr_from_lsu_i); + endcase + end + if (priv_match[i] && matched) begin + case (mcontrol6_32_tdata1_d[i].action) + //6'd0: breakpoint action currently not supported + 6'd1: mcontrol6_debug_d = 1'b1; //into debug mode; + default: ; + endcase + end + if (debug_mode_i && matched) begin + matched = 1'b0; + mcontrol6_32_tdata1_d[i].hit0 = 1'b0; + mcontrol6_32_tdata1_d[i].hit1 = 1'b1; + mcontrol6_debug_d = 1'b0; + end + end + // etrigger match logic + if (trigger_type_d[i] == 4'd5 && CVA6Cfg.Etrigger) begin + break_from_trigger_d = 1'b0; + case(priv_lvl_i) // trigger will only fire if current priv lvl is same as the trigger configuration + riscv::PRIV_LVL_M: if (etrigger32_tdata1_d[i].m) priv_match[i] = 1'b1; + riscv::PRIV_LVL_S: if (etrigger32_tdata1_d[i].s) priv_match[i] = 1'b1; + riscv::PRIV_LVL_U: if (etrigger32_tdata1_d[i].u) priv_match[i] = 1'b1; + default: priv_match[i] = 1'b0; + endcase + // S_MODE context match check + if (priv_lvl_i == riscv::PRIV_LVL_S && etrigger32_tdata1_d[i].s) begin + if (CVA6Cfg.IS_XLEN32) begin + scontext_match[i] = match_scontext( + scontext_i, + textra32_tdata3_d[i].sselect, + textra32_tdata3_d[i].sbytemask, + textra32_tdata3_d[i].svalue, + 1'b0 + ); + end else begin + scontext_match[i] = match_scontext( + scontext_i, + textra64_tdata3_d[i].sselect, + textra64_tdata3_d[i].sbytemask, + textra64_tdata3_d[i].svalue, + 1'b1 + ); + end + priv_match[i] &= scontext_match[i]; + end + if (tdata2_d[i][ex_i.cause]) e_matched_d = 1'b1; + if (mret_i || sret_i) mret_reg_d = 1'b1; + if (e_matched_q && priv_match[i] && mret_reg_q && commit_ack_i) begin + e_matched_d = 1'b0; + etrigger32_tdata1_d[i].hit = 1'b1; + case (etrigger32_tdata1_d[i].action) + 6'd0: break_from_trigger_d = 1'b1; //breakpoint + 6'd1: debug_from_trigger_d = 1'b1; //into debug mode; + default: ; + endcase + end + if (break_from_trigger_q) begin + etrigger32_tdata1_d[i].hit = 1'b0; + break_from_trigger_d = 1'b0; + end + if (debug_mode_i && debug_from_trigger_d) begin + etrigger32_tdata1_d[i].hit = 1'b0; + debug_from_trigger_d = 1'b0; + end + end + // itrigger match logic + if (trigger_type_d[i] == 4'd4 && CVA6Cfg.Itrigger) begin + break_from_trigger_d = 1'b0; + case(priv_lvl_i) // trigger will only fire if current priv lvl is same as the trigger configuration + riscv::PRIV_LVL_M: if (itrigger32_tdata1_d[i].m) priv_match[i] = 1'b1; + riscv::PRIV_LVL_S: if (itrigger32_tdata1_d[i].s) priv_match[i] = 1'b1; + riscv::PRIV_LVL_U: if (itrigger32_tdata1_d[i].u) priv_match[i] = 1'b1; + default: priv_match[i] = 1'b0; + endcase + // S_MODE context match check + if (priv_lvl_i == riscv::PRIV_LVL_S && itrigger32_tdata1_d[i].s) begin + if (CVA6Cfg.IS_XLEN32) begin + scontext_match[i] = match_scontext( + scontext_i, + textra32_tdata3_d[i].sselect, + textra32_tdata3_d[i].sbytemask, + textra32_tdata3_d[i].svalue, + 1'b0 + ); + end else begin + scontext_match[i] = match_scontext( + scontext_i, + textra64_tdata3_d[i].sselect, + textra64_tdata3_d[i].sbytemask, + textra64_tdata3_d[i].svalue, + 1'b1 + ); + end + priv_match[i] &= scontext_match[i]; + end + if (ex_i.cause[CVA6Cfg.XLEN-1]) begin + if (tdata2_d[i][ex_i.cause[4:0]]) e_matched_d = 1'b1; + end + if (mret_i || sret_i) mret_reg_d = 1'b1; + if (e_matched_q && priv_match[i] && mret_reg_q && commit_ack_i) begin + e_matched_d = 1'b0; + itrigger32_tdata1_d[i].hit = 1'b1; + case (itrigger32_tdata1_d[i].action) + 6'd0: break_from_trigger_d = 1'b1; //breakpoint + 6'd1: debug_from_trigger_d = 1'b1; //into debug mode; + default: ; + endcase + end + if (break_from_trigger_q) begin + itrigger32_tdata1_d[i].hit = 1'b0; + break_from_trigger_d = 1'b0; + end + if (debug_mode_i && debug_from_trigger_d) begin + itrigger32_tdata1_d[i].hit = 1'b0; + debug_from_trigger_d = 1'b0; + end + end + end + end + end + + + always_comb begin : read_path + tselect_o = '0; + tdata1_o = '0; + tdata2_o = '0; + tdata3_o = '0; + + // TSELECT read + tselect_o = {{(CVA6Cfg.XLEN - N_Triggers) {1'b0}}, tselect_q}; + + // TDATA1 read (depends on trigger type) + unique case (trigger_type_q[tselect_q]) + 4'd3: + tdata1_o = (CVA6Cfg.IS_XLEN32) ? icount32_tdata1_q[tselect_q] : { icount32_tdata1_q[tselect_q].t_type, icount32_tdata1_q[tselect_q].dmode, 32'd0, icount32_tdata1_q[tselect_q][26:0] }; + 4'd6: + tdata1_o = (CVA6Cfg.IS_XLEN32) ? mcontrol6_32_tdata1_q[tselect_q] : { mcontrol6_32_tdata1_q[tselect_q].t_type, mcontrol6_32_tdata1_q[tselect_q].dmode, 32'd0, mcontrol6_32_tdata1_q[tselect_q][26:0] }; + 4'd5: + tdata1_o = (CVA6Cfg.IS_XLEN32) ? etrigger32_tdata1_q[tselect_q] : { etrigger32_tdata1_q[tselect_q].t_type, etrigger32_tdata1_q[tselect_q].dmode, etrigger32_tdata1_q[tselect_q].hit, 45'd0, etrigger32_tdata1_q[tselect_q][12:0] }; + 4'd4: + tdata1_o = (CVA6Cfg.IS_XLEN32) ? itrigger32_tdata1_q[tselect_q] : { itrigger32_tdata1_q[tselect_q].t_type, itrigger32_tdata1_q[tselect_q].dmode, itrigger32_tdata1_q[tselect_q].hit, 45'd0, itrigger32_tdata1_q[tselect_q][12:0] }; + default: ; + endcase + + // TDATA2 read + tdata2_o = tdata2_q[tselect_q]; + + // TDATA3 read + tdata3_o = (CVA6Cfg.XLEN == 32) ? textra32_tdata3_q[tselect_q] : textra64_tdata3_q[tselect_q]; + end + + + always_ff @(posedge clk_i or negedge rst_ni) begin : state_update + if (~rst_ni) begin + if (CVA6Cfg.SDTRIG) begin + tselect_q <= '0; + mcontrol6_debug_q <= 1'b0; + e_matched_q <= 1'b0; + mret_reg_q <= 1'b0; + break_from_trigger_q <= 0; + debug_from_trigger_q <= 0; + in_trap_handler_q <= 0; + for (int i = 0; i < N_Triggers; ++i) begin + trigger_type_q[i] <= '0; + icount32_tdata1_q[i] <= '0; + icount32_tdata1_q[i].count <= 1; + mcontrol6_32_tdata1_q[i] <= '0; + textra32_tdata3_q[i] <= '0; + textra64_tdata3_q[i] <= '0; + tdata2_q[i] <= '0; + etrigger32_tdata1_q[i] <= '0; + itrigger32_tdata1_q[i] <= '0; + end + end + end else begin + if (CVA6Cfg.SDTRIG) begin + trigger_type_q <= trigger_type_d; + tselect_q <= tselect_d; + tdata2_q <= tdata2_d; + icount32_tdata1_q <= icount32_tdata1_d; + mcontrol6_32_tdata1_q <= mcontrol6_32_tdata1_d; + etrigger32_tdata1_q <= etrigger32_tdata1_d; + itrigger32_tdata1_q <= itrigger32_tdata1_d; + textra32_tdata3_q <= textra32_tdata3_d; + textra64_tdata3_q <= textra64_tdata3_d; + mcontrol6_debug_q <= mcontrol6_debug_d; + break_from_trigger_q <= break_from_trigger_d; + debug_from_trigger_q <= debug_from_trigger_d; + in_trap_handler_q <= in_trap_handler_d; + e_matched_q <= e_matched_d; + mret_reg_q <= mret_reg_d; + end + end + end + + // Outputs + assign debug_from_trigger_o = debug_from_trigger_q; + assign break_from_trigger_o = break_from_trigger_q; + assign debug_from_mcontrol_o = mcontrol6_debug_d & ~mcontrol6_debug_q; + +endmodule diff --git a/hw/vendor/cva6_cheri/docs/01_cva6_user/Trigger_Module.rst b/hw/vendor/cva6_cheri/docs/01_cva6_user/Trigger_Module.rst new file mode 100644 index 00000000..6179622a --- /dev/null +++ b/hw/vendor/cva6_cheri/docs/01_cva6_user/Trigger_Module.rst @@ -0,0 +1,323 @@ +.. + Copyright (c) 2023 OpenHW Group + Copyright (c) 2023 Thales DIS SAS + + SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 + + +Trigger Module (Sdtrig Extension) +================================= +The module provides powerful hardware breakpoints and watchpoints in the core. +Upon trigger match, the core supports two actions i.e. breakpoint excpetion generation and entry into debug mode. +The number of triggers is parameterized and the module itself is configurable under the SDTRIG bit. + +Configuration CSRs +~~~~~~~~~~~~~~~~~~ +* ``tselect`` : The currently selected trigger. +* ``tinfo`` : Shows the currently supported triggers. +* ``tdata1`` : Specifies trigger data like trigger type, configuration, privilege mode, status. +* ``tdata2`` : The value to be matched. +* ``tdata3`` : Specifies context data. +* ``scontext`` : Specifies supervisor mode context data. + + +Modified CSRs (Breakpoint action) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +* ``mstatus``: several fields are updated like previous privilege mode (``MPP``), previous interrupt enabled (``MPIE``) +* ``mepc`` : updated with the address of the instruction raising the exception. +* ``mcause`` : updated with a breakpoint code (0x00000003) indicating the event causing the trap. + +Modified CSRs (Debug action) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +* ``dpc`` : updated with the address of the instruction causing the debug request. +* ``dcsr`` : some fields are updated like debug entry cause (``CAUSE``) + + +Icount +------ +The ``icount`` trigger is used to delay a breakpoint or debug entry by a programmable number of instructions. It acts like a countdown timer based on instruction commits and gives deterministic trigger behavior useful for testing or stepping into debug mode after a specific number of instructions. + +Prerequisites +~~~~~~~~~~~~~ +- ``SDTRIG`` and ``Icount`` configuration bits must be enabled. +- Trigger type must be ``4'd3`` in ``tdata1``. + +Trigger Match Logic +~~~~~~~~~~~~~~~~~~~ +- The ``count`` field in ``tdata1`` is initialized with the number of instructions to wait. +- The counter decrements on every instruction commit (``commit_ack_i``). +- Countdown continues only outside trap handlers (``!in_trap_handler``) to avoid miscounts during exceptions. + +- Matching occurs when: + - ``count == 0`` + - Current privilege level matches the trigger config (``m/s/u``) + +- On match: + - ``pending`` is set + - The specified ``action`` is triggered: + - ``0`` → raise a breakpoint exception + - ``1`` → enter debug mode + +- Once in debug mode: + - ``pending`` is cleared + - ``hit`` is set and remains latched + +Key Fields (in ``tdata1``) +~~~~~~~~~~~~~~~~~~~~~~~~~~ +- ``count``: Current countdown value. +- ``action``: Determines what to do on match: + + - ``0``: Raise a breakpoint exception + - ``1``: Enter debug mode + +- ``m``, ``s``, ``u``: Privilege mode flags — trigger only fires in selected modes. +- ``pending``: Indicates the trigger condition has been met and is awaiting action. +- ``hit``: Latched once the action (break/debug) occurs. + +Action Taken on Match +~~~~~~~~~~~~~~~~~~~~~ +- Breakpoint (``action == 0``): + - Sets ``break_from_trigger_d = 1``, which results in: + - Trap entry + - Updates to ``mstatus``, ``mepc``, ``mcause`` + +- Debug Entry (``action == 1``): + - Sets ``debug_from_trigger_d = 1``, resulting in: + - Trap into debug mode + - Updates ``dpc``, ``dcsr`` + +Pseudocode +~~~~~~~~~~ +.. code-block:: text + + If icount trigger is enabled and type is 0x3: + On instruction execute: + If in trap handler: + Hold countdown + Else if not in trap and count > 0: + Decrement count + If count == 0 and privilege matches and not pending: + Set pending + If action == 0: raise breakpoint + If action == 1: enter debug mode + If breakpoint or debug action occurred: + Set hit + Clear pending (for debug) + + +Mcontrol6 +--------- +The ``mcontrol6`` trigger detects specific memory accesses (load/store), instruction fetches or execution of specific instructions. It matches on instruction address, instruction, load/store data and load/store addresses. It is the most general and powerful of the trigger types allowing address/data breakpoints, watchpoints, and instruction level filters. + +Prerequisites +~~~~~~~~~~~~~~~ +- ``SDTRIG`` and ``Mcontrol6`` configuration bits must be enabled. +- Trigger type must be ``4'd6`` in ``tdata1``. + +Trigger Match Logic +~~~~~~~~~~~~~~~~~~~ +A match occurs if: + +1. Privilege Mode Match: + - One of the flags ``m``, ``s``, ``u`` in ``tdata1`` must match current mode. + +2. Condition Match: + - Based on operation type and mode: + - Instruction Execute: + - Match against PC (`commit_instr_i.pc`) or instruction (`orig_instr_i`). + - Store Operation: + - Match against store data (`store_result_i`) or address (`vaddr_from_lsu_i`). + - Load Operation: + - Match against load result (`commit_instr_i.result`) or load address (`vaddr_from_lsu_i`). + +3. Selection Mode: + - If ``select == 1`` → compare data/instruction value. + - If ``select == 0`` → compare address. + +4. Match Type (`match` field): + - ``0`` → exact match (``==``) + - ``1`` → NAPOT (Naturally Aligned Power-of-Two) range match + - ``8`` → not equal (``!=``) + +5. Action (only one supported here): + - ``1`` → enter debug mode + +On match: +- The configured ``action`` is executed. +- In debug mode, internal state is reset: + - ``hit0`` is cleared + - ``hit1`` is latched + +Key Fields +~~~~~~~~~~ +- ``tdata1``: + - ``execute`` / ``load`` / ``store``: Operation type + - ``select``: Controls address vs. value comparison + - ``match``: Match type (exact, NAPOT, not-equal) + - ``action``: Trigger response (`1` → debug) + - ``m``, ``s``, ``u``: Privilege filters + - ``hit0``, ``hit1``: Used to track match state + +- ``tdata2``: Match value — can be an address, instruction or data value + +Pseudocode +~~~~~~~~~~ +.. code-block:: text + + If mcontrol6 is enabled and type is 0x6: + If privilege level matches tdata1: + Determine comparison target: + If execute: + If select == 0 → compare PC + If select == 1 → compare instruction + If store: + If select == 0 → compare store address + If select == 1 → compare store data + If load: + If select == 0 → compare load address + If select == 1 → compare load result + Use match field (==, NAPOT, !=) to evaluate + If match: + If action == 1 → enter debug mode + In debug mode: + Clear hit0, set hit1, reset trigger state + +Implementation Note: Debug Entry Timing +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Unlike other trigger types (`icount`, `etrigger`, `itrigger`) which assert debug entry directly at commit stage, the `mcontrol6` trigger initiates debug mode entry via a back-signal to the decode stage. +This difference is necessary because the values used for matching in `mcontrol6` (``orig_instr_i``, ``vaddr_from_lsu_i``, ``store_result_i``) originate from earlier pipeline stages and may not correspond to the instruction currently at the commit point. +To ensure that the correct program counter (``dpc``) is set — specifically, the PC of the instruction being watched or triggering the event — the implementation inserts a pseudo-instruction at the decode stage once a match is detected. This pseudo-instruction proceeds through the pipeline and commits in place of the original instruction. + +This approach guarantees: +- The proper instruction is associated with debug entry. +- ``dpc`` reflects the trigger-causing instruction's PC. + +This mechanism is only used for `mcontrol6` where precise coordination with the load/store or fetch context is required. Other trigger types do not require this level of staging and use commit-time debug entry. + + +Etrigger +-------- +The ``etrigger`` trigger monitors **exception causes** and fires a trigger action when a matching exception occurs depending on privilege mode. It is highly useful for detecting specific trap causes such as illegal instructions, page faults, breakpoints etc. + +Prerequisites +~~~~~~~~~~~~~ +- ``SDTRIG`` and ``Etrigger`` configuration bits must be enabled. +- Trigger type must be ``4'd5`` in ``tdata1``. + +Trigger Match Logic +~~~~~~~~~~~~~~~~~~~ +- The trigger matches and activates when both conditions are met: + + - The trigger checks if the current exception cause (`ex_i.cause`) is set in the exception mask stored in ``tdata2``. + - The current privilege mode (`m`, `s`, `u`) matches what is enabled in ``tdata1``. + +- On a match: + - The normal exception is suppressed. + - The ``hit`` bit in ``tdata1`` is latched. + - The specified ``action`` is triggered: + - ``0`` → raise a breakpoint exception + - ``1`` → enter debug mode + +- While in debug mode, ``hit`` and internal state are cleared. + +Key Fields +~~~~~~~~~~ +- ``tdata1``: + - ``action``: Determines the response: + - ``0``: Raise a breakpoint exception + - ``1``: Enter debug mode + - ``m``, ``s``, ``u``: Privilege level flags. + - ``hit``: Latched on trigger match. + +- ``tdata2``: Bitmask for exception causes where each bit corresponds to a cause number. For example: + - Bit 2 → Illegal instruction + - Bit 3 → Breakpoint + - Bit 11 → Environment call from M-mode + +Action Taken on Match +~~~~~~~~~~~~~~~~~~~~~ +- Breakpoint (``action == 0``): + - Raises exception via ``break_from_trigger_d = 1`` + - Updates ``mstatus``, ``mepc``, ``mcause`` + +- Debug Entry (``action == 1``): + - Sets ``debug_from_trigger_d = 1`` + - Updates ``dpc``, ``dcsr`` + +Pseudocode +~~~~~~~~~~ +.. code-block:: text + + If etrigger is enabled and type is 0x5: + If current priv level matches trigger (m/s/u): + If tdata2[exception_cause] is set: + Mark hit + If action == 0: raise breakpoint + If action == 1: enter debug mode + In debug mode: + Clear hit and trigger state + + + +Itrigger +-------- +The ``itrigger`` trigger monitors **interrupt causes** and fires a trigger action when a matching interrupt occurs at the configured privilege level. This allows catching specific hardware or software interrupt events and take debugging or trapping action immediately. + +Prerequisites +~~~~~~~~~~~~~ +- ``SDTRIG`` and ``Itrigger`` configuration bits must be enabled. +- Trigger type must be ``4'd4`` in ``tdata1``. + +Trigger Match Logic +~~~~~~~~~~~~~~~~~~~ +- The trigger matches when both of the following are true: + + - The current exception (`ex_i.cause`) is a valid interrupt (i.e., MSB of the cause is set). + - The lower 5 bits of `ex_i.cause` index into a set bit in the interrupt mask (`tdata2`). + - Privilege mode matches enabled flags in ``tdata1`` (``m``, ``s``, ``u``). + +- On a match: + - The normal exception is suppressed. + - The ``hit`` bit is latched. + - The configured ``action`` is taken: + - ``0`` → raise a breakpoint exception + - ``1`` → enter debug mode + +- When in debug mode: + - The ``hit`` bit and trigger state are cleared + +Key Fields +~~~~~~~~~~ +- ``tdata1``: + - ``action``: Determines what to do when the interrupt matches: + - ``0``: Breakpoint + - ``1``: Debug mode + - ``m``, ``s``, ``u``: Privilege level match + - ``hit``: Set on match + +- ``tdata2``: Interrupt cause bitmask + +Action Taken on Match +~~~~~~~~~~~~~~~~~~~~~ +- Breakpoint (``action == 0``): + - Raises exception via ``break_from_trigger_d = 1`` + - Updates ``mstatus``, ``mepc``, ``mcause`` + +- Debug Entry (``action == 1``): + - Sets ``debug_from_trigger_d = 1`` + - Updates ``dpc``, ``dcsr`` + +Pseudocode +~~~~~~~~~~~~ +.. code-block:: text + + If itrigger is enabled and type is 0x4: + If ex_i.cause[XLEN-1] == 1 (interrupt): + If tdata2[ex_i.cause[4:0]] is set: + If current priv level matches tdata1 (m/s/u): + Mark hit + If action == 0: raise breakpoint + If action == 1: enter debug mode + In debug mode: + Clear hit and internal match state + diff --git a/hw/vendor/cva6_cheri/util/toolchain-builder/README.md b/hw/vendor/cva6_cheri/util/toolchain-builder/README.md index f6b56e5d..9dff777d 100644 --- a/hw/vendor/cva6_cheri/util/toolchain-builder/README.md +++ b/hw/vendor/cva6_cheri/util/toolchain-builder/README.md @@ -53,6 +53,13 @@ upstream toolchain (default: GCC 13.1.0) for bare-metal 32-bit and 64-bit applic # 3. Build and install the toolchain (requires write+create permissions for $INSTALL_DIR.) bash build-toolchain.sh $INSTALL_DIR +Additionally, after getting the GCC toolchain, you can apply +`gcc-cva6-tune.patch` to add support for the `-mtune=cva6` option in GCC. + +```bash +cd util/toolchain-builder/src/gcc && git apply ../../gcc-cva6-tune.patch +``` + ## File and directory structure The base infrastructure for building compilation toolchains consists of two scripts diff --git a/hw/vendor/cva6_cheri/util/toolchain-builder/gcc-cva6-tune.patch b/hw/vendor/cva6_cheri/util/toolchain-builder/gcc-cva6-tune.patch new file mode 100644 index 00000000..35f6988b --- /dev/null +++ b/hw/vendor/cva6_cheri/util/toolchain-builder/gcc-cva6-tune.patch @@ -0,0 +1,178 @@ +diff --git a/gcc/config/riscv/cva6.md b/gcc/config/riscv/cva6.md +new file mode 100644 +index 000000000..2dc397ee1 +--- /dev/null ++++ b/gcc/config/riscv/cva6.md +@@ -0,0 +1,98 @@ ++(define_automaton "cva6") ++ ++;; CVA6 core ++;; This has 4 functional unit groups: ++;; - ALU + Branch unit + Mult/Div + CSR ++;; - Load unit ++;; - Store Unit ++;; - FPU + ALU2 (for the superscalar configuration) ++ ++;; Issue ports ++(define_cpu_unit "cva6_issue0" "cva6") ++(define_cpu_unit "cva6_issue1" "cva6") ;; if superscalar ++ ++;; Fixed-latency (or so-called) functional units ++(define_cpu_unit "cva6_alu0" "cva6") ;; Includes the result bus ++(define_cpu_unit "cva6_branch" "cva6") ++(define_cpu_unit "cva6_mul" "cva6") ++(define_cpu_unit "cva6_div" "cva6") ++;; FIXME add CSR unit? (define_cpu_unit "cva6_csr" "cva6") ++ ++;; Accelerator/CV-X-IF functional unit ++(define_cpu_unit "cva6_cvxif" "cva6") ++ ++;; Load-Store Unit ++(define_cpu_unit "cva6_load" "cva6") ++(define_cpu_unit "cva6_store" "cva6") ++ ++;; FPU and ALU2 units ++(define_cpu_unit "cva6_fpu" "cva6") ++(define_cpu_unit "cva6_alu2" "cva6") ;; if superscalar ++ ++;; ISSUE can be either ISSUE0 or ISSUE1 ++(define_reservation "cva6_issue" "cva6_issue0|cva6_issue1") ++ ++;; ALU can be either ALU0 or ALU2, and ALU2 is preferred (if available) ++(define_reservation "cva6_alu" "cva6_alu2|cva6_alu0") ++ ++;; Most (integer) instructions are dispatched to the ALUs ++;; (All instructions need an issue port first) ++(define_insn_reservation "cva6_int" 1 ++ (and (eq_attr "tune" "cva6") ++ (eq_attr "type" "const,arith,logical,shift,slt,move,fmove,auipc,nop,bitmanip,rotate,condmove")) ++ "cva6_issue, cva6_alu") ++;; ALU-ALU forwarding is possible between integer instructions ++;;(define_bypass 0 "cva6_int" "cva6_int") ++;; Actually disabled as it decreases performance for now ++ ++;; Branches/jumps/calls use the branch unit and the first ALU ++(define_insn_reservation "cva6_branch" 1 ++ (and (eq_attr "tune" "cva6") ++ (eq_attr "type" "branch,jump,call")) ++ "cva6_issue, cva6_branch+cva6_alu0") ++;; When a branch is used, then the store unit is locked ++(absence_set "cva6_store" "cva6_branch") ++ ++;; Multiplications use the multiplier, then the result bus of the ALU ++(define_insn_reservation "cva6_mul" 2 ;; 2 cycles before the result is available ++ (and (eq_attr "tune" "cva6") ++ (eq_attr "type" "imul")) ++ "cva6_issue, cva6_mul, cva6_alu0") ++ ++;; Divisions make the divider busy for a long time ++(define_insn_reservation "cva6_div" 42 ;; FIXME use a realistic duration instead of 42 ++ (and (eq_attr "tune" "cva6") ++ (eq_attr "type" "idiv")) ++ "cva6_issue, cva6_div*42") ;; FIXME use a realistic duration instead of 42 ++;; FIXME the duration of the division can be XLEN-dependent ++;; Using the divider prevents from using alu0 and mul (and vice versa) ++(exclusion_set "cva6_div" "cva6_alu0,cva6_mul") ++ ++;; Unknown/custom and accelerator transfer instructions use CV-X-IF ++(define_insn_reservation "cva6_cvxif" 1 ;; FIXME update duration ++ (and (eq_attr "tune" "cva6") ++ (eq_attr "type" "mfc,mtc,unknown,multi")) ++ "cva6_issue, cva6_cvxif") ++ ++;; Load instructions, (int or float) use the load unit ++(define_insn_reservation "cva6_load" 2 ;; latency = 2 cycles... ++ (and (eq_attr "tune" "cva6") ++ (eq_attr "type" "load,fpload")) ++ "cva6_issue, cva6_load") ;; ... pipelined: unit busy for 1 cycle only ++ ++;; Store instructions, (int or float) use the store unit ++(define_insn_reservation "cva6_store" 2 ;; latency = 2 cycles... ++ (and (eq_attr "tune" "cva6") ++ (eq_attr "type" "store,fpstore")) ++ "cva6_issue, cva6_store") ;; ... pipelined: unit busy for 1 cycle only ++;; Cannot use load and store units simultaneously ++(exclusion_set "cva6_load" "cva6_store") ++ ++;; Floating point processing use the FPU for a long time ++(define_insn_reservation "cva6_fp" 42 ;; FIXME use a realistic value instead of 42 ++ (and (eq_attr "tune" "cva6") ++ (eq_attr "type" "fadd,fmul,fmadd,fdiv,fcmp,fcvt,fsqrt")) ++ "cva6_fpu*42") ;; FIXME use a realistic value instead of 42 ++;; FIXME the duration of the FP ops can be XLEN-dependent ++;; Using the FPU prevents from using alu2 (and vice versa) ++(exclusion_set "cva6_fpu" "cva6_alu2") ;; if superscalar ++ ++;; FIXME define reservation for the following instructions: ++;; sfb_alu atomic +diff --git a/gcc/config/riscv/riscv-cores.def b/gcc/config/riscv/riscv-cores.def +index 7d87ab7ce..b0993fd48 100644 +--- a/gcc/config/riscv/riscv-cores.def ++++ b/gcc/config/riscv/riscv-cores.def +@@ -37,6 +37,7 @@ RISCV_TUNE("rocket", generic, rocket_tune_info) + RISCV_TUNE("sifive-3-series", generic, rocket_tune_info) + RISCV_TUNE("sifive-5-series", generic, rocket_tune_info) + RISCV_TUNE("sifive-7-series", sifive_7, sifive_7_tune_info) ++RISCV_TUNE("cva6", cva6, cva6_tune_info) + RISCV_TUNE("thead-c906", generic, thead_c906_tune_info) + RISCV_TUNE("size", generic, optimize_size_tune_info) + +diff --git a/gcc/config/riscv/riscv-opts.h b/gcc/config/riscv/riscv-opts.h +index cf0cd669b..ff2a5ce33 100644 +--- a/gcc/config/riscv/riscv-opts.h ++++ b/gcc/config/riscv/riscv-opts.h +@@ -52,7 +52,8 @@ extern enum riscv_isa_spec_class riscv_isa_spec; + /* Keep this list in sync with define_attr "tune" in riscv.md. */ + enum riscv_microarchitecture_type { + generic, +- sifive_7 ++ sifive_7, ++ cva6 + }; + extern enum riscv_microarchitecture_type riscv_microarchitecture; + +diff --git a/gcc/config/riscv/riscv.cc b/gcc/config/riscv/riscv.cc +index e88fa2d63..46a61b6b8 100644 +--- a/gcc/config/riscv/riscv.cc ++++ b/gcc/config/riscv/riscv.cc +@@ -339,6 +339,20 @@ static const struct riscv_tune_param sifive_7_tune_info = { + true, /* slow_unaligned_access */ + }; + ++/* Costs to use when optimizing for CVA6. */ ++static const struct riscv_tune_param cva6_tune_info = { ++ {COSTS_N_INSNS (4), COSTS_N_INSNS (5)}, /* fp_add */ ++ {COSTS_N_INSNS (4), COSTS_N_INSNS (5)}, /* fp_mul */ ++ {COSTS_N_INSNS (20), COSTS_N_INSNS (20)}, /* fp_div */ ++ {COSTS_N_INSNS (4), COSTS_N_INSNS (4)}, /* int_mul */ ++ {COSTS_N_INSNS (6), COSTS_N_INSNS (6)}, /* int_div */ ++ 2, /* issue_rate */ ++ 6, /* branch_cost */ ++ 2, /* memory_cost */ ++ 8, /* fmv_cost */ ++ false, /* slow_unaligned_access */ ++}; ++ + /* Costs to use when optimizing for T-HEAD c906. */ + static const struct riscv_tune_param thead_c906_tune_info = { + {COSTS_N_INSNS (4), COSTS_N_INSNS (5)}, /* fp_add */ +diff --git a/gcc/config/riscv/riscv.md b/gcc/config/riscv/riscv.md +index bc384d9ae..f4dacff34 100644 +--- a/gcc/config/riscv/riscv.md ++++ b/gcc/config/riscv/riscv.md +@@ -437,7 +437,7 @@ + ;; Microarchitectures we know how to tune for. + ;; Keep this in sync with enum riscv_microarchitecture. + (define_attr "tune" +- "generic,sifive_7" ++ "generic,sifive_7,cva6" + (const (symbol_ref "((enum attr_tune) riscv_microarchitecture)"))) + + ;; Describe a user's asm statement. +@@ -3145,5 +3145,6 @@ + (include "pic.md") + (include "generic.md") + (include "sifive-7.md") ++(include "cva6.md") + (include "thead.md") + (include "vector.md") From e092593fbf96556619a624701e8fa22ad05f94cd Mon Sep 17 00:00:00 2001 From: Marno van der Maas <mvdmaas+git@lowrisc.org> Date: Mon, 4 May 2026 10:58:47 +0100 Subject: [PATCH 06/10] [hw] Top changes for new CVA6 PCC specific types have been removed in favour of register types, make the appropriate change when definding the boot capability. --- hw/top_chip/rtl/top_chip_system.sv | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hw/top_chip/rtl/top_chip_system.sv b/hw/top_chip/rtl/top_chip_system.sv index d8bd1c8f..fb45817b 100644 --- a/hw/top_chip/rtl/top_chip_system.sv +++ b/hw/top_chip/rtl/top_chip_system.sv @@ -99,9 +99,9 @@ module top_chip_system #( endfunction localparam config_pkg::cva6_cfg_t CVA6Cfg = build_cva6_config(cva6_config_pkg::cva6_cfg); - cva6_cheri_pkg::cap_pcc_t boot_cap; + cva6_cheri_pkg::cap_reg_t boot_cap; always_comb begin : gen_boot_cap - boot_cap = cva6_cheri_pkg::PCC_ROOT_CAP; + boot_cap = cva6_cheri_pkg::REG_ROOT_CAP; boot_cap.addr = top_pkg::RomCtrlMemBase + 'h80; boot_cap.flags.int_mode = 1'b1; end From 8774b68976f4325d965db8b09d781cf0fbfd81f1 Mon Sep 17 00:00:00 2001 From: Marno van der Maas <mvdmaas+git@lowrisc.org> Date: Mon, 4 May 2026 12:06:24 +0100 Subject: [PATCH 07/10] [lint] HPDCache linting rules added --- hw/top_chip/lint/top_chip_system.vlt | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/hw/top_chip/lint/top_chip_system.vlt b/hw/top_chip/lint/top_chip_system.vlt index 44927a65..4a42ca58 100644 --- a/hw/top_chip/lint/top_chip_system.vlt +++ b/hw/top_chip/lint/top_chip_system.vlt @@ -7,7 +7,7 @@ `verilator_config // Initialising all RVFI signals to zero is OK in this case. -lint_off -rule WIDTHCONCAT -file "*/cva6_cheri/core/cva6_rvfi_probes.sv" -match "*More than a 8k bit replication is probably wrong: 8461" +lint_off -rule WIDTHCONCAT -file "*/cva6_cheri/core/cva6_rvfi_probes.sv" -match "*More than a 8k bit replication is probably wrong: 8475" // This is only when data width is equal to one. lint_off -rule UNSIGNED -file "*/rtl/rr_arb_tree.sv" -match "*Comparison is constant due to unsigned arithmetic" @@ -295,3 +295,17 @@ lint_off -rule UNUSEDSIGNAL -file "*/lowrisc_ip_rom_ctrl*/rtl/rom_ctrl.sv" -matc lint_off -rule UNUSEDSIGNAL -file "*/lowrisc_ip_rom_ctrl*/rtl/rom_ctrl.sv" -match "*kmac_rom_vld_outer*" lint_off -rule UNUSEDSIGNAL -file "*/lowrisc_ip_rom_ctrl*/rtl/rom_ctrl.sv" -match "*kmac_rom_last_outer*" lint_off -rule UNUSEDSIGNAL -file "*/lowrisc_ip_rom_ctrl*/rtl/rom_ctrl.sv" -match "*kmac_err*" + +// HPDCache +lint_off -rule CASEINCOMPLETE -file "*/hpdcache/rtl/src/utils/hpdcache_mem_to_axi_write.sv" -match "*req_i*" // Default values specified outside case +lint_off -rule UNDRIVEN -file "*/hpdcache/rtl/src/hwpf_stride/hwpf_stride.sv" -match "*hpdcache_req_o*" +lint_off -rule UNOPTFLAT -file "*/hpdcache/rtl/src/common/hpdcache_fxarb.sv" +lint_off -rule UNOPTFLAT -file "*/hpdcache/rtl/src/hpdcache.sv" +lint_off -rule UNOPTFLAT -file "*/hpdcache/rtl/src/hpdcache_core_arbiter.sv" +lint_off -rule UNOPTFLAT -file "*/hpdcache/rtl/src/hpdcache_ctrl.sv" +lint_off -rule UNOPTFLAT -file "*/hpdcache/rtl/src/hpdcache_ctrl_pe.sv" +lint_off -rule UNOPTFLAT -file "*/hpdcache/rtl/src/hpdcache_miss_handler.sv" +lint_off -rule UNUSEDPARAM -file "*/hpdcache/rtl/*/*.sv" // Harmless warning for vendored in code. +lint_off -rule UNUSEDSIGNAL -file "*/hpdcache/rtl/*/*.sv" // Harmless warning for vendored in code. +lint_off -rule WIDTHEXPAND -file "*/hpdcache/rtl/*/*.sv" // Not worth fixing width errors in vendored code. +lint_off -rule WIDTHTRUNC -file "*/hpdcache/rtl/*/*.sv" // Not worth fixing width errors in vendored code. From 74cf1b1a72c9aa020a4d7025da19e2c40ef799d9 Mon Sep 17 00:00:00 2001 From: Marno van der Maas <mvdmaas+git@lowrisc.org> Date: Mon, 4 May 2026 12:47:39 +0100 Subject: [PATCH 08/10] [vendor] HPDCache size of one patch Using $clog2 to determine the width of a signal that has size 1 will erroneously give zero. This changes that. --- hw/vendor/hpdcache.vendor.hjson | 1 + .../rtl/src/common/hpdcache_data_upsize.sv | 2 +- .../hpdcache/rtl/src/hpdcache_memctrl.sv | 4 ++- .../patches/hpdcache/0001_Size_One_Fix.patch | 35 +++++++++++++++++++ 4 files changed, 40 insertions(+), 2 deletions(-) create mode 100644 hw/vendor/patches/hpdcache/0001_Size_One_Fix.patch diff --git a/hw/vendor/hpdcache.vendor.hjson b/hw/vendor/hpdcache.vendor.hjson index 9d1e4b18..e576adf8 100644 --- a/hw/vendor/hpdcache.vendor.hjson +++ b/hw/vendor/hpdcache.vendor.hjson @@ -4,6 +4,7 @@ { name: "hpdcache", target_dir: "hpdcache", + patch_dir: "patches/hpdcache", upstream: { url: "https://github.com/Capabilities-Limited/cv-hpdcache.git", diff --git a/hw/vendor/hpdcache/rtl/src/common/hpdcache_data_upsize.sv b/hw/vendor/hpdcache/rtl/src/common/hpdcache_data_upsize.sv index 357f03d2..a9ea8ee1 100644 --- a/hw/vendor/hpdcache/rtl/src/common/hpdcache_data_upsize.sv +++ b/hw/vendor/hpdcache/rtl/src/common/hpdcache_data_upsize.sv @@ -58,7 +58,7 @@ import hpdcache_pkg::*; // Local definitions // {{{ localparam int WR_WORDS = RD_WIDTH/WR_WIDTH; - localparam int PTR_WIDTH = $clog2(DEPTH); + localparam int PTR_WIDTH = (DEPTH == 1) ? 1 : $clog2(DEPTH); localparam int WORDCNT_WIDTH = $clog2(WR_WORDS); typedef logic [PTR_WIDTH-1:0] bufptr_t; typedef logic [WORDCNT_WIDTH-1:0] wordptr_t; diff --git a/hw/vendor/hpdcache/rtl/src/hpdcache_memctrl.sv b/hw/vendor/hpdcache/rtl/src/hpdcache_memctrl.sv index bf2ac09f..89590e19 100644 --- a/hw/vendor/hpdcache/rtl/src/hpdcache_memctrl.sv +++ b/hw/vendor/hpdcache/rtl/src/hpdcache_memctrl.sv @@ -200,6 +200,8 @@ import hpdcache_pkg::*; HPDcacheCfg.u.dataWaysPerRamWord; localparam int unsigned HPDCACHE_DATA_RAM_X_CUTS = HPDcacheCfg.u.accessWords; localparam int unsigned HPDCACHE_ALL_CUTS = HPDCACHE_DATA_RAM_X_CUTS*HPDCACHE_DATA_RAM_Y_CUTS; + localparam int unsigned HPDCACHE_DATA_WAYS_PER_RAM_WORD_WIDTH = + (HPDcacheCfg.u.dataWaysPerRamWord == 1) ? 1 : $clog2(HPDcacheCfg.u.dataWaysPerRamWord); typedef logic [HPDCACHE_DIR_RAM_ADDR_WIDTH-1:0] hpdcache_dir_addr_t; @@ -207,7 +209,7 @@ import hpdcache_pkg::*; typedef hpdcache_data_word_t[HPDcacheCfg.u.dataWaysPerRamWord-1:0] hpdcache_data_ram_data_t; typedef hpdcache_data_be_t [HPDcacheCfg.u.dataWaysPerRamWord-1:0] hpdcache_data_ram_be_t; typedef logic [HPDCACHE_DATA_RAM_Y_CUTS-1:0] hpdcache_data_ram_row_idx_t; - typedef logic [$clog2(HPDcacheCfg.u.dataWaysPerRamWord)-1:0] hpdcache_data_ram_way_idx_t; + typedef logic [HPDCACHE_DATA_WAYS_PER_RAM_WORD_WIDTH-1:0] hpdcache_data_ram_way_idx_t; typedef logic [HPDCACHE_DATA_RAM_X_CUTS-1:0] hpdcache_data_row_enable_t; typedef hpdcache_data_row_enable_t [HPDCACHE_DATA_RAM_Y_CUTS-1:0] hpdcache_data_enable_t; diff --git a/hw/vendor/patches/hpdcache/0001_Size_One_Fix.patch b/hw/vendor/patches/hpdcache/0001_Size_One_Fix.patch new file mode 100644 index 00000000..abf4225f --- /dev/null +++ b/hw/vendor/patches/hpdcache/0001_Size_One_Fix.patch @@ -0,0 +1,35 @@ +diff --git a/rtl/src/common/hpdcache_data_upsize.sv b/rtl/src/common/hpdcache_data_upsize.sv +index 357f03d..a9ea8ee 100644 +--- a/rtl/src/common/hpdcache_data_upsize.sv ++++ b/rtl/src/common/hpdcache_data_upsize.sv +@@ -58,7 +58,7 @@ import hpdcache_pkg::*; + // Local definitions + // {{{ + localparam int WR_WORDS = RD_WIDTH/WR_WIDTH; +- localparam int PTR_WIDTH = $clog2(DEPTH); ++ localparam int PTR_WIDTH = (DEPTH == 1) ? 1 : $clog2(DEPTH); + localparam int WORDCNT_WIDTH = $clog2(WR_WORDS); + typedef logic [PTR_WIDTH-1:0] bufptr_t; + typedef logic [WORDCNT_WIDTH-1:0] wordptr_t; +diff --git a/rtl/src/hpdcache_memctrl.sv b/rtl/src/hpdcache_memctrl.sv +index bf2ac09..89590e1 100644 +--- a/rtl/src/hpdcache_memctrl.sv ++++ b/rtl/src/hpdcache_memctrl.sv +@@ -200,6 +200,8 @@ import hpdcache_pkg::*; + HPDcacheCfg.u.dataWaysPerRamWord; + localparam int unsigned HPDCACHE_DATA_RAM_X_CUTS = HPDcacheCfg.u.accessWords; + localparam int unsigned HPDCACHE_ALL_CUTS = HPDCACHE_DATA_RAM_X_CUTS*HPDCACHE_DATA_RAM_Y_CUTS; ++ localparam int unsigned HPDCACHE_DATA_WAYS_PER_RAM_WORD_WIDTH = ++ (HPDcacheCfg.u.dataWaysPerRamWord == 1) ? 1 : $clog2(HPDcacheCfg.u.dataWaysPerRamWord); + + typedef logic [HPDCACHE_DIR_RAM_ADDR_WIDTH-1:0] hpdcache_dir_addr_t; + +@@ -207,7 +209,7 @@ import hpdcache_pkg::*; + typedef hpdcache_data_word_t[HPDcacheCfg.u.dataWaysPerRamWord-1:0] hpdcache_data_ram_data_t; + typedef hpdcache_data_be_t [HPDcacheCfg.u.dataWaysPerRamWord-1:0] hpdcache_data_ram_be_t; + typedef logic [HPDCACHE_DATA_RAM_Y_CUTS-1:0] hpdcache_data_ram_row_idx_t; +- typedef logic [$clog2(HPDcacheCfg.u.dataWaysPerRamWord)-1:0] hpdcache_data_ram_way_idx_t; ++ typedef logic [HPDCACHE_DATA_WAYS_PER_RAM_WORD_WIDTH-1:0] hpdcache_data_ram_way_idx_t; + typedef logic [HPDCACHE_DATA_RAM_X_CUTS-1:0] hpdcache_data_row_enable_t; + typedef hpdcache_data_row_enable_t [HPDCACHE_DATA_RAM_Y_CUTS-1:0] hpdcache_data_enable_t; + From 46e7ce6b702c35bbf66bc30bb1a5fba65a72bcb6 Mon Sep 17 00:00:00 2001 From: Marno van der Maas <mvdmaas+git@lowrisc.org> Date: Mon, 4 May 2026 14:15:12 +0100 Subject: [PATCH 09/10] [lint] Missing vendor removed Ibex is not integrated in Mocha, so this can be removed. --- hw/vendor/REUSE.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/vendor/REUSE.toml b/hw/vendor/REUSE.toml index 7c007fbd..4aff6bd6 100644 --- a/hw/vendor/REUSE.toml +++ b/hw/vendor/REUSE.toml @@ -3,7 +3,7 @@ version = 1 [[annotations]] -path = ["lowrisc_ibex/**", "lowrisc_ip/**"] +path = ["lowrisc_ip/**"] SPDX-FileCopyrightText = "lowRISC Contributors." SPDX-License-Identifier = "Apache-2.0" From 21b698bb3ff050fff120763c37a29cc6e019052a Mon Sep 17 00:00:00 2001 From: Marno van der Maas <mvdmaas+git@lowrisc.org> Date: Tue, 5 May 2026 15:37:35 +0100 Subject: [PATCH 10/10] [common] fifo_v3 empty signal fixed Usage is zero when FIFO is empty as well as full, so we need to take into account the full signal when assigning to empty. --- hw/ip/common_cells/rtl/fifo_v3.sv | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/ip/common_cells/rtl/fifo_v3.sv b/hw/ip/common_cells/rtl/fifo_v3.sv index ebccd45d..11b40bf0 100644 --- a/hw/ip/common_cells/rtl/fifo_v3.sv +++ b/hw/ip/common_cells/rtl/fifo_v3.sv @@ -54,7 +54,7 @@ module fifo_v3 #( ); assign full_o = full | err | ~wready; - assign empty_o = (usage == '0) | err; + assign empty_o = ((usage == '0) & ~full) | err; assign usage_o = usage; assign unused_testmode = testmode_i;