├── .gitignore ├── .gitmodules ├── .readthedocs.yml ├── Dockerfile ├── LICENSE ├── Makefile ├── README.md ├── msg.mk ├── scripts ├── .ycm_extra_conf.py ├── adg_visualization.py ├── clang-format.sh ├── dse_graphs │ ├── adg_analysis_graph.py │ ├── dse_graphs.py │ └── dse_resource_bar.py ├── fccm2023-tutorial.sh ├── fpga_resource_model │ ├── ml_model │ │ ├── component_create_data.py │ │ └── component_model.py │ ├── synthesis.py │ ├── verilog_statistics.py │ └── vivado.tcl ├── init-submodules.sh ├── micro2022-tutorial.sh ├── pcc.py ├── sched_visualization.py ├── time_it.py ├── view_dot.sh └── view_sched.sh ├── setup.sh └── ss-stack-conda-env.yml /.gitignore: -------------------------------------------------------------------------------- 1 | ss-tools/* 2 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "dsa-apps"] 2 | path = dsa-apps 3 | url = https://github.com/polyarch/dsa-apps 4 | [submodule "dsa-docs"] 5 | path = docs 6 | url = https://github.com/polyarch/dsa-docs 7 | [submodule "dsa-scheduler"] 8 | path = dsa-scheduler 9 | url = https://github.com/polyarch/dsa-scheduler 10 | [submodule "chipyard"] 11 | path = chipyard 12 | url = https://github.com/PolyArch/chipyard.git 13 | [submodule "dsa-gem5"] 14 | path = dsa-gem5 15 | url = https://github.com/polyarch/dsa-gem5 16 | [submodule "dsa-llvm-project"] 17 | path = dsa-llvm-project 18 | url = https://github.com/polyarch/dsa-llvm-project 19 | [submodule "dsa-riscv-ext"] 20 | path = dsa-riscv-ext 21 | url = https://github.com/polyarch/dsa-riscv-ext 22 | -------------------------------------------------------------------------------- /.readthedocs.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | 3 | python: 4 | install: 5 | - requirements: docs/requirements.txt 6 | 7 | build: 8 | os: ubuntu-22.04 9 | tools: 10 | python: "3.10" 11 | 12 | # Build documentation in the docs/ directory with Sphinx 13 | sphinx: 14 | configuration: docs/conf.py 15 | 16 | submodules: 17 | recursive: false 18 | include: 19 | - docs 20 | - dsa-scheduler 21 | 22 | 23 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # Get the base centos8 image from Docker Hub 2 | FROM --platform=linux/amd64 centos:8 3 | 4 | LABEL maintainer="Sihao Liu ; Jian Weng" 5 | ENV DEBIAN_FRONTEND=noninteractive 6 | 7 | # Update apps on the base image 8 | RUN sed -i 's/mirrorlist/#mirrorlist/g' /etc/yum.repos.d/CentOS-Linux-* 9 | RUN sed -i 's|#baseurl=http://mirror.centos.org|baseurl=http://vault.centos.org|g' /etc/yum.repos.d/CentOS-Linux-* 10 | RUN dnf makecache --refresh 11 | RUN dnf update -y 12 | 13 | # Install packages 14 | RUN dnf groupinstall -y "Development Tools" 15 | RUN dnf config-manager --set-enabled powertools 16 | RUN dnf install -y gperf texinfo expat-devel util-linux-user dtc 17 | RUN dnf install -y autoconf automake curl bison flex libtool gmp-devel ncurses-devel \ 18 | patchutils bc flex bison java-11-openjdk-devel libpng-devel perl \ 19 | libmpc-devel mpfr zlib-devel zip unzip zsh tmux wget git vim emacs gettext 20 | RUN dnf install -y https://rpmfind.net/linux/opensuse/distribution/leap/15.3/repo/oss/x86_64/fd-8.1.1-bp153.1.14.x86_64.rpm 21 | RUN dnf update -y 22 | 23 | # Install verilator 24 | RUN cd /root && git clone http://git.veripool.org/git/verilator && cd verilator && \ 25 | git checkout v4.034 && autoconf && ./configure && make -j$(nproc) && make install && \ 26 | rm -rf /root/verilator 27 | 28 | # SBT for Scala 29 | RUN rm -f /etc/yum.repos.d/bintray-rpm.repo && \ 30 | curl -L https://www.scala-sbt.org/sbt-rpm.repo > sbt-rpm.repo && \ 31 | mv sbt-rpm.repo /etc/yum.repos.d/ && \ 32 | dnf install -y sbt 33 | RUN dnf update -y 34 | 35 | # Oh-my-zsh 36 | RUN sh -c "$(curl -fsSL https://raw.github.com/ohmyzsh/ohmyzsh/master/tools/install.sh)" 37 | 38 | # Install Anaconda3 39 | RUN cd /root && wget https://repo.anaconda.com/archive/Anaconda3-2022.05-Linux-x86_64.sh && \ 40 | chmod u+x ./Anaconda3-2022.05-Linux-x86_64.sh && \ 41 | ./Anaconda3-2022.05-Linux-x86_64.sh -b -p /root/anaconda3 42 | RUN dnf update -y 43 | 44 | # Initialize Anaconda3 under zsh 45 | RUN echo "source /root/anaconda3/bin/activate" >> /root/.zshrc 46 | RUN dnf update -y 47 | 48 | # Download the repos 49 | RUN cd /root && git clone https://github.com/polyarch/dsa-framework 50 | RUN cd /root/dsa-framework && ./scripts/init-submodules.sh 51 | RUN dnf update -y 52 | 53 | # Remove Anaconda install file 54 | RUN rm /root/Anaconda3-2022.05-Linux-x86_64.sh 55 | RUN dnf update -y 56 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 2-clause License 2 | 3 | LLVM, gem5, and riscv-gnu-toolchain retain their LICENSES in their respective folders. 4 | 5 | All modified code (including dsa-apps, dsa-gem5, dsa-llvm-project, dsa-riscv-ext, dsa-xform, and spatial-scheduler) holds the the following license: 6 | 7 | Copyright (c) 2020, The University of California (Regents). All Rights Reserved. 8 | 9 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 10 | 11 | 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 12 | 13 | 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 14 | 15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 16 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | default: all 2 | 3 | include msg.mk 4 | 5 | .PHONY: all 6 | all: dsa-scheduler chipyard dsa-llvm-project dsa-gem5 7 | echo "Please source chipyard/env.sh if this is a first-time build" 8 | 9 | .PHONY: clean 10 | clean: clean-gem5 clean-llvm clean-chipyard clean-scheduler 11 | rm -rf ss-tools 12 | 13 | # DSA RISC-V Extension 14 | .PHONY: dsa-ext 15 | dsa-ext: 16 | make -C dsa-riscv-ext COMPAT=1 17 | 18 | # Spatial Scheduler 19 | .PHONY: dsa-scheduler 20 | dsa-scheduler: dsa-ext 21 | make -C $@ all 22 | 23 | clean-scheduler: 24 | rm -rf dsa-scheduler/build 25 | rm -rf dsa-scheduler/libtorch dsa-scheduler/libtorch.zip 26 | 27 | # ChipYard and RISC-V GNU Toolchain 28 | .PHONY: chipyard 29 | chipyard: dsa-ext 30 | ifneq ($(wildcard $(SS)/chipyard/env-riscv-tools.sh),) 31 | echo "ChipYard RISC-V ENV Script Found, skip rebuild chipyard toolchain" 32 | else 33 | echo "ChipYard RISC-V ENV Script Not Found, Building Toolchain ... " 34 | cd ./chipyard && ./scripts/build-toolchains.sh --ignore-qemu riscv-tools 35 | echo "Please source chipyard/env.sh" 36 | endif 37 | 38 | clean-chipyard: 39 | rm -rf chipyard/riscv-tools-install chipyard/env.sh chipyard/env-riscv-tools.sh 40 | 41 | # LLVM compiler 42 | .PHONY: dsa-llvm-project 43 | dsa-llvm-project: chipyard dsa-scheduler 44 | cd $@ && mkdir -p build && cd build && \ 45 | cmake -G "Unix Makefiles" -DCMAKE_BUILD_TYPE="Release" \ 46 | -DBUILD_SHARED_LIBS=ON -DLLVM_USE_SPLIT_DWARF=ON \ 47 | -DCMAKE_INSTALL_PREFIX=$(SS_TOOLS) -DLLVM_OPTIMIZED_TABLEGEN=ON \ 48 | -DLLVM_BUILD_TESTS=False -DLLVM_TARGETS_TO_BUILD="RISCV" \ 49 | -DLLVM_DEFAULT_TARGET_TRIPLE="riscv64-unknown-linux-gnu" \ 50 | -DCMAKE_CROSSCOMPILING=True -DLLVM_ENABLE_RTTI=ON \ 51 | -DLLVM_ENABLE_PROJECTS="clang" ../llvm 52 | make -C $@/build install -j$$((`nproc`)) 53 | 54 | clean-llvm: clean-scheduler 55 | rm -rf dsa-llvm-project/build 56 | 57 | # Gem5 simulator 58 | .PHONY: dsa-gem5 59 | dsa-gem5: chipyard dsa-scheduler 60 | source chipyard/env.sh && cd $@ && scons build/RISCV/gem5.opt build/RISCV/gem5.debug -j`nproc` 61 | 62 | clean-gem5: clean-scheduler 63 | rm -rf dsa-gem5/build 64 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Getting Started 2 | 3 | Please refer to [this page](https://dsa-framework.readthedocs.io/projects/dsa-docs/en/latest/Basics/Setup.html) for how to setup and run examples. 4 | 5 | ## Documentation 6 | [Docs](https://dsa-framework.readthedocs.io/) is under development. 7 | 8 | ## Notice 9 | We are releasing our latest version of DSA-Framework on MICRO 2022 Tutorial. Please register [here](https://www.microarch.org/micro55/) and stayed tuned. 10 | -------------------------------------------------------------------------------- /msg.mk: -------------------------------------------------------------------------------- 1 | define env-msg 2 | 3 | The following environment variables must be defined 4 | SS_STACK (suggested: $(PWD)) 5 | SS_TOOLS (suggested: $$SS_STACK/ss-tools) 6 | 7 | Additionally, $$SS_TOOLS/bin must be in your $$PATH. 8 | 9 | endef 10 | 11 | ifeq ($(SS_TOOLS),) 12 | $(error $(env-msg)) 13 | endif 14 | 15 | ifeq ($(SS_STACK),) 16 | $(error $(env-msg)) 17 | endif 18 | 19 | ifeq ($(findstring $(SS_TOOLS)/bin,$(PATH)),) 20 | $(error $(env-msg)) 21 | endif 22 | -------------------------------------------------------------------------------- /scripts/.ycm_extra_conf.py: -------------------------------------------------------------------------------- 1 | # This file is NOT licensed under the GPLv3, which is the license for the rest 2 | # of YouCompleteMe. 3 | # 4 | # Here's the license text for this file: 5 | # 6 | # This is free and unencumbered software released into the public domain. 7 | # 8 | # Anyone is free to copy, modify, publish, use, compile, sell, or 9 | # distribute this software, either in source code form or as a compiled 10 | # binary, for any purpose, commercial or non-commercial, and by any 11 | # means. 12 | # 13 | # In jurisdictions that recognize copyright laws, the author or authors 14 | # of this software dedicate any and all copyright interest in the 15 | # software to the public domain. We make this dedication for the benefit 16 | # of the public at large and to the detriment of our heirs and 17 | # successors. We intend this dedication to be an overt act of 18 | # relinquishment in perpetuity of all present and future rights to this 19 | # software under copyright law. 20 | # 21 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 22 | # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 23 | # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 24 | # IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 25 | # OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 26 | # ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 27 | # OTHER DEALINGS IN THE SOFTWARE. 28 | # 29 | # For more information, please refer to 30 | 31 | from distutils.sysconfig import get_python_inc 32 | import platform 33 | import os 34 | import subprocess 35 | import ycm_core 36 | 37 | DIR_OF_THIS_SCRIPT = os.path.abspath( os.path.dirname( __file__ ) ) 38 | DIR_OF_THIRD_PARTY = os.path.join( DIR_OF_THIS_SCRIPT, 'third_party' ) 39 | SOURCE_EXTENSIONS = [ '.cpp', '.cxx', '.cc', '.c', '.m', '.mm' ] 40 | 41 | # These are the compilation flags that will be used in case there's no 42 | # compilation database set (by default, one is not set). 43 | # CHANGE THIS LIST OF FLAGS. YES, THIS IS THE DROID YOU HAVE BEEN LOOKING FOR. 44 | flags = [ 45 | '-Wall', 46 | '-Wextra', 47 | '-Werror', 48 | '-Wno-long-long', 49 | '-Wno-variadic-macros', 50 | '-fexceptions', 51 | '-DNDEBUG', 52 | # You 100% do NOT need -DUSE_CLANG_COMPLETER and/or -DYCM_EXPORT in your flags; 53 | # only the YCM source code needs it. 54 | '-DUSE_CLANG_COMPLETER', 55 | '-DYCM_EXPORT=', 56 | # THIS IS IMPORTANT! Without the '-x' flag, Clang won't know which language to 57 | # use when compiling headers. So it will guess. Badly. So C++ headers will be 58 | # compiled as C headers. You don't want that so ALWAYS specify the '-x' flag. 59 | # For a C project, you would set this to 'c' instead of 'c++'. 60 | '-x', 61 | 'c++', 62 | '-isystem', 63 | 'cpp/pybind11', 64 | '-isystem', 65 | 'cpp/BoostParts', 66 | '-isystem', 67 | get_python_inc(), 68 | '-isystem', 69 | 'cpp/llvm/include', 70 | '-isystem', 71 | 'cpp/llvm/tools/clang/include', 72 | '-I', 73 | 'cpp/ycm', 74 | '-I', 75 | 'cpp/ycm/ClangCompleter', 76 | '-isystem', 77 | 'cpp/ycm/tests/gmock/gtest', 78 | '-isystem', 79 | 'cpp/ycm/tests/gmock/gtest/include', 80 | '-isystem', 81 | 'cpp/ycm/tests/gmock', 82 | '-isystem', 83 | 'cpp/ycm/tests/gmock/include', 84 | '-isystem', 85 | 'cpp/ycm/benchmarks/benchmark/include', 86 | ] 87 | 88 | # Clang automatically sets the '-std=' flag to 'c++14' for MSVC 2015 or later, 89 | # which is required for compiling the standard library, and to 'c++11' for older 90 | # versions. 91 | if platform.system() != 'Windows': 92 | flags.append( '-std=c++11' ) 93 | 94 | 95 | # Set this to the absolute path to the folder (NOT the file!) containing the 96 | # compile_commands.json file to use that instead of 'flags'. See here for 97 | # more details: http://clang.llvm.org/docs/JSONCompilationDatabase.html 98 | # 99 | # You can get CMake to generate this file for you by adding: 100 | # set( CMAKE_EXPORT_COMPILE_COMMANDS 1 ) 101 | # to your CMakeLists.txt file. 102 | # 103 | # Most projects will NOT need to set this to anything; you can just change the 104 | # 'flags' list of compilation flags. Notice that YCM itself uses that approach. 105 | compilation_database_folder = '' 106 | 107 | if os.path.exists( compilation_database_folder ): 108 | database = ycm_core.CompilationDatabase( compilation_database_folder ) 109 | else: 110 | database = None 111 | 112 | 113 | def IsHeaderFile( filename ): 114 | extension = os.path.splitext( filename )[ 1 ] 115 | return extension in [ '.h', '.hxx', '.hpp', '.hh' ] 116 | 117 | 118 | def FindCorrespondingSourceFile( filename ): 119 | if IsHeaderFile( filename ): 120 | basename = os.path.splitext( filename )[ 0 ] 121 | for extension in SOURCE_EXTENSIONS: 122 | replacement_file = basename + extension 123 | if os.path.exists( replacement_file ): 124 | return replacement_file 125 | return filename 126 | 127 | 128 | def Settings( **kwargs ): 129 | if kwargs[ 'language' ] == 'cfamily': 130 | # If the file is a header, try to find the corresponding source file and 131 | # retrieve its flags from the compilation database if using one. This is 132 | # necessary since compilation databases don't have entries for header files. 133 | # In addition, use this source file as the translation unit. This makes it 134 | # possible to jump from a declaration in the header file to its definition 135 | # in the corresponding source file. 136 | filename = FindCorrespondingSourceFile( kwargs[ 'filename' ] ) 137 | 138 | if not database: 139 | return { 140 | 'flags': flags, 141 | 'include_paths_relative_to_dir': DIR_OF_THIS_SCRIPT, 142 | 'override_filename': filename 143 | } 144 | 145 | compilation_info = database.GetCompilationInfoForFile( filename ) 146 | if not compilation_info.compiler_flags_: 147 | return {} 148 | 149 | # Bear in mind that compilation_info.compiler_flags_ does NOT return a 150 | # python list, but a "list-like" StringVec object. 151 | final_flags = list( compilation_info.compiler_flags_ ) 152 | 153 | # NOTE: This is just for YouCompleteMe; it's highly likely that your project 154 | # does NOT need to remove the stdlib flag. DO NOT USE THIS IN YOUR 155 | # ycm_extra_conf IF YOU'RE NOT 100% SURE YOU NEED IT. 156 | try: 157 | final_flags.remove( '-stdlib=libc++' ) 158 | except ValueError: 159 | pass 160 | 161 | return { 162 | 'flags': final_flags, 163 | 'include_paths_relative_to_dir': compilation_info.compiler_working_dir_, 164 | 'override_filename': filename 165 | } 166 | return {} 167 | 168 | 169 | def GetStandardLibraryIndexInSysPath( sys_path ): 170 | for path in sys_path: 171 | if os.path.isfile( os.path.join( path, 'os.py' ) ): 172 | return sys_path.index( path ) 173 | raise RuntimeError( 'Could not find standard library path in Python path.' ) 174 | 175 | 176 | def PythonSysPath( **kwargs ): 177 | sys_path = kwargs[ 'sys_path' ] 178 | for folder in os.listdir( DIR_OF_THIRD_PARTY ): 179 | if folder == 'python-future': 180 | folder = os.path.join( folder, 'src' ) 181 | sys_path.insert( GetStandardLibraryIndexInSysPath( sys_path ) + 1, 182 | os.path.realpath( os.path.join( DIR_OF_THIRD_PARTY, 183 | folder ) ) ) 184 | continue 185 | 186 | if folder == 'cregex': 187 | interpreter_path = kwargs[ 'interpreter_path' ] 188 | major_version = subprocess.check_output( [ 189 | interpreter_path, '-c', 'import sys; print( sys.version_info[ 0 ] )' ] 190 | ).rstrip().decode( 'utf8' ) 191 | folder = os.path.join( folder, 'regex_{}'.format( major_version ) ) 192 | 193 | sys_path.insert( 0, os.path.realpath( os.path.join( DIR_OF_THIRD_PARTY, 194 | folder ) ) ) 195 | return sys_path 196 | -------------------------------------------------------------------------------- /scripts/adg_visualization.py: -------------------------------------------------------------------------------- 1 | import pandas as pd 2 | import os 3 | import sys 4 | from os.path import dirname 5 | import json 6 | 7 | from pyvis.network import Network 8 | 9 | def add_function_unit(network, function_units, node): 10 | name, number = node.split('.') 11 | operations = function_units[node]['dsagen2.comp.config.CompKeys$DsaOperations$']['OperationDataTypeSet'] 12 | datawidth = function_units[node]['dsagen2.comp.config.CompKeys$CompNode$']['compBits'] 13 | size = (int(datawidth) / 64) * 25 14 | 15 | title = name + ' ' + number + '\n' 16 | title += 'Operations: \n' 17 | for operation in operations: 18 | title += ' ' + operation + '\n' 19 | 20 | network.add_node(node, label='PE ' + number, title=title, size=size, shape='square', color='#f46049') 21 | 22 | 23 | def add_switch(network, switches, node): 24 | name, number = node.split('.') 25 | datawidth = switches[node]['dsagen2.comp.config.CompKeys$CompNode$']['compBits'] 26 | size = (int(datawidth) / 64) * 25 27 | 28 | network.add_node(node, label='SW ' + number, size=size, color='#fbbe5b') 29 | 30 | 31 | def adg_vis(filename, graph_name): 32 | if (not os.path.exists(filename)): 33 | return 34 | with open(filename) as f: 35 | data = json.load(f) 36 | 37 | links = pd.DataFrame.from_dict(data['DSAGenEdges']) 38 | nodes = pd.DataFrame(data['DSAGenNodes']) 39 | 40 | comp_nodes = pd.DataFrame() 41 | functional_units = pd.DataFrame() 42 | switches = pd.DataFrame() 43 | 44 | ivp_nodes = pd.DataFrame() 45 | ovp_nodes = pd.DataFrame() 46 | 47 | mem_nodes = pd.DataFrame() 48 | dma_nodes = pd.DataFrame() 49 | scratchpad_nodes = pd.DataFrame() 50 | rec_nodes = pd.DataFrame() 51 | gen_nodes = pd.DataFrame() 52 | reg_nodes = pd.DataFrame() 53 | 54 | for node in nodes: 55 | if not isinstance(nodes[node]['dsagen2.sync.config.SyncKeys$IVPNode$'], float): 56 | ivp_nodes[node] = nodes[node].dropna() 57 | if not isinstance(nodes[node]['dsagen2.sync.config.SyncKeys$OVPNode$'], float): 58 | ovp_nodes[node] = nodes[node].dropna() 59 | if not isinstance(nodes[node]['dsagen2.mem.config.MemKeys$MemNode$'], float): 60 | mem_nodes[node] = nodes[node].dropna() 61 | if nodes[node]['dsagen2.mem.config.MemKeys$MemNode$']['nodeType'] == 'ScratchpadMemory': 62 | scratchpad_nodes[node] = nodes[node].dropna() 63 | elif nodes[node]['dsagen2.mem.config.MemKeys$MemNode$']['nodeType'] == 'DirectMemoryAccess': 64 | dma_nodes[node] = nodes[node].dropna() 65 | elif nodes[node]['dsagen2.mem.config.MemKeys$MemNode$']['nodeType'] == 'RecurrenceEngine': 66 | rec_nodes[node] = nodes[node].dropna() 67 | elif nodes[node]['dsagen2.mem.config.MemKeys$MemNode$']['nodeType'] == 'GenerateEngine': 68 | gen_nodes[node] = nodes[node].dropna() 69 | else: 70 | reg_nodes[node] = nodes[node].dropna() 71 | if not isinstance(nodes[node]['dsagen2.comp.config.CompKeys$CompNode$'], float): 72 | if nodes[node]['dsagen2.comp.config.CompKeys$CompNode$']['nodeType'] == "ProcessingElement": 73 | functional_units[node] = nodes[node].dropna() 74 | else: 75 | switches[node] = nodes[node].dropna() 76 | comp_nodes[node] = nodes[node].dropna() 77 | 78 | net = Network(directed=True, width='100%') 79 | 80 | for node in functional_units: 81 | add_function_unit(net, functional_units, node) 82 | 83 | for node in switches: 84 | add_switch(net, switches, node) 85 | 86 | for node in ivp_nodes: 87 | name, number = node.split('.') 88 | net.add_node(node, label='IVP ' + number, color='#c895f6') 89 | 90 | for node in ovp_nodes: 91 | name, number = node.split('.') 92 | net.add_node(node, label='OVP ' + number, color='#571a8e') 93 | 94 | for node in dma_nodes: 95 | name, number = node.split('.') 96 | net.add_node(node, label='DMA ' + number, color='#00B7EB') 97 | 98 | for node in scratchpad_nodes: 99 | name, number = node.split('.') 100 | net.add_node(node, label='SPM ' + number, color='#89CFF0') 101 | 102 | for node in rec_nodes: 103 | name, number = node.split('.') 104 | net.add_node(node, label='REC ' + number, color='#1e9ae0') 105 | 106 | for node in gen_nodes: 107 | name, number = node.split('.') 108 | net.add_node(node, label='GEN ' + number, color='#0067A5') 109 | 110 | for node in reg_nodes: 111 | name, number = node.split('.') 112 | net.add_node(node, label='REG ' + number, color='#0F3D92') 113 | 114 | 115 | 116 | for i in range(len(links)): 117 | link = links.iloc[i] 118 | physics = True 119 | lineStyle = {'type': 'dynamic'} 120 | if link['SourceNodeType'] != "ProcessingElement" and link['SourceNodeType'] != "Switch": 121 | physics = False 122 | lineStyle['type'] = 'discrete' 123 | if link['SinkNodeType'] != "ProcessingElement" and link['SinkNodeType'] != "Switch": 124 | physics = False 125 | lineStyle['type'] = 'discrete' 126 | 127 | 128 | sourceId = str(link['SourceNodeType']) + '.' + str(link['SourceNodeId']) 129 | sinkId = str(link['SinkNodeType']) + '.' + str(link['SinkNodeId']) 130 | net.add_edge(sourceId, sinkId, arrows='to', smooth=lineStyle, physics=physics, weight=1) 131 | 132 | 133 | for node in ivp_nodes: 134 | name, number = node.split('.') 135 | connectId = int(number) + 1 136 | if connectId < len(ivp_nodes.columns): 137 | net.add_edge(node, name + '.' + str(connectId), hidden=True) 138 | net.add_edge(name + '.' + str(connectId), node, hidden=True) 139 | 140 | 141 | for node in ovp_nodes: 142 | name, number = node.split('.') 143 | connectId = int(number) + 1 144 | if connectId < len(ovp_nodes.columns): 145 | net.add_edge(node, name + '.' + str(connectId), hidden=True) 146 | net.add_edge(name + '.' + str(connectId), node, hidden=True) 147 | 148 | for node in dma_nodes: 149 | name, number = node.split('.') 150 | connectId = int(number) + 1 151 | if connectId < len(dma_nodes.columns): 152 | net.add_edge(node, name + '.' + str(connectId), hidden=True) 153 | net.add_edge(name + '.' + str(connectId), node, hidden=True) 154 | 155 | for node in scratchpad_nodes: 156 | name, number = node.split('.') 157 | connectId = int(number) + 1 158 | if connectId < len(scratchpad_nodes.columns): 159 | net.add_edge(node, name + '.' + str(connectId), hidden=True) 160 | net.add_edge(name + '.' + str(connectId), node, hidden=True) 161 | 162 | for node in rec_nodes: 163 | name, number = node.split('.') 164 | connectId = int(number) + 1 165 | if connectId < len(rec_nodes.columns): 166 | net.add_edge(node, name + '.' + str(connectId), hidden=True) 167 | net.add_edge(name + '.' + str(connectId), node, hidden=True) 168 | 169 | for node in gen_nodes: 170 | name, number = node.split('.') 171 | connectId = int(number) + 1 172 | if connectId < len(gen_nodes.columns): 173 | net.add_edge(node, name + '.' + str(connectId), hidden=True) 174 | net.add_edge(name + '.' + str(connectId), node, hidden=True) 175 | 176 | for node in reg_nodes: 177 | name, number = node.split('.') 178 | connectId = int(number) + 1 179 | if connectId < len(reg_nodes.columns): 180 | net.add_edge(node, name + '.' + str(connectId), hidden=True) 181 | net.add_edge(name + '.' + str(connectId), node, hidden=True) 182 | 183 | net.show(graph_name + '.html') 184 | 185 | print('Finished HTML: ' + graph_name) 186 | 187 | 188 | 189 | adg_file = sys.argv[1] 190 | 191 | if adg_file.endswith('.json'): 192 | adg_vis(adg_file, adg_file[:-5]) 193 | else: 194 | print('ADG JSON File Required. Given: ' + adg_file) -------------------------------------------------------------------------------- /scripts/clang-format.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [ -z $1 ]; then 4 | repo=`git rev-parse --show-toplevel` 5 | find $repo \( -iname \*.cc -o -iname \*.h -o -iname \*.cpp -o -iname \*.hh \) | xargs -P`nproc` clang-format -i 6 | else 7 | clang-format -i $1 8 | fi 9 | -------------------------------------------------------------------------------- /scripts/dse_graphs/adg_analysis_graph.py: -------------------------------------------------------------------------------- 1 | import matplotlib as mpl 2 | import matplotlib.pyplot as plt 3 | from matplotlib.patches import Patch 4 | import matplotlib.ticker as ticker 5 | import numpy as np 6 | import pandas as pd 7 | import os 8 | import glob 9 | import sys 10 | from os.path import dirname 11 | 12 | from multiprocessing import Process 13 | from math import atan2, degrees 14 | import numpy as np 15 | import json 16 | 17 | def bar(axis, data, value, asStr=True): 18 | indices = 0 19 | if asStr: 20 | indices = data[value].value_counts().index.to_numpy(dtype=str) 21 | else: 22 | indices = data[value].value_counts().index.to_numpy() 23 | values = data[value].value_counts().to_numpy() 24 | bar = axis.bar(indices, values) 25 | 26 | for index, value in enumerate(indices): 27 | axis.text(value, values[index], str(values[index])) 28 | return bar 29 | 30 | def functional_unit_bar(axis, data): 31 | data_lists = data['instructions'].to_numpy().flatten() 32 | instructions = np.array([]) 33 | for data in data_lists: 34 | instructions = np.append(instructions, data) 35 | 36 | indices = [] 37 | values = [] 38 | 39 | for instruction in instructions: 40 | if instruction in indices: 41 | values[np.where(np.array(indices)==instruction)[0][0]] += 1 42 | else: 43 | indices.append(instruction) 44 | values.append(1) 45 | 46 | bar = axis.bar(indices, values) 47 | axis.set_xticks(indices) 48 | axis.set_xticklabels(indices, rotation=45) 49 | 50 | for index, value in enumerate(indices): 51 | axis.text(value, values[index], str(values[index])) 52 | 53 | def flow_control_pie(axis, data): 54 | indices = data['flow_control'].value_counts().index.to_numpy() 55 | values = data['flow_control'].value_counts().to_numpy() 56 | 57 | colors = ['#1f77b4', '#ff7f0e'] 58 | if len(indices) == 0: 59 | axis.axis('off') 60 | return 61 | if len(indices) == 1: 62 | if indices[0]: 63 | colors = ['#1f77b4'] 64 | else: 65 | colors = ['#ff7f0e'] 66 | elif not indices[0]: 67 | colors = ['#ff7f0e', '#1f77b4'] 68 | 69 | axis.axis('equal') 70 | return axis.pie(values, labels=indices, colors=colors, autopct='%1.1f%%') 71 | 72 | 73 | def input_output_bar(axis, in_links, out_links): 74 | in_links_columns = np.unique(in_links) 75 | in_links_values = [0] * len(in_links_columns) 76 | for value in in_links: 77 | for i, column in enumerate(in_links_columns): 78 | if value == column: 79 | in_links_values[i] += 1 80 | 81 | out_links_columns = np.unique(out_links) 82 | out_links_values = [0] * len(out_links_columns) 83 | for value in out_links: 84 | for i, column in enumerate(out_links_columns): 85 | if value == column: 86 | out_links_values[i] += 1 87 | 88 | concatonated_index = np.unique(np.append(in_links_columns, out_links_columns)) 89 | 90 | final_outlinks_values = [0] * len(concatonated_index) 91 | final_inlinks_values = [0] * len(concatonated_index) 92 | for i, index in enumerate(concatonated_index): 93 | if index in in_links_columns: 94 | for j, value in enumerate(in_links_columns): 95 | if index == value: 96 | final_inlinks_values[i] = in_links_values[j] 97 | if index in out_links_columns: 98 | for j, value in enumerate(out_links_columns): 99 | if index == value: 100 | final_outlinks_values[i] = out_links_values[j] 101 | 102 | width = 0.25 103 | r = np.arange(len(concatonated_index)) 104 | 105 | bar = axis.bar(r, final_inlinks_values, width=width, facecolor='orange', edgecolor='black', label='in links') 106 | bar = axis.bar(r + width, final_outlinks_values, width=width, facecolor='blue', edgecolor='black', label='out links') 107 | 108 | axis.set_xticks(r) 109 | axis.set_xticklabels(concatonated_index) 110 | 111 | def _finditem(obj, key): 112 | if key in obj: return obj[key] 113 | for k, v in obj.items(): 114 | if isinstance(v,dict): 115 | item = _finditem(v, key) 116 | if item is not None: 117 | return item 118 | 119 | def adg_analysis(filename, name): 120 | if (not os.path.exists(filename)): 121 | return 122 | with open(filename) as f: 123 | data = json.load(f) 124 | 125 | links = pd.DataFrame.from_dict(data['DSAGenEdges']) 126 | nodes = pd.DataFrame(data['DSAGenNodes']) 127 | 128 | comp_nodes = pd.DataFrame() 129 | functional_units = pd.DataFrame() 130 | switches = pd.DataFrame() 131 | 132 | ivp_nodes = pd.DataFrame() 133 | ovp_nodes = pd.DataFrame() 134 | 135 | mem_nodes = pd.DataFrame() 136 | 137 | for node in nodes: 138 | if not isinstance(nodes[node]['dsagen2.sync.config.SyncKeys$IVPNode$'], float): 139 | ivp_nodes[node] = nodes[node].dropna() 140 | if not isinstance(nodes[node]['dsagen2.sync.config.SyncKeys$OVPNode$'], float): 141 | ovp_nodes[node] = nodes[node].dropna() 142 | if not isinstance(nodes[node]['dsagen2.mem.config.MemKeys$MemNode$'], float): 143 | mem_nodes[node] = nodes[node].dropna() 144 | 145 | if not isinstance(nodes[node]['dsagen2.comp.config.CompKeys$CompNode$'], float): 146 | if nodes[node]['dsagen2.comp.config.CompKeys$CompNode$']['nodeType'] == "ProcessingElement": 147 | functional_units[node] = nodes[node].dropna() 148 | else: 149 | switches[node] = nodes[node].dropna() 150 | comp_nodes[node] = nodes[node].dropna() 151 | if not os.path.exists(name): 152 | os.makedirs(name) 153 | fig, ax1 = plt.subplots(1, 1) 154 | 155 | x_values, y_values = ['links', 'nodes', 'processing elements', 'switches', 'input vector port', 'output vector port', 'memory node'], [len(links), len(nodes.columns), len(functional_units.columns), len(switches.columns), len(ivp_nodes.columns), len(ovp_nodes.columns), len(mem_nodes.columns)] 156 | 157 | ax1.bar(x_values, y_values) 158 | for index, value in enumerate(x_values): 159 | ax1.text(value, y_values[index], str(y_values[index])) 160 | ax1.set_title("Per-Unit Count") 161 | ax1.set_ylabel('Count') 162 | 163 | fig.set_size_inches(16, 12) 164 | fig.tight_layout() 165 | plt.savefig(name + '/' + name.split('/')[-1] + '-adg_count.png', dpi=300) 166 | plt.close() 167 | 168 | 169 | fig, (ax1, ax2, ax3, ax4) = plt.subplots(4, 1) 170 | 171 | pe_names = list(functional_units.columns) 172 | pe_inputs = [0 for i in range(len(pe_names))] 173 | pe_outputs = [0 for i in range(len(pe_names))] 174 | 175 | switch_names = list(switches.columns) 176 | switch_inputs = [0 for i in range(len(switch_names))] 177 | switch_outputs = [0 for i in range(len(switch_names))] 178 | 179 | ivp_names = list(ivp_nodes.columns) 180 | ivp_inputs = [0 for i in range(len(ivp_names))] 181 | ivp_outputs = [0 for i in range(len(ivp_names))] 182 | 183 | ovp_names = list(ovp_nodes.columns) 184 | ovp_inputs = [0 for i in range(len(ovp_names))] 185 | ovp_outputs = [0 for i in range(len(ovp_names))] 186 | 187 | for i in range(len(links)): 188 | link = links.iloc[i] 189 | if (link['SourceNodeType'] == "ProcessingElement"): 190 | for i, pe in enumerate(pe_names): 191 | pe_id = int(pe.split('.')[1]) 192 | if (pe_id == link['SourceNodeId']): 193 | pe_inputs[i] += 1 194 | if (link['SinkNodeType'] == "ProcessingElement"): 195 | for i, pe in enumerate(pe_names): 196 | pe_id = int(pe.split('.')[1]) 197 | if (pe_id == link['SinkNodeId']): 198 | pe_outputs[i] += 1 199 | if (link['SourceNodeType'] == "Switch"): 200 | for i, sw in enumerate(switch_names): 201 | sw_id = int(sw.split('.')[1]) 202 | if (sw_id == link['SourceNodeId']): 203 | switch_inputs[i] += 1 204 | if (link['SinkNodeType'] == "Switch"): 205 | for i, sw in enumerate(switch_names): 206 | sw_id = int(sw.split('.')[1]) 207 | if (sw_id == link['SinkNodeId']): 208 | switch_outputs[i] += 1 209 | if (link['SourceNodeType'] == "InputVectorPort"): 210 | for i, ivp in enumerate(ivp_names): 211 | ivp_id = int(ivp.split('.')[1]) 212 | if (ivp_id == link['SourceNodeId']): 213 | ivp_inputs[i] += 1 214 | if (link['SinkNodeType'] == "InputVectorPort"): 215 | for i, ivp in enumerate(ivp_names): 216 | ivp_id = int(ivp.split('.')[1]) 217 | if (ivp_id == link['SinkNodeId']): 218 | ivp_outputs[i] += 1 219 | if (link['SourceNodeType'] == "OutputVectorPort"): 220 | for i, ivp in enumerate(ovp_names): 221 | ovp_id = int(ivp.split('.')[1]) 222 | if (ovp_id == link['SourceNodeId']): 223 | ovp_inputs[i] += 1 224 | if (link['SinkNodeType'] == "OutputVectorPort"): 225 | for i, ovp in enumerate(ovp_names): 226 | ovp_id = int(ovp.split('.')[1]) 227 | if (ovp_id == link['SinkNodeId']): 228 | ovp_outputs[i] += 1 229 | 230 | input_output_bar(ax1, pe_outputs, pe_inputs) 231 | ax1.set_ylabel('# Processing Elements') 232 | ax1.set_xlabel('Degree') 233 | ax1.set_title("Processing Elements") 234 | 235 | input_output_bar(ax2, switch_outputs, switch_inputs) 236 | ax2.set_ylabel('# Switches') 237 | ax2.set_xlabel('Degree') 238 | ax2.set_title("Switches") 239 | 240 | input_output_bar(ax3, ivp_outputs, ivp_inputs) 241 | ax3.set_ylabel('# Input Vector Ports') 242 | ax3.set_xlabel('Degree') 243 | ax3.set_title("Input Vector Port") 244 | 245 | input_output_bar(ax4, ovp_outputs, ovp_inputs) 246 | ax4.set_ylabel('# Output Vector Ports') 247 | ax4.set_xlabel('Degree') 248 | ax4.set_title("Output Vector Port") 249 | fig.legend(handles=[Patch(facecolor='orange', edgecolor='black', 250 | label='Input-Links'), Patch(facecolor='blue', edgecolor='black', 251 | label='Output-Links')]) 252 | 253 | 254 | fig.set_size_inches(16, 12) 255 | fig.tight_layout() 256 | plt.savefig(name + '/' + name.split('/')[-1] + '-in-out-adg.png', dpi=300) 257 | plt.close() 258 | 259 | print('Finished!') 260 | 261 | adg_file = sys.argv[1] 262 | 263 | if adg_file.endswith('.json'): 264 | graph_folder = adg_file[:-5] 265 | if not os.path.exists(graph_folder): 266 | os.mkdir(graph_folder) 267 | adg_analysis(adg_file, adg_file[:-5]) 268 | else: 269 | print('ADG JSON File Required. Given: ' + adg_file) -------------------------------------------------------------------------------- /scripts/dse_graphs/dse_graphs.py: -------------------------------------------------------------------------------- 1 | import matplotlib as mpl 2 | import matplotlib.pyplot as plt 3 | import matplotlib.ticker as ticker 4 | import numpy as np 5 | import pandas as pd 6 | 7 | import os.path 8 | from os import path 9 | import sys 10 | 11 | from math import atan2, degrees, ceil, floor 12 | import numpy as np 13 | 14 | def get_resources(token): 15 | '''total lut: 65766.6, logic lut: 53923, ram lut: 12235.6, ff: 48493.8''' 16 | resources = token.str.split(',', expand=True) 17 | resources[0] = resources[0].str.extract('(\d+)', expand=False).astype(int) 18 | resources[1] = resources[1].str.extract('(\d+)', expand=False).astype(int) 19 | resources[2] = resources[2].str.extract('(\d+)', expand=False).astype(int) 20 | resources[3] = resources[3].str.extract('(\d+)', expand=False).astype(int) 21 | resources[4] = resources[4].str.extract('(\d+)', expand=False).astype(int) 22 | resources[5] = resources[5].str.extract('(\d+)', expand=False).astype(int) 23 | resources[6] = resources[6].str.extract('(\d+)', expand=False).astype(int) 24 | resources[7] = resources[7].str.extract('(\d+)', expand=False).astype(int) 25 | resources[8] = resources[8].str.extract('(\d+)', expand=False).astype(int) 26 | resources = resources.set_axis(['total lut', 'logic lut', 'srl', 'ram lut', 'ff', 'ramb36', 'ramb18', 'uram', 'dsp'], axis=1, copy=False) 27 | return resources 28 | 29 | def get_dfg_performance(token): 30 | axis = [] 31 | performances = token.str.split(',', expand=True) 32 | for i in range(performances.shape[1]): 33 | performances[i] = performances[i].str.split(':').str[1] 34 | performances[i] = performances[i].str.extract('(\d+)', expand=False).astype(float) 35 | return performances 36 | 37 | def get_workload(token): 38 | axis = [] 39 | workload = token.str.split(',', expand=True) 40 | for i in range(workload.shape[1]): 41 | axis.append(i) 42 | workload[i] = workload[i].str.extract('(\d+)', expand=False).astype(int) 43 | workload = workload.set_axis(axis) 44 | return workload 45 | 46 | def load_to_global(index, data, global_data): 47 | for i in range(len(data)): 48 | global_data[i][index] = np.asarray(data[i]) 49 | return global_data 50 | 51 | def load_line(filepath): 52 | if not os.path.exists(filepath): 53 | print("File doesnt exist: " + filepath) 54 | exit(1) 55 | line_data = pd.read_csv(filepath) 56 | line_data_resources = get_resources(line_data['Best Single-Core Resources']) 57 | ''' 58 | fu_resources = get_resources(line_data['Best FU Resource']) 59 | switch_resources = get_resources(line_data['Best Switch Resource']) 60 | vport_resources = get_resources(line_data['Best VPort Resource']) 61 | mem_resources = get_resources(line_data['Best Mem Resource']) 62 | ''' 63 | 64 | output_data = [0] * 18 65 | 66 | 67 | def load_resources(line_data_resources, startpos): 68 | output_data[startpos] = line_data_resources['total lut'].to_numpy() 69 | output_data[startpos + 1] = line_data_resources['logic lut'].to_numpy() 70 | output_data[startpos + 2] = line_data_resources['ram lut'].to_numpy() 71 | output_data[startpos + 3] = line_data_resources['srl'].to_numpy() 72 | output_data[startpos + 4] = line_data_resources['ff'].to_numpy() 73 | output_data[startpos + 5] = line_data_resources['ramb36'].to_numpy() 74 | output_data[startpos + 6] = line_data_resources['ramb18'].to_numpy() 75 | output_data[startpos + 7] = line_data_resources['uram'].to_numpy() 76 | output_data[startpos + 8] = line_data_resources['dsp'].to_numpy() 77 | 78 | line_data = pd.read_csv(filepath) 79 | output_data[0] = line_data['Time'].to_numpy() 80 | output_data[1] = line_data['Iteration'].to_numpy() 81 | output_data[2] = line_data['Best Objective Performance'].to_numpy() 82 | output_data[3] = line_data['Best Objective Area'].to_numpy() 83 | output_data[4] = line_data['Best Number of Cores'] 84 | output_data[5] = line_data['Best System Bus Size'] 85 | load_resources(line_data_resources, 6) 86 | return output_data 87 | 88 | 89 | def plot_lines(axis, colors, x_axis, y_axis): 90 | lines = [] 91 | lines.append(axis.plot(x_axis[0], y_axis[0], color=colors[0])) 92 | 93 | return lines 94 | 95 | def set_axis(axis, x_axis_name, y_axis_name): 96 | axis.set_xlabel(x_axis_name) 97 | axis.set_ylabel(y_axis_name) 98 | axis.axis(xmin=0, ymin=0) 99 | axis.grid(axis='y') 100 | 101 | def create_graph(filename, graph_folder): 102 | colors = ['red'] 103 | 104 | metrics = ['Time', 'Iteration', 'Best Objective', 'Best Performance', 'Best Resources', 'total lut', 'logic lut', 'ram lut', 'ff', 'total lut', 'logic lut', 'ram lut', 'ff', 'total lut', 'logic lut', 'ram lut', 'ff', 'total lut', 'logic lut', 'ram lut', 'ff', 'total lut', 'logic lut', 'ram lut', 'ff'] 105 | 106 | data = np.zeros((len(metrics) + 30, 1), dtype=object) 107 | data = load_to_global(i, load_line(filename), data) 108 | 109 | times = data[0, :] 110 | iterations = data[1, :] 111 | performance = data[2, :] 112 | area = data[3, :] 113 | cores = data[4, :] 114 | system_bus = data[5, :] 115 | total_lut = data[6, :] 116 | logic_lut = data[7, :] 117 | ram_lut = data[8, :] 118 | srl = data[9, :] 119 | ff = data[10, :] 120 | 121 | fig, (ax1, ax2) = plt.subplots(1, 2) 122 | 123 | p1axis = plot_lines(ax1, colors, times, performance) 124 | set_axis(ax1, 'Time (S)', 'Estimated IPC') 125 | 126 | p2axis = plot_lines(ax2, colors, times, area) 127 | set_axis(ax2, 'Time (S)', 'Single-Core-Area') 128 | 129 | fig.set_size_inches(14, 8) 130 | fig.tight_layout() 131 | plt.savefig(graph_folder + '/dse-objective.png', dpi=300) 132 | 133 | fig, ((ax1, ax2), (ax3, ax4)) = plt.subplots(2, 2) 134 | fig.tight_layout() 135 | 136 | p5axis = plot_lines(ax1, colors, times, total_lut) 137 | set_axis(ax1, 'Time (S)', 'Total Lut') 138 | 139 | p6axis = plot_lines(ax2, colors, times, logic_lut) 140 | set_axis(ax2, 'Time (S)', 'Logic LUT') 141 | 142 | p7axis = plot_lines(ax3, colors, times, ram_lut) 143 | set_axis(ax3, 'Time (S)', 'Ram LUT') 144 | 145 | p8axis = plot_lines(ax4, colors, times, ff) 146 | set_axis(ax4, 'Time (S)', 'Flip Flop') 147 | 148 | fig.set_size_inches(14, 8) 149 | plt.savefig(graph_folder + '/dse-resources.png', dpi=300) 150 | 151 | 152 | objectives_csv = sys.argv[1] 153 | 154 | if objectives_csv.endswith('.csv'): 155 | split_path = objectives_csv.split('/') 156 | dse_folder = '.' 157 | for i in range(len(split_path) - 1): 158 | dse_folder += '/' + split_path[i] 159 | dse_folder += '/dse_graphs' 160 | 161 | if not os.path.exists(dse_folder): 162 | os.mkdir(dse_folder) 163 | 164 | create_graph(objectives_csv, dse_folder) 165 | else: 166 | print('Objectives CSV File Required. Given: ' + objectives_csv) 167 | 168 | -------------------------------------------------------------------------------- /scripts/dse_graphs/dse_resource_bar.py: -------------------------------------------------------------------------------- 1 | import matplotlib as mpl 2 | import matplotlib.pyplot as plt 3 | import matplotlib.ticker as ticker 4 | import numpy as np 5 | import pandas as pd 6 | import os 7 | import glob 8 | import sys 9 | from os.path import dirname 10 | from multiprocessing import Process 11 | 12 | from math import atan2, degrees 13 | import numpy as np 14 | import json 15 | font = {'family' : 'normal', 16 | 'weight' : 'normal', 17 | 'size' : 12} 18 | 19 | def get_resources(token, dtype=int): 20 | "total lut: 0.16748, logic lut: 0.164201, srl: 0, ram lut: 0.240064, ff: 0.00965791, ramb36: 0, ramb18: 0, uram: 0, dsp: 0.0585052" 21 | resources = token.str.split(',', expand=True) 22 | resources[0] = resources[0].str.extract('(\d+)', expand=False).astype(dtype) 23 | resources[1] = resources[1].str.extract('(\d+)', expand=False).astype(dtype) 24 | resources[2] = resources[2].str.extract('(\d+)', expand=False).astype(dtype) 25 | resources[3] = resources[3].str.extract('(\d+)', expand=False).astype(dtype) 26 | resources[4] = resources[4].str.extract('(\d+)', expand=False).astype(dtype) 27 | resources[5] = resources[5].str.rsplit(':').str[-1] 28 | resources[5] = resources[5].str.extract('(\d+)', expand=False).astype(dtype) 29 | resources[6] = resources[6].str.rsplit(':').str[-1] 30 | resources[6] = resources[6].str.extract('(\d+)', expand=False).astype(dtype) 31 | resources[7] = resources[7].str.extract('(\d+)', expand=False).astype(dtype) 32 | resources[8] = resources[8].str.extract('(\d+)', expand=False).astype(dtype) 33 | resources = resources.set_axis(['total lut', 'logic lut', 'srl', 'ram lut', 'ff', 'ramb36', 'ramb18', 'uram', 'dsp'], axis=1, copy=False) 34 | return resources 35 | 36 | def get_resources_double(token, dtype=np.double): 37 | '''total lut: 65766.6, logic lut: 53923, ram lut: 12235.6, ff: 48493.8''' 38 | resources = token.str.split(',', expand=True) 39 | resources[0] = resources[0].str.extract('(\d+.\d+)', expand=False).astype(dtype) 40 | resources[1] = resources[1].str.extract('(\d+.\d+)', expand=False).astype(dtype) 41 | resources[2] = resources[2].str.extract('(\d+.\d+)', expand=False).astype(dtype) 42 | resources[3] = resources[3].str.extract('(\d+.\d+)', expand=False).astype(dtype) 43 | resources[4] = resources[4].str.extract('(\d+.\d+)', expand=False).astype(dtype) 44 | resources[5] = resources[5].str.extract('(\d+.\d+)', expand=False).astype(dtype) 45 | resources[6] = resources[6].str.extract('(\d+.\d+)', expand=False).astype(dtype) 46 | resources[7] = resources[7].str.extract('(\d+.\d+)', expand=False).astype(dtype) 47 | resources[8] = resources[8].str.extract('(\d+.\d+)', expand=False).astype(dtype) 48 | resources = resources.set_axis(['total lut', 'logic lut', 'srl', 'ram lut', 'ff', 'ramb36', 'ramb18', 'uram', 'dsp'], axis=1, copy=False) 49 | return resources 50 | 51 | def get_dfg_performance(token): 52 | axis = [] 53 | performances = token.str.split(',', expand=True) 54 | for i in range(performances.shape[1]): 55 | performances[i] = performances[i].str.split(':').str[1] 56 | performances[i] = performances[i].str.extract('(\d+)', expand=False).astype(int) 57 | return performances 58 | 59 | def get_utilization(token): 60 | pass 61 | 62 | def get_workload(token): 63 | axis = [] 64 | workload = token.str.split(',', expand=True) 65 | for i in range(workload.shape[1]): 66 | axis.append(i) 67 | workload[i] = workload[i].str.extract('(\d+)', expand=False).astype(int) 68 | workload = workload.set_axis(axis) 69 | return workload 70 | 71 | def load_to_global(index, data, global_data): 72 | for i in range(len(data)): 73 | global_data[i][index] = np.asarray(data[i]) 74 | return global_data 75 | 76 | def load_line(filepath): 77 | if not os.path.exists(filepath): 78 | print("File doesnt exist: " + filepath) 79 | exit(1) 80 | '''Best OVport Resources,Best Scratchpad Resources,Best DMA Resources,Best Recurrance Resources,Best Generate Resources,Best Register Resources,Best Core Resources,Best System Bus Resources 81 | ''' 82 | line_data = pd.read_csv(filepath) 83 | line_data_resources = get_resources(line_data['Best Resources']) 84 | best_single_normalized_resources = get_resources_double(line_data['Best Single-Core Normalized Resources'], np.double) 85 | best_normalized_resources = get_resources_double(line_data['Best Normalized Resources'], np.double) 86 | fu_resources = get_resources(line_data['Best Functional Unit Resources']) 87 | switch_resources = get_resources(line_data['Best Switch Resources']) 88 | ivport_resources = get_resources(line_data['Best IVPort Resources']) 89 | ovport_resources = get_resources(line_data['Best OVPort Resources']) 90 | scratchpad_resources = get_resources(line_data['Best Scratchpad Resources']) 91 | dma_resources = get_resources(line_data['Best DMA Resources']) 92 | recurrance_resources = get_resources(line_data['Best Recurrance Resources']) 93 | generate_resources = get_resources(line_data['Best Generate Resources']) 94 | register_resources = get_resources(line_data['Best Register Resources']) 95 | core_resources = get_resources(line_data['Best Core Resources']) 96 | system_bus_resources = get_resources(line_data['Best System Bus Resources']) 97 | 98 | output_data = [0] * 133 99 | ''' 100 | def load_resources(line_data_resources, startpos): 101 | output_data[startpos] = line_data_resources['total lut'].to_numpy() 102 | output_data[startpos + 1] = line_data_resources['logic lut'].to_numpy() 103 | output_data[startpos + 2] = line_data_resources['ram lut'].to_numpy() 104 | output_data[startpos + 3] = line_data_resources['srl'].to_numpy() 105 | output_data[startpos + 4] = line_data_resources['ff'].to_numpy() 106 | output_data[startpos + 5] = line_data_resources['ramb36'].to_numpy() 107 | output_data[startpos + 6] = line_data_resources['ramb18'].to_numpy() 108 | output_data[startpos + 7] = line_data_resources['uram'].to_numpy() 109 | output_data[startpos + 8] = line_data_resources['dsp'].to_numpy() 110 | ''' 111 | output_data[0] = line_data['Time'].to_numpy() 112 | output_data[1] = line_data['Iteration'].to_numpy() 113 | output_data[2] = line_data['Best Objective Area'].to_numpy() 114 | output_data[3] = line_data['Best Objective Performance'].to_numpy() 115 | output_data[4] = line_data['Best Util Overall'].to_numpy() 116 | output_data[5] = line_data['Best Link Util'].to_numpy() 117 | output_data[6] = line_data['Best Node Util'].to_numpy() 118 | ''' 119 | load_resources(line_data_resources, 7) 120 | load_resources(best_single_normalized_resources, 16) 121 | load_resources(best_normalized_resources, 25) 122 | load_resources(fu_resources, 34) 123 | load_resources(switch_resources, 43) 124 | load_resources(ivport_resources, 52) 125 | load_resources(ovport_resources, 61) 126 | load_resources(scratchpad_resources, 70) 127 | load_resources(dma_resources, 79) 128 | load_resources(recurrance_resources, 88) 129 | load_resources(generate_resources, 97) 130 | load_resources(register_resources, 106) 131 | load_resources(core_resources, 115) 132 | load_resources(system_bus_resources, 124) 133 | ''' 134 | output_data[7] = line_data_resources['total lut'].to_numpy() 135 | output_data[8] = line_data_resources['logic lut'].to_numpy() 136 | output_data[9] = line_data_resources['ram lut'].to_numpy() 137 | output_data[10] = line_data_resources['srl'].to_numpy() 138 | output_data[11] = line_data_resources['ff'].to_numpy() 139 | output_data[12] = line_data_resources['ramb36'].to_numpy() 140 | output_data[13] = line_data_resources['ramb18'].to_numpy() 141 | output_data[14] = line_data_resources['uram'].to_numpy() 142 | output_data[15] = line_data_resources['dsp'].to_numpy() 143 | 144 | output_data[16] = best_single_normalized_resources['total lut'].to_numpy() 145 | output_data[17] = best_single_normalized_resources['logic lut'].to_numpy() 146 | output_data[18] = best_single_normalized_resources['ram lut'].to_numpy() 147 | output_data[19] = best_single_normalized_resources['srl'].to_numpy() 148 | output_data[20] = best_single_normalized_resources['ff'].to_numpy() 149 | output_data[21] = best_single_normalized_resources['ramb36'].to_numpy() 150 | output_data[22] = best_single_normalized_resources['ramb18'].to_numpy() 151 | output_data[23] = best_single_normalized_resources['uram'].to_numpy() 152 | output_data[24] = best_single_normalized_resources['dsp'].to_numpy() 153 | 154 | output_data[25] = best_normalized_resources['total lut'].to_numpy() 155 | output_data[26] = best_normalized_resources['logic lut'].to_numpy() 156 | output_data[27] = best_normalized_resources['ram lut'].to_numpy() 157 | output_data[28] = best_normalized_resources['srl'].to_numpy() 158 | output_data[29] = best_normalized_resources['ff'].to_numpy() 159 | output_data[30] = best_normalized_resources['ramb36'].to_numpy() 160 | output_data[31] = best_normalized_resources['ramb18'].to_numpy() 161 | output_data[32] = best_normalized_resources['uram'].to_numpy() 162 | output_data[33] = best_normalized_resources['dsp'].to_numpy() 163 | 164 | output_data[34] = fu_resources['total lut'].to_numpy() 165 | output_data[35] = fu_resources['logic lut'].to_numpy() 166 | output_data[36] = fu_resources['ram lut'].to_numpy() 167 | output_data[37] = fu_resources['srl'].to_numpy() 168 | output_data[38] = fu_resources['ff'].to_numpy() 169 | output_data[39] = fu_resources['ramb36'].to_numpy() 170 | output_data[40] = fu_resources['ramb18'].to_numpy() 171 | output_data[41] = fu_resources['uram'].to_numpy() 172 | output_data[42] = fu_resources['dsp'].to_numpy() 173 | 174 | output_data[43] = switch_resources['total lut'].to_numpy() 175 | output_data[44] = switch_resources['logic lut'].to_numpy() 176 | output_data[45] = switch_resources['ram lut'].to_numpy() 177 | output_data[46] = switch_resources['srl'].to_numpy() 178 | output_data[47] = switch_resources['ff'].to_numpy() 179 | output_data[48] = switch_resources['ramb36'].to_numpy() 180 | output_data[49] = switch_resources['ramb18'].to_numpy() 181 | output_data[50] = switch_resources['uram'].to_numpy() 182 | output_data[51] = switch_resources['dsp'].to_numpy() 183 | 184 | output_data[52] = ivport_resources['total lut'].to_numpy() 185 | output_data[53] = ivport_resources['logic lut'].to_numpy() 186 | output_data[54] = ivport_resources['ram lut'].to_numpy() 187 | output_data[55] = ivport_resources['srl'].to_numpy() 188 | output_data[56] = ivport_resources['ff'].to_numpy() 189 | output_data[57] = ivport_resources['ramb36'].to_numpy() 190 | output_data[58] = ivport_resources['ramb18'].to_numpy() 191 | output_data[59] = ivport_resources['uram'].to_numpy() 192 | output_data[60] = ivport_resources['dsp'].to_numpy() 193 | 194 | output_data[61] = ovport_resources['total lut'].to_numpy() 195 | output_data[62] = ovport_resources['logic lut'].to_numpy() 196 | output_data[63] = ovport_resources['ram lut'].to_numpy() 197 | output_data[64] = ovport_resources['srl'].to_numpy() 198 | output_data[65] = ovport_resources['ff'].to_numpy() 199 | output_data[66] = ovport_resources['ramb36'].to_numpy() 200 | output_data[67] = ovport_resources['ramb18'].to_numpy() 201 | output_data[68] = ovport_resources['uram'].to_numpy() 202 | output_data[69] = ovport_resources['dsp'].to_numpy() 203 | 204 | output_data[70] = scratchpad_resources['total lut'].to_numpy() 205 | output_data[71] = scratchpad_resources['logic lut'].to_numpy() 206 | output_data[72] = scratchpad_resources['ram lut'].to_numpy() 207 | output_data[73] = scratchpad_resources['srl'].to_numpy() 208 | output_data[74] = scratchpad_resources['ff'].to_numpy() 209 | output_data[75] = scratchpad_resources['ramb36'].to_numpy() 210 | output_data[76] = scratchpad_resources['ramb18'].to_numpy() 211 | output_data[77] = scratchpad_resources['uram'].to_numpy() 212 | output_data[78] = scratchpad_resources['dsp'].to_numpy() 213 | 214 | output_data[79] = dma_resources['total lut'].to_numpy() 215 | output_data[80] = dma_resources['logic lut'].to_numpy() 216 | output_data[81] = dma_resources['ram lut'].to_numpy() 217 | output_data[82] = dma_resources['srl'].to_numpy() 218 | output_data[83] = dma_resources['ff'].to_numpy() 219 | output_data[84] = dma_resources['ramb36'].to_numpy() 220 | output_data[85] = dma_resources['ramb18'].to_numpy() 221 | output_data[86] = dma_resources['uram'].to_numpy() 222 | output_data[87] = dma_resources['dsp'].to_numpy() 223 | 224 | output_data[88] = recurrance_resources['total lut'].to_numpy() 225 | output_data[89] = recurrance_resources['logic lut'].to_numpy() 226 | output_data[90] = recurrance_resources['ram lut'].to_numpy() 227 | output_data[91] = recurrance_resources['srl'].to_numpy() 228 | output_data[92] = recurrance_resources['ff'].to_numpy() 229 | output_data[93] = recurrance_resources['ramb36'].to_numpy() 230 | output_data[94] = recurrance_resources['ramb18'].to_numpy() 231 | output_data[95] = recurrance_resources['uram'].to_numpy() 232 | output_data[96] = recurrance_resources['dsp'].to_numpy() 233 | 234 | output_data[97] = generate_resources['total lut'].to_numpy() 235 | output_data[98] = generate_resources['logic lut'].to_numpy() 236 | output_data[99] = generate_resources['ram lut'].to_numpy() 237 | output_data[100] = generate_resources['srl'].to_numpy() 238 | output_data[101] = generate_resources['ff'].to_numpy() 239 | output_data[102] = generate_resources['ramb36'].to_numpy() 240 | output_data[103] = generate_resources['ramb18'].to_numpy() 241 | output_data[104] = generate_resources['uram'].to_numpy() 242 | output_data[105] = generate_resources['dsp'].to_numpy() 243 | 244 | output_data[106] = register_resources['total lut'].to_numpy() 245 | output_data[107] = register_resources['logic lut'].to_numpy() 246 | output_data[108] = register_resources['ram lut'].to_numpy() 247 | output_data[109] = register_resources['srl'].to_numpy() 248 | output_data[110] = register_resources['ff'].to_numpy() 249 | output_data[111] = register_resources['ramb36'].to_numpy() 250 | output_data[112] = register_resources['ramb18'].to_numpy() 251 | output_data[113] = register_resources['uram'].to_numpy() 252 | output_data[114] = register_resources['dsp'].to_numpy() 253 | 254 | output_data[115] = core_resources['total lut'].to_numpy() 255 | output_data[116] = core_resources['logic lut'].to_numpy() 256 | output_data[117] = core_resources['ram lut'].to_numpy() 257 | output_data[118] = core_resources['srl'].to_numpy() 258 | output_data[119] = core_resources['ff'].to_numpy() 259 | output_data[120] = core_resources['ramb36'].to_numpy() 260 | output_data[121] = core_resources['ramb18'].to_numpy() 261 | output_data[122] = core_resources['uram'].to_numpy() 262 | output_data[123] = core_resources['dsp'].to_numpy() 263 | 264 | output_data[124] = system_bus_resources['total lut'].to_numpy() 265 | output_data[125] = system_bus_resources['logic lut'].to_numpy() 266 | output_data[126] = system_bus_resources['ram lut'].to_numpy() 267 | output_data[127] = system_bus_resources['srl'].to_numpy() 268 | output_data[128] = system_bus_resources['ff'].to_numpy() 269 | output_data[129] = system_bus_resources['ramb36'].to_numpy() 270 | output_data[130] = system_bus_resources['ramb18'].to_numpy() 271 | output_data[131] = system_bus_resources['uram'].to_numpy() 272 | output_data[132] = system_bus_resources['dsp'].to_numpy() 273 | 274 | return output_data 275 | 276 | 277 | def plot_lines(axis, line_label, colors, x_axis, y_axis): 278 | lines = [] 279 | for i in range(len(line_label)): 280 | lines.append(axis.plot(x_axis[i], y_axis[i], color=colors[i], label=line_label[i])) 281 | 282 | return lines 283 | 284 | def set_axis(axis, x_axis_name, y_axis_name): 285 | axis.set_xlabel(x_axis_name) 286 | axis.set_ylabel(y_axis_name) 287 | axis.axis(xmin=0, ymin=0) 288 | axis.grid(axis='y') 289 | 290 | 291 | 292 | # Create Pie chart with following figures 293 | def resource_pie(title, axis, fu, switch, ivport, ovport, scratchpad, dma, recurrance, generate, register, core, system_bus): 294 | if (fu == 0 and switch == 0 and ivport == 0 and ovport == 0 and scratchpad == 0 and dma == 0 and recurrance == 0 and generate == 0 and register == 0 and core == 0 and system_bus == 0): 295 | axis.axis('off') 296 | return 297 | 298 | labels = ['FU', 'Switch', 'IVPort', 'OVPort', 'Scratchpad', 'DMA', 'Recurrance', 'Generate', 'Register', 'Core', 'System Bus'] 299 | sizes = [fu, switch, ivport, ovport, scratchpad, dma, recurrance, generate, register, core, system_bus] 300 | colors = ['red', 'orange', 'yellow', 'green', 'dodgerblue', 'indigo', 'violet', 'teal', 'lime', 'pink', 'coral'] 301 | 302 | zero_index = [] 303 | for i in range(len(sizes)): 304 | if sizes[i] == 0: 305 | zero_index.append(i) 306 | 307 | for i in range(len(zero_index)): 308 | labels.pop(zero_index[i]) 309 | sizes.pop(zero_index[i]) 310 | colors.pop(zero_index[i]) 311 | for j in range(len(zero_index)): 312 | if zero_index[j] > zero_index[i]: 313 | zero_index[j] -= 1 314 | 315 | 316 | 317 | axis.pie(sizes, labels=labels, colors=colors, autopct='%1.1f%%', shadow=False, startangle=90) 318 | axis.axis('equal') # Equal aspect ratio ensures that pie is drawn as a circle. 319 | axis.set_title(title) 320 | 321 | def resource_area(filename, dse_folder): 322 | 323 | data = load_line(filename) 324 | 325 | times = data[0] 326 | iterations = data[1] 327 | best = data[2] 328 | performance = data[3] 329 | overall_util = data[4] 330 | link_util = data[5] 331 | node_util = data[6] 332 | 333 | normalized_total_lut = data[16] 334 | normalized_logic_lut = data[17] 335 | normalized_ram_lut = data[18] 336 | normalized_srl = data[19] 337 | normalized_ff = data[20] 338 | normalized_ramb36 = data[21] 339 | normalized_ramb18 = data[22] 340 | normalized_uram = data[23] 341 | normalized_dsp = data[24] 342 | 343 | scaled_normalized_total_lut = data[25] 344 | scaled_normalized_logic_lut = data[26] 345 | scaled_normalized_ram_lut = data[27] 346 | scaled_normalized_srl = data[28] 347 | scaled_normalized_ff = data[29] 348 | scaled_normalized_ramb36 = data[30] 349 | scaled_normalized_ramb18 = data[31] 350 | scaled_normalized_uram = data[32] 351 | scaled_normalized_dsp = data[33] 352 | 353 | fu_total_lut = data[34] 354 | fu_logic_lut = data[35] 355 | fu_ram_lut = data[36] 356 | fu_srl = data[37] 357 | fu_ff = data[38] 358 | fu_ramb36 = data[39] 359 | fu_ramb18 = data[40] 360 | fu_uram = data[41] 361 | fu_dsp = data[42] 362 | 363 | switch_total_lut = data[43] 364 | switch_logic_lut = data[44] 365 | switch_ram_lut = data[45] 366 | switch_srl = data[46] 367 | switch_ff = data[47] 368 | switch_ramb36 = data[48] 369 | switch_ramb18 = data[49] 370 | switch_uram = data[50] 371 | switch_dsp = data[51] 372 | 373 | ivport_total_lut = data[52] 374 | ivport_logic_lut = data[53] 375 | ivport_ram_lut = data[54] 376 | ivport_srl = data[55] 377 | ivport_ff = data[56] 378 | ivport_ramb36 = data[57] 379 | ivport_ramb18 = data[58] 380 | ivport_uram = data[59] 381 | ivport_dsp = data[60] 382 | 383 | ovport_total_lut = data[61] 384 | ovport_logic_lut = data[62] 385 | ovport_ram_lut = data[63] 386 | ovport_srl = data[64] 387 | ovport_ff = data[65] 388 | ovport_ramb36 = data[66] 389 | ovport_ramb18 = data[67] 390 | ovport_uram = data[68] 391 | ovport_dsp = data[69] 392 | 393 | scratch_total_lut = data[70] 394 | scratch_logic_lut = data[71] 395 | scratch_ram_lut = data[72] 396 | scratch_srl = data[73] 397 | scratch_ff = data[74] 398 | scratch_ramb36 = data[75] 399 | scratch_ramb18 = data[76] 400 | scratch_uram = data[77] 401 | scratch_dsp = data[78] 402 | 403 | dma_total_lut = data[79] 404 | dma_logic_lut = data[80] 405 | dma_ram_lut = data[81] 406 | dma_srl = data[82] 407 | dma_ff = data[83] 408 | dma_ramb36 = data[84] 409 | dma_ramb18 = data[85] 410 | dma_uram = data[86] 411 | dma_dsp = data[87] 412 | 413 | recurrance_total_lut = data[88] 414 | recurrance_logic_lut = data[89] 415 | recurrance_ram_lut = data[90] 416 | recurrance_srl = data[91] 417 | recurrance_ff = data[92] 418 | recurrance_ramb36 = data[93] 419 | recurrance_ramb18 = data[94] 420 | recurrance_uram = data[95] 421 | recurrance_dsp = data[96] 422 | 423 | generate_total_lut = data[97] 424 | generate_logic_lut = data[98] 425 | generate_ram_lut = data[99] 426 | generate_srl = data[100] 427 | generate_ff = data[101] 428 | generate_ramb36 = data[102] 429 | generate_ramb18 = data[103] 430 | generate_uram = data[104] 431 | generate_dsp = data[105] 432 | 433 | register_total_lut = data[106] 434 | register_logic_lut = data[107] 435 | register_ram_lut = data[108] 436 | register_srl = data[109] 437 | register_ff = data[110] 438 | register_ramb36 = data[111] 439 | register_ramb18 = data[112] 440 | register_uram = data[113] 441 | register_dsp = data[114] 442 | 443 | core_total_lut = data[115] 444 | core_logic_lut = data[116] 445 | core_ram_lut = data[117] 446 | core_srl = data[118] 447 | core_ff = data[119] 448 | core_ramb36 = data[120] 449 | core_ramb18 = data[121] 450 | core_uram = data[122] 451 | core_dsp = data[123] 452 | 453 | system_bus_total_lut = data[124] 454 | system_bus_logic_lut = data[125] 455 | system_bus_ram_lut = data[126] 456 | system_bus_srl = data[127] 457 | system_bus_ff = data[128] 458 | system_bus_ramb36 = data[129] 459 | system_bus_ramb18 = data[130] 460 | system_bus_uram = data[131] 461 | system_bus_dsp = data[132] 462 | 463 | 464 | fig, ((ax1, ax2, ax3), (ax4, ax5, ax6), (ax7, ax8, ax9)) = plt.subplots(3, 3) 465 | fig.set_size_inches(16, 12) 466 | 467 | resource_pie('Total Lut', ax1, fu_total_lut[-1], switch_total_lut[-1], ivport_total_lut[-1], ovport_total_lut[-1], scratch_total_lut[-1], dma_total_lut[-1], recurrance_total_lut[-1], generate_total_lut[-1], register_total_lut[-1], core_total_lut[-1], system_bus_total_lut[-1]) 468 | 469 | resource_pie('Logic Lut', ax2, fu_logic_lut[-1], switch_logic_lut[-1], ivport_logic_lut[-1], ovport_logic_lut[-1], scratch_logic_lut[-1], dma_logic_lut[-1], recurrance_logic_lut[-1], generate_logic_lut[-1], register_logic_lut[-1], core_logic_lut[-1], system_bus_logic_lut[-1]) 470 | 471 | resource_pie('Ram Lut', ax3, fu_ram_lut[-1], switch_ram_lut[-1], ivport_ram_lut[-1], ovport_ram_lut[-1], scratch_ram_lut[-1], dma_ram_lut[-1], recurrance_ram_lut[-1], generate_ram_lut[-1], register_ram_lut[-1], core_ram_lut[-1], system_bus_ram_lut[-1]) 472 | 473 | resource_pie('SRL', ax4, fu_srl[-1], switch_srl[-1], ivport_srl[-1], ovport_srl[-1], scratch_srl[-1], dma_srl[-1], recurrance_srl[-1], generate_srl[-1], register_srl[-1], core_srl[-1], system_bus_srl[-1]) 474 | 475 | resource_pie('FF', ax5, fu_ff[-1], switch_ff[-1], ivport_ff[-1], ovport_ff[-1], scratch_ff[-1], dma_ff[-1], recurrance_ff[-1], generate_ff[-1], register_ff[-1], core_ff[-1], system_bus_ff[-1]) 476 | 477 | resource_pie('Ramb36', ax6, fu_ramb36[-1], switch_ramb36[-1], ivport_ramb36[-1], ovport_ramb36[-1], scratch_ramb36[-1], dma_ramb36[-1], recurrance_ramb36[-1], generate_ramb36[-1], register_ramb36[-1], core_ramb36[-1], system_bus_ramb36[-1]) 478 | 479 | resource_pie('Ramb18', ax7, fu_ramb18[-1], switch_ramb18[-1], ivport_ramb18[-1], ovport_ramb18[-1], scratch_ramb18[-1], dma_ramb18[-1], recurrance_ramb18[-1], generate_ramb18[-1], register_ramb18[-1], core_ramb18[-1], system_bus_ramb18[-1]) 480 | 481 | resource_pie('Uram', ax8, fu_uram[-1], switch_uram[-1], ivport_uram[-1], ovport_uram[-1], scratch_uram[-1], dma_uram[-1], recurrance_uram[-1], generate_uram[-1], register_uram[-1], core_uram[-1], system_bus_uram[-1]) 482 | 483 | resource_pie('Dsp', ax9, fu_dsp[-1], switch_dsp[-1], ivport_dsp[-1], ovport_dsp[-1], scratch_dsp[-1], dma_dsp[-1], recurrance_dsp[-1], generate_dsp[-1], register_dsp[-1], core_dsp[-1], system_bus_dsp[-1]) 484 | 485 | plt.savefig(dse_folder + '/dse-final-resources.png', dpi=300) 486 | 487 | fig, ax1 = plt.subplots(1, 1) 488 | fig.set_size_inches(16, 12) 489 | 490 | ax1.plot(times, normalized_total_lut, color='blue', label='Total Lut') 491 | ax1.plot(times, normalized_logic_lut, color='green', label='Logic Lut') 492 | ax1.plot(times, normalized_ram_lut, color='red', label='Ram Lut') 493 | ax1.plot(times, normalized_srl, color='purple', label='SRL') 494 | ax1.plot(times, normalized_ff, color='orange', label='Flip Flop') 495 | ax1.plot(times, normalized_ramb36, color='purple', label='Ramb36') 496 | ax1.plot(times, normalized_ramb18, color='cyan', label='Ramb18') 497 | ax1.plot(times, normalized_uram, color='yellow', label='Uram') 498 | ax1.plot(times, normalized_dsp, color='lime', label='DSP') 499 | 500 | ax1.set_ylim(ymin=0) 501 | ax1.set_xlim(xmin=0) 502 | ax1.set_xlabel('Time (S)') 503 | ax1.set_ylabel('Normalized Resources') 504 | ax1.legend(loc='upper left') 505 | 506 | plt.savefig(dse_folder + '/dse-normalized-resources.png', dpi=300) 507 | plt.close() 508 | 509 | fig, ax1 = plt.subplots(1, 1) 510 | fig.set_size_inches(16, 12) 511 | 512 | 513 | ax1.plot(times, scaled_normalized_total_lut, color='blue', label='Total Lut') 514 | ax1.plot(times, scaled_normalized_logic_lut, color='green', label='Logic Lut') 515 | ax1.plot(times, scaled_normalized_ram_lut, color='red', label='Ram Lut') 516 | ax1.plot(times, scaled_normalized_srl, color='purple', label='SRL') 517 | ax1.plot(times, scaled_normalized_ff, color='orange', label='Flip Flop') 518 | ax1.plot(times, scaled_normalized_ramb36, color='purple', label='Ramb36') 519 | ax1.plot(times, scaled_normalized_ramb18, color='cyan', label='Ramb18') 520 | ax1.plot(times, scaled_normalized_uram, color='yellow', label='Uram') 521 | ax1.plot(times, scaled_normalized_dsp, color='lime', label='DSP') 522 | 523 | ax1.set_ylim(ymin=0) 524 | ax1.set_xlim(xmin=0) 525 | ax1.set_xlabel('Time (s)') 526 | ax1.set_ylabel('Scaled Normalized Resources') 527 | ax1.legend(loc='upper left') 528 | 529 | plt.savefig(dse_folder + '/dse-scaled-normalized-resources.png', dpi=300) 530 | plt.close() 531 | 532 | fig, ax1 = plt.subplots(1, 1) 533 | fig.set_size_inches(16, 12) 534 | ax1.plot(times, overall_util, color='blue', label='Overall Utilization') 535 | ax1.plot(times, link_util, color='green', label='Link Utilization') 536 | ax1.plot(times, node_util, color='red', label='Node Utilization') 537 | ax1.set_ylim(ymin=0) 538 | ax1.set_xlim(xmin=0) 539 | ax1.set_xlabel('Time (s)') 540 | ax1.set_ylabel('Percent Utilized') 541 | ax1.legend(loc='upper left') 542 | 543 | plt.savefig(dse_folder + '/dse-utilization.png', dpi=300) 544 | plt.close() 545 | 546 | print("Finished!") 547 | 548 | objectives_csv = sys.argv[1] 549 | 550 | if objectives_csv.endswith('.csv'): 551 | split_path = objectives_csv.split('/') 552 | dse_folder = '.' 553 | for i in range(len(split_path) - 1): 554 | dse_folder += '/' + split_path[i] 555 | dse_folder += '/dse_graphs' 556 | 557 | if not os.path.exists(dse_folder): 558 | os.mkdir(dse_folder) 559 | 560 | resource_area(objectives_csv, dse_folder) 561 | else: 562 | print('Objectives CSV File Required. Given: ' + objectives_csv) -------------------------------------------------------------------------------- /scripts/fccm2023-tutorial.sh: -------------------------------------------------------------------------------- 1 | ########################################### 2 | # Software Stack and Programming Interface 3 | ########################################### 4 | cd $SS/dsa-apps/sdk/compiled 5 | 6 | ## Run gem5 simulation 7 | make vecadd.ll 8 | make ss-vecadd.ll 9 | make ss-vecadd.s 10 | make ss-vecadd.out 11 | ./run.sh ss-vecadd.out 12 | 13 | ## DFG Visualization 14 | ss_sched vecadd_0_4.dfg -f 15 | neato -Tpng -overlap=false -Gepsilon=.0001 -o vecadd_0_1_1.png vecadd_0_1.gv 16 | 17 | ## ADG Visualization 18 | python3 $SS/scripts/adg_visualization.py generators/dsagen2/adg/Mesh7x5-Simple64-Full7I5O.json 19 | 20 | ## Scheduled DFG-ADG Visualization 21 | ss_sched vecadd_0_4.dfg ../../adg/Mesh7x5-Simple64-Full7I5O.json -f 22 | dot -Tpng -o vecadd_0_1.png vecadd_0_1.gv 23 | 24 | ## Demo a MV-Suite 25 | cd $SS/dsa-apps/demo 26 | ./run.sh ss-mv.out 27 | ./run.sh ss-crs.out 28 | 29 | ## Compile RISC-V Binary 30 | make ultraclean 31 | make ss-mv.riscv 32 | make ss-crs.riscv 33 | 34 | ## DFG Visualization 35 | ss_sched spmv_0_1_1.dfg -f 36 | neato -Tpng -Goverlap=false -Gepsilon=.0001 -o spmv_0_1_1.png spmv_0_1_1.gv 37 | 38 | ## Scheduled DFG-ADG Visualization 39 | ss_sched spmv_0_1_1.dfg ../adg/Mesh7x5-Simple64-Full7I5O.json -f 40 | python3 $SS/scripts/sched_visualization.py viz/sched-adg.json 41 | # dot -Tpng -o spmv_0_1_1.png spmv_0_1_1.gv (alternative method) 42 | 43 | ########################################### 44 | # Hardware Stack and RTL Simulation 45 | ########################################### 46 | cd $SS/chipyard 47 | 48 | ## Compile Verilator RTL Simulation File 49 | make -C sims/verilator CONFIG=MeshDSARocketConfig 50 | 51 | ## Run mv and crs 52 | cd $SS/chipyard 53 | make -C sims/verilator CONFIG=MeshDSARocketConfig BINARY=$SS/dsa-apps/demo/ss-mv.riscv run-binary-fast 54 | make -C sims/verilator CONFIG=MeshDSARocketConfig BINARY=$SS/dsa-apps/demo/ss-crs.riscv run-binary-fast 55 | 56 | ## Compile and Run all unit tests 57 | cd $SS/chipyard/generators/dsagen2 58 | make ADG=$SS/chipyard/generators/dsagen2/adg/Mesh7x5-Simple64-Full7I5O.json compile-microbench 59 | cd $SS/chipyard 60 | make -C sims/verilator CONFIG=MeshDSARocketConfig ss-run 61 | 62 | ########################################### 63 | # Visualization and Design Space Exploration 64 | ########################################### 65 | cd $SS/dsa-apps/demo 66 | 67 | ## DSE Commands 68 | python3 extract.py 69 | ss_sched dfgs.list ../adg/Mesh7x5-Full64-Full7I5O.json -x -f -m 200 --dse-timeout=500 70 | 71 | ## Post-DSE Visualization 72 | python3 $SS/scripts/adg_visualization.py viz/prunned-schedadg.json 73 | python3 $SS/scripts/sched_visualization.py viz/prunned-schedadg.json 74 | python3 $SS/scripts/dse_graphs/adg_analysis_graph.py viz/prunned-schedadg.json 75 | python3 $SS/scripts/dse_graphs/dse_resource_bar.py viz/objectives.csv 76 | python3 $SS/scripts/dse_graphs/dse_graphs.py viz/objectives.csv 77 | -------------------------------------------------------------------------------- /scripts/fpga_resource_model/ml_model/component_create_data.py: -------------------------------------------------------------------------------- 1 | import os 2 | import torch 3 | from torch.utils.data.dataset import Dataset 4 | import numpy as np 5 | import pandas as pd 6 | from os.path import dirname 7 | 8 | class DataSet(Dataset): 9 | def __init__(self, data_list): 10 | self.data = data_list 11 | self.data_len = len(data_list) 12 | 13 | def __getitem__(self, index): 14 | single_data = self.data[index] 15 | return (torch.FloatTensor(single_data[0]), torch.FloatTensor(single_data[1])) 16 | 17 | def __len__(self): 18 | return self.data_len 19 | 20 | 21 | def create_network_data(): 22 | home_dir = dirname(os.path.realpath(__file__)) 23 | parameter_csv_file_path = home_dir + "/component.csv" 24 | data = pd.read_csv(parameter_csv_file_path) 25 | data.dropna() 26 | 27 | 28 | # Drop values we don't care about 29 | data.drop(['File_Name', 'Instance', 30 | 'Module', 'SRLs', 'RAMB36', 'RAMB18', 'URAM', 'DSPBlocks'], inplace=True, axis=1) 31 | columns_titles = ['Inputs', 'Outputs', 'Depth', 'Static', 'Granularity', 'Datawidth', 'TotalLUTs', 'LogicLUTs', 'LUTRAMs', 'FFs'] 32 | data=data.reindex(columns=columns_titles) 33 | 34 | # For each Datapoint, split into [input, output] 35 | # Convert to List 36 | data_list = data.values.tolist() 37 | 38 | print(data.columns, 'columns') 39 | print(len(data_list), 'data points') 40 | 41 | # For each Datapoint, split into [input, output] 42 | network_data = [] 43 | for data in data_list: 44 | network_data.append((data[:-4], data[-4:])) 45 | 46 | return DataSet(network_data) 47 | 48 | if __name__ == "__main__": 49 | create_network_data() -------------------------------------------------------------------------------- /scripts/fpga_resource_model/ml_model/component_model.py: -------------------------------------------------------------------------------- 1 | from functools import partial 2 | import torch 3 | import torch.nn as nn 4 | import torch.nn.functional as F 5 | import torch.optim as optim 6 | import torch.nn.functional as F 7 | from torch.utils.data import random_split 8 | from component_create_data import create_network_data 9 | from torchvision import transforms 10 | from torch.utils.data.dataset import Dataset 11 | from torch.utils.data.sampler import SubsetRandomSampler 12 | from ray import tune 13 | from ray.tune import CLIReporter 14 | from ray.tune.schedulers import ASHAScheduler 15 | from ray.tune.suggest.skopt import SkOptSearch 16 | import os 17 | import sys 18 | 19 | import numpy as np 20 | 21 | 22 | ''' 23 | Input: [inputs, outputs, depth, static] 24 | Output: [TotalLUTs, LogicLUTs, LUTRAMs, FFs] 25 | ''' 26 | class NeuralNetwork(nn.Module): 27 | def __init__(self, in_features, out_features, hidden_features=50): 28 | super(NeuralNetwork, self).__init__() 29 | self.fc1 = nn.Linear(in_features, hidden_features) 30 | self.fc2 = nn.Linear(hidden_features, hidden_features) 31 | self.fc3 = nn.Linear(hidden_features, out_features) 32 | 33 | def forward(self, x): 34 | x = F.relu(self.fc1(x)) 35 | x = F.relu(self.fc2(x)) 36 | x = F.relu(self.fc3(x)) 37 | return x 38 | 39 | ''' 40 | Calculates the RMSE for data. Used to test. 41 | ''' 42 | class RMSELoss(nn.Module): 43 | def __init__(self, eps=1e-6): 44 | super().__init__() 45 | self.mse = nn.MSELoss() 46 | self.eps = eps 47 | 48 | def forward(self,yhat,y): 49 | loss = torch.sqrt(self.mse(yhat,y) + self.eps) 50 | return loss 51 | 52 | 53 | ''' 54 | Tests the network. Returns the average loss per example 55 | ''' 56 | def TestNework(model, criterion, test_loader): 57 | examples = 0 58 | total_losses = torch.Tensor([0, 0, 0, 0]) 59 | for i, data in enumerate(test_loader, 0): 60 | with torch.no_grad(): 61 | inputs, labels = data 62 | outputs = model(inputs) 63 | 64 | for output, given_output in zip(labels, outputs): 65 | total_losses += criterion(output, given_output) 66 | return total_losses / len(test_loader) 67 | 68 | def AverageTrainingHardware(model, test_loader): 69 | examples = 0 70 | model_total = torch.Tensor([0, 0, 0, 0]) 71 | real_total = torch.Tensor([0, 0, 0, 0]) 72 | for i, data in enumerate(test_loader, 0): 73 | with torch.no_grad(): 74 | inputs, labels = data 75 | outputs = model(inputs) 76 | for output, given_output in zip(labels, outputs): 77 | real_total += output 78 | model_total += given_output 79 | examples += 1 80 | 81 | return model_total / examples, real_total / examples 82 | 83 | def load_data(): 84 | data = create_network_data() 85 | 86 | test_abs = int(len(data) * 0.2) 87 | train_abs = int((len(data) - test_abs) * 0.8) 88 | validation_abs = int((len(data) - (test_abs + train_abs))) 89 | 90 | test_set, train_set, validation_set = random_split( 91 | data, [test_abs, train_abs, validation_abs]) 92 | 93 | return test_set, train_set, validation_set 94 | 95 | def train_model(config, checkpoint_dir=None): 96 | model = NeuralNetwork(6, 4) 97 | 98 | criterion = nn.MSELoss() 99 | optimizer = optim.Adam(model.parameters(), lr=config["lr"]) 100 | 101 | if checkpoint_dir: 102 | model_state, optimizer_state = torch.load( 103 | os.path.join(checkpoint_dir, "checkpoint")) 104 | net.load_state_dict(model_state) 105 | optimizer.load_state_dict(optimizer_state) 106 | 107 | test_set, train_set, validation_set = load_data() 108 | 109 | trainloader = torch.utils.data.DataLoader( 110 | train_set, 111 | batch_size=int(config["batch_size"]), 112 | shuffle=True, 113 | num_workers=8) 114 | valloader = torch.utils.data.DataLoader( 115 | validation_set, 116 | batch_size=int(config["batch_size"]), 117 | shuffle=True, 118 | num_workers=8) 119 | 120 | for epoch in range(int(config["epochs"])): # loop over the dataset multiple times 121 | running_loss = 0.0 122 | epoch_steps = 0 123 | for i, data in enumerate(trainloader, 0): 124 | # get the inputs; data is a list of [inputs, labels] 125 | inputs, labels = data 126 | 127 | # zero the parameter gradients 128 | optimizer.zero_grad() 129 | 130 | # forward + backward + optimize 131 | outputs = model(inputs) 132 | loss = criterion(outputs, labels) 133 | loss.backward() 134 | optimizer.step() 135 | 136 | # print statistics 137 | running_loss += loss.item() 138 | epoch_steps += 1 139 | if i % 2000 == 1999: # print every 2000 mini-batches 140 | print("[%d, %5d] loss: %.3f" % (epoch + 1, i + 1, 141 | running_loss / epoch_steps)) 142 | running_loss = 0.0 143 | 144 | # Validation loss 145 | val_loss = 0.0 146 | val_steps = 0 147 | total = 0 148 | total_losses = torch.Tensor([0, 0, 0, 0]) 149 | r1_total_losses = torch.Tensor([0, 0, 0, 0]) 150 | 151 | for i, data in enumerate(valloader, 0): 152 | with torch.no_grad(): 153 | inputs, labels = data 154 | outputs = model(inputs) 155 | 156 | for output, given_output in zip(labels, outputs): 157 | total_losses += criterion(output, given_output) 158 | r1_total_losses += nn.L1Loss()(output, given_output) 159 | total += 1 160 | 161 | loss = criterion(outputs, labels) 162 | val_loss += loss.cpu().numpy() 163 | val_steps += 1 164 | 165 | total_losses = total_losses / total 166 | r1_total_losses = r1_total_losses / total 167 | 168 | with tune.checkpoint_dir(epoch) as checkpoint_dir: 169 | path = os.path.join(checkpoint_dir, "checkpoint") 170 | torch.save((model.state_dict(), optimizer.state_dict()), path) 171 | 172 | 173 | tune.report(loss=(val_loss / val_steps), l1_loss=r1_total_losses) 174 | print("Finished Training") 175 | 176 | def main(): 177 | config = { 178 | "lr": tune.loguniform(1e-4, 1e-1), 179 | "batch_size": tune.choice([2, 4, 8, 16]), 180 | "epochs": tune.choice(range(50, 200, 10)) 181 | } 182 | 183 | scheduler = ASHAScheduler( 184 | metric="loss", 185 | mode="min", 186 | max_t=200, 187 | grace_period=1, 188 | reduction_factor=2) 189 | 190 | skopt_search = SkOptSearch( 191 | metric="loss", 192 | mode="min") 193 | 194 | 195 | reporter = CLIReporter( 196 | # parameter_columns=["l1", "l2", "lr", "batch_size"], 197 | metric_columns=["loss", "l1_loss", "training_iteration"]) 198 | 199 | result = tune.run( 200 | partial(train_model), 201 | resources_per_trial={"cpu": 5}, 202 | config=config, 203 | num_samples=10, 204 | scheduler=scheduler, 205 | search_alg=skopt_search, 206 | progress_reporter=reporter) 207 | 208 | test_set, train_set, validation_set = load_data() 209 | 210 | test_loader = torch.utils.data.DataLoader( 211 | test_set, 212 | batch_size=1, 213 | shuffle=True, 214 | num_workers=8) 215 | 216 | best_trial = result.get_best_trial("loss", "min", "last") 217 | print("Best trial config: {}".format(best_trial.config)) 218 | print("Best trial final validation loss: {}".format( 219 | best_trial.last_result["loss"])) 220 | print("Best trial l1 loss: {}".format(best_trial.last_result["l1_loss"])) 221 | 222 | best_trained_model = NeuralNetwork(6, 4) 223 | 224 | best_checkpoint_dir = best_trial.checkpoint.value 225 | model_state, optimizer_state = torch.load(os.path.join( 226 | best_checkpoint_dir, "checkpoint")) 227 | best_trained_model.load_state_dict(model_state) 228 | 229 | 230 | l1Loss = TestNework(best_trained_model, nn.L1Loss(), test_loader).numpy() 231 | mseLoss = TestNework(best_trained_model, nn.MSELoss(), test_loader).numpy() 232 | rmseLoss = TestNework(best_trained_model, RMSELoss(), test_loader).numpy() 233 | 234 | averages = AverageTrainingHardware(best_trained_model, test_loader) 235 | 236 | model_average = averages[0].numpy() 237 | real_average = averages[1].numpy() 238 | 239 | print("Average Loss with L1 :", l1Loss) 240 | print("Average Loss with MSE loss:", mseLoss) 241 | print("Average Loss with RMS Loss:", rmseLoss) 242 | print("Average Model:", model_average) 243 | print("Real Model:", real_average) 244 | 245 | inputs, outputs = test_set[0] 246 | 247 | traced_script_module = torch.jit.trace(best_trained_model, inputs) 248 | traced_script_module.save("models/component_model.pt") 249 | 250 | lines = ['Component Model, trained on ' + str(len(train_set)) + ' samples', 251 | 'Trained using Pytorch, RayTune, and SciOptimize', 252 | '', 253 | 'Parameters', 254 | ' Learning Rate: ' + str(best_trial.config["lr"]), 255 | ' Batch Size: ' + str(best_trial.config["batch_size"]), 256 | ' Epochs: ' + str(best_trial.config["epochs"]), 257 | 'Best trial final validation loss: ' + str(best_trial.last_result["loss"]), 258 | 'Best trial l1 loss: ' + str(best_trial.last_result["l1_loss"]), 259 | '', 260 | 'Average Loss with L1 ' + str(l1Loss), 261 | 'Average Loss with MSE loss: ' + str(mseLoss), 262 | 'Average Loss with RMS Loss: ' + str(rmseLoss), 263 | '', 264 | 'Average Model: ' + str(model_average), 265 | 'Real: ' + str(real_average) 266 | ] 267 | with open('models/component_model_info.txt', 'w') as f: 268 | f.write('\n'.join(lines)) 269 | 270 | 271 | if __name__ == "__main__": 272 | main() -------------------------------------------------------------------------------- /scripts/fpga_resource_model/synthesis.py: -------------------------------------------------------------------------------- 1 | import subprocess 2 | import os 3 | from os.path import dirname 4 | import glob 5 | import datetime 6 | import time 7 | import random 8 | 9 | 10 | def synthesize(name, verilog_file, tcl_script, build_folder, output_folder): 11 | print("Running: " + name + "\n") 12 | return subprocess.Popen(["vivado -nolog -nojournal -mode batch -source " + tcl_script + 13 | " -tclargs --file " + verilog_file + " --name " + name + " --output " + output_folder + " --wkdir " + 14 | build_folder], shell=True) 15 | 16 | def check_finished(): 17 | global finished_num 18 | for process in processes: 19 | process.poll() 20 | if process.returncode == 0: # process end 21 | print("normally end") 22 | finished_num += 1 23 | processes.remove(process) 24 | process.terminate() 25 | process.kill() 26 | elif process.returncode != 0 and process.returncode is not None: 27 | print("abnormally end : kill process") 28 | finished_num += 1 29 | processes.remove(process) 30 | process.terminate() 31 | process.kill() 32 | 33 | print("Starting Parsing Synthesis") 34 | 35 | # Get Path to Home Directory 36 | data_folder = "/data" 37 | 38 | # Get Path to TCL Script 39 | tcl_script = "./vivado.tcl" 40 | output_folder = data_folder + "/input_vector_port/output" 41 | build_folder = data_folder + "/build" 42 | verilog_folder = data_folder + "/input_vector_port/data" 43 | 44 | if not os.path.isdir(output_folder): 45 | print("Output Folder Doesnt Exist!") 46 | exit(1) 47 | 48 | # Multi-thread parameter 49 | parallel = 64 50 | processes = [] 51 | finished_num = 0 52 | has_run_num = 0 53 | 54 | start_time = time.time() 55 | 56 | verilog_files = glob.glob(verilog_folder + "/**/*.v", recursive=True) 57 | 58 | print(len(verilog_files)) 59 | 60 | # Randomly Make it for files to be used in case of early termination 61 | random.shuffle(verilog_files) 62 | 63 | # Get all the Verilog Files in the Verilog Folder 64 | for file in verilog_files: 65 | # Get File Name 66 | name = dirname(os.path.realpath(file)).split("/")[-1] 67 | # Wait for open process 68 | while(len(processes) >= parallel): 69 | time.sleep(3) 70 | intermediate_time = time.time() 71 | check_finished() 72 | print("\n" * 75) 73 | print("######################################################") 74 | print("Processes Run:", has_run_num, "| Processes Finished:", finished_num, "| Processes Left:", len(verilog_files) - has_run_num) 75 | print("Elapsed Time:", str(datetime.timedelta(seconds=round(intermediate_time - start_time))), "| Current Processes Running:", len(processes)) 76 | print("######################################################") 77 | print("\n" * 3) 78 | else: 79 | #There is an open process 80 | process = synthesize(name, file, tcl_script, build_folder, output_folder) 81 | has_run_num += 1 82 | processes.append(process) 83 | print("Finished File:", name) 84 | 85 | while(len(processes) > 0): 86 | time.sleep(3) 87 | intermediate_time = time.time() 88 | check_finished() 89 | print("\n" * 75) 90 | print("######################################################") 91 | print("Processes Run:", has_run_num, "| Processes Finished:", finished_num, "| Processes Left:", len(verilog_files) - has_run_num) 92 | print("Elapsed Time:", str(datetime.timedelta(seconds=round(intermediate_time - start_time))), "| Current Processes Running:", len(processes)) 93 | print("######################################################") 94 | print("\n" * 3) 95 | 96 | end_time = time.time() 97 | 98 | print("Finished! Total Time =", str(datetime.timedelta(seconds=round(end_time - start_time)))) -------------------------------------------------------------------------------- /scripts/fpga_resource_model/verilog_statistics.py: -------------------------------------------------------------------------------- 1 | 2 | import os 3 | import pandas as pd 4 | from os.path import dirname 5 | import csv 6 | 7 | def parse_reports(output_folder, csv_file_path): 8 | def parse_utilization(file_name): 9 | file_lines = [line.rstrip() for line in open(file_name, 'r')] 10 | 11 | data = [] 12 | file_line = file_lines[24] 13 | file_line = file_line.replace(' ', '') 14 | return file_line.split('|')[1:-1] 15 | 16 | report_directories = [ f.path for f in os.scandir(output_folder) if f.is_dir() ] 17 | 18 | 19 | with open(csv_file_path, 'w', newline='') as csvfile: 20 | csv_writer = csv.writer(csvfile, delimiter=',', 21 | quotechar='|', quoting=csv.QUOTE_MINIMAL) 22 | csv_writer.writerow(['File_Name', 'Inputs', 'Outputs', 'Depth', 'Stated', 'Discard', 'BusWidth', 'Padding', 'Module', 'TotalLUTs', 'LogicLUTs', 'LUTRAMs', 'SRLs', 'FFs', 'RAMB36', 'RAMB18', 'URAM', 'DSPBlocks']) 23 | 24 | print ("Total Reports:", len(report_directories)) 25 | for report in report_directories: 26 | report_name = report.split('/')[-1] 27 | report_params = report_name.split('_') 28 | 29 | for i in range(len(report_params)): 30 | report_params[i] = report_params[i][:-2] 31 | 32 | data = parse_utilization(report + "/utilization.txt") 33 | combined_params = [report_name] + report_params + data 34 | csv_writer.writerow(combined_params) 35 | 36 | def parse_given_parameters(parameter_file, output_csv): 37 | data = pd.read_csv(parameter_file) 38 | data.drop(['mapped.area-Number of ports', 'mapped.area-Number of cells', 39 | 'mapped.area-Number of combinational cells', 'mapped.area-Number of sequential cells', 40 | 'mapped.area-Number of macros/black boxes', 'mapped.area-Number of buf/inv', 41 | 'mapped.area-Number of references', 'mapped.area-Combinational area', 42 | 'mapped.area-Buf/Inv area', 'mapped.area-Noncombinational area', 43 | 'mapped.area-Macro/Black Box area', 'mapped.area-Net Interconnect area', 44 | 'mapped.area-Total cell area', 'mapped.area-Total area', 'mapped.power-Cell Internal Power', 45 | 'mapped.power-Net Switching Power', 'mapped.power-Total Dynamic Power', 'mapped.power-Total', 46 | 'mapped.power-Cell Leakage Power', 'mapped.power-io_pad', 'mapped.power-memory', 47 | 'mapped.power-black_box', 'mapped.power-clock_network', 'mapped.power-register', 48 | 'mapped.power-sequential', 'mapped.power-combinational', 'mapped.timing-data arrival time', 49 | 'mapped.timing-data required time', 'mapped.timing-slack', 'designName'], inplace=True, axis=1) 50 | data.to_csv(output_csv, index=False) 51 | 52 | home_dir = dirname(os.path.realpath(__file__)) 53 | data_folder = "/data" 54 | output_folder = data_folder + "/output_vector_port/output" 55 | util_csv_file_path = home_dir + "/output_vector_port.csv" 56 | 57 | parse_reports(output_folder, util_csv_file_path) 58 | 59 | print("parsed", util_csv_file_path) -------------------------------------------------------------------------------- /scripts/fpga_resource_model/vivado.tcl: -------------------------------------------------------------------------------- 1 | # File to Run 2 | set verilog_file "" 3 | set name "" 4 | set output_directory "" 5 | set working_directory "" 6 | 7 | if { $::argc > 0 } { 8 | for {set i 0} {$i < $::argc} {incr i} { 9 | set option [string trim [lindex $::argv $i]] 10 | switch -regexp -- $option { 11 | "--file" { incr i; set verilog_file [lindex $::argv $i] } 12 | "--name" { incr i; set name [lindex $::argv $i] } 13 | "--output" { incr i; set output_directory [lindex $::argv $i] } 14 | "--wkdir" {incr i; set working_directory [lindex $::argv $i]} 15 | "--help" { print_help } 16 | default { 17 | if { [regexp {^-} $option] } { 18 | puts "ERROR: Unknown option '$option' specified, please type '$script_file -tclargs --help' for usage info.\n" 19 | return 1 20 | } 21 | } 22 | } 23 | } 24 | } 25 | 26 | # Help information for this script 27 | proc print_help {} { 28 | variable script_file 29 | puts "\nDescription:" 30 | puts "Synthesize and Generate Report" 31 | puts "Syntax:" 32 | puts "$script_file -tclargs \[--file>\]" 33 | puts "$script_file -tclargs \[--help\]\n" 34 | puts "Usage:" 35 | puts "Name Description" 36 | puts "-------------------------------------------------------------------------\n" 37 | puts "\[--file \] Use the specified file.\n" 38 | puts "\[--name \] Use the specified project name.\n" 39 | puts "\[--output \] Copies reports to specified output folder.\n" 40 | puts "\[--wkdir \] Copies reports to specified output folder.\n" 41 | puts "\[--help\] Print help information for this script\n" 42 | puts "-------------------------------------------------------------------------\n" 43 | exit 0 44 | } 45 | 46 | # Create a directory and switch into it 47 | cd $working_directory 48 | file mkdir $name 49 | cd $name 50 | 51 | # Creates a work directory 52 | set wrkdir [file join [pwd] obj] 53 | 54 | # Set Part 55 | set part_fpga "xcvu9p-flga2104-2L-e" 56 | 57 | # Create In-Memory Project File 58 | create_project -part $part_fpga -force $name 59 | 60 | # Add Files 61 | set files [list \ 62 | "${verilog_file}" \ 63 | ] 64 | add_files -norecurse $files 65 | 66 | # Synthesize the design 67 | synth_design -top [lindex [find_top] 0] -flatten_hierarchy rebuilt -mode out_of_context 68 | 69 | set_property SEVERITY {Warning} [get_drc_checks NSTD-1] 70 | set_property SEVERITY {Warning} [get_drc_checks UCIO-1] 71 | 72 | # Create a report directory 73 | set rptdir [file join $wrkdir report] 74 | file mkdir $rptdir 75 | 76 | # Report utilization of the current device 77 | set rptutil [file join $rptdir utilization.txt] 78 | report_utilization -hierarchical -file $rptutil 79 | 80 | # Report the RAM resources utilized in the implemented design 81 | report_ram_utilization -file $rptutil -append -detail 82 | 83 | 84 | 85 | # change 86 | exec cp -f -r $rptdir "${output_directory}/${name}" 87 | exec rm -f -r $rptdir "${output_directory}/${name}/report" 88 | exec rm -r $wrkdir -------------------------------------------------------------------------------- /scripts/init-submodules.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Get the current path 4 | PWD=$(pwd) 5 | REPO_DIR="$(dirname "$(readlink -f "${BASH_SOURCE[0]:-${(%):-%x}}")")"/../ 6 | cd $REPO_DIR 7 | 8 | # Initialize non-chipyard repo 9 | git submodule update --init --recursive docs dsa-riscv-ext dsa-gem5 dsa-llvm-project dsa-scheduler dsa-apps 10 | 11 | # Initialize ChipYard repo 12 | git submodule update --init chipyard 13 | 14 | # Initialize ChipYard submodule 15 | cd chipyard 16 | git config --global url."https://".insteadOf git:// 17 | ./scripts/init-submodules-no-riscv-tools.sh --skip-validate 18 | 19 | # Initialize ChipYard FPGA repo 20 | #./scripts/init-fpga.sh 21 | 22 | # Initialize FireMarshal 23 | #cd software/firemarshal 24 | #./init-submodules.sh 25 | #cd ../.. 26 | 27 | # Initialize risc-v gnu for DSA ISA extension patch 28 | cd ./toolchains/riscv-tools 29 | git submodule update --init riscv-gnu-toolchain 30 | cd ./riscv-gnu-toolchain 31 | git submodule update --init riscv-binutils 32 | 33 | # Return to current path 34 | cd $PWD 35 | -------------------------------------------------------------------------------- /scripts/micro2022-tutorial.sh: -------------------------------------------------------------------------------- 1 | ########################################### 2 | # Software Stack and Programming Interface 3 | ########################################### 4 | cd $SS/dsa-apps/sdk/compiled 5 | 6 | ## Run gem5 simulation 7 | make vecadd.ll 8 | make ss-vecadd.ll 9 | make ss-vecadd.s 10 | make ss-vecadd.out 11 | ./run.sh ss-vecadd.out 12 | 13 | ## DFG Visualization 14 | ss_sched vecadd_0_1.dfg -f 15 | neato -Tpng -overlap=false -Gepsilon=.0001 -o vecadd_0_1_1.png vecadd_0_1.gv 16 | 17 | ## ADG Visualization 18 | python3 $SS/scripts/adg_visualization.py generators/dsagen2/adg/Mesh7x5-Simple64-Full7I5O.json 19 | 20 | ## Scheduled DFG-ADG Visualization 21 | ss_sched vecadd_0_1.dfg ../adg/Mesh7x5-Simple64-Full7I5O.json -f 22 | dot -Tpng -o vecadd_0_1.png vecadd_0_1.gv 23 | 24 | ## Demo a MV-Suite 25 | cd $SS/dsa-apps/demo 26 | ./run.sh ss-mv.out 27 | ./run.sh ss-crs.out 28 | 29 | ## Compile RISC-V Binary 30 | make ultraclean 31 | make ss-mv.riscv 32 | make ss-crs.riscv 33 | 34 | ## DFG Visualization 35 | ss_sched spmv_0_1_1.dfg -f 36 | neato -Tpng -Goverlap=false -Gepsilon=.0001 -o spmv_0_1_1.png spmv_0_1_1.gv 37 | 38 | ## Scheduled DFG-ADG Visualization 39 | ss_sched spmv_0_1_1.dfg ../adg/Mesh7x5-Simple64-Full7I5O.json -f 40 | python3 $SS/scripts/sched_visualization.py viz/sched-adg.json 41 | # dot -Tpng -o spmv_0_1_1.png spmv_0_1_1.gv (alternative method) 42 | 43 | ########################################### 44 | # Hardware Stack and RTL Simulation 45 | ########################################### 46 | cd $SS/chipyard 47 | 48 | ## Compile Verilator RTL Simulation File 49 | make -C sims/verilator CONFIG=MeshDSARocketConfig 50 | 51 | ## Compile all unit tests 52 | cd $SS/chipyard/generators/dsagen2 53 | make ADG=$SS/chipyard/generators/dsagen2/adg/Mesh7x5-Simple64-Full7I5O.json compile-microbench 54 | 55 | ## Run all unit tests 56 | cd $SS/chipyard 57 | make -C sims/verilator CONFIG=MeshDSARocketConfig ss-run 58 | 59 | ## Run mv and crs 60 | make -C sims/verilator CONFIG=MeshDSARocketConfig BINARY=$SS/dsa-apps/demo/ss-mv.riscv run-binary-fast 61 | make -C sims/verilator CONFIG=MeshDSARocketConfig BINARY=$SS/dsa-apps/demo/ss-crs.riscv run-binary-fast 62 | 63 | ########################################### 64 | # Visualization and Design Space Exploration 65 | ########################################### 66 | cd $SS/dsa-apps/demo 67 | 68 | ## DSE Commands 69 | python3 extract.py 70 | ss_sched dfgs.list ../adg/Mesh7x5-Full64-Full7I5O.json -x -f -m 200 --dse-timeout=500 71 | 72 | ## Post-DSE Visualization 73 | python3 $SS/scripts/adg_visualization.py viz/prunned-schedadg.json 74 | python3 $SS/scripts/sched_visualization.py viz/prunned-schedadg.json 75 | python3 $SS/scripts/dse_graphs/adg_analysis_graph.py viz/prunned-schedadg.json 76 | python3 $SS/scripts/dse_graphs/dse_resource_bar.py viz/objectives.csv 77 | python3 $SS/scripts/dse_graphs/dse_graphs.py viz/objectives.csv 78 | -------------------------------------------------------------------------------- /scripts/pcc.py: -------------------------------------------------------------------------------- 1 | # Process compile_commands.json 2 | 3 | import os 4 | import sys 5 | import subprocess 6 | 7 | data = eval(''.join(open(sys.argv[1]).readlines())) 8 | 9 | first = True 10 | print('[') 11 | for entry in data: 12 | if not first: 13 | print(',') 14 | f = '%s/%s' % (entry['directory'], entry['file']) 15 | len_prefix = len(entry['directory']) 16 | if os.path.islink(f): 17 | raw = subprocess.check_output('ls -l %s' % f, shell=True).decode('utf-8') 18 | assert '->' in raw 19 | tokens = raw.split() 20 | idx = tokens.index('->') 21 | entry['file'] = tokens[idx + 1][len_prefix+1:] 22 | entry['arguments'][-1] = tokens[idx + 1][len_prefix+1:] 23 | print('{\n\t"directory": "%s",\n\t"file": "%s",' % (entry['directory'], tokens[idx + 1][len_prefix+1:])) 24 | else: 25 | print('{\n\t"directory": "%s",\n\t"file": "%s",' % (entry['directory'], entry['file'])) 26 | print('\t"arguments": [') 27 | res = [] 28 | for elem in entry['arguments']: 29 | if '"' in elem: 30 | elem = elem.replace('"', chr(92) + chr(34)) 31 | s = '\t\t"%s"' % elem 32 | res.append(s) 33 | print(',\n'.join(res)) 34 | print('\t]') 35 | print('}', end='') 36 | first = False 37 | print(']') 38 | -------------------------------------------------------------------------------- /scripts/sched_visualization.py: -------------------------------------------------------------------------------- 1 | import pandas as pd 2 | import os 3 | import sys 4 | import random 5 | import json 6 | 7 | from pyvis.network import Network 8 | 9 | 10 | def add_function_unit(network, node): 11 | name, number = node.split('.') 12 | operations = functional_units[node]['dsagen2.comp.config.CompKeys$DsaOperations$']['OperationDataTypeSet'] 13 | datawidth = functional_units[node]['dsagen2.comp.config.CompKeys$CompNode$']['compBits'] 14 | size = (int(datawidth) / 64) * 25 15 | 16 | title = name + ' ' + number + '\n' 17 | title += 'Operations: \n' 18 | for operation in operations: 19 | title += '\t' + operation + '\n' 20 | 21 | network.add_node(node, label='PE ' + number, title=title, size=size, shape='square', color='#f46049') 22 | 23 | 24 | def add_switch(network, node): 25 | name, number = node.split('.') 26 | datawidth = switches[node]['dsagen2.comp.config.CompKeys$CompNode$']['compBits'] 27 | size = (int(datawidth) / 64) * 25 28 | 29 | network.add_node(node, label='SW ' + number, size=size, color='#fbbe5b') 30 | 31 | def generate_adg_graph(filename, graph_name, graph_folder, schedule): 32 | # First Check if file exists 33 | if (not os.path.exists(filename)): 34 | return 35 | with open(filename) as f: 36 | data = json.load(f) 37 | 38 | # Get the Data from the file 39 | links = pd.DataFrame.from_dict(data['DSAGenEdges']) 40 | nodes = pd.DataFrame(data['DSAGenNodes']) 41 | 42 | vertices = pd.DataFrame(schedule['Vertices']) 43 | edges = pd.DataFrame(schedule['Edges']) 44 | 45 | # Parse the input file 46 | comp_nodes = pd.DataFrame() 47 | functional_units = pd.DataFrame() 48 | switches = pd.DataFrame() 49 | 50 | ivp_nodes = pd.DataFrame() 51 | ovp_nodes = pd.DataFrame() 52 | 53 | mem_nodes = pd.DataFrame() 54 | dma_nodes = pd.DataFrame() 55 | scratchpad_nodes = pd.DataFrame() 56 | rec_nodes = pd.DataFrame() 57 | gen_nodes = pd.DataFrame() 58 | reg_nodes = pd.DataFrame() 59 | 60 | for node in nodes: 61 | if not isinstance(nodes[node]['dsagen2.sync.config.SyncKeys$IVPNode$'], float): 62 | ivp_nodes[node] = nodes[node].dropna() 63 | if not isinstance(nodes[node]['dsagen2.sync.config.SyncKeys$OVPNode$'], float): 64 | ovp_nodes[node] = nodes[node].dropna() 65 | if not isinstance(nodes[node]['dsagen2.mem.config.MemKeys$MemNode$'], float): 66 | mem_nodes[node] = nodes[node].dropna() 67 | if nodes[node]['dsagen2.mem.config.MemKeys$MemNode$']['nodeType'] == 'ScratchpadMemory': 68 | scratchpad_nodes[node] = nodes[node].dropna() 69 | elif nodes[node]['dsagen2.mem.config.MemKeys$MemNode$']['nodeType'] == 'DirectMemoryAccess': 70 | dma_nodes[node] = nodes[node].dropna() 71 | elif nodes[node]['dsagen2.mem.config.MemKeys$MemNode$']['nodeType'] == 'RecurrenceEngine': 72 | rec_nodes[node] = nodes[node].dropna() 73 | elif nodes[node]['dsagen2.mem.config.MemKeys$MemNode$']['nodeType'] == 'GenerateEngine': 74 | gen_nodes[node] = nodes[node].dropna() 75 | else: 76 | reg_nodes[node] = nodes[node].dropna() 77 | if not isinstance(nodes[node]['dsagen2.comp.config.CompKeys$CompNode$'], float): 78 | if nodes[node]['dsagen2.comp.config.CompKeys$CompNode$']['nodeType'] == "ProcessingElement": 79 | functional_units[node] = nodes[node].dropna() 80 | else: 81 | switches[node] = nodes[node].dropna() 82 | comp_nodes[node] = nodes[node].dropna() 83 | 84 | net = Network(directed=True, width='100%') 85 | 86 | 87 | # Add Functional Units 88 | for node in functional_units: 89 | name, number = node.split('.') 90 | operations = functional_units[node]['dsagen2.comp.config.CompKeys$DsaOperations$']['OperationDataTypeSet'] 91 | datawidth = functional_units[node]['dsagen2.comp.config.CompKeys$CompNode$']['compBits'] 92 | size = (int(datawidth) / 64) * 25 93 | 94 | title = name + ' ' + number + '\n' 95 | title += 'Operations: \n' 96 | for operation in operations: 97 | title += '\t' + operation + '\n' 98 | 99 | net.add_node(node, label='PE ' + number, title=title, size=size, shape='square', color='#CCDBDC') 100 | 101 | # Add Switches 102 | for node in switches: 103 | name, number = node.split('.') 104 | datawidth = switches[node]['dsagen2.comp.config.CompKeys$CompNode$']['compBits'] 105 | size = (int(datawidth) / 64) * 25 106 | 107 | net.add_node(node, label='SW ' + number, shape='circle', size=size, color='#CCDBDC') 108 | 109 | 110 | # Add Input Vector Ports 111 | for node in ivp_nodes: 112 | name, number = node.split('.') 113 | net.add_node(node, label='IVP ' + number, shape='circle', color='#CCDBDC') 114 | 115 | # Add Output Vector Ports 116 | for node in ovp_nodes: 117 | name, number = node.split('.') 118 | net.add_node(node, label='OVP ' + number, shape='circle', color='#CCDBDC') 119 | 120 | # Add Direct Memory Access 121 | for node in dma_nodes: 122 | name, number = node.split('.') 123 | net.add_node(node, label='DMA ' + number, shape='circle', color='#CCDBDC') 124 | 125 | # Add Scratchpad 126 | for node in scratchpad_nodes: 127 | name, number = node.split('.') 128 | net.add_node(node, label='SPM ' + number, shape='circle', color='#CCDBDC') 129 | 130 | for node in rec_nodes: 131 | name, number = node.split('.') 132 | net.add_node(node, label='REC ' + number, shape='circle', color='#CCDBDC') 133 | 134 | for node in gen_nodes: 135 | name, number = node.split('.') 136 | net.add_node(node, label='GEN ' + number, shape='circle', color='#CCDBDC') 137 | 138 | for node in reg_nodes: 139 | name, number = node.split('.') 140 | net.add_node(node, label='REG ' + number, shape='circle', color='#CCDBDC') 141 | 142 | 143 | # Add Links 144 | for i in range(len(links)): 145 | link = links.iloc[i] 146 | physics = True 147 | lineStyle = {'type': 'dynamic'} 148 | if link['SourceNodeType'] != "ProcessingElement" and link['SourceNodeType'] != "Switch": 149 | physics = False 150 | lineStyle['type'] = 'discrete' 151 | if link['SinkNodeType'] != "ProcessingElement" and link['SinkNodeType'] != "Switch": 152 | physics = False 153 | lineStyle['type'] = 'discrete' 154 | 155 | 156 | sourceId = str(link['SourceNodeType']) + '.' + str(link['SourceNodeId']) 157 | sinkId = str(link['SinkNodeType']) + '.' + str(link['SinkNodeId']) 158 | net.add_edge(sourceId, sinkId, arrows='to', smooth=lineStyle, color={'color': '#CCDBDC', 'opacity': .1}, dashes=True, physics=physics, weight=1) 159 | 160 | # Add hidden links between similar data and vector port types 161 | for node in ivp_nodes: 162 | name, number = node.split('.') 163 | connectId = int(number) + 1 164 | if connectId < len(ivp_nodes.columns): 165 | net.add_edge(node, name + '.' + str(connectId), hidden=True) 166 | net.add_edge(name + '.' + str(connectId), node, hidden=True) 167 | 168 | 169 | for node in ovp_nodes: 170 | name, number = node.split('.') 171 | connectId = int(number) + 1 172 | if connectId < len(ovp_nodes.columns): 173 | net.add_edge(node, name + '.' + str(connectId), hidden=True) 174 | net.add_edge(name + '.' + str(connectId), node, hidden=True) 175 | 176 | for node in dma_nodes: 177 | name, number = node.split('.') 178 | connectId = int(number) + 1 179 | if connectId < len(dma_nodes.columns): 180 | net.add_edge(node, name + '.' + str(connectId), hidden=True) 181 | net.add_edge(name + '.' + str(connectId), node, hidden=True) 182 | 183 | for node in scratchpad_nodes: 184 | name, number = node.split('.') 185 | connectId = int(number) + 1 186 | if connectId < len(scratchpad_nodes.columns): 187 | net.add_edge(node, name + '.' + str(connectId), hidden=True) 188 | net.add_edge(name + '.' + str(connectId), node, hidden=True) 189 | 190 | for node in rec_nodes: 191 | name, number = node.split('.') 192 | connectId = int(number) + 1 193 | if connectId < len(rec_nodes.columns): 194 | net.add_edge(node, name + '.' + str(connectId), hidden=True) 195 | net.add_edge(name + '.' + str(connectId), node, hidden=True) 196 | 197 | for node in gen_nodes: 198 | name, number = node.split('.') 199 | connectId = int(number) + 1 200 | if connectId < len(gen_nodes.columns): 201 | net.add_edge(node, name + '.' + str(connectId), hidden=True) 202 | net.add_edge(name + '.' + str(connectId), node, hidden=True) 203 | 204 | for node in reg_nodes: 205 | name, number = node.split('.') 206 | connectId = int(number) + 1 207 | if connectId < len(reg_nodes.columns): 208 | net.add_edge(node, name + '.' + str(connectId), hidden=True) 209 | net.add_edge(name + '.' + str(connectId), node, hidden=True) 210 | 211 | # First change the colors for vertices 212 | vertexNodes = {} 213 | 214 | for _, row in vertices.iterrows(): 215 | node = row['MappedNodeId'] 216 | if node in vertexNodes: 217 | vertexNodes[node].append(row) 218 | else: 219 | vertexNodes[node] = [row] 220 | 221 | 222 | for node in net.nodes: 223 | if node['id'] in vertexNodes: 224 | vertex = vertexNodes[node['id']] 225 | instructions = '' 226 | vertexNames = '' 227 | for v in vertex: 228 | vertexNames += str(v['VertexId']) + '.' 229 | if v['VertexType'] == 'Instruction': 230 | instructions += str(v['Instruction Name']) + '\n' 231 | 232 | if instructions != '': 233 | node['shape'] = 'box' 234 | node['label'] = instructions[:-1] 235 | else: 236 | node['shape'] = 'circle' 237 | 238 | node['vertices'] = vertexNames[:-1] 239 | 240 | default_colors = ['#ff8c00', '#00FF00', '#cd853f', '#00FFFF', '#FF00FF', '#5f9ea0', '#FCF5C7', '#FFC09F', '#ADF7B6', '#D7FFF1', '#9BC1BC', '#DAF7DC', '#FAFF81', '#BDA0BC', '#C1F7DC'] 241 | num_used = 0 242 | 243 | valueColors = {} 244 | 245 | nodeColors = {} 246 | linkColors = {} 247 | 248 | for _, row in edges.iterrows(): 249 | edge_links = row['Links'] 250 | edge_id = int(row['EdgeId']) 251 | value = str(row['ValueNode']) + '.' + str(row['ValueIndex']) 252 | 253 | # Get the Color for this Value 254 | if value not in valueColors: 255 | if num_used >= len(default_colors): 256 | default_colors.append("#" + "%06x" % random.randint(0, 0xFFFFFF)) 257 | valueColors[value] = default_colors[num_used] 258 | num_used += 1 259 | color = valueColors[value] 260 | 261 | for i, link in enumerate(edge_links): 262 | sourceId = str(link['SourceNodeType']) + '.' + str(link['SourceNodeId']) 263 | sinkId = str(link['SinkNodeType']) + '.' + str(link['SinkNodeId']) 264 | linkId = sourceId + '->' + sinkId 265 | 266 | if sourceId not in nodeColors: 267 | nodeColors[sourceId] = [color] 268 | elif color not in nodeColors[sourceId]: 269 | nodeColors[sourceId].append(color) 270 | 271 | # Only add sink if its not last node 272 | if i != len(edge_links) - 1: 273 | if sinkId not in nodeColors: 274 | nodeColors[sinkId] = [color] 275 | elif color not in nodeColors[sinkId]: 276 | nodeColors[sinkId].append(color) 277 | 278 | if linkId not in linkColors: 279 | linkColors[linkId] = [color] 280 | elif color not in linkColors[linkId]: 281 | linkColors[linkId].append(color) 282 | 283 | for node in net.nodes: 284 | name = node['id'] 285 | if node['id'] in vertexNodes: 286 | if name in nodeColors: 287 | node['color'] = nodeColors[name][0] 288 | 289 | for link in net.edges: 290 | name = link['from'] + '->' + link['to'] 291 | if name in linkColors: 292 | link['color'] = linkColors[name][0] 293 | link['dashes'] = False 294 | 295 | net.show(graph_folder + '/' + graph_name + '.html') 296 | print('Finished: ' + graph_folder + '/' + graph_name) 297 | 298 | def adg_vis(filename, graph_name): 299 | # First Check if file exists 300 | if (not os.path.exists(filename)): 301 | return 302 | 303 | with open(filename) as f: 304 | data = json.load(f) 305 | 306 | if 'Workloads' not in data: 307 | return 308 | 309 | graph_folder = filename[:-5] 310 | 311 | if not os.path.exists(graph_folder): 312 | os.mkdir(graph_folder) 313 | 314 | for workload_id, workload in enumerate(data['Workloads']): 315 | for schedule_id, schedule in enumerate(workload['Schedules']): 316 | name = graph_name + '-' + str(workload_id) + '_' + str(schedule_id) 317 | generate_adg_graph(filename, name, graph_folder, schedule) 318 | 319 | print('Finished: ' + graph_name) 320 | 321 | 322 | adg_file = sys.argv[1] 323 | 324 | if adg_file.endswith('.json'): 325 | adg_vis(adg_file, adg_file.split('/')[-1][:-5]) 326 | else: 327 | print('ADG JSON File Required. Given: ' + adg_file) -------------------------------------------------------------------------------- /scripts/time_it.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import subprocess, sys 4 | 5 | if len(sys.argv) != 2: 6 | print('Usage: ./time.py command') 7 | print('Command should print "ticks: x" to stdout!') 8 | else: 9 | total = 0 10 | for i in range(10): 11 | total += int(subprocess.check_output(sys.argv[1:]).decode('utf-8').lstrip('ticks: ').rstrip()) 12 | print(total / 10.) 13 | -------------------------------------------------------------------------------- /scripts/view_dot.sh: -------------------------------------------------------------------------------- 1 | #/bin/bash 2 | neato -Goverlap=false -Gstart=self -Gepsilon=.0000001 -Tsvg -o $1.svg $1 3 | eog $1.svg 4 | -------------------------------------------------------------------------------- /scripts/view_sched.sh: -------------------------------------------------------------------------------- 1 | #/bin/bash 2 | fdp -T svg -O $1 3 | eog $1.svg 4 | -------------------------------------------------------------------------------- /setup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | DIR="$( cd "$( dirname "$0" )" >/dev/null 2>&1 && pwd )" 3 | export MAKEFLAGS="-j$(($(nproc) - 2))" 4 | 5 | # Make sure conda is installed 6 | if ! command -v conda &> /dev/null 7 | then 8 | 9 | echo "conda could not be found. Please install conda package or 3rd-party like anaconda first" 10 | 11 | else 12 | 13 | echo "set SS="$DIR 14 | export SS=$DIR 15 | 16 | echo "set SS_STACK="$DIR 17 | export SS_STACK=$SS 18 | 19 | echo "set SS_TOOLS="$DIR/ss-tools 20 | export SS_TOOLS=$SS_STACK/ss-tools 21 | 22 | FILE=$SS/chipyard/env.sh 23 | if test -f "$FILE"; then 24 | echo source chipyard environment script 25 | source $SS/chipyard/env.sh 26 | fi 27 | 28 | echo add "$"SS_TOOLS/bin to "$"PATH 29 | export PATH=$SS_TOOLS/bin:$PATH 30 | echo add "$"RISCV/bin to "$"PATH 31 | export PATH=$RISCV/bin:$PATH 32 | echo add gem5 to "$"PATH 33 | export PATH=$SS/dsa-gem5/build/RISCV:$PATH 34 | 35 | echo add "$"SS_TOOLS/lib to "$"LD_LIBRARY_PATH 36 | export PATH=$SS/scripts:$PATH 37 | export LD_LIBRARY_PATH=$SS_TOOLS/lib64:$SS_TOOLS/lib:$SS/dsa-scheduler/3rd-party/libtorch/lib/${LD_LIBRARY_PATH:+":${LD_LIBRARY_PATH}"} 38 | 39 | find_in_conda_env(){ 40 | conda env list | grep "${@}" >/dev/null 2>/dev/null 41 | } 42 | if ! find_in_conda_env ".*ss-stack.*" ; then 43 | echo "ss-stack environment not found" 44 | conda env create -f $SS/ss-stack-conda-env.yml 45 | fi 46 | conda activate ss-stack 47 | 48 | export LD_LIBRARY_PATH=${CONDA_PREFIX}/lib:${LD_LIBRARY_PATH} 49 | 50 | fi 51 | 52 | # Support macOS compilation 53 | case "$OSTYPE" in 54 | solaris*) echo "SOLARIS" ;; 55 | darwin*) 56 | # Change flex version 57 | export PATH="/opt/homebrew/opt/flex/bin:$PATH" 58 | export LDFLAGS="-L/opt/homebrew/opt/flex/lib" 59 | export CPPFLAGS="-I/opt/homebrew/opt/flex/include" 60 | # Change bison version 61 | export PATH="/opt/homebrew/opt/bison/bin:$PATH" 62 | export LDFLAGS="-L/opt/homebrew/opt/bison/lib" 63 | echo "Switch Flex and Bison to Homebrew version" ;; 64 | linux*) echo "OS Type is Linux, Not need to switch Bison and Flex" ;; 65 | bsd*) echo "BSD" ;; 66 | msys*) echo "WINDOWS" ;; 67 | cygwin*) echo "ALSO WINDOWS" ;; 68 | *) echo "unknown: $OSTYPE" ;; 69 | esac 70 | -------------------------------------------------------------------------------- /ss-stack-conda-env.yml: -------------------------------------------------------------------------------- 1 | name: ss-stack 2 | channels: 3 | - defaults 4 | dependencies: 5 | - cmake 6 | - python=3.8 7 | - pip: 8 | - pyyaml==6.0 9 | - scons==3.0.1 10 | - typing-extensions==4.2.0 11 | - six 12 | - pyvis 13 | - matplotlib 14 | - pandas 15 | --------------------------------------------------------------------------------