From c89bdeab74afceea113a2f1bd0f970dbb72cbc49 Mon Sep 17 00:00:00 2001 From: Robert Winkler Date: Fri, 30 Aug 2024 12:42:21 +0200 Subject: [PATCH] modules/zstd: Introduce reworked ZstdDecoder Co-authored-by: Pawel Czarnecki Co-authored-by: Maciej Torhan Signed-off-by: Maciej Torhan Signed-off-by: Robert Winkler Signed-off-by: Pawel Czarnecki --- .github/workflows/modules-zstd.yml | 7 +- dependency_support/pip_requirements.in | 1 + dependency_support/pip_requirements_lock.txt | 99 ++ xls/modules/rle/BUILD | 85 ++ xls/modules/rle/rle_common.x | 11 + xls/modules/rle/rle_dec_adv.x | 211 +++ xls/modules/zstd/BUILD | 579 ++++++++ xls/modules/zstd/arbiter.v | 159 ++ xls/modules/zstd/axi_csr_accessor.x | 384 +++++ xls/modules/zstd/axi_interconnect.v | 988 +++++++++++++ xls/modules/zstd/axi_interconnect_wrapper.v | 424 ++++++ xls/modules/zstd/block_header.x | 7 +- xls/modules/zstd/block_header_dec.x | 293 ++++ xls/modules/zstd/cocotb/BUILD | 10 + xls/modules/zstd/cocotb/data_generator.py | 47 + xls/modules/zstd/cocotb/memory.py | 8 +- xls/modules/zstd/csr_config.x | 397 +++++ xls/modules/zstd/dec.x | 1395 ++++++++++++++++++ xls/modules/zstd/frame_header_dec.x | 670 +++++++++ xls/modules/zstd/memory/BUILD | 60 +- xls/modules/zstd/memory/mem_reader.x | 285 ++-- xls/modules/zstd/memory/mem_reader_wrapper.v | 4 +- xls/modules/zstd/memory/mem_writer.x | 69 +- xls/modules/zstd/priority_encoder.v | 92 ++ xls/modules/zstd/raw_block_dec_new_mem.x | 316 ++++ xls/modules/zstd/rle_block_dec_new_mem2.x | 245 +++ xls/modules/zstd/xls_fifo_wrapper.v | 53 + xls/modules/zstd/zstd_dec_cocotb_test.py | 313 ++++ xls/modules/zstd/zstd_dec_wrapper.v | 784 ++++++++++ 29 files changed, 7790 insertions(+), 206 deletions(-) create mode 100644 xls/modules/rle/rle_dec_adv.x create mode 100644 xls/modules/zstd/arbiter.v create mode 100644 xls/modules/zstd/axi_csr_accessor.x create mode 100644 xls/modules/zstd/axi_interconnect.v create mode 100644 xls/modules/zstd/axi_interconnect_wrapper.v create mode 100644 xls/modules/zstd/block_header_dec.x create mode 100644 xls/modules/zstd/cocotb/data_generator.py create mode 100644 xls/modules/zstd/csr_config.x create mode 100644 xls/modules/zstd/dec.x create mode 100644 xls/modules/zstd/frame_header_dec.x create mode 100644 xls/modules/zstd/priority_encoder.v create mode 100644 xls/modules/zstd/raw_block_dec_new_mem.x create mode 100644 xls/modules/zstd/rle_block_dec_new_mem2.x create mode 100644 xls/modules/zstd/xls_fifo_wrapper.v create mode 100644 xls/modules/zstd/zstd_dec_cocotb_test.py create mode 100644 xls/modules/zstd/zstd_dec_wrapper.v diff --git a/.github/workflows/modules-zstd.yml b/.github/workflows/modules-zstd.yml index a2daaa681e..9e928bbf86 100644 --- a/.github/workflows/modules-zstd.yml +++ b/.github/workflows/modules-zstd.yml @@ -2,8 +2,6 @@ name: Modules (ZSTD) on: # Avoid triggering on pushes to /all/ open PR branches. push: - branches: - - main paths: - 'xls/modules/zstd/**' pull_request: @@ -71,3 +69,8 @@ jobs: if: ${{ !cancelled() }} run: | bazel build -c opt -- $(bazel query 'filter(".*_place_and_route", kind(rule, //xls/modules/zstd/...))') + + - name: Build and run Verilog simulation tests (opt) + if: ${{ !cancelled() }} + run: | + bazel run -c opt -- $(bazel query 'filter(".*_cocotb_test", kind(rule, //xls/modules/zstd/...))') diff --git a/dependency_support/pip_requirements.in b/dependency_support/pip_requirements.in index 636beda523..0acda7bc38 100644 --- a/dependency_support/pip_requirements.in +++ b/dependency_support/pip_requirements.in @@ -12,6 +12,7 @@ pytest==8.2.2 cocotb==1.9.0 cocotbext-axi==0.1.24 cocotb_bus==0.2.1 +zstandard==0.23.0 # Note: numpy and scipy version availability seems to differ between Ubuntu # versions that we want to support (e.g. 18.04 vs 20.04), so we accept a diff --git a/dependency_support/pip_requirements_lock.txt b/dependency_support/pip_requirements_lock.txt index 109e6df1fb..415fb6681e 100644 --- a/dependency_support/pip_requirements_lock.txt +++ b/dependency_support/pip_requirements_lock.txt @@ -277,3 +277,102 @@ werkzeug==3.0.3 \ # via # -r dependency_support/pip_requirements.in # flask +zstandard==0.23.0 \ + --hash=sha256:034b88913ecc1b097f528e42b539453fa82c3557e414b3de9d5632c80439a473 \ + --hash=sha256:0a7f0804bb3799414af278e9ad51be25edf67f78f916e08afdb983e74161b916 \ + --hash=sha256:11e3bf3c924853a2d5835b24f03eeba7fc9b07d8ca499e247e06ff5676461a15 \ + --hash=sha256:12a289832e520c6bd4dcaad68e944b86da3bad0d339ef7989fb7e88f92e96072 \ + --hash=sha256:1516c8c37d3a053b01c1c15b182f3b5f5eef19ced9b930b684a73bad121addf4 \ + --hash=sha256:157e89ceb4054029a289fb504c98c6a9fe8010f1680de0201b3eb5dc20aa6d9e \ + --hash=sha256:1bfe8de1da6d104f15a60d4a8a768288f66aa953bbe00d027398b93fb9680b26 \ + --hash=sha256:1e172f57cd78c20f13a3415cc8dfe24bf388614324d25539146594c16d78fcc8 \ + --hash=sha256:1fd7e0f1cfb70eb2f95a19b472ee7ad6d9a0a992ec0ae53286870c104ca939e5 \ + --hash=sha256:203d236f4c94cd8379d1ea61db2fce20730b4c38d7f1c34506a31b34edc87bdd \ + --hash=sha256:27d3ef2252d2e62476389ca8f9b0cf2bbafb082a3b6bfe9d90cbcbb5529ecf7c \ + --hash=sha256:29a2bc7c1b09b0af938b7a8343174b987ae021705acabcbae560166567f5a8db \ + --hash=sha256:2ef230a8fd217a2015bc91b74f6b3b7d6522ba48be29ad4ea0ca3a3775bf7dd5 \ + --hash=sha256:2ef3775758346d9ac6214123887d25c7061c92afe1f2b354f9388e9e4d48acfc \ + --hash=sha256:2f146f50723defec2975fb7e388ae3a024eb7151542d1599527ec2aa9cacb152 \ + --hash=sha256:2fb4535137de7e244c230e24f9d1ec194f61721c86ebea04e1581d9d06ea1269 \ + --hash=sha256:32ba3b5ccde2d581b1e6aa952c836a6291e8435d788f656fe5976445865ae045 \ + --hash=sha256:34895a41273ad33347b2fc70e1bff4240556de3c46c6ea430a7ed91f9042aa4e \ + --hash=sha256:379b378ae694ba78cef921581ebd420c938936a153ded602c4fea612b7eaa90d \ + --hash=sha256:38302b78a850ff82656beaddeb0bb989a0322a8bbb1bf1ab10c17506681d772a \ + --hash=sha256:3aa014d55c3af933c1315eb4bb06dd0459661cc0b15cd61077afa6489bec63bb \ + --hash=sha256:4051e406288b8cdbb993798b9a45c59a4896b6ecee2f875424ec10276a895740 \ + --hash=sha256:40b33d93c6eddf02d2c19f5773196068d875c41ca25730e8288e9b672897c105 \ + --hash=sha256:43da0f0092281bf501f9c5f6f3b4c975a8a0ea82de49ba3f7100e64d422a1274 \ + --hash=sha256:445e4cb5048b04e90ce96a79b4b63140e3f4ab5f662321975679b5f6360b90e2 \ + --hash=sha256:48ef6a43b1846f6025dde6ed9fee0c24e1149c1c25f7fb0a0585572b2f3adc58 \ + --hash=sha256:50a80baba0285386f97ea36239855f6020ce452456605f262b2d33ac35c7770b \ + --hash=sha256:519fbf169dfac1222a76ba8861ef4ac7f0530c35dd79ba5727014613f91613d4 \ + --hash=sha256:53dd9d5e3d29f95acd5de6802e909ada8d8d8cfa37a3ac64836f3bc4bc5512db \ + --hash=sha256:53ea7cdc96c6eb56e76bb06894bcfb5dfa93b7adcf59d61c6b92674e24e2dd5e \ + --hash=sha256:576856e8594e6649aee06ddbfc738fec6a834f7c85bf7cadd1c53d4a58186ef9 \ + --hash=sha256:59556bf80a7094d0cfb9f5e50bb2db27fefb75d5138bb16fb052b61b0e0eeeb0 \ + --hash=sha256:5d41d5e025f1e0bccae4928981e71b2334c60f580bdc8345f824e7c0a4c2a813 \ + --hash=sha256:61062387ad820c654b6a6b5f0b94484fa19515e0c5116faf29f41a6bc91ded6e \ + --hash=sha256:61f89436cbfede4bc4e91b4397eaa3e2108ebe96d05e93d6ccc95ab5714be512 \ + --hash=sha256:62136da96a973bd2557f06ddd4e8e807f9e13cbb0bfb9cc06cfe6d98ea90dfe0 \ + --hash=sha256:64585e1dba664dc67c7cdabd56c1e5685233fbb1fc1966cfba2a340ec0dfff7b \ + --hash=sha256:65308f4b4890aa12d9b6ad9f2844b7ee42c7f7a4fd3390425b242ffc57498f48 \ + --hash=sha256:66b689c107857eceabf2cf3d3fc699c3c0fe8ccd18df2219d978c0283e4c508a \ + --hash=sha256:6a41c120c3dbc0d81a8e8adc73312d668cd34acd7725f036992b1b72d22c1772 \ + --hash=sha256:6f77fa49079891a4aab203d0b1744acc85577ed16d767b52fc089d83faf8d8ed \ + --hash=sha256:72c68dda124a1a138340fb62fa21b9bf4848437d9ca60bd35db36f2d3345f373 \ + --hash=sha256:752bf8a74412b9892f4e5b58f2f890a039f57037f52c89a740757ebd807f33ea \ + --hash=sha256:76e79bc28a65f467e0409098fa2c4376931fd3207fbeb6b956c7c476d53746dd \ + --hash=sha256:774d45b1fac1461f48698a9d4b5fa19a69d47ece02fa469825b442263f04021f \ + --hash=sha256:77da4c6bfa20dd5ea25cbf12c76f181a8e8cd7ea231c673828d0386b1740b8dc \ + --hash=sha256:77ea385f7dd5b5676d7fd943292ffa18fbf5c72ba98f7d09fc1fb9e819b34c23 \ + --hash=sha256:80080816b4f52a9d886e67f1f96912891074903238fe54f2de8b786f86baded2 \ + --hash=sha256:80a539906390591dd39ebb8d773771dc4db82ace6372c4d41e2d293f8e32b8db \ + --hash=sha256:82d17e94d735c99621bf8ebf9995f870a6b3e6d14543b99e201ae046dfe7de70 \ + --hash=sha256:837bb6764be6919963ef41235fd56a6486b132ea64afe5fafb4cb279ac44f259 \ + --hash=sha256:84433dddea68571a6d6bd4fbf8ff398236031149116a7fff6f777ff95cad3df9 \ + --hash=sha256:8c24f21fa2af4bb9f2c492a86fe0c34e6d2c63812a839590edaf177b7398f700 \ + --hash=sha256:8ed7d27cb56b3e058d3cf684d7200703bcae623e1dcc06ed1e18ecda39fee003 \ + --hash=sha256:9206649ec587e6b02bd124fb7799b86cddec350f6f6c14bc82a2b70183e708ba \ + --hash=sha256:983b6efd649723474f29ed42e1467f90a35a74793437d0bc64a5bf482bedfa0a \ + --hash=sha256:98da17ce9cbf3bfe4617e836d561e433f871129e3a7ac16d6ef4c680f13a839c \ + --hash=sha256:9c236e635582742fee16603042553d276cca506e824fa2e6489db04039521e90 \ + --hash=sha256:9da6bc32faac9a293ddfdcb9108d4b20416219461e4ec64dfea8383cac186690 \ + --hash=sha256:a05e6d6218461eb1b4771d973728f0133b2a4613a6779995df557f70794fd60f \ + --hash=sha256:a0817825b900fcd43ac5d05b8b3079937073d2b1ff9cf89427590718b70dd840 \ + --hash=sha256:a4ae99c57668ca1e78597d8b06d5af837f377f340f4cce993b551b2d7731778d \ + --hash=sha256:a8c86881813a78a6f4508ef9daf9d4995b8ac2d147dcb1a450448941398091c9 \ + --hash=sha256:a8fffdbd9d1408006baaf02f1068d7dd1f016c6bcb7538682622c556e7b68e35 \ + --hash=sha256:a9b07268d0c3ca5c170a385a0ab9fb7fdd9f5fd866be004c4ea39e44edce47dd \ + --hash=sha256:ab19a2d91963ed9e42b4e8d77cd847ae8381576585bad79dbd0a8837a9f6620a \ + --hash=sha256:ac184f87ff521f4840e6ea0b10c0ec90c6b1dcd0bad2f1e4a9a1b4fa177982ea \ + --hash=sha256:b0e166f698c5a3e914947388c162be2583e0c638a4703fc6a543e23a88dea3c1 \ + --hash=sha256:b2170c7e0367dde86a2647ed5b6f57394ea7f53545746104c6b09fc1f4223573 \ + --hash=sha256:b2d8c62d08e7255f68f7a740bae85b3c9b8e5466baa9cbf7f57f1cde0ac6bc09 \ + --hash=sha256:b4567955a6bc1b20e9c31612e615af6b53733491aeaa19a6b3b37f3b65477094 \ + --hash=sha256:b69bb4f51daf461b15e7b3db033160937d3ff88303a7bc808c67bbc1eaf98c78 \ + --hash=sha256:b8c0bd73aeac689beacd4e7667d48c299f61b959475cdbb91e7d3d88d27c56b9 \ + --hash=sha256:be9b5b8659dff1f913039c2feee1aca499cfbc19e98fa12bc85e037c17ec6ca5 \ + --hash=sha256:bf0a05b6059c0528477fba9054d09179beb63744355cab9f38059548fedd46a9 \ + --hash=sha256:c16842b846a8d2a145223f520b7e18b57c8f476924bda92aeee3a88d11cfc391 \ + --hash=sha256:c363b53e257246a954ebc7c488304b5592b9c53fbe74d03bc1c64dda153fb847 \ + --hash=sha256:c7c517d74bea1a6afd39aa612fa025e6b8011982a0897768a2f7c8ab4ebb78a2 \ + --hash=sha256:d20fd853fbb5807c8e84c136c278827b6167ded66c72ec6f9a14b863d809211c \ + --hash=sha256:d2240ddc86b74966c34554c49d00eaafa8200a18d3a5b6ffbf7da63b11d74ee2 \ + --hash=sha256:d477ed829077cd945b01fc3115edd132c47e6540ddcd96ca169facff28173057 \ + --hash=sha256:d50d31bfedd53a928fed6707b15a8dbeef011bb6366297cc435accc888b27c20 \ + --hash=sha256:dc1d33abb8a0d754ea4763bad944fd965d3d95b5baef6b121c0c9013eaf1907d \ + --hash=sha256:dc5d1a49d3f8262be192589a4b72f0d03b72dcf46c51ad5852a4fdc67be7b9e4 \ + --hash=sha256:e2d1a054f8f0a191004675755448d12be47fa9bebbcffa3cdf01db19f2d30a54 \ + --hash=sha256:e7792606d606c8df5277c32ccb58f29b9b8603bf83b48639b7aedf6df4fe8171 \ + --hash=sha256:ed1708dbf4d2e3a1c5c69110ba2b4eb6678262028afd6c6fbcc5a8dac9cda68e \ + --hash=sha256:f2d4380bf5f62daabd7b751ea2339c1a21d1c9463f1feb7fc2bdcea2c29c3160 \ + --hash=sha256:f3513916e8c645d0610815c257cbfd3242adfd5c4cfa78be514e5a3ebb42a41b \ + --hash=sha256:f8346bfa098532bc1fb6c7ef06783e969d87a99dd1d2a5a18a892c1d7a643c58 \ + --hash=sha256:f83fa6cae3fff8e98691248c9320356971b59678a17f20656a9e59cd32cee6d8 \ + --hash=sha256:fa6ce8b52c5987b3e34d5674b0ab529a4602b632ebab0a93b07bfb4dfc8f8a33 \ + --hash=sha256:fb2b1ecfef1e67897d336de3a0e3f52478182d6a47eda86cbd42504c5cbd009a \ + --hash=sha256:fc9ca1c9718cb3b06634c7c8dec57d24e9438b2aa9a0f02b8bb36bf478538880 \ + --hash=sha256:fd30d9c67d13d891f2360b2a120186729c111238ac63b43dbd37a5a40670b8ca \ + --hash=sha256:fd7699e8fd9969f455ef2926221e0233f81a2542921471382e77a9e2f2b57f4b \ + --hash=sha256:fe3b385d996ee0822fd46528d9f0443b880d4d05528fd26a9119a54ec3f91c69 + # via -r dependency_support/pip_requirements.in diff --git a/xls/modules/rle/BUILD b/xls/modules/rle/BUILD index 04c47760c5..ec9b880a30 100644 --- a/xls/modules/rle/BUILD +++ b/xls/modules/rle/BUILD @@ -17,6 +17,7 @@ load("@bazel_skylib//rules:build_test.bzl", "build_test") load("@rules_hdl//gds_write:build_defs.bzl", "gds_write") load("@rules_hdl//place_and_route:build_defs.bzl", "place_and_route") +load("@rules_hdl//synthesis:build_defs.bzl", "benchmark_synth") load("@rules_hdl//synthesis:build_defs.bzl", "synthesize_rtl") load("@rules_hdl//verilog:providers.bzl", "verilog_library") load( @@ -26,6 +27,7 @@ load( "xls_dslx_ir", "xls_dslx_library", "xls_dslx_test", + "xls_dslx_verilog", "xls_ir_opt_ir", "xls_ir_verilog", ) @@ -358,3 +360,86 @@ filegroup( srcs = glob(["*.x"]), visibility = ["//xls:xls_internal"], ) + +xls_dslx_library( + name = "rle_dec_adv_dslx", + srcs = [ + "rle_dec_adv.x", + ], + deps = [ + ":rle_common_dslx", + ], +) + +xls_dslx_test( + name = "rle_dec_adv_dslx_test", + library = ":rle_dec_adv_dslx", + tags = ["manual"], +) + +xls_dslx_verilog( + name = "rle_dec_adv_verilog", + codegen_args = { + "module_name": "RleDecAdv", + "delay_model": "asap7", + "pipeline_stages": "8", + "reset": "rst", + "use_system_verilog": "false", + }, + dslx_top = "RunLengthDecoderAdvInst", + library = ":rle_dec_adv_dslx", + # TODO: 2024-01-15: Workaround for https://github.com/google/xls/issues/869 + # Force proc inlining and set last internal proc as top proc for IR optimization + opt_ir_args = { + "inline_procs": "true", + "top": "__rle_dec_adv__RunLengthDecoderAdvInst__RunLengthDecoderAdv_0__21_64_8_next", + }, + tags = ["manual"], + verilog_file = "rle_dec_adv.v", +) + +xls_benchmark_ir( + name = "rle_dec_adv_opt_ir_benchmark", + src = ":rle_dec_adv_verilog.opt.ir", + benchmark_ir_args = { + "pipeline_stages": "3", + "delay_model": "asap7", + }, + tags = ["manual"], +) + +verilog_library( + name = "rle_dec_adv_verilog_lib", + srcs = [ + ":rle_dec_adv.v", + ], + tags = ["manual"], +) + +synthesize_rtl( + name = "rle_dec_adv_synth_asap7", + standard_cells = "@org_theopenroadproject_asap7sc7p5t_28//:asap7-sc7p5t_rev28_rvt", + tags = ["manual"], + top_module = "RleDecAdv", + deps = [ + ":rle_dec_adv_verilog_lib", + ], +) + +benchmark_synth( + name = "rle_dec_adv_benchmark_synth", + synth_target = ":rle_dec_adv_synth_asap7", + tags = ["manual"], +) + +place_and_route( + name = "rle_dec_adv_place_and_route", + clock_period = "750", + core_padding_microns = 2, + min_pin_distance = "0.5", + placement_density = "0.30", + stop_after_step = "global_routing", + synthesized_rtl = ":rle_dec_adv_synth_asap7", + tags = ["manual"], + target_die_utilization_percentage = "10", +) diff --git a/xls/modules/rle/rle_common.x b/xls/modules/rle/rle_common.x index 8b1217ff2c..9410c3e9e9 100644 --- a/xls/modules/rle/rle_common.x +++ b/xls/modules/rle/rle_common.x @@ -12,6 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. +import std; + // This file defines RLE common data structures // @@ -24,6 +26,15 @@ pub struct PlainData { last: bool, // flush RLE } +// Structure contains multiple uncompressed symbols. +// Structure is used as an output from a advanced RLE decoder. +// FIXME: add default value DATA_WIDTH_LOG2: u32 = {std::clog2(DATA_WIDTH + u32:1) } (https://github.com/google/xls/issues/1425) +pub struct PlainDataWithLen { + symbols: uN[DATA_WIDTH], + length: uN[DATA_WIDTH_LOG2], + last: bool, +} + // Structure contains compressed (symbol, counter) pairs. // Structure is used as an output from RLE encoder and // as an input to RLE decoder. diff --git a/xls/modules/rle/rle_dec_adv.x b/xls/modules/rle/rle_dec_adv.x new file mode 100644 index 0000000000..f6aee9c54f --- /dev/null +++ b/xls/modules/rle/rle_dec_adv.x @@ -0,0 +1,211 @@ +// Copyright 2023 The XLS Authors +// +// 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. + +import std; +import xls.modules.rle.rle_common as rle_common; + + +// structure to preserve the state of an RLE decoder +struct RunLengthDecoderAdvState { + // symbol to be repeated on output + symbol: bits[SYMBOL_WIDTH], + // count of symbols that has to be send + count: bits[COUNT_WIDTH], + // send last when repeat ends + last: bool, +} + +// RLE decoder implementation +pub proc RunLengthDecoderAdv { + // FIXME: replace with proc param SYMBOL_WIDTH, COUNT_WIDTH (https://github.com/google/xls/issues/1368) + type DecInData = rle_common::CompressedData; + // FIXME: replace with proc param OUTPUT_WIDTH (https://github.com/google/xls/issues/1368) + type DecOutData = rle_common::PlainDataWithLen; + + type State = RunLengthDecoderAdvState; + + input_r: chan in; + output_s: chan out; + + config ( + input_r: chan in, + output_s: chan out, + ) {(input_r, output_s)} + + init {( + State { + symbol: bits[SYMBOL_WIDTH]:0, + count: bits[COUNT_WIDTH]:0, + last: bool:false, + } + )} + + next (state: State) { + const MAX_OUTPUT_SYMBOLS = (OUTPUT_WIDTH / SYMBOL_WIDTH) as uN[COUNT_WIDTH]; + const OUTPUT_WIDTH_LOG2 = std::clog2(OUTPUT_WIDTH + u32:1); + + let state_input = DecInData { + symbol: state.symbol, + count: state.count, + last: state.last + }; + + // receive encoded symbol + let recv_next_symbol = (state.count == bits[COUNT_WIDTH]:0); + let (tok, input) = recv_if(join(), input_r, recv_next_symbol, state_input); + + // limit symbols to send + let symbols_to_send = if (input.count > MAX_OUTPUT_SYMBOLS) { + MAX_OUTPUT_SYMBOLS + } else { + input.count + }; + + let next_count = if input.count == bits[COUNT_WIDTH]:0 { + fail!("invalid_count_0", input.count) + } else { + input.count - symbols_to_send + }; + let done_sending = (next_count == bits[COUNT_WIDTH]:0); + let send_last = input.last && done_sending; + + // concatenate symbols to max output width + let output_symbols = for (i, output_symbols): (u32, uN[OUTPUT_WIDTH]) in range(u32:0, MAX_OUTPUT_SYMBOLS as u32) { + bit_slice_update(output_symbols, i * SYMBOL_WIDTH, input.symbol) + }(uN[OUTPUT_WIDTH]:0); + + // shift symbols + let output_symbols = output_symbols & (!uN[OUTPUT_WIDTH]:0 >> ((MAX_OUTPUT_SYMBOLS - symbols_to_send) << u32:3)); + + // send decoded symbol + let data_out = DecOutData { + symbols: output_symbols, + length: symbols_to_send as uN[OUTPUT_WIDTH_LOG2], + last: send_last + }; + let data_tok = send(tok, output_s, data_out); + + trace_fmt!("Sent RLE decoded data {:#x}", data_out); + + if (send_last) { + zero!() + } else { + State { + symbol: input.symbol, + count: next_count, + last: input.last, + } + } + } +} + +const INST_SYMBOL_WIDTH = u32:8; +const INST_COUNT_WIDTH = u32:21; +const INST_OUTPUT_WIDTH = u32:64; +const INST_OUTPUT_WIDTH_LOG2 = std::clog2(INST_OUTPUT_WIDTH + u32:1); + +pub proc RunLengthDecoderAdvInst { + type InstDecInData = rle_common::CompressedData; + type InstDecOutData = rle_common::PlainDataWithLen; + + config ( + input_r: chan in, + output_s: chan out, + ) { + spawn RunLengthDecoderAdv( + input_r, output_s + ); + } + + init { } + + next (state: ()) { } +} + +const TEST_SYMBOL_WIDTH = u32:8; +const TEST_COUNT_WIDTH = u32:21; +const TEST_OUTPUT_WIDTH = u32:64; +const TEST_OUTPUT_WIDTH_LOG2 = std::clog2(TEST_OUTPUT_WIDTH + u32:1); + +type TestDecInData = rle_common::CompressedData; +type TestDecOutData = rle_common::PlainDataWithLen; + +const TEST_INPUT_DATA = TestDecInData[8]:[ + TestDecInData {symbol: uN[TEST_SYMBOL_WIDTH]:0x01, count: uN[TEST_COUNT_WIDTH]:10, last: false}, + TestDecInData {symbol: uN[TEST_SYMBOL_WIDTH]:0x23, count: uN[TEST_COUNT_WIDTH]:1, last: false}, + TestDecInData {symbol: uN[TEST_SYMBOL_WIDTH]:0x45, count: uN[TEST_COUNT_WIDTH]:20, last: false}, + TestDecInData {symbol: uN[TEST_SYMBOL_WIDTH]:0x67, count: uN[TEST_COUNT_WIDTH]:7, last: false}, + TestDecInData {symbol: uN[TEST_SYMBOL_WIDTH]:0x89, count: uN[TEST_COUNT_WIDTH]:32, last: true}, + TestDecInData {symbol: uN[TEST_SYMBOL_WIDTH]:0xAB, count: uN[TEST_COUNT_WIDTH]:8, last: false}, + TestDecInData {symbol: uN[TEST_SYMBOL_WIDTH]:0xCD, count: uN[TEST_COUNT_WIDTH]:5, last: false}, + TestDecInData {symbol: uN[TEST_SYMBOL_WIDTH]:0xEF, count: uN[TEST_COUNT_WIDTH]:12, last: true}, +]; + +const TEST_OUTPUT_DATA = TestDecOutData[15]:[ + TestDecOutData {symbols: uN[TEST_OUTPUT_WIDTH]:0x0101_0101_0101_0101, length: uN[TEST_OUTPUT_WIDTH_LOG2]:8, last: false}, + TestDecOutData {symbols: uN[TEST_OUTPUT_WIDTH]:0x0000_0000_0000_0101, length: uN[TEST_OUTPUT_WIDTH_LOG2]:2, last: false}, + TestDecOutData {symbols: uN[TEST_OUTPUT_WIDTH]:0x0000_0000_0000_0023, length: uN[TEST_OUTPUT_WIDTH_LOG2]:1, last: false}, + TestDecOutData {symbols: uN[TEST_OUTPUT_WIDTH]:0x4545_4545_4545_4545, length: uN[TEST_OUTPUT_WIDTH_LOG2]:8, last: false}, + TestDecOutData {symbols: uN[TEST_OUTPUT_WIDTH]:0x4545_4545_4545_4545, length: uN[TEST_OUTPUT_WIDTH_LOG2]:8, last: false}, + TestDecOutData {symbols: uN[TEST_OUTPUT_WIDTH]:0x0000_0000_4545_4545, length: uN[TEST_OUTPUT_WIDTH_LOG2]:4, last: false}, + TestDecOutData {symbols: uN[TEST_OUTPUT_WIDTH]:0x0067_6767_6767_6767, length: uN[TEST_OUTPUT_WIDTH_LOG2]:7, last: false}, + TestDecOutData {symbols: uN[TEST_OUTPUT_WIDTH]:0x8989_8989_8989_8989, length: uN[TEST_OUTPUT_WIDTH_LOG2]:8, last: false}, + TestDecOutData {symbols: uN[TEST_OUTPUT_WIDTH]:0x8989_8989_8989_8989, length: uN[TEST_OUTPUT_WIDTH_LOG2]:8, last: false}, + TestDecOutData {symbols: uN[TEST_OUTPUT_WIDTH]:0x8989_8989_8989_8989, length: uN[TEST_OUTPUT_WIDTH_LOG2]:8, last: false}, + TestDecOutData {symbols: uN[TEST_OUTPUT_WIDTH]:0x8989_8989_8989_8989, length: uN[TEST_OUTPUT_WIDTH_LOG2]:8, last: true}, + TestDecOutData {symbols: uN[TEST_OUTPUT_WIDTH]:0xABAB_ABAB_ABAB_ABAB, length: uN[TEST_OUTPUT_WIDTH_LOG2]:8, last: false}, + TestDecOutData {symbols: uN[TEST_OUTPUT_WIDTH]:0x0000_00CD_CDCD_CDCD, length: uN[TEST_OUTPUT_WIDTH_LOG2]:5, last: false}, + TestDecOutData {symbols: uN[TEST_OUTPUT_WIDTH]:0xEFEF_EFEF_EFEF_EFEF, length: uN[TEST_OUTPUT_WIDTH_LOG2]:8, last: false}, + TestDecOutData {symbols: uN[TEST_OUTPUT_WIDTH]:0x0000_0000_EFEF_EFEF, length: uN[TEST_OUTPUT_WIDTH_LOG2]:4, last: true}, +]; + +#[test_proc] +proc RunLengthDecoderAdv_test { + terminator: chan out; + + input_s: chan out; + output_r: chan in; + + config (terminator: chan out) { + let (input_s, input_r) = chan("input"); + let (output_s, output_r) = chan("output"); + + spawn RunLengthDecoderAdv( + input_r, output_s + ); + + (terminator, input_s, output_r) + } + + init { } + + next (state: ()) { + let tok = join(); + + let tok = for ((i, test_input_data), tok): ((u32, TestDecInData), token) in enumerate(TEST_INPUT_DATA) { + let tok = send(tok, input_s, test_input_data); + trace_fmt!("Sent #{} input {:#x}", i + u32:1, test_input_data); + tok + }(tok); + + let tok = for ((i, test_output_data), tok): ((u32, TestDecOutData), token) in enumerate(TEST_OUTPUT_DATA) { + let (tok, output_data) = recv(tok, output_r); + trace_fmt!("Received #{} input {:#x}", i + u32:1, output_data); + assert_eq(test_output_data, output_data); + tok + }(tok); + + send(tok, terminator, true); + } +} diff --git a/xls/modules/zstd/BUILD b/xls/modules/zstd/BUILD index 037f89288b..c5bf06d3a0 100644 --- a/xls/modules/zstd/BUILD +++ b/xls/modules/zstd/BUILD @@ -17,6 +17,7 @@ load("@rules_hdl//place_and_route:build_defs.bzl", "place_and_route") load("@rules_hdl//synthesis:build_defs.bzl", "benchmark_synth", "synthesize_rtl") load("@rules_hdl//verilog:providers.bzl", "verilog_library") +load("@xls_pip_deps//:requirements.bzl", "requirement") load( "//xls/build_rules:xls_build_defs.bzl", "xls_benchmark_ir", @@ -379,6 +380,88 @@ place_and_route( target_die_utilization_percentage = "10", ) +xls_dslx_library( + name = "raw_block_dec_new_mem_dslx", + srcs = [ + "raw_block_dec_new_mem.x", + ], + deps = [ + ":common_dslx", + "//xls/modules/zstd/memory:mem_reader_dslx", + ], +) + +xls_dslx_test( + name = "raw_block_dec_new_mem_dslx_test", + library = ":raw_block_dec_new_mem_dslx", + tags = ["manual"], +) + +xls_dslx_verilog( + name = "raw_block_dec_new_mem_verilog", + codegen_args = { + "module_name": "RawBlockDecoder", + "delay_model": "asap7", + "pipeline_stages": "2", + "reset": "rst", + "use_system_verilog": "false", + }, + dslx_top = "RawBlockDecoderInst", + library = ":raw_block_dec_new_mem_dslx", + opt_ir_args = { + "inline_procs": "true", + "top": "__raw_block_dec_new_mem__RawBlockDecoderInst__RawBlockDecoder_0__32_32_next", + }, + tags = ["manual"], + verilog_file = "raw_block_dec_new_mem.v", +) + +xls_benchmark_ir( + name = "raw_block_dec_new_mem_opt_ir_benchmark", + src = ":raw_block_dec_new_mem_verilog.opt.ir", + benchmark_ir_args = { + "pipeline_stages": "2", + "delay_model": "asap7", + }, + tags = ["manual"], +) + +verilog_library( + name = "raw_block_dec_new_mem_verilog_lib", + srcs = [ + ":raw_block_dec_new_mem.v", + ], + tags = ["manual"], +) + +synthesize_rtl( + name = "raw_block_dec_new_mem_synth_asap7", + standard_cells = "@org_theopenroadproject_asap7sc7p5t_28//:asap7-sc7p5t_rev28_rvt", + tags = ["manual"], + top_module = "RawBlockDecoder", + deps = [ + ":raw_block_dec_new_mem_verilog_lib", + ], +) + +benchmark_synth( + name = "raw_block_dec_new_mem_benchmark_synth", + synth_target = ":raw_block_dec_new_mem_synth_asap7", + tags = ["manual"], +) + +place_and_route( + name = "raw_block_dec_new_mem_place_and_route", + clock_period = "750", + core_padding_microns = 2, + min_pin_distance = "0.5", + placement_density = "0.30", + stop_after_step = "global_routing", + synthesized_rtl = ":raw_block_dec_new_mem_synth_asap7", + tags = ["manual"], + target_die_utilization_percentage = "10", +) + xls_dslx_library( name = "rle_block_dec_dslx", srcs = [ @@ -1081,3 +1164,499 @@ place_and_route( tags = ["manual"], target_die_utilization_percentage = "10", ) + +xls_dslx_library( + name = "block_header_dec_dslx", + srcs = [ + "block_header_dec.x", + ], + deps = [ + ":block_header_dslx", + ":common_dslx", + "//xls/modules/zstd/memory:mem_reader_dslx", + ], +) + +xls_dslx_test( + name = "block_header_dec_dslx_test", + library = ":block_header_dec_dslx", + tags = ["manual"], +) + +xls_dslx_verilog( + name = "block_header_dec_verilog", + codegen_args = { + "module_name": "BlockHeaderDec", + "delay_model": "asap7", + "pipeline_stages": "4", + "reset": "rst", + "use_system_verilog": "false", + }, + dslx_top = "BlockHeaderDecoderInst", + library = ":block_header_dec_dslx", + opt_ir_args = { + "inline_procs": "true", + "top": "__block_header_dec__BlockHeaderDecoderInst__BlockHeaderDecoder_0__16_64_next", + }, + tags = ["manual"], + verilog_file = "block_header_dec.v", +) + +xls_benchmark_ir( + name = "block_header_dec_opt_ir_benchmark", + src = ":block_header_dec_verilog.opt.ir", + benchmark_ir_args = { + "pipeline_stages": "3", + "delay_model": "asap7", + }, + tags = ["manual"], +) + +verilog_library( + name = "block_header_dec_verilog_lib", + srcs = [ + ":block_header_dec.v", + ], + tags = ["manual"], +) + +synthesize_rtl( + name = "block_header_dec_synth_asap7", + standard_cells = "@org_theopenroadproject_asap7sc7p5t_28//:asap7-sc7p5t_rev28_rvt", + tags = ["manual"], + top_module = "BlockHeaderDec", + deps = [ + ":block_header_dec_verilog_lib", + ], +) + +benchmark_synth( + name = "block_header_dec_benchmark_synth", + synth_target = ":block_header_dec_synth_asap7", + tags = ["manual"], +) + +place_and_route( + name = "block_header_dec_place_and_route", + clock_period = "750", + core_padding_microns = 2, + min_pin_distance = "0.5", + placement_density = "0.30", + stop_after_step = "global_routing", + synthesized_rtl = ":block_header_dec_synth_asap7", + tags = ["manual"], + target_die_utilization_percentage = "10", +) + +xls_dslx_library( + name = "axi_csr_accessor_dslx", + srcs = [ + "axi_csr_accessor.x", + ], + deps = [ + ":csr_config_dslx", + "//xls/modules/zstd/memory:axi_dslx", + ], +) + +xls_dslx_test( + name = "axi_csr_accessor_dslx_test", + library = ":axi_csr_accessor_dslx", + tags = ["manual"], +) + +xls_dslx_verilog( + name = "axi_csr_accessor_verilog", + codegen_args = { + "module_name": "AxiCsrAccessor", + "delay_model": "asap7", + "pipeline_stages": "8", + "reset": "rst", + "use_system_verilog": "false", + }, + dslx_top = "AxiCsrAccessorInst", + library = ":axi_csr_accessor_dslx", + opt_ir_args = { + "inline_procs": "true", + "top": "__axi_csr_accessor__AxiCsrAccessorInst__AxiCsrAccessor_0__16_32_4_4_2_4_16_next", + }, + tags = ["manual"], + verilog_file = "axi_csr_accessor.v", +) + +xls_benchmark_ir( + name = "axi_csr_accessor_opt_ir_benchmark", + src = ":axi_csr_accessor_verilog.opt.ir", + benchmark_ir_args = { + "pipeline_stages": "4", + "delay_model": "asap7", + }, + tags = ["manual"], +) + +verilog_library( + name = "axi_csr_accessor_verilog_lib", + srcs = [ + ":axi_csr_accessor.v", + ], + tags = ["manual"], +) + +synthesize_rtl( + name = "axi_csr_accessor_synth_asap7", + standard_cells = "@org_theopenroadproject_asap7sc7p5t_28//:asap7-sc7p5t_rev28_rvt", + tags = ["manual"], + top_module = "AxiCsrAccessor", + deps = [ + ":axi_csr_accessor_verilog_lib", + ], +) + +benchmark_synth( + name = "axi_csr_accessor_benchmark_synth", + synth_target = ":axi_csr_accessor_synth_asap7", + tags = ["manual"], +) + +place_and_route( + name = "axi_csr_accessor_place_and_route", + clock_period = "750", + core_padding_microns = 2, + min_pin_distance = "0.5", + placement_density = "0.30", + stop_after_step = "global_routing", + synthesized_rtl = ":axi_csr_accessor_synth_asap7", + tags = ["manual"], + target_die_utilization_percentage = "10", +) + +xls_dslx_library( + name = "csr_config_dslx", + srcs = [ + "csr_config.x", + ], + deps = [ + "//xls/modules/zstd/memory:axi_dslx", + ], +) + +xls_dslx_test( + name = "csr_config_dslx_test", + library = ":csr_config_dslx", + tags = ["manual"], +) + +xls_dslx_verilog( + name = "csr_config_verilog", + codegen_args = { + "module_name": "CsrConfig", + "delay_model": "asap7", + "pipeline_stages": "8", + "reset": "rst", + "use_system_verilog": "false", + }, + dslx_top = "CsrConfigInst", + library = ":csr_config_dslx", + opt_ir_args = { + "inline_procs": "true", + "top": "__csr_config__CsrConfigInst__CsrConfig_0__2_32_4_32_2_4_next", + }, + tags = ["manual"], + verilog_file = "csr_config.v", +) + +xls_benchmark_ir( + name = "csr_config_opt_ir_benchmark", + src = ":csr_config_verilog.opt.ir", + benchmark_ir_args = { + "pipeline_stages": "4", + "delay_model": "asap7", + }, + tags = ["manual"], +) + +verilog_library( + name = "csr_config_verilog_lib", + srcs = [ + ":csr_config.v", + ], + tags = ["manual"], +) + +synthesize_rtl( + name = "csr_config_synth_asap7", + standard_cells = "@org_theopenroadproject_asap7sc7p5t_28//:asap7-sc7p5t_rev28_rvt", + tags = ["manual"], + top_module = "CsrConfig", + deps = [ + ":csr_config_verilog_lib", + ], +) + +benchmark_synth( + name = "csr_config_benchmark_synth", + synth_target = ":csr_config_synth_asap7", + tags = ["manual"], +) + +place_and_route( + name = "csr_config_place_and_route", + clock_period = "750", + core_padding_microns = 2, + min_pin_distance = "0.5", + placement_density = "0.30", + stop_after_step = "global_routing", + synthesized_rtl = ":csr_config_synth_asap7", + tags = ["manual"], + target_die_utilization_percentage = "10", +) + +xls_dslx_library( + name = "frame_header_dec_dslx", + srcs = [ + "frame_header_dec.x", + ], + deps = [ + "//xls/modules/zstd/memory:axi_dslx", + "//xls/modules/zstd/memory:mem_reader_dslx", + ], +) + +xls_dslx_test( + name = "frame_header_dec_dslx_test", + library = ":frame_header_dec_dslx", + tags = ["manual"], +) + +xls_dslx_verilog( + name = "frame_header_dec_verilog", + codegen_args = { + "module_name": "FrameHeaderDecoder", + "delay_model": "asap7", + "pipeline_stages": "8", + "reset": "rst", + "use_system_verilog": "false", + }, + dslx_top = "FrameHeaderDecoderInst", + library = ":frame_header_dec_dslx", + opt_ir_args = { + "inline_procs": "true", + "top": "__frame_header_dec__FrameHeaderDecoderInst__FrameHeaderDecoder_0__16_32_30_5_next", + }, + tags = ["manual"], + verilog_file = "frame_header_dec.v", +) + +xls_benchmark_ir( + name = "frame_header_dec_opt_ir_benchmark", + src = ":frame_header_dec_verilog.opt.ir", + benchmark_ir_args = { + "pipeline_stages": "4", + "delay_model": "asap7", + }, + tags = ["manual"], +) + +verilog_library( + name = "frame_header_dec_verilog_lib", + srcs = [ + ":csr_config.v", + ], + tags = ["manual"], +) + +synthesize_rtl( + name = "frame_header_dec_synth_asap7", + standard_cells = "@org_theopenroadproject_asap7sc7p5t_28//:asap7-sc7p5t_rev28_rvt", + tags = ["manual"], + top_module = "CsrConfig", + deps = [ + ":frame_header_dec_verilog_lib", + ], +) + +benchmark_synth( + name = "frame_header_dec_benchmark_synth", + synth_target = ":frame_header_dec_synth_asap7", + tags = ["manual"], +) + +place_and_route( + name = "frame_header_dec_place_and_route", + clock_period = "750", + core_padding_microns = 2, + min_pin_distance = "0.5", + placement_density = "0.30", + stop_after_step = "global_routing", + synthesized_rtl = ":frame_header_dec_synth_asap7", + tags = ["manual"], + target_die_utilization_percentage = "10", +) + +xls_dslx_library( + name = "rle_block_dec_new_mem2_dslx", + srcs = [ + "rle_block_dec_new_mem2.x", + ], + deps = [ + ":common_dslx", + ], +) + +xls_dslx_test( + name = "rle_block_dec_new_mem2_dslx_test", + library = ":rle_block_dec_new_mem2_dslx", + tags = ["manual"], +) + +xls_dslx_library( + name = "dec_dslx", + srcs = [ + "dec.x", + ], + deps = [ + ":axi_csr_accessor_dslx", + ":block_header_dec_dslx", + ":block_header_dslx", + ":common_dslx", + ":csr_config_dslx", + ":dec_mux_dslx", + ":frame_header_dec_dslx", + ":raw_block_dec_new_mem_dslx", + ":repacketizer_dslx", + ":rle_block_dec_new_mem2_dslx", + ":sequence_executor_dslx", + "//xls/examples:ram_dslx", + "//xls/modules/zstd/memory:mem_reader_dslx", + ], +) + +xls_dslx_test( + name = "dec_dslx_test", + library = ":dec_dslx", + tags = ["manual"], +) + +xls_dslx_verilog( + name = "dec_verilog", + codegen_args = { + "module_name": "ZstdDecoder", + "delay_model": "asap7", + "reset": "rst", + "worst_case_throughput": "1", + "use_system_verilog": "false", + # "clock_period_ps": "750", + # "clock_margin_percent": "20", + "multi_proc": "true", + "pipeline_stages": "8", + "flop_outputs": "true", + "flop_inputs": "true", + }, + dslx_top = "ZstdDecoderInst", + library = ":dec_dslx", + tags = ["manual"], + verilog_file = "dec.v", +) + +xls_dslx_verilog( + name = "dec_internal_verilog", + codegen_args = { + "module_name": "ZstdDecoderInsternal", + "delay_model": "asap7", + "reset": "rst", + "worst_case_throughput": "1", + "use_system_verilog": "false", + # "clock_period_ps": "750", + # "clock_margin_percent": "20", + "multi_proc": "true", + "pipeline_stages": "8", + }, + dslx_top = "ZstdDecoderInternalInst", + library = ":dec_dslx", + opt_ir_args = { + "top": "__dec__ZstdDecoderInternalInst__ZstdDecoderInternal_0__16_32_8_4_16_next", + }, + tags = ["manual"], + verilog_file = "dec_internal.v", +) + +xls_benchmark_ir( + name = "dec_internal_opt_ir_benchmark", + src = ":dec_internal_verilog.opt.ir", + benchmark_ir_args = { + # "top": " __dec__ZstdDecoderInternalInst__ZstdDecoderInternal_0__32_64_8_2_4_next", + "pipeline_stages": "4", + "delay_model": "asap7", + }, + tags = ["manual"], +) + +verilog_library( + name = "dec_internal_verilog_lib", + srcs = [ + ":csr_config.v", + ], + tags = ["manual"], +) + +synthesize_rtl( + name = "dec_internal_synth_asap7", + standard_cells = "@org_theopenroadproject_asap7sc7p5t_28//:asap7-sc7p5t_rev28_rvt", + tags = ["manual"], + top_module = "CsrConfig", + deps = [ + ":dec_internal_verilog_lib", + ], +) + +benchmark_synth( + name = "dec_internal_benchmark_synth", + synth_target = ":dec_internal_synth_asap7", + tags = ["manual"], +) + +place_and_route( + name = "dec_internal_place_and_route", + clock_period = "750", + core_padding_microns = 2, + min_pin_distance = "0.5", + placement_density = "0.30", + stop_after_step = "global_routing", + synthesized_rtl = ":dec_internal_synth_asap7", + tags = ["manual"], + target_die_utilization_percentage = "10", +) + +py_binary( + name = "zstd_dec_cocotb_test", + srcs = ["zstd_dec_cocotb_test.py"], + data = [ + ":arbiter.v", + ":axi_interconnect.v", + ":axi_interconnect_wrapper.v", + ":dec.v", + ":priority_encoder.v", + ":xls_fifo_wrapper.v", + ":zstd_dec_wrapper.v", + "@com_icarus_iverilog//:iverilog", + "@com_icarus_iverilog//:vvp", + ], + imports = ["."], + tags = ["manual"], + visibility = ["//xls:xls_users"], + deps = [ + requirement("cocotb"), + requirement("cocotbext-axi"), + requirement("pytest"), + requirement("zstandard"), + "//xls/common:runfiles", + "//xls/modules/zstd/cocotb:channel", + "//xls/modules/zstd/cocotb:data_generator", + "//xls/modules/zstd/cocotb:memory", + "//xls/modules/zstd/cocotb:utils", + "//xls/modules/zstd/cocotb:xlsstruct", + "@com_google_absl_py//absl:app", + "@com_google_absl_py//absl/flags", + "@com_google_protobuf//:protobuf_python", + ], +) diff --git a/xls/modules/zstd/arbiter.v b/xls/modules/zstd/arbiter.v new file mode 100644 index 0000000000..cfac70d1c6 --- /dev/null +++ b/xls/modules/zstd/arbiter.v @@ -0,0 +1,159 @@ +/* + +Copyright (c) 2014-2021 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`resetall +`timescale 1ns / 1ps +`default_nettype none + +/* + * Arbiter module + */ +module arbiter # +( + parameter PORTS = 4, + // select round robin arbitration + parameter ARB_TYPE_ROUND_ROBIN = 0, + // blocking arbiter enable + parameter ARB_BLOCK = 0, + // block on acknowledge assert when nonzero, request deassert when 0 + parameter ARB_BLOCK_ACK = 1, + // LSB priority selection + parameter ARB_LSB_HIGH_PRIORITY = 0 +) +( + input wire clk, + input wire rst, + + input wire [PORTS-1:0] request, + input wire [PORTS-1:0] acknowledge, + + output wire [PORTS-1:0] grant, + output wire grant_valid, + output wire [$clog2(PORTS)-1:0] grant_encoded +); + +reg [PORTS-1:0] grant_reg = 0, grant_next; +reg grant_valid_reg = 0, grant_valid_next; +reg [$clog2(PORTS)-1:0] grant_encoded_reg = 0, grant_encoded_next; + +assign grant_valid = grant_valid_reg; +assign grant = grant_reg; +assign grant_encoded = grant_encoded_reg; + +wire request_valid; +wire [$clog2(PORTS)-1:0] request_index; +wire [PORTS-1:0] request_mask; + +priority_encoder #( + .WIDTH(PORTS), + .LSB_HIGH_PRIORITY(ARB_LSB_HIGH_PRIORITY) +) +priority_encoder_inst ( + .input_unencoded(request), + .output_valid(request_valid), + .output_encoded(request_index), + .output_unencoded(request_mask) +); + +reg [PORTS-1:0] mask_reg = 0, mask_next; + +wire masked_request_valid; +wire [$clog2(PORTS)-1:0] masked_request_index; +wire [PORTS-1:0] masked_request_mask; + +priority_encoder #( + .WIDTH(PORTS), + .LSB_HIGH_PRIORITY(ARB_LSB_HIGH_PRIORITY) +) +priority_encoder_masked ( + .input_unencoded(request & mask_reg), + .output_valid(masked_request_valid), + .output_encoded(masked_request_index), + .output_unencoded(masked_request_mask) +); + +always @* begin + grant_next = 0; + grant_valid_next = 0; + grant_encoded_next = 0; + mask_next = mask_reg; + + if (ARB_BLOCK && !ARB_BLOCK_ACK && grant_reg & request) begin + // granted request still asserted; hold it + grant_valid_next = grant_valid_reg; + grant_next = grant_reg; + grant_encoded_next = grant_encoded_reg; + end else if (ARB_BLOCK && ARB_BLOCK_ACK && grant_valid && !(grant_reg & acknowledge)) begin + // granted request not yet acknowledged; hold it + grant_valid_next = grant_valid_reg; + grant_next = grant_reg; + grant_encoded_next = grant_encoded_reg; + end else if (request_valid) begin + if (ARB_TYPE_ROUND_ROBIN) begin + if (masked_request_valid) begin + grant_valid_next = 1; + grant_next = masked_request_mask; + grant_encoded_next = masked_request_index; + if (ARB_LSB_HIGH_PRIORITY) begin + mask_next = {PORTS{1'b1}} << (masked_request_index + 1); + end else begin + mask_next = {PORTS{1'b1}} >> (PORTS - masked_request_index); + end + end else begin + grant_valid_next = 1; + grant_next = request_mask; + grant_encoded_next = request_index; + if (ARB_LSB_HIGH_PRIORITY) begin + mask_next = {PORTS{1'b1}} << (request_index + 1); + end else begin + mask_next = {PORTS{1'b1}} >> (PORTS - request_index); + end + end + end else begin + grant_valid_next = 1; + grant_next = request_mask; + grant_encoded_next = request_index; + end + end +end + +always @(posedge clk) begin + if (rst) begin + grant_reg <= 0; + grant_valid_reg <= 0; + grant_encoded_reg <= 0; + mask_reg <= 0; + end else begin + grant_reg <= grant_next; + grant_valid_reg <= grant_valid_next; + grant_encoded_reg <= grant_encoded_next; + mask_reg <= mask_next; + end +end + +endmodule + +`resetall diff --git a/xls/modules/zstd/axi_csr_accessor.x b/xls/modules/zstd/axi_csr_accessor.x new file mode 100644 index 0000000000..01cef68cd9 --- /dev/null +++ b/xls/modules/zstd/axi_csr_accessor.x @@ -0,0 +1,384 @@ +// Copyright 2023-2024 The XLS Authors +// +// 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. + +// This file contains implementation of a proc that handles CSRs. It provides +// an AXI interface for reading and writing the values as well as separate +// request/response channels. Apart from that it has an output channel which +// notifies aboud changes made to CSRs. + +import std; +import xls.modules.zstd.memory.axi; +import xls.modules.zstd.csr_config; + + +struct AxiCsrAccessorState { + w_id: uN[ID_W], + w_addr: uN[ADDR_W], + r_id: uN[ID_W], + r_addr: uN[ADDR_W], +} + +pub proc AxiCsrAccessor< + ID_W: u32, ADDR_W: u32, DATA_W: u32, REGS_N: u32, + DATA_W_DIV8: u32 = { DATA_W / u32:8 }, + LOG2_REGS_N: u32 = { std::clog2(REGS_N) }, + LOG2_DATA_W_DIV8: u32 = { std::clog2(DATA_W / u32:8) }, +> { + type AxiAw = axi::AxiAw; + type AxiW = axi::AxiW; + type AxiB = axi::AxiB; + type AxiAr = axi::AxiAr; + type AxiR = axi::AxiR; + + type RdReq = csr_config::CsrRdReq; + type RdResp = csr_config::CsrRdResp; + type WrReq = csr_config::CsrWrReq; + type WrResp = csr_config::CsrWrResp; + + type State = AxiCsrAccessorState; + type Data = uN[DATA_W]; + type RegN = uN[LOG2_REGS_N]; + + axi_aw_r: chan in; + axi_w_r: chan in; + axi_b_s: chan out; + axi_ar_r: chan in; + axi_r_s: chan out; + + csr_rd_req_s: chan out; + csr_rd_resp_r: chan in; + csr_wr_req_s: chan out; + csr_wr_resp_r: chan in; + + config ( + axi_aw_r: chan in, + axi_w_r: chan in, + axi_b_s: chan out, + axi_ar_r: chan in, + axi_r_s: chan out, + + csr_rd_req_s: chan out, + csr_rd_resp_r: chan in, + csr_wr_req_s: chan out, + csr_wr_resp_r: chan in, + ) { + ( + axi_aw_r, axi_w_r, axi_b_s, + axi_ar_r, axi_r_s, + csr_rd_req_s, csr_rd_resp_r, + csr_wr_req_s, csr_wr_resp_r, + ) + } + + init { + zero!() + } + + next (state: State) { + let tok_0 = join(); + // write to CSR via AXI + let (tok_1_1, axi_aw, axi_aw_valid) = recv_non_blocking(tok_0, axi_aw_r, AxiAw {id: state.w_id, addr: state.w_addr, ..zero!()}); + + // validate axi aw + assert!(!(axi_aw_valid && axi_aw.addr as u32 >= REGS_N), "invalid_aw_addr"); + assert!(!(axi_aw_valid && axi_aw.len != u8:0), "invalid_aw_len"); + + let (tok_1_2, axi_w, axi_w_valid) = recv_non_blocking(tok_1_1, axi_w_r, zero!()); + + // Send WriteRequest to CSRs + let data_w = if axi_w_valid { + let (w_data, _, _) = for (i, (w_data, strb, mask)): (u32, (uN[DATA_W], uN[DATA_W_DIV8], uN[DATA_W])) in range(u32:0, DATA_W_DIV8) { + let w_data = if axi_w.strb as u1 { + w_data | (axi_w.data & mask) + } else { + w_data + }; + ( + w_data, + strb >> u32:1, + mask << u32:8, + ) + }((uN[DATA_W]:0, axi_w.strb, uN[DATA_W]:0xFF)); + w_data + } else { + uN[DATA_W]:0 + }; + + let wr_req = WrReq { + csr: (axi_aw.addr >> LOG2_DATA_W_DIV8) as uN[LOG2_REGS_N], + value: data_w + }; + + let tok_1_3 = send_if(tok_1_2, csr_wr_req_s, axi_w_valid, wr_req); + + let (tok_2_1, csr_wr_resp, csr_wr_resp_valid) = recv_non_blocking(tok_0, csr_wr_resp_r, zero!()); + let axi_write_resp = AxiB { + resp: axi::AxiWriteResp::OKAY, + id: axi_aw.id, + }; + let tok_2_2 = send_if(tok_2_1, axi_b_s, csr_wr_resp_valid, axi_write_resp); + + + // Send ReadRequest to CSRs + let (tok_3_1, axi_ar, axi_ar_valid) = recv_non_blocking(tok_0, axi_ar_r, AxiAr {id: state.r_id, addr: state.r_addr, ..zero!()}); + // validate ar bundle + assert!(!(axi_ar_valid && axi_ar.addr as u32 >= REGS_N), "invalid_ar_addr"); + assert!(!(axi_ar_valid && axi_ar.len != u8:0), "invalid_ar_len"); + let rd_req = RdReq { + csr: (axi_ar.addr >> LOG2_DATA_W_DIV8) as uN[LOG2_REGS_N], + }; + let tok_3_2 = send_if(tok_3_1, csr_rd_req_s, axi_ar_valid, rd_req); + + let (tok_4_1, csr_rd_resp, csr_rd_resp_valid) = recv_non_blocking(tok_0, csr_rd_resp_r, zero!()); + + let axi_read_resp = AxiR { + id: axi_ar.id, + data: csr_rd_resp.value, + resp: axi::AxiReadResp::OKAY, + last: true, + }; + let tok_4_2 = send_if(tok_4_1, axi_r_s, csr_rd_resp_valid, axi_read_resp); + + State { + w_id: axi_aw.id, + w_addr: axi_aw.addr, + r_id: axi_ar.id, + r_addr: axi_ar.addr, + } + } +} + +const INST_ID_W = u32:4; +const INST_DATA_W = u32:32; +const INST_ADDR_W = u32:16; +const INST_REGS_N = u32:16; +const INST_DATA_W_DIV8 = INST_DATA_W / u32:8; +const INST_LOG2_REGS_N = std::clog2(INST_REGS_N); + +proc AxiCsrAccessorInst { + type InstAxiAw = axi::AxiAw; + type InstAxiW = axi::AxiW; + type InstAxiB = axi::AxiB; + type InstAxiAr = axi::AxiAr; + type InstAxiR = axi::AxiR; + + type InstCsrRdReq = csr_config::CsrRdReq; + type InstCsrRdResp = csr_config::CsrRdResp; + type InstCsrWrReq = csr_config::CsrWrReq; + type InstCsrWrResp = csr_config::CsrWrResp; + type InstCsrChange = csr_config::CsrChange; + + config( + axi_aw_r: chan in, + axi_w_r: chan in, + axi_b_s: chan out, + axi_ar_r: chan in, + axi_r_s: chan out, + + + csr_rd_req_s: chan out, + csr_rd_resp_r: chan in, + csr_wr_req_s: chan out, + csr_wr_resp_r: chan in, + ) { + spawn AxiCsrAccessor ( + axi_aw_r, axi_w_r, axi_b_s, + axi_ar_r, axi_r_s, + csr_rd_req_s, csr_rd_resp_r, + csr_wr_req_s, csr_wr_resp_r, + ); + } + + init { } + + next (state: ()) { } +} + +const TEST_ID_W = u32:4; +const TEST_DATA_W = u32:32; +const TEST_ADDR_W = u32:16; +const TEST_REGS_N = u32:16; +const TEST_DATA_W_DIV8 = TEST_DATA_W / u32:8; +const TEST_LOG2_REGS_N = std::clog2(TEST_REGS_N); +const TEST_LOG2_DATA_W_DIV8 = std::clog2(TEST_DATA_W_DIV8); + +type TestCsr = uN[TEST_LOG2_REGS_N]; +type TestValue = uN[TEST_DATA_W]; + +struct TestData { + csr: uN[TEST_LOG2_REGS_N], + value: uN[TEST_DATA_W], +} + +const TEST_DATA = TestData[20]:[ + TestData{ csr: TestCsr:0, value: TestValue:0xca32_9f4a }, + TestData{ csr: TestCsr:1, value: TestValue:0x0fb3_fa42 }, + TestData{ csr: TestCsr:2, value: TestValue:0xe7ee_da41 }, + TestData{ csr: TestCsr:3, value: TestValue:0xef51_f98c }, + TestData{ csr: TestCsr:0, value: TestValue:0x97a3_a2d2 }, + TestData{ csr: TestCsr:0, value: TestValue:0xea06_e94b }, + TestData{ csr: TestCsr:1, value: TestValue:0x5fac_17ce }, + TestData{ csr: TestCsr:3, value: TestValue:0xf9d8_9938 }, + TestData{ csr: TestCsr:2, value: TestValue:0xc262_2d2e }, + TestData{ csr: TestCsr:2, value: TestValue:0xb4dd_424e }, + TestData{ csr: TestCsr:1, value: TestValue:0x01f9_b9e4 }, + TestData{ csr: TestCsr:1, value: TestValue:0x3020_6eec }, + TestData{ csr: TestCsr:3, value: TestValue:0x3124_87b5 }, + TestData{ csr: TestCsr:0, value: TestValue:0x0a49_f5e3 }, + TestData{ csr: TestCsr:2, value: TestValue:0xde3b_5d0f }, + TestData{ csr: TestCsr:3, value: TestValue:0x5948_c1b3 }, + TestData{ csr: TestCsr:0, value: TestValue:0xa26d_851f }, + TestData{ csr: TestCsr:3, value: TestValue:0x3fa9_59c0 }, + TestData{ csr: TestCsr:1, value: TestValue:0x4efd_dd09 }, + TestData{ csr: TestCsr:1, value: TestValue:0x6d75_058a }, +]; + +#[test_proc] +proc AxiCsrAccessorTest { + type TestAxiAw = axi::AxiAw; + type TestAxiW = axi::AxiW; + type TestAxiB = axi::AxiB; + type TestAxiAr = axi::AxiAr; + type TestAxiR = axi::AxiR; + + + type TestCsrRdReq = csr_config::CsrRdReq; + type TestCsrRdResp = csr_config::CsrRdResp; + type TestCsrWrReq = csr_config::CsrWrReq; + type TestCsrWrResp = csr_config::CsrWrResp; + type TestCsrChange = csr_config::CsrChange; + + terminator: chan out; + + axi_aw_s: chan out; + axi_w_s: chan out; + axi_b_r: chan in; + axi_ar_s: chan out; + axi_r_r: chan in; + + csr_rd_req_r: chan in; + csr_rd_resp_s: chan out; + csr_wr_req_r: chan in; + csr_wr_resp_s: chan out; + + config (terminator: chan out) { + let (axi_aw_s, axi_aw_r) = chan("axi_aw"); + let (axi_w_s, axi_w_r) = chan("axi_w"); + let (axi_b_s, axi_b_r) = chan("axi_b"); + let (axi_ar_s, axi_ar_r) = chan("axi_ar"); + let (axi_r_s, axi_r_r) = chan("axi_r"); + + let (csr_rd_req_s, csr_rd_req_r) = chan("csr_rd_req"); + let (csr_rd_resp_s, csr_rd_resp_r) = chan("csr_rd_resp"); + + let (csr_wr_req_s, csr_wr_req_r) = chan("csr_wr_req"); + let (csr_wr_resp_s, csr_wr_resp_r) = chan("csr_wr_resp"); + + spawn AxiCsrAccessor ( + axi_aw_r, axi_w_r, axi_b_s, + axi_ar_r, axi_r_s, + csr_rd_req_s, csr_rd_resp_r, + csr_wr_req_s, csr_wr_resp_r, + ); + + ( + terminator, + axi_aw_s, axi_w_s, axi_b_r, + axi_ar_s, axi_r_r, + csr_rd_req_r, csr_rd_resp_s, + csr_wr_req_r, csr_wr_resp_s, + ) + } + + init { } + + next (state: ()) { + // test writing via AXI + let tok = for ((i, test_data), tok): ((u32, TestData), token) in enumerate(TEST_DATA) { + // write CSR via AXI + let axi_aw = TestAxiAw { + id: i as uN[TEST_ID_W], + addr: (test_data.csr << TEST_LOG2_DATA_W_DIV8) as uN[TEST_ADDR_W], + size: axi::AxiAxSize::MAX_4B_TRANSFER, + len: u8:0, + burst: axi::AxiAxBurst::FIXED, + }; + let tok = send(tok, axi_aw_s, axi_aw); + trace_fmt!("Sent #{} AXI AW: {:#x}", i + u32:1, axi_aw); + + let axi_w = TestAxiW { + data: test_data.value, + strb: !uN[TEST_DATA_W_DIV8]:0, + last: true, + }; + let tok = send(tok, axi_w_s, axi_w); + trace_fmt!("Sent #{} AXI W: {:#x}", i + u32:1, axi_w); + + let expected_wr_req = TestCsrWrReq { + csr: test_data.csr, + value: test_data.value + }; + let (tok, wr_req) = recv(tok, csr_wr_req_r); + trace_fmt!("Received #{} CSR WriteRequest: {:#x}", i + u32:1, wr_req); + assert_eq(expected_wr_req, wr_req); + + let tok = send(tok, csr_wr_resp_s, TestCsrWrResp{}); + trace_fmt!("Sent #{} CsrWrResp", i + u32:1); + let (tok, axi_b) = recv(tok, axi_b_r); + trace_fmt!("Received #{} AXI B: {:#x}", i + u32:1, axi_b); + let expected_axi_resp = TestAxiB{ + resp: axi::AxiWriteResp::OKAY, + id: i as uN[TEST_ID_W], + }; + assert_eq(expected_axi_resp, axi_b); + + // read CSRs via AXI + let axi_ar = TestAxiAr { + id: i as uN[TEST_ID_W], + addr: (test_data.csr << TEST_LOG2_DATA_W_DIV8) as uN[TEST_ADDR_W], + len: u8:0, + ..zero!() + }; + let tok = send(tok, axi_ar_s, axi_ar); + trace_fmt!("Sent #{} AXI AR: {:#x}", i + u32:1, axi_ar); + + let expected_rd_req = TestCsrRdReq { + csr: test_data.csr, + }; + let (tok, rd_req) = recv(tok, csr_rd_req_r); + trace_fmt!("Received #{} CSR ReadRequest: {:#x}", i + u32:1, rd_req); + assert_eq(expected_rd_req, rd_req); + let rd_resp = TestCsrRdResp { + csr: test_data.csr, + value: test_data.value + }; + let tok = send(tok, csr_rd_resp_s, rd_resp); + trace_fmt!("Sent #{} CsrRdResp: {:#x}", i + u32:1, rd_resp); + + let (tok, axi_r) = recv(tok, axi_r_r); + trace_fmt!("Received #{} AXI R: {:#x}", i + u32:1, axi_r); + let expected_axi_rd_resp = TestAxiR{ + id: i as uN[TEST_ID_W], + data: test_data.value, + resp: axi::AxiReadResp::OKAY, + last: true, + }; + assert_eq(expected_axi_rd_resp, axi_r); + + tok + }(join()); + + send(tok, terminator, true); + } +} diff --git a/xls/modules/zstd/axi_interconnect.v b/xls/modules/zstd/axi_interconnect.v new file mode 100644 index 0000000000..14256ad7de --- /dev/null +++ b/xls/modules/zstd/axi_interconnect.v @@ -0,0 +1,988 @@ +/* + +Copyright (c) 2018 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`resetall +`timescale 1ns / 1ps +`default_nettype none + +/* + * AXI4 interconnect + */ +module axi_interconnect # +( + // Number of AXI inputs (slave interfaces) + parameter S_COUNT = 4, + // Number of AXI outputs (master interfaces) + parameter M_COUNT = 4, + // Width of data bus in bits + parameter DATA_WIDTH = 32, + // Width of address bus in bits + parameter ADDR_WIDTH = 32, + // Width of wstrb (width of data bus in words) + parameter STRB_WIDTH = (DATA_WIDTH/8), + // Width of ID signal + parameter ID_WIDTH = 8, + // Propagate awuser signal + parameter AWUSER_ENABLE = 0, + // Width of awuser signal + parameter AWUSER_WIDTH = 1, + // Propagate wuser signal + parameter WUSER_ENABLE = 0, + // Width of wuser signal + parameter WUSER_WIDTH = 1, + // Propagate buser signal + parameter BUSER_ENABLE = 0, + // Width of buser signal + parameter BUSER_WIDTH = 1, + // Propagate aruser signal + parameter ARUSER_ENABLE = 0, + // Width of aruser signal + parameter ARUSER_WIDTH = 1, + // Propagate ruser signal + parameter RUSER_ENABLE = 0, + // Width of ruser signal + parameter RUSER_WIDTH = 1, + // Propagate ID field + parameter FORWARD_ID = 0, + // Number of regions per master interface + parameter M_REGIONS = 1, + // Master interface base addresses + // M_COUNT concatenated fields of M_REGIONS concatenated fields of ADDR_WIDTH bits + // set to zero for default addressing based on M_ADDR_WIDTH + parameter M_BASE_ADDR = 0, + // Master interface address widths + // M_COUNT concatenated fields of M_REGIONS concatenated fields of 32 bits + parameter M_ADDR_WIDTH = {M_COUNT{{M_REGIONS{32'd24}}}}, + // Read connections between interfaces + // M_COUNT concatenated fields of S_COUNT bits + parameter M_CONNECT_READ = {M_COUNT{{S_COUNT{1'b1}}}}, + // Write connections between interfaces + // M_COUNT concatenated fields of S_COUNT bits + parameter M_CONNECT_WRITE = {M_COUNT{{S_COUNT{1'b1}}}}, + // Secure master (fail operations based on awprot/arprot) + // M_COUNT bits + parameter M_SECURE = {M_COUNT{1'b0}} +) +( + input wire clk, + input wire rst, + + /* + * AXI slave interfaces + */ + input wire [S_COUNT*ID_WIDTH-1:0] s_axi_awid, + input wire [S_COUNT*ADDR_WIDTH-1:0] s_axi_awaddr, + input wire [S_COUNT*8-1:0] s_axi_awlen, + input wire [S_COUNT*3-1:0] s_axi_awsize, + input wire [S_COUNT*2-1:0] s_axi_awburst, + input wire [S_COUNT-1:0] s_axi_awlock, + input wire [S_COUNT*4-1:0] s_axi_awcache, + input wire [S_COUNT*3-1:0] s_axi_awprot, + input wire [S_COUNT*4-1:0] s_axi_awqos, + input wire [S_COUNT*AWUSER_WIDTH-1:0] s_axi_awuser, + input wire [S_COUNT-1:0] s_axi_awvalid, + output wire [S_COUNT-1:0] s_axi_awready, + input wire [S_COUNT*DATA_WIDTH-1:0] s_axi_wdata, + input wire [S_COUNT*STRB_WIDTH-1:0] s_axi_wstrb, + input wire [S_COUNT-1:0] s_axi_wlast, + input wire [S_COUNT*WUSER_WIDTH-1:0] s_axi_wuser, + input wire [S_COUNT-1:0] s_axi_wvalid, + output wire [S_COUNT-1:0] s_axi_wready, + output wire [S_COUNT*ID_WIDTH-1:0] s_axi_bid, + output wire [S_COUNT*2-1:0] s_axi_bresp, + output wire [S_COUNT*BUSER_WIDTH-1:0] s_axi_buser, + output wire [S_COUNT-1:0] s_axi_bvalid, + input wire [S_COUNT-1:0] s_axi_bready, + input wire [S_COUNT*ID_WIDTH-1:0] s_axi_arid, + input wire [S_COUNT*ADDR_WIDTH-1:0] s_axi_araddr, + input wire [S_COUNT*8-1:0] s_axi_arlen, + input wire [S_COUNT*3-1:0] s_axi_arsize, + input wire [S_COUNT*2-1:0] s_axi_arburst, + input wire [S_COUNT-1:0] s_axi_arlock, + input wire [S_COUNT*4-1:0] s_axi_arcache, + input wire [S_COUNT*3-1:0] s_axi_arprot, + input wire [S_COUNT*4-1:0] s_axi_arqos, + input wire [S_COUNT*ARUSER_WIDTH-1:0] s_axi_aruser, + input wire [S_COUNT-1:0] s_axi_arvalid, + output wire [S_COUNT-1:0] s_axi_arready, + output wire [S_COUNT*ID_WIDTH-1:0] s_axi_rid, + output wire [S_COUNT*DATA_WIDTH-1:0] s_axi_rdata, + output wire [S_COUNT*2-1:0] s_axi_rresp, + output wire [S_COUNT-1:0] s_axi_rlast, + output wire [S_COUNT*RUSER_WIDTH-1:0] s_axi_ruser, + output wire [S_COUNT-1:0] s_axi_rvalid, + input wire [S_COUNT-1:0] s_axi_rready, + + /* + * AXI master interfaces + */ + output wire [M_COUNT*ID_WIDTH-1:0] m_axi_awid, + output wire [M_COUNT*ADDR_WIDTH-1:0] m_axi_awaddr, + output wire [M_COUNT*8-1:0] m_axi_awlen, + output wire [M_COUNT*3-1:0] m_axi_awsize, + output wire [M_COUNT*2-1:0] m_axi_awburst, + output wire [M_COUNT-1:0] m_axi_awlock, + output wire [M_COUNT*4-1:0] m_axi_awcache, + output wire [M_COUNT*3-1:0] m_axi_awprot, + output wire [M_COUNT*4-1:0] m_axi_awqos, + output wire [M_COUNT*4-1:0] m_axi_awregion, + output wire [M_COUNT*AWUSER_WIDTH-1:0] m_axi_awuser, + output wire [M_COUNT-1:0] m_axi_awvalid, + input wire [M_COUNT-1:0] m_axi_awready, + output wire [M_COUNT*DATA_WIDTH-1:0] m_axi_wdata, + output wire [M_COUNT*STRB_WIDTH-1:0] m_axi_wstrb, + output wire [M_COUNT-1:0] m_axi_wlast, + output wire [M_COUNT*WUSER_WIDTH-1:0] m_axi_wuser, + output wire [M_COUNT-1:0] m_axi_wvalid, + input wire [M_COUNT-1:0] m_axi_wready, + input wire [M_COUNT*ID_WIDTH-1:0] m_axi_bid, + input wire [M_COUNT*2-1:0] m_axi_bresp, + input wire [M_COUNT*BUSER_WIDTH-1:0] m_axi_buser, + input wire [M_COUNT-1:0] m_axi_bvalid, + output wire [M_COUNT-1:0] m_axi_bready, + output wire [M_COUNT*ID_WIDTH-1:0] m_axi_arid, + output wire [M_COUNT*ADDR_WIDTH-1:0] m_axi_araddr, + output wire [M_COUNT*8-1:0] m_axi_arlen, + output wire [M_COUNT*3-1:0] m_axi_arsize, + output wire [M_COUNT*2-1:0] m_axi_arburst, + output wire [M_COUNT-1:0] m_axi_arlock, + output wire [M_COUNT*4-1:0] m_axi_arcache, + output wire [M_COUNT*3-1:0] m_axi_arprot, + output wire [M_COUNT*4-1:0] m_axi_arqos, + output wire [M_COUNT*4-1:0] m_axi_arregion, + output wire [M_COUNT*ARUSER_WIDTH-1:0] m_axi_aruser, + output wire [M_COUNT-1:0] m_axi_arvalid, + input wire [M_COUNT-1:0] m_axi_arready, + input wire [M_COUNT*ID_WIDTH-1:0] m_axi_rid, + input wire [M_COUNT*DATA_WIDTH-1:0] m_axi_rdata, + input wire [M_COUNT*2-1:0] m_axi_rresp, + input wire [M_COUNT-1:0] m_axi_rlast, + input wire [M_COUNT*RUSER_WIDTH-1:0] m_axi_ruser, + input wire [M_COUNT-1:0] m_axi_rvalid, + output wire [M_COUNT-1:0] m_axi_rready +); + +parameter CL_S_COUNT = $clog2(S_COUNT); +parameter CL_M_COUNT = $clog2(M_COUNT); + +parameter AUSER_WIDTH = AWUSER_WIDTH > ARUSER_WIDTH ? AWUSER_WIDTH : ARUSER_WIDTH; + +// default address computation +function [M_COUNT*M_REGIONS*ADDR_WIDTH-1:0] calcBaseAddrs(input [31:0] dummy); + integer i; + reg [ADDR_WIDTH-1:0] base; + reg [ADDR_WIDTH-1:0] width; + reg [ADDR_WIDTH-1:0] size; + reg [ADDR_WIDTH-1:0] mask; + begin + calcBaseAddrs = {M_COUNT*M_REGIONS*ADDR_WIDTH{1'b0}}; + base = 0; + for (i = 0; i < M_COUNT*M_REGIONS; i = i + 1) begin + width = M_ADDR_WIDTH[i*32 +: 32]; + mask = {ADDR_WIDTH{1'b1}} >> (ADDR_WIDTH - width); + size = mask + 1; + if (width > 0) begin + if ((base & mask) != 0) begin + base = base + size - (base & mask); // align + end + calcBaseAddrs[i * ADDR_WIDTH +: ADDR_WIDTH] = base; + base = base + size; // increment + end + end + end +endfunction + +parameter M_BASE_ADDR_INT = M_BASE_ADDR ? M_BASE_ADDR : calcBaseAddrs(0); + +integer i, j; + +// check configuration +initial begin + if (M_REGIONS < 1 || M_REGIONS > 16) begin + $error("Error: M_REGIONS must be between 1 and 16 (instance %m)"); + $finish; + end + + for (i = 0; i < M_COUNT*M_REGIONS; i = i + 1) begin + if (M_ADDR_WIDTH[i*32 +: 32] && (M_ADDR_WIDTH[i*32 +: 32] < 12 || M_ADDR_WIDTH[i*32 +: 32] > ADDR_WIDTH)) begin + $error("Error: address width out of range (instance %m)"); + $finish; + end + end + + $display("Addressing configuration for axi_interconnect instance %m"); + for (i = 0; i < M_COUNT*M_REGIONS; i = i + 1) begin + if (M_ADDR_WIDTH[i*32 +: 32]) begin + $display("%2d (%2d): %x / %02d -- %x-%x", + i/M_REGIONS, i%M_REGIONS, + M_BASE_ADDR_INT[i*ADDR_WIDTH +: ADDR_WIDTH], + M_ADDR_WIDTH[i*32 +: 32], + M_BASE_ADDR_INT[i*ADDR_WIDTH +: ADDR_WIDTH] & ({ADDR_WIDTH{1'b1}} << M_ADDR_WIDTH[i*32 +: 32]), + M_BASE_ADDR_INT[i*ADDR_WIDTH +: ADDR_WIDTH] | ({ADDR_WIDTH{1'b1}} >> (ADDR_WIDTH - M_ADDR_WIDTH[i*32 +: 32])) + ); + end + end + + for (i = 0; i < M_COUNT*M_REGIONS; i = i + 1) begin + if ((M_BASE_ADDR_INT[i*ADDR_WIDTH +: ADDR_WIDTH] & (2**M_ADDR_WIDTH[i*32 +: 32]-1)) != 0) begin + $display("Region not aligned:"); + $display("%2d (%2d): %x / %2d -- %x-%x", + i/M_REGIONS, i%M_REGIONS, + M_BASE_ADDR_INT[i*ADDR_WIDTH +: ADDR_WIDTH], + M_ADDR_WIDTH[i*32 +: 32], + M_BASE_ADDR_INT[i*ADDR_WIDTH +: ADDR_WIDTH] & ({ADDR_WIDTH{1'b1}} << M_ADDR_WIDTH[i*32 +: 32]), + M_BASE_ADDR_INT[i*ADDR_WIDTH +: ADDR_WIDTH] | ({ADDR_WIDTH{1'b1}} >> (ADDR_WIDTH - M_ADDR_WIDTH[i*32 +: 32])) + ); + $error("Error: address range not aligned (instance %m)"); + $finish; + end + end + + for (i = 0; i < M_COUNT*M_REGIONS; i = i + 1) begin + for (j = i+1; j < M_COUNT*M_REGIONS; j = j + 1) begin + if (M_ADDR_WIDTH[i*32 +: 32] && M_ADDR_WIDTH[j*32 +: 32]) begin + if (((M_BASE_ADDR_INT[i*ADDR_WIDTH +: ADDR_WIDTH] & ({ADDR_WIDTH{1'b1}} << M_ADDR_WIDTH[i*32 +: 32])) <= (M_BASE_ADDR_INT[j*ADDR_WIDTH +: ADDR_WIDTH] | ({ADDR_WIDTH{1'b1}} >> (ADDR_WIDTH - M_ADDR_WIDTH[j*32 +: 32])))) + && ((M_BASE_ADDR_INT[j*ADDR_WIDTH +: ADDR_WIDTH] & ({ADDR_WIDTH{1'b1}} << M_ADDR_WIDTH[j*32 +: 32])) <= (M_BASE_ADDR_INT[i*ADDR_WIDTH +: ADDR_WIDTH] | ({ADDR_WIDTH{1'b1}} >> (ADDR_WIDTH - M_ADDR_WIDTH[i*32 +: 32]))))) begin + $display("Overlapping regions:"); + $display("%2d (%2d): %x / %2d -- %x-%x", + i/M_REGIONS, i%M_REGIONS, + M_BASE_ADDR_INT[i*ADDR_WIDTH +: ADDR_WIDTH], + M_ADDR_WIDTH[i*32 +: 32], + M_BASE_ADDR_INT[i*ADDR_WIDTH +: ADDR_WIDTH] & ({ADDR_WIDTH{1'b1}} << M_ADDR_WIDTH[i*32 +: 32]), + M_BASE_ADDR_INT[i*ADDR_WIDTH +: ADDR_WIDTH] | ({ADDR_WIDTH{1'b1}} >> (ADDR_WIDTH - M_ADDR_WIDTH[i*32 +: 32])) + ); + $display("%2d (%2d): %x / %2d -- %x-%x", + j/M_REGIONS, j%M_REGIONS, + M_BASE_ADDR_INT[j*ADDR_WIDTH +: ADDR_WIDTH], + M_ADDR_WIDTH[j*32 +: 32], + M_BASE_ADDR_INT[j*ADDR_WIDTH +: ADDR_WIDTH] & ({ADDR_WIDTH{1'b1}} << M_ADDR_WIDTH[j*32 +: 32]), + M_BASE_ADDR_INT[j*ADDR_WIDTH +: ADDR_WIDTH] | ({ADDR_WIDTH{1'b1}} >> (ADDR_WIDTH - M_ADDR_WIDTH[j*32 +: 32])) + ); + $error("Error: address ranges overlap (instance %m)"); + $finish; + end + end + end + end +end + +localparam [2:0] + STATE_IDLE = 3'd0, + STATE_DECODE = 3'd1, + STATE_WRITE = 3'd2, + STATE_WRITE_RESP = 3'd3, + STATE_WRITE_DROP = 3'd4, + STATE_READ = 3'd5, + STATE_READ_DROP = 3'd6, + STATE_WAIT_IDLE = 3'd7; + +reg [2:0] state_reg = STATE_IDLE, state_next; + +reg match; + +reg [CL_M_COUNT-1:0] m_select_reg = 2'd0, m_select_next; +reg [ID_WIDTH-1:0] axi_id_reg = {ID_WIDTH{1'b0}}, axi_id_next; +reg [ADDR_WIDTH-1:0] axi_addr_reg = {ADDR_WIDTH{1'b0}}, axi_addr_next; +reg axi_addr_valid_reg = 1'b0, axi_addr_valid_next; +reg [7:0] axi_len_reg = 8'd0, axi_len_next; +reg [2:0] axi_size_reg = 3'd0, axi_size_next; +reg [1:0] axi_burst_reg = 2'd0, axi_burst_next; +reg axi_lock_reg = 1'b0, axi_lock_next; +reg [3:0] axi_cache_reg = 4'd0, axi_cache_next; +reg [2:0] axi_prot_reg = 3'b000, axi_prot_next; +reg [3:0] axi_qos_reg = 4'd0, axi_qos_next; +reg [3:0] axi_region_reg = 4'd0, axi_region_next; +reg [AUSER_WIDTH-1:0] axi_auser_reg = {AUSER_WIDTH{1'b0}}, axi_auser_next; +reg [1:0] axi_bresp_reg = 2'b00, axi_bresp_next; +reg [BUSER_WIDTH-1:0] axi_buser_reg = {BUSER_WIDTH{1'b0}}, axi_buser_next; + +reg [S_COUNT-1:0] s_axi_awready_reg = 0, s_axi_awready_next; +reg [S_COUNT-1:0] s_axi_wready_reg = 0, s_axi_wready_next; +reg [S_COUNT-1:0] s_axi_bvalid_reg = 0, s_axi_bvalid_next; +reg [S_COUNT-1:0] s_axi_arready_reg = 0, s_axi_arready_next; + +reg [M_COUNT-1:0] m_axi_awvalid_reg = 0, m_axi_awvalid_next; +reg [M_COUNT-1:0] m_axi_bready_reg = 0, m_axi_bready_next; +reg [M_COUNT-1:0] m_axi_arvalid_reg = 0, m_axi_arvalid_next; +reg [M_COUNT-1:0] m_axi_rready_reg = 0, m_axi_rready_next; + +// internal datapath +reg [ID_WIDTH-1:0] s_axi_rid_int; +reg [DATA_WIDTH-1:0] s_axi_rdata_int; +reg [1:0] s_axi_rresp_int; +reg s_axi_rlast_int; +reg [RUSER_WIDTH-1:0] s_axi_ruser_int; +reg s_axi_rvalid_int; +reg s_axi_rready_int_reg = 1'b0; +wire s_axi_rready_int_early; + +reg [DATA_WIDTH-1:0] m_axi_wdata_int; +reg [STRB_WIDTH-1:0] m_axi_wstrb_int; +reg m_axi_wlast_int; +reg [WUSER_WIDTH-1:0] m_axi_wuser_int; +reg m_axi_wvalid_int; +reg m_axi_wready_int_reg = 1'b0; +wire m_axi_wready_int_early; + +assign s_axi_awready = s_axi_awready_reg; +assign s_axi_wready = s_axi_wready_reg; +assign s_axi_bid = {S_COUNT{axi_id_reg}}; +assign s_axi_bresp = {S_COUNT{axi_bresp_reg}}; +assign s_axi_buser = {S_COUNT{BUSER_ENABLE ? axi_buser_reg : {BUSER_WIDTH{1'b0}}}}; +assign s_axi_bvalid = s_axi_bvalid_reg; +assign s_axi_arready = s_axi_arready_reg; + +assign m_axi_awid = {M_COUNT{FORWARD_ID ? axi_id_reg : {ID_WIDTH{1'b0}}}}; +assign m_axi_awaddr = {M_COUNT{axi_addr_reg}}; +assign m_axi_awlen = {M_COUNT{axi_len_reg}}; +assign m_axi_awsize = {M_COUNT{axi_size_reg}}; +assign m_axi_awburst = {M_COUNT{axi_burst_reg}}; +assign m_axi_awlock = {M_COUNT{axi_lock_reg}}; +assign m_axi_awcache = {M_COUNT{axi_cache_reg}}; +assign m_axi_awprot = {M_COUNT{axi_prot_reg}}; +assign m_axi_awqos = {M_COUNT{axi_qos_reg}}; +assign m_axi_awregion = {M_COUNT{axi_region_reg}}; +assign m_axi_awuser = {M_COUNT{AWUSER_ENABLE ? axi_auser_reg[AWUSER_WIDTH-1:0] : {AWUSER_WIDTH{1'b0}}}}; +assign m_axi_awvalid = m_axi_awvalid_reg; +assign m_axi_bready = m_axi_bready_reg; +assign m_axi_arid = {M_COUNT{FORWARD_ID ? axi_id_reg : {ID_WIDTH{1'b0}}}}; +assign m_axi_araddr = {M_COUNT{axi_addr_reg}}; +assign m_axi_arlen = {M_COUNT{axi_len_reg}}; +assign m_axi_arsize = {M_COUNT{axi_size_reg}}; +assign m_axi_arburst = {M_COUNT{axi_burst_reg}}; +assign m_axi_arlock = {M_COUNT{axi_lock_reg}}; +assign m_axi_arcache = {M_COUNT{axi_cache_reg}}; +assign m_axi_arprot = {M_COUNT{axi_prot_reg}}; +assign m_axi_arqos = {M_COUNT{axi_qos_reg}}; +assign m_axi_arregion = {M_COUNT{axi_region_reg}}; +assign m_axi_aruser = {M_COUNT{ARUSER_ENABLE ? axi_auser_reg[ARUSER_WIDTH-1:0] : {ARUSER_WIDTH{1'b0}}}}; +assign m_axi_arvalid = m_axi_arvalid_reg; +assign m_axi_rready = m_axi_rready_reg; + +// slave side mux +wire [(CL_S_COUNT > 0 ? CL_S_COUNT-1 : 0):0] s_select; + +wire [ID_WIDTH-1:0] current_s_axi_awid = s_axi_awid[s_select*ID_WIDTH +: ID_WIDTH]; +wire [ADDR_WIDTH-1:0] current_s_axi_awaddr = s_axi_awaddr[s_select*ADDR_WIDTH +: ADDR_WIDTH]; +wire [7:0] current_s_axi_awlen = s_axi_awlen[s_select*8 +: 8]; +wire [2:0] current_s_axi_awsize = s_axi_awsize[s_select*3 +: 3]; +wire [1:0] current_s_axi_awburst = s_axi_awburst[s_select*2 +: 2]; +wire current_s_axi_awlock = s_axi_awlock[s_select]; +wire [3:0] current_s_axi_awcache = s_axi_awcache[s_select*4 +: 4]; +wire [2:0] current_s_axi_awprot = s_axi_awprot[s_select*3 +: 3]; +wire [3:0] current_s_axi_awqos = s_axi_awqos[s_select*4 +: 4]; +wire [AWUSER_WIDTH-1:0] current_s_axi_awuser = s_axi_awuser[s_select*AWUSER_WIDTH +: AWUSER_WIDTH]; +wire current_s_axi_awvalid = s_axi_awvalid[s_select]; +wire current_s_axi_awready = s_axi_awready[s_select]; +wire [DATA_WIDTH-1:0] current_s_axi_wdata = s_axi_wdata[s_select*DATA_WIDTH +: DATA_WIDTH]; +wire [STRB_WIDTH-1:0] current_s_axi_wstrb = s_axi_wstrb[s_select*STRB_WIDTH +: STRB_WIDTH]; +wire current_s_axi_wlast = s_axi_wlast[s_select]; +wire [WUSER_WIDTH-1:0] current_s_axi_wuser = s_axi_wuser[s_select*WUSER_WIDTH +: WUSER_WIDTH]; +wire current_s_axi_wvalid = s_axi_wvalid[s_select]; +wire current_s_axi_wready = s_axi_wready[s_select]; +wire [ID_WIDTH-1:0] current_s_axi_bid = s_axi_bid[s_select*ID_WIDTH +: ID_WIDTH]; +wire [1:0] current_s_axi_bresp = s_axi_bresp[s_select*2 +: 2]; +wire [BUSER_WIDTH-1:0] current_s_axi_buser = s_axi_buser[s_select*BUSER_WIDTH +: BUSER_WIDTH]; +wire current_s_axi_bvalid = s_axi_bvalid[s_select]; +wire current_s_axi_bready = s_axi_bready[s_select]; +wire [ID_WIDTH-1:0] current_s_axi_arid = s_axi_arid[s_select*ID_WIDTH +: ID_WIDTH]; +wire [ADDR_WIDTH-1:0] current_s_axi_araddr = s_axi_araddr[s_select*ADDR_WIDTH +: ADDR_WIDTH]; +wire [7:0] current_s_axi_arlen = s_axi_arlen[s_select*8 +: 8]; +wire [2:0] current_s_axi_arsize = s_axi_arsize[s_select*3 +: 3]; +wire [1:0] current_s_axi_arburst = s_axi_arburst[s_select*2 +: 2]; +wire current_s_axi_arlock = s_axi_arlock[s_select]; +wire [3:0] current_s_axi_arcache = s_axi_arcache[s_select*4 +: 4]; +wire [2:0] current_s_axi_arprot = s_axi_arprot[s_select*3 +: 3]; +wire [3:0] current_s_axi_arqos = s_axi_arqos[s_select*4 +: 4]; +wire [ARUSER_WIDTH-1:0] current_s_axi_aruser = s_axi_aruser[s_select*ARUSER_WIDTH +: ARUSER_WIDTH]; +wire current_s_axi_arvalid = s_axi_arvalid[s_select]; +wire current_s_axi_arready = s_axi_arready[s_select]; +wire [ID_WIDTH-1:0] current_s_axi_rid = s_axi_rid[s_select*ID_WIDTH +: ID_WIDTH]; +wire [DATA_WIDTH-1:0] current_s_axi_rdata = s_axi_rdata[s_select*DATA_WIDTH +: DATA_WIDTH]; +wire [1:0] current_s_axi_rresp = s_axi_rresp[s_select*2 +: 2]; +wire current_s_axi_rlast = s_axi_rlast[s_select]; +wire [RUSER_WIDTH-1:0] current_s_axi_ruser = s_axi_ruser[s_select*RUSER_WIDTH +: RUSER_WIDTH]; +wire current_s_axi_rvalid = s_axi_rvalid[s_select]; +wire current_s_axi_rready = s_axi_rready[s_select]; + +// master side mux +wire [ID_WIDTH-1:0] current_m_axi_awid = m_axi_awid[m_select_reg*ID_WIDTH +: ID_WIDTH]; +wire [ADDR_WIDTH-1:0] current_m_axi_awaddr = m_axi_awaddr[m_select_reg*ADDR_WIDTH +: ADDR_WIDTH]; +wire [7:0] current_m_axi_awlen = m_axi_awlen[m_select_reg*8 +: 8]; +wire [2:0] current_m_axi_awsize = m_axi_awsize[m_select_reg*3 +: 3]; +wire [1:0] current_m_axi_awburst = m_axi_awburst[m_select_reg*2 +: 2]; +wire current_m_axi_awlock = m_axi_awlock[m_select_reg]; +wire [3:0] current_m_axi_awcache = m_axi_awcache[m_select_reg*4 +: 4]; +wire [2:0] current_m_axi_awprot = m_axi_awprot[m_select_reg*3 +: 3]; +wire [3:0] current_m_axi_awqos = m_axi_awqos[m_select_reg*4 +: 4]; +wire [3:0] current_m_axi_awregion = m_axi_awregion[m_select_reg*4 +: 4]; +wire [AWUSER_WIDTH-1:0] current_m_axi_awuser = m_axi_awuser[m_select_reg*AWUSER_WIDTH +: AWUSER_WIDTH]; +wire current_m_axi_awvalid = m_axi_awvalid[m_select_reg]; +wire current_m_axi_awready = m_axi_awready[m_select_reg]; +wire [DATA_WIDTH-1:0] current_m_axi_wdata = m_axi_wdata[m_select_reg*DATA_WIDTH +: DATA_WIDTH]; +wire [STRB_WIDTH-1:0] current_m_axi_wstrb = m_axi_wstrb[m_select_reg*STRB_WIDTH +: STRB_WIDTH]; +wire current_m_axi_wlast = m_axi_wlast[m_select_reg]; +wire [WUSER_WIDTH-1:0] current_m_axi_wuser = m_axi_wuser[m_select_reg*WUSER_WIDTH +: WUSER_WIDTH]; +wire current_m_axi_wvalid = m_axi_wvalid[m_select_reg]; +wire current_m_axi_wready = m_axi_wready[m_select_reg]; +wire [ID_WIDTH-1:0] current_m_axi_bid = m_axi_bid[m_select_reg*ID_WIDTH +: ID_WIDTH]; +wire [1:0] current_m_axi_bresp = m_axi_bresp[m_select_reg*2 +: 2]; +wire [BUSER_WIDTH-1:0] current_m_axi_buser = m_axi_buser[m_select_reg*BUSER_WIDTH +: BUSER_WIDTH]; +wire current_m_axi_bvalid = m_axi_bvalid[m_select_reg]; +wire current_m_axi_bready = m_axi_bready[m_select_reg]; +wire [ID_WIDTH-1:0] current_m_axi_arid = m_axi_arid[m_select_reg*ID_WIDTH +: ID_WIDTH]; +wire [ADDR_WIDTH-1:0] current_m_axi_araddr = m_axi_araddr[m_select_reg*ADDR_WIDTH +: ADDR_WIDTH]; +wire [7:0] current_m_axi_arlen = m_axi_arlen[m_select_reg*8 +: 8]; +wire [2:0] current_m_axi_arsize = m_axi_arsize[m_select_reg*3 +: 3]; +wire [1:0] current_m_axi_arburst = m_axi_arburst[m_select_reg*2 +: 2]; +wire current_m_axi_arlock = m_axi_arlock[m_select_reg]; +wire [3:0] current_m_axi_arcache = m_axi_arcache[m_select_reg*4 +: 4]; +wire [2:0] current_m_axi_arprot = m_axi_arprot[m_select_reg*3 +: 3]; +wire [3:0] current_m_axi_arqos = m_axi_arqos[m_select_reg*4 +: 4]; +wire [3:0] current_m_axi_arregion = m_axi_arregion[m_select_reg*4 +: 4]; +wire [ARUSER_WIDTH-1:0] current_m_axi_aruser = m_axi_aruser[m_select_reg*ARUSER_WIDTH +: ARUSER_WIDTH]; +wire current_m_axi_arvalid = m_axi_arvalid[m_select_reg]; +wire current_m_axi_arready = m_axi_arready[m_select_reg]; +wire [ID_WIDTH-1:0] current_m_axi_rid = m_axi_rid[m_select_reg*ID_WIDTH +: ID_WIDTH]; +wire [DATA_WIDTH-1:0] current_m_axi_rdata = m_axi_rdata[m_select_reg*DATA_WIDTH +: DATA_WIDTH]; +wire [1:0] current_m_axi_rresp = m_axi_rresp[m_select_reg*2 +: 2]; +wire current_m_axi_rlast = m_axi_rlast[m_select_reg]; +wire [RUSER_WIDTH-1:0] current_m_axi_ruser = m_axi_ruser[m_select_reg*RUSER_WIDTH +: RUSER_WIDTH]; +wire current_m_axi_rvalid = m_axi_rvalid[m_select_reg]; +wire current_m_axi_rready = m_axi_rready[m_select_reg]; + +// arbiter instance +wire [S_COUNT*2-1:0] request; +wire [S_COUNT*2-1:0] acknowledge; +wire [S_COUNT*2-1:0] grant; +wire grant_valid; +wire [CL_S_COUNT:0] grant_encoded; + +wire read = grant_encoded[0]; +assign s_select = grant_encoded >> 1; + +arbiter #( + .PORTS(S_COUNT*2), + .ARB_TYPE_ROUND_ROBIN(1), + .ARB_BLOCK(1), + .ARB_BLOCK_ACK(1), + .ARB_LSB_HIGH_PRIORITY(1) +) +arb_inst ( + .clk(clk), + .rst(rst), + .request(request), + .acknowledge(acknowledge), + .grant(grant), + .grant_valid(grant_valid), + .grant_encoded(grant_encoded) +); + +genvar n; + +// request generation +generate +for (n = 0; n < S_COUNT; n = n + 1) begin + assign request[2*n] = s_axi_awvalid[n]; + assign request[2*n+1] = s_axi_arvalid[n]; +end +endgenerate + +// acknowledge generation +generate +for (n = 0; n < S_COUNT; n = n + 1) begin + assign acknowledge[2*n] = grant[2*n] && s_axi_bvalid[n] && s_axi_bready[n]; + assign acknowledge[2*n+1] = grant[2*n+1] && s_axi_rvalid[n] && s_axi_rready[n] && s_axi_rlast[n]; +end +endgenerate + +always @* begin + state_next = STATE_IDLE; + + match = 1'b0; + + m_select_next = m_select_reg; + axi_id_next = axi_id_reg; + axi_addr_next = axi_addr_reg; + axi_addr_valid_next = axi_addr_valid_reg; + axi_len_next = axi_len_reg; + axi_size_next = axi_size_reg; + axi_burst_next = axi_burst_reg; + axi_lock_next = axi_lock_reg; + axi_cache_next = axi_cache_reg; + axi_prot_next = axi_prot_reg; + axi_qos_next = axi_qos_reg; + axi_region_next = axi_region_reg; + axi_auser_next = axi_auser_reg; + axi_bresp_next = axi_bresp_reg; + axi_buser_next = axi_buser_reg; + + s_axi_awready_next = 0; + s_axi_wready_next = 0; + s_axi_bvalid_next = s_axi_bvalid_reg & ~s_axi_bready; + s_axi_arready_next = 0; + + m_axi_awvalid_next = m_axi_awvalid_reg & ~m_axi_awready; + m_axi_bready_next = 0; + m_axi_arvalid_next = m_axi_arvalid_reg & ~m_axi_arready; + m_axi_rready_next = 0; + + s_axi_rid_int = axi_id_reg; + s_axi_rdata_int = current_m_axi_rdata; + s_axi_rresp_int = current_m_axi_rresp; + s_axi_rlast_int = current_m_axi_rlast; + s_axi_ruser_int = current_m_axi_ruser; + s_axi_rvalid_int = 1'b0; + + m_axi_wdata_int = current_s_axi_wdata; + m_axi_wstrb_int = current_s_axi_wstrb; + m_axi_wlast_int = current_s_axi_wlast; + m_axi_wuser_int = current_s_axi_wuser; + m_axi_wvalid_int = 1'b0; + + case (state_reg) + STATE_IDLE: begin + // idle state; wait for arbitration + + if (grant_valid) begin + + axi_addr_valid_next = 1'b1; + + if (read) begin + // reading + axi_addr_next = current_s_axi_araddr; + axi_prot_next = current_s_axi_arprot; + axi_id_next = current_s_axi_arid; + axi_addr_next = current_s_axi_araddr; + axi_len_next = current_s_axi_arlen; + axi_size_next = current_s_axi_arsize; + axi_burst_next = current_s_axi_arburst; + axi_lock_next = current_s_axi_arlock; + axi_cache_next = current_s_axi_arcache; + axi_prot_next = current_s_axi_arprot; + axi_qos_next = current_s_axi_arqos; + axi_auser_next = current_s_axi_aruser; + s_axi_arready_next[s_select] = 1'b1; + end else begin + // writing + axi_addr_next = current_s_axi_awaddr; + axi_prot_next = current_s_axi_awprot; + axi_id_next = current_s_axi_awid; + axi_addr_next = current_s_axi_awaddr; + axi_len_next = current_s_axi_awlen; + axi_size_next = current_s_axi_awsize; + axi_burst_next = current_s_axi_awburst; + axi_lock_next = current_s_axi_awlock; + axi_cache_next = current_s_axi_awcache; + axi_prot_next = current_s_axi_awprot; + axi_qos_next = current_s_axi_awqos; + axi_auser_next = current_s_axi_awuser; + s_axi_awready_next[s_select] = 1'b1; + end + + state_next = STATE_DECODE; + end else begin + state_next = STATE_IDLE; + end + end + STATE_DECODE: begin + // decode state; determine master interface + + match = 1'b0; + for (i = 0; i < M_COUNT; i = i + 1) begin + for (j = 0; j < M_REGIONS; j = j + 1) begin + if (M_ADDR_WIDTH[(i*M_REGIONS+j)*32 +: 32] && (!M_SECURE[i] || !axi_prot_reg[1]) && ((read ? M_CONNECT_READ : M_CONNECT_WRITE) & (1 << (s_select+i*S_COUNT))) && (axi_addr_reg >> M_ADDR_WIDTH[(i*M_REGIONS+j)*32 +: 32]) == (M_BASE_ADDR_INT[(i*M_REGIONS+j)*ADDR_WIDTH +: ADDR_WIDTH] >> M_ADDR_WIDTH[(i*M_REGIONS+j)*32 +: 32])) begin + m_select_next = i; + axi_region_next = j; + match = 1'b1; + end + end + end + + if (match) begin + if (read) begin + // reading + m_axi_rready_next[m_select_reg] = s_axi_rready_int_early; + state_next = STATE_READ; + end else begin + // writing + s_axi_wready_next[s_select] = m_axi_wready_int_early; + state_next = STATE_WRITE; + end + end else begin + // no match; return decode error + if (read) begin + // reading + state_next = STATE_READ_DROP; + end else begin + // writing + axi_bresp_next = 2'b11; + s_axi_wready_next[s_select] = 1'b1; + state_next = STATE_WRITE_DROP; + end + end + end + STATE_WRITE: begin + // write state; store and forward write data + s_axi_wready_next[s_select] = m_axi_wready_int_early; + + if (axi_addr_valid_reg) begin + m_axi_awvalid_next[m_select_reg] = 1'b1; + end + axi_addr_valid_next = 1'b0; + + if (current_s_axi_wready && current_s_axi_wvalid) begin + m_axi_wdata_int = current_s_axi_wdata; + m_axi_wstrb_int = current_s_axi_wstrb; + m_axi_wlast_int = current_s_axi_wlast; + m_axi_wuser_int = current_s_axi_wuser; + m_axi_wvalid_int = 1'b1; + + if (current_s_axi_wlast) begin + s_axi_wready_next[s_select] = 1'b0; + m_axi_bready_next[m_select_reg] = 1'b1; + state_next = STATE_WRITE_RESP; + end else begin + state_next = STATE_WRITE; + end + end else begin + state_next = STATE_WRITE; + end + end + STATE_WRITE_RESP: begin + // write response state; store and forward write response + m_axi_bready_next[m_select_reg] = 1'b1; + + if (current_m_axi_bready && current_m_axi_bvalid) begin + m_axi_bready_next[m_select_reg] = 1'b0; + axi_bresp_next = current_m_axi_bresp; + s_axi_bvalid_next[s_select] = 1'b1; + state_next = STATE_WAIT_IDLE; + end else begin + state_next = STATE_WRITE_RESP; + end + end + STATE_WRITE_DROP: begin + // write drop state; drop write data + s_axi_wready_next[s_select] = 1'b1; + + axi_addr_valid_next = 1'b0; + + if (current_s_axi_wready && current_s_axi_wvalid && current_s_axi_wlast) begin + s_axi_wready_next[s_select] = 1'b0; + s_axi_bvalid_next[s_select] = 1'b1; + state_next = STATE_WAIT_IDLE; + end else begin + state_next = STATE_WRITE_DROP; + end + end + STATE_READ: begin + // read state; store and forward read response + m_axi_rready_next[m_select_reg] = s_axi_rready_int_early; + + if (axi_addr_valid_reg) begin + m_axi_arvalid_next[m_select_reg] = 1'b1; + end + axi_addr_valid_next = 1'b0; + + if (current_m_axi_rready && current_m_axi_rvalid) begin + s_axi_rid_int = axi_id_reg; + s_axi_rdata_int = current_m_axi_rdata; + s_axi_rresp_int = current_m_axi_rresp; + s_axi_rlast_int = current_m_axi_rlast; + s_axi_ruser_int = current_m_axi_ruser; + s_axi_rvalid_int = 1'b1; + + if (current_m_axi_rlast) begin + m_axi_rready_next[m_select_reg] = 1'b0; + state_next = STATE_WAIT_IDLE; + end else begin + state_next = STATE_READ; + end + end else begin + state_next = STATE_READ; + end + end + STATE_READ_DROP: begin + // read drop state; generate decode error read response + + s_axi_rid_int = axi_id_reg; + s_axi_rdata_int = {DATA_WIDTH{1'b0}}; + s_axi_rresp_int = 2'b11; + s_axi_rlast_int = axi_len_reg == 0; + s_axi_ruser_int = {RUSER_WIDTH{1'b0}}; + s_axi_rvalid_int = 1'b1; + + if (s_axi_rready_int_reg) begin + axi_len_next = axi_len_reg - 1; + if (axi_len_reg == 0) begin + state_next = STATE_WAIT_IDLE; + end else begin + state_next = STATE_READ_DROP; + end + end else begin + state_next = STATE_READ_DROP; + end + end + STATE_WAIT_IDLE: begin + // wait for idle state; wait untl grant valid is deasserted + + if (!grant_valid || acknowledge) begin + state_next = STATE_IDLE; + end else begin + state_next = STATE_WAIT_IDLE; + end + end + endcase +end + +always @(posedge clk) begin + if (rst) begin + state_reg <= STATE_IDLE; + + s_axi_awready_reg <= 0; + s_axi_wready_reg <= 0; + s_axi_bvalid_reg <= 0; + s_axi_arready_reg <= 0; + + m_axi_awvalid_reg <= 0; + m_axi_bready_reg <= 0; + m_axi_arvalid_reg <= 0; + m_axi_rready_reg <= 0; + end else begin + state_reg <= state_next; + + s_axi_awready_reg <= s_axi_awready_next; + s_axi_wready_reg <= s_axi_wready_next; + s_axi_bvalid_reg <= s_axi_bvalid_next; + s_axi_arready_reg <= s_axi_arready_next; + + m_axi_awvalid_reg <= m_axi_awvalid_next; + m_axi_bready_reg <= m_axi_bready_next; + m_axi_arvalid_reg <= m_axi_arvalid_next; + m_axi_rready_reg <= m_axi_rready_next; + end + + m_select_reg <= m_select_next; + axi_id_reg <= axi_id_next; + axi_addr_reg <= axi_addr_next; + axi_addr_valid_reg <= axi_addr_valid_next; + axi_len_reg <= axi_len_next; + axi_size_reg <= axi_size_next; + axi_burst_reg <= axi_burst_next; + axi_lock_reg <= axi_lock_next; + axi_cache_reg <= axi_cache_next; + axi_prot_reg <= axi_prot_next; + axi_qos_reg <= axi_qos_next; + axi_region_reg <= axi_region_next; + axi_auser_reg <= axi_auser_next; + axi_bresp_reg <= axi_bresp_next; + axi_buser_reg <= axi_buser_next; +end + +// output datapath logic (R channel) +reg [ID_WIDTH-1:0] s_axi_rid_reg = {ID_WIDTH{1'b0}}; +reg [DATA_WIDTH-1:0] s_axi_rdata_reg = {DATA_WIDTH{1'b0}}; +reg [1:0] s_axi_rresp_reg = 2'd0; +reg s_axi_rlast_reg = 1'b0; +reg [RUSER_WIDTH-1:0] s_axi_ruser_reg = 1'b0; +reg [S_COUNT-1:0] s_axi_rvalid_reg = 1'b0, s_axi_rvalid_next; + +reg [ID_WIDTH-1:0] temp_s_axi_rid_reg = {ID_WIDTH{1'b0}}; +reg [DATA_WIDTH-1:0] temp_s_axi_rdata_reg = {DATA_WIDTH{1'b0}}; +reg [1:0] temp_s_axi_rresp_reg = 2'd0; +reg temp_s_axi_rlast_reg = 1'b0; +reg [RUSER_WIDTH-1:0] temp_s_axi_ruser_reg = 1'b0; +reg temp_s_axi_rvalid_reg = 1'b0, temp_s_axi_rvalid_next; + +// datapath control +reg store_axi_r_int_to_output; +reg store_axi_r_int_to_temp; +reg store_axi_r_temp_to_output; + +assign s_axi_rid = {S_COUNT{s_axi_rid_reg}}; +assign s_axi_rdata = {S_COUNT{s_axi_rdata_reg}}; +assign s_axi_rresp = {S_COUNT{s_axi_rresp_reg}}; +assign s_axi_rlast = {S_COUNT{s_axi_rlast_reg}}; +assign s_axi_ruser = {S_COUNT{RUSER_ENABLE ? s_axi_ruser_reg : {RUSER_WIDTH{1'b0}}}}; +assign s_axi_rvalid = s_axi_rvalid_reg; + +// enable ready input next cycle if output is ready or the temp reg will not be filled on the next cycle (output reg empty or no input) +assign s_axi_rready_int_early = current_s_axi_rready | (~temp_s_axi_rvalid_reg & (~current_s_axi_rvalid | ~s_axi_rvalid_int)); + +always @* begin + // transfer sink ready state to source + s_axi_rvalid_next = s_axi_rvalid_reg; + temp_s_axi_rvalid_next = temp_s_axi_rvalid_reg; + + store_axi_r_int_to_output = 1'b0; + store_axi_r_int_to_temp = 1'b0; + store_axi_r_temp_to_output = 1'b0; + + if (s_axi_rready_int_reg) begin + // input is ready + if (current_s_axi_rready | ~current_s_axi_rvalid) begin + // output is ready or currently not valid, transfer data to output + s_axi_rvalid_next[s_select] = s_axi_rvalid_int; + store_axi_r_int_to_output = 1'b1; + end else begin + // output is not ready, store input in temp + temp_s_axi_rvalid_next = s_axi_rvalid_int; + store_axi_r_int_to_temp = 1'b1; + end + end else if (current_s_axi_rready) begin + // input is not ready, but output is ready + s_axi_rvalid_next[s_select] = temp_s_axi_rvalid_reg; + temp_s_axi_rvalid_next = 1'b0; + store_axi_r_temp_to_output = 1'b1; + end +end + +always @(posedge clk) begin + if (rst) begin + s_axi_rvalid_reg <= 1'b0; + s_axi_rready_int_reg <= 1'b0; + temp_s_axi_rvalid_reg <= 1'b0; + end else begin + s_axi_rvalid_reg <= s_axi_rvalid_next; + s_axi_rready_int_reg <= s_axi_rready_int_early; + temp_s_axi_rvalid_reg <= temp_s_axi_rvalid_next; + end + + // datapath + if (store_axi_r_int_to_output) begin + s_axi_rid_reg <= s_axi_rid_int; + s_axi_rdata_reg <= s_axi_rdata_int; + s_axi_rresp_reg <= s_axi_rresp_int; + s_axi_rlast_reg <= s_axi_rlast_int; + s_axi_ruser_reg <= s_axi_ruser_int; + end else if (store_axi_r_temp_to_output) begin + s_axi_rid_reg <= temp_s_axi_rid_reg; + s_axi_rdata_reg <= temp_s_axi_rdata_reg; + s_axi_rresp_reg <= temp_s_axi_rresp_reg; + s_axi_rlast_reg <= temp_s_axi_rlast_reg; + s_axi_ruser_reg <= temp_s_axi_ruser_reg; + end + + if (store_axi_r_int_to_temp) begin + temp_s_axi_rid_reg <= s_axi_rid_int; + temp_s_axi_rdata_reg <= s_axi_rdata_int; + temp_s_axi_rresp_reg <= s_axi_rresp_int; + temp_s_axi_rlast_reg <= s_axi_rlast_int; + temp_s_axi_ruser_reg <= s_axi_ruser_int; + end +end + +// output datapath logic (W channel) +reg [DATA_WIDTH-1:0] m_axi_wdata_reg = {DATA_WIDTH{1'b0}}; +reg [STRB_WIDTH-1:0] m_axi_wstrb_reg = {STRB_WIDTH{1'b0}}; +reg m_axi_wlast_reg = 1'b0; +reg [WUSER_WIDTH-1:0] m_axi_wuser_reg = 1'b0; +reg [M_COUNT-1:0] m_axi_wvalid_reg = 1'b0, m_axi_wvalid_next; + +reg [DATA_WIDTH-1:0] temp_m_axi_wdata_reg = {DATA_WIDTH{1'b0}}; +reg [STRB_WIDTH-1:0] temp_m_axi_wstrb_reg = {STRB_WIDTH{1'b0}}; +reg temp_m_axi_wlast_reg = 1'b0; +reg [WUSER_WIDTH-1:0] temp_m_axi_wuser_reg = 1'b0; +reg temp_m_axi_wvalid_reg = 1'b0, temp_m_axi_wvalid_next; + +// datapath control +reg store_axi_w_int_to_output; +reg store_axi_w_int_to_temp; +reg store_axi_w_temp_to_output; + +assign m_axi_wdata = {M_COUNT{m_axi_wdata_reg}}; +assign m_axi_wstrb = {M_COUNT{m_axi_wstrb_reg}}; +assign m_axi_wlast = {M_COUNT{m_axi_wlast_reg}}; +assign m_axi_wuser = {M_COUNT{WUSER_ENABLE ? m_axi_wuser_reg : {WUSER_WIDTH{1'b0}}}}; +assign m_axi_wvalid = m_axi_wvalid_reg; + +// enable ready input next cycle if output is ready or the temp reg will not be filled on the next cycle (output reg empty or no input) +assign m_axi_wready_int_early = current_m_axi_wready | (~temp_m_axi_wvalid_reg & (~current_m_axi_wvalid | ~m_axi_wvalid_int)); + +always @* begin + // transfer sink ready state to source + m_axi_wvalid_next = m_axi_wvalid_reg; + temp_m_axi_wvalid_next = temp_m_axi_wvalid_reg; + + store_axi_w_int_to_output = 1'b0; + store_axi_w_int_to_temp = 1'b0; + store_axi_w_temp_to_output = 1'b0; + + if (m_axi_wready_int_reg) begin + // input is ready + if (current_m_axi_wready | ~current_m_axi_wvalid) begin + // output is ready or currently not valid, transfer data to output + m_axi_wvalid_next[m_select_reg] = m_axi_wvalid_int; + store_axi_w_int_to_output = 1'b1; + end else begin + // output is not ready, store input in temp + temp_m_axi_wvalid_next = m_axi_wvalid_int; + store_axi_w_int_to_temp = 1'b1; + end + end else if (current_m_axi_wready) begin + // input is not ready, but output is ready + m_axi_wvalid_next[m_select_reg] = temp_m_axi_wvalid_reg; + temp_m_axi_wvalid_next = 1'b0; + store_axi_w_temp_to_output = 1'b1; + end +end + +always @(posedge clk) begin + if (rst) begin + m_axi_wvalid_reg <= 1'b0; + m_axi_wready_int_reg <= 1'b0; + temp_m_axi_wvalid_reg <= 1'b0; + end else begin + m_axi_wvalid_reg <= m_axi_wvalid_next; + m_axi_wready_int_reg <= m_axi_wready_int_early; + temp_m_axi_wvalid_reg <= temp_m_axi_wvalid_next; + end + + // datapath + if (store_axi_w_int_to_output) begin + m_axi_wdata_reg <= m_axi_wdata_int; + m_axi_wstrb_reg <= m_axi_wstrb_int; + m_axi_wlast_reg <= m_axi_wlast_int; + m_axi_wuser_reg <= m_axi_wuser_int; + end else if (store_axi_w_temp_to_output) begin + m_axi_wdata_reg <= temp_m_axi_wdata_reg; + m_axi_wstrb_reg <= temp_m_axi_wstrb_reg; + m_axi_wlast_reg <= temp_m_axi_wlast_reg; + m_axi_wuser_reg <= temp_m_axi_wuser_reg; + end + + if (store_axi_w_int_to_temp) begin + temp_m_axi_wdata_reg <= m_axi_wdata_int; + temp_m_axi_wstrb_reg <= m_axi_wstrb_int; + temp_m_axi_wlast_reg <= m_axi_wlast_int; + temp_m_axi_wuser_reg <= m_axi_wuser_int; + end +end + +endmodule + +`resetall diff --git a/xls/modules/zstd/axi_interconnect_wrapper.v b/xls/modules/zstd/axi_interconnect_wrapper.v new file mode 100644 index 0000000000..50017314f9 --- /dev/null +++ b/xls/modules/zstd/axi_interconnect_wrapper.v @@ -0,0 +1,424 @@ +/* + +Copyright (c) 2020 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`resetall +`timescale 1ns / 1ps +`default_nettype none + +/* + * AXI4 4x1 interconnect (wrapper) + */ +module axi_interconnect_wrapper # +( + parameter DATA_WIDTH = 32, + parameter ADDR_WIDTH = 32, + parameter STRB_WIDTH = (DATA_WIDTH/8), + parameter ID_WIDTH = 8, + parameter AWUSER_ENABLE = 0, + parameter AWUSER_WIDTH = 1, + parameter WUSER_ENABLE = 0, + parameter WUSER_WIDTH = 1, + parameter BUSER_ENABLE = 0, + parameter BUSER_WIDTH = 1, + parameter ARUSER_ENABLE = 0, + parameter ARUSER_WIDTH = 1, + parameter RUSER_ENABLE = 0, + parameter RUSER_WIDTH = 1, + parameter FORWARD_ID = 0, + parameter M_REGIONS = 1, + parameter M00_BASE_ADDR = 0, + parameter M00_ADDR_WIDTH = {M_REGIONS{32'd24}}, + parameter M00_CONNECT_READ = 4'b1111, + parameter M00_CONNECT_WRITE = 4'b1111, + parameter M00_SECURE = 1'b0 +) +( + input wire clk, + input wire rst, + + /* + * AXI slave interface + */ + input wire [ID_WIDTH-1:0] s00_axi_awid, + input wire [ADDR_WIDTH-1:0] s00_axi_awaddr, + input wire [7:0] s00_axi_awlen, + input wire [2:0] s00_axi_awsize, + input wire [1:0] s00_axi_awburst, + input wire s00_axi_awlock, + input wire [3:0] s00_axi_awcache, + input wire [2:0] s00_axi_awprot, + input wire [3:0] s00_axi_awqos, + input wire [AWUSER_WIDTH-1:0] s00_axi_awuser, + input wire s00_axi_awvalid, + output wire s00_axi_awready, + input wire [DATA_WIDTH-1:0] s00_axi_wdata, + input wire [STRB_WIDTH-1:0] s00_axi_wstrb, + input wire s00_axi_wlast, + input wire [WUSER_WIDTH-1:0] s00_axi_wuser, + input wire s00_axi_wvalid, + output wire s00_axi_wready, + output wire [ID_WIDTH-1:0] s00_axi_bid, + output wire [1:0] s00_axi_bresp, + output wire [BUSER_WIDTH-1:0] s00_axi_buser, + output wire s00_axi_bvalid, + input wire s00_axi_bready, + input wire [ID_WIDTH-1:0] s00_axi_arid, + input wire [ADDR_WIDTH-1:0] s00_axi_araddr, + input wire [7:0] s00_axi_arlen, + input wire [2:0] s00_axi_arsize, + input wire [1:0] s00_axi_arburst, + input wire s00_axi_arlock, + input wire [3:0] s00_axi_arcache, + input wire [2:0] s00_axi_arprot, + input wire [3:0] s00_axi_arqos, + input wire [ARUSER_WIDTH-1:0] s00_axi_aruser, + input wire s00_axi_arvalid, + output wire s00_axi_arready, + output wire [ID_WIDTH-1:0] s00_axi_rid, + output wire [DATA_WIDTH-1:0] s00_axi_rdata, + output wire [1:0] s00_axi_rresp, + output wire s00_axi_rlast, + output wire [RUSER_WIDTH-1:0] s00_axi_ruser, + output wire s00_axi_rvalid, + input wire s00_axi_rready, + + input wire [ID_WIDTH-1:0] s01_axi_awid, + input wire [ADDR_WIDTH-1:0] s01_axi_awaddr, + input wire [7:0] s01_axi_awlen, + input wire [2:0] s01_axi_awsize, + input wire [1:0] s01_axi_awburst, + input wire s01_axi_awlock, + input wire [3:0] s01_axi_awcache, + input wire [2:0] s01_axi_awprot, + input wire [3:0] s01_axi_awqos, + input wire [AWUSER_WIDTH-1:0] s01_axi_awuser, + input wire s01_axi_awvalid, + output wire s01_axi_awready, + input wire [DATA_WIDTH-1:0] s01_axi_wdata, + input wire [STRB_WIDTH-1:0] s01_axi_wstrb, + input wire s01_axi_wlast, + input wire [WUSER_WIDTH-1:0] s01_axi_wuser, + input wire s01_axi_wvalid, + output wire s01_axi_wready, + output wire [ID_WIDTH-1:0] s01_axi_bid, + output wire [1:0] s01_axi_bresp, + output wire [BUSER_WIDTH-1:0] s01_axi_buser, + output wire s01_axi_bvalid, + input wire s01_axi_bready, + input wire [ID_WIDTH-1:0] s01_axi_arid, + input wire [ADDR_WIDTH-1:0] s01_axi_araddr, + input wire [7:0] s01_axi_arlen, + input wire [2:0] s01_axi_arsize, + input wire [1:0] s01_axi_arburst, + input wire s01_axi_arlock, + input wire [3:0] s01_axi_arcache, + input wire [2:0] s01_axi_arprot, + input wire [3:0] s01_axi_arqos, + input wire [ARUSER_WIDTH-1:0] s01_axi_aruser, + input wire s01_axi_arvalid, + output wire s01_axi_arready, + output wire [ID_WIDTH-1:0] s01_axi_rid, + output wire [DATA_WIDTH-1:0] s01_axi_rdata, + output wire [1:0] s01_axi_rresp, + output wire s01_axi_rlast, + output wire [RUSER_WIDTH-1:0] s01_axi_ruser, + output wire s01_axi_rvalid, + input wire s01_axi_rready, + + input wire [ID_WIDTH-1:0] s02_axi_awid, + input wire [ADDR_WIDTH-1:0] s02_axi_awaddr, + input wire [7:0] s02_axi_awlen, + input wire [2:0] s02_axi_awsize, + input wire [1:0] s02_axi_awburst, + input wire s02_axi_awlock, + input wire [3:0] s02_axi_awcache, + input wire [2:0] s02_axi_awprot, + input wire [3:0] s02_axi_awqos, + input wire [AWUSER_WIDTH-1:0] s02_axi_awuser, + input wire s02_axi_awvalid, + output wire s02_axi_awready, + input wire [DATA_WIDTH-1:0] s02_axi_wdata, + input wire [STRB_WIDTH-1:0] s02_axi_wstrb, + input wire s02_axi_wlast, + input wire [WUSER_WIDTH-1:0] s02_axi_wuser, + input wire s02_axi_wvalid, + output wire s02_axi_wready, + output wire [ID_WIDTH-1:0] s02_axi_bid, + output wire [1:0] s02_axi_bresp, + output wire [BUSER_WIDTH-1:0] s02_axi_buser, + output wire s02_axi_bvalid, + input wire s02_axi_bready, + input wire [ID_WIDTH-1:0] s02_axi_arid, + input wire [ADDR_WIDTH-1:0] s02_axi_araddr, + input wire [7:0] s02_axi_arlen, + input wire [2:0] s02_axi_arsize, + input wire [1:0] s02_axi_arburst, + input wire s02_axi_arlock, + input wire [3:0] s02_axi_arcache, + input wire [2:0] s02_axi_arprot, + input wire [3:0] s02_axi_arqos, + input wire [ARUSER_WIDTH-1:0] s02_axi_aruser, + input wire s02_axi_arvalid, + output wire s02_axi_arready, + output wire [ID_WIDTH-1:0] s02_axi_rid, + output wire [DATA_WIDTH-1:0] s02_axi_rdata, + output wire [1:0] s02_axi_rresp, + output wire s02_axi_rlast, + output wire [RUSER_WIDTH-1:0] s02_axi_ruser, + output wire s02_axi_rvalid, + input wire s02_axi_rready, + + input wire [ID_WIDTH-1:0] s03_axi_awid, + input wire [ADDR_WIDTH-1:0] s03_axi_awaddr, + input wire [7:0] s03_axi_awlen, + input wire [2:0] s03_axi_awsize, + input wire [1:0] s03_axi_awburst, + input wire s03_axi_awlock, + input wire [3:0] s03_axi_awcache, + input wire [2:0] s03_axi_awprot, + input wire [3:0] s03_axi_awqos, + input wire [AWUSER_WIDTH-1:0] s03_axi_awuser, + input wire s03_axi_awvalid, + output wire s03_axi_awready, + input wire [DATA_WIDTH-1:0] s03_axi_wdata, + input wire [STRB_WIDTH-1:0] s03_axi_wstrb, + input wire s03_axi_wlast, + input wire [WUSER_WIDTH-1:0] s03_axi_wuser, + input wire s03_axi_wvalid, + output wire s03_axi_wready, + output wire [ID_WIDTH-1:0] s03_axi_bid, + output wire [1:0] s03_axi_bresp, + output wire [BUSER_WIDTH-1:0] s03_axi_buser, + output wire s03_axi_bvalid, + input wire s03_axi_bready, + input wire [ID_WIDTH-1:0] s03_axi_arid, + input wire [ADDR_WIDTH-1:0] s03_axi_araddr, + input wire [7:0] s03_axi_arlen, + input wire [2:0] s03_axi_arsize, + input wire [1:0] s03_axi_arburst, + input wire s03_axi_arlock, + input wire [3:0] s03_axi_arcache, + input wire [2:0] s03_axi_arprot, + input wire [3:0] s03_axi_arqos, + input wire [ARUSER_WIDTH-1:0] s03_axi_aruser, + input wire s03_axi_arvalid, + output wire s03_axi_arready, + output wire [ID_WIDTH-1:0] s03_axi_rid, + output wire [DATA_WIDTH-1:0] s03_axi_rdata, + output wire [1:0] s03_axi_rresp, + output wire s03_axi_rlast, + output wire [RUSER_WIDTH-1:0] s03_axi_ruser, + output wire s03_axi_rvalid, + input wire s03_axi_rready, + + /* + * AXI master interface + */ + output wire [ID_WIDTH-1:0] m00_axi_awid, + output wire [ADDR_WIDTH-1:0] m00_axi_awaddr, + output wire [7:0] m00_axi_awlen, + output wire [2:0] m00_axi_awsize, + output wire [1:0] m00_axi_awburst, + output wire m00_axi_awlock, + output wire [3:0] m00_axi_awcache, + output wire [2:0] m00_axi_awprot, + output wire [3:0] m00_axi_awqos, + output wire [3:0] m00_axi_awregion, + output wire [AWUSER_WIDTH-1:0] m00_axi_awuser, + output wire m00_axi_awvalid, + input wire m00_axi_awready, + output wire [DATA_WIDTH-1:0] m00_axi_wdata, + output wire [STRB_WIDTH-1:0] m00_axi_wstrb, + output wire m00_axi_wlast, + output wire [WUSER_WIDTH-1:0] m00_axi_wuser, + output wire m00_axi_wvalid, + input wire m00_axi_wready, + input wire [ID_WIDTH-1:0] m00_axi_bid, + input wire [1:0] m00_axi_bresp, + input wire [BUSER_WIDTH-1:0] m00_axi_buser, + input wire m00_axi_bvalid, + output wire m00_axi_bready, + output wire [ID_WIDTH-1:0] m00_axi_arid, + output wire [ADDR_WIDTH-1:0] m00_axi_araddr, + output wire [7:0] m00_axi_arlen, + output wire [2:0] m00_axi_arsize, + output wire [1:0] m00_axi_arburst, + output wire m00_axi_arlock, + output wire [3:0] m00_axi_arcache, + output wire [2:0] m00_axi_arprot, + output wire [3:0] m00_axi_arqos, + output wire [3:0] m00_axi_arregion, + output wire [ARUSER_WIDTH-1:0] m00_axi_aruser, + output wire m00_axi_arvalid, + input wire m00_axi_arready, + input wire [ID_WIDTH-1:0] m00_axi_rid, + input wire [DATA_WIDTH-1:0] m00_axi_rdata, + input wire [1:0] m00_axi_rresp, + input wire m00_axi_rlast, + input wire [RUSER_WIDTH-1:0] m00_axi_ruser, + input wire m00_axi_rvalid, + output wire m00_axi_rready +); + +localparam S_COUNT = 4; +localparam M_COUNT = 1; + +// parameter sizing helpers +function [ADDR_WIDTH*M_REGIONS-1:0] w_a_r(input [ADDR_WIDTH*M_REGIONS-1:0] val); + w_a_r = val; +endfunction + +function [32*M_REGIONS-1:0] w_32_r(input [32*M_REGIONS-1:0] val); + w_32_r = val; +endfunction + +function [S_COUNT-1:0] w_s(input [S_COUNT-1:0] val); + w_s = val; +endfunction + +function w_1(input val); + w_1 = val; +endfunction + +axi_interconnect #( + .S_COUNT(S_COUNT), + .M_COUNT(M_COUNT), + .DATA_WIDTH(DATA_WIDTH), + .ADDR_WIDTH(ADDR_WIDTH), + .STRB_WIDTH(STRB_WIDTH), + .ID_WIDTH(ID_WIDTH), + .AWUSER_ENABLE(AWUSER_ENABLE), + .AWUSER_WIDTH(AWUSER_WIDTH), + .WUSER_ENABLE(WUSER_ENABLE), + .WUSER_WIDTH(WUSER_WIDTH), + .BUSER_ENABLE(BUSER_ENABLE), + .BUSER_WIDTH(BUSER_WIDTH), + .ARUSER_ENABLE(ARUSER_ENABLE), + .ARUSER_WIDTH(ARUSER_WIDTH), + .RUSER_ENABLE(RUSER_ENABLE), + .RUSER_WIDTH(RUSER_WIDTH), + .FORWARD_ID(FORWARD_ID), + .M_REGIONS(M_REGIONS), + .M_BASE_ADDR({ w_a_r(M00_BASE_ADDR) }), + .M_ADDR_WIDTH({ w_32_r(M00_ADDR_WIDTH) }), + .M_CONNECT_READ({ w_s(M00_CONNECT_READ) }), + .M_CONNECT_WRITE({ w_s(M00_CONNECT_WRITE) }), + .M_SECURE({ w_1(M00_SECURE) }) +) +axi_interconnect_inst ( + .clk(clk), + .rst(rst), + .s_axi_awid({ s03_axi_awid, s02_axi_awid, s01_axi_awid, s00_axi_awid }), + .s_axi_awaddr({ s03_axi_awaddr, s02_axi_awaddr, s01_axi_awaddr, s00_axi_awaddr }), + .s_axi_awlen({ s03_axi_awlen, s02_axi_awlen, s01_axi_awlen, s00_axi_awlen }), + .s_axi_awsize({ s03_axi_awsize, s02_axi_awsize, s01_axi_awsize, s00_axi_awsize }), + .s_axi_awburst({ s03_axi_awburst, s02_axi_awburst, s01_axi_awburst, s00_axi_awburst }), + .s_axi_awlock({ s03_axi_awlock, s02_axi_awlock, s01_axi_awlock, s00_axi_awlock }), + .s_axi_awcache({ s03_axi_awcache, s02_axi_awcache, s01_axi_awcache, s00_axi_awcache }), + .s_axi_awprot({ s03_axi_awprot, s02_axi_awprot, s01_axi_awprot, s00_axi_awprot }), + .s_axi_awqos({ s03_axi_awqos, s02_axi_awqos, s01_axi_awqos, s00_axi_awqos }), + .s_axi_awuser({ s03_axi_awuser, s02_axi_awuser, s01_axi_awuser, s00_axi_awuser }), + .s_axi_awvalid({ s03_axi_awvalid, s02_axi_awvalid, s01_axi_awvalid, s00_axi_awvalid }), + .s_axi_awready({ s03_axi_awready, s02_axi_awready, s01_axi_awready, s00_axi_awready }), + .s_axi_wdata({ s03_axi_wdata, s02_axi_wdata, s01_axi_wdata, s00_axi_wdata }), + .s_axi_wstrb({ s03_axi_wstrb, s02_axi_wstrb, s01_axi_wstrb, s00_axi_wstrb }), + .s_axi_wlast({ s03_axi_wlast, s02_axi_wlast, s01_axi_wlast, s00_axi_wlast }), + .s_axi_wuser({ s03_axi_wuser, s02_axi_wuser, s01_axi_wuser, s00_axi_wuser }), + .s_axi_wvalid({ s03_axi_wvalid, s02_axi_wvalid, s01_axi_wvalid, s00_axi_wvalid }), + .s_axi_wready({ s03_axi_wready, s02_axi_wready, s01_axi_wready, s00_axi_wready }), + .s_axi_bid({ s03_axi_bid, s02_axi_bid, s01_axi_bid, s00_axi_bid }), + .s_axi_bresp({ s03_axi_bresp, s02_axi_bresp, s01_axi_bresp, s00_axi_bresp }), + .s_axi_buser({ s03_axi_buser, s02_axi_buser, s01_axi_buser, s00_axi_buser }), + .s_axi_bvalid({ s03_axi_bvalid, s02_axi_bvalid, s01_axi_bvalid, s00_axi_bvalid }), + .s_axi_bready({ s03_axi_bready, s02_axi_bready, s01_axi_bready, s00_axi_bready }), + .s_axi_arid({ s03_axi_arid, s02_axi_arid, s01_axi_arid, s00_axi_arid }), + .s_axi_araddr({ s03_axi_araddr, s02_axi_araddr, s01_axi_araddr, s00_axi_araddr }), + .s_axi_arlen({ s03_axi_arlen, s02_axi_arlen, s01_axi_arlen, s00_axi_arlen }), + .s_axi_arsize({ s03_axi_arsize, s02_axi_arsize, s01_axi_arsize, s00_axi_arsize }), + .s_axi_arburst({ s03_axi_arburst, s02_axi_arburst, s01_axi_arburst, s00_axi_arburst }), + .s_axi_arlock({ s03_axi_arlock, s02_axi_arlock, s01_axi_arlock, s00_axi_arlock }), + .s_axi_arcache({ s03_axi_arcache, s02_axi_arcache, s01_axi_arcache, s00_axi_arcache }), + .s_axi_arprot({ s03_axi_arprot, s02_axi_arprot, s01_axi_arprot, s00_axi_arprot }), + .s_axi_arqos({ s03_axi_arqos, s02_axi_arqos, s01_axi_arqos, s00_axi_arqos }), + .s_axi_aruser({ s03_axi_aruser, s02_axi_aruser, s01_axi_aruser, s00_axi_aruser }), + .s_axi_arvalid({ s03_axi_arvalid, s02_axi_arvalid, s01_axi_arvalid, s00_axi_arvalid }), + .s_axi_arready({ s03_axi_arready, s02_axi_arready, s01_axi_arready, s00_axi_arready }), + .s_axi_rid({ s03_axi_rid, s02_axi_rid, s01_axi_rid, s00_axi_rid }), + .s_axi_rdata({ s03_axi_rdata, s02_axi_rdata, s01_axi_rdata, s00_axi_rdata }), + .s_axi_rresp({ s03_axi_rresp, s02_axi_rresp, s01_axi_rresp, s00_axi_rresp }), + .s_axi_rlast({ s03_axi_rlast, s02_axi_rlast, s01_axi_rlast, s00_axi_rlast }), + .s_axi_ruser({ s03_axi_ruser, s02_axi_ruser, s01_axi_ruser, s00_axi_ruser }), + .s_axi_rvalid({ s03_axi_rvalid, s02_axi_rvalid, s01_axi_rvalid, s00_axi_rvalid }), + .s_axi_rready({ s03_axi_rready, s02_axi_rready, s01_axi_rready, s00_axi_rready }), + .m_axi_awid({ m00_axi_awid }), + .m_axi_awaddr({ m00_axi_awaddr }), + .m_axi_awlen({ m00_axi_awlen }), + .m_axi_awsize({ m00_axi_awsize }), + .m_axi_awburst({ m00_axi_awburst }), + .m_axi_awlock({ m00_axi_awlock }), + .m_axi_awcache({ m00_axi_awcache }), + .m_axi_awprot({ m00_axi_awprot }), + .m_axi_awqos({ m00_axi_awqos }), + .m_axi_awregion({ m00_axi_awregion }), + .m_axi_awuser({ m00_axi_awuser }), + .m_axi_awvalid({ m00_axi_awvalid }), + .m_axi_awready({ m00_axi_awready }), + .m_axi_wdata({ m00_axi_wdata }), + .m_axi_wstrb({ m00_axi_wstrb }), + .m_axi_wlast({ m00_axi_wlast }), + .m_axi_wuser({ m00_axi_wuser }), + .m_axi_wvalid({ m00_axi_wvalid }), + .m_axi_wready({ m00_axi_wready }), + .m_axi_bid({ m00_axi_bid }), + .m_axi_bresp({ m00_axi_bresp }), + .m_axi_buser({ m00_axi_buser }), + .m_axi_bvalid({ m00_axi_bvalid }), + .m_axi_bready({ m00_axi_bready }), + .m_axi_arid({ m00_axi_arid }), + .m_axi_araddr({ m00_axi_araddr }), + .m_axi_arlen({ m00_axi_arlen }), + .m_axi_arsize({ m00_axi_arsize }), + .m_axi_arburst({ m00_axi_arburst }), + .m_axi_arlock({ m00_axi_arlock }), + .m_axi_arcache({ m00_axi_arcache }), + .m_axi_arprot({ m00_axi_arprot }), + .m_axi_arqos({ m00_axi_arqos }), + .m_axi_arregion({ m00_axi_arregion }), + .m_axi_aruser({ m00_axi_aruser }), + .m_axi_arvalid({ m00_axi_arvalid }), + .m_axi_arready({ m00_axi_arready }), + .m_axi_rid({ m00_axi_rid }), + .m_axi_rdata({ m00_axi_rdata }), + .m_axi_rresp({ m00_axi_rresp }), + .m_axi_rlast({ m00_axi_rlast }), + .m_axi_ruser({ m00_axi_ruser }), + .m_axi_rvalid({ m00_axi_rvalid }), + .m_axi_rready({ m00_axi_rready }) +); + +endmodule + +`resetall diff --git a/xls/modules/zstd/block_header.x b/xls/modules/zstd/block_header.x index 455b3295e1..a0b52491b8 100644 --- a/xls/modules/zstd/block_header.x +++ b/xls/modules/zstd/block_header.x @@ -23,6 +23,7 @@ import xls.modules.zstd.common as common; type Buffer = buff::Buffer; type BufferStatus = buff::BufferStatus; type BlockType = common::BlockType; +type BlockSize = common::BlockSize; // Status values reported by the block header parsing function pub enum BlockHeaderStatus: u2 { @@ -35,7 +36,7 @@ pub enum BlockHeaderStatus: u2 { pub struct BlockHeader { last: bool, btype: BlockType, - size: u21, + size: BlockSize, } // Structure for returning results of block header parsing @@ -86,7 +87,7 @@ fn test_parse_block_header() { let result = parse_block_header(buffer); assert_eq(result, BlockHeaderResult { status: BlockHeaderStatus::OK, - header: BlockHeader { last: u1:1, btype: BlockType::RAW, size: u21:0x1000 }, + header: BlockHeader { last: u1:1, btype: BlockType::RAW, size: BlockSize:0x1000 }, buffer: Buffer { content: u32:0, length: u32:0 } }); @@ -94,7 +95,7 @@ fn test_parse_block_header() { let result = parse_block_header(buffer); assert_eq(result, BlockHeaderResult { status: BlockHeaderStatus::OK, - header: BlockHeader { last: u1:0, btype: BlockType::RLE, size: u21:0x1234 }, + header: BlockHeader { last: u1:0, btype: BlockType::RLE, size: BlockSize:0x1234 }, buffer: Buffer { content: u32:0, length: u32:0 } }); diff --git a/xls/modules/zstd/block_header_dec.x b/xls/modules/zstd/block_header_dec.x new file mode 100644 index 0000000000..45c69e921c --- /dev/null +++ b/xls/modules/zstd/block_header_dec.x @@ -0,0 +1,293 @@ +// Copyright 2024 The XLS Authors +// +// 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. + +import xls.modules.zstd.block_header as block_header; +import xls.modules.zstd.common as common; +import xls.modules.zstd.memory.mem_reader as mem_reader; + +type BlockSize = common::BlockSize; +type BlockType = common::BlockType; +type BlockHeader = block_header::BlockHeader; + +pub struct BlockHeaderDecoderReq { + addr: uN[ADDR_W], +} + +pub enum BlockHeaderDecoderStatus: u2 { + OKAY = 0, + CORRUPTED = 1, + MEMORY_ACCESS_ERROR = 2, +} + +pub struct BlockHeaderDecoderResp { + status: BlockHeaderDecoderStatus, + header: BlockHeader, + rle_symbol: u8, +} + +pub proc BlockHeaderDecoder { + type Req = BlockHeaderDecoderReq; + type Resp = BlockHeaderDecoderResp; + + type MemReaderReq = mem_reader::MemReaderReq; + type MemReaderResp = mem_reader::MemReaderResp; + type MemReaderStatus = mem_reader::MemReaderStatus; + + type Status = BlockHeaderDecoderStatus; + type Length = uN[ADDR_W]; + type Addr = uN[ADDR_W]; + + req_r: chan in; + resp_s: chan out; + mem_req_s: chan out; + mem_resp_r: chan in; + + config ( + req_r: chan in, + resp_s: chan out, + mem_req_s: chan out, + mem_resp_r: chan in, + ) { + (req_r, resp_s, mem_req_s, mem_resp_r) + } + + init { } + + next (state: ()) { + let tok0 = join(); + + // receive request + let (tok1_0, req, req_valid) = recv_non_blocking(tok0, req_r, zero!()); + + // send memory read request + let mem_req = MemReaderReq {addr: req.addr, length: Length:4 }; + let tok2_0 = send_if(tok1_0, mem_req_s, req_valid, mem_req); + + // receive memory read response + let (tok1_1, mem_resp, mem_resp_valid) = recv_non_blocking(tok0, mem_resp_r, zero!()); + + let header = block_header::extract_block_header(mem_resp.data as u24); + let rle_symbol = mem_resp.data[u32:24 +: u8]; + let status = match ( mem_resp.status == MemReaderStatus::OKAY, header.btype != BlockType::RESERVED) { + (true, true) => Status::OKAY, + (true, false) => Status::CORRUPTED, + ( _, _) => Status::MEMORY_ACCESS_ERROR, + }; + + let resp = Resp { status, header, rle_symbol }; + let tok2_1 = send_if(tok1_1, resp_s, mem_resp_valid, resp); + } +} + +const INST_DATA_W = u32:64; +const INST_ADDR_W = u32:16; + +proc BlockHeaderDecoderInst { + type Req = BlockHeaderDecoderReq; + type Resp = BlockHeaderDecoderResp; + type MemReaderReq = mem_reader::MemReaderReq; + type MemReaderResp = mem_reader::MemReaderResp; + + config ( + req_r: chan in, + resp_s: chan out, + mem_req_s: chan out, + mem_resp_r: chan in, + ) { + spawn BlockHeaderDecoder( req_r, resp_s, mem_req_s, mem_resp_r); + } + + init { } + next (state: ()) { } +} + +const TEST_DATA_W = u32:32; +const TEST_ADDR_W = u32:32; + +fn header_to_raw(header: BlockHeader, rle_symbol: u8) -> u32 { + rle_symbol ++ header.size ++ (header.btype as u2) ++ header.last +} + + +#[test_proc] +proc BlockHeaderDecoderTest { + type Req = BlockHeaderDecoderReq; + type Resp = BlockHeaderDecoderResp; + + type MemReaderReq = mem_reader::MemReaderReq; + type MemReaderResp = mem_reader::MemReaderResp; + type MemReaderStatus = mem_reader::MemReaderStatus; + + type Data = uN[TEST_DATA_W]; + type Addr = uN[TEST_ADDR_W]; + type Length = uN[TEST_ADDR_W]; + + terminator: chan out; + + req_s: chan out; + resp_r: chan in; + + mem_req_r: chan in; + mem_resp_s: chan out; + + config (terminator: chan out) { + + let (req_s, req_r) = chan("req"); + let (resp_s, resp_r) = chan("resp"); + + let (mem_req_s, mem_req_r) = chan("mem_req"); + let (mem_resp_s, mem_resp_r) = chan("mem_resp"); + + spawn BlockHeaderDecoder ( + req_r, resp_s, mem_req_s, mem_resp_r + ); + + (terminator, req_s, resp_r, mem_req_r, mem_resp_s) + } + + init { } + + next (state: ()) { + const LENGTH = Length:4; + + let tok = join(); + + // Test Raw + let addr = Addr:0x1234; + let header = BlockHeader { size: BlockSize:0x100, btype: BlockType::RAW, last: true}; + let rle_symbol = u8:0; + + let req = Req { addr }; + let tok = send(tok, req_s, req); + + let (tok, mem_req) = recv(tok, mem_req_r); + assert_eq(mem_req, MemReaderReq { addr, length: LENGTH }); + + let mem_resp = MemReaderResp { + status: MemReaderStatus::OKAY, + data: checked_cast(header_to_raw(header, rle_symbol)), + length: LENGTH, + last: true, + }; + let tok = send(tok, mem_resp_s, mem_resp); + let (tok, resp) = recv(tok, resp_r); + assert_eq(resp, Resp { + status: BlockHeaderDecoderStatus::OKAY, + header: header, + rle_symbol: rle_symbol + }); + + // Test RLE + let addr = Addr:0x2000; + let header = BlockHeader { size: BlockSize:0x40, btype: BlockType::RLE, last: false}; + let rle_symbol = u8:123; + + let req = Req { addr }; + let tok = send(tok, req_s, req); + + let (tok, mem_req) = recv(tok, mem_req_r); + assert_eq(mem_req, MemReaderReq { addr, length: LENGTH }); + + let mem_resp = MemReaderResp { + status: MemReaderStatus::OKAY, + data: checked_cast(header_to_raw(header, rle_symbol)), + length: LENGTH, + last: true, + }; + let tok = send(tok, mem_resp_s, mem_resp); + let (tok, resp) = recv(tok, resp_r); + assert_eq(resp, Resp { + status: BlockHeaderDecoderStatus::OKAY, + header: header, + rle_symbol: rle_symbol + }); + + // Test COMPRESSED + let addr = Addr:0x2000; + let header = BlockHeader { size: BlockSize:0x40, btype: BlockType::COMPRESSED, last: true}; + let rle_symbol = u8:0; + + let req = Req { addr }; + let tok = send(tok, req_s, req); + + let (tok, mem_req) = recv(tok, mem_req_r); + assert_eq(mem_req, MemReaderReq { addr, length: LENGTH }); + + let mem_resp = MemReaderResp { + status: MemReaderStatus::OKAY, + data: checked_cast(header_to_raw(header, rle_symbol)), + length: LENGTH, + last: true, + }; + let tok = send(tok, mem_resp_s, mem_resp); + let (tok, resp) = recv(tok, resp_r); + assert_eq(resp, Resp { + status: BlockHeaderDecoderStatus::OKAY, + header: header, + rle_symbol: rle_symbol + }); + + // Test RESERVED + let addr = Addr:0x2000; + let header = BlockHeader { size: BlockSize:0x40, btype: BlockType::RESERVED, last: true}; + let rle_symbol = u8:0; + + let req = Req { addr }; + let tok = send(tok, req_s, req); + + let (tok, mem_req) = recv(tok, mem_req_r); + assert_eq(mem_req, MemReaderReq { addr, length: LENGTH }); + + let mem_resp = MemReaderResp { + status: MemReaderStatus::OKAY, + data: checked_cast(header_to_raw(header, rle_symbol)), + length: LENGTH, + last: true, + }; + let tok = send(tok, mem_resp_s, mem_resp); + let (tok, resp) = recv(tok, resp_r); + assert_eq(resp, Resp { + status: BlockHeaderDecoderStatus::CORRUPTED, + header: header, + rle_symbol: rle_symbol + }); + + // Test memory error + let addr = Addr:0x2000; + let header = BlockHeader { size: BlockSize:0x40, btype: BlockType::RESERVED, last: true}; + let rle_symbol = u8:0; + + let req = Req { addr }; + let tok = send(tok, req_s, req); + + let (tok, mem_req) = recv(tok, mem_req_r); + assert_eq(mem_req, MemReaderReq { addr, length: LENGTH }); + + let mem_resp = MemReaderResp { + status: MemReaderStatus::ERROR, + data: checked_cast(header_to_raw(header, rle_symbol)), + length: LENGTH, + last: true, + }; + let tok = send(tok, mem_resp_s, mem_resp); + let (tok, resp) = recv(tok, resp_r); + assert_eq(resp, Resp { + status: BlockHeaderDecoderStatus::MEMORY_ACCESS_ERROR, + header: header, + rle_symbol: rle_symbol + }); + + send(tok, terminator, true); + } +} diff --git a/xls/modules/zstd/cocotb/BUILD b/xls/modules/zstd/cocotb/BUILD index b8ff7e3f8b..dd53221109 100644 --- a/xls/modules/zstd/cocotb/BUILD +++ b/xls/modules/zstd/cocotb/BUILD @@ -64,3 +64,13 @@ py_library( requirement("cocotb"), ], ) + +py_library( + name = "data_generator", + srcs = ["data_generator.py"], + deps = [ + "@zstd//:decodecorpus", + "//xls/common:runfiles", + ] +) + diff --git a/xls/modules/zstd/cocotb/data_generator.py b/xls/modules/zstd/cocotb/data_generator.py new file mode 100644 index 0000000000..831cbbce79 --- /dev/null +++ b/xls/modules/zstd/cocotb/data_generator.py @@ -0,0 +1,47 @@ +# Copyright 2024 The XLS Authors +# +# 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. + +from pathlib import Path +from enum import Enum + +from xls.common import runfiles +import subprocess + +class BlockType(Enum): + RAW = 0 + RLE = 1 + COMPRESSED = 2 + RANDOM = 3 + +def CallDecodecorpus(args): + decodecorpus = Path(runfiles.get_path("decodecorpus", repository = "zstd")) + cmd = args + cmd.insert(0, str(decodecorpus)) + cmd_concat = " ".join(cmd) + subprocess.run(cmd_concat, shell=True, check=True) + +def GenerateFrame(seed, btype, output_path): + args = [] + args.append("-s" + str(seed)) + if (btype != BlockType.RANDOM): + args.append("--block-type=" + str(btype.value)) + if (btype == BlockType.RLE): + args.append("--content-size") + # Test payloads up to 16KB + args.append("--max-content-size-log=14") + args.append("-p" + output_path); + args.append("-vvvvvvv"); + + CallDecodecorpus(args) + diff --git a/xls/modules/zstd/cocotb/memory.py b/xls/modules/zstd/cocotb/memory.py index 111b254c6c..52e512e053 100644 --- a/xls/modules/zstd/cocotb/memory.py +++ b/xls/modules/zstd/cocotb/memory.py @@ -18,7 +18,7 @@ from cocotbext.axi.sparse_memory import SparseMemory -def init_axi_mem(path: os.PathLike, **kwargs): +def init_axi_mem(path: os.PathLike, kwargs): with open(path, "rb") as f: sparse_mem = SparseMemory(size=kwargs["size"]) sparse_mem.write(0x0, f.read()) @@ -27,17 +27,17 @@ def init_axi_mem(path: os.PathLike, **kwargs): class AxiRamReadFromFile(AxiRamRead): def __init__(self, *args, path: os.PathLike, **kwargs): - init_axi_mem(path, **kwargs) + init_axi_mem(path, kwargs) super().__init__(*args, **kwargs) class AxiRamFromFile(AxiRam): def __init__(self, *args, path: os.PathLike, **kwargs): - init_axi_mem(path, **kwargs) + init_axi_mem(path, kwargs) super().__init__(*args, **kwargs) class AxiRamWriteFromFile(AxiRamWrite): def __init__(self, *args, path: os.PathLike, **kwargs): - init_axi_mem(path, **kwargs) + init_axi_mem(path, kwargs) super().__init__(*args, **kwargs) diff --git a/xls/modules/zstd/csr_config.x b/xls/modules/zstd/csr_config.x new file mode 100644 index 0000000000..a792757cfa --- /dev/null +++ b/xls/modules/zstd/csr_config.x @@ -0,0 +1,397 @@ +// Copyright 2023-2024 The XLS Authors +// +// 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. + +// This file contains implementation of a proc that handles CSRs. It provides +// an AXI interface for reading and writing the values as well as separate +// request/response channels. Apart from that it has an output channel which +// notifies aboud changes made to CSRs. + +import std; +import xls.modules.zstd.memory.axi; + +pub struct CsrRdReq { + csr: uN[LOG2_REGS_N], +} + +pub struct CsrRdResp { + csr: uN[LOG2_REGS_N], + value: uN[DATA_W], +} + +pub struct CsrWrReq { + csr: uN[LOG2_REGS_N], + value: uN[DATA_W], +} + +pub struct CsrWrResp { } + +pub struct CsrChange { + csr: uN[LOG2_REGS_N], +} + +struct CsrConfigState { + register_file: uN[DATA_W][REGS_N], +} + +pub proc CsrConfig< + ID_W: u32, ADDR_W: u32, DATA_W: u32, REGS_N: u32, + //REGS_INIT: u64[64] = {u64[64]:[u64:0, ...]}, + DATA_W_DIV8: u32 = { DATA_W / u32:8 }, + LOG2_REGS_N: u32 = { std::clog2(REGS_N) }, +> { + + type RdReq = CsrRdReq; + type RdResp = CsrRdResp; + type WrReq = CsrWrReq; + type WrResp = CsrWrResp; + type Change = CsrChange; + + type State = CsrConfigState; + type Data = uN[DATA_W]; + type RegN = uN[LOG2_REGS_N]; + + ext_csr_rd_req_r: chan in; + ext_csr_rd_resp_s: chan out; + ext_csr_wr_req_r: chan in; + ext_csr_wr_resp_s: chan out; + + csr_rd_req_r: chan in; + csr_rd_resp_s: chan out; + csr_wr_req_r: chan in; + csr_wr_resp_s: chan out; + + csr_change_s: chan out; + + config ( + ext_csr_rd_req_r: chan in, + ext_csr_rd_resp_s: chan out, + ext_csr_wr_req_r: chan in, + ext_csr_wr_resp_s: chan out, + + csr_rd_req_r: chan in, + csr_rd_resp_s: chan out, + csr_wr_req_r: chan in, + csr_wr_resp_s: chan out, + csr_change_s: chan out, + ) { + ( + ext_csr_rd_req_r, ext_csr_rd_resp_s, + ext_csr_wr_req_r, ext_csr_wr_resp_s, + csr_rd_req_r, csr_rd_resp_s, + csr_wr_req_r, csr_wr_resp_s, + csr_change_s, + ) + } + + init { + zero!() + } + + next (state: State) { + let register_file = state.register_file; + + let tok_0 = join(); + + // write to CSR + let (tok_1_1_1, ext_csr_wr_req, ext_csr_wr_req_valid) = recv_non_blocking(tok_0, ext_csr_wr_req_r, zero!()); + let (tok_1_1_2, csr_wr_req, csr_wr_req_valid) = recv_non_blocking(tok_0, csr_wr_req_r, zero!()); + + // Mux the Write Requests from External and Internal sources + // Write requests from external source take precedence before internal writes + let wr_req = if (ext_csr_wr_req_valid) { + ext_csr_wr_req + } else if {csr_wr_req_valid} { + csr_wr_req + } else { + zero!() + }; + + let wr_req_valid = ext_csr_wr_req_valid | csr_wr_req_valid; + + let register_file = if wr_req_valid { + update(register_file, wr_req.csr as u32, wr_req.value) + } else { + register_file + }; + + // Send Write Response + let tok_1_1 = join(tok_1_1_1, tok_1_1_2); + let tok_1_2_1 = send_if(tok_1_1, ext_csr_wr_resp_s, ext_csr_wr_req_valid, WrResp {}); + let tok_1_2_2 = send_if(tok_1_1, csr_wr_resp_s, csr_wr_req_valid, WrResp {}); + + // Send change notification + let tok_1_2 = join(tok_1_2_1, tok_1_2_2); + let tok_1_3 = send_if(tok_1_2, csr_change_s, wr_req_valid, Change { csr: wr_req.csr }); + + + // Read from CSRs + let (tok_2_1, ext_csr_rd_req, ext_csr_req_valid) = recv_non_blocking(tok_0, ext_csr_rd_req_r, zero!()); + + send_if(tok_2_1, ext_csr_rd_resp_s, ext_csr_req_valid, RdResp { + csr: ext_csr_rd_req.csr, + value: register_file[ext_csr_rd_req.csr as u32], + }); + + let (tok_3_1, csr_rd_req, csr_req_valid) = recv_non_blocking(tok_0, csr_rd_req_r, zero!()); + send_if(tok_3_1, csr_rd_resp_s, csr_req_valid, RdResp { + csr: csr_rd_req.csr, + value: register_file[csr_rd_req.csr as u32], + }); + + State { + register_file: register_file, + } + } +} + +const INST_ID_W = u32:32; +const INST_DATA_W = u32:32; +const INST_ADDR_W = u32:2; +const INST_REGS_N = u32:4; +const INST_DATA_W_DIV8 = INST_DATA_W / u32:8; +const INST_LOG2_REGS_N = std::clog2(INST_REGS_N); + +proc CsrConfigInst { + type InstCsrRdReq = CsrRdReq; + type InstCsrRdResp = CsrRdResp; + type InstCsrWrReq = CsrWrReq; + type InstCsrWrResp = CsrWrResp; + type InstCsrChange = CsrChange; + + config( + ext_csr_rd_req_r: chan in, + ext_csr_rd_resp_s: chan out, + ext_csr_wr_req_r: chan in, + ext_csr_wr_resp_s: chan out, + + csr_rd_req_r: chan in, + csr_rd_resp_s: chan out, + csr_wr_req_r: chan in, + csr_wr_resp_s: chan out, + csr_change_s: chan out, + ) { + spawn CsrConfig ( + ext_csr_rd_req_r, ext_csr_rd_resp_s, + ext_csr_wr_req_r, ext_csr_wr_resp_s, + csr_rd_req_r, csr_rd_resp_s, + csr_wr_req_r, csr_wr_resp_s, + csr_change_s, + ); + } + + init { } + + next (state: ()) { } +} + +const TEST_ID_W = u32:32; +const TEST_DATA_W = u32:32; +const TEST_ADDR_W = u32:2; +const TEST_REGS_N = u32:4; +const TEST_DATA_W_DIV8 = TEST_DATA_W / u32:8; +const TEST_LOG2_REGS_N = std::clog2(TEST_REGS_N); + +type TestCsr = uN[TEST_LOG2_REGS_N]; +type TestValue = uN[TEST_DATA_W]; + +struct TestData { + csr: uN[TEST_LOG2_REGS_N], + value: uN[TEST_DATA_W], +} + +const TEST_DATA = TestData[20]:[ + TestData{ csr: TestCsr:0, value: TestValue:0xca32_9f4a }, + TestData{ csr: TestCsr:1, value: TestValue:0x0fb3_fa42 }, + TestData{ csr: TestCsr:2, value: TestValue:0xe7ee_da41 }, + TestData{ csr: TestCsr:3, value: TestValue:0xef51_f98c }, + TestData{ csr: TestCsr:0, value: TestValue:0x97a3_a2d2 }, + TestData{ csr: TestCsr:0, value: TestValue:0xea06_e94b }, + TestData{ csr: TestCsr:1, value: TestValue:0x5fac_17ce }, + TestData{ csr: TestCsr:3, value: TestValue:0xf9d8_9938 }, + TestData{ csr: TestCsr:2, value: TestValue:0xc262_2d2e }, + TestData{ csr: TestCsr:2, value: TestValue:0xb4dd_424e }, + TestData{ csr: TestCsr:1, value: TestValue:0x01f9_b9e4 }, + TestData{ csr: TestCsr:1, value: TestValue:0x3020_6eec }, + TestData{ csr: TestCsr:3, value: TestValue:0x3124_87b5 }, + TestData{ csr: TestCsr:0, value: TestValue:0x0a49_f5e3 }, + TestData{ csr: TestCsr:2, value: TestValue:0xde3b_5d0f }, + TestData{ csr: TestCsr:3, value: TestValue:0x5948_c1b3 }, + TestData{ csr: TestCsr:0, value: TestValue:0xa26d_851f }, + TestData{ csr: TestCsr:3, value: TestValue:0x3fa9_59c0 }, + TestData{ csr: TestCsr:1, value: TestValue:0x4efd_dd09 }, + TestData{ csr: TestCsr:1, value: TestValue:0x6d75_058a }, +]; + +#[test_proc] +proc CsrConfig_test { + type TestCsrRdReq = CsrRdReq; + type TestCsrRdResp = CsrRdResp; + type TestCsrWrReq = CsrWrReq; + type TestCsrWrResp = CsrWrResp; + type TestCsrChange = CsrChange; + + terminator: chan out; + + ext_csr_rd_req_s: chan out; + ext_csr_rd_resp_r: chan in; + ext_csr_wr_req_s: chan out; + ext_csr_wr_resp_r: chan in; + + csr_rd_req_s: chan out; + csr_rd_resp_r: chan in; + csr_wr_req_s: chan out; + csr_wr_resp_r: chan in; + + csr_change_r: chan in; + + config (terminator: chan out) { + let (ext_csr_rd_req_s, ext_csr_rd_req_r) = chan("ext_csr_rd_req"); + let (ext_csr_rd_resp_s, ext_csr_rd_resp_r) = chan("ext_csr_rd_resp"); + + let (ext_csr_wr_req_s, ext_csr_wr_req_r) = chan("ext_csr_wr_req"); + let (ext_csr_wr_resp_s, ext_csr_wr_resp_r) = chan("ext_csr_wr_resp"); + + let (csr_rd_req_s, csr_rd_req_r) = chan("csr_rd_req"); + let (csr_rd_resp_s, csr_rd_resp_r) = chan("csr_rd_resp"); + + let (csr_wr_req_s, csr_wr_req_r) = chan("csr_wr_req"); + let (csr_wr_resp_s, csr_wr_resp_r) = chan("csr_wr_resp"); + + let (csr_change_s, csr_change_r) = chan("csr_change"); + + spawn CsrConfig ( + ext_csr_rd_req_r, ext_csr_rd_resp_s, + ext_csr_wr_req_r, ext_csr_wr_resp_s, + csr_rd_req_r, csr_rd_resp_s, + csr_wr_req_r, csr_wr_resp_s, + csr_change_s, + ); + + ( + terminator, + ext_csr_rd_req_s, ext_csr_rd_resp_r, + ext_csr_wr_req_s, ext_csr_wr_resp_r, + csr_rd_req_s, csr_rd_resp_r, + csr_wr_req_s, csr_wr_resp_r, + csr_change_r, + ) + } + + init { } + + next (state: ()) { + let expected_values = zero!(); + + // Test Writes through external interface + let (tok, expected_values) = for ((i, test_data), (tok, expected_values)): ((u32, TestData), (token, uN[TEST_DATA_W][TEST_REGS_N])) in enumerate(TEST_DATA) { + // write CSR via external interface + let wr_req = TestCsrWrReq { + csr: test_data.csr, + value: test_data.value, + }; + let tok = send(tok, ext_csr_wr_req_s, wr_req); + trace_fmt!("Sent #{} WrReq through external interface: {:#x}", i + u32:1, wr_req); + + let (tok, wr_resp) = recv(tok, ext_csr_wr_resp_r); + trace_fmt!("Received #{} WrResp through external interface: {:#x}", i + u32:1, wr_resp); + + // read CSR change + let (tok, csr_change) = recv(tok, csr_change_r); + trace_fmt!("Received #{} CSR change {:#x}", i + u32:1, csr_change); + + assert_eq(test_data.csr, csr_change.csr); + + // update expected values + let expected_values = update(expected_values, test_data.csr as u32, test_data.value); + + let tok = for (test_csr, tok): (u32, token) in u32:0..u32:4 { + let rd_req = TestCsrRdReq { + csr: test_csr as TestCsr, + }; + let expected_rd_resp = TestCsrRdResp{ + csr: test_csr as TestCsr, + value: expected_values[test_csr as u32] + }; + + // Read CSR via external interface + let tok = send(tok, ext_csr_rd_req_s, rd_req); + trace_fmt!("Sent #{} RdReq through external interface: {:#x}", i + u32:1, rd_req); + let (tok, rd_resp) = recv(tok, ext_csr_rd_resp_r); + trace_fmt!("Received #{} RdResp through external interface: {:#x}", i + u32:1, rd_resp); + assert_eq(expected_rd_resp, rd_resp); + + // Read CSR via internal interface + let tok = send(tok, csr_rd_req_s, rd_req); + trace_fmt!("Sent #{} RdReq through internal interface: {:#x}", i + u32:1, rd_req); + let (tok, csr_rd_resp) = recv(tok, csr_rd_resp_r); + trace_fmt!("Received #{} RdResp through internal interface: {:#x}", i + u32:1, csr_rd_resp); + assert_eq(expected_rd_resp, csr_rd_resp); + tok + }(tok); + + (tok, expected_values) + }((join(), expected_values)); + + // Test writes via internal interface + let (tok, _) = for ((i, test_data), (tok, expected_values)): ((u32, TestData), (token, uN[TEST_DATA_W][TEST_REGS_N])) in enumerate(TEST_DATA) { + // write CSR via request channel + let csr_wr_req = TestCsrWrReq { + csr: test_data.csr, + value: test_data.value, + }; + let tok = send(tok, csr_wr_req_s, csr_wr_req); + trace_fmt!("Sent #{} WrReq through internal interface: {:#x}", i + u32:1, csr_wr_req); + + let (tok, csr_wr_resp) = recv(tok, csr_wr_resp_r); + trace_fmt!("Received #{} WrResp through internal interface {:#x}", i + u32:1, csr_wr_resp); + + // read CSR change + let (tok, csr_change) = recv(tok, csr_change_r); + trace_fmt!("Received #{} CSR change {:#x}", i + u32:1, csr_change); + assert_eq(test_data.csr, csr_change.csr); + + // update expected values + let expected_values = update(expected_values, test_data.csr as u32, test_data.value); + + let tok = for (test_csr, tok): (u32, token) in u32:0..u32:4 { + let rd_req = TestCsrRdReq { + csr: test_csr as TestCsr, + }; + let expected_rd_resp = TestCsrRdResp{ + csr: test_csr as TestCsr, + value: expected_values[test_csr as u32] + }; + + // Read CSR via external interface + let tok = send(tok, ext_csr_rd_req_s, rd_req); + trace_fmt!("Sent #{} RdReq through external interface: {:#x}", i + u32:1, rd_req); + let (tok, rd_resp) = recv(tok, ext_csr_rd_resp_r); + trace_fmt!("Received #{} RdResp through external interface: {:#x}", i + u32:1, rd_resp); + assert_eq(expected_rd_resp, rd_resp); + + // Read CSR via internal interface + let tok = send(tok, csr_rd_req_s, rd_req); + trace_fmt!("Sent #{} RdReq through internal interface: {:#x}", i + u32:1, rd_req); + let (tok, csr_rd_resp) = recv(tok, csr_rd_resp_r); + trace_fmt!("Received #{} RdResp through internal interface: {:#x}", i + u32:1, csr_rd_resp); + assert_eq(expected_rd_resp, csr_rd_resp); + tok + }(tok); + + (tok, expected_values) + }((join(), expected_values)); + + send(tok, terminator, true); + } +} diff --git a/xls/modules/zstd/dec.x b/xls/modules/zstd/dec.x new file mode 100644 index 0000000000..1624f089af --- /dev/null +++ b/xls/modules/zstd/dec.x @@ -0,0 +1,1395 @@ +// Copyright 2024 The XLS Authors +// +// 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. + +// This file contains work-in-progress ZSTD decoder implementation +// More information about ZSTD decoding can be found in: +// https://datatracker.ietf.org/doc/html/rfc8878 + +import std; +import xls.examples.ram; +import xls.modules.zstd.axi_csr_accessor; +import xls.modules.zstd.common; +import xls.modules.zstd.memory.axi; +import xls.modules.zstd.csr_config; +import xls.modules.zstd.memory.mem_reader; +import xls.modules.zstd.frame_header_dec; +import xls.modules.zstd.block_header; +import xls.modules.zstd.block_header_dec; +import xls.modules.zstd.raw_block_dec_new_mem; +import xls.modules.zstd.rle_block_dec_new_mem2; +import xls.modules.zstd.dec_mux; +import xls.modules.zstd.sequence_executor; +import xls.modules.zstd.repacketizer; + +type BlockSize = common::BlockSize; +type BlockType = common::BlockType; +type BlockHeader = block_header::BlockHeader; + +enum ZstdDecoderInternalFsm: u4 { + IDLE = 0, + READ_CONFIG = 1, + DECODE_FRAME_HEADER = 2, + DECODE_BLOCK_HEADER = 3, + DECODE_RAW_BLOCK = 4, + DECODE_RLE_BLOCK = 5, + DECODE_COMPRESSED_BLOCK = 6, + DECODE_CHECKSUM = 7, + FINISH = 8, + ERROR = 13, + INVALID = 15, +} + +enum ZstdDecoderStatus: u3 { + OKAY = 0, + FINISHED = 1, + FRAME_HEADER_CORRUPTED = 2, + FRAME_HEADER_UNSUPPORTED_WINDOW_SIZE = 3, + BLOCK_HEADER_CORRUPTED = 4, +} + +enum Csr: u3 { + STATUS = 0, // Keeps the code describing the current state of the ZSTD Decoder + START = 1, // Writing 1 when decoder is in IDLE state starts the decoding process + RESET = 2, // Writing 1 will reset the decoder to the IDLE state + INPUT_BUFFER = 3, // Keeps the base address for the input buffer that is used for storing the frame to decode + OUTPUT_BUFFER = 4, // Keeps the base address for the output buffer, ZSTD Decoder will write the decoded frame into memory starting from this address. + WHO_AM_I = 5, // Contains the identification number of the ZSTD Decoder +} + +fn csr(c: Csr) -> uN[LOG2_REGS_N] { + c as uN[LOG2_REGS_N] +} + +struct ZstdDecoderInternalState { + fsm: ZstdDecoderInternalFsm, + + // Reading CSRs + conf_cnt: uN[LOG2_REGS_N], + conf_send: bool, + input_buffer: uN[AXI_ADDR_W], + input_buffer_valid: bool, + output_buffer: uN[AXI_ADDR_W], + output_buffer_valid: bool, + + // Writing to CSRs + csr_wr_req: csr_config::CsrWrReq, + csr_wr_req_valid: bool, + + // BH address + bh_addr: uN[AXI_ADDR_W], + + // Block + block_addr: uN[AXI_ADDR_W], + block_length: uN[AXI_ADDR_W], + block_last: bool, + block_id: u32, + block_rle_symbol: u8, + + // Req + req_sent: bool, +} + +proc ZstdDecoderInternal< + AXI_DATA_W: u32, AXI_ADDR_W: u32, REGS_N: u32, + LOG2_REGS_N:u32 = {std::clog2(REGS_N)}, + HB_RAM_N:u32 = {u32:8}, +> { + + type State = ZstdDecoderInternalState; + type Fsm = ZstdDecoderInternalFsm; + type Reg = uN[LOG2_REGS_N]; + type Data = uN[AXI_DATA_W]; + type Addr = uN[AXI_ADDR_W]; + + type CsrRdReq = csr_config::CsrRdReq; + type CsrRdResp = csr_config::CsrRdResp; + type CsrWrReq = csr_config::CsrWrReq; + type CsrWrResp = csr_config::CsrWrResp; + type CsrChange = csr_config::CsrChange; + + type MemReaderReq = mem_reader::MemReaderReq; + type MemReaderResp = mem_reader::MemReaderResp; + + type FrameHeaderDecoderStatus = frame_header_dec::FrameHeaderDecoderStatus; + type FrameHeaderDecoderReq = frame_header_dec::FrameHeaderDecoderReq; + type FrameHeaderDecoderResp = frame_header_dec::FrameHeaderDecoderResp; + + type BlockHeaderDecoderStatus = block_header_dec::BlockHeaderDecoderStatus; + type BlockHeaderDecoderReq = block_header_dec::BlockHeaderDecoderReq; + type BlockHeaderDecoderResp = block_header_dec::BlockHeaderDecoderResp; + + type RawBlockDecoderStatus = raw_block_dec_new_mem::RawBlockDecoderStatus; + type RawBlockDecoderReq = raw_block_dec_new_mem::RawBlockDecoderReq; + type RawBlockDecoderResp = raw_block_dec_new_mem::RawBlockDecoderResp; + + type RleBlockDecoderStatus = rle_block_dec_new_mem2::RleBlockDecoderStatus; + type RleBlockDecoderReq = rle_block_dec_new_mem2::RleBlockDecoderReq; + type RleBlockDecoderResp = rle_block_dec_new_mem2::RleBlockDecoderResp; + + // CsrConfig + csr_rd_req_s: chan out; + csr_rd_resp_r: chan in; + csr_wr_req_s: chan out; + csr_wr_resp_r: chan in; + csr_change_r: chan in; + + // MemReader + FameHeaderDecoder + fh_req_s: chan out; + fh_resp_r: chan in; + + // MemReader + BlockHeaderDecoder + bh_req_s: chan out; + bh_resp_r: chan in; + + // MemReader + RawBlockDecoder + raw_req_s: chan out; + raw_resp_r: chan in; + + // MemReader + RleBlockDecoder + rle_req_s: chan out; + rle_resp_r: chan in; + + notify_s: chan<()> out; + + init { + zero!() + } + + config( + csr_rd_req_s: chan out, + csr_rd_resp_r: chan in, + csr_wr_req_s: chan out, + csr_wr_resp_r: chan in, + csr_change_r: chan in, + + // MemReader + FameHeaderDecoder + fh_req_s: chan out, + fh_resp_r: chan in, + + // MemReader + BlockHeaderDecoder + bh_req_s: chan out, + bh_resp_r: chan in, + + // MemReader + RawBlockDecoder + raw_req_s: chan out, + raw_resp_r: chan in, + + // MemReader + RleBlockDecoder + rle_req_s: chan out, + rle_resp_r: chan in, + + notify_s: chan<()> out, + ) { + ( + csr_rd_req_s, csr_rd_resp_r, csr_wr_req_s, csr_wr_resp_r, csr_change_r, + fh_req_s, fh_resp_r, + bh_req_s, bh_resp_r, + raw_req_s, raw_resp_r, + rle_req_s, rle_resp_r, + notify_s, + ) + } + + next (state: State) { + let tok0 = join(); + + const CSR_REQS = CsrRdReq[2]:[ + CsrRdReq {csr: csr(Csr::INPUT_BUFFER)}, + CsrRdReq {csr: csr(Csr::OUTPUT_BUFFER)} + ]; + + const CSR_REQS_MAX = checked_cast(array_size(CSR_REQS) - u32:1); + + let (tok1_0, csr_change, csr_change_valid) = recv_non_blocking(tok0, csr_change_r, zero!()); + let is_start = (csr_change_valid && (csr_change.csr == csr(Csr::START))); + + let do_send_csr_req = (state.fsm == Fsm::READ_CONFIG) && (!state.conf_send); + let csr_req = CSR_REQS[state.conf_cnt]; + let tok1_1 = send_if(tok0, csr_rd_req_s, do_send_csr_req, csr_req); + if do_send_csr_req { + trace_fmt!("[READ_CONFIG] Sending read request {:#x}", csr_req); + } else {}; + + let do_recv_csr_resp = (state.fsm == Fsm::READ_CONFIG); + let (tok1_2, csr_data, csr_data_valid) = recv_if_non_blocking(tok0, csr_rd_resp_r, do_recv_csr_resp, zero!()); + if csr_data_valid { + trace_fmt!("[READ_CONFIG] Received CSR data: {:#x}", csr_data); + } else {}; + + let do_send_fh_req = (state.fsm == Fsm::DECODE_FRAME_HEADER) && !state.req_sent; + let fh_req = FrameHeaderDecoderReq { addr: state.input_buffer }; + let tok1_3 = send_if(tok0, fh_req_s, do_send_fh_req, fh_req); + if do_send_fh_req { + trace_fmt!("[DECODE_FRAME_HEADER] Sending FH request {:#x}", fh_req); + } else {}; + + let do_recv_fh_resp = (state.fsm == Fsm::DECODE_FRAME_HEADER); + let (tok1_4, fh_resp, fh_resp_valid) = recv_if_non_blocking(tok0, fh_resp_r, do_recv_fh_resp, zero!()); + if fh_resp_valid { + trace_fmt!("[DECODE_FRAME_HEADER]: Received FH {:#x}", fh_resp); + } else {}; + + let do_send_notify = (state.fsm == Fsm::ERROR || state.fsm == Fsm::FINISH); + let tok = send_if(tok0, notify_s, do_send_notify, ()); + if do_send_notify { + trace_fmt!("[[NOTIFY]]"); + } else {}; + + let tok1_5 = send_if(tok0, csr_wr_req_s, state.csr_wr_req_valid, state.csr_wr_req); + let (tok, _, _) = recv_non_blocking(tok0, csr_wr_resp_r, zero!()); + if state.csr_wr_req_valid { + trace_fmt!("[[CSR_WR_REQ]] Request: {:#x}", state.csr_wr_req); + } else {}; + + let do_send_bh_req = (state.fsm == Fsm::DECODE_BLOCK_HEADER) && !state.req_sent; + let bh_req = BlockHeaderDecoderReq { addr: state.bh_addr }; + let tok1_6 = send_if(tok0, bh_req_s, do_send_bh_req, bh_req); + if do_send_bh_req { + trace_fmt!("[DECODE_BLOCK_HEADER]: Sending BH request: {:#x}", bh_req); + } else {}; + + let do_recv_bh_resp = (state.fsm == Fsm::DECODE_BLOCK_HEADER); + let (tok1_4, bh_resp, bh_resp_valid) = recv_if_non_blocking(tok0, bh_resp_r, do_recv_bh_resp, zero!()); + if bh_resp_valid { + trace_fmt!("[DECODE_BLOCK_HEADER]: Received BH {:#x}", bh_resp); + } else {}; + + let do_send_raw_req = (state.fsm == Fsm::DECODE_RAW_BLOCK) && !state.req_sent; + let raw_req = RawBlockDecoderReq { + id: state.block_id, + last_block: state.block_last, + addr: state.block_addr, + length: state.block_length, + }; + let tok1_6 = send_if(tok0, raw_req_s, do_send_raw_req, raw_req); + if do_send_raw_req { + trace_fmt!("[DECODE_RAW_BLOCK]: Sending RAW request: {:#x}", raw_req); + } else {}; + + let do_recv_raw_resp = (state.fsm == Fsm::DECODE_RAW_BLOCK); + let (tok1_7, raw_resp, raw_resp_valid) = recv_if_non_blocking(tok0, raw_resp_r, do_recv_raw_resp, zero!()); + if raw_resp_valid { + trace_fmt!("[DECODE_RAW_BLOCK]: Received RAW {:#x}", raw_resp); + } else {}; + + let do_send_rle_req = (state.fsm == Fsm::DECODE_RLE_BLOCK) && !state.req_sent; + let rle_req = RleBlockDecoderReq { + id: state.block_id, + symbol: state.block_rle_symbol, + length: checked_cast(state.block_length), + last_block: state.block_last, + }; + let tok1_7 = send_if(tok0, rle_req_s, do_send_rle_req, rle_req); + if do_send_rle_req { + trace_fmt!("[DECODE_RLE_BLOCK]: Sending RLE request: {:#x}", rle_req); + } else {}; + + let do_recv_rle_resp = (state.fsm == Fsm::DECODE_RLE_BLOCK); + let (tok1_8, rle_resp, rle_resp_valid) = recv_if_non_blocking(tok0, rle_resp_r, do_recv_rle_resp, zero!()); + if raw_resp_valid { + trace_fmt!("[DECODE_RLE_BLOCK]: Received RAW {:#x}", raw_resp); + } else {}; + + let new_state = match (state.fsm) { + Fsm::IDLE => { + trace_fmt!("[IDLE]"); + if is_start { + State { fsm: Fsm::READ_CONFIG, conf_cnt: CSR_REQS_MAX, ..zero!() } + } else { zero!() } + }, + + Fsm::READ_CONFIG => { + trace_fmt!("[READ_CONFIG]"); + let is_input_buffer_csr = (csr_data.csr == csr(Csr::INPUT_BUFFER)); + let input_buffer = if csr_data_valid && is_input_buffer_csr { checked_cast(csr_data.value) } else { state.input_buffer }; + let input_buffer_valid = if csr_data_valid && is_input_buffer_csr { true } else { state.input_buffer_valid }; + + let is_output_buffer_csr = (csr_data.csr == csr(Csr::OUTPUT_BUFFER)); + let output_buffer = if (csr_data_valid && is_output_buffer_csr) { checked_cast(csr_data.value) } else { state.output_buffer }; + let output_buffer_valid = if (csr_data_valid && is_output_buffer_csr) { true } else { state.output_buffer_valid }; + + let all_collected = input_buffer_valid & output_buffer_valid; + let fsm = if all_collected { Fsm::DECODE_FRAME_HEADER } else { Fsm::READ_CONFIG }; + + let conf_send = (state.conf_cnt == Reg:0); + let conf_cnt = if conf_send { Reg:0 } else {state.conf_cnt - Reg:1}; + + State { + fsm, conf_cnt, conf_send, input_buffer, input_buffer_valid, output_buffer, output_buffer_valid, + ..zero!() + } + }, + + Fsm::DECODE_FRAME_HEADER => { + trace_fmt!("[DECODE_FRAME_HEADER]"); + let error = (fh_resp.status != FrameHeaderDecoderStatus::OKAY); + + let csr_wr_req_valid = (fh_resp_valid && error); + let csr_wr_req = CsrWrReq { + csr: csr(Csr::STATUS), + value: checked_cast(fh_resp.status), + }; + + let fsm = match (fh_resp_valid, error) { + ( true, false) => Fsm::DECODE_BLOCK_HEADER, + ( true, true) => Fsm::ERROR, + ( _, _) => Fsm::DECODE_FRAME_HEADER, + }; + + let bh_addr = state.input_buffer + fh_resp.length as Addr; + let req_sent = if !fh_resp_valid && !error { true } else { false }; + State {fsm, csr_wr_req, csr_wr_req_valid, bh_addr, req_sent, ..state } + }, + + Fsm::DECODE_BLOCK_HEADER => { + trace_fmt!("[DECODE_BLOCK_HEADER]"); + let error = (bh_resp.status != BlockHeaderDecoderStatus::OKAY); + + let csr_wr_req_valid = (bh_resp_valid && error); + let csr_wr_req = CsrWrReq { + csr: csr(Csr::STATUS), + value: checked_cast(bh_resp.status), + }; + + let fsm = match (bh_resp_valid, error, bh_resp.header.btype) { + ( true, false, BlockType::RAW ) => Fsm::DECODE_RAW_BLOCK, + ( true, false, BlockType::RLE ) => Fsm::DECODE_RLE_BLOCK, + ( true, false, BlockType::COMPRESSED) => Fsm::ERROR, + ( true, true, _) => Fsm::ERROR, + ( _, _, _) => Fsm::DECODE_BLOCK_HEADER, + }; + + let (block_addr, block_length, block_last, block_rle_symbol, bh_addr) = if bh_resp_valid { + let block_addr = state.bh_addr + Addr:3; + let block_length = checked_cast(bh_resp.header.size); + let block_rle_symbol = bh_resp.rle_symbol; + let bh_addr = if bh_resp.header.btype == BlockType::RLE { + block_addr + Addr:1 + } else { + block_addr + block_length + }; + + trace_fmt!("bh_addr: {:#x}", bh_addr); + + (block_addr, block_length, bh_resp.header.last, block_rle_symbol, bh_addr) + } else { + (state.block_addr, state.block_length, state.block_last, state.block_rle_symbol, state.bh_addr) + }; + + let req_sent = if !bh_resp_valid && !error { true } else { false }; + State { + fsm, bh_addr, req_sent, + block_addr, block_length, block_last, block_rle_symbol, + csr_wr_req, csr_wr_req_valid, + ..state + } + }, + + Fsm::DECODE_RAW_BLOCK => { + trace_fmt!("[DECODE_RAW_BLOCK]"); + + let error = (raw_resp.status != RawBlockDecoderStatus::OKAY); + + let csr_wr_req_valid = (raw_resp_valid && error); + let csr_wr_req = CsrWrReq { + csr: csr(Csr::STATUS), + value: checked_cast(raw_resp.status), + }; + + let fsm = match (raw_resp_valid, error, state.block_last) { + (true, false, false) => Fsm::DECODE_BLOCK_HEADER, + (true, false, true) => Fsm::DECODE_CHECKSUM, + (true, true, _) => Fsm::ERROR, + ( _, _, _) => Fsm::DECODE_RAW_BLOCK, + }; + + let req_sent = if !raw_resp_valid && !error { true } else { false }; + let block_id = if raw_resp_valid { state.block_id + u32:1} else {state.block_id }; + + let state = State {fsm, block_id, csr_wr_req, csr_wr_req_valid, req_sent, ..state}; + if fsm == Fsm::DECODE_BLOCK_HEADER { + trace_fmt!("Going to decode block header: {:#x}", state); + } else {}; + + state + }, + + Fsm::DECODE_RLE_BLOCK => { + trace_fmt!("[DECODE_RLE_BLOCK]"); + let error = (rle_resp.status != RleBlockDecoderStatus::OKAY); + + let csr_wr_req_valid = (rle_resp_valid && error); + let csr_wr_req = CsrWrReq { + csr: csr(Csr::STATUS), + value: checked_cast(rle_resp.status), + }; + + let fsm = match (rle_resp_valid, error, state.block_last) { + (true, false, false) => Fsm::DECODE_BLOCK_HEADER, + (true, false, true) => Fsm::DECODE_CHECKSUM, + (true, true, _) => Fsm::ERROR, + ( _, _, _) => Fsm::DECODE_RLE_BLOCK, + }; + + let req_sent = if !rle_resp_valid && !error { true } else { false }; + let block_id = if rle_resp_valid { state.block_id + u32:1} else {state.block_id }; + + let state = State {fsm, block_id, csr_wr_req, csr_wr_req_valid, req_sent, ..state}; + if fsm == Fsm::DECODE_BLOCK_HEADER { + trace_fmt!("Going to decode block header: {:#x}", state); + } else {}; + + state + }, + + Fsm::DECODE_CHECKSUM => { + trace_fmt!("[DECODE_CHECKSUM]"); + State {fsm: Fsm::FINISH, ..zero!() } + + }, + + Fsm::ERROR => { + trace_fmt!("[ERROR]"); + State { fsm: Fsm::IDLE, ..zero!() } + }, + + Fsm::FINISH => { + trace_fmt!("[FINISH]"); + let csr_wr_req_valid = true; + let csr_wr_req = CsrWrReq { + csr: csr(Csr::STATUS), + value: checked_cast(fh_resp.status), + }; + + State { fsm: Fsm::IDLE, csr_wr_req, csr_wr_req_valid, ..zero!() } + }, + + _ => zero!(), + }; + + new_state + } +} + +const TEST_AXI_DATA_W = u32:64; +const TEST_AXI_ADDR_W = u32:32; +const TEST_AXI_ID_W = u32:8; +const TEST_AXI_DEST_W = u32:8; +const TEST_REGS_N = u32:5; +const TEST_WINDOW_LOG_MAX = u32:30; + +const TEST_HB_ADDR_W = sequence_executor::ZSTD_RAM_ADDR_WIDTH; +const TEST_HB_DATA_W = sequence_executor::RAM_DATA_WIDTH; +const TEST_HB_NUM_PARTITIONS = sequence_executor::RAM_NUM_PARTITIONS; +const TEST_HB_SIZE_KB = sequence_executor::ZSTD_HISTORY_BUFFER_SIZE_KB; + +const TEST_LOG2_REGS_N = std::clog2(TEST_REGS_N); +const TEST_AXI_DATA_W_DIV8 = TEST_AXI_DATA_W / u32:8; +const TEST_HB_RAM_N = u32:8; + +const TEST_HB_SIZE = sequence_executor::ram_size(TEST_HB_SIZE_KB); +const TEST_HB_RAM_WORD_PARTITION_SIZE = sequence_executor::RAM_WORD_PARTITION_SIZE; +const TEST_HB_RAM_SIMULTANEOUS_READ_WRITE_BEHAVIOR = sequence_executor::TEST_RAM_SIMULTANEOUS_READ_WRITE_BEHAVIOR; +const TEST_HB_RAM_INITIALIZED = sequence_executor::TEST_RAM_INITIALIZED; +const TEST_HB_RAM_ASSERT_VALID_READ:bool = {false}; + + +#[test_proc] +proc ZstdDecoderInternalTest { + + type BlockType = common::BlockType; + type BlockSize = common::BlockSize; + type BlockHeader = block_header::BlockHeader; + type BlockHeaderDecoderStatus = block_header_dec::BlockHeaderDecoderStatus; + + type FrameHeaderDecoderReq = frame_header_dec::FrameHeaderDecoderReq; + type FrameHeaderDecoderResp = frame_header_dec::FrameHeaderDecoderResp; + type FrameHeaderDecoderStatus = frame_header_dec::FrameHeaderDecoderStatus; + type FrameContentSize = frame_header_dec::FrameContentSize; + type FrameHeader = frame_header_dec::FrameHeader; + type WindowSize = frame_header_dec::WindowSize; + type DictionaryId = frame_header_dec::DictionaryId; + + type CsrRdReq = csr_config::CsrRdReq; + type CsrRdResp = csr_config::CsrRdResp; + type CsrWrReq = csr_config::CsrWrReq; + type CsrWrResp = csr_config::CsrWrResp; + type CsrChange = csr_config::CsrChange; + + type FrameHeaderDecoderReq = frame_header_dec::FrameHeaderDecoderReq; + type FrameHeaderDecoderResp = frame_header_dec::FrameHeaderDecoderResp; + + type BlockHeaderDecoderReq = block_header_dec::BlockHeaderDecoderReq; + type BlockHeaderDecoderResp = block_header_dec::BlockHeaderDecoderResp; + + type RawBlockDecoderReq = raw_block_dec_new_mem::RawBlockDecoderReq; + type RawBlockDecoderResp = raw_block_dec_new_mem::RawBlockDecoderResp; + type RawBlockDecoderStatus = raw_block_dec_new_mem::RawBlockDecoderStatus; + + type RleBlockDecoderReq = rle_block_dec_new_mem2::RleBlockDecoderReq; + type RleBlockDecoderResp = rle_block_dec_new_mem2::RleBlockDecoderResp; + type RleBlockDecoderStatus = rle_block_dec_new_mem2::RleBlockDecoderStatus; + + terminator: chan out; + + csr_rd_req_r: chan in; + csr_rd_resp_s: chan out; + csr_wr_req_r: chan in; + csr_wr_resp_s: chan out; + csr_change_s: chan out; + + fh_req_r: chan in; + fh_resp_s: chan out; + + bh_req_r: chan in; + bh_resp_s: chan out; + + raw_req_r: chan in; + raw_resp_s: chan out; + + rle_req_r: chan in; + rle_resp_s: chan out; + + notify_r: chan<()> in; + + init {} + + config(terminator: chan out) { + let (csr_rd_req_s, csr_rd_req_r) = chan("csr_rd_req"); + let (csr_rd_resp_s, csr_rd_resp_r) = chan("csr_rd_resp"); + let (csr_wr_req_s, csr_wr_req_r) = chan("csr_wr_req"); + let (csr_wr_resp_s, csr_wr_resp_r) = chan("csr_wr_resp"); + let (csr_change_s, csr_change_r) = chan("csr_change"); + + let (fh_req_s, fh_req_r) = chan("fh_req"); + let (fh_resp_s, fh_resp_r) = chan("fh_resp"); + + let (bh_req_s, bh_req_r) = chan("bh_req"); + let (bh_resp_s, bh_resp_r) = chan("bh_resp"); + + let (raw_req_s, raw_req_r) = chan("raw_req"); + let (raw_resp_s, raw_resp_r) = chan("raw_resp"); + + let (rle_req_s, rle_req_r) = chan("rle_req"); + let (rle_resp_s, rle_resp_r) = chan("rle_resp"); + + let (notify_s, notify_r) = chan<()>("notify"); + + spawn ZstdDecoderInternal( + csr_rd_req_s, csr_rd_resp_r, csr_wr_req_s, csr_wr_resp_r, csr_change_r, + fh_req_s, fh_resp_r, + bh_req_s, bh_resp_r, + raw_req_s, raw_resp_r, + rle_req_s, rle_resp_r, + notify_s, + ); + + ( + terminator, + csr_rd_req_r, csr_rd_resp_s, csr_wr_req_r, csr_wr_resp_s, csr_change_s, + fh_req_r, fh_resp_s, + bh_req_r, bh_resp_s, + raw_req_r, raw_resp_s, + rle_req_r, rle_resp_s, + notify_r, + ) + } + + next (state: ()) { + type Addr = uN[TEST_AXI_ADDR_W]; + type Length = uN[TEST_AXI_ADDR_W]; + + let tok = join(); + + // Error in frame header + + let tok = send(tok, csr_change_s, CsrChange { csr: csr(Csr::START)}); + let (tok, csr_data) = recv(tok, csr_rd_req_r); + assert_eq(csr_data, CsrRdReq {csr: csr(Csr::OUTPUT_BUFFER)}); + let (tok, csr_data) = recv(tok, csr_rd_req_r); + assert_eq(csr_data, CsrRdReq {csr: csr(Csr::INPUT_BUFFER)}); + + send(tok, csr_rd_resp_s, CsrRdResp { + csr: csr(Csr::INPUT_BUFFER), + value: uN[TEST_AXI_DATA_W]:0x1000 + }); + send(tok, csr_rd_resp_s, CsrRdResp { + csr: csr(Csr::OUTPUT_BUFFER), + value: uN[TEST_AXI_DATA_W]:0x2000 + }); + let (tok, fh_req) = recv(tok, fh_req_r); + assert_eq(fh_req, FrameHeaderDecoderReq { addr: Addr:0x1000 }); + + let tok = send(tok, fh_resp_s, FrameHeaderDecoderResp { + status: FrameHeaderDecoderStatus::CORRUPTED, + header: FrameHeader { + window_size: WindowSize:100, + frame_content_size: FrameContentSize:200, + dictionary_id: DictionaryId:123, + content_checksum_flag: u1:1, + }, + length: u5:3, + }); + + + let (tok, ()) = recv(tok, notify_r); + + // Correct case + let tok = send(tok, csr_change_s, CsrChange { csr: csr(Csr::START)}); + let (tok, csr_data) = recv(tok, csr_rd_req_r); + assert_eq(csr_data, CsrRdReq {csr: csr(Csr::OUTPUT_BUFFER)}); + let (tok, csr_data) = recv(tok, csr_rd_req_r); + assert_eq(csr_data, CsrRdReq {csr: csr(Csr::INPUT_BUFFER)}); + + send(tok, csr_rd_resp_s, CsrRdResp { + csr: csr(Csr::INPUT_BUFFER), + value: uN[TEST_AXI_DATA_W]:0x1000 + }); + send(tok, csr_rd_resp_s, CsrRdResp { + csr: csr(Csr::OUTPUT_BUFFER), + value: uN[TEST_AXI_DATA_W]:0x2000 + }); + let (tok, fh_req) = recv(tok, fh_req_r); + assert_eq(fh_req, FrameHeaderDecoderReq { addr: Addr:0x1000 }); + + let tok = send(tok, fh_resp_s, FrameHeaderDecoderResp { + status: FrameHeaderDecoderStatus::OKAY, + header: FrameHeader { + window_size: WindowSize:100, + frame_content_size: FrameContentSize:200, + dictionary_id: DictionaryId:123, + content_checksum_flag: u1:1, + }, + length: u5:3, + }); + + let (tok, bh_req) = recv(tok, bh_req_r); + assert_eq(bh_req, BlockHeaderDecoderReq { + addr: Addr:0x1003, + }); + + let tok = send(tok, bh_resp_s, BlockHeaderDecoderResp { + status: BlockHeaderDecoderStatus::OKAY, + header: BlockHeader { + last: false, + btype: BlockType::RAW, + size: BlockSize:0x1000, + }, + rle_symbol: u8:0, + }); + + let (tok, raw_req) = recv(tok, raw_req_r); + assert_eq(raw_req, RawBlockDecoderReq { + last_block: false, + id: u32:0, + addr: Addr:0x1006, + length: Length:0x1000 + }); + + let tok = send(tok, raw_resp_s, RawBlockDecoderResp { + status: RawBlockDecoderStatus::OKAY, + }); + + let (tok, bh_req) = recv(tok, bh_req_r); + assert_eq(bh_req, BlockHeaderDecoderReq { + addr: Addr:0x2006 + }); + let tok = send(tok, bh_resp_s, BlockHeaderDecoderResp { + status: BlockHeaderDecoderStatus::OKAY, + header: BlockHeader { + last: false, + btype: BlockType::RLE, + size: BlockSize:0x1000, + }, + rle_symbol: u8:123, + }); + + let (tok, rle_req) = recv(tok, rle_req_r); + assert_eq(rle_req, RleBlockDecoderReq { + id: u32:1, + symbol: u8:123, + last_block: false, + length: checked_cast(Length:0x1000), + }); + let tok = send(tok, rle_resp_s, RleBlockDecoderResp { + status: RleBlockDecoderStatus::OKAY, + }); + + let (tok, bh_req) = recv(tok, bh_req_r); + assert_eq(bh_req, BlockHeaderDecoderReq { + addr: Addr:0x200A, + }); + + let tok = send(tok, bh_resp_s, BlockHeaderDecoderResp { + status: BlockHeaderDecoderStatus::OKAY, + header: BlockHeader { + last: true, + btype: BlockType::RAW, + size: BlockSize:0x1000, + }, + rle_symbol: u8:0, + }); + + let (tok, raw_req) = recv(tok, raw_req_r); + assert_eq(raw_req, RawBlockDecoderReq { + last_block: true, + id: u32:2, + addr: Addr:0x200D, + length: Length:0x1000 + }); + + let tok = send(tok, raw_resp_s, RawBlockDecoderResp { + status: RawBlockDecoderStatus::OKAY, + }); + + let (tok, ()) = recv(tok, notify_r); + + send(tok, terminator, true); + } +} + + +proc ZstdDecoder< + // AXI parameters + AXI_DATA_W: u32, AXI_ADDR_W: u32, AXI_ID_W: u32, AXI_DEST_W: u32, + // decoder parameters + REGS_N: u32, WINDOW_LOG_MAX: u32, + HB_ADDR_W: u32, HB_DATA_W: u32, HB_NUM_PARTITIONS: u32, HB_SIZE_KB: u32, + // calculated parameters + AXI_DATA_W_DIV8: u32 = {AXI_DATA_W / u32:8}, + LOG2_REGS_N: u32 = {std::clog2(REGS_N)}, + HB_RAM_N: u32 = {u32:8}, +> { + type CsrAxiAr = axi::AxiAr; + type CsrAxiR = axi::AxiR; + type CsrAxiAw = axi::AxiAw; + type CsrAxiW = axi::AxiW; + type CsrAxiB = axi::AxiB; + + type CsrRdReq = csr_config::CsrRdReq; + type CsrRdResp = csr_config::CsrRdResp; + type CsrWrReq = csr_config::CsrWrReq; + type CsrWrResp = csr_config::CsrWrResp; + type CsrChange = csr_config::CsrChange; + + type MemAxiAr = axi::AxiAr; + type MemAxiR = axi::AxiR; + type MemAxiAw = axi::AxiAw; + type MemAxiW = axi::AxiW; + type MemAxiB = axi::AxiB; + + type MemReaderReq = mem_reader::MemReaderReq; + type MemReaderResp = mem_reader::MemReaderResp; + + type FrameHeaderDecoderReq = frame_header_dec::FrameHeaderDecoderReq; + type FrameHeaderDecoderResp = frame_header_dec::FrameHeaderDecoderResp; + + type BlockHeaderDecoderReq = block_header_dec::BlockHeaderDecoderReq; + type BlockHeaderDecoderResp = block_header_dec::BlockHeaderDecoderResp; + + type RawBlockDecoderReq = raw_block_dec_new_mem::RawBlockDecoderReq; + type RawBlockDecoderResp = raw_block_dec_new_mem::RawBlockDecoderResp; + type ExtendedBlockDataPacket = common::ExtendedBlockDataPacket; + + type RleBlockDecoderReq = rle_block_dec_new_mem2::RleBlockDecoderReq; + type RleBlockDecoderResp = rle_block_dec_new_mem2::RleBlockDecoderResp; + + type SequenceExecutorPacket = common::SequenceExecutorPacket; + type ZstdDecodedPacket = common::ZstdDecodedPacket; + + type RamRdReq = ram::ReadReq; + type RamRdResp = ram::ReadResp; + type RamWrReq = ram::WriteReq; + type RamWrResp = ram::WriteResp; + + // Complex Block Decoder + cmp_output_s: chan out; + + init {} + + config( + // AXI Ctrl (subordinate) + csr_axi_aw_r: chan in, + csr_axi_w_r: chan in, + csr_axi_b_s: chan out, + csr_axi_ar_r: chan in, + csr_axi_r_s: chan out, + + // AXI Frame Header Decoder (manager) + fh_axi_ar_s: chan out, + fh_axi_r_r: chan in, + + //// AXI Block Header Decoder (manager) + bh_axi_ar_s: chan out, + bh_axi_r_r: chan in, + + //// AXI RAW Block Decoder (manager) + raw_axi_ar_s: chan out, + raw_axi_r_r: chan in, + + // History Buffer + ram_rd_req_0_s: chan out, + ram_rd_req_1_s: chan out, + ram_rd_req_2_s: chan out, + ram_rd_req_3_s: chan out, + ram_rd_req_4_s: chan out, + ram_rd_req_5_s: chan out, + ram_rd_req_6_s: chan out, + ram_rd_req_7_s: chan out, + ram_rd_resp_0_r: chan in, + ram_rd_resp_1_r: chan in, + ram_rd_resp_2_r: chan in, + ram_rd_resp_3_r: chan in, + ram_rd_resp_4_r: chan in, + ram_rd_resp_5_r: chan in, + ram_rd_resp_6_r: chan in, + ram_rd_resp_7_r: chan in, + ram_wr_req_0_s: chan out, + ram_wr_req_1_s: chan out, + ram_wr_req_2_s: chan out, + ram_wr_req_3_s: chan out, + ram_wr_req_4_s: chan out, + ram_wr_req_5_s: chan out, + ram_wr_req_6_s: chan out, + ram_wr_req_7_s: chan out, + ram_wr_resp_0_r: chan in, + ram_wr_resp_1_r: chan in, + ram_wr_resp_2_r: chan in, + ram_wr_resp_3_r: chan in, + ram_wr_resp_4_r: chan in, + ram_wr_resp_5_r: chan in, + ram_wr_resp_6_r: chan in, + ram_wr_resp_7_r: chan in, + + // Decoder output + output_s: chan out, + notify_s: chan<()> out, + ) { + const CHANNEL_DEPTH = u32:1; + + // CSRs + + let (ext_csr_rd_req_s, ext_csr_rd_req_r) = chan("csr_rd_req"); + let (ext_csr_rd_resp_s, ext_csr_rd_resp_r) = chan("csr_rd_resp"); + let (ext_csr_wr_req_s, ext_csr_wr_req_r) = chan("csr_wr_req"); + let (ext_csr_wr_resp_s, ext_csr_wr_resp_r) = chan("csr_wr_resp"); + + let (csr_rd_req_s, csr_rd_req_r) = chan("csr_rd_req"); + let (csr_rd_resp_s, csr_rd_resp_r) = chan("csr_rd_resp"); + let (csr_wr_req_s, csr_wr_req_r) = chan("csr_wr_req"); + let (csr_wr_resp_s, csr_wr_resp_r) = chan("csr_wr_resp"); + + let (csr_change_s, csr_change_r) = chan("csr_change"); + + spawn axi_csr_accessor::AxiCsrAccessor( + csr_axi_aw_r, csr_axi_w_r, csr_axi_b_s, // csr write from AXI + csr_axi_ar_r, csr_axi_r_s, // csr read from AXI + ext_csr_rd_req_s, ext_csr_rd_resp_r, // csr read to CsrConfig + ext_csr_wr_req_s, ext_csr_wr_resp_r, // csr write to CsrConfig + ); + + spawn csr_config::CsrConfig( + ext_csr_rd_req_r, ext_csr_rd_resp_s, // csr read from AxiCsrAccessor + ext_csr_wr_req_r, ext_csr_wr_resp_s, // csr write from AxiCsrAccessor + csr_rd_req_r, csr_rd_resp_s, // csr read from design + csr_wr_req_r, csr_wr_resp_s, // csr write from design + csr_change_s, // notification about csr change + ); + + // Frame Header + + let (fh_mem_rd_req_s, fh_mem_rd_req_r) = chan("fh_mem_rd_req"); + let (fh_mem_rd_resp_s, fh_mem_rd_resp_r) = chan("fh_mem_rd_resp"); + + spawn mem_reader::MemReader( + fh_mem_rd_req_r, fh_mem_rd_resp_s, + fh_axi_ar_s, fh_axi_r_r, + ); + + let (fh_req_s, fh_req_r) = chan("fh_req"); + let (fh_resp_s, fh_resp_r) = chan("fh_resp"); + + spawn frame_header_dec::FrameHeaderDecoder( + fh_mem_rd_req_s, fh_mem_rd_resp_r, + fh_req_r, fh_resp_s, + ); + + // Block Header + + let (bh_mem_rd_req_s, bh_mem_rd_req_r) = chan("bh_mem_rd_req"); + let (bh_mem_rd_resp_s, bh_mem_rd_resp_r) = chan("bh_mem_rd_resp"); + + spawn mem_reader::MemReader( + bh_mem_rd_req_r, bh_mem_rd_resp_s, + bh_axi_ar_s, bh_axi_r_r, + ); + + let (bh_req_s, bh_req_r) = chan("bh_req"); + let (bh_resp_s, bh_resp_r) = chan("bh_resp"); + + spawn block_header_dec::BlockHeaderDecoder( + bh_req_r, bh_resp_s, + bh_mem_rd_req_s, bh_mem_rd_resp_r, + ); + + // Raw Block Decoder + + let (raw_mem_rd_req_s, raw_mem_rd_req_r) = chan("raw_mem_rd_req"); + let (raw_mem_rd_resp_s, raw_mem_rd_resp_r) = chan("raw_mem_rd_resp"); + + spawn mem_reader::MemReader( + raw_mem_rd_req_r, raw_mem_rd_resp_s, + raw_axi_ar_s, raw_axi_r_r, + ); + + let (raw_req_s, raw_req_r) = chan("raw_req"); + let (raw_resp_s, raw_resp_r) = chan("raw_resp"); + let (raw_output_s, raw_output_r) = chan("raw_output"); + + spawn raw_block_dec_new_mem::RawBlockDecoder( + raw_req_r, raw_resp_s, raw_output_s, + raw_mem_rd_req_s, raw_mem_rd_resp_r, + ); + + // RLE Block Decoder + + let (rle_req_s, rle_req_r) = chan("rle_req"); + let (rle_resp_s, rle_resp_r) = chan("rle_resp"); + let (rle_output_s, rle_output_r) = chan("rle_output"); + + spawn rle_block_dec_new_mem2::RleBlockDecoder( + rle_req_r, rle_resp_s, rle_output_s + ); + + // Collecting Packets + + let (cmp_output_s, cmp_output_r) = chan("cmp_output"); + let (seq_exec_input_s, seq_exec_input_r) = chan("demux_output"); + + spawn dec_mux::DecoderMux( + raw_output_r, rle_output_r, cmp_output_r, + seq_exec_input_s, + ); + + // Sequence Execution + + let (seq_exec_looped_s, seq_exec_looped_r) = chan("seq_exec_looped"); + let (seq_exec_output_s, seq_exec_output_r) = chan("seq_exec_output"); + + spawn sequence_executor::SequenceExecutor( + seq_exec_input_r, seq_exec_output_s, + seq_exec_looped_r, seq_exec_looped_s, + ram_rd_req_0_s, ram_rd_req_1_s, ram_rd_req_2_s, ram_rd_req_3_s, + ram_rd_req_4_s, ram_rd_req_5_s, ram_rd_req_6_s, ram_rd_req_7_s, + ram_rd_resp_0_r, ram_rd_resp_1_r, ram_rd_resp_2_r, ram_rd_resp_3_r, + ram_rd_resp_4_r, ram_rd_resp_5_r, ram_rd_resp_6_r, ram_rd_resp_7_r, + ram_wr_req_0_s, ram_wr_req_1_s, ram_wr_req_2_s, ram_wr_req_3_s, + ram_wr_req_4_s, ram_wr_req_5_s, ram_wr_req_6_s, ram_wr_req_7_s, + ram_wr_resp_0_r, ram_wr_resp_1_r, ram_wr_resp_2_r, ram_wr_resp_3_r, + ram_wr_resp_4_r, ram_wr_resp_5_r, ram_wr_resp_6_r, ram_wr_resp_7_r + ); + + // Repacketizer + + spawn repacketizer::Repacketizer(seq_exec_output_r, output_s); + + // Zstd Decoder Control + + spawn ZstdDecoderInternal ( + csr_rd_req_s, csr_rd_resp_r, csr_wr_req_s, csr_wr_resp_r, csr_change_r, + fh_req_s, fh_resp_r, + bh_req_s, bh_resp_r, + raw_req_s, raw_resp_r, + rle_req_s, rle_resp_r, + notify_s, + ); + + (cmp_output_s,) + } + + next (state: ()) { + send_if(join(), cmp_output_s, false, zero!()); + } +} + +//#[test_proc] +//proc ZstdDecoderTest { +// type CsrAxiAr = axi::AxiAr; +// type CsrAxiR = axi::AxiR; +// type CsrAxiAw = axi::AxiAw; +// type CsrAxiW = axi::AxiW; +// type CsrAxiB = axi::AxiB; +// +// type CsrRdReq = csr_config::CsrRdReq; +// type CsrRdResp = csr_config::CsrRdResp; +// type CsrWrReq = csr_config::CsrWrReq; +// type CsrWrResp = csr_config::CsrWrResp; +// type CsrChange = csr_config::CsrChange; +// +// type MemAxiAr = axi::AxiAr; +// type MemAxiR = axi::AxiR; +// type MemAxiAw = axi::AxiAw; +// type MemAxiW = axi::AxiW; +// type MemAxiB = axi::AxiB; +// +// type RamRdReq = ram::ReadReq; +// type RamRdResp = ram::ReadResp; +// type RamWrReq = ram::WriteReq; +// type RamWrResp = ram::WriteResp; +// +// type ZstdDecodedPacket = common::ZstdDecodedPacket; +// terminator: chan out; +// +// init {} +// +// config(terminator: chan out) { +// +// let (csr_axi_aw_s, csr_axi_aw_r) = chan("csr_axi_aw"); +// let (csr_axi_w_s, csr_axi_w_r) = chan("csr_axi_w"); +// let (csr_axi_b_s, csr_axi_b_r) = chan("csr_axi_b"); +// let (csr_axi_ar_s, csr_axi_ar_r) = chan("csr_axi_ar"); +// let (csr_axi_r_s, csr_axi_r_r) = chan("csr_axi_r"); +// +// let (fh_axi_ar_s, fh_axi_ar_r) = chan("fh_axi_ar_s"); +// let (fh_axi_r_s, fh_axi_r_r) = chan("fh_axi_r_r"); +// +// let (bh_axi_ar_s, bh_axi_ar_r) = chan("bh_axi_ar"); +// let (bh_axi_r_s, bh_axi_r_r) = chan("bh_axi_r"); +// +// let (raw_axi_ar_s, raw_axi_ar_r) = chan("raw_axi_ar"); +// let (raw_axi_r_s, raw_axi_r_r) = chan("raw_axi_r"); +// +// let (rle_axi_ar_s, rle_axi_ar_r) = chan("rle_axi_ar"); +// let (rle_axi_r_s, rle_axi_r_r) = chan("rle_axi_r"); +// +// let (ram_rd_req_0_s, ram_rd_req_0_r) = chan("ram_rd_req_0"); +// let (ram_rd_req_1_s, ram_rd_req_1_r) = chan("ram_rd_req_1"); +// let (ram_rd_req_2_s, ram_rd_req_2_r) = chan("ram_rd_req_2"); +// let (ram_rd_req_3_s, ram_rd_req_3_r) = chan("ram_rd_req_3"); +// let (ram_rd_req_4_s, ram_rd_req_4_r) = chan("ram_rd_req_4"); +// let (ram_rd_req_5_s, ram_rd_req_5_r) = chan("ram_rd_req_5"); +// let (ram_rd_req_6_s, ram_rd_req_6_r) = chan("ram_rd_req_6"); +// let (ram_rd_req_7_s, ram_rd_req_7_r) = chan("ram_rd_req_7"); +// +// let (ram_rd_resp_0_s, ram_rd_resp_0_r) = chan("ram_rd_resp_0"); +// let (ram_rd_resp_1_s, ram_rd_resp_1_r) = chan("ram_rd_resp_1"); +// let (ram_rd_resp_2_s, ram_rd_resp_2_r) = chan("ram_rd_resp_2"); +// let (ram_rd_resp_3_s, ram_rd_resp_3_r) = chan("ram_rd_resp_3"); +// let (ram_rd_resp_4_s, ram_rd_resp_4_r) = chan("ram_rd_resp_4"); +// let (ram_rd_resp_5_s, ram_rd_resp_5_r) = chan("ram_rd_resp_5"); +// let (ram_rd_resp_6_s, ram_rd_resp_6_r) = chan("ram_rd_resp_6"); +// let (ram_rd_resp_7_s, ram_rd_resp_7_r) = chan("ram_rd_resp_7"); +// +// let (ram_wr_req_0_s, ram_wr_req_0_r) = chan("ram_wr_req_0"); +// let (ram_wr_req_1_s, ram_wr_req_1_r) = chan("ram_wr_req_1"); +// let (ram_wr_req_2_s, ram_wr_req_2_r) = chan("ram_wr_req_2"); +// let (ram_wr_req_3_s, ram_wr_req_3_r) = chan("ram_wr_req_3"); +// let (ram_wr_req_4_s, ram_wr_req_4_r) = chan("ram_wr_req_4"); +// let (ram_wr_req_5_s, ram_wr_req_5_r) = chan("ram_wr_req_5"); +// let (ram_wr_req_6_s, ram_wr_req_6_r) = chan("ram_wr_req_6"); +// let (ram_wr_req_7_s, ram_wr_req_7_r) = chan("ram_wr_req_7"); +// +// let (ram_wr_resp_0_s, ram_wr_resp_0_r) = chan("ram_wr_resp_0"); +// let (ram_wr_resp_1_s, ram_wr_resp_1_r) = chan("ram_wr_resp_1"); +// let (ram_wr_resp_2_s, ram_wr_resp_2_r) = chan("ram_wr_resp_2"); +// let (ram_wr_resp_3_s, ram_wr_resp_3_r) = chan("ram_wr_resp_3"); +// let (ram_wr_resp_4_s, ram_wr_resp_4_r) = chan("ram_wr_resp_4"); +// let (ram_wr_resp_5_s, ram_wr_resp_5_r) = chan("ram_wr_resp_5"); +// let (ram_wr_resp_6_s, ram_wr_resp_6_r) = chan("ram_wr_resp_6"); +// let (ram_wr_resp_7_s, ram_wr_resp_7_r) = chan("ram_wr_resp_7"); +// +// let (output_s, output_r) = chan("output"); +// let (notify_s, notify_r) = chan<()>("notify"); +// +// spawn ZstdDecoder< +// TEST_AXI_DATA_W, TEST_AXI_ADDR_W, TEST_AXI_ID_W, TEST_AXI_DEST_W, +// TEST_REGS_N, TEST_WINDOW_LOG_MAX, +// TEST_HB_ADDR_W, TEST_HB_DATA_W, TEST_HB_NUM_PARTITIONS, TEST_HB_SIZE_KB, +// >( +// csr_axi_aw_r, csr_axi_w_r, csr_axi_b_s, csr_axi_ar_r, csr_axi_r_s, +// fh_axi_ar_s, fh_axi_r_r, +// bh_axi_ar_s, bh_axi_r_r, +// raw_axi_ar_s, raw_axi_r_r, +// rle_axi_ar_s, rle_axi_r_r, +// ram_rd_req_0_s, ram_rd_req_1_s, ram_rd_req_2_s, ram_rd_req_3_s, +// ram_rd_req_4_s, ram_rd_req_5_s, ram_rd_req_6_s, ram_rd_req_7_s, +// ram_rd_resp_0_r, ram_rd_resp_1_r, ram_rd_resp_2_r, ram_rd_resp_3_r, +// ram_rd_resp_4_r, ram_rd_resp_5_r, ram_rd_resp_6_r, ram_rd_resp_7_r, +// ram_wr_req_0_s, ram_wr_req_1_s, ram_wr_req_2_s, ram_wr_req_3_s, +// ram_wr_req_4_s, ram_wr_req_5_s, ram_wr_req_6_s, ram_wr_req_7_s, +// ram_wr_resp_0_r, ram_wr_resp_1_r, ram_wr_resp_2_r, ram_wr_resp_3_r, +// ram_wr_resp_4_r, ram_wr_resp_5_r, ram_wr_resp_6_r, ram_wr_resp_7_r, +// output_s, notify_s, +// ); +// +// spawn ram::RamModel< +// TEST_HB_DATA_W, TEST_HB_RAM_SIZE, TEST_HB_RAM_WORD_PARTITION_SIZE, +// TEST_HB_RAM_SIMULTANEOUS_READ_WRITE_BEHAVIOR, TEST_HB_RAM_INITIALIZED, +// TEST_HB_RAM_ASSERT_VALID_READ +// > (ram_rd_req_0_r, ram_rd_resp_0_s, ram_wr_req_0_r, ram_wr_resp_0_s); +// +// spawn ram::RamModel< +// TEST_HB_DATA_W, TEST_HB_RAM_SIZE, TEST_HB_RAM_WORD_PARTITION_SIZE, +// TEST_HB_RAM_SIMULTANEOUS_READ_WRITE_BEHAVIOR, TEST_HB_RAM_INITIALIZED, +// TEST_HB_RAM_ASSERT_VALID_READ +// > (ram_rd_req_1_r, ram_rd_resp_1_s, ram_wr_req_1_r, ram_wr_resp_1_s); +// +// spawn ram::RamModel< +// TEST_HB_DATA_W, TEST_HB_RAM_SIZE, TEST_HB_RAM_WORD_PARTITION_SIZE, +// TEST_HB_RAM_SIMULTANEOUS_READ_WRITE_BEHAVIOR, TEST_HB_RAM_INITIALIZED, +// TEST_RAM_ASSERT_VALID_READ +// > (ram_rd_req_2_r, ram_rd_resp_2_s, ram_wr_req_2_r, ram_wr_resp_2_s); +// +// spawn ram::RamModel< +// RAM_DATA_WIDTH, TEST_RAM_SIZE, RAM_WORD_PARTITION_SIZE, +// TEST_HB_RAM_SIMULTANEOUS_READ_WRITE_BEHAVIOR, TEST_HB_RAM_INITIALIZED, +// TEST_RAM_ASSERT_VALID_READ +// > (ram_rd_req_3_r, ram_rd_resp_3_s, ram_wr_req_3_r, ram_wr_resp_3_s); +// +// spawn ram::RamModel< +// TEST_HB_DATA_W, TEST_HB_RAM_SIZE, TEST_HB_RAM_WORD_PARTITION_SIZE, +// TEST_HB_RAM_SIMULTANEOUS_READ_WRITE_BEHAVIOR, TEST_HB_RAM_INITIALIZED, +// TEST_RAM_ASSERT_VALID_READ +// > (ram_rd_req_4_r, ram_rd_resp_4_s, ram_wr_req_4_r, ram_wr_resp_4_s); +// +// spawn ram::RamModel< +// TEST_HB_DATA_W, TEST_HB_RAM_SIZE, TEST_HB_RAM_WORD_PARTITION_SIZE, +// TEST_HB_RAM_SIMULTANEOUS_READ_WRITE_BEHAVIOR, TEST_HB_RAM_INITIALIZED, +// TEST_RAM_ASSERT_VALID_READ +// > (ram_rd_req_5_r, ram_rd_resp_5_s, ram_wr_req_5_r, ram_wr_resp_5_s); +// +// spawn ram::RamModel< +// TEST_HB_DATA_W, TEST_HB_RAM_SIZE, TEST_HB_RAM_WORD_PARTITION_SIZE, +// TEST_HB_RAM_SIMULTANEOUS_READ_WRITE_BEHAVIOR, TEST_HB_RAM_INITIALIZED, +// TEST_RAM_ASSERT_VALID_READ +// > (ram_rd_req_6_r, ram_rd_resp_6_s, ram_wr_req_6_r, ram_wr_resp_6_s); +// +// spawn ram::RamModel< +// TEST_HB_DATA_W, TEST_HB_RAM_SIZE, TEST_HB_RAM_WORD_PARTITION_SIZE, +// TEST_HB_RAM_SIMULTANEOUS_READ_WRITE_BEHAVIOR, TEST_HB_RAM_INITIALIZED, +// TEST_RAM_ASSERT_VALID_READ +// > (ram_rd_req_7_r, ram_rd_resp_7_s, ram_wr_req_7_r, ram_wr_resp_7_s); +// +// ( +// terminator, +// csr_axi_aw_s, csr_axi_w_s, csr_axi_b_r, csr_axi_ar_s, csr_axi_s_r, +// fh_axi_ar_r, fh_axi_s_s, +// bh_axi_ar_r, bh_axi_s_s, +// raw_axi_ar_r, raw_axi_s_s, +// rle_axi_ar_r, rle_axi_s_s, +// ram_sd_seq_0_r, ram_sd_seq_1_r, ram_sd_seq_2_r, ram_sd_seq_3_r, +// ram_sd_seq_4_r, ram_sd_seq_5_r, ram_sd_seq_6_r, ram_sd_seq_7_r, +// ram_sd_sesp_0_s, ram_sd_sesp_1_s, ram_sd_sesp_2_s, ram_sd_sesp_3_s, +// ram_sd_sesp_4_s, ram_sd_sesp_5_s, ram_sd_sesp_6_s, ram_sd_sesp_7_s, +// ram_wr_seq_0_r, ram_wr_seq_1_r, ram_wr_seq_2_r, ram_wr_seq_3_r, +// ram_wr_seq_4_r, ram_wr_seq_5_r, ram_wr_seq_6_r, ram_wr_seq_7_r, +// ram_wr_sesp_0_s, ram_wr_sesp_1_s, ram_wr_sesp_2_s, ram_wr_sesp_3_s, +// ram_wr_sesp_4_s, ram_wr_sesp_5_s, ram_wr_sesp_6_s, ram_wr_sesp_7_s, +// output_r, notify_r, +// ) +// } +// +// next (state: ()) { +// +// trace_fmt!("Test start"); +// +// send(join(), terminator, true); +// } +//} + + +const INST_AXI_DATA_W = u32:32; +const INST_AXI_ADDR_W = u32:16; +const INST_AXI_ID_W = u32:4; +const INST_AXI_DEST_W = u32:4; +const INST_REGS_N = u32:16; +const INST_WINDOW_LOG_MAX = u32:30; +const INST_HB_ADDR_W = sequence_executor::ZSTD_RAM_ADDR_WIDTH; +const INST_HB_DATA_W = sequence_executor::RAM_DATA_WIDTH; +const INST_HB_NUM_PARTITIONS = sequence_executor::RAM_NUM_PARTITIONS; +const INST_HB_SIZE_KB = sequence_executor::ZSTD_HISTORY_BUFFER_SIZE_KB; + +const INST_LOG2_REGS_N = std::clog2(INST_REGS_N); +const INST_AXI_DATA_W_DIV8 = INST_AXI_DATA_W / u32:8; +const INST_HB_RAM_N = u32:8; + +proc ZstdDecoderInternalInst { + type State = ZstdDecoderInternalState; + type Fsm = ZstdDecoderInternalFsm; + + type CsrRdReq = csr_config::CsrRdReq; + type CsrRdResp = csr_config::CsrRdResp; + type CsrWrReq = csr_config::CsrWrReq; + type CsrWrResp = csr_config::CsrWrResp; + type CsrChange = csr_config::CsrChange; + + type FrameHeaderDecoderReq = frame_header_dec::FrameHeaderDecoderReq; + type FrameHeaderDecoderResp = frame_header_dec::FrameHeaderDecoderResp; + + type BlockHeaderDecoderReq = block_header_dec::BlockHeaderDecoderReq; + type BlockHeaderDecoderResp = block_header_dec::BlockHeaderDecoderResp; + + type RawBlockDecoderReq = raw_block_dec_new_mem::RawBlockDecoderReq; + type RawBlockDecoderResp = raw_block_dec_new_mem::RawBlockDecoderResp; + + type RleBlockDecoderReq = rle_block_dec_new_mem2::RleBlockDecoderReq; + type RleBlockDecoderResp = rle_block_dec_new_mem2::RleBlockDecoderResp; + + init { } + + config( + csr_rd_req_s: chan out, + csr_rd_resp_r: chan in, + csr_wr_req_s: chan out, + csr_wr_resp_r: chan in, + csr_change_r: chan in, + + // MemReader + FameHeaderDecoder + fh_req_s: chan out, + fh_resp_r: chan in, + + // MemReader + BlockHeaderDecoder + bh_req_s: chan out, + bh_resp_r: chan in, + + // MemReader + RawBlockDecoder + raw_req_s: chan out, + raw_resp_r: chan in, + + // MemReader + RleBlockDecoder + rle_req_s: chan out, + rle_resp_r: chan in, + + // IRQ + notify_s: chan<()> out, + ) { + spawn ZstdDecoderInternal< + INST_AXI_DATA_W, INST_AXI_ADDR_W, INST_REGS_N, + > ( + csr_rd_req_s, csr_rd_resp_r, csr_wr_req_s, csr_wr_resp_r, csr_change_r, + fh_req_s, fh_resp_r, + bh_req_s, bh_resp_r, + raw_req_s, raw_resp_r, + rle_req_s, rle_resp_r, + notify_s, + ); + + } + + next(state: ()) {} +} + +proc ZstdDecoderInst { + type CsrAxiAr = axi::AxiAr; + type CsrAxiR = axi::AxiR; + type CsrAxiAw = axi::AxiAw; + type CsrAxiW = axi::AxiW; + type CsrAxiB = axi::AxiB; + + type MemAxiAr = axi::AxiAr; + type MemAxiR = axi::AxiR; + type MemAxiAw = axi::AxiAw; + type MemAxiW = axi::AxiW; + type MemAxiB = axi::AxiB; + + type RamRdReq = ram::ReadReq; + type RamRdResp = ram::ReadResp; + type RamWrReq = ram::WriteReq; + type RamWrResp = ram::WriteResp; + + type ZstdDecodedPacket = common::ZstdDecodedPacket; + + init { } + + config( + // AXI Ctrl (subordinate) + csr_axi_aw_r: chan in, + csr_axi_w_r: chan in, + csr_axi_b_s: chan out, + csr_axi_ar_r: chan in, + csr_axi_r_s: chan out, + + // AXI Frame Header Decoder (manager) + fh_axi_ar_s: chan out, + fh_axi_r_r: chan in, + + // AXI Block Header Decoder (manager) + bh_axi_ar_s: chan out, + bh_axi_r_r: chan in, + + // AXI RAW Block Decoder (manager) + raw_axi_ar_s: chan out, + raw_axi_r_r: chan in, + + // History Buffer + ram_rd_req_0_s: chan out, + ram_rd_req_1_s: chan out, + ram_rd_req_2_s: chan out, + ram_rd_req_3_s: chan out, + ram_rd_req_4_s: chan out, + ram_rd_req_5_s: chan out, + ram_rd_req_6_s: chan out, + ram_rd_req_7_s: chan out, + ram_rd_resp_0_r: chan in, + ram_rd_resp_1_r: chan in, + ram_rd_resp_2_r: chan in, + ram_rd_resp_3_r: chan in, + ram_rd_resp_4_r: chan in, + ram_rd_resp_5_r: chan in, + ram_rd_resp_6_r: chan in, + ram_rd_resp_7_r: chan in, + ram_wr_req_0_s: chan out, + ram_wr_req_1_s: chan out, + ram_wr_req_2_s: chan out, + ram_wr_req_3_s: chan out, + ram_wr_req_4_s: chan out, + ram_wr_req_5_s: chan out, + ram_wr_req_6_s: chan out, + ram_wr_req_7_s: chan out, + ram_wr_resp_0_r: chan in, + ram_wr_resp_1_r: chan in, + ram_wr_resp_2_r: chan in, + ram_wr_resp_3_r: chan in, + ram_wr_resp_4_r: chan in, + ram_wr_resp_5_r: chan in, + ram_wr_resp_6_r: chan in, + ram_wr_resp_7_r: chan in, + + // Decoder output + output_s: chan out, + notify_s: chan<()> out, + ) { + spawn ZstdDecoder< + INST_AXI_DATA_W, INST_AXI_ADDR_W, INST_AXI_ID_W, INST_AXI_DEST_W, + INST_REGS_N, INST_WINDOW_LOG_MAX, + INST_HB_ADDR_W, INST_HB_DATA_W, INST_HB_NUM_PARTITIONS, INST_HB_SIZE_KB, + >( + csr_axi_aw_r, csr_axi_w_r, csr_axi_b_s, csr_axi_ar_r, csr_axi_r_s, + fh_axi_ar_s, fh_axi_r_r, + bh_axi_ar_s, bh_axi_r_r, + raw_axi_ar_s, raw_axi_r_r, + ram_rd_req_0_s, ram_rd_req_1_s, ram_rd_req_2_s, ram_rd_req_3_s, + ram_rd_req_4_s, ram_rd_req_5_s, ram_rd_req_6_s, ram_rd_req_7_s, + ram_rd_resp_0_r, ram_rd_resp_1_r, ram_rd_resp_2_r, ram_rd_resp_3_r, + ram_rd_resp_4_r, ram_rd_resp_5_r, ram_rd_resp_6_r, ram_rd_resp_7_r, + ram_wr_req_0_s, ram_wr_req_1_s, ram_wr_req_2_s, ram_wr_req_3_s, + ram_wr_req_4_s, ram_wr_req_5_s, ram_wr_req_6_s, ram_wr_req_7_s, + ram_wr_resp_0_r, ram_wr_resp_1_r, ram_wr_resp_2_r, ram_wr_resp_3_r, + ram_wr_resp_4_r, ram_wr_resp_5_r, ram_wr_resp_6_r, ram_wr_resp_7_r, + output_s, notify_s, + ); + } + + next (state: ()) {} +} diff --git a/xls/modules/zstd/frame_header_dec.x b/xls/modules/zstd/frame_header_dec.x new file mode 100644 index 0000000000..8647435996 --- /dev/null +++ b/xls/modules/zstd/frame_header_dec.x @@ -0,0 +1,670 @@ +// Copyright 2024 The XLS Authors +// +// 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. + +// This file contains utilities related to ZSTD Frame Header parsing. +// More information about the ZSTD Frame Header can be found in: +// https://datatracker.ietf.org/doc/html/rfc8878#section-3.1.1.1 + +import std; +import xls.modules.zstd.memory.mem_reader; + +pub type WindowSize = u64; +pub type FrameContentSize = u64; +pub type DictionaryId = u32; + +// Structure for data obtained from decoding the Frame_Header_Descriptor +pub struct FrameHeader { + window_size: WindowSize, + frame_content_size: FrameContentSize, + dictionary_id: DictionaryId, + content_checksum_flag: u1, +} + +// Status values reported by the frame header parsing function +pub enum FrameHeaderDecoderStatus: u2 { + OKAY = 0, + CORRUPTED = 1, + UNSUPPORTED_WINDOW_SIZE = 2, +} + +pub struct FrameHeaderDecoderReq { + addr: uN[ADDR_W], +} + +pub struct FrameHeaderDecoderResp { + status: FrameHeaderDecoderStatus, + header: FrameHeader, + length: u5, +} + +// Maximal mantissa value for calculating maximal accepted window_size +// as per https://datatracker.ietf.org/doc/html/rfc8878#name-window-descriptor +const MAX_MANTISSA = WindowSize:0b111; + +// Structure for holding ZSTD Frame_Header_Descriptor data, as in: +// https://datatracker.ietf.org/doc/html/rfc8878#section-3.1.1.1.1 +pub struct FrameHeaderDescriptor { + frame_content_size_flag: u2, + single_segment_flag: u1, + unused: u1, + reserved: u1, + content_checksum_flag: u1, + dictionary_id_flag: u2, +} + +// Auxiliary constant that can be used to initialize Proc's state +// with empty FrameHeader, because `zero!` cannot be used in that context +pub const ZERO_FRAME_HEADER = zero!(); +pub const FRAME_CONTENT_SIZE_NOT_PROVIDED_VALUE = FrameContentSize::MAX; + +// Extracts Frame_Header_Descriptor fields from 8-bit chunk of data +// that is assumed to be a valid Frame_Header_Descriptor +fn extract_frame_header_descriptor(data:u8) -> FrameHeaderDescriptor { + FrameHeaderDescriptor { + frame_content_size_flag: data[6:8], + single_segment_flag: data[5:6], + unused: data[4:5], + reserved: data[3:4], + content_checksum_flag: data[2:3], + dictionary_id_flag: data[0:2], + } +} + +#[test] +fn test_extract_frame_header_descriptor() { + assert_eq( + extract_frame_header_descriptor(u8:0xA4), + FrameHeaderDescriptor { + frame_content_size_flag: u2:0x2, + single_segment_flag: u1:0x1, + unused: u1:0x0, + reserved: u1:0x0, + content_checksum_flag: u1:0x1, + dictionary_id_flag: u2:0x0 + } + ); + + assert_eq( + extract_frame_header_descriptor(u8:0x0), + FrameHeaderDescriptor { + frame_content_size_flag: u2:0x0, + single_segment_flag: u1:0x0, + unused: u1:0x0, + reserved: u1:0x0, + content_checksum_flag: u1:0x0, + dictionary_id_flag: u2:0x0 + } + ); +} + +// Returns a boolean showing if the Window_Descriptor section exists +// for the frame with the given FrameHeaderDescriptor +fn window_descriptor_exists(desc: FrameHeaderDescriptor) -> bool { + desc.single_segment_flag == u1:0 +} + +#[test] +fn test_window_descriptor_exists() { + let zero_desc = zero!(); + + let desc_with_ss = FrameHeaderDescriptor {single_segment_flag: u1:1, ..zero_desc}; + assert_eq(window_descriptor_exists(desc_with_ss), false); + + let desc_without_ss = FrameHeaderDescriptor {single_segment_flag: u1:0, ..zero_desc}; + assert_eq(window_descriptor_exists(desc_without_ss), true); +} + +// Extracts window size from 8-bit chunk of data +// that is assumed to be a valid Window_Descriptor +fn extract_window_size_from_window_descriptor(data: u8) -> u64 { + let exponent = data[3:8]; + let mantissa = data[0:3]; + + let window_base = (u42:1 << (u6:10 + exponent as u6)); + let window_base_add = (window_base >> u2:3) as u42; + // optimization: perform multiplication by a 3-bit value with adds and shifts + // because XLS only allows multiplying operands of the same width + let window_add = match mantissa { + u3:0 => u42:0, + u3:1 => window_base_add, // u39 + u3:2 => window_base_add + window_base_add, // u39 + u39 = u40 + u3:3 => (window_base_add << u1:1) + window_base_add, // u40 + u39 = u41 + u3:4 => (window_base_add << u1:1) + (window_base_add << u1:1), // u40 + u40 = u41 + u3:5 => (window_base_add << u2:2) + window_base_add, // u41 + u39 = u42 + u3:6 => (window_base_add << u2:2) + (window_base_add << u2:1), // u41 + u40 = u42 + u3:7 => (window_base_add << u2:3) - window_base_add, // u42 - u39 = u42 + _ => fail!("extract_window_size_from_window_descriptor_unreachable", u42:0), + }; + + window_base as u64 + window_add as u64 +} + +#[test] +fn test_extract_window_size_from_window_descriptor() { + assert_eq(extract_window_size_from_window_descriptor(u8:0x0), u64:0x400); + assert_eq(extract_window_size_from_window_descriptor(u8:0x9), u64:0x900); + assert_eq(extract_window_size_from_window_descriptor(u8:0xFF), u64:0x3c000000000); +} + +// Returns boolean showing if the Frame_Content_Size section exists for +// the frame with the given FrameHeaderDescriptor. +fn frame_content_size_exists(desc: FrameHeaderDescriptor) -> bool { + desc.single_segment_flag != u1:0 || desc.frame_content_size_flag != u2:0 +} + +#[test] +fn test_frame_content_size_exists() { + let zero_desc = zero!(); + + let desc = FrameHeaderDescriptor {single_segment_flag: u1:0, frame_content_size_flag: u2:0, ..zero_desc}; + assert_eq(frame_content_size_exists(desc), false); + + let desc = FrameHeaderDescriptor {single_segment_flag: u1:0, frame_content_size_flag: u2:2, ..zero_desc}; + assert_eq(frame_content_size_exists(desc), true); + + let desc = FrameHeaderDescriptor {single_segment_flag: u1:1, frame_content_size_flag: u2:0, ..zero_desc}; + assert_eq(frame_content_size_exists(desc), true); + + let desc = FrameHeaderDescriptor {single_segment_flag: u1:1, frame_content_size_flag: u2:3, ..zero_desc}; + assert_eq(frame_content_size_exists(desc), true); +} + + +// Calculate maximal accepted window_size for given WINDOW_LOG_MAX and return whether given +// window_size should be accepted or discarded. +// Based on window_size calculation from: RFC 8878 +// https://datatracker.ietf.org/doc/html/rfc8878#name-window-descriptor +fn window_size_valid(window_size: WindowSize) -> bool { + let max_window_size = (WindowSize:1 << WINDOW_LOG_MAX) + (((WindowSize:1 << WINDOW_LOG_MAX) >> WindowSize:3) * MAX_MANTISSA); + + window_size <= max_window_size +} + + +pub fn parse_frame_header(header_raw: uN[112]) -> (FrameHeader, u4, u1) { + let fhd_raw = header_raw[0:8]; + let fhd = extract_frame_header_descriptor(fhd_raw); + // RFC8878 Section 3.1.1.1.1.4 + // "This [reserved] bit is reserved for some future feature. Its value + // must be zero. A decoder compliant with this specification version must + // ensure it is not set." + let header_ok = !fhd.reserved; + + let window_descriptor_start = u32:1; + // RFC8878 Section 3.1.1.1.2 + // "When Single_Segment_Flag is set, Window_Descriptor is not present." + let window_descriptor_len = match fhd.single_segment_flag { + u1:0 => u1:1, + u1:1 => u1:0, + _ => fail!("window_descriptor_len_unreachable", u1:0), + }; + let window_descriptor_raw = header_raw[u32:8*window_descriptor_start+:u8]; + let window_size = extract_window_size_from_window_descriptor(window_descriptor_raw); + + let dictionary_id_start = window_descriptor_start + window_descriptor_len as u32; + let dictionary_id_len = match fhd.dictionary_id_flag { + u2:0 => u32:0, + u2:1 => u32:1, + u2:2 => u32:2, + u2:3 => u32:4, + _ => fail!("dictionary_id_len_unreachable", u32:0), + }; + let dictionary_id_raw = header_raw[u32:8*dictionary_id_start+:u32]; + let dictionary_id = dictionary_id_raw & match fhd.dictionary_id_flag { + u2:0 => u32:0x0000_0000, + u2:1 => u32:0x0000_00ff, + u2:2 => u32:0x0000_ffff, + u2:3 => u32:0xffff_ffff, + _ => fail!("dictionary_id_unreachable", u32:0), + }; + + let frame_content_size_start = dictionary_id_start + dictionary_id_len; + // RFC8878 Section 3.1.1.1.1.1 + // "When Frame_Content_Size_Flag is 0, FCS_Field_Size depends on + // Single_Segment_Flag: If Single_Segment_Flag is set, FCS_Field_Siz + // is 1. Otherwise, FCS_Field_Size is 0;" + let frame_content_size_len = match (fhd.frame_content_size_flag, fhd.single_segment_flag) { + (u2:0, u1:0) => u32:0, + (u2:0, u1:1) => u32:1, + (u2:1, _) => u32:2, + (u2:2, _) => u32:4, + (u2:3, _) => u32:8, + _ => fail!("frame_content_size_len_unreachable", u32:0), + }; + + let frame_content_size_raw = header_raw[u32:8*frame_content_size_start+:u64]; + let frame_content_size_masked = frame_content_size_raw & match frame_content_size_len { + u32:0 => u64:0x0000_0000_0000_0000, + u32:1 => u64:0x0000_0000_0000_00ff, + u32:2 => u64:0x0000_0000_0000_ffff, + u32:4 => u64:0x0000_0000_ffff_ffff, + u32:8 => u64:0xffff_ffff_ffff_ffff, + _ => fail!("frame_content_size_masked_unreachable", u64:0), + }; + + // RFC8878 Section 3.1.1.1.4 + // "When FCS_Field_Size is 2, the offset of 256 is added." + let frame_content_size = frame_content_size_masked + match frame_content_size_len { + u32:2 => u64:256, + _ => u64:0, + }; + + // RFC8878 Section 3.1.1.1.2 + // "When Single_Segment_Flag is set, Window_Descriptor is not present. + // In this case, Window_Size is Frame_Content_Size [...]" + let window_size = if (window_descriptor_exists(fhd)) { + window_size + } else if (frame_content_size_exists(fhd)) { + frame_content_size + } else { + WindowSize:0 + }; + + let total_header_len = (frame_content_size_start + frame_content_size_len) as u4; + + (FrameHeader { + window_size: window_size, + frame_content_size: if frame_content_size_len != u32:0 { frame_content_size } else { FrameContentSize:0 }, + dictionary_id: if dictionary_id_len != u32:0 { dictionary_id } else { DictionaryId:0 }, + content_checksum_flag: fhd.content_checksum_flag, + }, total_header_len, header_ok) +} + + +#[test] +fn test_parse_frame_header() { + // normal case + let test_vec = uN[112]:0x1234567890ABCDEF_CAFE_09_C2; + let (frame_header_result, len, ok) = parse_frame_header(test_vec); + assert_eq(frame_header_result, FrameHeader { + window_size: u64:0x900, + frame_content_size: u64:0x1234567890ABCDEF, + dictionary_id: u32:0xCAFE, + content_checksum_flag: u1:0, + }); + assert_eq(len, u4:12); + assert_eq(ok, u1:1); + + // SingleSegmentFlag is set + let test_vec = uN[112]:0xaa20; + let (frame_header_result, len, ok) = parse_frame_header(test_vec); + assert_eq(frame_header_result, FrameHeader { + window_size: u64:0xaa, + frame_content_size: u64:0xaa, + dictionary_id: u32:0x0, + content_checksum_flag: u1:0, + }); + assert_eq(len, u4:2); + assert_eq(ok, u1:1); + + // SingleSegmentFlag is set and FrameContentSize is bigger than accepted window_size + let test_vec = uN[112]:0x1234567890ABCDEF_CAFE_E2; + let (frame_header_result, len, ok) = parse_frame_header(test_vec); + assert_eq(frame_header_result, FrameHeader { + window_size: u64:0x1234567890ABCDEF, + frame_content_size: u64:0x1234567890ABCDEF, + dictionary_id: u32:0xCAFE, + content_checksum_flag: u1:0, + }); + assert_eq(len, u4:11); + assert_eq(ok, u1:1); + + // Frame header descriptor is corrupted (we don't check frame header and length) + let test_vec = uN[112]:0x1234567890ABCDEF_1234_09_CA; + let (_, _, ok) = parse_frame_header(test_vec); + assert_eq(ok, u1:0); + + // Large window size + let test_vec = uN[112]:0xd310; + let (frame_header_result, len, ok) = parse_frame_header(test_vec); + assert_eq(frame_header_result, FrameHeader { + window_size: u64:0x1600000000, + ..zero!() + }); + assert_eq(len, u4:2); + assert_eq(ok, u1:1); + + // Large window size + let test_vec = uN[112]:0xf45b5b5b0db1; + let (frame_header_result, len, ok) = parse_frame_header(test_vec); + assert_eq(frame_header_result, FrameHeader { + window_size: u64:0xf45b5b5b, + frame_content_size: u64:0xf45b5b5b, + dictionary_id: u32:0xD, + content_checksum_flag: u1:0, + }); + assert_eq(len, u4:6); + assert_eq(ok, u1:1); + + // Large window size + let test_vec = uN[112]:0xc0659db6813a16b33f3da53a79e4; + let (frame_header_result, len, ok) = parse_frame_header(test_vec); + assert_eq(frame_header_result, FrameHeader { + window_size: u64:0x3a16b33f3da53a79, + frame_content_size: u64:0x3a16b33f3da53a79, + dictionary_id: u32:0, + content_checksum_flag: u1:1, + }); + assert_eq(len, u4:9); + assert_eq(ok, u1:1); +} + + +enum FrameHeaderDecoderFsm: u1 { + RECV = 0, + RESP = 1 +} + +// Magic number value, as in: +// https://datatracker.ietf.org/doc/html/rfc8878#section-3.1.1 +const MAGIC_NUMBER = u32:0xFD2FB528; +const MAGIC_NUMBER_LEN = u32:4; + +const MAX_HEADER_LEN = u32:14; +const MAX_MAGIC_PLUS_HEADER_LEN = MAGIC_NUMBER_LEN + MAX_HEADER_LEN; + +struct FrameHeaderDecoderState { + fsm: FrameHeaderDecoderFsm, + xfers: u32, + raw_header: uN[XFER_SIZE][XFER_COUNT], +} + +pub proc FrameHeaderDecoder< + WINDOW_LOG_MAX: u32, + DATA_W: u32, + ADDR_W: u32, + XFERS_FOR_HEADER: u32 = {((MAX_MAGIC_PLUS_HEADER_LEN * u32:8) / DATA_W) + u32:1}, +> { + type State = FrameHeaderDecoderState; + type Fsm = FrameHeaderDecoderFsm; + type Req = FrameHeaderDecoderReq; + type Resp = FrameHeaderDecoderResp; + type ReaderReq = mem_reader::MemReaderReq; + type ReaderResp = mem_reader::MemReaderResp; + + reader_req_s: chan out; + reader_resp_r: chan in; + + decode_req_r: chan in; + decode_resp_s: chan out; + + config( + reader_req_s: chan out, + reader_resp_r: chan in, + decode_req_r: chan in, + decode_resp_s: chan out + ) { + (reader_req_s, reader_resp_r, decode_req_r, decode_resp_s) + } + + init { zero!() } + + next(state: State) { + type ReaderReq = mem_reader::MemReaderReq; + type State = FrameHeaderDecoderState; + + let tok0 = join(); + let (tok_req, req, do_req) = recv_non_blocking(tok0, decode_req_r, zero!()); + send_if(tok_req, reader_req_s, do_req, ReaderReq { addr: req.addr, length: MAX_MAGIC_PLUS_HEADER_LEN as uN[ADDR_W] }); + + let do_recv = (state.fsm == Fsm::RECV); + let (tok, resp, recvd) = recv_if_non_blocking(tok0, reader_resp_r, do_recv, zero!()); + + let do_resp = (state.fsm == Fsm::RESP); + let raw_header_bits = state.raw_header as uN[DATA_W * XFERS_FOR_HEADER]; + let raw_magic_number = raw_header_bits[:s32:8 * MAGIC_NUMBER_LEN as s32]; + let raw_header = raw_header_bits[s32:8 * MAGIC_NUMBER_LEN as s32 : s32:8 * MAX_MAGIC_PLUS_HEADER_LEN as s32]; + let magic_number_ok = raw_magic_number == MAGIC_NUMBER; + let (decoded_header, header_len, header_ok) = parse_frame_header(raw_header); + + let status = if (!header_ok || !magic_number_ok) { + FrameHeaderDecoderStatus::CORRUPTED + } else if (!window_size_valid(decoded_header.window_size)) { + FrameHeaderDecoderStatus::UNSUPPORTED_WINDOW_SIZE + } else { + FrameHeaderDecoderStatus::OKAY + }; + + let header_result = FrameHeaderDecoderResp { + status: status, + header: decoded_header, + length: header_len as u5 + MAGIC_NUMBER_LEN as u5, + }; + + send_if(tok0, decode_resp_s, do_resp, header_result); + + let next_state = match (state.fsm) { + Fsm::RECV => { + if (recvd) { + // raw_header is updated from the highest to lowest index because + // highest index in an array contains least significant bytes when + // casting to a bit vector + let update_idx = XFERS_FOR_HEADER - state.xfers - u32:1; + let next_raw_header = update(state.raw_header, update_idx, resp.data); + if (resp.last) { + State { raw_header: next_raw_header, fsm: Fsm::RESP, ..state } + } else { + State { raw_header: next_raw_header, xfers: state.xfers + u32:1, ..state } + } + } else { + state + } + }, + Fsm::RESP => { + State { fsm: Fsm::RECV, xfers: u32:0, ..state } + }, + _ => fail!("FrameHeaderDecoder_fsm_unreachable", zero!()) + }; + + next_state + } +} + +// The largest allowed WindowLog for DSLX tests +pub const TEST_WINDOW_LOG_MAX = u32:22; +pub const TEST_DATA_W = u32:32; +pub const TEST_ADDR_W = u32:16; +pub const TEST_XFERS_FOR_HEADER = ((MAX_MAGIC_PLUS_HEADER_LEN * u32:8) / TEST_DATA_W) + u32:1; + +#[test_proc] +proc FrameHeaderDecoderTest { + type Req = FrameHeaderDecoderReq; + type Resp = FrameHeaderDecoderResp; + type ReaderReq = mem_reader::MemReaderReq; + type ReaderResp = mem_reader::MemReaderResp; + + terminator: chan out; + + reader_req_r: chan in; + reader_resp_s: chan out; + + decode_req_s: chan out; + decode_resp_r: chan in; + + config(terminator: chan out) { + let (reader_req_s, reader_req_r) = chan("reader_req"); + let (reader_resp_s, reader_resp_r) = chan("reader_resp"); + let (decode_req_s, decode_req_r) = chan("decode_req"); + let (decode_resp_s, decode_resp_r) = chan("decode_resp"); + spawn FrameHeaderDecoder( + reader_req_s, + reader_resp_r, + decode_req_r, + decode_resp_s + ); + (terminator, reader_req_r, reader_resp_s, decode_req_s, decode_resp_r) + } + + init {} + + next(state: ()) { + let tok = join(); + let tests: (u32[TEST_XFERS_FOR_HEADER], FrameHeaderDecoderResp)[7] = [ + ( + // normal case + [u32:0xFD2FB528, u32:0xCAFE_09_C2, u32:0x90ABCDEF, u32:0x12345678, u32:0x0], + FrameHeaderDecoderResp { + header: FrameHeader { + window_size: u64:0x900, + frame_content_size: u64:0x1234567890ABCDEF, + dictionary_id: u32:0xCAFE, + content_checksum_flag: u1:0, + }, + status: FrameHeaderDecoderStatus::OKAY, + length: u5:16 + }, + ), ( + // SingleSegmentFlag is set + [u32:0xFD2FB528, u32:0xAA20, u32:0x0, u32:0x0, u32:0x0], + FrameHeaderDecoderResp { + header: FrameHeader { + window_size: u64:0xaa, + frame_content_size: u64:0xaa, + dictionary_id: u32:0x0, + content_checksum_flag: u1:0, + }, + status: FrameHeaderDecoderStatus::OKAY, + length: u5:6 + }, + ), ( + // SingleSegmentFlag is set and FrameContentSize is bigger than accepted window_size + [u32:0xFD2FB528, u32:0xEF_CAFE_E2, u32:0x7890ABCD, u32:0x123456, u32:0x0], + FrameHeaderDecoderResp { + header: FrameHeader { + window_size: u64:0x1234567890ABCDEF, + frame_content_size: u64:0x1234567890ABCDEF, + dictionary_id: u32:0xCAFE, + content_checksum_flag: u1:0, + }, + status: FrameHeaderDecoderStatus::UNSUPPORTED_WINDOW_SIZE, + length: u5:15 + }, + ), ( + // Frame header descriptor is corrupted (we don't check 'header' and 'length' fields) + [u32:0xFD2FB528, u32:0x1234_09_CA, u32:0x90ABCDEF, u32:0x12345678, u32:0x0], + FrameHeaderDecoderResp { + header: zero!(), + status: FrameHeaderDecoderStatus::CORRUPTED, + length: u5:0 + }, + ), ( + // Window size required by frame is too big for given decoder configuration + [u32:0xFD2FB528, u32:0xD310, u32:0x0, u32:0x0, u32:0x0], + FrameHeaderDecoderResp { + header: FrameHeader { + window_size: u64:0x1600000000, + ..zero!() + }, + status: FrameHeaderDecoderStatus::UNSUPPORTED_WINDOW_SIZE, + length: u5:6 + }, + ), ( + // Window size required by frame is too big for given decoder configuration + [u32:0xFD2FB528, u32:0x5B5B0DB1, u32:0xF45B, u32:0x0, u32:0x0], + FrameHeaderDecoderResp { + header: FrameHeader { + window_size: u64:0xf45b5b5b, + frame_content_size: u64:0xf45b5b5b, + dictionary_id: u32:0xD, + content_checksum_flag: u1:0, + }, + status: FrameHeaderDecoderStatus::UNSUPPORTED_WINDOW_SIZE, + length: u5:10 + }, + ), ( + // Window size required by frame is too big for given decoder configuration + [u32:0xFD2FB528, u32:0xA53A79E4, u32:0x16B33F3D, u32:0x9DB6813A, u32:0xC065], + FrameHeaderDecoderResp { + header: FrameHeader { + window_size: u64:0x3a16b33f3da53a79, + frame_content_size: u64:0x3a16b33f3da53a79, + dictionary_id: u32:0, + content_checksum_flag: u1:1, + }, + status: FrameHeaderDecoderStatus::UNSUPPORTED_WINDOW_SIZE, + length: u5:13 + } + ) + ]; + + const ADDR = u16:0x1234; + let tok = for ((_, (test_vec, expected)), tok): ((u32, (u32[TEST_XFERS_FOR_HEADER], FrameHeaderDecoderResp)), token) in enumerate(tests) { + let tok = send(tok, decode_req_s, FrameHeaderDecoderReq { addr: ADDR }); + let (tok, recv_data) = recv(tok, reader_req_r); + + assert_eq(recv_data, ReaderReq { addr: ADDR, length: MAX_MAGIC_PLUS_HEADER_LEN as u16 }); + + let tok = for ((j, word), tok): ((u32, u32), token) in enumerate(test_vec) { + let last = j + u32:1 == array_size(test_vec); + send(tok, reader_resp_s, ReaderResp { + status: mem_reader::MemReaderStatus::OKAY, + data: word, + length: if !last { (TEST_DATA_W / u32:8) as u16 } else { (MAX_MAGIC_PLUS_HEADER_LEN % TEST_XFERS_FOR_HEADER) as u16 }, + last: last, + }) + }(tok); + + let (tok, recv_data) = recv(tok, decode_resp_r); + if (recv_data.status == FrameHeaderDecoderStatus::OKAY || recv_data.status == FrameHeaderDecoderStatus::UNSUPPORTED_WINDOW_SIZE) { + assert_eq(recv_data, expected); + } else { + // if the header is corrupted we don't offer any guarantees + // about its contents so we just check that the status matches + assert_eq(recv_data.status, expected.status); + }; + + tok + }(tok); + + send(tok, terminator, true); + } +} + + +// Largest allowed WindowLog accepted by libzstd decompression function +// https://github.com/facebook/zstd/blob/v1.4.7/lib/decompress/zstd_decompress.c#L296 +// Use only in C++ tests when comparing DSLX ZSTD Decoder with libzstd +pub const TEST_WINDOW_LOG_MAX_LIBZSTD = u32:30; + +proc FrameHeaderDecoderInst { + type Req = FrameHeaderDecoderReq; + type Resp = FrameHeaderDecoderResp; + type ReaderReq = mem_reader::MemReaderReq; + type ReaderResp = mem_reader::MemReaderResp; + + reader_req_s: chan out; + reader_resp_r: chan in; + + decode_req_r: chan in; + decode_resp_s: chan out; + + config( + reader_req_s: chan out, + reader_resp_r: chan in, + decode_req_r: chan in, + decode_resp_s: chan out, + ) { + spawn FrameHeaderDecoder( + reader_req_s, + reader_resp_r, + decode_req_r, + decode_resp_s + ); + (reader_req_s, reader_resp_r, decode_req_r, decode_resp_s) + } + + init {} + + next(state: ()) {} +} diff --git a/xls/modules/zstd/memory/BUILD b/xls/modules/zstd/memory/BUILD index 76c25da32f..bf679aee66 100644 --- a/xls/modules/zstd/memory/BUILD +++ b/xls/modules/zstd/memory/BUILD @@ -12,14 +12,13 @@ # See the License for the specific language governing permissions and # limitations under the License. -load("@xls_pip_deps//:requirements.bzl", "requirement") load("@rules_hdl//place_and_route:build_defs.bzl", "place_and_route") load("@rules_hdl//synthesis:build_defs.bzl", "benchmark_synth", "synthesize_rtl") load("@rules_hdl//verilog:providers.bzl", "verilog_library") +load("@xls_pip_deps//:requirements.bzl", "requirement") load( "//xls/build_rules:xls_build_defs.bzl", "xls_benchmark_ir", - "xls_benchmark_verilog", "xls_dslx_library", "xls_dslx_test", "xls_dslx_verilog", @@ -396,9 +395,6 @@ xls_dslx_verilog( codegen_args = mem_reader_codegen_args, dslx_top = "MemReaderInst", library = ":mem_reader_dslx", - opt_ir_args = { - "top": "__mem_reader__MemReaderInst__MemReaderAdv_0__16_128_16_8_8_2_2_16_64_8_next", - }, tags = ["manual"], verilog_file = "mem_reader.v", ) @@ -439,6 +435,60 @@ place_and_route( target_die_utilization_percentage = "10", ) +mem_reader_adv_codegen_args = common_codegen_args | { + "module_name": "mem_reader_adv", + "pipeline_stages": "4", + "streaming_channel_data_suffix": "_data", + "flop_inputs_kind": "skid", + "flop_outputs_kind": "skid", + "clock_period_ps": "2600", +} + +xls_dslx_verilog( + name = "mem_reader_adv_verilog", + codegen_args = mem_reader_adv_codegen_args, + dslx_top = "MemReaderAdvInst", + library = ":mem_reader_dslx", + tags = ["manual"], + verilog_file = "mem_reader_adv.v", +) + +verilog_library( + name = "mem_reader_adv_verilog_lib", + srcs = [ + ":mem_reader_adv.v", + ], + tags = ["manual"], +) + +synthesize_rtl( + name = "mem_reader_adv_synth_asap7", + standard_cells = "@org_theopenroadproject_asap7sc7p5t_28//:asap7-sc7p5t_rev28_rvt", + tags = ["manual"], + top_module = "mem_reader_adv", + deps = [ + ":mem_reader_adv_verilog_lib", + ], +) + +benchmark_synth( + name = "mem_reader_adv_benchmark_synth", + synth_target = ":mem_reader_adv_synth_asap7", + tags = ["manual"], +) + +place_and_route( + name = "mem_reader_adv_place_and_route", + clock_period = CLOCK_PERIOD_PS, + core_padding_microns = 2, + min_pin_distance = "0.5", + placement_density = "0.30", + stop_after_step = "global_routing", + synthesized_rtl = ":mem_reader_adv_synth_asap7", + tags = ["manual"], + target_die_utilization_percentage = "10", +) + py_test( name = "mem_reader_cocotb_test", srcs = ["mem_reader_cocotb_test.py"], diff --git a/xls/modules/zstd/memory/mem_reader.x b/xls/modules/zstd/memory/mem_reader.x index 4d63cc4edd..ea96264728 100644 --- a/xls/modules/zstd/memory/mem_reader.x +++ b/xls/modules/zstd/memory/mem_reader.x @@ -37,7 +37,7 @@ pub enum MemReaderStatus : u1 { // Request that can be submited to MemReader to read data from an AXI bus pub struct MemReaderReq { - offset: uN[DSLX_ADDR_W], // + addr: uN[DSLX_ADDR_W], // length: uN[DSLX_ADDR_W] // } @@ -49,25 +49,17 @@ pub struct MemReaderResp { last: bool, // if this is the last packet to expect as a response } -// Contains configuration data for the MemReader proc, -// It can only be set once, before submiting any request. -pub struct MemReaderCtrl { - base: uN[AXI_ADDR_W] // base address -} - enum MemReaderFsm : u3 { - CONFIGURE = 0, - REQUEST = 1, - RESPONSE = 2, - RESPONSE_ZERO = 3, - RESPONSE_ERROR = 4, + REQUEST = 0, + RESPONSE = 1, + RESPONSE_ZERO = 2, + RESPONSE_ERROR = 3, } struct MemReaderState { fsm: MemReaderFsm, error: bool, base: uN[AXI_ADDR_W], - req_len: uN[AXI_ADDR_W], } // A proc implementing the logic for issuing requests to AxiReader, @@ -83,7 +75,6 @@ proc MemReaderInternal< AXI_TO_DSLX_RATIO: u32 = {AXI_DATA_W / DSLX_DATA_W}, AXI_TO_DSLX_RATIO_W: u32 = {std::clog2((AXI_DATA_W / DSLX_DATA_W) + u32:1)} > { - type Ctrl = MemReaderCtrl; type Req = MemReaderReq; type Resp = MemReaderResp; type Length = uN[DSLX_ADDR_W]; @@ -110,7 +101,6 @@ proc MemReaderInternal< const_assert!(AXI_TO_DSLX_RATIO == AXI_DATA_W / DSLX_DATA_W); const_assert!(AXI_TO_DSLX_RATIO_W == std::clog2((AXI_DATA_W / DSLX_DATA_W) + u32:1)); - ctrl_r: chan in; req_r: chan in; resp_s: chan out; @@ -120,14 +110,13 @@ proc MemReaderInternal< axi_st_out_r: chan in; config( - ctrl_r: chan in, req_r: chan in, resp_s: chan out, reader_req_s: chan out, reader_err_r: chan in, axi_st_out_r: chan in, ) { - (ctrl_r, req_r, resp_s, reader_req_s, reader_err_r, axi_st_out_r) + (req_r, resp_s, reader_req_s, reader_err_r, axi_st_out_r) } init { zero!() } @@ -148,17 +137,13 @@ proc MemReaderInternal< let (tok, error_info, error) = recv_non_blocking(tok0, reader_err_r, zero!()); - // Configure - let do_configure = (state.fsm == Fsm::CONFIGURE); - let (tok, ctrl) = recv_if(tok0, ctrl_r, do_configure, zero!()); - // Request let do_handle_req = !error && (state.fsm == Fsm::REQUEST); let (tok, req) = recv_if(tok0, req_r, do_handle_req, zero!()); let is_zero_len = (req.length == uN[DSLX_ADDR_W]:0); let reader_req = axi_reader::AxiReaderReq { - addr: state.base + req.offset, + addr: req.addr, len: req.length }; let do_send_reader_req = !error && !is_zero_len && (state.fsm == Fsm::REQUEST); @@ -184,20 +169,13 @@ proc MemReaderInternal< let tok = send_if(tok0, resp_s, do_send_resp, reader_resp); let next_state = match (state.fsm) { - Fsm::CONFIGURE => { - State { - fsm: Fsm::REQUEST, - base: ctrl.base, - ..zero!() - } - }, Fsm::REQUEST => { if error { State { fsm: Fsm::RESPONSE_ERROR, ..zero!() } } else if is_zero_len { State { fsm: Fsm::RESPONSE_ZERO, ..state } } else { - State { fsm: Fsm::RESPONSE, req_len: req.length, ..state } + State { fsm: Fsm::RESPONSE, ..state } } }, Fsm::RESPONSE => { @@ -236,7 +214,7 @@ proc MemReaderInternal< // A proc that integrates other procs to create a functional design for // performing AXI read transactions. It allows for connecting narrow DSLX-side // with wider AXI-side, if the wider side has to be a multiple of the narrower side. -proc MemReaderAdv< +pub proc MemReaderAdv< // DSLX side parameters DSLX_DATA_W: u32, DSLX_ADDR_W: u32, // AXI side parameters @@ -247,7 +225,6 @@ proc MemReaderAdv< AXI_TO_DSLX_RATIO: u32 = {AXI_DATA_W / DSLX_DATA_W}, AXI_TO_DSLX_RATIO_W: u32 = {std::clog2((AXI_DATA_W / DSLX_DATA_W) + u32:1)} > { - type Ctrl = MemReaderCtrl; type Req = MemReaderReq; type Resp = MemReaderResp; @@ -259,7 +236,6 @@ proc MemReaderAdv< type AxiReaderError = axi_reader::AxiReaderError; config( - ctrl_r: chan in, req_r: chan in, resp_s: chan out, axi_ar_s: chan out, @@ -275,7 +251,7 @@ proc MemReaderAdv< spawn MemReaderInternal< DSLX_DATA_W, DSLX_ADDR_W, AXI_DATA_W, AXI_ADDR_W, AXI_DEST_W, AXI_ID_W, - >(ctrl_r, req_r, resp_s, reader_req_s, reader_err_r, axi_st_out_r); + >(req_r, resp_s, reader_req_s, reader_err_r, axi_st_out_r); spawn axi_reader::AxiReader< AXI_ADDR_W, AXI_DATA_W, AXI_DEST_W, AXI_ID_W @@ -299,11 +275,11 @@ proc MemReaderAdv< // A proc that integrates other procs to create a functional design for // performing AXI read transactions. The proc allows for interfacing with // AXI bus that has the same data width as DSLX-side of the design. -proc MemReader< +pub proc MemReader< DATA_W: u32, ADDR_W: u32, DEST_W: u32, ID_W: u32, + CHANNEL_DEPTH: u32 = {u32:0}, DATA_W_DIV8: u32 = {DATA_W / u32:8}, > { - type Ctrl = MemReaderCtrl; type Req = MemReaderReq; type Resp = MemReaderResp; @@ -315,22 +291,20 @@ proc MemReader< type AxiReaderError = axi_reader::AxiReaderError; config( - ctrl_r: chan in, req_r: chan in, resp_s: chan out, axi_ar_s: chan out, axi_r_r: chan in ) { - let (reader_req_s, reader_req_r) = chan("reader_req"); - let (reader_err_s, reader_err_r) = chan("reader_err"); + let (reader_req_s, reader_req_r) = chan("reader_req"); + let (reader_err_s, reader_err_r) = chan("reader_err"); - let (axi_st_in_s, axi_st_in_r) = chan("axi_st_in"); - let (axi_st_remove_s, axi_st_remove_r) = chan("axi_st_remove"); - let (axi_st_out_s, axi_st_out_r) = chan("axi_st_out"); + let (axi_st_in_s, axi_st_in_r) = chan("axi_st_in"); + let (axi_st_out_s, axi_st_out_r) = chan("axi_st_out"); spawn MemReaderInternal< DATA_W, ADDR_W, DATA_W, ADDR_W, DEST_W, ID_W - >(ctrl_r, req_r, resp_s, reader_req_s, reader_err_r, axi_st_out_r); + >(req_r, resp_s, reader_req_s, reader_err_r, axi_st_out_r); spawn axi_reader::AxiReader< ADDR_W, DATA_W, DEST_W, ID_W @@ -338,7 +312,7 @@ proc MemReader< spawn axi_stream_remove_empty::AxiStreamRemoveEmpty< DATA_W, DEST_W, ID_W - >(axi_st_remove_r, axi_st_out_s); + >(axi_st_in_r, axi_st_out_s); () } @@ -347,40 +321,38 @@ proc MemReader< } -const INST_AXI_DATA_W = u32:128; -const INST_AXI_ADDR_W = u32:16; -const INST_AXI_DEST_W = u32:8; -const INST_AXI_ID_W = u32:8; -const INST_AXI_DATA_W_DIV8 = INST_AXI_DATA_W / u32:8; +const INST_ADV_AXI_DATA_W = u32:128; +const INST_ADV_AXI_ADDR_W = u32:16; +const INST_ADV_AXI_DEST_W = u32:8; +const INST_ADV_AXI_ID_W = u32:8; +const INST_ADV_AXI_DATA_W_DIV8 = INST_ADV_AXI_DATA_W / u32:8; -const INST_DSLX_ADDR_W = u32:16; -const INST_DSLX_DATA_W = u32:64; -const INST_DSLX_DATA_W_DIV8 = INST_DSLX_DATA_W / u32:8; +const INST_ADV_DSLX_ADDR_W = u32:16; +const INST_ADV_DSLX_DATA_W = u32:64; +const INST_ADV_DSLX_DATA_W_DIV8 = INST_ADV_DSLX_DATA_W / u32:8; -proc MemReaderInst { - type Ctrl = MemReaderCtrl; - type Req = MemReaderReq; - type Resp = MemReaderResp; +proc MemReaderAdvInst { + type Req = MemReaderReq; + type Resp = MemReaderResp; - type AxiReaderReq = axi_reader::AxiReaderReq; - type AxiAr = axi::AxiAr; - type AxiR = axi::AxiR; - type AxiStream = axi_st::AxiStream; + type AxiReaderReq = axi_reader::AxiReaderReq; + type AxiAr = axi::AxiAr; + type AxiR = axi::AxiR; + type AxiStream = axi_st::AxiStream; - type State = MemReaderState; + type State = MemReaderState; type Fsm = MemReaderFsm; config( - ctrl_r: chan in, req_r: chan in, resp_s: chan out, axi_ar_s: chan out, axi_r_r: chan in ) { spawn MemReaderAdv< - INST_DSLX_DATA_W, INST_DSLX_ADDR_W, - INST_AXI_DATA_W, INST_AXI_ADDR_W, INST_AXI_DEST_W, INST_AXI_ID_W, - >(ctrl_r, req_r, resp_s, axi_ar_s, axi_r_r); + INST_ADV_DSLX_DATA_W, INST_ADV_DSLX_ADDR_W, + INST_ADV_AXI_DATA_W, INST_ADV_AXI_ADDR_W, INST_ADV_AXI_DEST_W, INST_ADV_AXI_ID_W, + >(req_r, resp_s, axi_ar_s, axi_r_r); () } @@ -390,19 +362,17 @@ proc MemReaderInst { } proc MemReaderInternalInst { - type Ctrl = MemReaderCtrl; - type Req = MemReaderReq; - type Resp = MemReaderResp; + type Req = MemReaderReq; + type Resp = MemReaderResp; - type AxiReaderReq = axi_reader::AxiReaderReq; + type AxiReaderReq = axi_reader::AxiReaderReq; type AxiReaderError = axi_reader::AxiReaderError; - type AxiAr = axi::AxiAr; - type AxiR = axi::AxiR; - type AxiStreamOutput = axi_st::AxiStream; + type AxiAr = axi::AxiAr; + type AxiR = axi::AxiR; + type AxiStreamOutput = axi_st::AxiStream; config( - ctrl_r: chan in, req_r: chan in, resp_s: chan out, reader_req_s: chan out, @@ -410,9 +380,45 @@ proc MemReaderInternalInst { axi_st_out_r: chan in ) { spawn MemReaderInternal< - INST_DSLX_DATA_W, INST_DSLX_ADDR_W, - INST_AXI_DATA_W, INST_AXI_ADDR_W, INST_AXI_DEST_W, INST_AXI_ID_W, - >(ctrl_r, req_r, resp_s, reader_req_s, reader_err_r, axi_st_out_r); + INST_ADV_DSLX_DATA_W, INST_ADV_DSLX_ADDR_W, + INST_ADV_AXI_DATA_W, INST_ADV_AXI_ADDR_W, INST_ADV_AXI_DEST_W, INST_ADV_AXI_ID_W, + >(req_r, resp_s, reader_req_s, reader_err_r, axi_st_out_r); + () + } + + init { } + next(state: ()) { } +} + +const INST_DATA_W = u32:64; +const INST_ADDR_W = u32:16; +const INST_DEST_W = u32:8; +const INST_ID_W = u32:8; +const INST_DATA_W_DIV8 = INST_DATA_W / u32:8; + + +proc MemReaderInst { + type Req = MemReaderReq; + type Resp = MemReaderResp; + + type AxiReaderReq = axi_reader::AxiReaderReq; + type AxiAr = axi::AxiAr; + type AxiR = axi::AxiR; + type AxiStream = axi_st::AxiStream; + + type State = MemReaderState; + type Fsm = MemReaderFsm; + + config( + req_r: chan in, + resp_s: chan out, + axi_ar_s: chan out, + axi_r_r: chan in + ) { + spawn MemReader< + INST_DATA_W, INST_ADDR_W, INST_DEST_W, INST_ID_W, + >(req_r, resp_s, axi_ar_s, axi_r_r); + () } @@ -432,7 +438,6 @@ const TEST_DSLX_DATA_W_DIV8 = TEST_DSLX_DATA_W / u32:8; #[test_proc] proc MemReaderTest { - type Ctrl = MemReaderCtrl; type Req = MemReaderReq; type Resp = MemReaderResp; type Fsm = MemReaderFsm; @@ -443,7 +448,6 @@ proc MemReaderTest { type AxiStream = axi_st::AxiStream; type Addr = uN[TEST_AXI_ADDR_W]; - type Offset = uN[TEST_DSLX_ADDR_W]; type Length = uN[TEST_DSLX_ADDR_W]; type Data = uN[TEST_DSLX_DATA_W]; @@ -463,14 +467,12 @@ proc MemReaderTest { type Status = MemReaderStatus; terminator: chan out; - ctrl_s: chan out; req_s: chan out; resp_r: chan in; axi_ar_r: chan in; axi_r_s: chan out; config(terminator: chan out) { - let (ctrl_s, ctrl_r) = chan("ctrl"); let (req_s, req_r) = chan("req"); let (resp_s, resp_r) = chan("resp"); let (axi_ar_s, axi_ar_r) = chan("axi_ar"); @@ -479,8 +481,8 @@ proc MemReaderTest { spawn MemReaderAdv< TEST_DSLX_DATA_W, TEST_DSLX_ADDR_W, TEST_AXI_DATA_W, TEST_AXI_ADDR_W, TEST_AXI_DEST_W, TEST_AXI_ID_W, - >(ctrl_r, req_r, resp_s, axi_ar_s, axi_r_r); - (terminator, ctrl_s, req_s, resp_r, axi_ar_r, axi_r_s) + >(req_r, resp_s, axi_ar_s, axi_r_r); + (terminator, req_s, resp_r, axi_ar_r, axi_r_s) } init { } @@ -488,62 +490,8 @@ proc MemReaderTest { next(state: ()) { let tok = join(); - let tok = send(tok, ctrl_s, Ctrl { base: Addr:0xFFF }); - - // empty transfers, should be just confirmed internaly - let tok = send(tok, req_s, Req { offset: Offset:2, length: Length:17 }); - let (tok, ar) = recv(tok, axi_ar_r); - assert_eq(ar, AxiAr { - id: AxiId:0x0, - addr: AxiAddr:0x1000, - region: AxiRegion:0x0, - len: AxiLength:0x1, - size: AxiSize::MAX_16B_TRANSFER, - burst: AxiBurst::INCR, - cache: AxiCache::DEV_NO_BUF, - prot: AxiProt:0x0, - qos: AxiQos:0x0 - }); - - let tok = send(tok, axi_r_s, AxiR { - id: AxiId:0x0, - data: AxiData:0x1122_3344_5566_7788_9900_AABB_CCDD_EEFF, - resp: AxiResp::OKAY, - last: AxiLast:false - }); - - let tok = send(tok, axi_r_s, AxiR { - id: AxiId:0x0, - data: AxiData:0x1122_3344_5566_7788_9900_AABB_CCDD_EEFF, - resp: AxiResp::OKAY, - last: AxiLast:true - }); - - let (tok, resp) = recv(tok, resp_r); - assert_eq(resp, Resp { - status: Status::OKAY, - data: Data:0x8899_00AA_BBCC_DDEE, - length: Length:8, - last: false - }); - - let (tok, resp) = recv(tok, resp_r); - assert_eq(resp, Resp { - status: Status::OKAY, - data: Data:0xFF11_2233_4455_6677, - length: Length:8, - last: false - }); - - let (tok, resp) = recv(tok, resp_r); - assert_eq(resp, Resp { - status: Status::OKAY, - data: Data:0xEE, - length: Length:1, - last: true - }); - - let tok = send(tok, req_s, Req { offset: Offset:0x101, length: Length:0x0 }); + // empty transfers, should be just confirmed internaly + let tok = send(tok, req_s, Req { addr: Addr:0x1100, length: Length:0x0 }); let (tok, resp) = recv(tok, resp_r); assert_eq(resp, Resp { status: Status::OKAY, @@ -555,7 +503,7 @@ proc MemReaderTest { // aligned transfer shorter than full AXI-side, // that fits one DSLX-side width - let tok = send(tok, req_s, Req { offset: Offset:0x101, length: Length:0x1 }); + let tok = send(tok, req_s, Req { addr: Addr:0x1100, length: Length:0x1 }); let (tok, ar) = recv(tok, axi_ar_r); assert_eq(ar, AxiAr { id: AxiId:0x0, @@ -587,7 +535,7 @@ proc MemReaderTest { // unaligned transfer shorter than full AXI-side, // that fits one DSLX-side width - let tok = send(tok, req_s, Req { offset: Offset:0x2, length: Length:0x1 }); + let tok = send(tok, req_s, Req { addr: Addr:0x1001, length: Length:0x1 }); let (tok, ar) = recv(tok, axi_ar_r); assert_eq(ar, AxiAr { id: AxiId:0x0, @@ -619,7 +567,7 @@ proc MemReaderTest { // unaligned transfer shorter than full AXI-side, // that fits one DSLX-side width and crosess 4k boundary - let tok = send(tok, req_s, Req { offset: Offset:0x0, length: Length:0x2 }); + let tok = send(tok, req_s, Req { addr: Addr:0xFFF, length: Length:0x2 }); let (tok, ar) = recv(tok, axi_ar_r); assert_eq(ar, AxiAr { id: AxiId:0x0, @@ -669,7 +617,7 @@ proc MemReaderTest { }); - let tok = send(tok, req_s, Req { offset: Offset:0x0, length: Length:0x10 }); + let tok = send(tok, req_s, Req { addr: Addr:0xFFF, length: Length:0x10 }); let (tok, ar) = recv(tok, axi_ar_r); assert_eq(ar, AxiAr { id: AxiId:0x0, @@ -726,6 +674,59 @@ proc MemReaderTest { last: true }); + let tok = send(tok, req_s, Req { addr: Addr:0x1001, length: Length:17 }); + let (tok, ar) = recv(tok, axi_ar_r); + assert_eq(ar, AxiAr { + id: AxiId:0x0, + addr: AxiAddr:0x1000, + region: AxiRegion:0x0, + len: AxiLength:0x1, + size: AxiSize::MAX_16B_TRANSFER, + burst: AxiBurst::INCR, + cache: AxiCache::DEV_NO_BUF, + prot: AxiProt:0x0, + qos: AxiQos:0x0 + }); + + let tok = send(tok, axi_r_s, AxiR { + id: AxiId:0x0, + data: AxiData:0x1122_3344_5566_7788_9900_AABB_CCDD_EEFF, + resp: AxiResp::OKAY, + last: AxiLast:false + }); + + let tok = send(tok, axi_r_s, AxiR { + id: AxiId:0x0, + data: AxiData:0x1122_3344_5566_7788_9900_AABB_CCDD_EEFF, + resp: AxiResp::OKAY, + last: AxiLast:true + }); + + let (tok, resp) = recv(tok, resp_r); + assert_eq(resp, Resp { + status: Status::OKAY, + data: Data:0x8899_00AA_BBCC_DDEE, + length: Length:8, + last: false + }); + + let (tok, resp) = recv(tok, resp_r); + assert_eq(resp, Resp { + status: Status::OKAY, + data: Data:0xFF11_2233_4455_6677, + length: Length:8, + last: false + }); + + let (tok, resp) = recv(tok, resp_r); + assert_eq(resp, Resp { + status: Status::OKAY, + data: Data:0xEE, + length: Length:1, + last: true + }); + + send(tok, terminator, true); } } diff --git a/xls/modules/zstd/memory/mem_reader_wrapper.v b/xls/modules/zstd/memory/mem_reader_wrapper.v index 0485a9ccb6..639548e695 100644 --- a/xls/modules/zstd/memory/mem_reader_wrapper.v +++ b/xls/modules/zstd/memory/mem_reader_wrapper.v @@ -59,7 +59,7 @@ module mem_reader_wrapper #( output wire axi_r_rready, input wire [ AXI_ID_W-1:0] axi_r_rid, input wire [AXI_DATA_W-1:0] axi_r_rdata, - input wire [ 1:0] axi_r_rresp, + input wire [ 2:0] axi_r_rresp, input wire axi_r_rlast ); @@ -86,7 +86,7 @@ module mem_reader_wrapper #( wire axi_r_vld; wire axi_r_rdy; - assign axi_r_data = {axi_r_rid, axi_r_rdata, 1'b0, axi_r_rresp, axi_r_rlast}; + assign axi_r_data = {axi_r_rid, axi_r_rdata, axi_r_rresp, axi_r_rlast}; assign axi_r_vld = axi_r_rvalid; assign axi_r_rready = axi_r_rdy; diff --git a/xls/modules/zstd/memory/mem_writer.x b/xls/modules/zstd/memory/mem_writer.x index d9e1be36a8..277c9910ef 100644 --- a/xls/modules/zstd/memory/mem_writer.x +++ b/xls/modules/zstd/memory/mem_writer.x @@ -38,7 +38,7 @@ import xls.modules.zstd.memory.axi_writer; import xls.modules.zstd.memory.axi_stream_add_empty; pub struct MemWriterReq { - offset: uN[ADDR_W], + addr: uN[ADDR_W], length: uN[ADDR_W], } @@ -48,14 +48,10 @@ pub struct MemWriterDataPacket { last: bool, } -pub struct MemWriterCtrl { - base: uN[ADDR_W], -} - -enum MemWriterFsm : u4 { - IDLE = 0, - RECV_REQ = 1, - SEND_WRITE_REQ = 2, +enum MemWriterFsm : u2 { + RECV_REQ = 0, + SEND_WRITE_REQ = 1, + RECV_DATA = 2, SEND_DATA = 3, } @@ -67,7 +63,6 @@ struct MemWriterState< DATA_W_DIV8: u32 > { fsm: MemWriterFsm, - base: uN[ADDR_W], req_len: sN[ADDR_W], axi_writer_req: axi_writer::AxiWriterRequest, } @@ -77,7 +72,6 @@ proc MemWriter< DATA_W_DIV8: u32 = {DATA_W / u32:8}, DATA_W_LOG2: u32 = {std::clog2(DATA_W / u32:8)} > { - type Ctrl = MemWriterCtrl; type Req = MemWriterReq; type Data = MemWriterDataPacket; type AxiWriterReq = axi_writer::AxiWriterRequest; @@ -96,7 +90,6 @@ proc MemWriter< type Id = uN[ID_W]; type Dest = uN[DEST_W]; - ctrl_r: chan in; req_in_r: chan in; data_in_r: chan in; axi_writer_req_s: chan out; @@ -105,7 +98,6 @@ proc MemWriter< resp_s: chan out; config( - ctrl_r: chan in, req_in_r: chan in, data_in_r: chan in, axi_aw_s: chan out, @@ -125,41 +117,27 @@ proc MemWriter< ADDR_W, DATA_W, DEST_W, ID_W >(axi_writer_req_r, resp_s, axi_aw_s, axi_w_s, axi_b_r, axi_st_padded_r); - (ctrl_r, req_in_r, data_in_r, axi_writer_req_s, padding_req_s, axi_st_raw_s, resp_s) + (req_in_r, data_in_r, axi_writer_req_s, padding_req_s, axi_st_raw_s, resp_s) } - init { - (State { - fsm: Fsm::IDLE, - ..zero!() - }) - } + init { zero!() } next(state: State) { let tok_0 = join(); - let (tok_1, ctrl) = recv_if(tok_0, ctrl_r, state.fsm == Fsm::IDLE, zero!()); let (tok_2, req_in) = recv_if(tok_0, req_in_r, state.fsm == Fsm::RECV_REQ, zero!()); let tok_3 = send_if(tok_0, axi_writer_req_s, state.fsm == Fsm::SEND_WRITE_REQ, state.axi_writer_req); let tok_4 = send_if(tok_3, padding_req_s, state.fsm == Fsm::SEND_WRITE_REQ, state.axi_writer_req); let (tok_5, data_in) = recv_if(tok_0, data_in_r, state.fsm == Fsm::SEND_DATA, zero!()); let next_state = match(state.fsm) { - Fsm::IDLE => { - State { - fsm: Fsm::RECV_REQ, - base: ctrl.base, - ..state - } - }, Fsm::RECV_REQ => { State { fsm: Fsm::SEND_WRITE_REQ, req_len: req_in.length as sLength, axi_writer_req: AxiWriterReq { - address: state.base + req_in.offset, + address: req_in.addr, length: req_in.length }, - ..state } }, Fsm::SEND_WRITE_REQ => { @@ -215,7 +193,6 @@ const INST_DATA_W_LOG2 = u32:6; const INST_WRITER_ID = u32:2; proc MemWriterInst { - type InstCtrl = MemWriterCtrl; type InstReq = MemWriterReq; type InstData = MemWriterDataPacket; type InstAxiStream = axi_st::AxiStream; @@ -225,7 +202,6 @@ proc MemWriterInst { type InstAxiWriterResp = axi_writer::AxiWriterResp; config( - ctrl_r: chan in, req_in_r: chan in, data_in_r: chan in, axi_aw_s: chan out, @@ -235,7 +211,7 @@ proc MemWriterInst { ) { spawn MemWriter< INST_ADDR_W, INST_DATA_W, INST_DEST_W, INST_ID_W, INST_WRITER_ID - >(ctrl_r, req_in_r, data_in_r, axi_aw_s, axi_w_s, axi_b_r, resp_s); + >(req_in_r, data_in_r, axi_aw_s, axi_w_s, axi_b_r, resp_s); () } @@ -252,7 +228,6 @@ const TEST_ID_W = TEST_DATA_W / u32:8; const TEST_DATA_W_LOG2 = u32:6; const TEST_WRITER_ID = u32:2; -type TestCtrl = MemWriterCtrl; type TestReq = MemWriterReq; type TestData = MemWriterDataPacket; type TestAxiWriterResp = axi_writer::AxiWriterResp; @@ -275,7 +250,6 @@ type TestDest = uN[TEST_DEST_W]; #[test_proc] proc MemWriterTest { terminator: chan out; - ctrl_s: chan out; req_in_s: chan out; data_in_s: chan out; axi_aw_r: chan in; @@ -286,7 +260,6 @@ proc MemWriterTest { config( terminator: chan out, ) { - let (ctrl_s, ctrl_r) = chan("ctrl"); let (req_in_s, req_in_r) = chan("req_in"); let (data_in_s, data_in_r) = chan("data_in"); let (axi_aw_s, axi_aw_r) = chan("axi_aw"); @@ -295,18 +268,18 @@ proc MemWriterTest { let (resp_s, resp_r) = chan("resp"); spawn MemWriter< TEST_ADDR_W, TEST_DATA_W, TEST_DEST_W, TEST_ID_W, TEST_WRITER_ID - >(ctrl_r, req_in_r, data_in_r, axi_aw_s, axi_w_s, axi_b_r, resp_s); - (terminator, ctrl_s, req_in_s, data_in_s, axi_aw_r, axi_w_r, axi_b_s, resp_r) + >(req_in_r, data_in_r, axi_aw_s, axi_w_s, axi_b_r, resp_s); + (terminator, req_in_s, data_in_s, axi_aw_r, axi_w_r, axi_b_s, resp_r) } init { () } next(state: ()) { - let tok = send(join(), ctrl_s, TestCtrl{base: TestAddr:0}); + let tok = join(); // Aligned single transfer let tok = send(tok, req_in_s, TestReq { - offset: TestAddr:0x0, + addr: TestAddr:0x0, length: TestLength:4 }); let tok = send(tok, data_in_s, TestData { @@ -337,7 +310,7 @@ proc MemWriterTest { // Unaligned single transfer let tok = send(tok, req_in_s, TestReq { - offset: TestAddr:0x10, + addr: TestAddr:0x10, length: TestLength:1 }); let tok = send(tok, data_in_s, TestData { @@ -368,7 +341,7 @@ proc MemWriterTest { // Unaligned single transfer let tok = send(tok, req_in_s, TestReq { - offset: TestAddr:0x24, + addr: TestAddr:0x24, length: TestLength:2 }); let tok = send(tok, data_in_s, TestData { @@ -399,7 +372,7 @@ proc MemWriterTest { // Unaligned single transfer let tok = send(tok, req_in_s, TestReq { - offset: TestAddr:0x38, + addr: TestAddr:0x38, length: TestLength:3 }); let tok = send(tok, data_in_s, TestData { @@ -430,7 +403,7 @@ proc MemWriterTest { // Unaligned single transfer let tok = send(tok, req_in_s, TestReq { - offset: TestAddr:0x71, + addr: TestAddr:0x71, length: TestLength:1 }); let tok = send(tok, data_in_s, TestData { @@ -461,7 +434,7 @@ proc MemWriterTest { // Unaligned 2 transfers let tok = send(tok, req_in_s, TestReq { - offset: TestAddr:0xf3, + addr: TestAddr:0xf3, length: TestLength:3 }); let tok = send(tok, data_in_s, TestData { @@ -498,7 +471,7 @@ proc MemWriterTest { // Unligned 3 transfers let tok = send(tok, req_in_s, TestReq { - offset: TestAddr:0x1f3, + addr: TestAddr:0x1f3, length: TestLength:7 }); let tok = send(tok, data_in_s, TestData { @@ -546,7 +519,7 @@ proc MemWriterTest { // Crossing AXI 4kB boundary, aligned 2 burst transfers let tok = send(tok, req_in_s, TestReq { - offset: TestAddr:0x0FFC, + addr: TestAddr:0x0FFC, length: TestLength:8 }); let tok = send(tok, data_in_s, TestData { @@ -600,7 +573,7 @@ proc MemWriterTest { // Crossing AXI 4kB boundary, unaligned 2 burst transfers let tok = send(tok, req_in_s, TestReq { - offset: TestAddr:0x1FFF, + addr: TestAddr:0x1FFF, length: TestLength:7 }); let tok = send(tok, data_in_s, TestData { diff --git a/xls/modules/zstd/priority_encoder.v b/xls/modules/zstd/priority_encoder.v new file mode 100644 index 0000000000..cf82512ba8 --- /dev/null +++ b/xls/modules/zstd/priority_encoder.v @@ -0,0 +1,92 @@ +/* + +Copyright (c) 2014-2021 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`resetall +`timescale 1ns / 1ps +`default_nettype none + +/* + * Priority encoder module + */ +module priority_encoder # +( + parameter WIDTH = 4, + // LSB priority selection + parameter LSB_HIGH_PRIORITY = 0 +) +( + input wire [WIDTH-1:0] input_unencoded, + output wire output_valid, + output wire [$clog2(WIDTH)-1:0] output_encoded, + output wire [WIDTH-1:0] output_unencoded +); + +parameter LEVELS = WIDTH > 2 ? $clog2(WIDTH) : 1; +parameter W = 2**LEVELS; + +// pad input to even power of two +wire [W-1:0] input_padded = {{W-WIDTH{1'b0}}, input_unencoded}; + +wire [W/2-1:0] stage_valid[LEVELS-1:0]; +wire [W/2-1:0] stage_enc[LEVELS-1:0]; + +generate + genvar l, n; + + // process input bits; generate valid bit and encoded bit for each pair + for (n = 0; n < W/2; n = n + 1) begin : loop_in + assign stage_valid[0][n] = |input_padded[n*2+1:n*2]; + if (LSB_HIGH_PRIORITY) begin + // bit 0 is highest priority + assign stage_enc[0][n] = !input_padded[n*2+0]; + end else begin + // bit 0 is lowest priority + assign stage_enc[0][n] = input_padded[n*2+1]; + end + end + + // compress down to single valid bit and encoded bus + for (l = 1; l < LEVELS; l = l + 1) begin : loop_levels + for (n = 0; n < W/(2*2**l); n = n + 1) begin : loop_compress + assign stage_valid[l][n] = |stage_valid[l-1][n*2+1:n*2]; + if (LSB_HIGH_PRIORITY) begin + // bit 0 is highest priority + assign stage_enc[l][(n+1)*(l+1)-1:n*(l+1)] = stage_valid[l-1][n*2+0] ? {1'b0, stage_enc[l-1][(n*2+1)*l-1:(n*2+0)*l]} : {1'b1, stage_enc[l-1][(n*2+2)*l-1:(n*2+1)*l]}; + end else begin + // bit 0 is lowest priority + assign stage_enc[l][(n+1)*(l+1)-1:n*(l+1)] = stage_valid[l-1][n*2+1] ? {1'b1, stage_enc[l-1][(n*2+2)*l-1:(n*2+1)*l]} : {1'b0, stage_enc[l-1][(n*2+1)*l-1:(n*2+0)*l]}; + end + end + end +endgenerate + +assign output_valid = stage_valid[LEVELS-1]; +assign output_encoded = stage_enc[LEVELS-1]; +assign output_unencoded = 1 << output_encoded; + +endmodule + +`resetall diff --git a/xls/modules/zstd/raw_block_dec_new_mem.x b/xls/modules/zstd/raw_block_dec_new_mem.x new file mode 100644 index 0000000000..669b66d5b1 --- /dev/null +++ b/xls/modules/zstd/raw_block_dec_new_mem.x @@ -0,0 +1,316 @@ +// Copyright 2024 The XLS Authors +// +// 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. + +// This file contains the implementation of RawBlockDecoder responsible for decoding +// ZSTD Raw Blocks. More information about Raw Block's format can be found in: +// https://datatracker.ietf.org/doc/html/rfc8878#section-3.1.1.2.2 + +import xls.modules.zstd.common as common; +import xls.modules.zstd.memory.mem_reader as mem_reader; + +type BlockDataPacket = common::BlockDataPacket; +type BlockPacketLength = common::BlockPacketLength; +type BlockData = common::BlockData; +type ExtendedBlockDataPacket = common::ExtendedBlockDataPacket; +type CopyOrMatchContent = common::CopyOrMatchContent; +type CopyOrMatchLength = common::CopyOrMatchLength; +type SequenceExecutorMessageType = common::SequenceExecutorMessageType; + +pub struct RawBlockDecoderReq { + id: u32, + addr: uN[ADDR_W], + length: uN[ADDR_W], + last_block: bool, +} + +pub enum RawBlockDecoderStatus: u1 { + OKAY = 0, + ERROR = 1, +} + +pub struct RawBlockDecoderResp { + status: RawBlockDecoderStatus, +} + +struct RawBlockDecoderState { + id: u32, // ID of the block + last_block: bool, // if the block is the last one +} + +// RawBlockDecoder is responsible for decoding Raw Blocks, +// it should be a part of the ZSTD Decoder pipeline. +pub proc RawBlockDecoder { + type Req = RawBlockDecoderReq; + type Resp = RawBlockDecoderResp; + type Output = ExtendedBlockDataPacket; + type Status = RawBlockDecoderStatus; + + type MemReaderReq = mem_reader::MemReaderReq; + type MemReaderResp = mem_reader::MemReaderResp; + type MemReaderStatus = mem_reader::MemReaderStatus; + + type State = RawBlockDecoderState; + + // decoder input + req_r: chan in; + resp_s: chan out; + + // decoder output + output_s: chan out; + + // memory interface + mem_req_s: chan out; + mem_resp_r: chan in; + + init { zero!() } + + config( + req_r: chan in, + resp_s: chan out, + output_s: chan out, + + mem_req_s: chan out, + mem_resp_r: chan in, + ) { + ( + req_r, resp_s, output_s, + mem_req_s, mem_resp_r, + ) + } + + next(state: State) { + let tok0 = join(); + + // receive request + let (tok1_0, req, req_valid) = recv_non_blocking(tok0, req_r, zero!>()); + + // update ID and last in state + let state = if req_valid { + State { id: req.id, last_block: req.last_block} + } else { state }; + + // send memory read request + let req = MemReaderReq { addr: req.addr, length: req.length }; + let tok2_0 = send_if(tok1_0, mem_req_s, req_valid, req); + + // receive memory read response + let (tok1_1, mem_resp, mem_resp_valid) = recv_non_blocking(tok0, mem_resp_r, zero!()); + let mem_resp_error = (mem_resp.status != MemReaderStatus::OKAY); + + // prepare output data, decoded RAW block is always a literal + let output_data = Output { + msg_type: SequenceExecutorMessageType::LITERAL, + packet: BlockDataPacket { + last: mem_resp.last, + last_block: state.last_block, + id: state.id, + data: checked_cast(mem_resp.data), + length: checked_cast(mem_resp.length ++ u3:0), + }, + }; + + // send output data + let mem_resp_correct = mem_resp_valid && !mem_resp_error; + let tok2_1 = send_if(tok1_1, output_s, mem_resp_correct, output_data); + + // send response after block end + let resp = if mem_resp_correct { + Resp { status: Status::OKAY } + } else { + Resp { status: Status::ERROR } + }; + + let do_send_resp = mem_resp_valid && mem_resp.last; + let tok2_2 = send_if(tok1_1, resp_s, do_send_resp, resp); + + state + } +} + +const INST_DATA_W = u32:32; +const INST_ADDR_W = u32:32; + +pub proc RawBlockDecoderInst { + type Req = RawBlockDecoderReq; + type Resp = RawBlockDecoderResp; + type Output = ExtendedBlockDataPacket; + + type MemReaderReq = mem_reader::MemReaderReq; + type MemReaderResp = mem_reader::MemReaderResp; + + config ( + req_r: chan in, + resp_s: chan out, + output_s: chan out, + mem_req_s: chan out, + mem_resp_r: chan in, + ) { + spawn RawBlockDecoder( + req_r, resp_s, output_s, mem_req_s, mem_resp_r + ); + } + + init { } + + next (state: ()) { } +} + +const TEST_DATA_W = u32:64; +const TEST_ADDR_W = u32:32; + +#[test_proc] +proc RawBlockDecoderTest { + type Req = RawBlockDecoderReq; + type Resp = RawBlockDecoderResp; + type Output = ExtendedBlockDataPacket; + + type MemReaderReq = mem_reader::MemReaderReq; + type MemReaderResp = mem_reader::MemReaderResp; + + type Data = uN[TEST_DATA_W]; + type Addr = uN[TEST_ADDR_W]; + type Length = uN[TEST_ADDR_W]; + + terminator: chan out; + + req_s: chan out; + resp_r: chan in; + output_r: chan in; + + mem_req_r: chan in; + mem_resp_s: chan out; + + config(terminator: chan out) { + let (req_s, req_r) = chan("req"); + let (resp_s, resp_r) = chan("resp"); + let (output_s, output_r) = chan("output"); + + let (mem_req_s, mem_req_r) = chan("mem_req"); + let (mem_resp_s, mem_resp_r) = chan("mem_resp"); + + spawn RawBlockDecoder( + req_r, resp_s, output_s, mem_req_s, mem_resp_r + ); + + (terminator, req_s, resp_r, output_r, mem_req_r, mem_resp_s) + } + + init { } + + next(state: ()) { + + let tok = join(); + + // Test 0 + let req = Req { id: u32:0, last_block: false, addr: Addr:0, length: Length:8 }; + let tok = send(tok, req_s, req); + + let (tok, mem_req) = recv(tok, mem_req_r); + assert_eq(mem_req, MemReaderReq { addr: Addr:0, length: Length:8 }); + + let mem_resp = MemReaderResp { + status: mem_reader::MemReaderStatus::OKAY, + data: Data:0x1122_3344, + length: Length:8, + last: true, + }; + let tok = send(tok, mem_resp_s, mem_resp); + let (tok, output) = recv(tok, output_r); + assert_eq(output, Output { + msg_type: SequenceExecutorMessageType::LITERAL, + packet: BlockDataPacket { + last: true, + last_block: false, + id: u32:0, + data: Data:0x1122_3344, + length: Length:64, + }, + }); + + // Test 1 + let req = Req { id: u32:1, last_block: true, addr: Addr:0x1001, length: Length:15 }; + let tok = send(tok, req_s, req); + + let (tok, mem_req) = recv(tok, mem_req_r); + assert_eq(mem_req, MemReaderReq { addr: Addr:0x1001, length: Length:15 }); + + let mem_resp = MemReaderResp { + status: mem_reader::MemReaderStatus::OKAY, + data: Data:0x1122_3344_5566_7788, + length: Length:8, + last: false + }; + let tok = send(tok, mem_resp_s, mem_resp); + + let mem_resp = MemReaderResp { + status: mem_reader::MemReaderStatus::OKAY, + data: Data:0xAA_BBCC_DDEE_FF99, + length: Length:7, + last: true, + }; + let tok = send(tok, mem_resp_s, mem_resp); + + let (tok, output) = recv(tok, output_r); + assert_eq(output, Output { + msg_type: SequenceExecutorMessageType::LITERAL, + packet: BlockDataPacket { + last: false, + last_block: true, + id: u32:1, + data: Data:0x1122_3344_5566_7788, + length: Length:64, + }, + }); + + let (tok, output) = recv(tok, output_r); + assert_eq(output, Output { + msg_type: SequenceExecutorMessageType::LITERAL, + packet: BlockDataPacket { + last: true, + last_block: true, + id: u32:1, + data: Data:0xAA_BBCC_DDEE_FF99, + length: Length:56, + }, + }); + + // Test 2 + let req = Req {id: u32:2, last_block: false, addr: Addr:0x2000, length: Length:0 }; + let tok = send(tok, req_s, req); + + let (tok, mem_req) = recv(tok, mem_req_r); + assert_eq(mem_req, MemReaderReq { addr: Addr:0x2000, length: Length:0 }); + + let mem_resp = MemReaderResp { + status: mem_reader::MemReaderStatus::OKAY, + data: Data:0x0, + length: Length:0, + last: true, + }; + let tok = send(tok, mem_resp_s, mem_resp); + let (tok, output) = recv(tok, output_r); + assert_eq(output, Output { + msg_type: SequenceExecutorMessageType::LITERAL, + packet: BlockDataPacket { + last: true, + last_block: false, + id: u32:2, + data: Data:0x0, + length: Length:0, + }, + }); + + send(tok, terminator, true); + } +} diff --git a/xls/modules/zstd/rle_block_dec_new_mem2.x b/xls/modules/zstd/rle_block_dec_new_mem2.x new file mode 100644 index 0000000000..6b3f821e65 --- /dev/null +++ b/xls/modules/zstd/rle_block_dec_new_mem2.x @@ -0,0 +1,245 @@ +// Copyright 2023 The XLS Authors +// +// 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. + +import std; + +import xls.modules.zstd.common; + + +type BlockDataPacket = common::BlockDataPacket; +type BlockPacketLength = common::BlockPacketLength; +type BlockData = common::BlockData; +type BlockSize = common::BlockSize; + +type ExtendedBlockDataPacket = common::ExtendedBlockDataPacket; +type CopyOrMatchContent = common::CopyOrMatchContent; +type CopyOrMatchLength = common::CopyOrMatchLength; +type SequenceExecutorMessageType = common::SequenceExecutorMessageType; + + +pub enum RleBlockDecoderStatus: u1 { + OKAY = 0, +} + +pub struct RleBlockDecoderReq { + id: u32, + symbol: u8, + length: BlockSize, + last_block: bool, +} + +pub struct RleBlockDecoderResp { + status: RleBlockDecoderStatus +} + +struct RleBlockDecoderState { + req: RleBlockDecoderReq, + req_valid: bool, +} + +pub proc RleBlockDecoder { + type Req = RleBlockDecoderReq; + type Resp = RleBlockDecoderResp; + type Output = ExtendedBlockDataPacket; + + type State = RleBlockDecoderState; + + req_r: chan in; + resp_s: chan out; + output_s: chan out; + + config( req_r: chan in, + resp_s: chan out, + output_s: chan out, + ) { (req_r, resp_s, output_s) } + + init { zero!() } + + next(state: State) { + const MAX_OUTPUT_SYMBOLS = (DATA_W / u32:8); + const MAX_LEN = MAX_OUTPUT_SYMBOLS as uN[common::BLOCK_SIZE_WIDTH]; + + let tok0 = join(); + + let (tok1, req) = recv_if(tok0, req_r, !state.req_valid, state.req); + + let last = req.length <= MAX_LEN; + let length = if last { req.length } else { MAX_LEN }; + let data = unroll_for! (i, data): (u32, uN[DATA_W]) in range(u32:0, MAX_OUTPUT_SYMBOLS) { + bit_slice_update(data, i * u32:8, req.symbol) + }(uN[DATA_W]:0); + + let output = Output { + msg_type: SequenceExecutorMessageType::LITERAL, + packet: BlockDataPacket { + last, + last_block: req.last_block, + id: req.id, + data: checked_cast(data), + length: checked_cast(length << 3), + } + }; + + send_if(tok1, resp_s, last, zero!()); + send(tok1, output_s, output); + + if last { + zero!() + } else { + let length = req.length - MAX_LEN; + State { + req: Req { length, ..req }, + req_valid: true, + } + } + } +} + + +const TEST_DATA_W = u32:64; + +#[test_proc] +proc RleBlockDecoderTest { + type Req = RleBlockDecoderReq; + type Resp = RleBlockDecoderResp; + type Output = ExtendedBlockDataPacket; + + type Data = uN[TEST_DATA_W]; + type Length = uN[common::BLOCK_SIZE_WIDTH]; + + terminator: chan out; + + req_s: chan out; + resp_r: chan in; + output_r: chan in; + + config (terminator: chan out) { + let (req_s, req_r) = chan("req"); + let (resp_s, resp_r) = chan("resp"); + let (output_s, output_r) = chan("output"); + + spawn RleBlockDecoder( + req_r, resp_s, output_s + ); + + (terminator, req_s, resp_r, output_r) + } + + init { } + + next (state: ()) { + let tok = join(); + + let tok = send(tok, req_s, Req { id: u32:5, symbol: u8:0xAB, length: Length:0x28, last_block: true }); + let (tok, resp) = recv(tok, resp_r); + assert_eq(resp, Resp { status: RleBlockDecoderStatus::OKAY }); + + let (tok, output) = recv(tok, output_r); + assert_eq(output, Output { + msg_type: SequenceExecutorMessageType::LITERAL, + packet: BlockDataPacket { + last: false, + last_block: true, + id: u32:5, + data: BlockData:0xABAB_ABAB_ABAB_ABAB, + length: BlockPacketLength:64 + } + }); + + let (tok, output) = recv(tok, output_r); + assert_eq(output, Output { + msg_type: SequenceExecutorMessageType::LITERAL, + packet: BlockDataPacket { + last: false, + last_block: true, + id: u32:5, + data: BlockData:0xABAB_ABAB_ABAB_ABAB, + length: BlockPacketLength:64 + } + }); + + + let (tok, output) = recv(tok, output_r); + assert_eq(output, Output { + msg_type: SequenceExecutorMessageType::LITERAL, + packet: BlockDataPacket { + last: false, + last_block: true, + id: u32:5, + data: BlockData:0xABAB_ABAB_ABAB_ABAB, + length: BlockPacketLength:64 + } + }); + + + let (tok, output) = recv(tok, output_r); + assert_eq(output, Output { + msg_type: SequenceExecutorMessageType::LITERAL, + packet: BlockDataPacket { + last: false, + last_block: true, + id: u32:5, + data: BlockData:0xABAB_ABAB_ABAB_ABAB, + length: BlockPacketLength:64 + } + }); + + + let (tok, output) = recv(tok, output_r); + assert_eq(output, Output { + msg_type: SequenceExecutorMessageType::LITERAL, + packet: BlockDataPacket { + last: true, + last_block: true, + id: u32:5, + data: BlockData:0xABAB_ABAB_ABAB_ABAB, + length: BlockPacketLength:64 + } + }); + + let tok = send(tok, req_s, Req { id: u32:1, symbol: u8:0xAB, length: Length:0, last_block: true }); + let (tok, resp) = recv(tok, resp_r); + assert_eq(resp, Resp { status: RleBlockDecoderStatus::OKAY }); + + let (tok, output) = recv(tok, output_r); + assert_eq(output, Output { + msg_type: SequenceExecutorMessageType::LITERAL, + packet: BlockDataPacket { + last: true, + last_block: true, + id: u32:1, + data: BlockData:0xABAB_ABAB_ABAB_ABAB, + length: BlockPacketLength:0 + } + }); + + let tok = send(tok, req_s, Req { id: u32:10, symbol: u8:0xAB, length: Length:0, last_block: false }); + let (tok, resp) = recv(tok, resp_r); + assert_eq(resp, Resp { status: RleBlockDecoderStatus::OKAY }); + + let (tok, output) = recv(tok, output_r); + assert_eq(output, Output { + msg_type: SequenceExecutorMessageType::LITERAL, + packet: BlockDataPacket { + last: true, + last_block: false, + id: u32:10, + data: BlockData:0xABAB_ABAB_ABAB_ABAB, + length: BlockPacketLength:0 + } + }); + + send(tok, terminator, true); + } +} diff --git a/xls/modules/zstd/xls_fifo_wrapper.v b/xls/modules/zstd/xls_fifo_wrapper.v new file mode 100644 index 0000000000..1336042b29 --- /dev/null +++ b/xls/modules/zstd/xls_fifo_wrapper.v @@ -0,0 +1,53 @@ +// simple fifo implementation +module xls_fifo_wrapper ( +clk, rst, +push_ready, push_data, push_valid, +pop_ready, pop_data, pop_valid); + parameter Width = 32, + Depth = 32, + EnableBypass = 0, + RegisterPushOutputs = 1, + RegisterPopOutputs = 1; + localparam AddrWidth = $clog2(Depth) + 1; + input wire clk; + input wire rst; + output wire push_ready; + input wire [Width-1:0] push_data; + input wire push_valid; + input wire pop_ready; + output wire [Width-1:0] pop_data; + output wire pop_valid; + + // Require depth be 1 and bypass disabled. + //initial begin + // if (EnableBypass || Depth != 1 || !RegisterPushOutputs || RegisterPopOutputs) begin + // // FIFO configuration not supported. + // $fatal(1); + // end + //end + + + reg [Width-1:0] mem; + reg full; + + assign push_ready = !full; + assign pop_valid = full; + assign pop_data = mem; + + always @(posedge clk) begin + if (rst == 1'b1) begin + full <= 1'b0; + end else begin + if (push_valid && push_ready) begin + mem <= push_data; + full <= 1'b1; + end else if (pop_valid && pop_ready) begin + mem <= mem; + full <= 1'b0; + end else begin + mem <= mem; + full <= full; + end + end + end +endmodule diff --git a/xls/modules/zstd/zstd_dec_cocotb_test.py b/xls/modules/zstd/zstd_dec_cocotb_test.py new file mode 100644 index 0000000000..365b87f45b --- /dev/null +++ b/xls/modules/zstd/zstd_dec_cocotb_test.py @@ -0,0 +1,313 @@ +#!/usr/bin/env python +# Copyright 2024 The XLS Authors +# +# 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. + + +from enum import Enum +from pathlib import Path +import tempfile + +import cocotb +from cocotb.clock import Clock +from cocotb.triggers import ClockCycles, Event +from cocotb.binary import BinaryValue +from cocotb_bus.scoreboard import Scoreboard + +from cocotbext.axi.axi_master import AxiMaster +from cocotbext.axi.axi_channels import AxiAWBus, AxiWBus, AxiBBus, AxiWriteBus, AxiARBus, AxiRBus, AxiReadBus, AxiBus, AxiBTransaction, AxiBSource, AxiBSink, AxiBMonitor, AxiRTransaction, AxiRSource, AxiRSink, AxiRMonitor +from cocotbext.axi.axi_ram import AxiRam +from cocotbext.axi.sparse_memory import SparseMemory + +import zstandard + +from xls.common import runfiles +from xls.modules.zstd.cocotb.channel import ( + XLSChannel, + XLSChannelDriver, + XLSChannelMonitor, +) +from xls.modules.zstd.cocotb.data_generator import GenerateFrame, BlockType +from xls.modules.zstd.cocotb.memory import init_axi_mem, AxiRamFromFile +from xls.modules.zstd.cocotb.utils import reset, run_test +from xls.modules.zstd.cocotb.xlsstruct import XLSStruct, xls_dataclass + +MAX_ENCODED_FRAME_SIZE_B = 16384 + +# Override default widths of AXI response signals +signal_widths = {"bresp": 3} +AxiBBus._signal_widths = signal_widths +AxiBTransaction._signal_widths = signal_widths +AxiBSource._signal_widths = signal_widths +AxiBSink._signal_widths = signal_widths +AxiBMonitor._signal_widths = signal_widths +signal_widths = {"rresp": 3, "rlast": 1} +AxiRBus._signal_widths = signal_widths +AxiRTransaction._signal_widths = signal_widths +AxiRSource._signal_widths = signal_widths +AxiRSink._signal_widths = signal_widths +AxiRMonitor._signal_widths = signal_widths + +@xls_dataclass +class NotifyStruct(XLSStruct): + pass + +@xls_dataclass +class OutputStruct(XLSStruct): + data: 64 + length: 32 + last: 1 + +class CSR(Enum): + """ + Maps the offsets to the ZSTD Decoder registers + """ + Status = 0x0 + Start = 0x4 + Reset = 0x8 + InputBuffer = 0xC + OutputBuffer = 0x10 + WhoAmI = 0x14 + +class Status(Enum): + """ + Codes for the Status register + """ + IDLE = 0x0 + RUNNING = 0x1 + +def set_termination_event(monitor, event, transactions): + def terminate_cb(_): + if monitor.stats.received_transactions == transactions: + event.set() + monitor.add_callback(terminate_cb) + +def connect_axi_read_bus(dut, name=""): + AXI_AR = "axi_ar" + AXI_R = "axi_r" + + if name != "": + name += "_" + + bus_axi_ar = AxiARBus.from_prefix(dut, name + AXI_AR) + bus_axi_r = AxiRBus.from_prefix(dut, name + AXI_R) + + return AxiReadBus(bus_axi_ar, bus_axi_r) + +def connect_axi_write_bus(dut, name=""): + AXI_AW = "axi_aw" + AXI_W = "axi_w" + AXI_B = "axi_b" + + if name != "": + name += "_" + + bus_axi_aw = AxiAWBus.from_prefix(dut, name + AXI_AW) + bus_axi_w = AxiWBus.from_prefix(dut, name + AXI_W) + bus_axi_b = AxiBBus.from_prefix(dut, name + AXI_B) + + return AxiWriteBus(bus_axi_aw, bus_axi_w, bus_axi_b) + +def connect_axi_bus(dut, name=""): + bus_axi_read = connect_axi_read_bus(dut, name) + bus_axi_write = connect_axi_write_bus(dut, name) + + return AxiBus(bus_axi_write, bus_axi_read) + +def get_decoded_frame_bytes(ifh): + dctx = zstandard.ZstdDecompressor() + return dctx.decompress(ifh.read()) + +def get_decoded_frame_buffer(ifh, address, memory=SparseMemory(size=MAX_ENCODED_FRAME_SIZE_B)): + dctx = zstandard.ZstdDecompressor() + memory.write(address, dctx.decompress(ifh.read())) + return memory + +async def csr_write(cpu, csr, data): + if type(data) is int: + data = data.to_bytes(4, byteorder='little') + assert len(data) <= 4 + await cpu.write(csr.value, data) + +async def csr_read(cpu, csr): + return await cpu.read(csr.value, 4) + +async def test_csr(dut): + + clock = Clock(dut.clk, 10, units="us") + cocotb.start_soon(clock.start()) + + dut.rst.setimmediatevalue(0) + await ClockCycles(dut.clk, 20) + dut.rst.setimmediatevalue(1) + await ClockCycles(dut.clk, 20) + dut.rst.setimmediatevalue(0) + + csr_bus = connect_axi_bus(dut, "csr") + + cpu = AxiMaster(csr_bus, dut.clk, dut.rst) + + await ClockCycles(dut.clk, 10) + i = 0 + for reg in CSR: + expected = bytearray.fromhex("EFBEADDE") + expected[0] += i + await csr_write(cpu, reg, expected) + read = await csr_read(cpu, reg) + assert read.data == expected + i += 1 + await ClockCycles(dut.clk, 10) + +async def configure_decoder(cpu, ibuf_addr, obuf_addr): + status = await csr_read(cpu, CSR.Status) + if int.from_bytes(status.data, byteorder='little') != Status.IDLE.value: + await csr_write(cpu, CSR.Reset, 0x1) + await csr_write(cpu, CSR.InputBuffer, ibuf_addr) + await csr_write(cpu, CSR.OutputBuffer, obuf_addr) + +async def start_decoder(cpu): + await csr_write(cpu, CSR.Start, 0x1) + +async def wait_for_idle(cpu): + status = await csr_read(cpu, CSR.Status) + while (int.from_bytes(status.data, byteorder='little') != Status.IDLE.value): + status = await csr_read(cpu, CSR.Status) + +async def mock_decoder(dut, memory, mem_bus, csr, csr_bus, encoded_frame_path, obuf_addr): + MOCK_NOTIFY_CHANNEL = "notify" + driver_notify = XLSChannelDriver(dut, MOCK_NOTIFY_CHANNEL, dut.clk) + start = int.from_bytes(csr.read(CSR.Start.value, 4), byteorder='little') + status = int.from_bytes(csr.read(CSR.Status.value, 4), byteorder='little') + while ((start != 0x1) | (status != Status.IDLE.value)): + start = int.from_bytes(csr.read(CSR.Start.value, 4), byteorder='little') + status = int.from_bytes(csr.read(CSR.Status.value, 4), byteorder='little') + csr.write_dword(CSR.Start.value, 0x0) + csr.write_dword(CSR.Status.value, Status.RUNNING.value) + with open(encoded_frame_path, "rb") as encoded_frame_fh: + get_decoded_frame_buffer(encoded_frame_fh, obuf_addr, memory) + memory.hexdump(obuf_addr, memory.size-obuf_addr) + csr.write_dword(CSR.Status.value, Status.IDLE.value) + #TODO: signal finished decoding on the IRQ line + await driver_notify.send(NotifyStruct()) + +def generate_expected_output(decoded_frame): + packets = [] + frame_len = len(decoded_frame) + last_len = frame_len % 8 + for i in range(frame_len // 8): + start_id = i * 8 + end_id = start_id + 8 + packet_data = int.from_bytes(decoded_frame[start_id:end_id], byteorder='little') + last_packet = (end_id==(frame_len-1)) + packet = OutputStruct(data=packet_data, length=64, last=last_packet) + packets.append(packet) + if (last_len): + packet_data = int.from_bytes(decoded_frame[-last_len:], byteorder='little') + packet = OutputStruct(data=packet_data, length=last_len*8, last=True) + packets.append(packet) + return packets + +async def test_decoder(dut, test_cases, block_type): + NOTIFY_CHANNEL = "notify" + OUTPUT_CHANNEL = "output" + + clock = Clock(dut.clk, 10, units="us") + cocotb.start_soon(clock.start()) + + dut.rst.setimmediatevalue(0) + await ClockCycles(dut.clk, 20) + dut.rst.setimmediatevalue(1) + await ClockCycles(dut.clk, 20) + dut.rst.setimmediatevalue(0) + + memory_bus = connect_axi_bus(dut, "memory") + csr_bus = connect_axi_bus(dut, "csr") + notify_channel = XLSChannel(dut, NOTIFY_CHANNEL, dut.clk, start_now=True) + monitor_notify = XLSChannelMonitor(dut, NOTIFY_CHANNEL, dut.clk, NotifyStruct) + + output_channel = XLSChannel(dut, OUTPUT_CHANNEL, dut.clk, start_now=True) + monitor_output = XLSChannelMonitor(dut, OUTPUT_CHANNEL, dut.clk, OutputStruct) + + scoreboard = Scoreboard(dut) + + terminate = Event() + set_termination_event(monitor_notify, terminate, 1) + + cpu = AxiMaster(csr_bus, dut.clk, dut.rst) + + for i in range(test_cases): + #FIXME: use delete_on_close=False after moving to python 3.12 + with tempfile.NamedTemporaryFile(delete=False) as encoded: + mem_size = MAX_ENCODED_FRAME_SIZE_B + ibuf_addr = 0x0 + obuf_addr = mem_size // 2 + GenerateFrame(i+1, block_type, encoded.name) + expected_decoded_frame = get_decoded_frame_bytes(encoded) + print("Expected decoded frame: {}".format(expected_decoded_frame)) + expected_output_packets = generate_expected_output(expected_decoded_frame) + print("Expected output: {}".format(expected_output_packets)) + scoreboard.add_interface(monitor_output, expected_output_packets) + encoded.close() + memory = AxiRamFromFile(bus=memory_bus, clock=dut.clk, reset=dut.rst, path=encoded.name, size=mem_size) + memory.hexdump(0, mem_size) + await configure_decoder(cpu, ibuf_addr, obuf_addr) + await start_decoder(cpu) + ##await mock_decoder(dut, memory, memory_bus, csr, csr_bus, encoded.name, obuf_addr) + #await terminate.wait() + #await wait_for_idle(cpu) + #decoded_frame = memory.read(obuf_addr, memory.size-obuf_addr) + #assert decoded_frame == expected_decoded_frame + + await ClockCycles(dut.clk, 5000) + +@cocotb.test(timeout_time=50, timeout_unit="ms") +async def zstd_csr_test(dut): + await test_csr(dut) + +@cocotb.test(timeout_time=20000, timeout_unit="ms") +async def zstd_raw_frames_test(dut): + test_cases = 1 + block_type = BlockType.RAW + await test_decoder(dut, test_cases, block_type) + +@cocotb.test(timeout_time=50000, timeout_unit="ms") +async def zstd_rle_frames_test(dut): + test_cases = 1 + block_type = BlockType.RLE + await test_decoder(dut, test_cases, block_type) + +#@cocotb.test(timeout_time=20000, timeout_unit="ms") +#async def zstd_compressed_frames_test(dut): +# test_cases = 1 +# block_type = BlockType.COMPRESSED +# await test_decoder(dut, test_cases, block_type) +# +#@cocotb.test(timeout_time=20000, timeout_unit="ms") +#async def zstd_random_frames_test(dut): +# test_cases = 1 +# block_type = BlockType.RANDOM +# await test_decoder(dut, test_cases, block_type) + +if __name__ == "__main__": + toplevel = "zstd_dec_wrapper" + verilog_sources = [ + "xls/modules/zstd/dec.v", + "xls/modules/zstd/xls_fifo_wrapper.v", + "xls/modules/zstd/zstd_dec_wrapper.v", + "xls/modules/zstd/axi_interconnect_wrapper.v", + "xls/modules/zstd/axi_interconnect.v", + "xls/modules/zstd/arbiter.v", + "xls/modules/zstd/priority_encoder.v", + ] + test_module=[Path(__file__).stem] + run_test(toplevel, test_module, verilog_sources) diff --git a/xls/modules/zstd/zstd_dec_wrapper.v b/xls/modules/zstd/zstd_dec_wrapper.v new file mode 100644 index 0000000000..a1239fabeb --- /dev/null +++ b/xls/modules/zstd/zstd_dec_wrapper.v @@ -0,0 +1,784 @@ +// Copyright 2024 The XLS Authors +// +// 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. + +`default_nettype none + +module zstd_dec_wrapper #( + parameter AXI_DATA_W = 32, + parameter AXI_ADDR_W = 16, + parameter AXI_ID_W = 4, + parameter AXI_STRB_W = 4, + parameter AWUSER_WIDTH = 1, + parameter WUSER_WIDTH = 1, + parameter BUSER_WIDTH = 1, + parameter ARUSER_WIDTH = 1, + parameter RUSER_WIDTH = 1, + parameter OUTPUT_WIDTH = 97 +) ( + input wire clk, + input wire rst, + + // AXI Master interface for the memory connection + output wire [AXI_ID_W-1:0] memory_axi_aw_awid, + output wire [AXI_ADDR_W-1:0] memory_axi_aw_awaddr, + output wire [7:0] memory_axi_aw_awlen, + output wire [2:0] memory_axi_aw_awsize, + output wire [1:0] memory_axi_aw_awburst, + output wire memory_axi_aw_awlock, + output wire [3:0] memory_axi_aw_awcache, + output wire [2:0] memory_axi_aw_awprot, + output wire [3:0] memory_axi_aw_awqos, + output wire [3:0] memory_axi_aw_awregion, + output wire [AWUSER_WIDTH-1:0] memory_axi_aw_awuser, + output wire memory_axi_aw_awvalid, + input wire memory_axi_aw_awready, + output wire [AXI_DATA_W-1:0] memory_axi_w_wdata, + output wire [AXI_STRB_W-1:0] memory_axi_w_wstrb, + output wire memory_axi_w_wlast, + output wire [WUSER_WIDTH-1:0] memory_axi_w_wuser, + output wire memory_axi_w_wvalid, + input wire memory_axi_w_wready, + input wire [AXI_ID_W-1:0] memory_axi_b_bid, + input wire [2:0] memory_axi_b_bresp, + input wire [BUSER_WIDTH-1:0] memory_axi_b_buser, + input wire memory_axi_b_bvalid, + output wire memory_axi_b_bready, + output wire [AXI_ID_W-1:0] memory_axi_ar_arid, + output wire [AXI_ADDR_W-1:0] memory_axi_ar_araddr, + output wire [7:0] memory_axi_ar_arlen, + output wire [2:0] memory_axi_ar_arsize, + output wire [1:0] memory_axi_ar_arburst, + output wire memory_axi_ar_arlock, + output wire [3:0] memory_axi_ar_arcache, + output wire [2:0] memory_axi_ar_arprot, + output wire [3:0] memory_axi_ar_arqos, + output wire [3:0] memory_axi_ar_arregion, + output wire [ARUSER_WIDTH-1:0] memory_axi_ar_aruser, + output wire memory_axi_ar_arvalid, + input wire memory_axi_ar_arready, + input wire [AXI_ID_W-1:0] memory_axi_r_rid, + input wire [AXI_DATA_W-1:0] memory_axi_r_rdata, + input wire [2:0] memory_axi_r_rresp, + input wire memory_axi_r_rlast, + input wire [RUSER_WIDTH-1:0] memory_axi_r_ruser, + input wire memory_axi_r_rvalid, + output wire memory_axi_r_rready, + + // AXI Slave interface for the CSR access + input wire [AXI_ID_W-1:0] csr_axi_aw_awid, + input wire [AXI_ADDR_W-1:0] csr_axi_aw_awaddr, + input wire [7:0] csr_axi_aw_awlen, + input wire [2:0] csr_axi_aw_awsize, + input wire [1:0] csr_axi_aw_awburst, + input wire csr_axi_aw_awlock, + input wire [3:0] csr_axi_aw_awcache, + input wire [2:0] csr_axi_aw_awprot, + input wire [3:0] csr_axi_aw_awqos, + input wire [3:0] csr_axi_aw_awregion, + input wire [AWUSER_WIDTH-1:0] csr_axi_aw_awuser, + input wire csr_axi_aw_awvalid, + output wire csr_axi_aw_awready, + input wire [AXI_DATA_W-1:0] csr_axi_w_wdata, + input wire [AXI_STRB_W-1:0] csr_axi_w_wstrb, + input wire csr_axi_w_wlast, + input wire [WUSER_WIDTH-1:0] csr_axi_w_wuser, + input wire csr_axi_w_wvalid, + output wire csr_axi_w_wready, + output wire [AXI_ID_W-1:0] csr_axi_b_bid, + output wire [2:0] csr_axi_b_bresp, + output wire [BUSER_WIDTH-1:0] csr_axi_b_buser, + output wire csr_axi_b_bvalid, + input wire csr_axi_b_bready, + input wire [AXI_ID_W-1:0] csr_axi_ar_arid, + input wire [AXI_ADDR_W-1:0] csr_axi_ar_araddr, + input wire [7:0] csr_axi_ar_arlen, + input wire [2:0] csr_axi_ar_arsize, + input wire [1:0] csr_axi_ar_arburst, + input wire csr_axi_ar_arlock, + input wire [3:0] csr_axi_ar_arcache, + input wire [2:0] csr_axi_ar_arprot, + input wire [3:0] csr_axi_ar_arqos, + input wire [3:0] csr_axi_ar_arregion, + input wire [ARUSER_WIDTH-1:0] csr_axi_ar_aruser, + input wire csr_axi_ar_arvalid, + output wire csr_axi_ar_arready, + output wire [AXI_ID_W-1:0] csr_axi_r_rid, + output wire [AXI_DATA_W-1:0] csr_axi_r_rdata, + output wire [2:0] csr_axi_r_rresp, + output wire csr_axi_r_rlast, + output wire [RUSER_WIDTH-1:0] csr_axi_r_ruser, + output wire csr_axi_r_rvalid, + input wire csr_axi_r_rready, + + output wire notify_data, + output wire notify_vld, + input wire notify_rdy, + + output wire [OUTPUT_WIDTH-1:0] output_data, + output wire output_vld, + input wire output_rdy +); + + /* + * MemReader AXI interfaces + */ + // RawBlockDecoder + wire raw_block_decoder_axi_ar_arvalid; + wire raw_block_decoder_axi_ar_arready; + wire [ AXI_ID_W-1:0] raw_block_decoder_axi_ar_arid; + wire [AXI_ADDR_W-1:0] raw_block_decoder_axi_ar_araddr; + wire [ 3:0] raw_block_decoder_axi_ar_arregion; + wire [ 7:0] raw_block_decoder_axi_ar_arlen; + wire [ 2:0] raw_block_decoder_axi_ar_arsize; + wire [ 1:0] raw_block_decoder_axi_ar_arburst; + wire [ 3:0] raw_block_decoder_axi_ar_arcache; + wire [ 2:0] raw_block_decoder_axi_ar_arprot; + wire [ 3:0] raw_block_decoder_axi_ar_arqos; + + wire raw_block_decoder_axi_r_rvalid; + wire raw_block_decoder_axi_r_rready; + wire [ AXI_ID_W-1:0] raw_block_decoder_axi_r_rid; + wire [AXI_DATA_W-1:0] raw_block_decoder_axi_r_rdata; + wire [ 2:0] raw_block_decoder_axi_r_rresp; + wire raw_block_decoder_axi_r_rlast; + + + // BlockHeaderDecoder + wire block_header_decoder_axi_ar_arvalid; + wire block_header_decoder_axi_ar_arready; + wire [ AXI_ID_W-1:0] block_header_decoder_axi_ar_arid; + wire [AXI_ADDR_W-1:0] block_header_decoder_axi_ar_araddr; + wire [ 3:0] block_header_decoder_axi_ar_arregion; + wire [ 7:0] block_header_decoder_axi_ar_arlen; + wire [ 2:0] block_header_decoder_axi_ar_arsize; + wire [ 1:0] block_header_decoder_axi_ar_arburst; + wire [ 3:0] block_header_decoder_axi_ar_arcache; + wire [ 2:0] block_header_decoder_axi_ar_arprot; + wire [ 3:0] block_header_decoder_axi_ar_arqos; + + wire block_header_decoder_axi_r_rvalid; + wire block_header_decoder_axi_r_rready; + wire [ AXI_ID_W-1:0] block_header_decoder_axi_r_rid; + wire [AXI_DATA_W-1:0] block_header_decoder_axi_r_rdata; + wire [ 2:0] block_header_decoder_axi_r_rresp; + wire block_header_decoder_axi_r_rlast; + + + // FrameHeaderDecoder + wire frame_header_decoder_axi_ar_arvalid; + wire frame_header_decoder_axi_ar_arready; + wire [ AXI_ID_W-1:0] frame_header_decoder_axi_ar_arid; + wire [AXI_ADDR_W-1:0] frame_header_decoder_axi_ar_araddr; + wire [ 3:0] frame_header_decoder_axi_ar_arregion; + wire [ 7:0] frame_header_decoder_axi_ar_arlen; + wire [ 2:0] frame_header_decoder_axi_ar_arsize; + wire [ 1:0] frame_header_decoder_axi_ar_arburst; + wire [ 3:0] frame_header_decoder_axi_ar_arcache; + wire [ 2:0] frame_header_decoder_axi_ar_arprot; + wire [ 3:0] frame_header_decoder_axi_ar_arqos; + + wire frame_header_decoder_axi_r_rvalid; + wire frame_header_decoder_axi_r_rready; + wire [ AXI_ID_W-1:0] frame_header_decoder_axi_r_rid; + wire [AXI_DATA_W-1:0] frame_header_decoder_axi_r_rdata; + wire [ 2:0] frame_header_decoder_axi_r_rresp; + wire frame_header_decoder_axi_r_rlast; + + + /* + * MemWriter AXI interfaces + */ + + // SequenceExecutor + wire [ AXI_ID_W-1:0] sequence_executor_axi_aw_awid; + wire [AXI_ADDR_W-1:0] sequence_executor_axi_aw_awaddr; + wire [ 2:0] sequence_executor_axi_aw_awsize; + wire [ 7:0] sequence_executor_axi_aw_awlen; + wire [ 1:0] sequence_executor_axi_aw_awburst; + wire sequence_executor_axi_aw_awvalid; + wire sequence_executor_axi_aw_awready; + + wire [AXI_DATA_W-1:0] sequence_executor_axi_w_wdata; + wire [AXI_STRB_W-1:0] sequence_executor_axi_w_wstrb; + wire sequence_executor_axi_w_wlast; + wire sequence_executor_axi_w_wvalid; + wire sequence_executor_axi_w_wready; + + wire [ AXI_ID_W-1:0] sequence_executor_axi_b_bid; + wire [ 2:0] sequence_executor_axi_b_bresp; + wire sequence_executor_axi_b_bvalid; + wire sequence_executor_axi_b_bready; + + /* + * XLS Channels representing AXI interfaces + */ + + // CSR + wire [32:0] dec__csr_axi_aw; + wire dec__csr_axi_aw_rdy; + wire dec__csr_axi_aw_vld; + wire [36:0] dec__csr_axi_w; + wire dec__csr_axi_w_rdy; + wire dec__csr_axi_w_vld; + wire [6:0] dec__csr_axi_b; + wire dec__csr_axi_b_rdy; + wire dec__csr_axi_b_vld; + wire [47:0] dec__csr_axi_ar; + wire dec__csr_axi_ar_rdy; + wire dec__csr_axi_ar_vld; + wire [39:0] dec__csr_axi_r; + wire dec__csr_axi_r_rdy; + wire dec__csr_axi_r_vld; + + // Frame Header Decoder + wire [47:0] dec__fh_axi_ar; + wire dec__fh_axi_ar_rdy; + wire dec__fh_axi_ar_vld; + wire [39:0] dec__fh_axi_r; + wire dec__fh_axi_r_rdy; + wire dec__fh_axi_r_vld; + + // Block Header Decoder + wire [47:0] dec__bh_axi_ar; + wire dec__bh_axi_ar_rdy; + wire dec__bh_axi_ar_vld; + wire [39:0] dec__bh_axi_r; + wire dec__bh_axi_r_rdy; + wire dec__bh_axi_r_vld; + + // Raw Block Decoder + wire [47:0] dec__raw_axi_ar; + wire dec__raw_axi_ar_rdy; + wire dec__raw_axi_ar_vld; + wire [39:0] dec__raw_axi_r; + wire dec__raw_axi_r_rdy; + wire dec__raw_axi_r_vld; + + /* + * Mapping XLS Channels to AXI channels fields + */ + + // CSR + assign dec__csr_axi_aw = { + csr_axi_aw_awid, + csr_axi_aw_awaddr, + csr_axi_aw_awsize, + csr_axi_aw_awlen, + csr_axi_aw_awburst + }; + assign dec__csr_axi_aw_vld = csr_axi_aw_awvalid; + assign csr_axi_aw_awready = dec__csr_axi_aw_rdy; + assign dec__csr_axi_w = { + csr_axi_w_wdata, + csr_axi_w_wstrb, + csr_axi_w_wlast + }; + assign dec__csr_axi_w_vld = csr_axi_w_wvalid; + assign csr_axi_w_wready = dec__csr_axi_w_rdy; + assign { + csr_axi_b_bresp, + csr_axi_b_bid + } = dec__csr_axi_b; + assign csr_axi_b_bvalid = dec__csr_axi_b_vld; + assign dec__csr_axi_b_rdy = csr_axi_b_bready; + assign dec__csr_axi_ar = { + csr_axi_ar_arid, + csr_axi_ar_araddr, + csr_axi_ar_arregion, + csr_axi_ar_arlen, + csr_axi_ar_arsize, + csr_axi_ar_arburst, + csr_axi_ar_arcache, + csr_axi_ar_arprot, + csr_axi_ar_arqos + }; + assign dec__csr_axi_ar_vld = csr_axi_ar_arvalid; + assign csr_axi_ar_arready = dec__csr_axi_ar_rdy; + assign { + csr_axi_r_rid, + csr_axi_r_rdata, + csr_axi_r_rresp, + csr_axi_r_rlast + } = dec__csr_axi_r; + assign csr_axi_r_rvalid = dec__csr_axi_r_vld; + assign dec__csr_axi_r_rdy = csr_axi_r_rready; + + // Frame Header Decoder + assign { + frame_header_decoder_axi_ar_arid, + frame_header_decoder_axi_ar_araddr, + frame_header_decoder_axi_ar_arregion, + frame_header_decoder_axi_ar_arlen, + frame_header_decoder_axi_ar_arsize, + frame_header_decoder_axi_ar_arburst, + frame_header_decoder_axi_ar_arcache, + frame_header_decoder_axi_ar_arprot, + frame_header_decoder_axi_ar_arqos + } = dec__fh_axi_ar; + assign frame_header_decoder_axi_ar_arvalid = dec__fh_axi_ar_vld; + assign dec__fh_axi_ar_rdy = frame_header_decoder_axi_ar_arready; + assign dec__fh_axi_r = { + frame_header_decoder_axi_r_rid, + frame_header_decoder_axi_r_rdata, + frame_header_decoder_axi_r_rresp, + frame_header_decoder_axi_r_rlast}; + assign dec__fh_axi_r_vld = frame_header_decoder_axi_r_rvalid; + assign frame_header_decoder_axi_r_rready = dec__fh_axi_r_rdy; + + // Block Header Decoder + assign { + block_header_decoder_axi_ar_arid, + block_header_decoder_axi_ar_araddr, + block_header_decoder_axi_ar_arregion, + block_header_decoder_axi_ar_arlen, + block_header_decoder_axi_ar_arsize, + block_header_decoder_axi_ar_arburst, + block_header_decoder_axi_ar_arcache, + block_header_decoder_axi_ar_arprot, + block_header_decoder_axi_ar_arqos + } = dec__bh_axi_ar; + assign block_header_decoder_axi_ar_arvalid = dec__bh_axi_ar_vld; + assign dec__bh_axi_ar_rdy = block_header_decoder_axi_ar_arready; + assign dec__bh_axi_r = { + block_header_decoder_axi_r_rid, + block_header_decoder_axi_r_rdata, + block_header_decoder_axi_r_rresp, + block_header_decoder_axi_r_rlast}; + assign dec__bh_axi_r_vld = block_header_decoder_axi_r_rvalid; + assign block_header_decoder_axi_r_rready = dec__bh_axi_r_rdy; + + // Raw Block Decoder + assign { + raw_block_decoder_axi_ar_arid, + raw_block_decoder_axi_ar_araddr, + raw_block_decoder_axi_ar_arregion, + raw_block_decoder_axi_ar_arlen, + raw_block_decoder_axi_ar_arsize, + raw_block_decoder_axi_ar_arburst, + raw_block_decoder_axi_ar_arcache, + raw_block_decoder_axi_ar_arprot, + raw_block_decoder_axi_ar_arqos + } = dec__raw_axi_ar; + assign raw_block_decoder_axi_ar_arvalid = dec__raw_axi_ar_vld; + assign dec__raw_axi_ar_rdy = raw_block_decoder_axi_ar_arready; + assign dec__raw_axi_r = { + raw_block_decoder_axi_r_rid, + raw_block_decoder_axi_r_rdata, + raw_block_decoder_axi_r_rresp, + raw_block_decoder_axi_r_rlast}; + assign dec__raw_axi_r_vld = raw_block_decoder_axi_r_rvalid; + assign raw_block_decoder_axi_r_rready = dec__raw_axi_r_rdy; + +assign csr_axi_b_buser = 1'b0; +assign csr_axi_r_ruser = 1'b0; +assign notify_data = notify_vld; + + /* + * ZSTD Decoder instance + */ + ZstdDecoder ZstdDecoder ( + .clk(clk), + .rst(rst), + + // CSR Interface + .dec__csr_axi_aw_r(dec__csr_axi_aw), + .dec__csr_axi_aw_r_vld(dec__csr_axi_aw_vld), + .dec__csr_axi_aw_r_rdy(dec__csr_axi_aw_rdy), + .dec__csr_axi_w_r(dec__csr_axi_w), + .dec__csr_axi_w_r_vld(dec__csr_axi_w_vld), + .dec__csr_axi_w_r_rdy(dec__csr_axi_w_rdy), + .dec__csr_axi_b_s(dec__csr_axi_b), + .dec__csr_axi_b_s_vld(dec__csr_axi_b_vld), + .dec__csr_axi_b_s_rdy(dec__csr_axi_b_rdy), + .dec__csr_axi_ar_r(dec__csr_axi_ar), + .dec__csr_axi_ar_r_vld(dec__csr_axi_ar_vld), + .dec__csr_axi_ar_r_rdy(dec__csr_axi_ar_rdy), + .dec__csr_axi_r_s(dec__csr_axi_r), + .dec__csr_axi_r_s_vld(dec__csr_axi_r_vld), + .dec__csr_axi_r_s_rdy(dec__csr_axi_r_rdy), + + // FrameHeaderDecoder + .dec__fh_axi_ar_s(dec__fh_axi_ar), + .dec__fh_axi_ar_s_vld(dec__fh_axi_ar_vld), + .dec__fh_axi_ar_s_rdy(dec__fh_axi_ar_rdy), + .dec__fh_axi_r_r(dec__fh_axi_r), + .dec__fh_axi_r_r_vld(dec__fh_axi_r_vld), + .dec__fh_axi_r_r_rdy(dec__fh_axi_r_rdy), + + // BlockHeaderDecoder + .dec__bh_axi_ar_s(dec__bh_axi_ar), + .dec__bh_axi_ar_s_vld(dec__bh_axi_ar_vld), + .dec__bh_axi_ar_s_rdy(dec__bh_axi_ar_rdy), + .dec__bh_axi_r_r(dec__bh_axi_r), + .dec__bh_axi_r_r_vld(dec__bh_axi_r_vld), + .dec__bh_axi_r_r_rdy(dec__bh_axi_r_rdy), + + // RawBlockDecoder + .dec__raw_axi_ar_s(dec__raw_axi_ar), + .dec__raw_axi_ar_s_vld(dec__raw_axi_ar_vld), + .dec__raw_axi_ar_s_rdy(dec__raw_axi_ar_rdy), + .dec__raw_axi_r_r(dec__raw_axi_r), + .dec__raw_axi_r_r_vld(dec__raw_axi_r_vld), + .dec__raw_axi_r_r_rdy(dec__raw_axi_r_rdy), + + // Other ports + .dec__notify_s_vld(notify_vld), + .dec__notify_s_rdy(notify_rdy), + .dec__output_s(output_data), + .dec__output_s_vld(output_vld), + .dec__output_s_rdy(output_rdy), + + .dec__ram_rd_req_0_s(), + .dec__ram_rd_req_1_s(), + .dec__ram_rd_req_2_s(), + .dec__ram_rd_req_3_s(), + .dec__ram_rd_req_4_s(), + .dec__ram_rd_req_5_s(), + .dec__ram_rd_req_6_s(), + .dec__ram_rd_req_7_s(), + .dec__ram_rd_req_0_s_vld(), + .dec__ram_rd_req_1_s_vld(), + .dec__ram_rd_req_2_s_vld(), + .dec__ram_rd_req_3_s_vld(), + .dec__ram_rd_req_4_s_vld(), + .dec__ram_rd_req_5_s_vld(), + .dec__ram_rd_req_6_s_vld(), + .dec__ram_rd_req_7_s_vld(), + .dec__ram_rd_req_0_s_rdy('1), + .dec__ram_rd_req_1_s_rdy('1), + .dec__ram_rd_req_2_s_rdy('1), + .dec__ram_rd_req_3_s_rdy('1), + .dec__ram_rd_req_4_s_rdy('1), + .dec__ram_rd_req_5_s_rdy('1), + .dec__ram_rd_req_6_s_rdy('1), + .dec__ram_rd_req_7_s_rdy('1), + + .dec__ram_rd_resp_0_r('0), + .dec__ram_rd_resp_1_r('0), + .dec__ram_rd_resp_2_r('0), + .dec__ram_rd_resp_3_r('0), + .dec__ram_rd_resp_4_r('0), + .dec__ram_rd_resp_5_r('0), + .dec__ram_rd_resp_6_r('0), + .dec__ram_rd_resp_7_r('0), + .dec__ram_rd_resp_0_r_vld('1), + .dec__ram_rd_resp_1_r_vld('1), + .dec__ram_rd_resp_2_r_vld('1), + .dec__ram_rd_resp_3_r_vld('1), + .dec__ram_rd_resp_4_r_vld('1), + .dec__ram_rd_resp_5_r_vld('1), + .dec__ram_rd_resp_6_r_vld('1), + .dec__ram_rd_resp_7_r_vld('1), + .dec__ram_rd_resp_0_r_rdy(), + .dec__ram_rd_resp_1_r_rdy(), + .dec__ram_rd_resp_2_r_rdy(), + .dec__ram_rd_resp_3_r_rdy(), + .dec__ram_rd_resp_4_r_rdy(), + .dec__ram_rd_resp_5_r_rdy(), + .dec__ram_rd_resp_6_r_rdy(), + .dec__ram_rd_resp_7_r_rdy(), + + .dec__ram_wr_req_0_s(), + .dec__ram_wr_req_1_s(), + .dec__ram_wr_req_2_s(), + .dec__ram_wr_req_3_s(), + .dec__ram_wr_req_4_s(), + .dec__ram_wr_req_5_s(), + .dec__ram_wr_req_6_s(), + .dec__ram_wr_req_7_s(), + .dec__ram_wr_req_0_s_vld(), + .dec__ram_wr_req_1_s_vld(), + .dec__ram_wr_req_2_s_vld(), + .dec__ram_wr_req_3_s_vld(), + .dec__ram_wr_req_4_s_vld(), + .dec__ram_wr_req_5_s_vld(), + .dec__ram_wr_req_6_s_vld(), + .dec__ram_wr_req_7_s_vld(), + .dec__ram_wr_req_0_s_rdy('1), + .dec__ram_wr_req_1_s_rdy('1), + .dec__ram_wr_req_2_s_rdy('1), + .dec__ram_wr_req_3_s_rdy('1), + .dec__ram_wr_req_4_s_rdy('1), + .dec__ram_wr_req_5_s_rdy('1), + .dec__ram_wr_req_6_s_rdy('1), + .dec__ram_wr_req_7_s_rdy('1), + + .dec__ram_wr_resp_0_r_vld('1), + .dec__ram_wr_resp_1_r_vld('1), + .dec__ram_wr_resp_2_r_vld('1), + .dec__ram_wr_resp_3_r_vld('1), + .dec__ram_wr_resp_4_r_vld('1), + .dec__ram_wr_resp_5_r_vld('1), + .dec__ram_wr_resp_6_r_vld('1), + .dec__ram_wr_resp_7_r_vld('1), + .dec__ram_wr_resp_0_r_rdy(), + .dec__ram_wr_resp_1_r_rdy(), + .dec__ram_wr_resp_2_r_rdy(), + .dec__ram_wr_resp_3_r_rdy(), + .dec__ram_wr_resp_4_r_rdy(), + .dec__ram_wr_resp_5_r_rdy(), + .dec__ram_wr_resp_6_r_rdy(), + .dec__ram_wr_resp_7_r_rdy() + ); + + assign frame_header_decoder_axi_r_rresp[2] = '0; + assign block_header_decoder_axi_r_rresp[2] = '0; + assign raw_block_decoder_axi_r_rresp[2] = '0; + assign sequence_executor_axi_b_bresp[2] = '0; + assign memory_axi_b_bresp[2] = '0; + assign memory_axi_r_rresp[2] = '0; + /* + * AXI Interconnect + */ + axi_interconnect_wrapper #( + .DATA_WIDTH(AXI_DATA_W), + .ADDR_WIDTH(AXI_ADDR_W), + .M00_ADDR_WIDTH(AXI_ADDR_W), + .M00_BASE_ADDR(32'd0), + .STRB_WIDTH(AXI_STRB_W), + .ID_WIDTH(AXI_ID_W) + ) axi_memory_interconnect ( + .clk(clk), + .rst(rst), + + /* + * AXI slave interfaces + */ + // FrameHeaderDecoder + .s00_axi_awid('0), + .s00_axi_awaddr('0), + .s00_axi_awlen('0), + .s00_axi_awsize('0), + .s00_axi_awburst('0), + .s00_axi_awlock('0), + .s00_axi_awcache('0), + .s00_axi_awprot('0), + .s00_axi_awqos('0), + .s00_axi_awuser('0), + .s00_axi_awvalid('0), + .s00_axi_awready(), + .s00_axi_wdata('0), + .s00_axi_wstrb('0), + .s00_axi_wlast('0), + .s00_axi_wuser('0), + .s00_axi_wvalid(), + .s00_axi_wready(), + .s00_axi_bid(), + .s00_axi_bresp(), + .s00_axi_buser(), + .s00_axi_bvalid(), + .s00_axi_bready('0), + .s00_axi_arid(frame_header_decoder_axi_ar_arid), + .s00_axi_araddr(frame_header_decoder_axi_ar_araddr), + .s00_axi_arlen(frame_header_decoder_axi_ar_arlen), + .s00_axi_arsize(frame_header_decoder_axi_ar_arsize), + .s00_axi_arburst(frame_header_decoder_axi_ar_arburst), + .s00_axi_arlock('0), + .s00_axi_arcache(frame_header_decoder_axi_ar_arcache), + .s00_axi_arprot(frame_header_decoder_axi_ar_arprot), + .s00_axi_arqos(frame_header_decoder_axi_ar_arqos), + .s00_axi_aruser('0), + .s00_axi_arvalid(frame_header_decoder_axi_ar_arvalid), + .s00_axi_arready(frame_header_decoder_axi_ar_arready), + .s00_axi_rid(frame_header_decoder_axi_r_rid), + .s00_axi_rdata(frame_header_decoder_axi_r_rdata), + .s00_axi_rresp(frame_header_decoder_axi_r_rresp[1:0]), + .s00_axi_rlast(frame_header_decoder_axi_r_rlast), + .s00_axi_ruser(), + .s00_axi_rvalid(frame_header_decoder_axi_r_rvalid), + .s00_axi_rready(frame_header_decoder_axi_r_rready), + + // BlockHeaderDecoder + .s01_axi_awid('0), + .s01_axi_awaddr('0), + .s01_axi_awlen('0), + .s01_axi_awsize('0), + .s01_axi_awburst('0), + .s01_axi_awlock('0), + .s01_axi_awcache('0), + .s01_axi_awprot('0), + .s01_axi_awqos('0), + .s01_axi_awuser('0), + .s01_axi_awvalid('0), + .s01_axi_awready(), + .s01_axi_wdata('0), + .s01_axi_wstrb('0), + .s01_axi_wlast('0), + .s01_axi_wuser('0), + .s01_axi_wvalid(), + .s01_axi_wready(), + .s01_axi_bid(), + .s01_axi_bresp(), + .s01_axi_buser(), + .s01_axi_bvalid(), + .s01_axi_bready('0), + .s01_axi_arid(block_header_decoder_axi_ar_arid), + .s01_axi_araddr(block_header_decoder_axi_ar_araddr), + .s01_axi_arlen(block_header_decoder_axi_ar_arlen), + .s01_axi_arsize(block_header_decoder_axi_ar_arsize), + .s01_axi_arburst(block_header_decoder_axi_ar_arburst), + .s01_axi_arlock('0), + .s01_axi_arcache(block_header_decoder_axi_ar_arcache), + .s01_axi_arprot(block_header_decoder_axi_ar_arprot), + .s01_axi_arqos(block_header_decoder_axi_ar_arqos), + .s01_axi_aruser('0), + .s01_axi_arvalid(block_header_decoder_axi_ar_arvalid), + .s01_axi_arready(block_header_decoder_axi_ar_arready), + .s01_axi_rid(block_header_decoder_axi_r_rid), + .s01_axi_rdata(block_header_decoder_axi_r_rdata), + .s01_axi_rresp(block_header_decoder_axi_r_rresp[1:0]), + .s01_axi_rlast(block_header_decoder_axi_r_rlast), + .s01_axi_ruser(), + .s01_axi_rvalid(block_header_decoder_axi_r_rvalid), + .s01_axi_rready(block_header_decoder_axi_r_rready), + + // RawBlockDecoder + .s02_axi_awid('0), + .s02_axi_awaddr('0), + .s02_axi_awlen('0), + .s02_axi_awsize('0), + .s02_axi_awburst('0), + .s02_axi_awlock('0), + .s02_axi_awcache('0), + .s02_axi_awprot('0), + .s02_axi_awqos('0), + .s02_axi_awuser('0), + .s02_axi_awvalid('0), + .s02_axi_awready(), + .s02_axi_wdata('0), + .s02_axi_wstrb('0), + .s02_axi_wlast('0), + .s02_axi_wuser('0), + .s02_axi_wvalid(), + .s02_axi_wready(), + .s02_axi_bid(), + .s02_axi_bresp(), + .s02_axi_buser(), + .s02_axi_bvalid(), + .s02_axi_bready('0), + .s02_axi_arid(raw_block_decoder_axi_ar_arid), + .s02_axi_araddr(raw_block_decoder_axi_ar_araddr), + .s02_axi_arlen(raw_block_decoder_axi_ar_arlen), + .s02_axi_arsize(raw_block_decoder_axi_ar_arsize), + .s02_axi_arburst(raw_block_decoder_axi_ar_arburst), + .s02_axi_arlock('0), + .s02_axi_arcache(raw_block_decoder_axi_ar_arcache), + .s02_axi_arprot(raw_block_decoder_axi_ar_arprot), + .s02_axi_arqos(raw_block_decoder_axi_ar_arqos), + .s02_axi_aruser('0), + .s02_axi_arvalid(raw_block_decoder_axi_ar_arvalid), + .s02_axi_arready(raw_block_decoder_axi_ar_arready), + .s02_axi_rid(raw_block_decoder_axi_r_rid), + .s02_axi_rdata(raw_block_decoder_axi_r_rdata), + .s02_axi_rresp(raw_block_decoder_axi_r_rresp[1:0]), + .s02_axi_rlast(raw_block_decoder_axi_r_rlast), + .s02_axi_ruser(), + .s02_axi_rvalid(raw_block_decoder_axi_r_rvalid), + .s02_axi_rready(raw_block_decoder_axi_r_rready), + + // SequenceExecutor + .s03_axi_awid('0), + .s03_axi_awaddr('0), + .s03_axi_awlen('0), + .s03_axi_awsize('0), + .s03_axi_awburst('0), + .s03_axi_awlock('0), + .s03_axi_awcache('0), + .s03_axi_awprot('0), + .s03_axi_awqos('0), + .s03_axi_awuser('0), + .s03_axi_awvalid('0), + .s03_axi_awready(), + .s03_axi_wdata('0), + .s03_axi_wstrb('0), + .s03_axi_wlast('0), + .s03_axi_wuser('0), + .s03_axi_wvalid('0), + .s03_axi_wready(), + .s03_axi_bid(), + .s03_axi_bresp(), + .s03_axi_buser(), + .s03_axi_bvalid(), + .s03_axi_bready('0), + .s03_axi_arid('0), + .s03_axi_araddr('0), + .s03_axi_arlen('0), + .s03_axi_arsize('0), + .s03_axi_arburst('0), + .s03_axi_arlock('0), + .s03_axi_arcache('0), + .s03_axi_arprot('0), + .s03_axi_arqos('0), + .s03_axi_aruser('0), + .s03_axi_arvalid('0), + .s03_axi_arready(), + .s03_axi_rid(), + .s03_axi_rdata(), + .s03_axi_rresp(), + .s03_axi_rlast(), + .s03_axi_ruser(), + .s03_axi_rvalid(), + .s03_axi_rready('0), + + /* + * AXI master interface + */ + // Outside-facing AXI interface of the ZSTD Decoder + .m00_axi_awid(memory_axi_aw_awid), + .m00_axi_awaddr(memory_axi_aw_awaddr), + .m00_axi_awlen(memory_axi_aw_awlen), + .m00_axi_awsize(memory_axi_aw_awsize), + .m00_axi_awburst(memory_axi_aw_awburst), + .m00_axi_awlock(memory_axi_aw_awlock), + .m00_axi_awcache(memory_axi_aw_awcache), + .m00_axi_awprot(memory_axi_aw_awprot), + .m00_axi_awqos(memory_axi_aw_awqos), + .m00_axi_awregion(memory_axi_aw_awregion), + .m00_axi_awuser(memory_axi_aw_awuser), + .m00_axi_awvalid(memory_axi_aw_awvalid), + .m00_axi_awready(memory_axi_aw_awready), + .m00_axi_wdata(memory_axi_w_wdata), + .m00_axi_wstrb(memory_axi_w_wstrb), + .m00_axi_wlast(memory_axi_w_wlast), + .m00_axi_wuser(memory_axi_w_wuser), + .m00_axi_wvalid(memory_axi_w_wvalid), + .m00_axi_wready(memory_axi_w_wready), + .m00_axi_bid(memory_axi_b_bid), + .m00_axi_bresp(memory_axi_b_bresp[1:0]), + .m00_axi_buser(memory_axi_b_buser), + .m00_axi_bvalid(memory_axi_b_bvalid), + .m00_axi_bready(memory_axi_b_bready), + .m00_axi_arid(memory_axi_ar_arid), + .m00_axi_araddr(memory_axi_ar_araddr), + .m00_axi_arlen(memory_axi_ar_arlen), + .m00_axi_arsize(memory_axi_ar_arsize), + .m00_axi_arburst(memory_axi_ar_arburst), + .m00_axi_arlock(memory_axi_ar_arlock), + .m00_axi_arcache(memory_axi_ar_arcache), + .m00_axi_arprot(memory_axi_ar_arprot), + .m00_axi_arqos(memory_axi_ar_arqos), + .m00_axi_arregion(memory_axi_ar_arregion), + .m00_axi_aruser(memory_axi_ar_aruser), + .m00_axi_arvalid(memory_axi_ar_arvalid), + .m00_axi_arready(memory_axi_ar_arready), + .m00_axi_rid(memory_axi_r_rid), + .m00_axi_rdata(memory_axi_r_rdata), + .m00_axi_rresp(memory_axi_r_rresp[1:0]), + .m00_axi_rlast(memory_axi_r_rlast), + .m00_axi_ruser(memory_axi_r_ruser), + .m00_axi_rvalid(memory_axi_r_rvalid), + .m00_axi_rready(memory_axi_r_rready) + ); + +endmodule : zstd_dec_wrapper