diff --git a/hw/ip/common_cells/rtl/fifo_v3.sv b/hw/ip/common_cells/rtl/fifo_v3.sv index ebccd45df..11b40bf07 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; diff --git a/hw/top_chip/lint/top_chip_system.vlt b/hw/top_chip/lint/top_chip_system.vlt index 44927a65a..4a42ca58c 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. diff --git a/hw/top_chip/rtl/top_chip_system.sv b/hw/top_chip/rtl/top_chip_system.sv index 95477bfae..fb45817b7 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, @@ -95,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 diff --git a/hw/vendor/REUSE.toml b/hw/vendor/REUSE.toml index 8581fc393..4aff6bd63 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" @@ -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 0248a3d14..3a5fdd172 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/cva6_cheri.lock.hjson b/hw/vendor/cva6_cheri.lock.hjson index 14d0a9481..6856e2763 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 abdb74b8a..6cce0f4c7 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 4ff4b7c70..153db3b21 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 1bb70ab67..c725beb54 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 103cd41d0..0f4e2d64e 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 276a67e6f..a602927b9 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 3adca597e..c7e4ec03f 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 b100a656d..a02a47555 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 c1ea36e29..7bcbdc3dd 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 5376d5c10..dad2f29ce 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 d6a91ecf6..c5c46353e 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 4cea1c5cd..f3c087ac5 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 000000000..461b7d089 --- /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 1663cc706..000000000 --- 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 fe919baad..eaaa0561e 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 3601d7076..0b55a649c 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 c8695af7e..7ee905060 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 8091e6a82..a76c62e06 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 d9e9f2331..1c9e5dbbb 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 a78f6039d..c7fc775d4 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 f082dc5c8..61b7b7798 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 7727dc703..3ec440af5 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 759ab0fac..511958894 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 1ff8686ba..4aefea66a 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 af18ed6ac..ad93dd2b3 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 7f9cea824..1caad7cf6 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 3edd0d1b5..9d5e1b5b1 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 d969dbc54..67e9d2765 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 19dc06972..78bd15a23 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 631ce2265..434d272ed 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 6542c060a..000000000 --- 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 ce45c6199..000000000 --- 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 5c503ab2d..000000000 --- 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 ac3001241..5243acdc8 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 1bc520119..c59a554f2 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 ca0f02fee..119131294 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 148a4664f..3fc397fd3 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 bc8d58c08..6e172ef15 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 10f96ada2..e28699efc 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 75cab2b91..bae241298 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 665e2106f..0c1bfe7da 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 409e903bd..a38215c9d 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 a2ddc6e94..f903e3836 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 463ea59cd..beb544390 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 17299549b..bc26cbfa1 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 758983739..6bc06746a 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 a42295ff9..417c24d2d 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 25d1082f5..b178557e1 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 39695fbf8..8b8be8d59 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 dc14b0f66..7c419307f 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 8ff7146b1..3cb2ac1f8 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 b6516830d..f951007e0 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 385ef9046..9eaf6d49a 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 000000000..800d88355 --- /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 806158fdd..fdf4390ff 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 b92088406..61f89bd89 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 62ee5387a..aa825bdc3 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 81b7aa7f0..f954db0c6 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 96ecab076..0500f6d52 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 9941243ae..96b1781b4 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 dfe479a69..cf5174e89 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 000000000..890eefa4f --- /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 000000000..81eac1493 --- /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 1f028f8fa..42329691c 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 659cf1f04..e0884d403 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 5902bd996..d1cdf35c5 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 804ebcc33..223318fa9 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 8062394e3..19ee3a522 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 dcec2a5af..fe7212038 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 b5a966dc1..c9aabe4f0 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 c52f8f829..176063caa 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 0099f9e5e..000000000 --- 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 d18a604e5..000000000 --- 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 723ab29c6..d464b14db 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 000000000..57adffc3d --- /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 187cae74e..c57ce8960 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 670d2c69f..d4c27f8c4 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 73f95deb6..2b2248e0a 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 5083a009c..512d39984 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 4cb31b5dc..30ba145ce 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 8ab597624..4e930088a 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 b6c44f7f7..7ae4b50af 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 fc325db26..7304a6f6a 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 a59c490cb..8e5e283ec 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 e55b925f9..51e407a34 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 000000000..f900ae85b --- /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 000000000..6179622a4 --- /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 f6b56e5dc..9dff777d6 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 000000000..35f6988bb --- /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") diff --git a/hw/vendor/hpdcache.core b/hw/vendor/hpdcache.core new file mode 100644 index 000000000..83182ed21 --- /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 diff --git a/hw/vendor/hpdcache.lock.hjson b/hw/vendor/hpdcache.lock.hjson new file mode 100644 index 000000000..7532fd4a3 --- /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.vendor.hjson b/hw/vendor/hpdcache.vendor.hjson new file mode 100644 index 000000000..e576adf81 --- /dev/null +++ b/hw/vendor/hpdcache.vendor.hjson @@ -0,0 +1,18 @@ +// 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", + patch_dir: "patches/hpdcache", + + upstream: { + url: "https://github.com/Capabilities-Limited/cv-hpdcache.git", + rev: "cva6-zcheri-support", + }, + + exclude_from_upstream: [ + ".github", + "docs/old", + ], +} diff --git a/hw/vendor/hpdcache/.gitignore b/hw/vendor/hpdcache/.gitignore new file mode 100644 index 000000000..6ceb24a00 --- /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 000000000..a752930a9 --- /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 000000000..4924893d5 --- /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 000000000..3ee489ddb --- /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 000000000..ce692f924 --- /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 000000000..06b1c616a --- /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 000000000..5b4971db6 --- /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 000000000..567609b12 --- /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 000000000..279d97adb --- /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 000000000..d0c3cbf10 --- /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 000000000..dcf1e7ce1 --- /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 000000000..498801935 --- /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 000000000..7c514afd1 --- /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 000000000..21b3ec38f --- /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 000000000..64cf7d163 --- /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 000000000..abaed9b1c --- /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 000000000..b6ba5f92a --- /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 000000000..12bd4f97f --- /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 000000000..e09292cd6 Binary files /dev/null and b/hw/vendor/hpdcache/docs/source/images/hpdcache_core.pdf differ diff --git a/hw/vendor/hpdcache/docs/source/images/hpdcache_core.svg b/hw/vendor/hpdcache/docs/source/images/hpdcache_core.svg new file mode 100644 index 000000000..d5f03b1dc --- /dev/null +++ b/hw/vendor/hpdcache/docs/source/images/hpdcache_core.svg @@ -0,0 +1,3446 @@ + + + + + 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 000000000..b8a26854c Binary files /dev/null and b/hw/vendor/hpdcache/docs/source/images/hpdcache_csr_addr_space.pdf differ 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 000000000..b1caa0f0b --- /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 000000000..c37f24dc2 Binary files /dev/null and b/hw/vendor/hpdcache/docs/source/images/hpdcache_data_ram_organization.pdf differ 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 000000000..915dd28aa --- /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 000000000..3f6f2a9ff Binary files /dev/null and b/hw/vendor/hpdcache/docs/source/images/hpdcache_highlevel_integration.pdf differ 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 000000000..df808bab8 --- /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 000000000..a30c92fb2 Binary files /dev/null and b/hw/vendor/hpdcache/docs/source/images/hpdcache_request_address_data_alignment.pdf differ 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 000000000..5d9119ecf --- /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 000000000..8c71d80d0 Binary files /dev/null and b/hw/vendor/hpdcache/docs/source/images/hpdcache_request_arbiter.pdf differ 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 000000000..9fe73bbad --- /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 000000000..8788175c7 Binary files /dev/null and b/hw/vendor/hpdcache/docs/source/images/hpdcache_vipt.pdf differ diff --git a/hw/vendor/hpdcache/docs/source/images/hpdcache_vipt.svg b/hw/vendor/hpdcache/docs/source/images/hpdcache_vipt.svg new file mode 100755 index 000000000..3c236b37a --- /dev/null +++ b/hw/vendor/hpdcache/docs/source/images/hpdcache_vipt.svg @@ -0,0 +1,911 @@ + + + + + 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 000000000..c4fecb266 --- /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 000000000..7c3a2c3cf Binary files /dev/null and b/hw/vendor/hpdcache/docs/source/images/wave_back_to_back.pdf differ 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 000000000..fea7140c0 --- /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 000000000..fe8a44623 --- /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 000000000..cb1f95e40 Binary files /dev/null and b/hw/vendor/hpdcache/docs/source/images/wave_ready_before_valid.pdf differ 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 000000000..a7dd0fa11 --- /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 000000000..35075278a --- /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 000000000..0597a44bb Binary files /dev/null and b/hw/vendor/hpdcache/docs/source/images/wave_ready_when_valid.pdf differ 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 000000000..587f606ee --- /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 000000000..16c313eb1 --- /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 000000000..48530cd21 Binary files /dev/null and b/hw/vendor/hpdcache/docs/source/images/wave_valid_before_ready.pdf differ diff --git a/hw/vendor/hpdcache/docs/source/images/wave_valid_before_ready.svg b/hw/vendor/hpdcache/docs/source/images/wave_valid_before_ready.svg new file mode 100755 index 000000000..ccc7b4f67 --- /dev/null +++ b/hw/vendor/hpdcache/docs/source/images/wave_valid_before_ready.svg @@ -0,0 +1,4 @@ + + + +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 000000000..21794e7a4 --- /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 000000000..110c12afe --- /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 000000000..bac3fe143 --- /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 000000000..cb24eceb8 --- /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 000000000..f04efab57 --- /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 000000000..d73dab083 --- /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 000000000..b8ea64362 --- /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 000000000..f57b069af --- /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 000000000..361059882 --- /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 000000000..fcf93b14c --- /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 000000000..764a21d78 --- /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 000000000..71ec0fb63 --- /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 000000000..a20fff2a2 --- /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 000000000..a56919d46 --- /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 000000000..697cf4f49 --- /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 000000000..f5c0ce0cd --- /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 000000000..82460627c --- /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 000000000..8b1378917 --- /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 000000000..7f9fc314d --- /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 000000000..df0d63097 --- /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 000000000..30aed4cc8 --- /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 000000000..769a2d19f --- /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 000000000..a9ea8ee14 --- /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 = (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; + 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 000000000..0f7e7322a --- /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 000000000..f5495c9e1 --- /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 000000000..4085e3352 --- /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 000000000..b4d9575a2 --- /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 000000000..4e3d571ca --- /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 000000000..6daacfb83 --- /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 000000000..969ef8fc9 --- /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 000000000..e9a107e28 --- /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 000000000..906a9f9cb --- /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 000000000..184e6fbf4 --- /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 000000000..e185bc404 --- /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 000000000..7d30e3dd0 --- /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 000000000..d4cab7de7 --- /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 000000000..4adc06b40 --- /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 000000000..43bdb4506 --- /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 000000000..a4771e3bd --- /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 000000000..863c5885c --- /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 000000000..7288c731f --- /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 000000000..4f776e42c --- /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 000000000..eebc0534d --- /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 000000000..5058ba28b --- /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 000000000..c291005dd --- /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 000000000..0082e875c --- /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 000000000..aa8ec3118 --- /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 000000000..e7b2985b6 --- /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 000000000..2a35859ee --- /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 000000000..fef55f83c --- /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 000000000..d74d1bb05 --- /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 000000000..4ee1d797f --- /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 000000000..8eabcae57 --- /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 000000000..ea31ea0fb --- /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 000000000..957c5c653 --- /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 000000000..121f3bdcb --- /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 000000000..89590e19f --- /dev/null +++ b/hw/vendor/hpdcache/rtl/src/hpdcache_memctrl.sv @@ -0,0 +1,1163 @@ +/* + * 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; + 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; + + 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 [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; + + 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 000000000..f4a6eaead --- /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 000000000..970daf1d3 --- /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 000000000..5bae524aa --- /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 000000000..a4e375447 --- /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 000000000..08c1c1fac --- /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 000000000..9ea6c631e --- /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 000000000..11b38dfa4 --- /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 000000000..fa8c8fb89 --- /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 000000000..77152462a --- /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 000000000..fd846bd91 --- /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 000000000..1fc54967e --- /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 000000000..3470b7862 --- /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 000000000..df9cf9758 --- /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 000000000..a487006c6 --- /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 000000000..c55af126d --- /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 000000000..c42b35c56 --- /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 000000000..a18e997bd --- /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 000000000..73e3f81a6 --- /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 000000000..7cb95ae13 --- /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 000000000..c1502a985 --- /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 000000000..3219e4041 --- /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 000000000..46cf72600 --- /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 000000000..5846b3617 --- /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 000000000..9ab2398c7 --- /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 000000000..ea800a9e1 --- /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 000000000..2f6585fb7 --- /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 000000000..2adf4121d --- /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 000000000..379fc24b1 --- /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 000000000..a2a491ed9 --- /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 000000000..c89e404da --- /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 000000000..c38846cfd --- /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 000000000..5f4365cac --- /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 000000000..388443802 --- /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 000000000..470b6ff16 --- /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 000000000..f990f9888 --- /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 000000000..52e2f2f8b --- /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 000000000..aedf53857 --- /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 000000000..a2cb29967 --- /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 000000000..54ea0b898 --- /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 000000000..084022e8b --- /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 000000000..011512144 --- /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 000000000..c6e70f13c --- /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 000000000..7ca593d19 --- /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 000000000..6c0b228b6 --- /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 000000000..1423b7f6c --- /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 000000000..f7b5b9458 --- /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 000000000..542644749 --- /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 000000000..c7d93c094 --- /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 000000000..ab5134d9b --- /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 000000000..e9686e913 --- /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 000000000..36bf803a1 --- /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 000000000..e3919f63c --- /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 000000000..97160e69e --- /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 000000000..fdc2c69d3 --- /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 000000000..593626243 --- /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 000000000..053c7882d --- /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 000000000..b6fa6ea95 --- /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 000000000..2da62730e --- /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 000000000..4c56c8474 --- /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 000000000..2fdc0a2e9 --- /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 000000000..9e26b808e --- /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 000000000..58744063f --- /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 000000000..8fdaa989c --- /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 000000000..76bcba70f --- /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 000000000..31259a601 --- /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 000000000..156bd1a17 --- /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 000000000..e3675f50b --- /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 000000000..28d149e13 --- /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 000000000..420dae67c --- /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__ 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 000000000..abf4225f0 --- /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; + diff --git a/util/artefacts.py b/util/artefacts.py index 758e04c7e..39c5903af 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"],