├── doc ├── .gitignore ├── Makefile ├── mainpage.dox ├── layout.xml └── extra_stylesheet.css ├── .gitmodules ├── .gitignore ├── hls ├── ProducerConsumer │ └── Grout_0 │ │ ├── go_hls.tcl │ │ └── Makefile ├── run_hls_global_setup.tcl ├── hls_Makefile └── nvhls_exec.tcl ├── cmod ├── ProducerConsumer │ ├── Grout_0.hpp │ ├── interconnect_config.hpp │ ├── ProducerConsumerArray.hpp │ ├── testbench.cpp │ ├── Makefile │ └── ProducerConsumerPart.hpp ├── cov_Makefile ├── Interconnect.mk ├── .clang-format ├── cmod_Makefile └── include │ └── interconnect │ ├── InterconnectTypeConfig.hpp │ ├── InterconnectPre.hpp │ ├── InterconnectHandlerFixed.hpp │ ├── InterconnectHandlerFlit.hpp │ ├── InterconnectChannels.hpp │ ├── InterconnectAnnotate.hpp │ └── interconnect_gen_utils.hpp ├── TUTORIAL.md ├── README.md └── LICENSE /doc/.gitignore: -------------------------------------------------------------------------------- 1 | latex/* 2 | html/* 3 | *tmp 4 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "matchlib"] 2 | path = matchlib 3 | url = https://github.com/NVlabs/matchlib.git 4 | -------------------------------------------------------------------------------- /doc/Makefile: -------------------------------------------------------------------------------- 1 | DOXYCOMM = Doxyfile 2 | 3 | .PHONY: doxygen 4 | doxygen: $(DOXYCOMM) 5 | doxygen $(DOXYCOMM) 6 | echo 'include : ["_*.html"]' >> html/_config.yml 7 | 8 | .PHONY: clean 9 | clean: 10 | rm -rf html latex xml 11 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Compiled Object files 5 | *.slo 6 | *.lo 7 | *.o 8 | *.obj 9 | 10 | # Precompiled Headers 11 | *.gch 12 | *.pch 13 | 14 | # Compiled Dynamic libraries 15 | *.so 16 | *.dylib 17 | *.dll 18 | 19 | # Fortran module files 20 | *.mod 21 | *.smod 22 | 23 | # Compiled Static libraries 24 | *.lai 25 | *.la 26 | *.a 27 | *.lib 28 | 29 | # Executables 30 | *.exe 31 | *.out 32 | *.app 33 | 34 | # Python 35 | __pycache__/ 36 | 37 | # Other 38 | work/ 39 | *.log 40 | *.code-workspace 41 | sim_*_gen 42 | sim_*_model 43 | catapult_cache/ 44 | -------------------------------------------------------------------------------- /hls/ProducerConsumer/Grout_0/go_hls.tcl: -------------------------------------------------------------------------------- 1 | # 2 | # SPDX-FileCopyrightText: Copyright (c) 2019-2021 NVIDIA CORPORATION & AFFILIATES. All rights reserved. 3 | # SPDX-License-Identifier: Apache-2.0 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | # 17 | 18 | source ../../../nvhls_exec.tcl 19 | 20 | proc nvhls::usercmd_pre_analyze {} { 21 | directive set -STRICT_MIO_SCHEDULING false 22 | directive set -REGISTER_THRESHOLD 512 23 | directive set -MEM_MAP_THRESHOLD 2048 24 | } 25 | 26 | nvhls::run 27 | -------------------------------------------------------------------------------- /cmod/ProducerConsumer/Grout_0.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: Copyright (c) 2020-2021 NVIDIA CORPORATION & 3 | * AFFILIATES. All rights reserved. 4 | * SPDX-License-Identifier: Apache-2.0 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | // This is a stub needed for the generated interconnect, so that downstream flow 20 | // and simulation knows where to look for it. 21 | 22 | #ifndef GROUT_0_H 23 | #define GROUT_0_H 24 | 25 | #include 26 | #include 27 | #include 28 | 29 | #include 30 | #include "interconnect_config.hpp" 31 | 32 | #if defined(INTERCONNECT_GEN) 33 | #include ic_header(INTERCONNECT_GEN) 34 | #endif // if defined(INTERCONNECT_GEN) 35 | 36 | #endif 37 | -------------------------------------------------------------------------------- /cmod/cov_Makefile: -------------------------------------------------------------------------------- 1 | # 2 | # SPDX-FileCopyrightText: Copyright (c) 2017-2021 NVIDIA CORPORATION & AFFILIATES. All rights reserved. 3 | # SPDX-License-Identifier: Apache-2.0 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | # 17 | 18 | CTC = ctcwrap -i m -C "NO_EXCLUDE=*matchlib/*.h" 19 | COV_XML ?= coverage.xml 20 | MAKE_TARGET ?= all run 21 | 22 | MON.dat: $(MAKE_TARGET) 23 | 24 | # Apparently CTC doesn't like full path g++ 25 | #CC_ := $(CC) 26 | CC_ = "g++" 27 | 28 | cov: CC = $(CTC) $(CC_) 29 | cov: clean MON.dat 30 | ctcpost \ 31 | -x $(COV_XML) \ 32 | -t timing.txt \ 33 | -p coverage.txt \ 34 | -u untested.txt \ 35 | MON.dat \ 36 | MON.sym 37 | 38 | ctcxmlmerge \ 39 | $(COV_XML) \ 40 | -x coverage_merged.xml \ 41 | -p coverage_profile.txt \ 42 | 43 | ctc2html \ 44 | -i coverage_profile.txt \ 45 | -o web-report 46 | 47 | cov_clean: 48 | rm -rf MON.dat MON.sym coverage.xml timing.txt coverage.txt untested.txt coverage_merged.xml coverage_profile.txt web-report 49 | -------------------------------------------------------------------------------- /cmod/ProducerConsumer/interconnect_config.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: Copyright (c) 2020-2021 NVIDIA CORPORATION & 3 | * AFFILIATES. All rights reserved. 4 | * SPDX-License-Identifier: Apache-2.0 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | // Header file to declare interconnects, units, and messages for Interconnect 20 | // Prototyping Assistant. (IPA) 21 | 22 | #ifndef __INTERCONNECT_CONFIG_H__ 23 | #define __INTERCONNECT_CONFIG_H__ 24 | 25 | #include 26 | 27 | namespace interconnect_config { 28 | IC_BEGIN_INTERCONNECTS 29 | IC_ADD_INTERCONNECT(pcmodulearray) 30 | IC_END_INTERCONNECTS 31 | 32 | // Maps are used to set custom numbered routing, for flit adapter traffic or 33 | // other more custom uses. 34 | IC_BEGIN_DEST_MAPS 35 | IC_END_DEST_MAPS 36 | 37 | IC_BEGIN_PARTITION_TYPES 38 | IC_BEGIN_PARTITION(ProducerConsumerPart) 39 | IC_END_PARTITION 40 | IC_END_PARTITION_TYPES 41 | 42 | IC_BEGIN_MESSAGE_TYPES 43 | IC_ADD_MESSAGE_TYPE(my_msg, NVUINTW(64)) 44 | IC_ADD_MESSAGE_TYPE(my_ctrl, bool) 45 | IC_END_MESSAGE_TYPES 46 | }; 47 | 48 | #endif 49 | -------------------------------------------------------------------------------- /hls/ProducerConsumer/Grout_0/Makefile: -------------------------------------------------------------------------------- 1 | # 2 | # SPDX-FileCopyrightText: Copyright (c) 2019-2021 NVIDIA CORPORATION & AFFILIATES. All rights reserved. 3 | # SPDX-License-Identifier: Apache-2.0 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | # 17 | # 18 | 19 | ROOT := ../../.. 20 | 21 | PE_WIDTH ?= 4 22 | PE_HEIGHT ?= 4 23 | 24 | BUILD ?= $(PE_WIDTH)x$(PE_HEIGHT) 25 | 26 | # The below must match up with interconnect_designer.py and interconnect_update.py. They can't be changed arbitrarly. 27 | INTERCONNECT_SC_OBJECT_PATH := my_testbench.dut.pcmodulearray_inst.interconnect_0 28 | INTERCONNECT_TB_OBJECT_PATH := my_testbench.dut.interconnect_0 29 | 30 | include ${ROOT}/cmod/Interconnect.mk 31 | 32 | BUP_BLOCKS := 33 | RUN_SCVERIFY := 0 34 | 35 | HLS(LSF_QUEUE) ?= o_cpu_16G_8H 36 | 37 | include ${ROOT}/hls/hls_Makefile 38 | 39 | COMPILER_FLAGS += VECTOR_SIZE=$(VECTOR_SIZE) VECTOR_LANES=$(VECTOR_LANES) ACTIVATION_WORDWIDTH=$(ACTIVATION_WIDTH) WEIGHT_WORDWIDTH=$(WEIGHT_WIDTH) DATAFLOW=$(DATAFLOW) WC_DEPTH=$(WC_DEPTH) OC_DEPTH=$(OC_DEPTH) PE_WIDTH=$(PE_WIDTH) PE_HEIGHT=$(PE_HEIGHT) 40 | COMPILER_FLAGS += DISABLE_PEPARTITIONTEST 41 | COMPILER_FLAGS += INTERCONNECT_MODELER_NATIVE 42 | COMPILER_FLAGS += INTERCONNECT_GEN=$(INTERCONNECT_GEN_HPP_FILE) 43 | 44 | COMPILER_FLAGS += DEBUG_LEVEL=2 45 | 46 | SEARCH_PATH += $(RAPIDJSON_HOME)/include 47 | SEARCH_PATH += ${TOT}/cmod/include 48 | 49 | SRC_PATH = ${ROOT}/cmod/ProducerConsumer 50 | -------------------------------------------------------------------------------- /cmod/ProducerConsumer/ProducerConsumerArray.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: Copyright (c) 2021 NVIDIA CORPORATION & AFFILIATES. 3 | * All rights reserved. 4 | * SPDX-License-Identifier: Apache-2.0 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | #ifndef __PRODUCER_CONSUMER_H__ 20 | #define __PRODUCER_CONSUMER_H__ 21 | 22 | #include 23 | #include 24 | #include 25 | 26 | #include 27 | #include 28 | #include "interconnect_config.hpp" 29 | 30 | #ifdef INTERCONNECT_GEN 31 | #include ic_header(INTERCONNECT_GEN) 32 | #endif // ifdef INTERCONNECT_GEN 33 | 34 | #include "ProducerConsumerPart.hpp" 35 | 36 | class ProducerConsumerArray : public match::Module { 37 | public: 38 | SC_HAS_PROCESS(ProducerConsumerArray); 39 | static const int kPEWidth = PE_WIDTH; 40 | static const int kPEHeight = PE_HEIGHT; 41 | 42 | ProducerConsumerPart *pe_part_inst[kPEWidth * kPEHeight]; 43 | IC_HAS_INTERCONNECT(pcmodulearray); 44 | 45 | ProducerConsumerArray(sc_module_name nm) : match::Module(nm) { 46 | ic.clk(clk); 47 | ic.rst(rst); 48 | 49 | for (int i = 0; i < kPEWidth * kPEHeight; i++) { 50 | pe_part_inst[i] = 51 | new ProducerConsumerPart(sc_gen_unique_name("pe_part_inst"), i); 52 | 53 | pe_part_inst[i]->clk(clk); 54 | pe_part_inst[i]->rst(rst); 55 | IC_PART_BIND(*pe_part_inst[i], i); 56 | } 57 | } 58 | }; 59 | 60 | #endif 61 | -------------------------------------------------------------------------------- /cmod/Interconnect.mk: -------------------------------------------------------------------------------- 1 | # 2 | # SPDX-FileCopyrightText: Copyright (c) 2020-2021 NVIDIA CORPORATION & AFFILIATES. All rights reserved. 3 | # SPDX-License-Identifier: Apache-2.0 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | # 17 | # Additional options needed for interconnect generation 18 | 19 | ifndef INTERCONNECT_SC_OBJECT_PATH 20 | $(error INTERCONNECT_SC_OBJECT_PATH is not set) 21 | endif 22 | export INTERCONNECT_SC_OBJECT_PATH 23 | 24 | ifndef INTERCONNECT_TB_OBJECT_PATH 25 | $(error INTERCONNECT_TB_OBJECT_PATH is not set) 26 | endif 27 | export INTERCONNECT_TB_OBJECT_PATH 28 | 29 | # Set default binary names. 30 | ifneq ($(strip $(BUILD)),) 31 | EXE_NAME ?= sim_$(BUILD) 32 | BUILDPREFIX=$(BUILD). 33 | BUILDSUFFIX=_$(BUILD) 34 | else 35 | EXE_NAME ?= sim_test 36 | BUILDPREFIX= 37 | BUILDSUFFIX= 38 | endif 39 | 40 | INTERCONNECT_DIR=interconnect$(BUILDSUFFIX) 41 | INTERCONNECT_GEN_HPP_FILE=$(INTERCONNECT_DIR)/interconnect_gen.hpp 42 | 43 | CONNECTIVITY_JSON=$(INTERCONNECT_DIR)/$(INTERCONNECT_SC_OBJECT_PATH).connectivity.json 44 | CONNECTIVITY_TB_JSON=$(INTERCONNECT_DIR)/$(INTERCONNECT_TB_OBJECT_PATH).connectivity.json 45 | INPUT_JSON=$(INTERCONNECT_DIR)/$(INTERCONNECT_SC_OBJECT_PATH).input.json 46 | OUTPUT_JSON=$(INTERCONNECT_DIR)/$(INTERCONNECT_SC_OBJECT_PATH).output.json 47 | OUTPUT_TB_JSON=$(INTERCONNECT_DIR)/$(INTERCONNECT_TB_OBJECT_PATH).output.json 48 | YAML_CONFIG=$(INTERCONNECT_DIR)/interconnect.yaml 49 | YAML_CONFIG_TEMPLATE=$(YAML_CONFIG).template 50 | YAML_CONFIG_USER=$(wildcard $(YAML_CONFIG).user) 51 | INTERCONNECT_RPT=$(INTERCONNECT_DIR)/interconnect.rpt 52 | INTERCONNECT_PHY_RPT=$(INTERCONNECT_DIR)/interconnect.phy.rpt.json 53 | -------------------------------------------------------------------------------- /cmod/.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | Language: Cpp 3 | # BasedOnStyle: Google 4 | AccessModifierOffset: -1 5 | AlignAfterOpenBracket: true 6 | AlignConsecutiveAssignments: false 7 | AlignEscapedNewlinesLeft: true 8 | AlignOperands: true 9 | AlignTrailingComments: true 10 | AllowAllParametersOfDeclarationOnNextLine: true 11 | AllowShortBlocksOnASingleLine: false 12 | AllowShortCaseLabelsOnASingleLine: false 13 | AllowShortFunctionsOnASingleLine: Inline 14 | AllowShortIfStatementsOnASingleLine: false 15 | AllowShortLoopsOnASingleLine: false 16 | AlwaysBreakAfterDefinitionReturnType: None 17 | AlwaysBreakBeforeMultilineStrings: true 18 | AlwaysBreakTemplateDeclarations: true 19 | BinPackArguments: true 20 | BinPackParameters: true 21 | BreakBeforeBinaryOperators: None 22 | BreakBeforeBraces: Attach 23 | BreakBeforeTernaryOperators: true 24 | BreakConstructorInitializersBeforeComma: false 25 | ColumnLimit: 80 26 | CommentPragmas: '^ IWYU pragma:' 27 | ConstructorInitializerAllOnOneLineOrOnePerLine: true 28 | ConstructorInitializerIndentWidth: 4 29 | ContinuationIndentWidth: 4 30 | Cpp11BracedListStyle: true 31 | DerivePointerAlignment: true 32 | DisableFormat: false 33 | ExperimentalAutoDetectBinPacking: false 34 | ForEachMacros: [ foreach, Q_FOREACH, BOOST_FOREACH ] 35 | IndentCaseLabels: true 36 | IndentWidth: 2 37 | IndentWrappedFunctionNames: false 38 | KeepEmptyLinesAtTheStartOfBlocks: true 39 | MacroBlockBegin: '' 40 | MacroBlockEnd: '' 41 | MaxEmptyLinesToKeep: 1 42 | NamespaceIndentation: None 43 | ObjCBlockIndentWidth: 2 44 | ObjCSpaceAfterProperty: false 45 | ObjCSpaceBeforeProtocolList: false 46 | PenaltyBreakBeforeFirstCallParameter: 1 47 | PenaltyBreakComment: 300 48 | PenaltyBreakFirstLessLess: 120 49 | PenaltyBreakString: 1000 50 | PenaltyExcessCharacter: 1000000 51 | PenaltyReturnTypeOnItsOwnLine: 200 52 | PointerAlignment: Left 53 | SpaceAfterCStyleCast: false 54 | SpaceBeforeAssignmentOperators: true 55 | SpaceBeforeParens: ControlStatements 56 | SpaceInEmptyParentheses: false 57 | SpacesBeforeTrailingComments: 2 58 | SpacesInAngles: false 59 | SpacesInContainerLiterals: true 60 | SpacesInCStyleCastParentheses: false 61 | SpacesInParentheses: false 62 | SpacesInSquareBrackets: false 63 | Standard: Auto 64 | TabWidth: 8 65 | UseTab: Never 66 | ... 67 | 68 | -------------------------------------------------------------------------------- /cmod/cmod_Makefile: -------------------------------------------------------------------------------- 1 | # 2 | # SPDX-FileCopyrightText: Copyright (c) 2016-2021 NVIDIA CORPORATION & AFFILIATES. All rights reserved. 3 | # SPDX-License-Identifier: Apache-2.0 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | # 17 | 18 | TOT := $(abspath $(dir $(lastword $(MAKEFILE_LIST)))../) 19 | ROOT ?= $(TOT) 20 | 21 | # Location of submodules 22 | MATCHLIB_HOME ?= $(ROOT)/matchlib 23 | RAPIDJSON_HOME ?= $(MATCHLIB_HOME)/rapidjson 24 | 25 | CC = g++ 26 | 27 | INCDIR ?= 28 | INCDIR += -I. -I$(SYSTEMC_HOME)/include -I$(BOOST_HOME)/include -I$(CATAPULT_HOME)/Mgc_home/shared/include 29 | INCDIR += -I$(TOT)/cmod/include 30 | INCDIR += -I$(MATCHLIB_HOME)/cmod/include 31 | INCDIR += -I$(RAPIDJSON_HOME)/include 32 | 33 | LIBDIR ?= 34 | LIBDIR += -L. -L$(SYSTEMC_HOME)/lib-linux64 -L$(BOOST_HOME)/lib 35 | 36 | CFLAGS ?= 37 | CFLAGS += -Wall -Wno-unknown-pragmas -std=c++11 $(INCDIR) $(LIBDIR) 38 | 39 | HLS_CATAPULT ?= 1 40 | ifeq ($(HLS_CATAPULT),1) 41 | CFLAGS += -DHLS_CATAPULT 42 | endif 43 | 44 | LIBS ?= 45 | LIBS += -lstdc++ -lsystemc -lm -lpthread -lboost_timer -lboost_chrono -lboost_system 46 | 47 | # SIM_MODE 48 | # 0 = Synthesis view of Connections port and combinational code. 49 | # This option can cause failed simulations due to SystemC's timing model. 50 | # 1 = Cycle-accurate view of Connections port and channel code, CONNECTIONS_ACCURATE_SIM. (default) 51 | # 2 = Faster TLM view of Connections port and channel code, CONNECTIONS_FAST_SIM. 52 | SIM_MODE ?= 1 53 | ifeq ($(SIM_MODE),1) 54 | USER_FLAGS += -DCONNECTIONS_ACCURATE_SIM -DSC_INCLUDE_DYNAMIC_PROCESSES 55 | endif 56 | ifeq ($(SIM_MODE),2) 57 | USER_FLAGS += -DCONNECTIONS_FAST_SIM -DSC_INCLUDE_DYNAMIC_PROCESSES 58 | endif 59 | 60 | # RAND_STALL 61 | # 0 = Random stall of ports and channels disabled (default) 62 | # 1 = Random stall of ports and channels enabled 63 | # This feature aids in latency insensitive design verication. 64 | # Note: Only valid if SIM_MODE = 1 (accurate) or 2 (fast) 65 | ifeq ($(RAND_STALL),1) 66 | USER_FLAGS += -DCONN_RAND_STALL 67 | endif 68 | 69 | .PHONY: Build 70 | Build: all 71 | 72 | clean: cov_clean 73 | rm -rf *.o sim_* dump.vcd 74 | 75 | CMOD_DIR:=$(shell dirname $(realpath $(lastword $(MAKEFILE_LIST)))) 76 | include $(CMOD_DIR)/cov_Makefile 77 | 78 | -------------------------------------------------------------------------------- /cmod/include/interconnect/InterconnectTypeConfig.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: Copyright (c) 2019-2021 NVIDIA CORPORATION & 3 | * AFFILIATES. All rights reserved. 4 | * SPDX-License-Identifier: Apache-2.0 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | #ifndef __INTERCONNECT_TYPE_CONFIG_H__ 20 | #define __INTERCONNECT_TYPE_CONFIG_H__ 21 | 22 | #include 23 | #include 24 | #include "InterconnectChannels.hpp" 25 | #include 26 | 27 | namespace interconnect { 28 | 29 | #ifdef CONNECTIONS_SIM_ONLY 30 | 31 | template 32 | struct InterconnectTypeConfig { 33 | typedef IM IM_t; 34 | 35 | typedef Connections::OutBlocking IM_src_t; 36 | typedef Connections::InBlocking IM_dest_t; 37 | typedef Connections::Combinational IM_chan_t; 38 | typedef interconnect::CombinationalLink IM_chanlink_t; 39 | 40 | typedef std::vector srcs_t; 41 | typedef std::vector dests_t; 42 | 43 | typedef std::map channels_begin_t; 44 | typedef std::map channels_middle_inner_t; 45 | typedef std::map channels_middle_t; 46 | typedef std::map channels_end_t; 47 | 48 | typedef std::map srcs_typeid_t; 49 | typedef std::map dests_typeid_t; 50 | typedef std::map srcs_user_typeid_t; 51 | typedef std::map dests_user_typeid_t; 52 | typedef std::map srcs_width_t; 53 | typedef std::map dests_width_t; 54 | 55 | typedef std::vector channels_valid_pairs_reverse_inner_t; 56 | typedef std::map 57 | channels_valid_pairs_reverse_t; 58 | 59 | typedef NVUINT32 idx_t; 60 | typedef NVUINT32 dest_map_idx_t; 61 | typedef std::map > dest_maps_t; 62 | typedef std::vector dest_maps_idx_vec_t; 63 | 64 | typedef NVUINT64 cycles_count_t; 65 | typedef std::map > 66 | cycle_count_channel_t; 67 | }; 68 | 69 | #endif 70 | }; 71 | 72 | #endif // __INTERCONNECT_TYPE_CONFIG_H__ 73 | -------------------------------------------------------------------------------- /cmod/include/interconnect/InterconnectPre.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: Copyright (c) 2019-2021 NVIDIA CORPORATION & 3 | * AFFILIATES. All rights reserved. 4 | * SPDX-License-Identifier: Apache-2.0 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | #ifndef __INTERCONNECT_PRE_HPP__ 20 | #define __INTERCONNECT_PRE_HPP__ 21 | 22 | //////////////////////////////////////////////////////////////////////////////////////////////////////// 23 | 24 | namespace interconnect { 25 | 26 | // Declaration 27 | template 28 | class Msg; 29 | 30 | template 31 | class InterconnectMessageSCLV; 32 | 33 | template 34 | class Interconnect; 35 | 36 | template 37 | class InterconnectBase; 38 | }; // namespace interconnect 39 | 40 | template 42 | class InterconnectInterface; 43 | 44 | namespace interconnect { 45 | enum { MaxDataWidth = 1500, MaxDestCount = 4096 }; 46 | 47 | // Internal generic interconnect message type 48 | typedef InterconnectMessageSCLV IM_t; 50 | 51 | typedef unsigned int MSG_ID_t; 52 | typedef unsigned int width_t; 53 | 54 | #ifdef CONNECTIONS_SIM_ONLY 55 | 56 | struct DestDirectoryEntry { 57 | protected: 58 | std::vector dests; 59 | unsigned int counter; // most recently used counter 60 | unsigned int total; 61 | 62 | public: 63 | DestDirectoryEntry() : counter(0), total(0){}; 64 | 65 | void add(const unsigned int &i) { 66 | dests.push_back(i); 67 | total++; 68 | NVHLS_ASSERT(total == dests.size()); 69 | } 70 | 71 | unsigned int get_next() { 72 | NVHLS_ASSERT(total == dests.size()); 73 | NVHLS_ASSERT(total > 0); 74 | NVHLS_ASSERT(counter < total); 75 | 76 | unsigned int ret_val = dests[counter]; 77 | counter++; 78 | if (counter >= total) 79 | counter = 0; 80 | return ret_val; 81 | } 82 | }; 83 | 84 | class ICManager { 85 | public: 86 | std::map *> registered_ic; 87 | std::map, 88 | DestDirectoryEntry> dest_directory; 89 | }; 90 | 91 | template 92 | struct ICManager_statics { 93 | static ICManager icm; 94 | }; 95 | 96 | inline ICManager &get_ICManager() { 97 | return ICManager_statics::icm; 98 | } 99 | 100 | template 101 | ICManager ICManager_statics::icm; 102 | 103 | #endif // CONNECTIONS_SIM_ONLY 104 | 105 | // Forward declarations 106 | template 107 | unsigned int get_real_dest_idx(InterconnectBase &ic, 108 | const unsigned int &dest_idx, 109 | const unsigned int &dest_map_idx); 110 | 111 | // Destination struct 112 | struct Destination : public nvhls_message { 113 | NVUINT32 part_id; 114 | NVUINT32 port_id; 115 | 116 | explicit Destination(unsigned int part_id_ = 0, unsigned int port_id_ = 0) { 117 | part_id = part_id_; 118 | port_id = port_id_; 119 | } 120 | 121 | // Marshaller 122 | enum { 123 | width = 124 | Wrapped::width + Wrapped::width 125 | }; 126 | 127 | template 128 | void Marshall(Marshaller &m) { 129 | m &part_id; 130 | m &port_id; 131 | } 132 | }; 133 | }; 134 | 135 | #endif // ifndef __INTERCONNECT_PRE_HPP__ 136 | -------------------------------------------------------------------------------- /hls/run_hls_global_setup.tcl: -------------------------------------------------------------------------------- 1 | # 2 | # SPDX-FileCopyrightText: Copyright (c) 2016-2021 NVIDIA CORPORATION & AFFILIATES. All rights reserved. 3 | # SPDX-License-Identifier: Apache-2.0 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | # 17 | 18 | #global setup file that runs to right before the go analyze step 19 | 20 | set ROOT $::env(ROOT) 21 | set BUILD $::env(BUILD) 22 | 23 | # Set project directory name (Default: Catapult) 24 | logfile move ${BUILD}.log 25 | if { ![file exists ${BUILD}.ccs] } { 26 | if { ${BUILD} != "" } { 27 | project new -directory ${BUILD} 28 | } 29 | } 30 | 31 | options set Cache/UserCacheHome "${ROOT}/hls/catapult_cache" 32 | options set Cache/DefaultCacheHomeEnabled false 33 | 34 | solution new -state initial 35 | solution options defaults 36 | flow package require /SCVerify 37 | solution options set /Output/PackageOutput false 38 | 39 | solution options set Input/CppStandard c++11 40 | 41 | ## Use fsdb file for power flow - make sure your environment var $NOVAS_INST_DIR has been set before you launch Catapult. 42 | solution options set /Flows/LowPower/SWITCHING_ACTIVITY_TYPE fsdb 43 | ## SCVerify settings 44 | solution options set /Flows/SCVerify/USE_MSIM false 45 | solution options set /Flows/SCVerify/USE_OSCI false 46 | solution options set /Flows/SCVerify/USE_VCS true 47 | solution options set /Flows/SCVerify/DISABLE_EMPTY_INPUTS true 48 | solution options set /Flows/VCS/VCS_HOME $env(VCS_HOME) 49 | if { [info exist env(VG_GNU_PACKAGE)] } { 50 | solution options set /Flows/VCS/VG_GNU_PACKAGE $env(VG_GNU_PACKAGE) 51 | } else { 52 | solution options set /Flows/VCS/VG_GNU_PACKAGE $env(VCS_HOME)/gnu/linux 53 | } 54 | solution options set /Flows/VCS/VG_ENV64_SCRIPT source_me.csh 55 | solution options set /Flows/VCS/SYSC_VERSION 2.3.1 56 | # SystemVerilog assertions 57 | solution options set /Output/InlinedPropertyLang sva 58 | 59 | # Verilog/VHDL 60 | solution options set Output OutputVerilog true 61 | solution options set Output/OutputVHDL false 62 | # Reset FFs 63 | solution options set Architectural/DefaultResetClearsAllRegs yes 64 | 65 | # General constrains. Please refer to tool ref manual for detailed descriptions. 66 | directive set -DESIGN_GOAL area 67 | directive set -SPECULATE true 68 | directive set -MERGEABLE true 69 | directive set -REGISTER_THRESHOLD 256 70 | directive set -MEM_MAP_THRESHOLD 32 71 | directive set -FSM_ENCODING none 72 | directive set -REG_MAX_FANOUT 0 73 | directive set -NO_X_ASSIGNMENTS true 74 | directive set -SAFE_FSM false 75 | directive set -REGISTER_SHARING_LIMIT 0 76 | directive set -ASSIGN_OVERHEAD 0 77 | directive set -TIMING_CHECKS true 78 | directive set -MUXPATH true 79 | directive set -REALLOC true 80 | directive set -UNROLL no 81 | directive set -IO_MODE super 82 | directive set -REGISTER_IDLE_SIGNAL false 83 | directive set -IDLE_SIGNAL {} 84 | directive set -TRANSACTION_DONE_SIGNAL true 85 | directive set -DONE_FLAG {} 86 | directive set -START_FLAG {} 87 | directive set -BLOCK_SYNC none 88 | directive set -TRANSACTION_SYNC ready 89 | directive set -DATA_SYNC none 90 | directive set -RESET_CLEARS_ALL_REGS yes 91 | directive set -CLOCK_OVERHEAD 20.000000 92 | directive set -OPT_CONST_MULTS use_library 93 | directive set -CHARACTERIZE_ROM false 94 | directive set -PROTOTYPE_ROM true 95 | directive set -ROM_THRESHOLD 64 96 | directive set -CLUSTER_ADDTREE_IN_WIDTH_THRESHOLD 0 97 | directive set -CLUSTER_OPT_CONSTANT_INPUTS true 98 | directive set -CLUSTER_RTL_SYN false 99 | directive set -CLUSTER_FAST_MODE false 100 | directive set -CLUSTER_TYPE combinational 101 | directive set -COMPGRADE fast 102 | directive set -PIPELINE_RAMP_UP true 103 | -------------------------------------------------------------------------------- /hls/hls_Makefile: -------------------------------------------------------------------------------- 1 | # 2 | # SPDX-FileCopyrightText: Copyright (c) 2016-2021 NVIDIA CORPORATION & AFFILIATES. All rights reserved. 3 | # SPDX-License-Identifier: Apache-2.0 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | # 17 | 18 | TOT := $(abspath $(dir $(lastword $(MAKEFILE_LIST)))../) 19 | 20 | # Location of submodules 21 | MATCHLIB_HOME ?= $(ROOT)/matchlib 22 | RAPIDJSON_HOME ?= $(MATCHLIB_HOME)/rapidjson 23 | 24 | export ROOT := $(TOT) 25 | export TOP_NAME ?= $(shell basename `pwd`) 26 | export CLK_PERIOD ?= 2 27 | export SRC_PATH ?= $(ROOT)/cmod/unittests 28 | export SEARCH_PATH ?= $(ROOT)/cmod $(ROOT)/cmod/include $(MATCHLIB_HOME)/cmod/include $(RAPIDJSON_HOME)/include $(BOOST_HOME)/include 29 | export HLS_CATAPULT ?= 1 30 | export RUN_SCVERIFY ?= 1 31 | export SYSTEMC_DESIGN ?= 1 32 | 33 | export RUN_CDESIGN_CHECKER ?= 0 34 | 35 | # Default compiler flags set by switches below. 36 | export COMPILER_FLAGS ?= 37 | 38 | # Default directory Catapult creates (empty the default is Catapult) 39 | # Clean and nuke targets will always assume Catapult to be safe. 40 | export BUILD ?= Catapult 41 | 42 | BUILDNAME := $(if $(filter ${BUILD},Catapult),,_${BUILD}) 43 | FULLNAME := ${TOPMODULE}${BUILDNAME} 44 | 45 | # SIM_MODE (SystemC code, RTL is unaffected) 46 | # 0 = Synthesis view of Connections port and combinational code. 47 | # This option can cause failed simulations due to SystemC’s timing model. 48 | # 1 = Cycle-accurate view of Connections port and channel code, CONNECTIONS_ACCURATE_SIM. (default) 49 | # 2 = Faster TLM view of Connections port and channel code, CONNECTIONS_FAST_SIM. 50 | SIM_MODE ?= 1 51 | ifeq ($(SIM_MODE),1) 52 | COMPILER_FLAGS += CONNECTIONS_ACCURATE_SIM SC_INCLUDE_DYNAMIC_PROCESSES 53 | endif 54 | ifeq ($(SIM_MODE),2) 55 | COMPILER_FLAGS += CONNECTIONS_FAST_SIM SC_INCLUDE_DYNAMIC_PROCESSES 56 | endif 57 | 58 | ifneq ($(shell echo "${CATAPULT_HOME}" | grep '10\.5'),) 59 | # 10.x we do not add this capability in. 60 | else 61 | # Assumes 10.6a and above 62 | USER_FLAGS += -DCAP_CONNECTIONS_MULTI_CLK 63 | endif 64 | 65 | # RAND_STALL (SystemC code, RTL is unaffected) 66 | # 0 = Random stall of ports and channels disabled (default) 67 | # 1 = Random stall of ports and channels enabled 68 | # This feature aids in latency insensitive design verication. 69 | # Note: Only valid if SIM_MODE = 1 (accurate) or 2 (fast) 70 | RAND_STALL ?= 0 71 | ifeq ($(RAND_STALL),1) 72 | COMPILER_FLAGS += CONN_RAND_STALL 73 | endif 74 | 75 | export FSDB_VCS_RD_SC_VALPTR_PROTECT=1 76 | 77 | .PHONY: all 78 | all: hls 79 | 80 | hls: 81 | mkdir -p work/ 82 | cd work && catapult -shell -product ultra -file ../go_hls.tcl 83 | $(eval timestamp := $(shell date "+%Y_%m_%d_%H%M")) 84 | /bin/mv work/${BUILD} work/${BUILD}_${timestamp} 85 | /bin/ln -sf work/${BUILD}_${timestamp} ${BUILD} 86 | /bin/mv work/$(BUILD).log work/${BUILD}_${timestamp}/catapult.log 87 | /bin/mv work/$(BUILD).ccs work/${BUILD}_${timestamp}/ 88 | /bin/ln -sf work/${BUILD}_${timestamp}/catapult.log catapult.log 89 | 90 | shell: 91 | mkdir -p work/ 92 | cd work && catapult -shell -product ultra 93 | 94 | gui: 95 | mkdir -p work/ 96 | cd work && catapult -product ultra 97 | 98 | cdc: 99 | cd work && env RUN_CDESIGN_CHECKER=1 catapult -shell -product ultra -file go_hls.tcl 100 | $(ROOT)/hls/design_checker_summary.py 101 | cat DesignCheckSummary.csv 102 | 103 | .PHONY: clean 104 | clean: 105 | /bin/rm -rf ./catapult_cache 106 | /bin/rm -rf ./*~ 107 | /bin/rm -rf ./*.key 108 | /bin/rm -rf ./core* 109 | /bin/rm -f "./${BUILD}" 110 | /bin/rm -rf "./work/${BUILD}" 111 | /bin/rm -f "./work/${BUILD}.log" 112 | /bin/rm -rf "./work/${BUILD}"_* 113 | /bin/rm -rf ./*.log 114 | /bin/rm -rf ./design_checker_*.tcl 115 | 116 | .PHONY: nuke 117 | nuke: clean 118 | /bin/rm -rf "./work/" 119 | 120 | -------------------------------------------------------------------------------- /cmod/ProducerConsumer/testbench.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: Copyright (c) 2020-2021 NVIDIA CORPORATION & 3 | * AFFILIATES. All rights reserved. 4 | * SPDX-License-Identifier: Apache-2.0 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | #include "interconnect_config.hpp" 32 | 33 | #include "ProducerConsumerArray.hpp" 34 | 35 | class DUT : public sc_module { 36 | public: 37 | sc_in_clk clk; 38 | sc_in rst; 39 | 40 | ProducerConsumerArray pcmodulearray_inst; 41 | 42 | DUT(sc_module_name nm) 43 | : sc_module(nm), pcmodulearray_inst("pcmodulearray_inst") { 44 | pcmodulearray_inst.clk(clk); 45 | pcmodulearray_inst.rst(rst); 46 | } 47 | }; 48 | 49 | SC_MODULE(testbench) { 50 | public: 51 | DUT dut; 52 | 53 | sc_clock clk; 54 | sc_signal rst; 55 | 56 | SC_HAS_PROCESS(testbench); 57 | testbench(sc_module_name name_) 58 | : sc_module(name_), 59 | dut("dut"), 60 | clk("clk", 1.0, SC_NS, 0.5, 0, SC_NS, true), 61 | rst("rst") { 62 | 63 | Connections::set_sim_clk(&clk); 64 | 65 | dut.clk(clk); 66 | dut.rst(rst); 67 | 68 | SC_THREAD(run); 69 | } 70 | 71 | void run() { 72 | // reset 73 | rst = 0; 74 | cout << "@" << sc_time_stamp() << " Asserting Reset " << endl; 75 | wait(2, SC_NS); 76 | cout << "@" << sc_time_stamp() << " Deasserting Reset " << endl; 77 | rst = 1; 78 | wait(10000.0, SC_NS); 79 | sc_stop(); 80 | } 81 | }; 82 | 83 | int sc_main(int argc, char* argv[]) { 84 | sc_report_handler::set_actions(SC_WARNING, SC_DO_NOTHING); 85 | testbench my_testbench("my_testbench"); 86 | 87 | for (unsigned int i = 0; i < PE_WIDTH * PE_HEIGHT; ++i) { 88 | get_pc_log().PE_unicast_only[i] = true; 89 | } 90 | 91 | for (unsigned int i = 0; i < PE_WIDTH * PE_HEIGHT; ++i) { 92 | get_pc_log().PE_activity[i] = PE_ACTIVITY; 93 | 94 | int i_x_coor = i % PE_WIDTH; 95 | int i_y_coor = i / PE_WIDTH; 96 | for (unsigned int j = 0; j < PE_WIDTH * PE_HEIGHT; ++j) { 97 | if (i == j) { 98 | get_pc_log().PE_dest_probs[i][j] = 0; 99 | continue; // Always skip loopback 100 | } 101 | int j_x_coor = j % PE_WIDTH; 102 | int j_y_coor = j / PE_WIDTH; 103 | int mdist = abs(i_x_coor - j_x_coor) + abs(i_y_coor - j_y_coor); 104 | if (PE_LOCAL_ONLY) { 105 | NVHLS_ASSERT(mdist > 0); 106 | if (mdist <= 1) { 107 | get_pc_log().PE_dest_probs[i][j] = 0.5; 108 | } else { 109 | get_pc_log().PE_dest_probs[i][j] = 0; 110 | } 111 | } else { 112 | // if(i == 0 && j == 7) { 113 | get_pc_log().PE_dest_probs[i][j] = 0.5; 114 | //} else { 115 | // get_pc_log().PE_dest_probs[i][j] = 0; 116 | //} 117 | } 118 | } 119 | } 120 | 121 | sc_report_handler::set_actions(SC_ERROR, SC_DISPLAY); 122 | sc_start(); 123 | 124 | bool rc = (sc_report_handler::get_count(SC_ERROR) > 0); 125 | 126 | // Don't print stats in elab only mode. 127 | const char* nvhls_elab_only = std::getenv("NVHLS_ELAB_ONLY"); 128 | if (nvhls_elab_only && std::string(nvhls_elab_only) == "1") { 129 | return rc; 130 | } 131 | 132 | #if !defined(INTERCONNECT_GEN) 133 | my_testbench.dut.pcmodulearray_inst.ic.pretty_print(); 134 | my_testbench.dut.pcmodulearray_inst.ic.print_statistics(); 135 | #endif 136 | get_pc_log().print_stats(); 137 | get_pc_log().print_pre_stats(); 138 | 139 | if (rc) 140 | cout << "Simulation FAILED\n"; 141 | else 142 | cout << "Simulation PASSED\n"; 143 | return rc; 144 | }; 145 | -------------------------------------------------------------------------------- /hls/nvhls_exec.tcl: -------------------------------------------------------------------------------- 1 | # 2 | # SPDX-FileCopyrightText: Copyright (c) 2019-2021 NVIDIA CORPORATION & AFFILIATES. All rights reserved. 3 | # SPDX-License-Identifier: Apache-2.0 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | # 17 | 18 | source $env(ROOT)/hls/run_hls_global_setup.tcl 19 | 20 | namespace eval nvhls { 21 | proc run {} { 22 | 23 | # Get all input variables set from Makefile 24 | global env 25 | set USER_VARS {TOP_NAME CLK_PERIOD SRC_PATH SEARCH_PATH HLS_CATAPULT RUN_SCVERIFY COMPILER_FLAGS SYSTEMC_DESIGN RUN_CDESIGN_CHECKER} 26 | 27 | echo "***USER SETTINGS***" 28 | foreach var $USER_VARS { 29 | if [info exists env($var)] { 30 | echo "$var = $env($var)" 31 | set $var $env($var) 32 | } else { 33 | echo "Warning: $var not set by user" 34 | set $var "" 35 | } 36 | } 37 | 38 | options set Input/SearchPath ". $SEARCH_PATH" 39 | 40 | set_input_files $SRC_PATH $TOP_NAME $SYSTEMC_DESIGN 41 | set_compiler_flags $HLS_CATAPULT $COMPILER_FLAGS 42 | usercmd_pre_analyze 43 | go analyze 44 | setup_libs 45 | setup_clocks $CLK_PERIOD 46 | setup_hier $TOP_NAME 47 | usercmd_pre_compile 48 | go compile 49 | if { $RUN_CDESIGN_CHECKER eq "1" } { run_design_checker; exit } 50 | go libraries 51 | go assembly 52 | usercmd_post_assembly 53 | go architect 54 | usercmd_post_architect 55 | go allocate 56 | go schedule 57 | go dpfsm 58 | go extract 59 | 60 | if { $RUN_SCVERIFY eq "1" } { 61 | flow run /SCVerify/launch_make ./scverify/Verify_concat_sim_rtl_v_vcs.mk {} SIMTOOL=vcs sim 62 | } 63 | 64 | project save 65 | 66 | exit 67 | } 68 | 69 | proc set_input_files {SRC_PATH TOP_NAME SYSC} { 70 | if { $SYSC eq "1" } { 71 | set type SYSTEMC 72 | set ext hpp 73 | } else { 74 | set type C++ 75 | set ext cpp 76 | } 77 | solution file add [list $SRC_PATH/$TOP_NAME.$ext] -type $type 78 | solution file add [list $SRC_PATH/testbench.cpp] -type $type -exclude true 79 | } 80 | 81 | proc set_compiler_flags {HLS_CATAPULT COMPILER_FLAGS} { 82 | if { $HLS_CATAPULT eq "1" } { 83 | set HLS_CATAPULT_FLAG "-DHLS_CATAPULT" 84 | } else { 85 | set HLS_CATAPULT_FLAG "" 86 | } 87 | set FLAG_STR "" 88 | foreach flag $COMPILER_FLAGS { 89 | append FLAG_STR "-D$flag " 90 | } 91 | options set Input/CompilerFlags "-D_SYNTHESIS_ $HLS_CATAPULT_FLAG $FLAG_STR" 92 | } 93 | 94 | proc setup_libs {} { 95 | solution library add nangate-45nm_beh -- -rtlsyntool OasysRTL 96 | solution library add ram_nangate-45nm-singleport_beh 97 | solution library add ram_nangate-45nm-separate_beh 98 | } 99 | 100 | proc setup_clocks {period} { 101 | set name clk 102 | set CLK_PERIODby2 [expr $period/2.0] 103 | directive set -CLOCKS "$name \"-CLOCK_PERIOD $period -CLOCK_EDGE rising -CLOCK_UNCERTAINTY 0.0 -CLOCK_HIGH_TIME $CLK_PERIODby2 -RESET_SYNC_NAME rst -RESET_ASYNC_NAME arst_n -RESET_KIND sync -RESET_SYNC_ACTIVE high -RESET_ASYNC_ACTIVE low -ENABLE_NAME {} -ENABLE_ACTIVE high\" " 104 | directive set -CLOCK_NAME $name 105 | } 106 | 107 | proc setup_hier {TOP_NAME} { 108 | directive set -DESIGN_HIERARCHY "$TOP_NAME" 109 | } 110 | 111 | proc run_design_checker {} { 112 | flow run /CDesignChecker/write_options {VER_MODE Custom RULES {{-abr -severity error } {-abw -severity error } {-acc -severity warning } {-acs -severity warning } {-aic -severity warning } {-als -severity warning } {-aob -severity error } {-apt -severity info } {-cas -severity error } {-ccc -severity warning } {-cia -severity warning } {-cmc -severity info } {-cns -severity warning } {-cwb -severity warning } {-dbz -severity error } {-fxd -severity warning } {-ise -severity error } {-lrc -severity info } {-mxs -severity info } {-ovl -severity error } {-pdd -severity warning } {-rrt -severity error } {-sat -severity warning } {-stf -severity info } {-sud -severity warning } {-umr -severity error }}} 113 | flow run /CDesignChecker/launch_sleccpc_sh ./CDesignChecker/design_checker.sh Custom 114 | } 115 | 116 | proc usercmd_pre_analyze {} {} 117 | proc usercmd_pre_compile {} {} 118 | proc usercmd_post_assembly {} {} 119 | proc usercmd_post_architect {} {} 120 | } 121 | -------------------------------------------------------------------------------- /doc/mainpage.dox: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: Copyright (c) 2016-2021 NVIDIA CORPORATION & AFFILIATES. All rights reserved. 3 | * SPDX-License-Identifier: Apache-2.0 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | /** 19 | 20 | \mainpage 21 | 22 | \tableofcontents 23 | 24 | \section sec_intro What is Interconnect Prototyping Assistant? 25 | 26 | \par 27 | Interconnect Prototyping Assistant (IPA) is a framework for interconnet modeling and generation for high-level synthesis flows. 28 | Key motivations are: 29 | - *** 30 | 31 | \par 32 | IPA's programming interface is almost entirely done through macros in [Interconnect.hpp](_interconnect_8hpp.html). 33 | 34 | \par 35 | In addition, there is a collection of [auxiliary non-synthesizable components] (group___match_util.html) useful for building testbench and debug infrastructure of developed HW. 36 | 37 | \section sec_directory Directory organization 38 | 39 | - bin 40 | \par 41 | Python scripts related to IPA. 42 | 43 | - cmod 44 | \par 45 | C model code 46 | 47 | - interconnect 48 | \par 49 | Interconnect Prototyping Assitant and example live in this directory. 50 | 51 | - include 52 | \par 53 | Contains Interconnect Prototyping Assistant SystemC library. 54 | 55 | - ProducerConsumer 56 | \par 57 | Example implementation using IPA. 58 | 59 | - hls 60 | \par 61 | High-Level-Synthesis (hls) - includes examples of hls setup scripts for synthesizing using Mentor Graphics Catapult. Each example is in dedicated sub-directory. 62 | 63 | - doc 64 | \par 65 | Sources for generation of this Doxygen documentation. 66 | 67 | \section sec_license License 68 | 69 | \par 70 | 71 | Copyright (c) 2016-2021, NVIDIA CORPORATION. All rights reserved. 72 | 73 | Licensed under the Apache License, Version 2.0 (the "License"); 74 | you may not use this file except in compliance with the License. 75 | You may obtain a copy of the License at 76 | 77 | http://www.apache.org/licenses/LICENSE-2.0 78 | 79 | Unless required by applicable law or agreed to in writing, software 80 | distributed under the License is distributed on an "AS IS" BASIS, 81 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 82 | See the License for the specific language governing permissions and 83 | limitations under the License. 84 | 85 | When indicated in the source code, some compute functions in MatchLib 86 | have been derived from code in the Algorithmic C (tm) Datatypes v3.7.1 distributed 87 | with the following license: 88 | 89 | Algorithmic C (tm) Datatypes 90 | 91 | Software Version: 3.7 92 | 93 | Release Date : Sat Jun 25 13:27:03 PDT 2016 94 | Release Type : Production Release 95 | Release Build : 3.7.1 96 | 97 | Copyright 2004-2016, Mentor Graphics Corporation, 98 | 99 | All Rights Reserved. 100 | 101 | Licensed under the Apache License, Version 2.0 (the "License"); 102 | you may not use this file except in compliance with the License. 103 | You may obtain a copy of the License at 104 | 105 | http://www.apache.org/licenses/LICENSE-2.0 106 | 107 | Unless required by applicable law or agreed to in writing, software 108 | distributed under the License is distributed on an "AS IS" BASIS, 109 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 110 | implied. 111 | See the License for the specific language governing permissions and 112 | limitations under the License. 113 | 114 | 115 | The most recent version of this package is available at github. 116 | 117 | 118 | 119 | */ 120 | 121 | -------------------------------------------------------------------------------- /cmod/ProducerConsumer/Makefile: -------------------------------------------------------------------------------- 1 | # 2 | # SPDX-FileCopyrightText: Copyright (c) 2016-2021 NVIDIA CORPORATION & AFFILIATES. All rights reserved. 3 | # SPDX-License-Identifier: Apache-2.0 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | # 17 | 18 | ROOT = $(abspath ../../) 19 | include $(ROOT)/cmod/cmod_Makefile 20 | 21 | # Configure build name and debug level. 22 | BUILD ?= $(PE_WIDTH)x$(PE_HEIGHT) 23 | DEBUG_LEVEL ?= -1 24 | 25 | # The below must match up with interconnect_designer.py 26 | INTERCONNECT_SC_OBJECT_PATH := my_testbench.dut.pcmodulearray_inst.interconnect_0 27 | INTERCONNECT_TB_OBJECT_PATH := my_testbench.dut.interconnect_0 28 | 29 | include $(ROOT)/cmod/Interconnect.mk 30 | 31 | PE_WIDTH ?= 4 32 | PE_HEIGHT ?= 4 33 | RI_BUFSIZE ?= 1 34 | 35 | # Needed for Catapult 10.6+ compatibility to maintain original Connections signal naming. 36 | USER_FLAGS += -DCONNECTIONS_NAMING_ORIGINAL 37 | USER_FLAGS += -DSC_INCLUDE_DYNAMIC_PROCESSES -DCONNECTIONS_ACCURATE_SIM 38 | USER_FLAGS += -DRAPIDJSON_HAS_STDSTRING=1 -DENABLE_PERF_COUNTERS 39 | USER_FLAGS += -DPE_WIDTH=$(PE_WIDTH) -DPE_HEIGHT=$(PE_HEIGHT) 40 | ifneq ($(shell echo "${CATAPULT_HOME}" | grep '10\.5'),) 41 | # 10.x we do not add this capability in. 42 | else 43 | # Assumes 10.6a and above 44 | USER_FLAGS += -DCAP_CONNECTIONS_MULTI_CLK 45 | endif 46 | 47 | INTERCONNECT_DISABLE_CONGESTION ?= 0 48 | 49 | # For CSolver 50 | CFLAGS += -I${VCS_HOME}/include 51 | CFLAGS += -g 52 | 53 | PE_ACTIVITY ?= 1.00 54 | USER_FLAGS += -DPE_ACTIVITY=$(PE_ACTIVITY) 55 | 56 | PE_LOCAL_ONLY ?= 0 57 | USER_FLAGS += -DPE_LOCAL_ONLY=$(PE_LOCAL_ONLY) 58 | 59 | CLEANUP_BA_FILES ?= 0 60 | USER_FLAGS += -O1 -Wno-uninitialized -Wno-unused-local-typedefs -Wno-unused-value 61 | 62 | EXE_GEN_NAME ?= $(EXE_NAME)_gen 63 | EXE_MODEL_NAME ?= $(EXE_NAME)_model 64 | 65 | SLEEP ?= 66 | 67 | ifeq ($(SLEEP),1) 68 | SLEEP_CMD ?= sleep 5 69 | else 70 | SLEEP_CMD ?= 71 | endif 72 | 73 | 74 | ###### Run targets for interconnect mode 75 | 76 | ifneq ($(INTERCONNECT_GEN),) 77 | 78 | ifeq ($(INTERCONNECT_GEN),1) 79 | .PHONY: all run 80 | all: ${EXE_GEN_NAME} 81 | run: run_gen 82 | else 83 | .PHONY: all run 84 | all: ${EXE_MODEL_NAME} 85 | run: run_model 86 | endif 87 | 88 | else 89 | .PHONY: all run 90 | all: ${EXE_MODEL_NAME} ${EXE_GEN_NAME} 91 | run: run_model run_gen 92 | endif 93 | 94 | ${EXE_MODEL_NAME}: $(wildcard *.h) $(filter-out $(INTERCONNECT_GEN_HPP_FILE),$(wildcard *.hpp)) $(wildcard *.cpp) $(wildcard $(ROOT)/cmod/magnet/include/*.h) 95 | mkdir -p "$(INTERCONNECT_DIR)" 96 | env BUILD=$(BUILD) $(CC) -o ${EXE_MODEL_NAME} -DHLS_CATAPULT -DDEBUG_LEVEL=$(DEBUG_LEVEL) $(CFLAGS) $(USER_FLAGS) testbench.cpp $(BOOSTLIBS) $(LIBS) 97 | 98 | ${EXE_GEN_NAME}: $(wildcard *.h) $(filter-out $(INTERCONNECT_GEN_HPP_FILE),$(wildcard *.hpp)) $(wildcard *.cpp) $(wildcard $(ROOT)/cmod/magnet/include/*.h) $(INTERCONNECT_GEN_HPP_FILE) 99 | mkdir -p "$(INTERCONNECT_DIR)" 100 | env BUILD=$(BUILD) $(CC) -o ${EXE_GEN_NAME} -DHLS_CATAPULT -DDEBUG_LEVEL=$(DEBUG_LEVEL) $(CFLAGS) $(USER_FLAGS) -DINTERCONNECT_GEN="$(INTERCONNECT_GEN_HPP_FILE)" testbench.cpp $(BOOSTLIBS) $(LIBS) 101 | 102 | # Run elaboration to generate connectivity.json 103 | $(CONNECTIVITY_JSON): ${EXE_MODEL_NAME} 104 | mkdir -p "$(INTERCONNECT_DIR)" 105 | env BUILD=$(BUILD) NVHLS_ELAB_ONLY=1 ./${EXE_MODEL_NAME} 106 | rm -f "$(CONNECTIVITY_TB_JSON)" 107 | 108 | $(YAML_CONFIG_TEMPLATE) $(INTERCONNECT_RPT): $(CONNECTIVITY_JSON) $(ROOT)/bin/interconnect_designer.py 109 | mkdir -p "$(INTERCONNECT_DIR)" 110 | BUILD=$(BUILD) $(ROOT)/bin/interconnect_designer.py init 111 | 112 | $(YAML_CONFIG): $(YAML_CONFIG_USER) $(YAML_CONFIG_TEMPLATE) 113 | mkdir -p "$(INTERCONNECT_DIR)" 114 | ln -sf $(notdir $<) $@ 115 | $(SLEEP_CMD) 116 | 117 | $(INTERCONNECT_GEN_HPP_FILE) $(INTERCONNECT_PHY_RPT): $(YAML_CONFIG) $(ROOT)/bin/interconnect_designer.py $(ROOT)/bin/interconnect_generate.py 118 | mkdir -p "$(INTERCONNECT_DIR)" 119 | env BUILD=$(BUILD) $(ROOT)/bin/interconnect_designer.py generate 120 | $(SLEEP_CMD) 121 | 122 | $(INPUT_JSON): $(YAML_CONFIG) $(ROOT)/bin/interconnect_designer.py 123 | mkdir -p "$(INTERCONNECT_DIR)" 124 | env BUILD=$(BUILD) $(ROOT)/bin/interconnect_designer.py annotate 125 | $(SLEEP_CMD) 126 | 127 | 128 | .PHONY: run_model 129 | run_model $(OUTPUT_JSON): ${EXE_MODEL_NAME} $(INPUT_JSON) 130 | mkdir -p "$(INTERCONNECT_DIR)" 131 | env BUILD=$(BUILD) INTERCONNECT_DISABLE_CONGESTION=$(INTERCONNECT_DISABLE_CONGESTION) ./${EXE_MODEL_NAME} 132 | rm -f "$(OUTPUT_TB_JSON)" 133 | rm -f "$(OUTPUT_JSON)" 134 | ifeq ($(CLEANUP_BA_FILES),1) 135 | rm -f "$(INPUT_JSON)" 136 | endif 137 | 138 | .PHONY: run_gen 139 | run_gen: ${EXE_GEN_NAME} 140 | mkdir -p "$(INTERCONNECT_DIR)" 141 | env BUILD=$(BUILD) INTERCONNECT_DISABLE_CONGESTION=$(INTERCONNECT_DISABLE_CONGESTION) ./${EXE_GEN_NAME} 142 | 143 | ###### Shared targets 144 | .PHONY: clean 145 | clean: cov_clean 146 | rm -f "${EXE_NAME}" "${EXE_MODEL_NAME}" "${EXE_GEN_NAME}" dump.vcd nvhls_runset.mk 147 | rm -f "${CONNECTIVITY_JSON}" "${INPUT_JSON}" "${OUTPUT_JSON}" 148 | rm -f "${CONNECTIVITY_TB_JSON}" "${OUTPUT_TB_JSON}" 149 | rm -f "${YAML_CONFIG}" "${YAML_CONFIG_TEMPLATE}" "$(INTERCONNECT_RPT)" "$(INTERCONNECT_GEN_HPP_FILE)" "$(INTERCONNECT_PHY_RPT)" 150 | rm -f "$(INTERCONNECT_DIR)/interconnect_gen_mazeRoute.pm" "$(INTERCONNECT_DIR)/interconnect_gen_mazeRoute_post.sh" 151 | ! test -d "$(INTERCONNECT_DIR)" || rmdir --ignore-fail-on-non-empty "$(INTERCONNECT_DIR)" 152 | 153 | .PHONY: sim_clean 154 | sim_clean: 155 | rm -rf *.o sim_* parallel_runs* 156 | -------------------------------------------------------------------------------- /TUTORIAL.md: -------------------------------------------------------------------------------- 1 | # IPA Tutorial 2 | 3 | Before starting this tutorial, set up the environment variables listed in README.md. 4 | 5 | This tutorial will cover an arrayed _producer_ and _consumer_ testbench. Each unit or partition has a single producer (source) and a single consumer (sink). Each producer sends randomly generated traffic to random chosen consumers as the simulation runs. The producer-consumer units are tiled in an array. 6 | 7 | Take a look within the Producer Consumer demo directory: 8 | 9 | `cd cmod/ProducerConsumer/` 10 | 11 | Take a look first at `interconnect_config.hpp`. This file declares identifiers for the top-level interconnect, partition (e.g. unit) types, and message types. In particular, one interconnect object (`pcmodulearray`), one partition type (`ProducerConsumerPart`), and one message type (`my_msg`) are included in the example. 12 | 13 | The SystemC file testbench.cpp is the main code entry point for SystemC simulations, while ProducerConsumerArray.hpp defines the main top-level sc_module under test. 14 | 15 | Compile and run the design: 16 | 17 | `make run` 18 | 19 | This command will run both the initial modeling mode of IPA (can be run seperately as `make run_model`) and the generation mode (`make run_gen`). The modeling mode emulates estimated channel latency, capacity, and congestion using automatic Connections combinational channel back annotations, but is not as accurate as generated SystemC model simulation, especially in moderate and high congestion situations. Examine the output, which will print number of messages sent and average measured latency between every pair of sinks and sources. You will notice the latency is all very similar on this initial run, between 7-11 cycles. The Makefile, testbench, and design are all parameterized to set different Producer-Consumer array sizes, though the PE_WIDTH and PE_HEIGHT make variables. By default, they set the array to 4x4 in size. Subsequently, the work directory created is called `interconnect_4x4`. Let's investigate what was generated within that direcctory: 20 | 21 | `cd interconnect_4x4` 22 | 23 | The IPA-generated interconnect config template is given in `interconnect.yaml.template`, which is symlinked to `interconnect.yaml` by the Makefile by default. However, if a user-specified interconnect config file `interconnect.yaml.user` is provided, then that is symlinked intead. 24 | 25 | `cp interconnect.yaml.template interconnect.yaml.user` 26 | 27 | Edit `interconnect.yaml.user` with your favorite text editor. You will see that the `my_msg` class is initially assigned to the `link` group (directly-connected, pairwise links between every producer and consumer) and that the default floorplan coordinates for each unit are set to the origin (x=0, y=0). Let's modify the `interconnect.yaml.user` so that a `noc` is generated (uniform mesh network-on-chip) and arrange the units in a floorplanned array. 28 | 29 | ``` 30 | groups: 31 | 2: {_conn_type: 'N:N', _dests: 16, _srcs: 16, _width: 64, name: my_msg} 32 | topologies: 33 | crossbar: 34 | groups: [] 35 | options: {bus_width: 64, crossbar_latency: 2, crossbar_x_coor: 0, crossbar_y_coor: 0, 36 | wire_prop_speed: 1} 37 | type: crossbar 38 | link: 39 | groups: [] 40 | options: {wire_prop_speed: 1} 41 | type: link 42 | noc: 43 | groups: [my_msg] 44 | options: {bus_width: 64, router_latency: 1, router_module: HybridRouter, router_spacing: 1, 45 | wire_prop_speed: 1} 46 | type: noc 47 | units: 48 | my_testbench.dut.pemodulearray_inst.pe_part_inst_0: {x_coor: 0, y_coor: 0} 49 | my_testbench.dut.pemodulearray_inst.pe_part_inst_1: {x_coor: 1, y_coor: 0} 50 | my_testbench.dut.pemodulearray_inst.pe_part_inst_2: {x_coor: 2, y_coor: 0} 51 | my_testbench.dut.pemodulearray_inst.pe_part_inst_3: {x_coor: 3, y_coor: 0} 52 | my_testbench.dut.pemodulearray_inst.pe_part_inst_4: {x_coor: 0, y_coor: 1} 53 | my_testbench.dut.pemodulearray_inst.pe_part_inst_5: {x_coor: 1, y_coor: 1} 54 | my_testbench.dut.pemodulearray_inst.pe_part_inst_6: {x_coor: 2, y_coor: 1} 55 | my_testbench.dut.pemodulearray_inst.pe_part_inst_7: {x_coor: 3, y_coor: 1} 56 | my_testbench.dut.pemodulearray_inst.pe_part_inst_8: {x_coor: 0, y_coor: 2} 57 | my_testbench.dut.pemodulearray_inst.pe_part_inst_9: {x_coor: 1, y_coor: 2} 58 | my_testbench.dut.pemodulearray_inst.pe_part_inst_10: {x_coor: 2, y_coor: 2} 59 | my_testbench.dut.pemodulearray_inst.pe_part_inst_11: {x_coor: 3, y_coor: 2} 60 | my_testbench.dut.pemodulearray_inst.pe_part_inst_12: {x_coor: 0, y_coor: 3} 61 | my_testbench.dut.pemodulearray_inst.pe_part_inst_13: {x_coor: 1, y_coor: 3} 62 | my_testbench.dut.pemodulearray_inst.pe_part_inst_14: {x_coor: 2, y_coor: 3} 63 | my_testbench.dut.pemodulearray_inst.pe_part_inst_15: {x_coor: 3, y_coor: 3} 64 | ``` 65 | 66 | Note that units for `x_coor`, `y_coor`, `router_spacing`, `crossbar_x_coor`, and `crossbar_y_coor` are unitless but must be consistent with `wire_prop_speed`, which is in cycles per unit-distance. For example, if 1 unit of distance = 100 micron then wire_prop_speed (wired route propogation speed) is cycles per 100 micron of distance, and `x_coor` / `y_coor` are in intervals of 100 micron. The coordinates and `wire_prop_speed` given need not be integers; floats are allowed. 67 | 68 | Save the file, and go back up one level above the `interconnect_4x4` directory. 69 | 70 | ` cd ..` 71 | 72 | Now, rerun the simulations. 73 | 74 | `make run` 75 | 76 | Inspect the output now, and you should see higher and more varied measured latency (10-80 cycles), due to the increased floorplan distance between the units, the hop latency through the routers, and the cross-sectional bandwidth limits since all of the producers are sending messages every cycle. Let's dial down the activity rate of the producers to 5% per cycle of activity: 77 | 78 | `make clean; make run PE_ACTIVITY=0.05` 79 | 80 | The measured latency should now be lower (10-35 cycles) because there is less congestion in the network. 81 | 82 | Now, let's now exam the generated interconnect code, in `interconnect_4x4/interconnect_gen.hpp`. This file includes synthesizable code to encode the message (including destination information), interfaces to connect the unit message ports with the routers, and instantiations of the routers themselves. The top-level interconnect object is named `Grout_0` and thus a `Grout_0.hpp` is needed in the source code directory for Catapult HLS to refer to. 83 | 84 | Next, bring the generated interconnect through high-level synthesis: 85 | 86 | ``` 87 | cd ../../../hls/interconnect/ProducerConsumer/Grout_0/` 88 | make 89 | ``` 90 | 91 | Once HLS is complete, the resulting RTL can be viewed in `4x4/Grout_0.v1/concat_rtl.v`. 92 | -------------------------------------------------------------------------------- /doc/layout.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Interconnect Prototyping Assistant (IPA) 2 | ======== 3 | 4 | # Getting Started 5 | 6 | Interconnect Prototyping Assistant (IPA) is a VLSI interconnect modeling and generation framework built atop [MatchLib](https://github.com/NVlabs/matchlib) and [Connections](https://github.com/hlslibs/matchlib_connections) libraries. 7 | 8 | ## Tool versions 9 | 10 | MatchLib is regressed against the following tool/dependency verions: 11 | 12 | * `gcc` - 4.9.3 (with C++11) 13 | * `systemc` - 2.3.1 14 | * `boost` - 1.68.0 15 | * `doxygen` - 1.8.11 16 | * `make` - 3.82 17 | * `catapult` - 10.5a 18 | * `connections` - included with catapult 19 | * `vcs` - 2017.03-SP2-11 20 | * `verdi` - 2017.12-SP2-11 21 | * `rapidjson` - v1.1.0 22 | * `python` - 3.4.2 23 | 24 | ## Environment requirements 25 | 26 | Makefiles assume the appropriate definition of the following environment variables: 27 | 28 | * `MATCHLIB_HOME` 29 | * `BOOST_HOME` 30 | * `SYSTEMC_HOME` 31 | * `CATAPULT_HOME` 32 | * `VCS_HOME` 33 | * `NOVAS_INST_DIR` 34 | 35 | In addition, the boost and systemc library locations are expected to be in `LD_LIBRARY_PATH`. 36 | 37 | ## Build and run example 38 | 39 | ### Recursively checkout matchlib submodule 40 | 41 | git submodule update --init --recursive 42 | 43 | MatchLib will be downloaded to matchlib/ and rapidjson to matchlib/rapidjson/. 44 | 45 | ### C++ compile and simulate 46 | cd cmod/ProducerConsumer/ 47 | make 48 | make run 49 | 50 | ### HLS run 51 | cd hls/ProducerConsumer/Grout_0 52 | make hls 53 | 54 | # Directory structure 55 | 56 | * `TUTORIAL.md` information on getting started 57 | * `bin` contains Python scripts for interconnect prototyping assistant 58 | * `cmod/include/interconnect/*.h` contains header files for functions and classes for Interconnect Prototyping Assistant 59 | * `cmod/ProducerConsumer/` sub-directories containing example IPA use 60 | * `hls/ProducerConsumer/Grout_0/` sub-directories contain HLS scripts for IPA implementation 61 | * `doc/` contains Makefiles for building Doxygen-based documentation 62 | 63 | # API Reference 64 | 65 | Supported language: SystemC. 66 | 67 | See Doxygen generated documentation in `doc/` with more information. 68 | 69 | ## Macro Reference 70 | 71 | ### Message and Unit Macros 72 | 73 | `IC_MESSAGE(MSG_TYPE)`: Message data type for message classes of `MSG_TYPE`, whose payload is defined in the interconnect config header. Can be used to instantiate new messages (ex. `IC_Message(my_msg) new_message;`), or passed as a template parameter to Connections ports (ex. `Connections::In`). 74 | 75 | `IC_TO(DEST_ID)`: Used along with the `<<` operator to append dest_id of `DEST_ID` to the message's destination list. Multiple destinations can be added for a single message. For example, `im << IC_TO(42) << IC_TO(86) << IC_TO(27);` will add destination indexes of 42, 86, and 27 to message `im`. Destination indexes are assigned in the IC_BIND_PART macro described below. `DEST_ID` may be a runtime variable. 76 | 77 | ### Partition Macros 78 | 79 | `IC_HAS_INTERFACE(PART_REF)`: Added to the partition class declaration. Declares that the class is a partition of name `PART_REF`. PART_REF should match the C++ class name and the name given to `IC_ADD_PARTITION()` in the interconnect config header. 80 | 81 | `IC_BIND_PORT(PORT_INST)`: Added to the partition class constructor. Registered the `Connections::In` and `::Out` port `PORT_INST` with the Interconnect API. Should only be called once per port. 82 | 83 | ### Top-Level Macros 84 | 85 | `IC_HAS_INTERCONNECT(INTERCONNECT_REF)`: Added to the declaration of the top-level class. `INTERCONNECT_REF` should match the name given in `IC_ADD_INTERCONNECT()` in the interconnect config header. 86 | 87 | `IC_BIND_PART(PART_INST, DEST_ID)`: Added to the top-level class's constructor. Registers partition instance `PART_INST` with the Interconnect API, and assigns it a destination index of `DEST_ID` that can be references with `IC_TO(). `DEST_ID` must be a constant at compile time. 88 | 89 | ## Interconnect Configuration File Reference 90 | 91 | File type: YAML 92 | 93 | ### Interconnect Topology Options 94 | 95 | #### Common YAML Options 96 | 97 | `wire_prop_speed`: Sets the speed in clock cycles of wires per unit-distance. For `X` units of manhattan distance, `ceil(X/wire_prop_speed)` of clock cycles are needed. (default: 1.0) 98 | `extra_latency` (modeling only): Extra latency to add to an interconnect type, in clock cycles. Useful for sensitivity analysis. Will also be added to generated interconnect as increased number of retiming stages. (default: 0) 99 | `extra_capacity` (modeling only): Extra link capacity to add to an interconnect type, in clock cycles. Useful for sensitivity analysis. Will also be added to generated interconnect as increased number buffers within retiming stages. (default: 0) 100 | 101 | #### Directly-Connected Links 102 | 103 | YAML interconnect topology type set to `link`. 104 | 105 | No additional supported options beyond the common options set above. 106 | 107 | #### Centralized Crossbar 108 | 109 | YAML interconnect topology type set to `crossbar`. 110 | 111 | Supported YAML options: 112 | 113 | * `crossbar_latency` (modeling only): Cycle latency through the crossbar, from input to output. (default: 2) 114 | * `crossbar_bw` (modeling only): Amount of bandwidth supported by the crossbar as a multiples of the max message size through the crossbar. -1 indicates no limits. (default: -1) 115 | * `crossbar_x_coor`: x coordinate of centralized crossbar on floorplan. (default: 0.0) 116 | * `crossbar_y_coor`: y coordinate of centralized crossbar on floorplan. (default: 0.0) 117 | 118 | #### Uniform Mesh Network-on-Chip 119 | 120 | YAML interconnect topology type set to `noc`. 121 | 122 | Supported YAML options: 123 | 124 | `router_latency` (modeling only): Cycle latency through a router, from input to output. It should be set for what the router can be scheduled for in HLS. (default: 1) 125 | `router_spacing`: Unit distance between routers on floorplan. (default: 1.0) 126 | `bus_width`: Flit width of the generated network-on-chip. Messages are serialized and deserialized to this width. (default: 64) 127 | Unit Options 128 | 129 | #### Supported options for units: 130 | 131 | `x_coor`: X-coordinate in arbitrary units of partition instance on floorplan,. (default: 0.0) 132 | `y_coor`: Y-coordinate in arbitrary units of partition instance on floorplan. (default: 0.0) 133 | `inject` (modeling only): When True marks that message to/from this partition should not be annotated with latency or capacity during modeling. Useful for testbench partitions that don't have physical meaning. (default: False) 134 | 135 | # Known Issues 136 | 137 | CONNECTIONS_FAST_SIM is currently not supported by IPA (will not compile). 138 | 139 | Newer versions of Connections bundled with Catapult are supported, but a compiler flag may need to be used. In particular, Connections multi clock support will not compile without the `-DCAP_CONNECTIONS_MULTI_CLK` compiler flag. The sample Makefile in cmod/interconnect/ProducerConsumer/ attempts to detect the Catapult version, and set the flag appropriately. IPA always uses the first registered clock (assumes one clock for the system). Multi clock support within IPA will come at a later time. 140 | 141 | # Contributors 142 | 143 | MatchLib originated as a project of [NVIDIA Research](https://research.nvidia.com). 144 | 145 | Contributors to the initial open-source release (alphabetical): Ben Keller, Brucek Khailany, Nathaniel Pinckney, Rangharajan Venkatesan 146 | 147 | IPA's channel annotation feature is based on MatchLib's implementation and dependent on RapidJSON released under the MIT License. 148 | 149 | # Attribution 150 | 151 | If used for research, please cite the [ICCAD paper](https://d1qx31qr3h6wln.cloudfront.net/publications/IPA_ICCAD_2021_Manuscript.pdf): 152 | 153 | Nathaniel Pinckney, Rangharajan Venkatesan, Ben Keller, and Brucek Khailany, "IPA: Floorplan-Aware SystemC Interconnect Performance Modeling and Generation for HLS-based SoCs," in International Conference On Computer-Aided Design (ICCAD), November 2021. 154 | 155 | -------------------------------------------------------------------------------- /cmod/include/interconnect/InterconnectHandlerFixed.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: Copyright (c) 2019-2021 NVIDIA CORPORATION & 3 | * AFFILIATES. All rights reserved. 4 | * SPDX-License-Identifier: Apache-2.0 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | #ifndef __INTERCONNECT_HANDLER_FIXED_H__ 20 | #define __INTERCONNECT_HANDLER_FIXED_H__ 21 | 22 | #include "Interconnect.hpp" 23 | 24 | namespace interconnect { 25 | 26 | #ifdef CONNECTIONS_SIM_ONLY 27 | 28 | // CRTP Mix-in to handle fixed ports 29 | template 30 | class InterconnectHandlerFixed { 31 | 32 | protected: 33 | // Declarations 34 | class FixedSrcHandlerIface; 35 | class FixedDestHandlerIface; 36 | 37 | public: 38 | typedef IC interconnect_t; 39 | typedef IM IM_t; 40 | typedef Connections::OutBlocking IM_src_t; 41 | typedef Connections::InBlocking IM_dest_t; 42 | 43 | std::map fixed_srcs; 44 | std::map fixed_dests; 45 | 46 | template 47 | void BindFixed(Connections::OutBlocking &p) { 48 | interconnect_t &ic = static_cast(*this); 49 | 50 | FixedSrcHandler *is = new FixedSrcHandler(ic, p); 51 | ic.template Bind(is->im); 52 | fixed_srcs[&p] = is; 53 | 54 | std::string port_name = p.val.name(); 55 | if (port_name.substr(port_name.length() - 4, 4) == "_val") { 56 | port_name.erase(port_name.length() - 4, 4); 57 | } 58 | ic.set_src_name(&is->im, port_name); 59 | } 60 | 61 | template 62 | void BindFixed(Connections::InBlocking &p) { 63 | interconnect_t &ic = static_cast(*this); 64 | 65 | FixedDestHandler *ij = new FixedDestHandler(ic, p); 66 | ic.template Bind(ij->im); 67 | fixed_dests[&p] = ij; 68 | 69 | std::string port_name = p.val.name(); 70 | if (port_name.substr(port_name.length() - 4, 4) == "_val") { 71 | port_name.erase(port_name.length() - 4, 4); 72 | } 73 | ic.set_dest_name(&ij->im, port_name); 74 | } 75 | 76 | template 77 | void set_fixed(Connections::OutBlocking &s, 78 | Connections::InBlocking &d) { 79 | interconnect_t &ic = static_cast(*this); 80 | 81 | NVHLS_ASSERT(ic.find_src_idx(fixed_srcs[&s]->im_ptr) != -1); 82 | NVHLS_ASSERT(ic.find_dest_idx(fixed_dests[&d]->im_ptr) != -1); 83 | 84 | NVHLS_ASSERT(fixed_srcs.find(&s) != fixed_srcs.end() && fixed_srcs[&s]); 85 | NVHLS_ASSERT(fixed_dests.find(&d) != fixed_dests.end() && fixed_dests[&d]); 86 | 87 | fixed_srcs[&s]->src_idx = ic.find_src_idx(fixed_srcs[&s]->im_ptr); 88 | fixed_srcs[&s]->dest_idx = ic.find_dest_idx(fixed_dests[&d]->im_ptr); 89 | 90 | fixed_dests[&d]->src_idx = ic.find_src_idx(fixed_srcs[&s]->im_ptr); 91 | fixed_dests[&d]->dest_idx = ic.find_dest_idx(fixed_dests[&d]->im_ptr); 92 | } 93 | 94 | protected: 95 | // Interface for handler that is template-less 96 | struct FixedSrcHandlerIface { 97 | public: 98 | typename interconnect_t::IM_src_t *im_ptr; 99 | typename interconnect_t::idx_t src_idx; 100 | typename interconnect_t::idx_t dest_idx; 101 | }; 102 | 103 | // class FixedSrcHandler 104 | template 105 | class FixedSrcHandler : public sc_module, 106 | public Connections::Blocking_abs, 107 | public FixedSrcHandlerIface { 108 | SC_HAS_PROCESS(FixedSrcHandler); 109 | 110 | public: 111 | interconnect_t ⁣ 112 | 113 | typename interconnect_t::IM_src_t im; 114 | Connections::OutBlocking &p; 115 | sc_signal::width> > int_msg; 116 | sc_signal int_rdy; 117 | sc_signal int_val; 118 | 119 | explicit FixedSrcHandler(interconnect_t &ic_, 120 | Connections::OutBlocking &p_) 121 | : sc_module(sc_module_name(sc_gen_unique_name("fixed_src_handler"))), 122 | ic(ic_), 123 | im(sc_gen_unique_name("im")), 124 | p(p_), 125 | int_msg("int_msg"), 126 | int_rdy("int_rdy"), 127 | int_val("int_val") { 128 | // Pass up to handler interface 129 | this->im_ptr = &im; 130 | 131 | p.msg(int_msg); 132 | p.rdy(int_rdy); 133 | p.val(int_val); 134 | 135 | #ifdef CONNECTIONS_SIM_ONLY 136 | // SC_METHOD(do_bypass); 137 | declare_method_process(do_bypass_handle, sc_gen_unique_name("do_bypass"), 138 | SC_CURRENT_USER_MODULE, do_bypass); 139 | this->sensitive << p.msg << p.val << im.rdy; 140 | 141 | im.disable_spawn(); 142 | #endif 143 | } 144 | 145 | void do_bypass() { 146 | bool is_val = int_val.read(); 147 | 148 | // Set val and msg respectively 149 | im.val.write(is_val); 150 | int_rdy.write(im.rdy.read()); 151 | 152 | // Only convert if is_val (to prevent X's) 153 | if (is_val) { 154 | // Convert msg 155 | Marshaller::width> marshaller(int_msg.read()); 156 | Wrapped result; 157 | result.Marshall(marshaller); 158 | 159 | // Create the InterconnectMessage 160 | typename interconnect_t::IM_t im_msg; 161 | Message m = result.val; 162 | im_msg.set_msg(ic, m); 163 | im_msg.set_src_idx(ic, ic.get_src_from_idx(this->src_idx)); 164 | 165 | typename interconnect_t::route_t *current_route = ic.create_route(); 166 | current_route->add_dest(this->dest_idx); 167 | im_msg.set_route(ic, current_route); 168 | delete current_route; 169 | 170 | // Convert back to Marshall'd form 171 | Marshaller::width> im_marshaller; 172 | Wrapped im_wm(im_msg); 173 | im_wm.Marshall(im_marshaller); 174 | im.msg.write(im_marshaller.GetResult()); 175 | } 176 | } 177 | }; // class FixedSrcHandler 178 | 179 | // Interface for handler that is template-less 180 | class FixedDestHandlerIface { 181 | public: 182 | typename interconnect_t::IM_dest_t *im_ptr; 183 | typename interconnect_t::idx_t src_idx; 184 | typename interconnect_t::idx_t dest_idx; 185 | }; 186 | 187 | // class FixedDestHandler 188 | template 189 | class FixedDestHandler : public sc_module, 190 | public Connections::Blocking_abs, 191 | public FixedDestHandlerIface { 192 | SC_HAS_PROCESS(FixedDestHandler); 193 | 194 | public: 195 | interconnect_t ⁣ 196 | typename interconnect_t::IM_dest_t im; 197 | Connections::InBlocking &p; 198 | sc_signal::width> > int_msg; 199 | sc_signal int_rdy; 200 | sc_signal int_val; 201 | 202 | explicit FixedDestHandler(interconnect_t &ic_, 203 | Connections::InBlocking &p_) 204 | : sc_module(sc_module_name(sc_gen_unique_name("interconnect_joiner"))), 205 | ic(ic_), 206 | im(sc_gen_unique_name("im")), 207 | p(p_), 208 | int_msg("int_msg"), 209 | int_rdy("int_rdy"), 210 | int_val("int_val") { 211 | // Pass up to handler interface 212 | this->im_ptr = &im; 213 | 214 | p.msg(int_msg); 215 | p.rdy(int_rdy); 216 | p.val(int_val); 217 | 218 | #ifdef CONNECTIONS_SIM_ONLY 219 | // SC_METHOD(do_bypass); 220 | declare_method_process(do_bypass_handle, sc_gen_unique_name("do_bypass"), 221 | SC_CURRENT_USER_MODULE, do_bypass); 222 | this->sensitive << im.msg << im.val << p.rdy; 223 | 224 | im.disable_spawn(); 225 | #endif 226 | } 227 | 228 | void do_bypass() { 229 | bool is_val = im.val.read(); 230 | 231 | // Set val and msg respectively 232 | int_val.write(is_val); 233 | im.rdy.write(int_rdy.read()); 234 | 235 | // Only convert if is_val (to prevent X's) 236 | if (is_val) { 237 | // Convert msg from bits 238 | Marshaller::width> im_marshaller( 239 | im.msg.read()); 240 | Wrapped im_result; 241 | im_result.Marshall(im_marshaller); 242 | typename interconnect_t::IM_t im_msg = im_result.val; 243 | 244 | // Convert msg to bits and write 245 | Marshaller::width> marshaller; 246 | Message m; 247 | im_msg.get_msg(ic, m); 248 | Wrapped wm(m); 249 | wm.Marshall(marshaller); 250 | int_msg.write(marshaller.GetResult()); 251 | } 252 | } 253 | }; // classFixedDestHandler 254 | 255 | }; // class InterconnecHandlerFixed 256 | 257 | #endif 258 | }; 259 | 260 | #endif // __INTERCONNECT_HANDLER_FIXED_H__ 261 | -------------------------------------------------------------------------------- /doc/extra_stylesheet.css: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * General 3 | *****************************************************************************/ 4 | 5 | body, div, p, dl { 6 | font: 12px/22px arial,sans-serif; 7 | } 8 | 9 | table { 10 | font: 12px/18px arial,sans-serif; 11 | } 12 | 13 | body { 14 | background-color: #EEE; 15 | color: black; 16 | margin: 0; 17 | } 18 | 19 | /****************************************************************************** 20 | * Page width 21 | *****************************************************************************/ 22 | 23 | div.contents { 24 | width: 1600px; 25 | margin-left: auto; 26 | margin-right: auto; 27 | margin-top: 10px; 28 | padding-left: 60px; 29 | padding-right: 60px; 30 | padding-bottom: 20px; 31 | background-color: white; 32 | border-radius: 8px; 33 | border: 1px solid #AAA; 34 | } 35 | 36 | div.contents p:first-of-type { 37 | color: #666666; 38 | } 39 | 40 | div.textblock p:first-of-type { 41 | color: #666666; 42 | } 43 | 44 | div.header { 45 | width: 1600px; 46 | margin-left: auto; 47 | margin-right: auto; 48 | margin-top: 10px; 49 | padding-left: 60px; 50 | padding-right: 60px; 51 | background-image: none; 52 | background-color: white; 53 | border-radius: 8px; 54 | border: 1px solid #AAA; 55 | } 56 | 57 | #top { 58 | width: 1000px; 59 | margin-left: auto; 60 | margin-right: auto; 61 | } 62 | 63 | #MSearchBox { 64 | margin-top: 6px; 65 | margin-left: 330px; 66 | right: auto; 67 | } 68 | 69 | address.footer { 70 | width: 880px; 71 | margin-left: auto; 72 | margin-right: auto; 73 | } 74 | 75 | 76 | 77 | /****************************************************************************** 78 | * Navigation bar 79 | *****************************************************************************/ 80 | 81 | 82 | #projectname { 83 | font: bold 300% "courier new",courier,monospace; 84 | } 85 | 86 | div.headertitle { 87 | padding: 5px 5px 0px 0px; 88 | } 89 | 90 | div.title { 91 | color: #2B4068; 92 | padding-top: 8px; 93 | padding-bottom: 8px; 94 | font: bold 115% "courier new",courier,monospace; 95 | padding-left: 40px; 96 | text-indent: -40px; 97 | } 98 | 99 | #navrow1 { 100 | border-top: 1px solid #7092BE; 101 | } 102 | 103 | 104 | #titlearea { 105 | border-bottom: none; 106 | } 107 | 108 | .tablist li { 109 | line-height: 30px; 110 | background-image: url('tab_b_alt.png'); 111 | } 112 | 113 | .tablist a, .navpath li.navelem a, .tablist li.current a, .tablist a:hover { 114 | text-shadow: none; 115 | } 116 | 117 | .navpath li.navelem a { 118 | color: white; 119 | } 120 | 121 | .navpath ul { 122 | background-image: url('tab_a.png'); 123 | border: none; 124 | } 125 | 126 | li.navelem { 127 | padding: 0 20px; 128 | font-style:italic; 129 | } 130 | 131 | .tabs { 132 | font-size: 12px; 133 | } 134 | 135 | .tabs2, .tabs3, .tabs4 { 136 | font-size: 11px; 137 | } 138 | 139 | .tabs, .tabs2, .tabs3, .tabs4 { 140 | border-right: 1px solid #7092BE; 141 | border-left: 1px solid #7092BE; 142 | background-image: url('tab_b_alt.png'); 143 | } 144 | 145 | 146 | /****************************************************************************** 147 | * TestFooter 148 | *****************************************************************************/ 149 | 150 | hr.footer { 151 | display: none; 152 | } 153 | 154 | .footer { 155 | background-color: #EEE; 156 | } 157 | 158 | /****************************************************************************** 159 | * Text content 160 | *****************************************************************************/ 161 | 162 | .contents a { 163 | color: #5675B2; 164 | } 165 | 166 | .contents a:visited { 167 | color: #5675B2; 168 | } 169 | 170 | h2 { 171 | margin-top: 30px; 172 | margin-bottom: 0px; 173 | margin-left: 40px; 174 | font-weight: bold; 175 | font-size: 110%; 176 | color: #2B4068; 177 | } 178 | 179 | h1, h2.groupheader, tr.heading h2 { 180 | margin-left: -10px; 181 | margin-top: 30px; 182 | margin-bottom: 10px; 183 | padding-left: 8px; 184 | padding-top: 6px; 185 | padding-bottom: 4px; 186 | font: bold 110% "courier new",courier,monospace; 187 | color: white; 188 | background-image: url('tab_a.png'); 189 | background-color: white; 190 | border-top: none; 191 | border-bottom: none; 192 | border-radius: 8px; 193 | background-repeat: repeat-x; 194 | background-position: 0 -5px; 195 | width: 101%; 196 | } 197 | 198 | 199 | div.textblock { 200 | margin-top: 20px; 201 | } 202 | 203 | 204 | p.startli, p.startdd, p.starttd { 205 | margin-top: 1em; 206 | margin-bottom: 1em; 207 | } 208 | 209 | /** Workaround where image in brief class description creates unwanted "." caption */ 210 | .caption { 211 | display: none; 212 | } 213 | 214 | .centercaption { 215 | font-weight: bold; 216 | font-size: 80%; 217 | text-align: center; 218 | } 219 | 220 | dt { 221 | font-style: italic; 222 | } 223 | 224 | .memproto, dl.reflist dt { 225 | padding-top: 20px; 226 | border-top: 2px solid #888; 227 | border-left: 2px solid #888; 228 | border-right: 2px solid #888; 229 | border-bottom: 1px solid #ECECEC; 230 | background-image: none; 231 | background-color: #FAFAFB; 232 | text-shadow: none; 233 | box-shadow: 3px 3px 30px rgba(0, 0, 0, 0.05); 234 | } 235 | 236 | div.memdoc { 237 | padding-bottom: 20px; 238 | border-bottom: 2px solid #888; 239 | border-left: 2px solid #888; 240 | border-right: 2px solid #888; 241 | background-image: none; 242 | background-color: #FFF; 243 | box-shadow: 3px 3px 30px rgba(0, 0, 0, 0.05); 244 | } 245 | 246 | div.memdoc p { 247 | margin-left: 40px; 248 | color: #666666; 249 | } 250 | 251 | div.memdoc dt { 252 | color: black; 253 | } 254 | 255 | div.toc { 256 | box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.15); 257 | margin: 10px -10px 40px 20px; 258 | width: 225px; 259 | } 260 | 261 | /** Detailed class name and template params */ 262 | div.textblock h3 { 263 | font: 11px/16px "courier new",courier,monospace; 264 | color: cadetblue; 265 | white-space: pre-wrap; 266 | margin-bottom: 40px; 267 | } 268 | 269 | table.memname tbody tr:first-child td:nth-child(2) { 270 | padding-top: 50px; 271 | } 272 | 273 | table.memname tbody tr td { 274 | } 275 | 276 | table.memname tbody tr td.paramtype { 277 | padding-left: 40px; 278 | } 279 | 280 | table.memname tbody tr td.memname { 281 | position: absolute; 282 | width: 760px; 283 | } 284 | 285 | dl.section.user b { 286 | color: #222; 287 | } 288 | 289 | dl.section.user a b { 290 | color: #5675B2; 291 | } 292 | 293 | dl.section.user a:visited b { 294 | color: #5675B2; 295 | } 296 | 297 | dl.section.user dd { 298 | margin-right: 40px; 299 | color: #666666; 300 | } 301 | 302 | /****************************************************************************** 303 | * Class/namespace indices 304 | *****************************************************************************/ 305 | 306 | .directory tr.even { 307 | border-bottom: 1px solid rgba(0, 0, 0, 0.05); 308 | border-top: 1px solid rgba(0, 0, 0, 0.05); 309 | background-color: white; 310 | } 311 | 312 | td.entry { 313 | padding-top: 3px; 314 | } 315 | 316 | 317 | /****************************************************************************** 318 | * Code syntax highlighting 319 | *****************************************************************************/ 320 | 321 | div.line { 322 | line-height: normal; 323 | font-size: 11px; 324 | } 325 | 326 | span.comment { 327 | color: green; 328 | } 329 | 330 | span.preprocessor, span.keyword, span.keywordflow, span.keywordtype { 331 | color: darkred; 332 | font-weight: bold; 333 | } 334 | 335 | a.code, a.code:visited { 336 | color: #4665A2; 337 | font-weight: bold; 338 | } 339 | 340 | div.fragment { 341 | color: black; 342 | border-color: #DDD; 343 | padding: 14px 14px 14px 14px; 344 | margin-top: 20px; 345 | margin-bottom: 20px; 346 | background-color: white; 347 | border-radius: 8px 8px 8px 8px; 348 | box-shadow: 3px 3px 20px rgba(0, 0, 0, 0.04); 349 | border-style: dashed; 350 | } 351 | 352 | 353 | 354 | 355 | /****************************************************************************** 356 | * Brief member descriptions 357 | *****************************************************************************/ 358 | 359 | .mdescLeft, .mdescRight, .memItemLeft, .memItemRight, .memTemplItemLeft, .memTemplItemRight, .memTemplParams { 360 | background-color: white; 361 | } 362 | 363 | .directory td.desc { 364 | padding-bottom: 1px; 365 | } 366 | 367 | .params, .paramname, .retval, .paramname { 368 | vertical-align: top; 369 | padding-right: 8px; 370 | } 371 | 372 | dl.tparams, dl.params { 373 | color: black; 374 | } 375 | 376 | div.memitem { 377 | margin-bottom: 40px; 378 | margin-top: 40px; 379 | } 380 | 381 | .memTemplParams, td.memTemplItemLeft, td.memItemLeft { 382 | padding-left: 20px; 383 | font-family: "courier new",courier,monospace; 384 | font-size: 85%; 385 | } 386 | 387 | .memTemplParams { 388 | color: cadetblue; 389 | white-space: normal; 390 | } 391 | 392 | td.memTemplItemRight, td.memItemRight { 393 | vertical-align: top; 394 | font-family: "courier new",courier,monospace; 395 | } 396 | 397 | td.memItemRight a.el:first-of-type, td.memTemplItemRight a.el:first-of-type { 398 | color: darkGoldenrod; 399 | } 400 | 401 | td.memTemplItemLeft, td.memItemLeft { 402 | color: #A286A0; 403 | } 404 | 405 | div.groupHeader { 406 | margin-left: 10px; 407 | margin-bottom: 12px; 408 | font-style:italic; 409 | } 410 | 411 | td.memSeparator { 412 | border-bottom: none; 413 | border-top: 1px dashed #999; 414 | line-height: 8px; 415 | } 416 | 417 | td.mdescLeft, td.mdescRight { 418 | padding-top: 10px; 419 | padding-bottom: 20px; 420 | } 421 | 422 | td.mdescLeft{ 423 | color: #666666; 424 | } 425 | 426 | td.mdescRight { 427 | color: #666666; 428 | } 429 | 430 | td.memname, td.paramtype, div.memtemplate { 431 | font-family: "courier new",courier,monospace; 432 | } 433 | 434 | .memtemplate { 435 | padding-bottom: 10px; 436 | line-height: 180%; 437 | font-size: 85%; 438 | color: cadetblue; 439 | } 440 | 441 | td.paramname { 442 | font-family: "courier new",courier,monospace; 443 | font-weight: bold; 444 | color: darkred; 445 | } 446 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /cmod/include/interconnect/InterconnectHandlerFlit.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: Copyright (c) 2019-2021 NVIDIA CORPORATION & 3 | * AFFILIATES. All rights reserved. 4 | * SPDX-License-Identifier: Apache-2.0 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | #ifndef __INTERCONNECT_HANDLER_FLIT_H__ 20 | #define __INTERCONNECT_HANDLER_FLIT_H__ 21 | 22 | #include "Interconnect.hpp" 23 | 24 | #include 25 | 26 | namespace interconnect { 27 | 28 | #ifdef CONNECTIONS_SIM_ONLY 29 | // CRTP Mix-in to handle fixed ports 30 | template 31 | class InterconnectHandlerFlit { 32 | 33 | protected: 34 | // Declarations 35 | class FlitSrcHandlerIface; 36 | class FlitDestHandlerIface; 37 | 38 | public: 39 | typedef IC interconnect_t; 40 | typedef IM IM_t; 41 | typedef Connections::OutBlocking IM_src_t; 42 | typedef Connections::InBlocking IM_dest_t; 43 | 44 | typedef typename InterconnectTypeConfig::idx_t idx_t; 45 | typedef typename InterconnectTypeConfig::dest_map_idx_t dest_map_idx_t; 46 | 47 | std::map flit_srcs; 48 | std::map flit_dests; 49 | 50 | template 52 | void BindFlit(Connections::OutBlocking &p) { 53 | interconnect_t &ic = static_cast(*this); 54 | 55 | auto *is = 56 | new FlitSrcHandler( 57 | ic, p, MSG_TYPE::map_id); 58 | is->clk(ic.clk); 59 | is->rst(ic.rst); 60 | ic.template Bind(is->im, MSG_TYPE::msg_id, MSG_TYPE::name); 61 | flit_srcs[&p] = is; 62 | 63 | std::string port_name = p.val.name(); 64 | if (port_name.substr(port_name.length() - 4, 4) == "_val") { 65 | port_name.erase(port_name.length() - 4, 4); 66 | } 67 | ic.set_src_name(&is->im, port_name); 68 | } 69 | 70 | template 72 | void BindFlit(Connections::InBlocking &p) { 73 | interconnect_t &ic = static_cast(*this); 74 | 75 | auto *ij = 76 | new FlitDestHandler( 77 | ic, p, MSG_TYPE::map_id); 78 | ij->clk(ic.clk); 79 | ij->rst(ic.rst); 80 | ic.template Bind(ij->im, MSG_TYPE::msg_id, MSG_TYPE::name); 81 | flit_dests[&p] = ij; 82 | 83 | std::string port_name = p.val.name(); 84 | if (port_name.substr(port_name.length() - 4, 4) == "_val") { 85 | port_name.erase(port_name.length() - 4, 4); 86 | } 87 | ic.set_dest_name(&ij->im, port_name); 88 | } 89 | 90 | template 92 | void BindFlit(Connections::OutBlocking &p, 93 | unsigned int custom_id) { 94 | this->BindFlit(p); 95 | // No map for sources 96 | } 97 | 98 | template 100 | void BindFlit(Connections::InBlocking &p, 101 | unsigned int custom_id) { 102 | interconnect_t &ic = static_cast(*this); 103 | 104 | NVHLS_ASSERT_MSG(custom_id > 0, 105 | "Must set a custom destination index for In flit ports!"); 106 | ic.create_dest_map(MSG_TYPE::map_id, true); 107 | this->BindFlit(p); 108 | this->config_dest_map_idx_flit(MSG_TYPE::map_id, custom_id, p); 109 | } 110 | 111 | template 112 | void config_dest_map_idx_flit(dest_map_idx_t dest_map_idx, idx_t dest_idx, 113 | Connections::InBlocking &p) { 114 | interconnect_t &ic = static_cast(*this); 115 | 116 | ic.config_dest_map_idx( 117 | dest_map_idx, dest_idx, 118 | *(flit_dests[(Connections::Blocking_abs *)&p]->im_ptr)); 119 | } 120 | 121 | protected: 122 | // Interface for handler that is template-less 123 | struct FlitSrcHandlerIface { 124 | public: 125 | typename interconnect_t::IM_src_t *im_ptr; 126 | }; 127 | 128 | template 130 | class FlitSrcHandler : public sc_module, 131 | public Connections::Blocking_abs, 132 | public FlitSrcHandlerIface { 133 | SC_HAS_PROCESS(FlitSrcHandler); 134 | 135 | protected: 136 | static const int BUFFERSIZE = 17; 137 | 138 | dest_map_idx_t dest_map_idx; 139 | 140 | // Local State 141 | sc_lv::width * BUFFERSIZE> packet_buf; 142 | unsigned int packet_buf_pos; 143 | FIFO num_flit_fifo; 144 | NVUINT5 num_flits; 145 | typename interconnect_t::route_t *current_route; 146 | 147 | public: 148 | sc_in_clk clk; 149 | sc_in rst; 150 | 151 | interconnect_t ⁣ 152 | 153 | typename interconnect_t::IM_src_t im; 154 | Connections::OutBlocking &p; 155 | Connections::Combinational int_chan; 156 | 157 | explicit FlitSrcHandler(interconnect_t &ic_, 158 | Connections::OutBlocking &p_, 159 | dest_map_idx_t dest_map_idx_) 160 | : sc_module(sc_module_name(sc_gen_unique_name("flit_src_handler"))), 161 | dest_map_idx(dest_map_idx_), 162 | packet_buf_pos(0), 163 | clk("clk"), 164 | rst("rst"), 165 | ic(ic_), 166 | im(sc_gen_unique_name("im")), 167 | p(p_), 168 | int_chan("int_chan") { 169 | // Pass up to handler interface 170 | this->im_ptr = &im; 171 | 172 | p(int_chan); 173 | 174 | SC_THREAD(flit_src_handler_run); 175 | sensitive << clk.pos(); 176 | async_reset_signal_is(rst, false); 177 | } 178 | 179 | protected: 180 | void FillInFifo() { 181 | Flit_t noc_flit; 182 | if (int_chan.PopNB(noc_flit)) { 183 | num_flits++; 184 | bool is_head_flit = 185 | noc_flit.flit_id.isHeader() || noc_flit.flit_id.isSingle(); 186 | bool is_tail_flit = 187 | noc_flit.flit_id.isTail() || noc_flit.flit_id.isSingle(); 188 | 189 | Marshaller::width> flit_marshaller; 190 | Wrapped flit_wm(noc_flit); 191 | flit_wm.Marshall(flit_marshaller); 192 | packet_buf.range(Wrapped::width + packet_buf_pos - 1, 193 | packet_buf_pos) = flit_marshaller.GetResult(); 194 | packet_buf_pos += Wrapped::width; 195 | 196 | // Decode the destination 197 | if (is_head_flit) { 198 | bool is_mcast = noc_flit.data[router_t::mcast_dest_width]; 199 | if (is_mcast) { 200 | // Multicast 201 | // Extract different local and global destinations 202 | NVUINTW(router_t::mcast_dest_width) 203 | route = 204 | nvhls::get_slc(noc_flit.data, 0); 205 | NVUINTW(router_t::NoC_mcast_dest_width) 206 | NoC_out_dest_local = nvhls::get_slc( 207 | route, router_t::NoP_mcast_dest_width); 208 | 209 | current_route = ic.create_route(); 210 | for (int i = 0; i < router_t::NoC_mcast_dest_width; i++) { 211 | if (NoC_out_dest_local[i]) { 212 | current_route->add_dest(i, dest_map_idx); 213 | } 214 | } 215 | } else { 216 | // Unicast 217 | // Extract different local and global destinations 218 | NVUINTW(router_t::ucast_dest_width) 219 | route = 220 | nvhls::get_slc(noc_flit.data, 0); 221 | NVUINTW(router_t::NoC_ucast_dest_width) 222 | NoC_out_dest_local = 223 | nvhls::get_slc(route, 0); 224 | 225 | current_route = ic.create_route(); 226 | current_route->add_dest(NoC_out_dest_local.to_uint64(), 227 | dest_map_idx); 228 | } 229 | } 230 | 231 | if (is_tail_flit) { 232 | num_flit_fifo.push(num_flits, 0); 233 | num_flits = 0; 234 | } 235 | } 236 | } 237 | 238 | void flit_src_handler_run() { 239 | int_chan.ResetRead(); 240 | im.Reset(); 241 | packet_buf = 0; 242 | packet_buf_pos = 0; 243 | num_flits = 0; 244 | current_route = 0; 245 | 246 | while (1) { 247 | wait(); 248 | FillInFifo(); 249 | if (!num_flit_fifo.isEmpty(0)) { 250 | 251 | // Create the InterconnectMessage 252 | typename interconnect_t::IM_t im_msg; 253 | im_msg.set_msg(ic, packet_buf); 254 | 255 | im_msg.set_route(ic, current_route); 256 | 257 | // Push it onto the interface 258 | im.Push(im_msg); 259 | 260 | // Reset for next packet 261 | num_flit_fifo.incrHead(0); 262 | packet_buf = 0; 263 | packet_buf_pos = 0; 264 | } 265 | } 266 | } 267 | }; // class FlitSrcHandler 268 | 269 | // Interface for handler that is template-less 270 | class FlitDestHandlerIface { 271 | public: 272 | typename interconnect_t::IM_dest_t *im_ptr; 273 | }; 274 | 275 | // class FlitDestHandler 276 | template 278 | class FlitDestHandler : public sc_module, 279 | public Connections::Blocking_abs, 280 | public FlitDestHandlerIface { 281 | SC_HAS_PROCESS(FlitDestHandler); 282 | 283 | protected: 284 | static const int BUFFERSIZE = 17; 285 | 286 | dest_map_idx_t dest_map_idx; 287 | 288 | // Local State 289 | sc_lv::width * BUFFERSIZE> packet_buf; 290 | unsigned int packet_buf_pos; 291 | 292 | public: 293 | sc_in_clk clk; 294 | sc_in rst; 295 | 296 | interconnect_t ⁣ 297 | typename interconnect_t::IM_dest_t im; 298 | Connections::InBlocking &p; 299 | Connections::Combinational int_chan; 300 | 301 | explicit FlitDestHandler(interconnect_t &ic_, 302 | Connections::InBlocking &p_, 303 | dest_map_idx_t dest_map_idx_) 304 | : sc_module(sc_module_name(sc_gen_unique_name("interconnect_joiner"))), 305 | dest_map_idx(dest_map_idx_), 306 | clk("clk"), 307 | rst("rst"), 308 | ic(ic_), 309 | im(sc_gen_unique_name("im")), 310 | p(p_), 311 | int_chan("int_chan") { 312 | // Pass up to handler interface 313 | this->im_ptr = &im; 314 | 315 | p(int_chan); 316 | 317 | SC_THREAD(flit_dest_handler_run); 318 | sensitive << clk.pos(); 319 | async_reset_signal_is(rst, false); 320 | } 321 | 322 | protected: 323 | void flit_dest_handler_run() { 324 | int_chan.ResetWrite(); 325 | im.Reset(); 326 | packet_buf = 0; 327 | packet_buf_pos = 0; 328 | 329 | while (1) { 330 | wait(); 331 | if (packet_buf_pos == 0) { 332 | typename interconnect_t::IM_t im_msg; 333 | im_msg = im.Pop(); 334 | im_msg.get_msg(ic, packet_buf); 335 | } 336 | 337 | Flit_t flit; 338 | Marshaller::width> flit_marshaller(packet_buf.range( 339 | Wrapped::width + packet_buf_pos - 1, packet_buf_pos)); 340 | Wrapped flit_result; 341 | flit_result.Marshall(flit_marshaller); 342 | flit = flit_result.val; 343 | 344 | int_chan.Push(flit); 345 | 346 | if (flit.flit_id.isTail() || flit.flit_id.isSingle()) { 347 | packet_buf_pos = 0; 348 | } else { 349 | packet_buf_pos += Wrapped::width; 350 | } 351 | } 352 | } 353 | }; // classFlitDestHandler 354 | 355 | }; // class InterconnecHandlerFlit 356 | 357 | #endif 358 | }; 359 | 360 | #endif // __INTERCONNECT_HANDLER_FLIT_H__ 361 | -------------------------------------------------------------------------------- /cmod/ProducerConsumer/ProducerConsumerPart.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: Copyright (c) 2021 NVIDIA CORPORATION & AFFILIATES. 3 | * All rights reserved. 4 | * SPDX-License-Identifier: Apache-2.0 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | #ifndef PRODUCER_CONSUMER_PART_H 20 | #define PRODUCER_CONSUMER_PART_H 21 | 22 | #include 23 | 24 | #define NUM_DESTS 16 25 | 26 | typedef struct flit_data { 27 | unsigned int payload; 28 | sc_time send_time; 29 | } flit_data_t; 30 | 31 | typedef struct stat { 32 | 33 | sc_time cum_latency; 34 | sc_time max_latency; 35 | unsigned long long num_msgs; 36 | 37 | stat() { 38 | cum_latency = sc_time(0, SC_NS); 39 | max_latency = sc_time(0, SC_NS); 40 | num_msgs = 0; 41 | } 42 | 43 | } stat_t; 44 | 45 | class ProducerConsumerLog { 46 | public: 47 | static const int kDebugLevel = 4; 48 | 49 | unsigned int num_flits_in_flight; 50 | unsigned int num_flits_send_total; 51 | unsigned int num_flits_recv_total; 52 | 53 | std::map > stats; 54 | std::map > > 55 | flits_in_flight; 56 | 57 | unsigned int pre_num_flits_in_flight; 58 | unsigned int pre_num_flits_send_total; 59 | unsigned int pre_num_flits_recv_total; 60 | std::map > pre_stats; 61 | std::map > > 62 | pre_flits_in_flight; 63 | 64 | std::array 65 | PE_activity; // 1.0 = every cycle, 0.0 = no cycles 66 | std::array 67 | PE_unicast_only; // 0 = multicast allowed, 1 = unicast only 68 | std::array, PE_WIDTH * PE_HEIGHT> 69 | PE_dest_probs; // indiviual probs to send to each. Outer = sender, inner 70 | // = dest. 71 | 72 | ProducerConsumerLog() { 73 | num_flits_in_flight = 0; 74 | num_flits_send_total = 0; 75 | num_flits_recv_total = 0; 76 | 77 | pre_num_flits_in_flight = 0; 78 | pre_num_flits_send_total = 0; 79 | pre_num_flits_recv_total = 0; 80 | 81 | for (unsigned int i = 0; i < PE_WIDTH * PE_HEIGHT; ++i) { 82 | PE_activity[i] = 0; 83 | PE_unicast_only[i] = false; 84 | for (unsigned int j = 0; j < PE_WIDTH * PE_HEIGHT; ++j) { 85 | PE_dest_probs[i][j] = 0; 86 | } 87 | } 88 | } 89 | 90 | void record_pre_send(unsigned int pe_id_sender, unsigned int pe_id_receiver, 91 | unsigned int payload, sc_time pre_time) { 92 | pre_num_flits_in_flight++; 93 | pre_num_flits_send_total++; 94 | 95 | flit_data_t data({.payload = payload, .send_time = pre_time}); 96 | pre_flits_in_flight[pe_id_sender][pe_id_receiver].push_back(data); 97 | } 98 | 99 | void record_send(unsigned int pe_id_sender, unsigned int pe_id_receiver, 100 | unsigned int payload) { 101 | num_flits_in_flight++; 102 | num_flits_send_total++; 103 | 104 | flit_data_t data({.payload = payload, .send_time = sc_time_stamp()}); 105 | flits_in_flight[pe_id_sender][pe_id_receiver].push_back(data); 106 | } 107 | 108 | void record_recv(unsigned int pe_id_receiver, unsigned int payload) { 109 | num_flits_in_flight--; 110 | num_flits_recv_total++; 111 | 112 | std::vector pe_id_senders; 113 | for (auto sender_it = flits_in_flight.begin(); 114 | sender_it != flits_in_flight.end(); ++sender_it) { 115 | if (sender_it->second.find(pe_id_receiver) == sender_it->second.end()) 116 | continue; 117 | if (sender_it->second[pe_id_receiver].front().payload == payload) { 118 | pe_id_senders.push_back(sender_it->first); 119 | } 120 | } 121 | NVHLS_ASSERT(pe_id_senders.size() == 1); 122 | 123 | unsigned int pe_id_sender = pe_id_senders[0]; 124 | 125 | NVHLS_ASSERT(!flits_in_flight[pe_id_sender][pe_id_receiver].empty()); 126 | 127 | sc_time tof = 128 | sc_time_stamp() - 129 | flits_in_flight[pe_id_sender][pe_id_receiver].front().send_time; 130 | if (stats[pe_id_sender][pe_id_receiver].max_latency < tof) { 131 | stats[pe_id_sender][pe_id_receiver].max_latency = tof; 132 | } 133 | stats[pe_id_sender][pe_id_receiver].cum_latency += tof; 134 | stats[pe_id_sender][pe_id_receiver].num_msgs++; 135 | flits_in_flight[pe_id_sender][pe_id_receiver].pop_front(); 136 | } 137 | 138 | void record_pre_recv(unsigned int pe_id_receiver, unsigned int payload) { 139 | pre_num_flits_in_flight--; 140 | pre_num_flits_recv_total++; 141 | 142 | std::vector pe_id_senders; 143 | for (auto sender_it = pre_flits_in_flight.begin(); 144 | sender_it != pre_flits_in_flight.end(); ++sender_it) { 145 | if (sender_it->second.find(pe_id_receiver) == sender_it->second.end()) 146 | continue; 147 | if (sender_it->second[pe_id_receiver].front().payload == payload) { 148 | pe_id_senders.push_back(sender_it->first); 149 | } 150 | } 151 | NVHLS_ASSERT(pe_id_senders.size() == 1); 152 | 153 | unsigned int pe_id_sender = pe_id_senders[0]; 154 | 155 | NVHLS_ASSERT(!pre_flits_in_flight[pe_id_sender][pe_id_receiver].empty()); 156 | 157 | sc_time tof = 158 | sc_time_stamp() - 159 | pre_flits_in_flight[pe_id_sender][pe_id_receiver].front().send_time; 160 | if (pre_stats[pe_id_sender][pe_id_receiver].max_latency < tof) { 161 | pre_stats[pe_id_sender][pe_id_receiver].max_latency = tof; 162 | } 163 | pre_stats[pe_id_sender][pe_id_receiver].cum_latency += tof; 164 | pre_stats[pe_id_sender][pe_id_receiver].num_msgs++; 165 | pre_flits_in_flight[pe_id_sender][pe_id_receiver].pop_front(); 166 | } 167 | 168 | void print_stats() { 169 | sc_time cum_latency(0, SC_NS); 170 | sc_time max_latency(0, SC_NS); 171 | unsigned long long num_msgs(0); 172 | 173 | for (auto sender_it = stats.begin(); sender_it != stats.end(); 174 | ++sender_it) { 175 | for (auto receiver_it = sender_it->second.begin(); 176 | receiver_it != sender_it->second.end(); ++receiver_it) { 177 | unsigned int pe_id_sender = sender_it->first; 178 | unsigned int pe_id_receiver = receiver_it->first; 179 | stat_t stat = receiver_it->second; 180 | 181 | cout << "Avg latency from " << pe_id_sender << " to " << pe_id_receiver 182 | << " = " << (stat.cum_latency / stat.num_msgs) / sc_time(1, SC_NS) 183 | << " cycles (sent " << stat.num_msgs << ", max " 184 | << stat.max_latency << ")" << endl; 185 | 186 | cum_latency += stat.cum_latency; 187 | num_msgs += stat.num_msgs; 188 | if (max_latency < stat.max_latency) { 189 | max_latency = stat.max_latency; 190 | } 191 | } 192 | } 193 | 194 | cout << "Overall network avg latency: " 195 | << (cum_latency / num_msgs) / sc_time(1, SC_NS) << " cycles (" 196 | << num_msgs << " total messages, max " << max_latency << ")" << endl; 197 | } 198 | 199 | void print_pre_stats() { 200 | sc_time cum_latency(0, SC_NS); 201 | unsigned long long num_msgs(0); 202 | 203 | for (auto sender_it = pre_stats.begin(); sender_it != pre_stats.end(); 204 | ++sender_it) { 205 | for (auto receiver_it = sender_it->second.begin(); 206 | receiver_it != sender_it->second.end(); ++receiver_it) { 207 | unsigned int pe_id_sender = sender_it->first; 208 | unsigned int pe_id_receiver = receiver_it->first; 209 | stat_t stat = receiver_it->second; 210 | 211 | CDCOUT(sc_time_stamp() 212 | << ": " 213 | << "Avg latency from " << pe_id_sender << " to " 214 | << pe_id_receiver << " = " 215 | << (stat.cum_latency / stat.num_msgs) / sc_time(1, SC_NS) 216 | << " cycles (sent " << stat.num_msgs << ")" << endl, 217 | kDebugLevel); 218 | 219 | cum_latency += stat.cum_latency; 220 | num_msgs += stat.num_msgs; 221 | } 222 | } 223 | 224 | cout << "Overall packet avg latency: " 225 | << (cum_latency / num_msgs) / sc_time(1, SC_NS) << " cycles (" 226 | << num_msgs << " total messages)" << endl; 227 | } 228 | }; 229 | 230 | template 231 | struct pc_log_statics { 232 | static ProducerConsumerLog pc_log; 233 | static unsigned int num_flits_in_flight; 234 | }; 235 | 236 | template 237 | ProducerConsumerLog pc_log_statics::pc_log; 238 | 239 | inline ProducerConsumerLog& get_pc_log() { 240 | return pc_log_statics::pc_log; 241 | } 242 | 243 | template 244 | class ProducerConsumer : public sc_module { 245 | SC_HAS_PROCESS(ProducerConsumer); 246 | 247 | unsigned int pe_id; 248 | 249 | public: 250 | sc_in_clk clk; 251 | sc_in rst; 252 | 253 | Connections::In in_port; 254 | Connections::Out out_port; 255 | 256 | // Connections::Out out_port_ctrl; 257 | // Connections::In in_port_ctrl; 258 | 259 | ProducerConsumer(sc_module_name nm, unsigned int pe_id_) : sc_module(nm) { 260 | pe_id = pe_id_; 261 | 262 | SC_THREAD(ProducerRun); 263 | sensitive << clk.pos(); 264 | async_reset_signal_is(rst, false); 265 | 266 | SC_THREAD(ConsumerRun); 267 | sensitive << clk.pos(); 268 | async_reset_signal_is(rst, false); 269 | } 270 | 271 | void ProducerRun() { 272 | out_port.Reset(); 273 | 274 | while (1) { 275 | wait(); 276 | 277 | float bw_prob = get_pc_log().PE_activity[pe_id]; 278 | if (bw_prob == 0.0) 279 | continue; 280 | if (bw_prob < 1.0 && ((static_cast(rand()) / 281 | static_cast(RAND_MAX)) >= bw_prob)) 282 | continue; 283 | 284 | IC_MESSAGE(my_msg) im; 285 | im = rand(); 286 | 287 | std::vector dest_idxs; 288 | 289 | if (!get_pc_log().PE_unicast_only[pe_id]) { 290 | for (unsigned int dest_idx = 0; dest_idx < PE_WIDTH * PE_HEIGHT; 291 | ++dest_idx) { 292 | float bw_prob = get_pc_log().PE_dest_probs[pe_id][dest_idx]; 293 | if (bw_prob == 0.0) 294 | continue; 295 | if (bw_prob < 1.0 && ((static_cast(rand()) / 296 | static_cast(RAND_MAX)) >= bw_prob)) 297 | continue; 298 | im << IC_TO(dest_idx); 299 | dest_idxs.push_back(dest_idx); 300 | } 301 | } else { 302 | float total_prob = 0; 303 | for (unsigned int dest_idx = 0; dest_idx < PE_WIDTH * PE_HEIGHT; 304 | ++dest_idx) { 305 | float bw_prob = get_pc_log().PE_dest_probs[pe_id][dest_idx]; 306 | total_prob += bw_prob; 307 | } 308 | if (total_prob == 0.0) 309 | continue; 310 | 311 | float rand_result = 312 | (static_cast(rand()) / static_cast(RAND_MAX)) * 313 | total_prob; 314 | for (unsigned int dest_idx = 0; dest_idx < PE_WIDTH * PE_HEIGHT; 315 | ++dest_idx) { 316 | float bw_prob = get_pc_log().PE_dest_probs[pe_id][dest_idx]; 317 | if (bw_prob == 0.0) 318 | continue; 319 | rand_result -= bw_prob; 320 | if (rand_result <= 0) { 321 | im << IC_TO(dest_idx); 322 | dest_idxs.push_back(dest_idx); 323 | break; 324 | } 325 | } 326 | } 327 | 328 | // Record how long we've been waiting to send in "pre_time" 329 | sc_time pre_time = sc_time_stamp(); 330 | 331 | out_port.Push(im); 332 | 333 | for (auto it = dest_idxs.begin(); it != dest_idxs.end(); ++it) { 334 | get_pc_log().record_pre_send(pe_id, *it, im.get_msg(), pre_time); 335 | get_pc_log().record_send(pe_id, *it, im.get_msg()); 336 | } 337 | } 338 | } 339 | void ConsumerRun() { 340 | in_port.Reset(); 341 | 342 | while (1) { 343 | wait(); 344 | IC_MESSAGE(my_msg) im = in_port.Pop(); 345 | get_pc_log().record_recv(pe_id, im.get_msg()); 346 | get_pc_log().record_pre_recv(pe_id, im.get_msg()); 347 | } 348 | } 349 | }; 350 | 351 | class ProducerConsumerPart : public sc_module { 352 | public: 353 | IC_HAS_INTERFACE(ProducerConsumerPart); 354 | 355 | sc_in_clk clk; 356 | sc_in rst; 357 | 358 | ProducerConsumer pc_inst; 359 | 360 | ProducerConsumerPart(sc_module_name nm, unsigned int pe_id) 361 | : sc_module(nm), pc_inst("pc_inst", pe_id) { 362 | // During actual HLS we want ProducerConsumerPart to bind clock, 363 | // but the scverify wrapper doesn't bind clock, so we must bind 364 | // it here. 365 | #if defined(__SYNTHESIS__) || !defined(INTERCONNECT_GEN) 366 | ic_interface.clk(clk); 367 | ic_interface.rst(rst); 368 | #endif 369 | 370 | // Bind to pe module 371 | pc_inst.clk(clk); 372 | pc_inst.rst(rst); 373 | 374 | // Bind to interconnect interface 375 | IC_PORT_BIND(pc_inst.in_port); 376 | IC_PORT_BIND(pc_inst.out_port); 377 | } 378 | }; 379 | 380 | #endif 381 | -------------------------------------------------------------------------------- /cmod/include/interconnect/InterconnectChannels.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: Copyright (c) 2019-2021 NVIDIA CORPORATION & 3 | * AFFILIATES. All rights reserved. 4 | * SPDX-License-Identifier: Apache-2.0 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | #ifndef __INTERCONNECT_CHANNELS_HPP__ 20 | #define __INTERCONNECT_CHANNELS_HPP__ 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | #include 28 | 29 | namespace interconnect { 30 | 31 | #ifdef CONNECTIONS_SIM_ONLY 32 | 33 | ///////////// 34 | 35 | class ConManagerLinks; 36 | ConManagerLinks& get_conManagerLinks(); 37 | 38 | template 39 | struct ConManagerLinks_statics { 40 | static ConManagerLinks conManagerLinks; 41 | }; 42 | 43 | inline ConManagerLinks& get_conManagerLinks() { 44 | return ConManagerLinks_statics::conManagerLinks; 45 | } 46 | 47 | class CombinationalLink_abs; 48 | template 49 | class CombinationalLink; 50 | 51 | class CombinationalLink_abs { 52 | public: 53 | double bw_prob; 54 | 55 | virtual void Reset_BWProb() { 56 | NVHLS_ASSERT_MSG(0, "Unreachable virtual function in abstract class!"); 57 | } 58 | 59 | virtual bool EvaluateCongest(bool degraded_mode = false) { 60 | NVHLS_ASSERT_MSG(0, "Unreachable virtual function in abstract class!"); 61 | return 0; 62 | } 63 | 64 | virtual bool IsValid() { 65 | NVHLS_ASSERT_MSG(0, "Unreachable virtual function in abstract class!"); 66 | return 0; 67 | } 68 | 69 | virtual bool ShouldSkipUpdateCongest() { 70 | NVHLS_ASSERT_MSG(0, "Unreachable virtual function in abstract class!"); 71 | return 0; 72 | } 73 | 74 | virtual const char* const get_name() { 75 | NVHLS_ASSERT_MSG(0, "Unreachable virtual function in abstract class!"); 76 | } 77 | }; 78 | 79 | // A list of Combinationals that share the same link, 80 | // for back-annotation modeling. 81 | class LinkGroup { 82 | public: 83 | std::map weights; 84 | std::map link_probs; 85 | 86 | int num_total_count; 87 | float num_non_zero_count = -1; 88 | 89 | double count_active_weights(double min_weight = 0) { 90 | double a = 0; 91 | for (std::map::iterator it = 92 | weights.begin(); 93 | it != weights.end(); ++it) { 94 | if (it->second < min_weight) 95 | continue; 96 | if (!it->first->ShouldSkipUpdateCongest()) // ! ShouldSkipUpdateCongest() 97 | // (this includes isBypass) 98 | // would be better to use. We 99 | // can't stall when isBypass() 100 | // but we can still count it 101 | // towards active. 102 | a += it->second; 103 | } 104 | return a; 105 | } 106 | 107 | double max_active_weights() { 108 | double a = 0; 109 | for (std::map::iterator it = 110 | weights.begin(); 111 | it != weights.end(); ++it) { 112 | if (!it->first->ShouldSkipUpdateCongest()) // ! ShouldSkipUpdateCongest() 113 | // (this includes isBypass) 114 | // would be better to use. We 115 | // can't stall when isBypass() 116 | // but we can still count it 117 | // towards active. 118 | a = std::max(a, it->second); 119 | } 120 | return a; 121 | } 122 | 123 | double count_active_links() { 124 | double a = 0; 125 | for (std::map::iterator it = 126 | weights.begin(); 127 | it != weights.end(); ++it) { 128 | if (!it->first->ShouldSkipUpdateCongest()) // ! ShouldSkipUpdateCongest() 129 | // (this includes isBypass) 130 | // would be better to use. We 131 | // can't stall when isBypass() 132 | // but we can still count it 133 | // towards active. 134 | a += 1; 135 | } 136 | return a; 137 | } 138 | 139 | inline double bound(double v, double min_v, double max_v) { 140 | return std::max(min_v, std::min(max_v, v)); 141 | } 142 | 143 | void UpdateCongest(bool degraded_mode = false) { 144 | double max_weight = max_active_weights(); 145 | double active_weights = count_active_weights(max_weight); 146 | double active_links = count_active_links(); 147 | 148 | if (active_links == 0) 149 | return; 150 | 151 | NVHLS_ASSERT(active_weights > 0); 152 | 153 | for (std::map::iterator it = 154 | link_probs.begin(); 155 | it != link_probs.end(); ++it) { 156 | // If we're not valid, then skip calculations. 157 | if (it->first->ShouldSkipUpdateCongest()) 158 | continue; 159 | 160 | // Calculate local bw prob 161 | double ratio; 162 | if (!degraded_mode) { 163 | ratio = weights[it->first] / active_weights; 164 | } else { 165 | ratio = 1.0 / active_links; 166 | } 167 | 168 | // limit range of bw_prob to 0.0 <= <= 1.0 169 | double link_prob = it->second * ratio; 170 | if (weights[it->first] < max_weight && !degraded_mode) { 171 | link_prob = 0; 172 | } 173 | NVHLS_ASSERT(0 <= link_prob && link_prob <= 1.0); 174 | 175 | it->first->bw_prob = std::min(link_prob, it->first->bw_prob); 176 | } 177 | } 178 | 179 | void CalculateNonZero() { 180 | num_non_zero_count = 0; 181 | num_total_count = 0; 182 | 183 | for (std::map::iterator it = 184 | link_probs.begin(); 185 | it != link_probs.end(); ++it) { 186 | if (it->first->ShouldSkipUpdateCongest()) 187 | continue; 188 | num_total_count++; 189 | num_non_zero_count += it->first->bw_prob; 190 | } 191 | } 192 | }; 193 | 194 | class ConManagerLinks : public Connections::ConManager { 195 | public: 196 | std::vector link_groups; 197 | std::vector 198 | links; // Links of the connnections themselves 199 | std::map 200 | links_tracked; // Links of the connnections themselves 201 | 202 | ConManagerLinks() : Connections::ConManager() { 203 | sc_spawn(sc_bind(&ConManagerLinks::run, this, true), 204 | "connection_manager_links_run"); 205 | } 206 | 207 | template 208 | void addlink(T* c) { 209 | links.push_back(static_cast(c)); 210 | links_tracked[c] = static_cast(c); 211 | } 212 | 213 | void run(bool value) { 214 | wait(SC_ZERO_TIME); // Allow the simulation to catch up with clock 215 | // registration, etc... 216 | 217 | #ifdef CAP_CONNECTIONS_MULTI_CLK 218 | // Find all that we (ConManagerLinks) are tracking and remove from 219 | // tracked_per_clk. 220 | for (auto it_to_find = links_tracked.begin(); 221 | it_to_find != links_tracked.end(); ++it_to_find) { 222 | for (auto it_outer = 223 | Connections::get_conManager().tracked_per_clk.begin(); 224 | it_outer != Connections::get_conManager().tracked_per_clk.end(); 225 | ++it_outer) { 226 | for (auto it_inner = (*it_outer)->begin(); 227 | it_inner != (*it_outer)->end(); ++it_inner) 228 | if (*it_inner == it_to_find->second) { 229 | (*it_outer)->erase(it_inner); 230 | } 231 | } 232 | } 233 | #endif 234 | 235 | bool last_degraded_mode = false; 236 | 237 | #ifdef CAP_CONNECTIONS_MULTI_CLK 238 | Connections::get_sim_clk().post_delay( 239 | 0); // align to occur just after the cycle 240 | #else 241 | Connections::get_sim_clk() 242 | .post_delay(); // align to occur just after the cycle 243 | #endif 244 | 245 | while (1) { 246 | bool degraded_mode = false; 247 | 248 | // Post(): Update, set rdy and val appropriately 249 | for (std::vector::iterator it = links.begin(); 250 | it != links.end();) 251 | if (links_tracked[(*it)]->Post()) 252 | ++it; 253 | else 254 | links.erase(it); 255 | 256 | // Wait zero cycle, so that all signals update before counting valids. 257 | wait(SC_ZERO_TIME); 258 | 259 | // UpdateCongest(): For each link group, calculate individual link 260 | // probability. 261 | for (std::vector::iterator it = link_groups.begin(); 262 | it != link_groups.end(); ++it) { 263 | (*it)->UpdateCongest(degraded_mode); 264 | } 265 | 266 | int num_total_count = 0; 267 | float num_non_zero_count = 0; 268 | for (std::vector::iterator it = link_groups.begin(); 269 | it != link_groups.end(); ++it) { 270 | (*it)->CalculateNonZero(); 271 | num_total_count += (*it)->num_total_count; 272 | num_non_zero_count += (*it)->num_non_zero_count; 273 | NVHLS_ASSERT((*it)->num_non_zero_count == 0 || 274 | (*it)->num_total_count > 0); 275 | } 276 | if (num_total_count > 0 && num_non_zero_count == 0) { 277 | degraded_mode = true; 278 | if (last_degraded_mode != degraded_mode) { 279 | cout << "@" << sc_time_stamp() 280 | << ": Warning: Deadlock detection triggered, rerunning " 281 | "congestion calculation in degraded mode so that it can " 282 | "continue..." 283 | << endl; 284 | } 285 | // Deadlock case, re-do UpdateCongestion with balanced 286 | for (std::vector::iterator it = links.begin(); 287 | it != links.end(); ++it) { 288 | (*it)->Reset_BWProb(); 289 | } 290 | for (std::vector::iterator it = link_groups.begin(); 291 | it != link_groups.end(); ++it) { 292 | (*it)->UpdateCongest(degraded_mode); 293 | } 294 | num_total_count = 0; 295 | num_non_zero_count = 0; 296 | for (std::vector::iterator it = link_groups.begin(); 297 | it != link_groups.end(); ++it) { 298 | (*it)->CalculateNonZero(); 299 | num_total_count += (*it)->num_total_count; 300 | num_non_zero_count += (*it)->num_non_zero_count; 301 | NVHLS_ASSERT((*it)->num_non_zero_count == 0 || 302 | (*it)->num_total_count > 0); 303 | } 304 | } 305 | last_degraded_mode = degraded_mode; 306 | NVHLS_ASSERT(num_total_count == 0 || num_non_zero_count > 0); 307 | // EvaluateCongest: examine and deassert rdy to limit bandwidth 308 | for (std::vector::iterator it = links.begin(); 309 | it != links.end();) 310 | if ((*it)->EvaluateCongest(false)) 311 | ++it; 312 | else 313 | links.erase(it); 314 | 315 | // Post -> Pre delay like normal 316 | #ifdef CAP_CONNECTIONS_MULTI_CLK 317 | Connections::get_sim_clk().post2pre_delay(0); 318 | #else 319 | Connections::get_sim_clk().post2pre_delay(); 320 | #endif 321 | 322 | for (std::vector::iterator it = links.begin(); 323 | it != links.end();) 324 | if (links_tracked[(*it)]->Pre()) 325 | ++it; 326 | else 327 | links.erase(it); 328 | 329 | // Pre -> Post delay like normal 330 | Connections::get_sim_clk().pre2post_delay(); 331 | } 332 | } 333 | }; 334 | 335 | template 336 | ConManagerLinks ConManagerLinks_statics::conManagerLinks; 337 | 338 | template 339 | class CombinationalLink : public Connections::Combinational, 340 | public CombinationalLink_abs { 341 | protected: 342 | static const int precision = 1000; 343 | 344 | public: 345 | CombinationalLink() 346 | : Connections::Combinational(), CombinationalLink_abs() { 347 | Reset_BWProb(); 348 | Connections::get_conManager().remove(this); 349 | Connections::get_conManager().remove_annotate(this); 350 | get_conManagerLinks().addlink(this); 351 | get_conManagerLinks().add_annotate(this); 352 | } 353 | 354 | explicit CombinationalLink(const char* name) 355 | : Connections::Combinational(name), CombinationalLink_abs() { 356 | Reset_BWProb(); 357 | Connections::get_conManager().remove(this); 358 | Connections::get_conManager().remove_annotate(this); 359 | get_conManagerLinks().addlink(this); 360 | get_conManagerLinks().add_annotate(this); 361 | } 362 | 363 | void Reset_SIM() { 364 | Reset_BWProb(); 365 | Connections::Combinational::Reset_SIM(); 366 | } 367 | 368 | bool EvaluateCongest(bool degraded_mode) { 369 | if (!ShouldSkipUpdateCongest() && !degraded_mode) { 370 | NVHLS_ASSERT(bw_prob >= 0 && bw_prob <= 1.0); 371 | 372 | if (this->val_set_by_api && (bw_prob < 1.0) && 373 | ((static_cast(rand()) / static_cast(RAND_MAX)) > 374 | bw_prob)) { 375 | // Stall due to congestion modeling! 376 | this->_VLDNAMEOUT_.write(false); 377 | this->val_set_by_api = false; 378 | } 379 | } 380 | // Reset bw_prob after using it. 381 | Reset_BWProb(); 382 | 383 | return true; 384 | } 385 | 386 | bool IsValid() { 387 | 388 | #ifdef CONNECTIONS_ACCURATE_SIM 389 | return this->_VLDNAMEOUT_.read(); 390 | #else 391 | return false; // FIXME CONNECTONS_FAST_SIM is currently unsupported by IPA 392 | #endif 393 | } 394 | 395 | bool IsReady() { 396 | #ifdef CONNECTIONS_ACCURATE_SIM 397 | return this->_RDYNAMEOUT_.read(); 398 | #else 399 | return false; // FIXME CONNECTONS_FAST_SIM is currently unsupported by IPA 400 | #endif 401 | } 402 | 403 | virtual bool ShouldSkipUpdateCongest() { 404 | #ifdef CONNECTIONS_ACCURATE_SIM 405 | return (!(IsValid() && IsReady())) || this->is_bypass(); 406 | #else 407 | return false; // FIXME CONNECTONS_FAST_SIM is currently unsupported by IPA 408 | #endif 409 | } 410 | 411 | void Reset_BWProb() { bw_prob = 1.0; } 412 | 413 | virtual const char* const get_name() { return this->name(); } 414 | }; 415 | 416 | #else 417 | 418 | #endif 419 | }; 420 | 421 | #endif // __INTERCONNECT_CHANNELS_HPP__ 422 | -------------------------------------------------------------------------------- /cmod/include/interconnect/InterconnectAnnotate.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: Copyright (c) 2019-2021 NVIDIA CORPORATION & 3 | * AFFILIATES. All rights reserved. 4 | * SPDX-License-Identifier: Apache-2.0 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | // Reimplementation of Connections back annotation, extended for congestion 20 | // modeling support. 21 | // 22 | // See https://github.com/hlslibs/matchlib_connections for the original 23 | // annotate.h. 24 | // 25 | 26 | //======================================================================== 27 | // InterconnectAnnotate.h 28 | //======================================================================== 29 | 30 | #ifndef __INTERCONNECT_ANNOTATE_HPP__ 31 | #define __INTERCONNECT_ANNOTATE_HPP__ 32 | 33 | #include 34 | #include 35 | #include 36 | #include 37 | 38 | #include 39 | 40 | #include 41 | 42 | #include 43 | #include 44 | #include 45 | #include 46 | #include 47 | 48 | namespace interconnect { 49 | const int kDebugLevel = 5; 50 | #ifdef CONNECTIONS_ACCURATE_SIM 51 | 52 | template 53 | void __annotate_vector(std::vector &v, 54 | IC &ic, const std::string &root_name, 55 | rapidjson::Document &d, bool connectivity_only = false) { 56 | 57 | std::map middle_str_to_chan; 58 | 59 | NVHLS_ASSERT(d.IsObject()); 60 | 61 | /////////////////////////// CHANNELS ////////////////////////////////// 62 | // Check if array exists, if not add it. 63 | if (!d.HasMember("channels")) { 64 | rapidjson::Value v_channels; 65 | v_channels.SetObject(); 66 | d.AddMember("channels", v_channels, d.GetAllocator()); 67 | } 68 | rapidjson::Value &v_channels = d["channels"]; 69 | 70 | for (typename IC::channels_middle_t::iterator it_src = 71 | ic.channels_middle.begin(); 72 | it_src != ic.channels_middle.end(); ++it_src) { 73 | for (typename IC::channels_middle_inner_t::iterator it_dest = 74 | it_src->second.begin(); 75 | it_dest != it_src->second.end(); ++it_dest) { 76 | 77 | // Determine name after removing root_name 78 | std::string it_name = (it_dest->second)->name(); 79 | std::size_t pos = it_name.find(root_name); 80 | if (pos == std::string::npos) { 81 | continue; 82 | } // Skip if doesn't match root_name 83 | it_name.erase(pos, root_name.length()); 84 | 85 | // Add to string to chan mapping, used for links below. 86 | middle_str_to_chan[it_name] = it_dest->second; 87 | 88 | // Check if this channel exists in DOM, if not add it. 89 | if (!v_channels.HasMember(it_name.c_str())) { 90 | rapidjson::Value v_name; 91 | v_name.SetString(it_name.c_str(), d.GetAllocator()); 92 | rapidjson::Value v_channel; 93 | v_channel.SetObject(); 94 | v_channels.AddMember(v_name, v_channel, d.GetAllocator()); 95 | } 96 | rapidjson::Value &v_channel = v_channels[it_name.c_str()]; 97 | 98 | // Check if latency exists, if not add it 99 | if (!v_channel.HasMember("latency")) { 100 | rapidjson::Value v_latency; 101 | v_latency.SetInt(0); 102 | v_channel.AddMember("latency", v_latency, d.GetAllocator()); 103 | } 104 | rapidjson::Value &v_latency = v_channel["latency"]; 105 | 106 | // Check if capacity exists, if not add it 107 | if (!v_channel.HasMember("capacity")) { 108 | rapidjson::Value v_capacity; 109 | v_capacity.SetInt(0); 110 | v_channel.AddMember("capacity", v_capacity, d.GetAllocator()); 111 | } 112 | rapidjson::Value &v_capacity = v_channel["capacity"]; 113 | 114 | // Get the src_name and dest_name after subtracting off root 115 | std::string src_name = (it_dest->second)->src_name(); 116 | { 117 | if (src_name.length() >= 4 && 118 | src_name.substr(src_name.length() - 4, 4) == "_" _VLDNAMESTR_) { 119 | src_name.erase(src_name.length() - 4, 4); 120 | } 121 | std::size_t pos = src_name.find(root_name); 122 | if (pos != std::string::npos) { 123 | src_name.erase(pos, root_name.length()); 124 | } 125 | } 126 | 127 | std::string dest_name = (it_dest->second)->dest_name(); 128 | { 129 | if (dest_name.length() >= 4 && 130 | dest_name.substr(dest_name.length() - 4, 4) == "_" _VLDNAMESTR_) { 131 | dest_name.erase(dest_name.length() - 4, 4); 132 | } 133 | std::size_t pos = dest_name.find(root_name); 134 | if (pos != std::string::npos) { 135 | dest_name.erase(pos, root_name.length()); 136 | } 137 | } 138 | 139 | // Add the net driver/receiver, always an output never an input. 140 | if (v_channel.HasMember("src_name")) { 141 | if (strcmp(v_channel["src_name"].GetString(), src_name.c_str()) != 0) { 142 | cout << "Error: During annotation src_name in input doesn't match " 143 | "real " 144 | "src_name (" 145 | << v_channel["src_name"].GetString() 146 | << " != " << src_name.c_str() << ")" << endl; 147 | assert(0); 148 | } 149 | } else { 150 | rapidjson::Value v_src_name; 151 | v_src_name.SetString(src_name.c_str(), d.GetAllocator()); 152 | v_channel.AddMember("src_name", v_src_name, d.GetAllocator()); 153 | } 154 | 155 | if (v_channel.HasMember("dest_name")) { 156 | if (strcmp(v_channel["dest_name"].GetString(), dest_name.c_str()) != 157 | 0) { 158 | cout << "Error: During annotation dest_name in input doesn't match " 159 | "real dest_name (" 160 | << v_channel["dest_name"].GetString() 161 | << " != " << dest_name.c_str() << ")" << endl; 162 | assert(0); 163 | } 164 | } else { 165 | rapidjson::Value v_dest_name; 166 | v_dest_name.SetString(dest_name.c_str(), d.GetAllocator()); 167 | v_channel.AddMember("dest_name", v_dest_name, d.GetAllocator()); 168 | } 169 | 170 | // Add width 171 | if (!v_channel.HasMember("width")) { 172 | rapidjson::Value v_width; 173 | v_channel.AddMember("width", v_width, d.GetAllocator()); 174 | } 175 | rapidjson::Value &v_width = v_channel["width"]; 176 | v_width.SetInt(ic.srcs_width[it_src->first]); 177 | 178 | // Add user_typeid 179 | int user_typeid = ic.srcs_user_typeid[it_src->first]; 180 | NVHLS_ASSERT(user_typeid != -1); 181 | if (!v_channel.HasMember("user_typeid")) { 182 | rapidjson::Value v_user_typeid; 183 | v_channel.AddMember("user_typeid", v_user_typeid, d.GetAllocator()); 184 | } 185 | rapidjson::Value &v_user_typeid = v_channel["user_typeid"]; 186 | v_user_typeid.SetInt(ic.srcs_user_typeid[it_src->first]); 187 | 188 | if (v_channel.HasMember("msg_name")) { 189 | v_channel.RemoveMember("msg_name"); 190 | } 191 | if (ic.msg_names.find(user_typeid) != ic.msg_names.end()) { 192 | rapidjson::Value v_msg_name; 193 | v_msg_name.SetString(ic.msg_names[user_typeid], d.GetAllocator()); 194 | v_channel.AddMember("msg_name", v_msg_name, d.GetAllocator()); 195 | } else { 196 | NVHLS_ASSERT_MSG(0, "userid is missing a msg_name"); 197 | } 198 | 199 | // Warning: typeid isn't guarenteed to be stable between runs. 200 | // We can't use it as a key of types, but for groupings. 201 | // Add typeid 202 | if (v_channel.HasMember("typeid")) { 203 | v_channel.RemoveMember("typeid"); 204 | } 205 | rapidjson::Value v_typeid; 206 | v_typeid.SetUint64(ic.srcs_typeid[it_src->first]); 207 | v_channel.AddMember("typeid", v_typeid, d.GetAllocator()); 208 | 209 | // Sanity check v_latency 210 | if (!v_latency.IsInt()) { 211 | static const char *kTypeNames[] = {"Null", "False", "True", "Object", 212 | "Array", "String", "Number"}; 213 | CDCOUT("Error: Type of member v_latency is" 214 | << kTypeNames[v_latency.GetType()] 215 | << ". Please ensure it is of type Int." << endl, 216 | interconnect::kDebugLevel); 217 | } 218 | assert(v_latency.IsInt()); 219 | 220 | // Sanity check v_capacity 221 | if (!v_capacity.IsInt()) { 222 | static const char *kTypeNames[] = {"Null", "False", "True", "Object", 223 | "Array", "String", "Number"}; 224 | CDCOUT("Error: Type of member v_capacity is" 225 | << kTypeNames[v_capacity.GetType()] 226 | << ". Please ensure it is of type Int." << endl, 227 | interconnect::kDebugLevel); 228 | } 229 | assert(v_capacity.IsInt()); 230 | 231 | if (connectivity_only) { 232 | NVHLS_ASSERT(v_channel.HasMember("latency")); 233 | NVHLS_ASSERT(v_channel.HasMember("capacity")); 234 | v_channel.RemoveMember("latency"); 235 | v_channel.RemoveMember("capacity"); 236 | } else { 237 | // Annotate based on the value. 238 | (it_dest->second)->annotate(v_latency.GetInt(), v_capacity.GetInt()); 239 | } 240 | } 241 | } 242 | 243 | // Check if it exists in the links 244 | // "links" holds array of channel groups. 245 | // Each channel group calls out a channel. 246 | // Check if array exists, if not add it. 247 | const char *ic_disable_congestion = 248 | std::getenv("INTERCONNECT_DISABLE_CONGESTION"); 249 | if (d.HasMember("links") && ic_disable_congestion && 250 | std::string(ic_disable_congestion) != "1") { 251 | rapidjson::Value &v_links = d["links"]; 252 | 253 | assert(v_links.IsArray()); 254 | for (rapidjson::SizeType i = 0; i < v_links.Size(); i++) { 255 | rapidjson::Value &v_linkgroup = v_links[i]; 256 | assert(v_linkgroup.IsArray()); 257 | 258 | interconnect::LinkGroup *link_group = new LinkGroup(); 259 | interconnect::get_conManagerLinks().link_groups.push_back(link_group); 260 | 261 | for (rapidjson::SizeType j = 0; j < v_linkgroup.Size(); j++) { 262 | rapidjson::Value &v_channel_link = v_linkgroup[j]; 263 | std::string chan_string = v_channel_link["channel_name"].GetString(); 264 | typename std::map::iterator 265 | it = middle_str_to_chan.find(chan_string); 266 | if (it != middle_str_to_chan.end()) { 267 | if (it->second->is_bypass()) { 268 | CDCOUT("Warning: Channel " 269 | << chan_string 270 | << " is in bypass, so link probability won't be active. " 271 | "Adding to link group anyway..." 272 | << endl, 273 | interconnect::kDebugLevel); 274 | } 275 | if (v_channel_link.HasMember("bw_prob")) 276 | link_group->link_probs[it->second] = 277 | v_channel_link["bw_prob"].GetDouble(); 278 | else 279 | link_group->link_probs[it->second] = 1.0; 280 | 281 | if (v_channel_link.HasMember("weight")) 282 | link_group->weights[it->second] = 283 | v_channel_link["weight"].GetDouble(); 284 | else 285 | link_group->weights[it->second] = 1.0; 286 | } else { 287 | CDCOUT("Warning: Couldn't find channel " 288 | << chan_string << " for link group during annotation!" 289 | << endl, 290 | interconnect::kDebugLevel); 291 | } 292 | } 293 | } 294 | } 295 | } 296 | 297 | template 298 | void annotate_design(const sc_object &root, IC &ic, std::string base_name = "", 299 | std::string input_dir_path = "", 300 | std::string output_dir_path = "") { 301 | bool explicit_input_dir = false; 302 | 303 | // Add delim if non-empty base_name 304 | if (base_name.length() > 0) { 305 | base_name += "."; 306 | } 307 | 308 | base_name += root.name(); 309 | base_name += "."; 310 | 311 | // Sanity check input and output paths if they exist 312 | if (input_dir_path.length() > 0) { 313 | input_dir_path += "/"; 314 | explicit_input_dir = true; 315 | } 316 | if (output_dir_path.length() > 0) { 317 | output_dir_path += "/"; 318 | } 319 | 320 | std::string input_path = input_dir_path + base_name + "input.json"; 321 | std::string output_path = output_dir_path + base_name + "output.json"; 322 | 323 | // Create DOM object. 324 | rapidjson::Document d; 325 | 326 | // Try reading document from input.json 327 | std::ifstream ifs(input_path.c_str()); 328 | if (!ifs.fail()) { 329 | 330 | const unsigned int read_tries = 10; 331 | unsigned int read_tries_ = read_tries; 332 | bool first = false; 333 | CDCOUT("Info: Reading back-annotation from " 334 | << input_path << ". Please diff " << input_path << " and " 335 | << output_path << " to ensure it matches expectations." << endl, 336 | interconnect::kDebugLevel); 337 | do { 338 | if (read_tries_ < read_tries) { 339 | cout << "Info: retrying read of " << input_path << "..." << endl; 340 | sleep(5); 341 | ifs.close(); 342 | ifs.open(input_path.c_str()); 343 | } 344 | if (ifs.good()) { 345 | rapidjson::IStreamWrapper isw(ifs); 346 | d.ParseStream(isw); 347 | } 348 | } while (!d.IsObject() && --read_tries_); 349 | if (read_tries_ == 0) { 350 | cout << "Error: failed to read in correct annotation object from " 351 | << input_path << endl; 352 | exit(-1); 353 | } else { 354 | cout << "Info: Annotated. " << dec << read_tries_ 355 | << " tries were remaining." << endl; 356 | } 357 | } else { 358 | cout << "Warning: Could not read input json " << input_path.c_str() << endl; 359 | d.SetObject(); 360 | } 361 | 362 | std::string root_name = std::string(root.name()) + "."; 363 | assert(root_name.length() > 1); // Should be something other than just "." 364 | 365 | __annotate_vector(interconnect::get_conManagerLinks().tracked_annotate, ic, 366 | root_name, d); 367 | // Output DOM to file 368 | std::ofstream ofs(output_path.c_str()); 369 | rapidjson::OStreamWrapper osw(ofs); 370 | rapidjson::PrettyWriter writer(osw); 371 | d.Accept(writer); 372 | } 373 | 374 | template 375 | void save_connectivity(const sc_object &root, IC &ic, 376 | std::string base_name = "", 377 | std::string connectivity_dir_path = "") { 378 | // Add delim if non-empty base_name 379 | if (base_name.length() > 0) { 380 | base_name += "."; 381 | } 382 | 383 | base_name += root.name(); 384 | base_name += "."; 385 | 386 | // Sanity check input and output paths if they exist 387 | if (connectivity_dir_path.length() > 0) { 388 | connectivity_dir_path += "/"; 389 | } 390 | 391 | std::string connectivity_path = 392 | connectivity_dir_path + base_name + "connectivity.json"; 393 | 394 | // Create DOM object. 395 | rapidjson::Document d; 396 | d.SetObject(); 397 | 398 | std::string root_name = std::string(root.name()) + "."; 399 | assert(root_name.length() > 1); // Should be something other than just "." 400 | 401 | /////////////////////////// MESSAGES ////////////////////////////////// 402 | // List of message types and corresponding ids 403 | 404 | for (auto it = ic.msg_names.begin(); it != ic.msg_names.end(); ++it) { 405 | rapidjson::Pointer(std::string() + "/msgs/" + it->second).Set(d, it->first); 406 | } 407 | 408 | /////////////////////////// IC_ID ////////////////////////////////// 409 | rapidjson::Pointer("/ic_id").Set(d, ic.ic_id); 410 | 411 | /////////////////////////// IC INTERFACES ///////////////////////////// 412 | for (auto it = ic.part_names.begin(); it != ic.part_names.end(); ++it) { 413 | const unsigned int part_id = it->first; 414 | const char *part_name = it->second; 415 | rapidjson::Pointer(std::string() + "/parts/" + part_name + "/part_id") 416 | .Set(d, part_id); 417 | 418 | NVHLS_ASSERT(ic.part_to_part_type.size() == ic.part_inst_names.size()) 419 | 420 | int i = 0; 421 | for (auto ij = ic.part_to_part_type.begin(); 422 | ij != ic.part_to_part_type.end(); ++ij) { 423 | if (ij->second == part_id) { 424 | const unsigned int user_part_id = ij->first; 425 | rapidjson::Pointer(std::string() + "/parts/" + part_name + "/insts/" + 426 | std::to_string(i)) 427 | .Set(d, ij->first); 428 | rapidjson::Pointer(std::string() + "/parts/" + part_name + 429 | "/inst_names/" + std::to_string(i)) 430 | .Set(d, ic.part_inst_names[ij->first]); 431 | i++; 432 | } 433 | } 434 | 435 | for (auto ij = ic.part_msgs_srcs[part_id].begin(); 436 | ij != ic.part_msgs_srcs[part_id].end(); ++ij) { 437 | const unsigned int msg_id = ij->first; 438 | int i = 0; 439 | for (auto port_id_p = ij->second.begin(); port_id_p != ij->second.end(); 440 | ++port_id_p) { 441 | rapidjson::Pointer(std::string() + "/parts/" + part_name + 442 | "/src_ports/" + ic.msg_names[msg_id] + "/" + 443 | std::to_string(i++)) 444 | .Set(d, *port_id_p); 445 | } 446 | } 447 | 448 | for (auto ij = ic.part_msgs_dests[part_id].begin(); 449 | ij != ic.part_msgs_dests[part_id].end(); ++ij) { 450 | const unsigned int msg_id = ij->first; 451 | int i = 0; 452 | for (auto port_id_p = ij->second.begin(); port_id_p != ij->second.end(); 453 | ++port_id_p) { 454 | rapidjson::Pointer(std::string() + "/parts/" + part_name + 455 | "/dest_ports/" + ic.msg_names[msg_id] + "/" + 456 | std::to_string(i++)) 457 | .Set(d, *port_id_p); 458 | } 459 | } 460 | } 461 | 462 | /////////////////////////// CHANNELS ////////////////////////////////// 463 | __annotate_vector(Connections::get_conManager().tracked_annotate, ic, 464 | root_name, d, true); 465 | 466 | // Output DOM to file 467 | std::ofstream ofs(connectivity_path.c_str()); 468 | rapidjson::OStreamWrapper osw(ofs); 469 | rapidjson::PrettyWriter writer(osw); 470 | d.Accept(writer); 471 | } 472 | 473 | #else 474 | 475 | /** 476 | * \brief Back annotate Combinational connections for a design 477 | * \ingroup Connections 478 | * 479 | * \tparam root Root sc_object to annotate. 480 | * \tparam base_name Base file name (optional, defaults to name of root 481 | * within hierarchy) 482 | * \tparam base_name Input directory (optional, defaults to current working 483 | * directory) 484 | * \tparam base_name Output directory (optional, defaults to current working 485 | * directory) 486 | * 487 | * \par Description 488 | * When a design is annotated, it writes out a base_name.output.json with 489 | * all annotatable 490 | * Combinational LI channels, defaulting to a latency and capacity of 0 for 491 | * each (simple 492 | * wire). base_name.output.json can be used as a template for 493 | * base_name.input.json, which 494 | * it will read in and annotate based on given latency and capacity. 495 | * Latency is given in 496 | * units of global clock cycles, while capacity is number of units that can 497 | * be stored in 498 | * the channel. For a retimed path, latency and capacity should be equal, 499 | * while for 500 | * buffered paths capacity will exceed latency. 501 | * 502 | * Back annotation will only work with CONNECTIONS_ACCURATE_SIM define 503 | * enabled, and 504 | * in MARSHALL_PORT or DIRECT_PORT modes. It will not work in TLM_PORT 505 | * (CONNECTIONS_FAST_SIM) 506 | * or SYN_PORT modes. Additionally, it is dependent on RapidJSON as a git 507 | * submodule to read 508 | * and write JSON file format. 509 | * 510 | * In the base_name.output.json, a list of combinational names and 511 | * connecting ports is given. 512 | * UNBOUND indicates a Combinational channel that hasn't been Bind()'d on 513 | * one or more ports, 514 | * while TLM_INTERFACE indicates a channel that is along a TLM_PORT 515 | * interface in cosimulation 516 | * cases. 517 | * 518 | * \par A Simple Example 519 | * \code 520 | * #include 521 | * #include 522 | * 523 | * ... 524 | * // Add to sc_main, to annotate testbench DUT. 525 | * // Always generates a my_testbench.dut.output.json of the annotated 526 | * design 527 | * // and tries to read in my_testbench.dut.input.json if it exists. 528 | * annotate_design(my_testbench.dut); 529 | * ... 530 | * \endcode 531 | * \par 532 | * 533 | */ 534 | void annotate_design(const sc_object &root, std::string base_name = "", 535 | std::string input_dir_path = "", 536 | std::string output_dir_path = "") { 537 | cerr << "WARNING: Cannot annotate_design() unless running in sim accurate " 538 | "mode!" 539 | << endl; 540 | cerr << "WARNING: Design will not be annotated." << endl; 541 | } 542 | 543 | template 544 | void save_connectivity(const sc_object &root, IC &ic, 545 | std::string base_name = "", 546 | std::string connectivity_dir_path = "") { 547 | cerr << "WARNING: Cannot save_connectivity() unless running in sim accurate " 548 | "mode!" 549 | << endl; 550 | cerr << "WARNING: Connectivity will not be saved." << endl; 551 | } 552 | 553 | #endif 554 | }; 555 | 556 | #endif // __INTERCONNECT_ANNOTATE_HPP__ 557 | -------------------------------------------------------------------------------- /cmod/include/interconnect/interconnect_gen_utils.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: Copyright (c) 2019-2021 NVIDIA CORPORATION & 3 | * AFFILIATES. All rights reserved. 4 | * SPDX-License-Identifier: Apache-2.0 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | #ifndef __INTERCONNECT_GEN_UTILS_HPP__ 20 | #define __INTERCONNECT_GEN_UTILS_HPP__ 21 | 22 | // 8/21/20 npinckney - Ensure that nvhls_assert.h overrides SystemC 23 | #undef NVHLS_ASSERT_H 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | 32 | namespace interconnect { 33 | 34 | ///////////// Interconnect Message /////////////// 35 | template 36 | class LinkMsg : public nvhls_message { 37 | public: 38 | static const MSG_ID_t MSG_ID = MSG_TYPE::msg_id; 39 | typedef typename MSG_TYPE::data_t data_t; 40 | 41 | data_t msg; 42 | sc_lv dest_bits; 43 | 44 | //// Constructor-Destructor 45 | LinkMsg() { dest_bits = 0; } 46 | LinkMsg(const data_t &m) { 47 | dest_bits = 0; 48 | set_msg(m); 49 | } 50 | 51 | virtual inline unsigned int dest_map(const Destination &dest) { 52 | NVHLS_ASSERT(0); 53 | return 0; 54 | } 55 | 56 | //// Operators 57 | LinkMsg &operator=(const data_t &m) { 58 | set_msg(m); 59 | return *this; 60 | } 61 | LinkMsg &operator<<(const Destination &dest) { 62 | dest_bits[dest_map(dest)] = 1; 63 | NVHLS_ASSERT(0); 64 | return *this; 65 | } 66 | operator data_t() { return get_msg(); } 67 | 68 | // Some message types implement this for mapping (e.g. supermsg types) 69 | sc_lv get_dest_insts() { return dest_bits; } 70 | 71 | //// Setter-Getter 72 | void set_msg(const data_t &m) { msg = m; } 73 | data_t get_msg() const { return msg; } 74 | //// Temporary getters and setters until API changed 75 | template 76 | void set_msg(interconnect_t &ic, const data_t &m) { 77 | set_msg(m); 78 | } 79 | template 80 | void get_msg(interconnect_t &ic, data_t &m) { 81 | m = get_msg(); 82 | } 83 | // Conversion to generic type 84 | template 85 | operator InterconnectMessageSCLV() { 86 | // NVHLS_ASSERT(0); 87 | assert(MaxDestCount == interconnect::MaxDestCount); 88 | assert(Wrapped::width <= MaxDataWidth); 89 | 90 | InterconnectMessageSCLV new_im; 91 | new_im.set_msg(msg); 92 | return new_im; 93 | } 94 | 95 | //// Marshall support 96 | enum { width = Wrapped::width + Wrapped>::width }; 97 | 98 | template 99 | void Marshall(Marshaller &m) { 100 | m &msg; 101 | m &dest_bits; 102 | } 103 | }; 104 | 105 | // Stub specialization 106 | template 107 | class LinkMsg : public nvhls_message { 108 | public: 109 | static const MSG_ID_t MSG_ID = MSG_TYPE::msg_id; 110 | typedef typename MSG_TYPE::data_t data_t; 111 | 112 | // The actual message payload. 113 | data_t msg; 114 | 115 | //// Constructor-Destructor 116 | LinkMsg() {} 117 | LinkMsg(const data_t &m) { set_msg(m); } 118 | 119 | virtual inline unsigned int dest_map(const Destination &dest) { 120 | NVHLS_ASSERT(0); 121 | return 0; 122 | } 123 | 124 | //// Operators 125 | LinkMsg &operator=(const data_t &m) { 126 | set_msg(m); 127 | return *this; 128 | } 129 | 130 | LinkMsg &operator<<(const Destination &dest) { 131 | NVHLS_ASSERT(0); 132 | return *this; 133 | } 134 | operator data_t() { return get_msg(); } 135 | 136 | //// Setter-Getter 137 | void set_msg(const data_t &m) { msg = m; } 138 | data_t get_msg() { return msg; } 139 | //// Temporary getters and setters until API changed 140 | template 141 | void set_msg(interconnect_t &ic, const data_t &m) { 142 | set_msg(m); 143 | } 144 | template 145 | void get_msg(interconnect_t &ic, data_t &m) { 146 | m = get_msg(); 147 | } 148 | // Conversion to generic type 149 | template 150 | operator InterconnectMessageSCLV() { 151 | // NVHLS_ASSERT(0); 152 | assert(MaxDestCount == interconnect::MaxDestCount); 153 | assert(Wrapped::width <= MaxDataWidth); 154 | 155 | InterconnectMessageSCLV new_im; 156 | new_im.set_msg(msg); 157 | return new_im; 158 | } 159 | template 160 | explicit LinkMsg( 161 | const InterconnectMessageSCLV &im) {} 162 | 163 | //// Marshall support 164 | enum { width = Wrapped::width }; 165 | 166 | template 167 | void Marshall(Marshaller &m) { 168 | m &msg; 169 | } 170 | }; 171 | 172 | // ///////////// Interconnect Retiming Stage /////////////// 173 | 174 | // Internal single internal retiming stage. 175 | template 177 | class RetimingStagesInt : public sc_module { 178 | SC_HAS_PROCESS(RetimingStagesInt); 179 | 180 | public: 181 | sc_in_clk clk; 182 | sc_in rst; 183 | 184 | // New version (fifo based) 185 | Connections::In in_port; 186 | Connections::OutBuffered out_port; 187 | 188 | RetimingStagesInt(sc_module_name name_) : sc_module(name_) { 189 | SC_CTHREAD(run, clk.pos()); 190 | async_reset_signal_is(rst, false); 191 | } 192 | 193 | void run() { 194 | in_port.Reset(); 195 | out_port.Reset(); 196 | 197 | Message m; 198 | bool popped = false; 199 | 200 | #pragma hls_pipeline_init_interval 1 201 | #pragma pipeline_stall_mode flush 202 | while (1) { 203 | wait(); 204 | if (!popped) 205 | popped = in_port.PopNB(m); 206 | if (!out_port.Full() && popped) { 207 | out_port.Push(m); 208 | popped = false; 209 | } 210 | out_port.TransferNB(); 211 | } 212 | } 213 | }; 214 | 215 | // Variable retiming stages. 216 | template 219 | class RetimingStages : public sc_module { 220 | public: 221 | sc_in_clk clk; 222 | sc_in rst; 223 | 224 | // New version (fifo based) 225 | Connections::InBuffered in_port; 226 | Connections::OutBuffered out_port; 227 | 228 | nvhls::nv_array, num_stages> fifos; 229 | nvhls::nv_array, num_stages - 1> 230 | fifos_conn; 231 | 232 | RetimingStages() 233 | : sc_module(sc_module_name(sc_gen_unique_name("retimingstages"))), 234 | fifos("fifos"), 235 | fifos_conn("fifos_conn") { 236 | for (int i = 0; i < num_stages; i++) { 237 | fifos[i].clk(clk); 238 | fifos[i].rst(rst); 239 | if (i == 0) { 240 | fifos[i].in_port(in_port); 241 | } else { 242 | fifos[i].in_port(fifos_conn[i - 1]); 243 | } 244 | if (i == num_stages - 1) { 245 | fifos[i].out_port(out_port); 246 | } else { 247 | fifos[i].out_port(fifos_conn[i]); 248 | } 249 | } 250 | } 251 | 252 | explicit RetimingStages(sc_module_name name_) 253 | : sc_module(name_), fifos("fifos"), fifos_conn("fifos_conn") { 254 | for (int i = 0; i < num_stages; i++) { 255 | fifos[i].clk(clk); 256 | fifos[i].rst(rst); 257 | if (i == 0) { 258 | fifos[i].in_port(in_port); 259 | } else { 260 | fifos[i].in_port(fifos_conn[i - 1]); 261 | } 262 | if (i == num_stages - 1) { 263 | fifos[i].out_port(out_port); 264 | } else { 265 | fifos[i].out_port(fifos_conn[i]); 266 | } 267 | } 268 | } 269 | }; 270 | 271 | // Specialization for single stage case. 272 | template 274 | class RetimingStages : public sc_module { 275 | public: 276 | sc_in_clk clk; 277 | sc_in rst; 278 | 279 | Connections::InBuffered in_port; 280 | Connections::OutBuffered out_port; 281 | 282 | nvhls::nv_array, 1> fifos; 283 | 284 | RetimingStages() 285 | : sc_module(sc_module_name(sc_gen_unique_name("retimingstages"))), fifos("fifos") { 286 | fifos[0].clk(clk); 287 | fifos[0].rst(rst); 288 | fifos[0].in_port(in_port); 289 | fifos[0].out_port(out_port); 290 | } 291 | 292 | explicit RetimingStages(sc_module_name name_) 293 | : sc_module(name_), fifos("fifos") { 294 | fifos[0].clk(clk); 295 | fifos[0].rst(rst); 296 | fifos[0].in_port(in_port); 297 | fifos[0].out_port(out_port); 298 | } 299 | }; 300 | 301 | // Alias to 1 case since we always need a buffer to get extra capacity. 302 | template 304 | class RetimingStages 305 | : public RetimingStages { 306 | public: 307 | RetimingStages() 308 | : RetimingStages( 309 | sc_module_name(sc_gen_unique_name("retimingstages"))) {} 310 | 311 | explicit RetimingStages(sc_module_name name_) 312 | : RetimingStages(name_) {} 313 | }; 314 | 315 | // Specialization for 0 retiming stages, just a bypass instead. 316 | template 317 | class RetimingStages : public sc_module { 318 | SC_HAS_PROCESS(RetimingStages); 319 | 320 | public: 321 | Connections::In in_port; 322 | Connections::Out out_port; 323 | 324 | RetimingStages() : sc_module(sc_module_name(sc_gen_unique_name("retimingstages"))) { 325 | declare_method_process(do_bypass_handle, sc_gen_unique_name("do_bypass"), 326 | SC_CURRENT_USER_MODULE, do_bypass); 327 | sensitive << in_port._DATNAME_ << in_port._VLDNAME_ << out_port._RDYNAME_; 328 | 329 | #ifdef CONNECTIONS_SIM_ONLY 330 | in_port.disable_spawn(); 331 | out_port.disable_spawn(); 332 | #endif 333 | } 334 | 335 | RetimingStages(sc_module_name name_) : sc_module(name_) { 336 | declare_method_process(do_bypass_handle, sc_gen_unique_name("do_bypass"), 337 | SC_CURRENT_USER_MODULE, do_bypass); 338 | sensitive << in_port._DATNAME_ << in_port._VLDNAME_ << out_port._RDYNAME_; 339 | 340 | #ifdef CONNECTIONS_SIM_ONLY 341 | in_port.disable_spawn(); 342 | out_port.disable_spawn(); 343 | #endif 344 | } 345 | 346 | void do_bypass() { 347 | out_port._VLDNAME_.write(in_port._VLDNAME_.read()); 348 | in_port._RDYNAME_.write(out_port._RDYNAME_.read()); 349 | out_port._DATNAME_.write(in_port._DATNAME_.read()); 350 | } 351 | }; 352 | 353 | ///////////// Interconnect Interface /////////////// 354 | //#pragma hls_ungroup 355 | template 357 | class LinkSender : public sc_module { 358 | SC_HAS_PROCESS(LinkSender); 359 | 360 | protected: 361 | Connections::Combinational> in_port; 362 | 363 | public: 364 | static const int kDebugLevel = 0; 365 | 366 | sc_in_clk clk; 367 | sc_in rst; 368 | Connections::OutBuffered, 1, PortType> 369 | out_ports[dest_count]; 370 | 371 | LinkSender() : sc_module(sc_module_name(sc_gen_unique_name("link_sender"))) { 372 | SC_CTHREAD(run, clk.pos()); 373 | async_reset_signal_is(rst, false); 374 | } 375 | 376 | void Bind(Connections::OutBlocking> &p) { 377 | p(in_port); 378 | } 379 | 380 | void run() { 381 | in_port.ResetRead(); 382 | 383 | #pragma hls_unroll yes 384 | for (int i = 0; i < dest_count; ++i) { 385 | out_ports[i].Reset(); 386 | } 387 | 388 | bool all_sent = true; 389 | interconnect::Msg data; 390 | sc_lv dest_bits = 0; 391 | 392 | #pragma hls_pipeline_init_interval 1 393 | #pragma pipeline_stall_mode flush 394 | while (1) { 395 | wait(); 396 | 397 | if (all_sent) { 398 | interconnect::Msg in_data; 399 | CDCOUT(sc_time_stamp() << " " << name() << " LinkSender - " 400 | << MSG_TYPE::name << " Checking input port\n", 401 | kDebugLevel); 402 | if (in_port.PopNB(in_data)) { 403 | data = in_data; 404 | dest_bits = data.dest_bits; 405 | CDCOUT(sc_time_stamp() << " " << name() << " LinkSender - " 406 | << MSG_TYPE::name << " Popped data: " 407 | << ((typename MSG_TYPE::data_t)data) << endl, 408 | kDebugLevel); 409 | all_sent = false; 410 | } 411 | } 412 | 413 | // Push and update. 414 | #pragma hls_unroll yes 415 | for (int i = 0; i < dest_count; ++i) { 416 | if (dest_bits[i] == 1) { 417 | if (!out_ports[i].Full()) { 418 | out_ports[i].Push(data); 419 | CDCOUT(sc_time_stamp() << " " << name() << " LinkSender - " 420 | << MSG_TYPE::name << " Dest: " << i 421 | << " FIFO push output data: " 422 | << ((typename MSG_TYPE::data_t)data) << endl, 423 | kDebugLevel); 424 | dest_bits[i] = 0; 425 | } 426 | } 427 | } 428 | 429 | // End condition 430 | all_sent = (dest_bits == 0); 431 | 432 | #pragma hls_unroll yes 433 | for (int i = 0; i < dest_count; ++i) { 434 | out_ports[i].TransferNB(); 435 | } 436 | } 437 | } 438 | }; 439 | 440 | // Stub specialization 441 | //#pragma hls_ungroup 442 | template 443 | class LinkSender : public sc_module { 444 | SC_HAS_PROCESS(LinkSender); 445 | 446 | protected: 447 | Connections::Combinational> in_port; 448 | 449 | public: 450 | sc_in_clk clk; 451 | sc_in rst; 452 | 453 | LinkSender() : sc_module(sc_module_name(sc_gen_unique_name("link_sender"))) { 454 | SC_CTHREAD(run, clk.pos()); 455 | async_reset_signal_is(rst, false); 456 | } 457 | 458 | void Bind(Connections::OutBlocking> &p) { 459 | p(in_port); 460 | } 461 | 462 | void run() { 463 | in_port.ResetRead(); 464 | 465 | interconnect::Msg data; 466 | 467 | #pragma hls_pipeline_init_interval 1 468 | #pragma pipeline_stall_mode flush 469 | while (1) { 470 | wait(); 471 | in_port.Pop(); // Always run, since no destinations do not block 472 | } 473 | } 474 | }; 475 | 476 | //#pragma hls_ungroup 477 | template 479 | class LinkSenderPort : public sc_module { 480 | SC_HAS_PROCESS(LinkSenderPort); 481 | 482 | public: 483 | static const int kDebugLevel = 0; 484 | 485 | sc_in_clk clk; 486 | sc_in rst; 487 | Connections::In> in_port; 488 | Connections::OutBuffered, 1, PortType> 489 | out_ports[dest_count]; 490 | 491 | LinkSenderPort() 492 | : sc_module(sc_module_name(sc_gen_unique_name("link_sender"))) { 493 | SC_CTHREAD(run, clk.pos()); 494 | async_reset_signal_is(rst, false); 495 | } 496 | 497 | void Bind(Connections::InBlocking> &p) { 498 | in_port(p); 499 | } 500 | 501 | void run() { 502 | in_port.Reset(); 503 | 504 | #pragma hls_unroll yes 505 | for (int i = 0; i < dest_count; ++i) { 506 | out_ports[i].Reset(); 507 | } 508 | 509 | bool all_sent = true; 510 | interconnect::Msg data; 511 | sc_lv dest_bits = 0; 512 | 513 | #pragma hls_pipeline_init_interval 1 514 | #pragma pipeline_stall_mode flush 515 | while (1) { 516 | wait(); 517 | 518 | if (all_sent) { 519 | interconnect::Msg in_data; 520 | CDCOUT(sc_time_stamp() << " " << name() << " LinkSender - " 521 | << MSG_TYPE::name << " Checking input port\n", 522 | kDebugLevel); 523 | if (in_port.PopNB(in_data)) { 524 | data = in_data; 525 | dest_bits = data.dest_bits; 526 | CDCOUT(sc_time_stamp() << " " << name() << " LinkSender - " 527 | << MSG_TYPE::name << " Popped data: " 528 | << ((typename MSG_TYPE::data_t)data) << endl, 529 | kDebugLevel); 530 | all_sent = false; 531 | } 532 | } 533 | 534 | // Push and update. 535 | #pragma hls_unroll yes 536 | for (int i = 0; i < dest_count; ++i) { 537 | if (dest_bits[i] == 1) { 538 | if (!out_ports[i].Full()) { 539 | out_ports[i].Push(data); 540 | CDCOUT(sc_time_stamp() << " " << name() << " LinkSender - " 541 | << MSG_TYPE::name << " Dest: " << i 542 | << " FIFO push output data: " 543 | << ((typename MSG_TYPE::data_t)data) << endl, 544 | kDebugLevel); 545 | dest_bits[i] = 0; 546 | } 547 | } 548 | } 549 | 550 | // End condition 551 | all_sent = (dest_bits == 0); 552 | 553 | #pragma hls_unroll yes 554 | for (int i = 0; i < dest_count; ++i) { 555 | out_ports[i].TransferNB(); 556 | } 557 | } 558 | } 559 | }; 560 | 561 | //#pragma hls_ungroup 562 | template 564 | class LinkReceiver : public sc_module { 565 | SC_HAS_PROCESS(LinkReceiver); 566 | 567 | protected: 568 | Connections::CombinationalBufferedPorts, 0, 1> 569 | out_port; 570 | 571 | public: 572 | sc_in_clk clk; 573 | sc_in rst; 574 | Connections::InBuffered, 1, PortType> 575 | in_ports[src_count]; 576 | 577 | LinkReceiver() 578 | : sc_module(sc_module_name(sc_gen_unique_name("link_receiver"))) { 579 | SC_CTHREAD(run, clk.pos()); 580 | async_reset_signal_is(rst, false); 581 | } 582 | 583 | void Bind(Connections::InBlocking> &p) { 584 | p(out_port); 585 | } 586 | 587 | void run() { 588 | Arbiter arb; 589 | 590 | out_port.ResetWrite(); 591 | 592 | #pragma hls_unroll yes 593 | for (int i = 0; i < src_count; ++i) { 594 | in_ports[i].Reset(); 595 | } 596 | 597 | interconnect::Msg data; 598 | 599 | #pragma hls_pipeline_init_interval 1 600 | #pragma pipeline_stall_mode flush 601 | while (1) { 602 | wait(); 603 | 604 | #pragma hls_unroll yes 605 | for (int i = 0; i < src_count; ++i) { 606 | in_ports[i].TransferNB(); 607 | } 608 | 609 | NVUINTW(src_count) valid, select; 610 | NVUINTW(nvhls::index_width::val) select_id; 611 | #pragma hls_unroll yes 612 | for (int i = 0; i < src_count; i++) { 613 | valid[i] = !in_ports[i].Empty(); 614 | } 615 | select = arb.pick(valid); 616 | one_hot_to_bin::val>(select, 617 | select_id); 618 | if ((valid != 0) && !out_port.FullWrite()) { 619 | out_port.Push(in_ports[select_id].Pop()); 620 | } 621 | 622 | out_port.TransferNBWrite(); 623 | } 624 | } 625 | }; 626 | 627 | // Stub specialization 628 | //#pragma hls_ungroup 629 | template 630 | class LinkReceiver : public sc_module { 631 | SC_HAS_PROCESS(LinkReceiver); 632 | 633 | protected: 634 | Connections::CombinationalBufferedPorts, 0, 1> 635 | out_port; 636 | 637 | public: 638 | sc_in_clk clk; 639 | sc_in rst; 640 | 641 | LinkReceiver() 642 | : sc_module(sc_module_name(sc_gen_unique_name("link_receiver"))) {} 643 | 644 | void Bind(Connections::InBlocking> &p) { 645 | p(out_port); 646 | } 647 | }; 648 | 649 | //#pragma hls_ungroup 650 | template 652 | class LinkReceiverPort : public sc_module { 653 | SC_HAS_PROCESS(LinkReceiverPort); 654 | 655 | public: 656 | sc_in_clk clk; 657 | sc_in rst; 658 | Connections::OutBuffered, 1> out_port; 659 | Connections::InBuffered, 1, PortType> 660 | in_ports[src_count]; 661 | 662 | LinkReceiverPort() 663 | : sc_module(sc_module_name(sc_gen_unique_name("link_receiver"))) { 664 | SC_CTHREAD(run, clk.pos()); 665 | async_reset_signal_is(rst, false); 666 | } 667 | 668 | void Bind(Connections::OutBlocking> &p) { 669 | out_port(p); 670 | } 671 | 672 | void run() { 673 | Arbiter arb; 674 | 675 | out_port.Reset(); 676 | 677 | #pragma hls_unroll yes 678 | for (int i = 0; i < src_count; ++i) { 679 | in_ports[i].Reset(); 680 | } 681 | 682 | interconnect::Msg data; 683 | 684 | #pragma hls_pipeline_init_interval 1 685 | #pragma pipeline_stall_mode flush 686 | while (1) { 687 | wait(); 688 | 689 | #pragma hls_unroll yes 690 | for (int i = 0; i < src_count; ++i) { 691 | in_ports[i].TransferNB(); 692 | } 693 | 694 | NVUINTW(src_count) valid, select; 695 | NVUINTW(nvhls::index_width::val) select_id; 696 | #pragma hls_unroll yes 697 | for (int i = 0; i < src_count; i++) { 698 | valid[i] = !in_ports[i].Empty(); 699 | } 700 | select = arb.pick(valid); 701 | one_hot_to_bin::val>(select, 702 | select_id); 703 | if ((valid != 0) && !out_port.Full()) { 704 | out_port.Push(in_ports[select_id].Pop()); 705 | } 706 | 707 | out_port.TransferNB(); 708 | } 709 | } 710 | }; 711 | 712 | //#pragma hls_ungroup 713 | template 716 | class CrossbarICTop : public sc_module { 717 | SC_HAS_PROCESS(CrossbarICTop); 718 | 719 | public: 720 | sc_in_clk clk; 721 | sc_in rst; 722 | 723 | Connections::In, PortType> in_ports[num_srcs]; 724 | Connections::OutBuffered, 1, PortType> 725 | out_ports[num_dests]; 726 | 727 | ArbitratedCrossbar, num_srcs, num_dests, 728 | LenInputBuffer, LenOutputBuffer> arbxbar; 729 | 730 | CrossbarICTop() : sc_module(sc_module_name(sc_gen_unique_name("crossbar"))) { 731 | SC_THREAD(run); 732 | sensitive << clk.pos(); 733 | async_reset_signal_is(rst, false); 734 | } 735 | 736 | void run() { 737 | #pragma hls_unroll yes 738 | for (int inp_lane = 0; inp_lane < num_srcs; inp_lane++) { 739 | in_ports[inp_lane].Reset(); 740 | } 741 | 742 | #pragma hls_unroll yes 743 | for (int out_lane = 0; out_lane < num_dests; out_lane++) { 744 | out_ports[out_lane].Reset(); 745 | } 746 | 747 | #pragma hls_pipeline_init_interval 1 748 | while (1) { 749 | wait(); 750 | 751 | interconnect::Msg data_in_reg[num_srcs]; 752 | NVUINTW(nvhls::index_width::val) dest_in_reg[num_srcs]; 753 | bool valid_in_reg[num_srcs]; 754 | 755 | #pragma hls_unroll yes 756 | for (int inp_lane = 0; inp_lane < num_srcs; inp_lane++) { 757 | if (!arbxbar.isInputFull(inp_lane) && LenInputBuffer > 0) { 758 | valid_in_reg[inp_lane] = 759 | in_ports[inp_lane].PopNB(data_in_reg[inp_lane]); 760 | sc_lv v = data_in_reg[inp_lane].get_dest_insts(); 761 | 762 | dest_in_reg[inp_lane] = 763 | 0; // FIXME: get the destination from the packet. 764 | 765 | #pragma hls_unroll yes 766 | for (int i = 0; i < num_dests; ++i) { 767 | if (v[i] == 1) { 768 | dest_in_reg[inp_lane] = i; 769 | break; 770 | } 771 | } 772 | } else { 773 | valid_in_reg[inp_lane] = false; 774 | } 775 | } 776 | 777 | // Outputs from ArbitratedCrossbar 778 | interconnect::Msg data_out_reg[num_dests]; 779 | bool valid_out_reg[num_dests]; 780 | bool ready_reg[num_srcs]; 781 | 782 | arbxbar.run(data_in_reg, dest_in_reg, valid_in_reg, data_out_reg, 783 | valid_out_reg, ready_reg); 784 | 785 | // Push only the valid outputs. 786 | #pragma hls_unroll yes 787 | for (int out_lane = 0; out_lane < num_dests; out_lane++) { 788 | if (valid_out_reg[out_lane]) { 789 | if (!out_ports[out_lane].Full()) { 790 | out_ports[out_lane].Push(arbxbar.pop(out_lane)); 791 | } 792 | } 793 | out_ports[out_lane].TransferNB(); 794 | } 795 | } 796 | } 797 | }; 798 | 799 | // We have a couple options for handling destinations: 800 | // 1. UnionMsg type only has a "dest_bit" of the type message, 801 | // the subtype with the partition dest is has to be unmarshalled out. 802 | // However, may not be too bad for bit swizzling? 803 | // 804 | // 2. Instead create a union destination based on number of unique partitions. 805 | // Encode this along with message type. Probably necessary to route effectively, 806 | // when we do serdes already 807 | // 808 | // 3. Or dest_bits is max of sub msg_bit types. msg is payload only portion of 809 | // other bit types. 810 | // 811 | // This seems the most reasonable... we will need a per-msg dest map though 812 | // hmmm. 813 | template 814 | class UnionMsgMulticast : public nvhls_message { 815 | public: 816 | static const unsigned int num_dests_ = num_dests; 817 | typedef sc_lv data_t; 818 | 819 | data_t msg_bits; 820 | sc_lv dest_bits; 821 | NVUINTW(nvhls::index_width::val) 822 | msg_type_bits; // FIXME: probably roll this into payload, so that it is Msg 823 | // compatible. 824 | 825 | //// Constructor-Destructor 826 | UnionMsgMulticast() { dest_bits = 0; } 827 | UnionMsgMulticast(const data_t &m) { 828 | dest_bits = 0; 829 | set_msg(m); 830 | } 831 | 832 | virtual inline unsigned int dest_map(const Destination &dest) { 833 | NVHLS_ASSERT(0); 834 | return 0; 835 | } 836 | 837 | // Borrows heavily from SCLV mechanics, given a 838 | // marshable type <= num_dests and num_msg_bits 839 | // add it to the message and dest. 840 | template 841 | void __set_msg(const interconnect::Msg &msg_) { 842 | NVHLS_ASSERT_MSG(Wrapped::width <= num_bits, 843 | "Other message exceeded supported bits"); 844 | NVHLS_ASSERT_MSG(interconnect::Msg::dest_count <= num_dests, 845 | "Other message exceeded supported dests"); 846 | 847 | // Convert from Message to general sc_lv type 848 | Marshaller::width> marshaller; 849 | Wrapped wm(msg_.get_msg()); 850 | wm.Marshall(marshaller); 851 | msg_bits = 0; 852 | msg_bits.range(Wrapped::width - 1, 0) = 853 | marshaller.GetResult(); 854 | } 855 | 856 | template 857 | void __get_msg(interconnect::Msg &m_) { 858 | NVHLS_ASSERT_MSG(Wrapped::width <= num_bits, 859 | "Other message exceeded supported bits"); 860 | NVHLS_ASSERT_MSG(interconnect::Msg::dest_count <= num_dests, 861 | "Other message exceeded supported dests"); 862 | 863 | // Convert from general sc_lv type to Message 864 | sc_lv::width> mbits; 865 | mbits = msg_bits.range(Wrapped::width - 1, 0); 866 | Marshaller::width> marshaller(mbits); 867 | Wrapped result; 868 | result.Marshall(marshaller); 869 | m_ = result._VLDNAME_; 870 | } 871 | 872 | //// Marshall support 873 | enum { 874 | width = Wrapped::width + Wrapped>::width + 875 | Wrapped::val)>::width 876 | }; 877 | 878 | template 879 | void Marshall(Marshaller &m) { 880 | m &msg_bits; 881 | m &dest_bits; 882 | m &msg_type_bits; 883 | } 884 | }; 885 | }; 886 | 887 | #endif // ifndef __INTERCONNECT_GEN_UTILS_HPP__ 888 | --------------------------------------------------------------------------------