diff --git a/.travis/regression.sh b/.travis/regression.sh index da8431965..5d9be167d 100755 --- a/.travis/regression.sh +++ b/.travis/regression.sh @@ -11,12 +11,12 @@ compiled_file="compiled_$benchmark" tb_formal_postfix="_top_formal_verification_random_tb" verilog_output_dirname="${benchmark}_Verilog" log_file="${benchmark}_sim.log" +new_reg_sh="my_regression.sh" cd $fpga_flow_scripts -perl rewrite_path_in_file.pl -i $vpr_path/regression_verilog.sh -perl rewrite_path_in_file.pl -i $vpr_path/VerilogNetlists/ff.v +perl rewrite_path_in_file.pl -i $vpr_path/regression_verilog.sh -o $vpr_path/$new_reg_sh cd $my_pwd @@ -28,7 +28,7 @@ rm -f $log_file rm -f $compiled_file # Start the script -> run the fpga generation -> run the simulation -> check the log file -source regression_verilog.sh +source $new_reg_sh iverilog -o $compiled_file $verilog_output_dirname/SRC/$benchmark$include_netlists -s $benchmark$tb_formal_postfix vvp $compiled_file -j 16 >> $log_file diff --git a/CMakeLists.txt b/CMakeLists.txt index cff30ba79..f3d4730f9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -137,6 +137,7 @@ enable_testing() add_subdirectory(yosys) add_subdirectory(abc) add_subdirectory(ace2) +add_subdirectory(libs) add_subdirectory(vpr7_x2p) # run make to extract compiler options, linker options and list of source files @@ -175,8 +176,13 @@ set_target_properties(libace ace LIBRARY_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/ace2" RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/ace2") - # Set output locations to be in the main source tree under the relevant folder +set_target_properties(libvtrutil + PROPERTIES + ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/libs/libvtrutil" + LIBRARY_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/libs/libvtrutil" + RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/libs/libvtrutil") + set_target_properties(libarchfpga read_arch PROPERTIES ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/vpr7_x2p/libarchfpga" diff --git a/fpga_flow/arch/template/k6_N10_sram_chain_HC_template.xml b/fpga_flow/arch/template/k6_N10_sram_chain_HC_template.xml index b7630261f..a56d6b3cb 100644 --- a/fpga_flow/arch/template/k6_N10_sram_chain_HC_template.xml +++ b/fpga_flow/arch/template/k6_N10_sram_chain_HC_template.xml @@ -330,7 +330,7 @@ - + @@ -372,7 +372,7 @@ - + diff --git a/fpga_flow/benchmarks/Blif/Test_Modes/test_modes.power b/fpga_flow/benchmarks/Blif/Test_Modes/test_modes.power deleted file mode 100644 index b9730e324..000000000 --- a/fpga_flow/benchmarks/Blif/Test_Modes/test_modes.power +++ /dev/null @@ -1,172 +0,0 @@ ------------------------------------ Summary ------------------------------------ -Circuit: /research/ece/lnis/USERS/tang/github/OpenFPGA/fpga_flow/benchmarks/Blif/Test_Modes/test_modes -Architecture: k6_N10_sram_chain_HC_template.xml -Technology (nm): 45 -Voltage: 0.90 -Temperature: 85 -Critical Path: 5.8141e-09 -Size of FPGA: 2 x 2 -Channel Width: 200 - ------------------------------------ Warnings ----------------------------------- -No transistor counter function for BLIF model: .frac_lut6 -No transistor counter function for BLIF model: .subckt adder -No transistor counter function for BLIF model: .subckt shift -Attempted to search for a transistor with a capacitance smaller than the smallest in the technology file. - -No dynamic power defined for BLIF model: .subckt adder -No leakage power defined for BLIF model: .subckt adder -No dynamic power defined for BLIF model: .frac_lut6 -No leakage power defined for BLIF model: .frac_lut6 -No dynamic power defined for BLIF model: .subckt shift -No leakage power defined for BLIF model: .subckt shift - -------------------------------- Power Breakdown -------------------------------- -Component Power (W) %-Total %-Dynamic Method - -Total 0.0002701 1 0.7897 - Routing 0.0001289 0.4773 0.7668 - Switch Box 2.212e-05 0.08191 0 - Connection Box 0.0001068 0.3954 0.9256 - Global Wires 0 0 -nan - PB Types 8.066e-05 0.2986 0.6884 - Primitives 4.913e-05 0.1819 0.8837 - Interc Structures 8.866e-06 0.03283 0.5489 - Buffers and Wires 2.266e-05 0.08389 0.3197 - Other Estimation Methods 0 0 -nan - Clock 6.051e-05 0.224 0.9736 - ----------------------------- Power Breakdown by PB ----------------------------- -This sections provides a detailed breakdown of power usage by PB (physical -block). For each PB, the power is listed, which is the sum power of all -instances of the block. It also indicates its percentage of total power (entire -FPGA), as well as the percentage of its power that is dynamic (vs. static). It -also indicates the method used for power estimation. - -The data includes: - Modes: When a pb contains multiple modes, each mode is listed, with - its power statistics. - Bufs/Wires: Power of all local buffers and local wire switching - (transistor-level estimation only). - Interc: Power of local interconnect multiplexers (transistor- - level estimation only) - -Description of Estimation Methods: - Transistor Auto-Size: Transistor-level power estimation. Local buffers and - wire lengths are automatically sized. This is the default estimation - method. - Transistor Specify-Size: Transistor-level power estimation. Local buffers - and wire lengths are only inserted where specified by the user in the - architecture file. - Pin-Toggle: Dynamic power is calculated using enery-per-toggle of the PB - input pins. Static power is absolute. - C-Internal: Dynamic power is calculated using an internal equivalent - capacitance for PB type. Static power is absolute. - Absolute: Dynamic and static power are absolutes from the architecture file. - Sum of Children: Power of PB is only the sum of all child PBs; interconnect - between the PB and its children is ignored. - Ignore: Power of PB is ignored. - - -Component Power (W) %-Total %-Dynamic Method - -io 0 0 -nan Ignore -clb 8.066e-05 0.2986 0.6884 Transistor Auto-Size - Bufs/Wires 1.43e-05 0.05294 0.2804 - Interc: 8.462e-06 0.03133 0.542 - crossbar0 3.015e-06 0.01116 0.5188 - crossbar1 3.264e-06 0.01208 0.5568 - crossbar2 1.076e-06 0.003984 0.5484 - crossbar3 8.245e-07 0.003053 0.5364 - crossbar4 0 0 -nan - crossbar5 0 0 -nan - clks 0 0 -nan - carry_in 2.821e-07 0.001045 0.6112 - fle 5.79e-05 0.2144 0.8106 Transistor Auto-Size - Bufs/Wires 6.769e-06 0.02506 0.3446 - Mode:fle_phy 3.361e-05 0.1244 0.9139 - Interc: 0 0 -nan - direct_clk 0 0 -nan - mux1 0 0 -nan - mux2 0 0 -nan - frac_logic 3.388e-07 0.001254 0 Transistor Auto-Size - Bufs/Wires 3.388e-07 0.001254 0 - Interc: 0 0 -nan - mux1 0 0 -nan - mux2 0 0 -nan - frac_lut6 0 0 -nan Transistor Auto-Size - Bufs/Wires 0 0 -nan - adder_phy 0 0 -nan Transistor Auto-Size - Bufs/Wires 0 0 -nan - ff_phy 3.327e-05 0.1232 0.9232 Transistor Auto-Size - Bufs/Wires 0 0 -nan - Mode:n2_lut5 1.741e-05 0.06444 0.7925 - Interc: 0 0 -nan - lut5inter 1.741e-05 0.06444 0.7925 Transistor Auto-Size - Bufs/Wires 5.658e-07 0.002095 0.6977 - Interc: 0 0 -nan - complete1 0 0 -nan - ble5 1.684e-05 0.06235 0.7956 Transistor Auto-Size - Bufs/Wires 0 0 -nan - Mode:blut5 1.12e-05 0.04146 0.8091 - Interc: 0 0 -nan - flut5 1.12e-05 0.04146 0.8091 Transistor Auto-Size - Bufs/Wires 2.007e-07 0.000743 0.7628 - Interc: 2.646e-07 0.0009797 0.684 - mux1 2.646e-07 0.0009797 0.684 - lut5 1.655e-06 0.006127 0.2417 Transistor Auto-Size - Bufs/Wires 0 0 -nan - Mode:wire 0 0 -nan - Interc: 0 0 -nan - complete:lut5 0 0 -nan - Mode:lut5 1.655e-06 0.006127 0.2417 - Interc: 0 0 -nan - lut 1.655e-06 0.006127 0.2417 Transistor Auto-Size - Bufs/Wires 0 0 -nan - ff 9.079e-06 0.03361 0.9173 Transistor Auto-Size - Bufs/Wires 0 0 -nan - Mode:arithmetic 5.641e-06 0.02088 0.7689 - Interc: 0 0 -nan - arithmetic 5.641e-06 0.02088 0.7689 Transistor Auto-Size - Bufs/Wires 3.732e-07 0.001382 0.7081 - Interc: 1.399e-07 0.0005179 0.708 - sumout 1.399e-07 0.0005179 0.708 - lut4 7.913e-07 0.00293 0 Transistor Auto-Size - Bufs/Wires 0 0 -nan - Mode:wire 0 0 -nan - Interc: 0 0 -nan - complete:lut4 0 0 -nan - Mode:lut4 7.913e-07 0.00293 0 - Interc: 0 0 -nan - lut 7.913e-07 0.00293 0 Transistor Auto-Size - Bufs/Wires 0 0 -nan - adder 0 0 -nan Transistor Auto-Size - Bufs/Wires 0 0 -nan - ff 4.336e-06 0.01606 0.9163 Transistor Auto-Size - Bufs/Wires 0 0 -nan - Mode:n1_lut6 0 0 -nan - Interc: 0 0 -nan - ble6 0 0 -nan Transistor Auto-Size - Bufs/Wires 0 0 -nan - Interc: 0 0 -nan - mux1 0 0 -nan - lut6 0 0 -nan Transistor Auto-Size - Bufs/Wires 0 0 -nan - Mode:wire 0 0 -nan - Interc: 0 0 -nan - complete:lut6 0 0 -nan - Mode:lut6 0 0 -nan - Interc: 0 0 -nan - lut 0 0 -nan Transistor Auto-Size - Bufs/Wires 0 0 -nan - ff 0 0 -nan Transistor Auto-Size - Bufs/Wires 0 0 -nan - Mode:shift_register 1.134e-07 0.0004199 0.7826 - Interc: 0 0 -nan - ble_shift 1.134e-07 0.0004199 0.7826 Transistor Auto-Size - Bufs/Wires 1.134e-07 0.0004199 0.7826 - Interc: 0 0 -nan - direct3 0 0 -nan - ff 0 0 -nan Transistor Auto-Size - Bufs/Wires 0 0 -nan - diff --git a/fpga_flow/scripts/arch_rewrite.pl b/fpga_flow/scripts/arch_rewrite.pl deleted file mode 100644 index 0a3274f50..000000000 --- a/fpga_flow/scripts/arch_rewrite.pl +++ /dev/null @@ -1,122 +0,0 @@ -#!usr/bin/perl -w -use strict; -use Cwd; -#use Shell; -use FileHandle; -#Use the time -use Time::gmtime; - -my $arch_file; -my $new_arch_file; -my $overwrite = "TRUE"; -my $keyword = "OPENFPGAPATHKEYWORD"; -my $folder_top = "OpenFPGA"; - -sub print_usage() -{ - print "Usage:\n"; - print " perl [-options]\n"; - print " Options:(Mandatory!)\n"; - print " -i \n"; - print " Options:(Optional)\n"; - print " -o \n"; - print "\n"; - return; -} - -sub opts_read() -{ - if ($#ARGV == -1){ - print "Error: Not enough input argument!\n"; - &print_usage(); - exit(1); - } else { - for (my $iargv = 0; $iargv < $#ARGV+1; $iargv++){ - if ("-i" eq $ARGV[$iargv]){ - $arch_file = $ARGV[$iargv+1]; - $iargv++; - } elsif ("-o" eq $ARGV[$iargv]){ - $new_arch_file = $ARGV[$iargv+1]; - $overwrite = "FALSE"; - $iargv++; - } else { - die "WRONG ARGUMENT"; - } - } - } - return; -} - -sub rewriting_required_check($) -{ - my ($arch) = @_; - open(F, $arch); - my @lines=; - close F; - my $grep_result = grep ($keyword, @lines); - if($grep_result >= 1){ - print "Rewrite needed\n"; - return 1; - } else { - print "Rewrite NOT needed\n"; - return 0; - } -} - -sub save_original($) -{ - my ($template) = @_; - my $renamed_template = "$template".".bak"; - rename($template, $renamed_template); - - return $renamed_template; -} - -sub findPath(){ - my $path; - my $dir = cwd; - my @folders = split("/", $dir); - for(my $count = 0; $count < ($#folders -1); $count++){ - print "path fragment = $folders[$count]\n"; - if($folders[$count] eq ""){ - } else { - $path = "$path"."/"."$folders[$count]"; - if($folders[$count] eq $folder_top){ - print "$path\n"; - return $path; - } - } - } - die "ERROR: Script launched from the outside of the $folder_top folder!\n"; -} - -sub rewrite_file($ $) -{ - my ($arch, $template) = @_; - my $myPath = &findPath(); - open(IN, '<'.$template); - open(OUT, '>'.$arch); - while(){ - $_ =~ s/$keyword/$myPath/g; - print OUT $_; - } - return; -} - -sub main() -{ - &opts_read(); - my $rewrite_needed = &rewriting_required_check($arch_file); - if($rewrite_needed == 1){ - if($overwrite eq "true"){ - my $template_file = &save_original($arch_file); - &rewrite_file($arch_file, $template_file); - } else { - &rewrite_file($new_arch_file, $arch_file); - } - } - return; -} - -&main(); -exit(1); diff --git a/fpga_flow/scripts/rewrite_path_in_file.pl b/fpga_flow/scripts/rewrite_path_in_file.pl index fadedb606..b65868f60 100644 --- a/fpga_flow/scripts/rewrite_path_in_file.pl +++ b/fpga_flow/scripts/rewrite_path_in_file.pl @@ -6,8 +6,12 @@ use FileHandle; #Use the time use Time::gmtime; -my $my_file; +my $arch_file; +my $new_arch_file; +my $overwrite = "TRUE"; my $keyword = "OPENFPGAPATHKEYWORD"; +my $default_keyword = "TRUE"; +my $change_to; my $folder_top = "OpenFPGA"; sub print_usage() @@ -15,7 +19,10 @@ sub print_usage() print "Usage:\n"; print " perl [-options]\n"; print " Options:(Mandatory!)\n"; - print " -i \n"; + print " -i \n"; + print " Options:(Optional)\n"; + print " -o \n"; + print " -k \n"; print "\n"; return; } @@ -29,7 +36,17 @@ sub opts_read() } else { for (my $iargv = 0; $iargv < $#ARGV+1; $iargv++){ if ("-i" eq $ARGV[$iargv]){ - $my_file = $ARGV[$iargv+1]; + $arch_file = $ARGV[$iargv+1]; + $iargv++; + } elsif ("-o" eq $ARGV[$iargv]){ + $new_arch_file = $ARGV[$iargv+1]; + $overwrite = "FALSE"; + $iargv++; + } elsif ("-k" eq $ARGV[$iargv]){ + $keyword = $ARGV[$iargv+1]; + $change_to = $ARGV[$iargv+2]; + $default_keyword = "FALSE"; + $iargv++; $iargv++; } else { die "WRONG ARGUMENT"; @@ -41,8 +58,8 @@ sub opts_read() sub rewriting_required_check($) { - my ($file) = @_; - open(F, $file); + my ($arch) = @_; + open(F, $arch); my @lines=; close F; my $grep_result = grep ($keyword, @lines); @@ -68,7 +85,7 @@ sub findPath(){ my $path; my $dir = cwd; my @folders = split("/", $dir); - for(my $count = 0; $count < $#folders; $count++){ + for(my $count = 0; $count < ($#folders -1); $count++){ if($folders[$count] eq ""){ } else { $path = "$path"."/"."$folders[$count]"; @@ -81,15 +98,23 @@ sub findPath(){ die "ERROR: Script launched from the outside of the $folder_top folder!\n"; } -sub create_new($ $) +sub rewrite_file($ $) { - my ($file, $template) = @_; - my $myPath = &findPath(); + my ($arch, $template) = @_; open(IN, '<'.$template); - open(OUT, '>'.$file); - while(){ - $_ =~ s/$keyword/$myPath/g; - print OUT $_; + open(OUT, '>'.$arch); + + if($default_keyword eq "TRUE"){ + my $myPath = &findPath(); + while(){ + $_ =~ s/$keyword/$myPath/g; + print OUT $_; + } + } else { + while(){ + $_ =~ s/$keyword/$change_to/g; + print OUT $_; + } } return; } @@ -97,12 +122,17 @@ sub create_new($ $) sub main() { &opts_read(); - my $rewrite_needed = &rewriting_required_check($my_file); + my $rewrite_needed = &rewriting_required_check($arch_file); if($rewrite_needed == 1){ - my $template_file = &save_original($my_file); - &create_new($my_file, $template_file); + if($overwrite eq "TRUE"){ + my $template_file = &save_original($arch_file); + &rewrite_file($arch_file, $template_file); + } else { + &rewrite_file($new_arch_file, $arch_file); + } } return; } &main(); +exit(1); diff --git a/libs/CMakeLists.txt b/libs/CMakeLists.txt new file mode 100644 index 000000000..aed5d49c0 --- /dev/null +++ b/libs/CMakeLists.txt @@ -0,0 +1,10 @@ +#VTR developed libraries +#add_subdirectory(libarchfpga) +add_subdirectory(libvtrutil) +add_subdirectory(liblog) +#add_subdirectory(libpugiutil) +#add_subdirectory(libeasygl) +#add_subdirectory(librtlnumber) + +#Externally developed libraries +#add_subdirectory(EXTERNAL) diff --git a/libs/liblog/CMakeLists.txt b/libs/liblog/CMakeLists.txt new file mode 100644 index 000000000..b0d9ace35 --- /dev/null +++ b/libs/liblog/CMakeLists.txt @@ -0,0 +1,24 @@ +cmake_minimum_required(VERSION 2.8.12) + +project("liblog") + +file(GLOB_RECURSE EXEC_SOURCES src/main.cpp) +file(GLOB_RECURSE LIB_SOURCES src/*.cpp) +file(GLOB_RECURSE LIB_HEADERS src/*.h) +files_to_dirs(LIB_HEADERS LIB_INCLUDE_DIRS) + +#Remove test executable from library +list(REMOVE_ITEM LIB_SOURCES ${EXEC_SOURCES}) + +#Create the library +add_library(liblog STATIC + ${LIB_HEADERS} + ${LIB_SOURCES}) +target_include_directories(liblog PUBLIC ${LIB_INCLUDE_DIRS}) +set_target_properties(liblog PROPERTIES PREFIX "") #Avoid extra 'lib' prefix + +#Create the test executable +add_executable(test_log ${EXEC_SOURCES}) +target_link_libraries(test_log liblog) + +install(TARGETS test_log liblog DESTINATION bin) diff --git a/libs/liblog/LICENSE.txt b/libs/liblog/LICENSE.txt new file mode 100644 index 000000000..41116e903 --- /dev/null +++ b/libs/liblog/LICENSE.txt @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2014 Jason Luu + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/libs/liblog/Readme.txt b/libs/liblog/Readme.txt new file mode 100644 index 000000000..8f89aafdb --- /dev/null +++ b/libs/liblog/Readme.txt @@ -0,0 +1,13 @@ +Simple Logger Library + +Author: Jason Luu +Date: Sept 5, 2014 + +This library provides simple logging operations. + +- Output messages to both file and terminal +- Tag messages based on feedback type (eg. info, warning, or error) +- Track number of warnings and errors + +Specialized programming knowledge: +- Requires usage of variable arguments in standard C library to properly wrap printf and fprintf diff --git a/libs/liblog/src/log.cpp b/libs/liblog/src/log.cpp new file mode 100644 index 000000000..f301f0598 --- /dev/null +++ b/libs/liblog/src/log.cpp @@ -0,0 +1,121 @@ +/** + * Lightweight logging tool. Automatically prepend messages with prefixes and store in log file. + * + * Author: Jason Luu + * Date: Sept 5, 2014 + */ + + +#include +#include /* Allows for variable arguments, necessary for wrapping printf */ +#include "log.h" + +#define LOG_DEFAULT_FILE_NAME "output.log" + +static int log_warning = 0; +static int log_error = 0; +FILE *log_stream = nullptr; + + +static void check_init(); + +/* Set the output file of logger. + If different than current log file, close current log file and reopen to new log file +*/ +void log_set_output_file(const char *filename) { + if(log_stream != nullptr) { + fclose(log_stream); + } + + if (filename == nullptr) { + log_stream = nullptr; + } else { + + log_stream = fopen(filename, "w"); + if(log_stream == nullptr) { + printf("Error writing to file %s\n\n", filename); + } + } +} + +void log_print_direct(const char* message, ...) { + va_list args; + va_start(args, message); + vprintf(message, args); + va_end(args); +} + +void log_print_info(const char* message, ...) { + check_init(); /* Check if output log file setup, if not, then this function also sets it up */ + + va_list args; + va_start(args, message); + vprintf(message, args); + va_end(args); + + if (log_stream) { + va_start(args, message); /* Must reset variable arguments so that they can be read again */ + vfprintf(log_stream, message, args); + va_end(args); + + fflush(log_stream); + } +} + +void log_print_warning(const char* /*filename*/, unsigned int /*line_num*/, const char* message, ...) { + check_init(); /* Check if output log file setup, if not, then this function also sets it up */ + + va_list args; + va_start(args, message); + log_warning++; + + printf("Warning %d: ", log_warning); + vprintf(message, args); + va_end(args); + + if (log_stream) { + va_start(args, message); /* Must reset variable arguments so that they can be read again */ + fprintf(log_stream, "Warning %d: ", log_warning); + vfprintf(log_stream, message, args); + + va_end(args); + fflush(log_stream); + } +} + +void log_print_error(const char* /*filename*/, unsigned int /*line_num*/, const char* message, ...) { + check_init(); /* Check if output log file setup, if not, then this function also sets it up */ + + va_list args; + va_start(args, message); + log_error++; + + check_init(); + fprintf(stderr, "Error %d: ", log_error); + vfprintf(stderr, message, args); + va_end(args); + + if (log_stream) { + va_start(args, message); /* Must reset variable arguments so that they can be read again */ + fprintf(log_stream, "Error %d: ", log_error); + vfprintf(log_stream, message, args); + + va_end(args); + + fflush(log_stream); + } +} + +/** + * Check if output log file setup, if not, then this function also sets it up + */ +static void check_init() { + //We now allow a nullptr log_stream (i.e. no log file) so nothing to do here +} + + +void log_close() { + if (log_stream) { + fclose(log_stream); + } +} diff --git a/libs/liblog/src/log.h b/libs/liblog/src/log.h new file mode 100644 index 000000000..3d152fb21 --- /dev/null +++ b/libs/liblog/src/log.h @@ -0,0 +1,22 @@ +/** + * Lightweight logging tool. Automatically prepend messages with prefixes and store in log file. + * + * Init/Change name of log file using log_set_output_file, when done, call log_close + * + * Author: Jason Luu + * Date: Sept 5, 2014 + */ + +#ifndef LOG_H +#define LOG_H + +void log_set_output_file(const char *filename); + +void log_print_direct(const char* message, ...); +void log_print_info(const char* message, ...); +void log_print_warning(const char* filename, unsigned int line_num, const char* message, ...); +void log_print_error(const char* filename, unsigned int line_num, const char* message, ...); + +void log_close(); + +#endif diff --git a/libs/liblog/src/main.cpp b/libs/liblog/src/main.cpp new file mode 100644 index 000000000..ecb7d9ab6 --- /dev/null +++ b/libs/liblog/src/main.cpp @@ -0,0 +1,18 @@ +/** Jason Luu + * Test program for logger + */ + +#include "log.h" + +int main() { + int x = 10, y = 20; + float a = 1.5f, b = -2.01f; + log_print_info("Testing logger\n\n"); + log_print_info("Output separate strings: %s %s\n", "pass", "[PASS]"); + log_print_info("Output two integers: x = %d y = %d\n", x, y); + log_print_warning(__FILE__, __LINE__, "Test warning on floating point arguments %g %g\n", a, b); + log_print_error(__FILE__, __LINE__, "Test error on two variables %g %g \n\n", a - x, b + y); + + log_print_info("Test complete\n"); + return 0; +} \ No newline at end of file diff --git a/libs/libvtrutil/CMakeLists.txt b/libs/libvtrutil/CMakeLists.txt new file mode 100644 index 000000000..4b689150c --- /dev/null +++ b/libs/libvtrutil/CMakeLists.txt @@ -0,0 +1,91 @@ +cmake_minimum_required(VERSION 2.8.12) + +project("libvtrutil") + +#Version info +set(VTR_VERSION_FILE_IN ${CMAKE_CURRENT_SOURCE_DIR}/src/vtr_version.cpp.in) +set(VTR_VERSION_FILE_OUT ${CMAKE_CURRENT_BINARY_DIR}/vtr_version.cpp) + +#Compiler info +set(VTR_COMPILER_INFO "${CMAKE_CXX_COMPILER_ID} ${CMAKE_CXX_COMPILER_VERSION} on ${CMAKE_SYSTEM} ${CMAKE_SYSTEM_PROCESSOR}") + +#Set default version numbers in case not specified +if(NOT DEFINED VTR_VERSION_MAJOR) + set(VTR_VERSION_MAJOR 0) +endif() + +if(NOT DEFINED VTR_VERSION_MINOR) + set(VTR_VERSION_MINOR 0) +endif() + +if(NOT DEFINED VTR_VERSION_PATCH) + set(VTR_VERSION_PATCH 0) +endif() + +# We always update the vtr_version.cpp file every time the project is built, +# to ensure the git revision and dirty status are up to date. +# +# We need to do this in two stages: +# +# 1) We a custom target 'version' (which is always out of date) so it will always be run. +# It touches the unprocessed version input file so it too will always be out of date. +# +# 2) The custom command depends on the touched version input file and generates the processed +# version file, with updated values. The custom command uses the configure_version.cmake +# script to generate the up-to-date vtr_version.cpp +add_custom_target(version ALL + COMMAND ${CMAKE_COMMAND} -E touch ${VTR_VERSION_FILE_IN}) + +add_custom_command(OUTPUT ${VTR_VERSION_FILE_OUT} + COMMAND ${CMAKE_COMMAND} + -D IN_FILE=${VTR_VERSION_FILE_IN} + -D OUT_FILE=${VTR_VERSION_FILE_OUT} + -D VTR_VERSION_MAJOR=${VTR_VERSION_MAJOR} + -D VTR_VERSION_MINOR=${VTR_VERSION_MINOR} + -D VTR_VERSION_PATCH=${VTR_VERSION_PATCH} + -D VTR_VERSION_PRERELEASE=${VTR_VERSION_PRERELEASE} + -D VTR_COMPILER_INFO=${VTR_COMPILER_INFO} + -D VTR_BUILD_TYPE=${CMAKE_BUILD_TYPE} + -P ${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules/configure_version.cmake + MAIN_DEPENDENCY ${VTR_VERSION_FILE_IN} + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + VERBATIM) + + +# +# Source files and library +# +file(GLOB_RECURSE LIB_SOURCES src/*.cpp) +file(GLOB_RECURSE LIB_HEADERS src/*.hpp src/*.h) +files_to_dirs(LIB_HEADERS LIB_INCLUDE_DIRS) + +#Add the version file to the sources +list(APPEND LIB_SOURCES ${VTR_VERSION_FILE_OUT}) + +#Create the library +add_library(libvtrutil STATIC + ${LIB_HEADERS} + ${LIB_SOURCES}) +target_include_directories(libvtrutil PUBLIC ${LIB_INCLUDE_DIRS}) +set_target_properties(libvtrutil PROPERTIES PREFIX "") #Avoid extra 'lib' prefix + +#Ensure version is always up to date by requiring version to be run first +add_dependencies(libvtrutil version) + +#Specify link-time dependancies +target_link_libraries(libvtrutil + liblog) + +install(TARGETS libvtrutil DESTINATION bin) + +# +# Unit Tests +# +#file(GLOB_RECURSE TEST_SOURCES test/*.cpp) +#add_executable(test_vtrutil ${TEST_SOURCES}) +#target_link_libraries(test_vtrutil +# libvtrutil +# libcatch) + +#add_test(NAME test_vtrutil COMMAND test_vtrutil --use-colour=yes) + diff --git a/libs/libvtrutil/cmake/modules/configure_version.cmake b/libs/libvtrutil/cmake/modules/configure_version.cmake new file mode 100644 index 000000000..72519f2c3 --- /dev/null +++ b/libs/libvtrutil/cmake/modules/configure_version.cmake @@ -0,0 +1,40 @@ +# +# Versioning information +# +#Figure out the git revision +find_package(Git QUIET) +if(GIT_FOUND) + exec_program(${GIT_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR} + ARGS describe --always --long --dirty + OUTPUT_VARIABLE VTR_VCS_REVISION + RETURN_VALUE GIT_DESCRIBE_RETURN_VALUE) + + if(NOT GIT_DESCRIBE_RETURN_VALUE EQUAL 0) + #Git describe failed, usually this means we + #aren't in a git repo -- so don't set a VCS + #revision + set(VTR_VCS_REVISION "unkown") + endif() +else() + #Couldn't find git, so can't look-up VCS revision + set(VTR_VCS_REVISION "unkown") +endif() + + +#Set the version according to semver.org +set(VTR_VERSION "${VTR_VERSION_MAJOR}.${VTR_VERSION_MINOR}.${VTR_VERSION_PATCH}") +if(VTR_VERSION_PRERELEASE) + set(VTR_VERSION "${VTR_VERSION}-${VTR_VERSION_PRERELEASE}") +endif() +set(VTR_VERSION_SHORT ${VTR_VERSION}) +if(VTR_VCS_REVISION) + set(VTR_VERSION "${VTR_VERSION}+${VTR_VCS_REVISION}") +endif() + +#Other build meta-data +string(TIMESTAMP VTR_BUILD_TIMESTAMP) +set(VTR_BUILD_TIMESTAMP "${VTR_BUILD_TIMESTAMP} (${VTR_BUILD_TYPE} build)") + +message(STATUS "VTR Version: ${VTR_VERSION}") + +configure_file(${IN_FILE} ${OUT_FILE}) diff --git a/libs/libvtrutil/src/picosha2.h b/libs/libvtrutil/src/picosha2.h new file mode 100644 index 000000000..7ebe499c5 --- /dev/null +++ b/libs/libvtrutil/src/picosha2.h @@ -0,0 +1,366 @@ +/* +The MIT License (MIT) + +Copyright (C) 2014 okdshin + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ +#ifndef PICOSHA2_H +#define PICOSHA2_H +//picosha2:20140213 +#include +#include +#include +#include +#include +#include + +namespace picosha2 +{ +typedef unsigned long word_t; +typedef unsigned char byte_t; + +namespace detail +{ +inline byte_t mask_8bit(byte_t x){ + return x&0xff; +} + +inline word_t mask_32bit(word_t x){ + return x&0xffffffff; +} + +const word_t add_constant[64] = { + 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, + 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, + 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, + 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, + 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, + 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, + 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, + 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, + 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, + 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, + 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, + 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, + 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, + 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, + 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, + 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 +}; + +const word_t initial_message_digest[8] = { + 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, + 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19 +}; + +inline word_t ch(word_t x, word_t y, word_t z){ + return (x&y)^((~x)&z); +} + +inline word_t maj(word_t x, word_t y, word_t z){ + return (x&y)^(x&z)^(y&z); +} + +inline word_t rotr(word_t x, std::size_t n){ + assert(n < 32); + return mask_32bit((x>>n)|(x<<(32-n))); +} + +inline word_t bsig0(word_t x){ + return rotr(x, 2)^rotr(x, 13)^rotr(x, 22); +} + +inline word_t bsig1(word_t x){ + return rotr(x, 6)^rotr(x, 11)^rotr(x, 25); +} + +inline word_t shr(word_t x, std::size_t n){ + assert(n < 32); + return x >> n; +} + +inline word_t ssig0(word_t x){ + return rotr(x, 7)^rotr(x, 18)^shr(x, 3); +} + +inline word_t ssig1(word_t x){ + return rotr(x, 17)^rotr(x, 19)^shr(x, 10); +} + +template +void hash256_block(RaIter1 message_digest, RaIter2 first, RaIter2 /*last*/){ + word_t w[64]; + std::fill(w, w+64, 0); + for(std::size_t i = 0; i < 16; ++i){ + w[i] = (static_cast(mask_8bit(*(first+i*4)))<<24) + |(static_cast(mask_8bit(*(first+i*4+1)))<<16) + |(static_cast(mask_8bit(*(first+i*4+2)))<<8) + |(static_cast(mask_8bit(*(first+i*4+3)))); + } + for(std::size_t i = 16; i < 64; ++i){ + w[i] = mask_32bit(ssig1(w[i-2])+w[i-7]+ssig0(w[i-15])+w[i-16]); + } + + word_t a = *message_digest; + word_t b = *(message_digest+1); + word_t c = *(message_digest+2); + word_t d = *(message_digest+3); + word_t e = *(message_digest+4); + word_t f = *(message_digest+5); + word_t g = *(message_digest+6); + word_t h = *(message_digest+7); + + for(std::size_t i = 0; i < 64; ++i){ + word_t temp1 = h+bsig1(e)+ch(e,f,g)+add_constant[i]+w[i]; + word_t temp2 = bsig0(a)+maj(a,b,c); + h = g; + g = f; + f = e; + e = mask_32bit(d+temp1); + d = c; + c = b; + b = a; + a = mask_32bit(temp1+temp2); + } + *message_digest += a; + *(message_digest+1) += b; + *(message_digest+2) += c; + *(message_digest+3) += d; + *(message_digest+4) += e; + *(message_digest+5) += f; + *(message_digest+6) += g; + *(message_digest+7) += h; + for(std::size_t i = 0; i < 8; ++i){ + *(message_digest+i) = mask_32bit(*(message_digest+i)); + } +} + +}//namespace detail + +template +void output_hex(InIter first, InIter last, std::ostream& os){ + std::ios::fmtflags orig_flags = os.flags(); + std::streamsize orig_width = os.width(); + char orig_fill = os.fill(); + + + os.setf(std::ios::hex, std::ios::basefield); + while(first != last){ + os.width(2); + os.fill('0'); + os << static_cast(*first); + ++first; + } + os.flags(orig_flags); + os.fill(orig_fill); + os.width(orig_width); +} + +template +void bytes_to_hex_string(InIter first, InIter last, std::string& hex_str){ + std::ostringstream oss; + output_hex(first, last, oss); + hex_str.assign(oss.str()); +} + +template +void bytes_to_hex_string(const InContainer& bytes, std::string& hex_str){ + bytes_to_hex_string(bytes.begin(), bytes.end(), hex_str); +} + +template +std::string bytes_to_hex_string(InIter first, InIter last){ + std::string hex_str; + bytes_to_hex_string(first, last, hex_str); + return hex_str; +} + +template +std::string bytes_to_hex_string(const InContainer& bytes){ + std::string hex_str; + bytes_to_hex_string(bytes, hex_str); + return hex_str; +} + +class hash256_one_by_one { +public: + hash256_one_by_one(){ + init(); + } + + void init(){ + buffer_.clear(); + std::fill(data_length_digits_, data_length_digits_+4, 0); + std::copy(detail::initial_message_digest, detail::initial_message_digest+8, h_); + } + + template + void process(RaIter first, RaIter last){ + add_to_data_length(std::distance(first, last)); + std::copy(first, last, std::back_inserter(buffer_)); + std::size_t i = 0; + for(;i+64 <= buffer_.size(); i+=64){ + detail::hash256_block(h_, buffer_.begin()+i, buffer_.begin()+i+64); + } + buffer_.erase(buffer_.begin(), buffer_.begin()+i); + } + + void finish(){ + byte_t temp[64]; + std::fill(temp, temp+64, 0); + std::size_t remains = buffer_.size(); + std::copy(buffer_.begin(), buffer_.end(), temp); + temp[remains] = 0x80; + + if(remains > 55){ + std::fill(temp+remains+1, temp+64, 0); + detail::hash256_block(h_, temp, temp+64); + std::fill(temp, temp+64-4, 0); + } + else { + std::fill(temp+remains+1, temp+64-4, 0); + } + + write_data_bit_length(&(temp[56])); + detail::hash256_block(h_, temp, temp+64); + } + + template + void get_hash_bytes(OutIter first, OutIter last)const{ + for(const word_t* iter = h_; iter != h_+8; ++iter){ + for(std::size_t i = 0; i < 4 && first != last; ++i){ + *(first++) = detail::mask_8bit(static_cast((*iter >> (24-8*i)))); + } + } + } + +private: + void add_to_data_length(word_t n) { + word_t carry = 0; + data_length_digits_[0] += n; + for(std::size_t i = 0; i < 4; ++i) { + data_length_digits_[i] += carry; + if(data_length_digits_[i] >= 65536u) { + carry = data_length_digits_[i]>>16; + data_length_digits_[i] &= 65535u; + } + else { + break; + } + } + } + void write_data_bit_length(byte_t* begin) { + word_t data_bit_length_digits[4]; + std::copy( + data_length_digits_, data_length_digits_+4, + data_bit_length_digits + ); + + // convert byte length to bit length (multiply 8 or shift 3 times left) + word_t carry = 0; + for(std::size_t i = 0; i < 4; ++i) { + word_t before_val = data_bit_length_digits[i]; + data_bit_length_digits[i] <<= 3; + data_bit_length_digits[i] |= carry; + data_bit_length_digits[i] &= 65535u; + carry = (before_val >> (16-3)) & 65535u; + } + + // write data_bit_length + for(int i = 3; i >= 0; --i) { + (*begin++) = static_cast(data_bit_length_digits[i] >> 8); + (*begin++) = static_cast(data_bit_length_digits[i]); + } + } + std::vector buffer_; + word_t data_length_digits_[4]; //as 64bit integer (16bit x 4 integer) + word_t h_[8]; +}; + +inline void get_hash_hex_string(const hash256_one_by_one& hasher, std::string& hex_str){ + byte_t hash[32]; + hasher.get_hash_bytes(hash, hash+32); + return bytes_to_hex_string(hash, hash+32, hex_str); +} + +inline std::string get_hash_hex_string(const hash256_one_by_one& hasher){ + std::string hex_str; + get_hash_hex_string(hasher, hex_str); + return hex_str; +} + +template +void hash256(RaIter first, RaIter last, OutIter first2, OutIter last2){ + hash256_one_by_one hasher; + //hasher.init(); + hasher.process(first, last); + hasher.finish(); + hasher.get_hash_bytes(first2, last2); +} + +template +void hash256(RaIter first, RaIter last, OutContainer& dst){ + hash256(first, last, dst.begin(), dst.end()); +} + +template +void hash256(const RaContainer& src, OutIter first, OutIter last){ + hash256(src.begin(), src.end(), first, last); +} + +template +void hash256(const RaContainer& src, OutContainer& dst){ + hash256(src.begin(), src.end(), dst.begin(), dst.end()); +} + + +template +void hash256_hex_string(RaIter first, RaIter last, std::string& hex_str){ + byte_t hashed[32]; + hash256(first, last, hashed, hashed+32); + std::ostringstream oss; + output_hex(hashed, hashed+32, oss); + hex_str.assign(oss.str()); +} + +template +std::string hash256_hex_string(RaIter first, RaIter last){ + std::string hex_str; + hash256_hex_string(first, last, hex_str); + return hex_str; +} + +inline void hash256_hex_string(const std::string& src, std::string& hex_str){ + hash256_hex_string(src.begin(), src.end(), hex_str); +} + +template +void hash256_hex_string(const RaContainer& src, std::string& hex_str){ + hash256_hex_string(src.begin(), src.end(), hex_str); +} + +template +std::string hash256_hex_string(const RaContainer& src){ + return hash256_hex_string(src.begin(), src.end()); +} + +}//namespace picosha2 + +#endif //PICOSHA2_H diff --git a/libs/libvtrutil/src/vtr_assert.cpp b/libs/libvtrutil/src/vtr_assert.cpp new file mode 100644 index 000000000..abe884f0b --- /dev/null +++ b/libs/libvtrutil/src/vtr_assert.cpp @@ -0,0 +1,21 @@ +#include "vtr_assert.h" + +#include //fprintf, stderr +#include //abort + +namespace vtr { namespace assert { + +void handle_assert(const char* expr, const char* file, unsigned int line, const char* function, const char* msg) { + fprintf(stderr, "%s:%d", file, line); + if(function) { + fprintf(stderr, " %s:", function); + } + fprintf(stderr, " Assertion '%s' failed", expr); + if(msg) { + fprintf(stderr, " (%s)", msg); + } + fprintf(stderr, ".\n"); + std::abort(); +} + +}} //namespace diff --git a/libs/libvtrutil/src/vtr_assert.h b/libs/libvtrutil/src/vtr_assert.h new file mode 100644 index 000000000..8fea7a4af --- /dev/null +++ b/libs/libvtrutil/src/vtr_assert.h @@ -0,0 +1,133 @@ +#ifndef VTR_ASSERT_H +#define VTR_ASSERT_H +/* + * The header defines useful assertion macros for VTR projects. + * + * Three types of assertions are defined: + * VTR_ASSERT_OPT - low overhead assertions that should always be enabled + * VTR_ASSERT - medium overhead assertions that are usually be enabled + * VTR_ASSERT_SAFE - high overhead assertions typically enabled only for debugging + * VTR_ASSERT_DEBUG - very high overhead assertions typically enabled only for extreme debugging + * Each of the above assertions also have a *_MSG variants (e.g. VTR_ASSERT_MSG(expr, msg)) + * which takes an additional argument specifying additional message text to be shown. + * By convention the message should state the condition *being checked* (and not the failure condition), + * since that the condition failed is obvious from the assertion failure itself. + * + * The macro VTR_ASSERT_LEVEL specifies the level of assertion checking desired: + * + * VTR_ASSERT_LEVEL == 4: VTR_ASSERT_OPT, VTR_ASSERT, VTR_ASSERT_SAFE, VTR_ASSERT_DEBUG enabled + * VTR_ASSERT_LEVEL == 3: VTR_ASSERT_OPT, VTR_ASSERT, VTR_ASSERT_SAFE enabled + * VTR_ASSERT_LEVEL == 2: VTR_ASSERT_OPT, VTR_ASSERT enabled + * VTR_ASSERT_LEVEL == 1: VTR_ASSERT_OPT enabled + * VTR_ASSERT_LEVEL == 0: No assertion checking enabled + * Note that an assertion levels beyond 4 are currently treated the same as level 4 + */ + +//Set a default assertion level if none is specified +#ifndef VTR_ASSERT_LEVEL +# define VTR_ASSERT_LEVEL 2 +#endif + +//Enable the assertions based on the specified level +#if VTR_ASSERT_LEVEL >= 4 +# define VTR_ASSERT_DEBUG_ENABLED +#endif + +#if VTR_ASSERT_LEVEL >= 3 +# define VTR_ASSERT_SAFE_ENABLED +#endif + +#if VTR_ASSERT_LEVEL >= 2 +#define VTR_ASSERT_ENABLED +#endif + +#if VTR_ASSERT_LEVEL >= 1 +# define VTR_ASSERT_OPT_ENABLED +#endif + +//Define the user assertion macros +#ifdef VTR_ASSERT_DEBUG_ENABLED +# define VTR_ASSERT_DEBUG(expr) VTR_ASSERT_IMPL(expr, nullptr) +# define VTR_ASSERT_DEBUG_MSG(expr, msg) VTR_ASSERT_IMPL(expr, msg) +#else +# define VTR_ASSERT_DEBUG(expr) VTR_ASSERT_IMPL_NOP(expr, nullptr) +# define VTR_ASSERT_DEBUG_MSG(expr, msg) VTR_ASSERT_IMPL_NOP(expr, msg) +#endif + +#ifdef VTR_ASSERT_SAFE_ENABLED +# define VTR_ASSERT_SAFE(expr) VTR_ASSERT_IMPL(expr, nullptr) +# define VTR_ASSERT_SAFE_MSG(expr, msg) VTR_ASSERT_IMPL(expr, msg) +#else +# define VTR_ASSERT_SAFE(expr) VTR_ASSERT_IMPL_NOP(expr, nullptr) +# define VTR_ASSERT_SAFE_MSG(expr, msg) VTR_ASSERT_IMPL_NOP(expr, msg) +#endif + +#ifdef VTR_ASSERT_ENABLED +# define VTR_ASSERT(expr) VTR_ASSERT_IMPL(expr, nullptr) +# define VTR_ASSERT_MSG(expr, msg) VTR_ASSERT_IMPL(expr, msg) +#else +# define VTR_ASSERT(expr) VTR_ASSERT_IMPL_NOP(expr, nullptr) +# define VTR_ASSERT_MSG(expr, msg) VTR_ASSERT_IMPL_NOP(expr, msg) +#endif + +#ifdef VTR_ASSERT_OPT_ENABLED +# define VTR_ASSERT_OPT(expr) VTR_ASSERT_IMPL(expr, nullptr) +# define VTR_ASSERT_OPT_MSG(expr, msg) VTR_ASSERT_IMPL(expr, msg) +#else +# define VTR_ASSERT_OPT(expr) VTR_ASSERT_IMPL_NOP(expr, nullptr) +# define VTR_ASSERT_OPT_MSG(expr, msg) VTR_ASSERT_IMPL_NOP(expr, msg) +#endif + + +//Define the assertion implementation macro +// We wrap the check in a do {} while() to ensure the function-like +// macro can be always be followed by a ';' +#define VTR_ASSERT_IMPL(expr, msg) do { \ + if(!(expr)) { \ + vtr::assert::handle_assert(#expr, __FILE__, __LINE__, VTR_ASSERT_FUNCTION, msg); \ + } \ + } while(false) + +//Define the no-op assertion implementation macro +// We wrap the check in a do {} while() to ensure the function-like +// macro can be always be followed by a ';' +// +// Note that to avoid 'unused' variable warnings when assertions are +// disabled, we pass the expr and msg to sizeof(). We use sizeof specifically +// since it accepts expressions, and the C++ standard gaurentees sizeof's arguments +// are never evaluated (ensuring any expensive expressions are not evaluated when +// assertions are disabled). To avoid warnings about the unused result of sizeof() +// we cast it to void. +#define VTR_ASSERT_IMPL_NOP(expr, msg) do { \ + static_cast(sizeof(expr)); \ + static_cast(sizeof(msg)); \ + } while(false) + +//Figure out what macro to use to get the name of the current function +// We default to __func__ which is defined in C99 +// +// g++ > 2.6 define __PRETTY_FUNC__ which includes class/namespace/overload +// information, so we prefer to use it if possible +#define VTR_ASSERT_FUNCTION __func__ +#ifdef __GNUC__ +# ifdef __GNUC_MINOR__ +# if __GNUC__ >= 2 && __GNUC_MINOR__ > 6 +# undef VTR_ASSERT_FUNCTION +# define VTR_ASSERT_FUNCTION __PRETTY_FUNCTION__ +# endif +# endif +#endif + +namespace vtr { namespace assert { + //Assertion handling routine + // + //Note that we mark the routine with the standard C++11 + //attribute 'noreturn' which tells the compiler this + //function will never return. This should ensure the + //compiler won't warn about detected conditions such as + //dead-code or potential null pointer dereferences + //which are gaurded against by assertions. + [[noreturn]] void handle_assert(const char* expr, const char* file, unsigned int line, const char* function, const char* msg); +}} //namespace + +#endif //VTR_ASSERT_H diff --git a/libs/libvtrutil/src/vtr_bimap.h b/libs/libvtrutil/src/vtr_bimap.h new file mode 100644 index 000000000..cf3d8c9af --- /dev/null +++ b/libs/libvtrutil/src/vtr_bimap.h @@ -0,0 +1,142 @@ +#ifndef VTR_BIMAP +#define VTR_BIMAP +#include +#include +#include "vtr_flat_map.h" +#include "vtr_linear_map.h" + +#include "vtr_error.h" + +namespace vtr { + +/* + * A map-like class which provides a bi-directonal mapping between key and value + * + * Keys and values can be looked up directly by passing either the key or value. + * the indexing operator will throw if the key/value does not exist. + */ +template class Map=std::map, template class InvMap=std::map> +class bimap { + public: //Public types + typedef typename Map::const_iterator iterator; + typedef typename InvMap::const_iterator inverse_iterator; + public: //Accessors + + //Iterators + iterator begin() const { return map_.begin(); } + iterator end() const { return map_.end(); } + inverse_iterator inverse_begin() const { return inverse_map_.begin(); } + inverse_iterator inverse_end() const { return inverse_map_.end(); } + + //Return an iterator to the key-value pair matching key, or end() if not found + iterator find(const K key) const { + return map_.find(key); + } + + //Return an iterator to the value-key pair matching value, or inverse_end() if not found + inverse_iterator find(const V value) const { + return inverse_map_.find(value); + } + + //Return an immutable reference to the value matching key + //Will throw an exception if key is not found + const V& operator[] (const K key) const { + auto iter = find(key); + if(iter == end()) { + throw VtrError("Invalid bimap key during look-up", __FILE__, __LINE__); + } + return iter->second; + } + + //Return an immutable reference to the key matching value + //Will throw an exception if value is not found + const K& operator[] (const V value) const { + auto iter = find(value); + if(iter == inverse_end()) { + throw VtrError("Invalid bimap value during inverse look-up", __FILE__, __LINE__); + } + return iter->second; + } + + //Return the number of key-value pairs stored + std::size_t size() const { VTR_ASSERT(map_.size() == inverse_map_.size()); return map_.size(); } + + //Return true if there are no key-value pairs stored + bool empty() const { return (size() == 0); } + + //Return true if the specified key exists + bool contains(const K key) const { return find(key) != end(); } + + //Return true if the specified value exists + bool contains(const V value) const { return find(value) != inverse_end(); } + + public: //Mutators + + //Drop all stored key-values + void clear() { map_.clear(); inverse_map_.clear(); } + + //Insert a key-value pair, if not already in map + std::pair insert(const K key, const V value) { + auto ret1 = map_.insert({key,value}); + auto ret2 = inverse_map_.insert({value,key}); + + VTR_ASSERT(ret1.second == ret2.second); + + //Return true if inserted + return ret1; + } + + //Update a key-value pair, will insert if not already in map + void update(const K key, const V value) { + map_[key] = value; + inverse_map_[value] = key; + } + + //Remove the specified key (and it's associated value) + void erase(const K key) { + auto iter = map_.find(key); + if(iter != map_.end()) { + V val = iter->second; + map_.erase(iter); + + auto inv_iter = inverse_map_.find(val); + VTR_ASSERT(inv_iter != inverse_map_.end()); + inverse_map_.erase(inv_iter); + } + } + + //Remove the specified value (and it's associated key) + void erase(const V val) { + auto inv_iter = inverse_map_.find(val); + if(inv_iter != inverse_map_.end()) { + K key = inv_iter->second; + inverse_map_.erase(inv_iter); + + auto iter = map_.find(key); + VTR_ASSERT(iter != map_.end()); + map_.erase(iter); + } + } + + //Swap (this enables std::swap via ADL) + friend void swap(bimap& x, bimap& y) { + std::swap(x.map_, y.map_); + std::swap(x.inverse_map_, y.inverse_map_); + } + private: + Map map_; + InvMap inverse_map_; +}; + +template +using unordered_bimap = bimap; + +template +using flat_bimap = bimap; + +template +using linear_bimap = bimap; + +} + +#endif diff --git a/libs/libvtrutil/src/vtr_color_map.cpp b/libs/libvtrutil/src/vtr_color_map.cpp new file mode 100644 index 000000000..3c52bb539 --- /dev/null +++ b/libs/libvtrutil/src/vtr_color_map.cpp @@ -0,0 +1,831 @@ +#include +#include +#include "vtr_color_map.h" +#include "vtr_assert.h" + +namespace vtr { + +//Inferno data from MatPlotLib +static std::vector> inferno_data = { + {0.001462f, 0.000466f, 0.013866f}, + {0.002267f, 0.001270f, 0.018570f}, + {0.003299f, 0.002249f, 0.024239f}, + {0.004547f, 0.003392f, 0.030909f}, + {0.006006f, 0.004692f, 0.038558f}, + {0.007676f, 0.006136f, 0.046836f}, + {0.009561f, 0.007713f, 0.055143f}, + {0.011663f, 0.009417f, 0.063460f}, + {0.013995f, 0.011225f, 0.071862f}, + {0.016561f, 0.013136f, 0.080282f}, + {0.019373f, 0.015133f, 0.088767f}, + {0.022447f, 0.017199f, 0.097327f}, + {0.025793f, 0.019331f, 0.105930f}, + {0.029432f, 0.021503f, 0.114621f}, + {0.033385f, 0.023702f, 0.123397f}, + {0.037668f, 0.025921f, 0.132232f}, + {0.042253f, 0.028139f, 0.141141f}, + {0.046915f, 0.030324f, 0.150164f}, + {0.051644f, 0.032474f, 0.159254f}, + {0.056449f, 0.034569f, 0.168414f}, + {0.061340f, 0.036590f, 0.177642f}, + {0.066331f, 0.038504f, 0.186962f}, + {0.071429f, 0.040294f, 0.196354f}, + {0.076637f, 0.041905f, 0.205799f}, + {0.081962f, 0.043328f, 0.215289f}, + {0.087411f, 0.044556f, 0.224813f}, + {0.092990f, 0.045583f, 0.234358f}, + {0.098702f, 0.046402f, 0.243904f}, + {0.104551f, 0.047008f, 0.253430f}, + {0.110536f, 0.047399f, 0.262912f}, + {0.116656f, 0.047574f, 0.272321f}, + {0.122908f, 0.047536f, 0.281624f}, + {0.129285f, 0.047293f, 0.290788f}, + {0.135778f, 0.046856f, 0.299776f}, + {0.142378f, 0.046242f, 0.308553f}, + {0.149073f, 0.045468f, 0.317085f}, + {0.155850f, 0.044559f, 0.325338f}, + {0.162689f, 0.043554f, 0.333277f}, + {0.169575f, 0.042489f, 0.340874f}, + {0.176493f, 0.041402f, 0.348111f}, + {0.183429f, 0.040329f, 0.354971f}, + {0.190367f, 0.039309f, 0.361447f}, + {0.197297f, 0.038400f, 0.367535f}, + {0.204209f, 0.037632f, 0.373238f}, + {0.211095f, 0.037030f, 0.378563f}, + {0.217949f, 0.036615f, 0.383522f}, + {0.224763f, 0.036405f, 0.388129f}, + {0.231538f, 0.036405f, 0.392400f}, + {0.238273f, 0.036621f, 0.396353f}, + {0.244967f, 0.037055f, 0.400007f}, + {0.251620f, 0.037705f, 0.403378f}, + {0.258234f, 0.038571f, 0.406485f}, + {0.264810f, 0.039647f, 0.409345f}, + {0.271347f, 0.040922f, 0.411976f}, + {0.277850f, 0.042353f, 0.414392f}, + {0.284321f, 0.043933f, 0.416608f}, + {0.290763f, 0.045644f, 0.418637f}, + {0.297178f, 0.047470f, 0.420491f}, + {0.303568f, 0.049396f, 0.422182f}, + {0.309935f, 0.051407f, 0.423721f}, + {0.316282f, 0.053490f, 0.425116f}, + {0.322610f, 0.055634f, 0.426377f}, + {0.328921f, 0.057827f, 0.427511f}, + {0.335217f, 0.060060f, 0.428524f}, + {0.341500f, 0.062325f, 0.429425f}, + {0.347771f, 0.064616f, 0.430217f}, + {0.354032f, 0.066925f, 0.430906f}, + {0.360284f, 0.069247f, 0.431497f}, + {0.366529f, 0.071579f, 0.431994f}, + {0.372768f, 0.073915f, 0.432400f}, + {0.379001f, 0.076253f, 0.432719f}, + {0.385228f, 0.078591f, 0.432955f}, + {0.391453f, 0.080927f, 0.433109f}, + {0.397674f, 0.083257f, 0.433183f}, + {0.403894f, 0.085580f, 0.433179f}, + {0.410113f, 0.087896f, 0.433098f}, + {0.416331f, 0.090203f, 0.432943f}, + {0.422549f, 0.092501f, 0.432714f}, + {0.428768f, 0.094790f, 0.432412f}, + {0.434987f, 0.097069f, 0.432039f}, + {0.441207f, 0.099338f, 0.431594f}, + {0.447428f, 0.101597f, 0.431080f}, + {0.453651f, 0.103848f, 0.430498f}, + {0.459875f, 0.106089f, 0.429846f}, + {0.466100f, 0.108322f, 0.429125f}, + {0.472328f, 0.110547f, 0.428334f}, + {0.478558f, 0.112764f, 0.427475f}, + {0.484789f, 0.114974f, 0.426548f}, + {0.491022f, 0.117179f, 0.425552f}, + {0.497257f, 0.119379f, 0.424488f}, + {0.503493f, 0.121575f, 0.423356f}, + {0.509730f, 0.123769f, 0.422156f}, + {0.515967f, 0.125960f, 0.420887f}, + {0.522206f, 0.128150f, 0.419549f}, + {0.528444f, 0.130341f, 0.418142f}, + {0.534683f, 0.132534f, 0.416667f}, + {0.540920f, 0.134729f, 0.415123f}, + {0.547157f, 0.136929f, 0.413511f}, + {0.553392f, 0.139134f, 0.411829f}, + {0.559624f, 0.141346f, 0.410078f}, + {0.565854f, 0.143567f, 0.408258f}, + {0.572081f, 0.145797f, 0.406369f}, + {0.578304f, 0.148039f, 0.404411f}, + {0.584521f, 0.150294f, 0.402385f}, + {0.590734f, 0.152563f, 0.400290f}, + {0.596940f, 0.154848f, 0.398125f}, + {0.603139f, 0.157151f, 0.395891f}, + {0.609330f, 0.159474f, 0.393589f}, + {0.615513f, 0.161817f, 0.391219f}, + {0.621685f, 0.164184f, 0.388781f}, + {0.627847f, 0.166575f, 0.386276f}, + {0.633998f, 0.168992f, 0.383704f}, + {0.640135f, 0.171438f, 0.381065f}, + {0.646260f, 0.173914f, 0.378359f}, + {0.652369f, 0.176421f, 0.375586f}, + {0.658463f, 0.178962f, 0.372748f}, + {0.664540f, 0.181539f, 0.369846f}, + {0.670599f, 0.184153f, 0.366879f}, + {0.676638f, 0.186807f, 0.363849f}, + {0.682656f, 0.189501f, 0.360757f}, + {0.688653f, 0.192239f, 0.357603f}, + {0.694627f, 0.195021f, 0.354388f}, + {0.700576f, 0.197851f, 0.351113f}, + {0.706500f, 0.200728f, 0.347777f}, + {0.712396f, 0.203656f, 0.344383f}, + {0.718264f, 0.206636f, 0.340931f}, + {0.724103f, 0.209670f, 0.337424f}, + {0.729909f, 0.212759f, 0.333861f}, + {0.735683f, 0.215906f, 0.330245f}, + {0.741423f, 0.219112f, 0.326576f}, + {0.747127f, 0.222378f, 0.322856f}, + {0.752794f, 0.225706f, 0.319085f}, + {0.758422f, 0.229097f, 0.315266f}, + {0.764010f, 0.232554f, 0.311399f}, + {0.769556f, 0.236077f, 0.307485f}, + {0.775059f, 0.239667f, 0.303526f}, + {0.780517f, 0.243327f, 0.299523f}, + {0.785929f, 0.247056f, 0.295477f}, + {0.791293f, 0.250856f, 0.291390f}, + {0.796607f, 0.254728f, 0.287264f}, + {0.801871f, 0.258674f, 0.283099f}, + {0.807082f, 0.262692f, 0.278898f}, + {0.812239f, 0.266786f, 0.274661f}, + {0.817341f, 0.270954f, 0.270390f}, + {0.822386f, 0.275197f, 0.266085f}, + {0.827372f, 0.279517f, 0.261750f}, + {0.832299f, 0.283913f, 0.257383f}, + {0.837165f, 0.288385f, 0.252988f}, + {0.841969f, 0.292933f, 0.248564f}, + {0.846709f, 0.297559f, 0.244113f}, + {0.851384f, 0.302260f, 0.239636f}, + {0.855992f, 0.307038f, 0.235133f}, + {0.860533f, 0.311892f, 0.230606f}, + {0.865006f, 0.316822f, 0.226055f}, + {0.869409f, 0.321827f, 0.221482f}, + {0.873741f, 0.326906f, 0.216886f}, + {0.878001f, 0.332060f, 0.212268f}, + {0.882188f, 0.337287f, 0.207628f}, + {0.886302f, 0.342586f, 0.202968f}, + {0.890341f, 0.347957f, 0.198286f}, + {0.894305f, 0.353399f, 0.193584f}, + {0.898192f, 0.358911f, 0.188860f}, + {0.902003f, 0.364492f, 0.184116f}, + {0.905735f, 0.370140f, 0.179350f}, + {0.909390f, 0.375856f, 0.174563f}, + {0.912966f, 0.381636f, 0.169755f}, + {0.916462f, 0.387481f, 0.164924f}, + {0.919879f, 0.393389f, 0.160070f}, + {0.923215f, 0.399359f, 0.155193f}, + {0.926470f, 0.405389f, 0.150292f}, + {0.929644f, 0.411479f, 0.145367f}, + {0.932737f, 0.417627f, 0.140417f}, + {0.935747f, 0.423831f, 0.135440f}, + {0.938675f, 0.430091f, 0.130438f}, + {0.941521f, 0.436405f, 0.125409f}, + {0.944285f, 0.442772f, 0.120354f}, + {0.946965f, 0.449191f, 0.115272f}, + {0.949562f, 0.455660f, 0.110164f}, + {0.952075f, 0.462178f, 0.105031f}, + {0.954506f, 0.468744f, 0.099874f}, + {0.956852f, 0.475356f, 0.094695f}, + {0.959114f, 0.482014f, 0.089499f}, + {0.961293f, 0.488716f, 0.084289f}, + {0.963387f, 0.495462f, 0.079073f}, + {0.965397f, 0.502249f, 0.073859f}, + {0.967322f, 0.509078f, 0.068659f}, + {0.969163f, 0.515946f, 0.063488f}, + {0.970919f, 0.522853f, 0.058367f}, + {0.972590f, 0.529798f, 0.053324f}, + {0.974176f, 0.536780f, 0.048392f}, + {0.975677f, 0.543798f, 0.043618f}, + {0.977092f, 0.550850f, 0.039050f}, + {0.978422f, 0.557937f, 0.034931f}, + {0.979666f, 0.565057f, 0.031409f}, + {0.980824f, 0.572209f, 0.028508f}, + {0.981895f, 0.579392f, 0.026250f}, + {0.982881f, 0.586606f, 0.024661f}, + {0.983779f, 0.593849f, 0.023770f}, + {0.984591f, 0.601122f, 0.023606f}, + {0.985315f, 0.608422f, 0.024202f}, + {0.985952f, 0.615750f, 0.025592f}, + {0.986502f, 0.623105f, 0.027814f}, + {0.986964f, 0.630485f, 0.030908f}, + {0.987337f, 0.637890f, 0.034916f}, + {0.987622f, 0.645320f, 0.039886f}, + {0.987819f, 0.652773f, 0.045581f}, + {0.987926f, 0.660250f, 0.051750f}, + {0.987945f, 0.667748f, 0.058329f}, + {0.987874f, 0.675267f, 0.065257f}, + {0.987714f, 0.682807f, 0.072489f}, + {0.987464f, 0.690366f, 0.079990f}, + {0.987124f, 0.697944f, 0.087731f}, + {0.986694f, 0.705540f, 0.095694f}, + {0.986175f, 0.713153f, 0.103863f}, + {0.985566f, 0.720782f, 0.112229f}, + {0.984865f, 0.728427f, 0.120785f}, + {0.984075f, 0.736087f, 0.129527f}, + {0.983196f, 0.743758f, 0.138453f}, + {0.982228f, 0.751442f, 0.147565f}, + {0.981173f, 0.759135f, 0.156863f}, + {0.980032f, 0.766837f, 0.166353f}, + {0.978806f, 0.774545f, 0.176037f}, + {0.977497f, 0.782258f, 0.185923f}, + {0.976108f, 0.789974f, 0.196018f}, + {0.974638f, 0.797692f, 0.206332f}, + {0.973088f, 0.805409f, 0.216877f}, + {0.971468f, 0.813122f, 0.227658f}, + {0.969783f, 0.820825f, 0.238686f}, + {0.968041f, 0.828515f, 0.249972f}, + {0.966243f, 0.836191f, 0.261534f}, + {0.964394f, 0.843848f, 0.273391f}, + {0.962517f, 0.851476f, 0.285546f}, + {0.960626f, 0.859069f, 0.298010f}, + {0.958720f, 0.866624f, 0.310820f}, + {0.956834f, 0.874129f, 0.323974f}, + {0.954997f, 0.881569f, 0.337475f}, + {0.953215f, 0.888942f, 0.351369f}, + {0.951546f, 0.896226f, 0.365627f}, + {0.950018f, 0.903409f, 0.380271f}, + {0.948683f, 0.910473f, 0.395289f}, + {0.947594f, 0.917399f, 0.410665f}, + {0.946809f, 0.924168f, 0.426373f}, + {0.946392f, 0.930761f, 0.442367f}, + {0.946403f, 0.937159f, 0.458592f}, + {0.946903f, 0.943348f, 0.474970f}, + {0.947937f, 0.949318f, 0.491426f}, + {0.949545f, 0.955063f, 0.507860f}, + {0.951740f, 0.960587f, 0.524203f}, + {0.954529f, 0.965896f, 0.540361f}, + {0.957896f, 0.971003f, 0.556275f}, + {0.961812f, 0.975924f, 0.571925f}, + {0.966249f, 0.980678f, 0.587206f}, + {0.971162f, 0.985282f, 0.602154f}, + {0.976511f, 0.989753f, 0.616760f}, + {0.982257f, 0.994109f, 0.631017f}, + {0.988362f, 0.998364f, 0.644924f}}; + +//Plasma data from MatPlotLib +static std::vector> plasma_data = { + {5.03832136e-02f, 2.98028976e-02f, 5.27974883e-01f}, + {6.35363639e-02f, 2.84259729e-02f, 5.33123681e-01f}, + {7.53531234e-02f, 2.72063728e-02f, 5.38007001e-01f}, + {8.62217979e-02f, 2.61253206e-02f, 5.42657691e-01f}, + {9.63786097e-02f, 2.51650976e-02f, 5.47103487e-01f}, + {1.05979704e-01f, 2.43092436e-02f, 5.51367851e-01f}, + {1.15123641e-01f, 2.35562500e-02f, 5.55467728e-01f}, + {1.23902903e-01f, 2.28781011e-02f, 5.59423480e-01f}, + {1.32380720e-01f, 2.22583774e-02f, 5.63250116e-01f}, + {1.40603076e-01f, 2.16866674e-02f, 5.66959485e-01f}, + {1.48606527e-01f, 2.11535876e-02f, 5.70561711e-01f}, + {1.56420649e-01f, 2.06507174e-02f, 5.74065446e-01f}, + {1.64069722e-01f, 2.01705326e-02f, 5.77478074e-01f}, + {1.71573925e-01f, 1.97063415e-02f, 5.80805890e-01f}, + {1.78950212e-01f, 1.92522243e-02f, 5.84054243e-01f}, + {1.86212958e-01f, 1.88029767e-02f, 5.87227661e-01f}, + {1.93374449e-01f, 1.83540593e-02f, 5.90329954e-01f}, + {2.00445260e-01f, 1.79015512e-02f, 5.93364304e-01f}, + {2.07434551e-01f, 1.74421086e-02f, 5.96333341e-01f}, + {2.14350298e-01f, 1.69729276e-02f, 5.99239207e-01f}, + {2.21196750e-01f, 1.64970484e-02f, 6.02083323e-01f}, + {2.27982971e-01f, 1.60071509e-02f, 6.04867403e-01f}, + {2.34714537e-01f, 1.55015065e-02f, 6.07592438e-01f}, + {2.41396253e-01f, 1.49791041e-02f, 6.10259089e-01f}, + {2.48032377e-01f, 1.44393586e-02f, 6.12867743e-01f}, + {2.54626690e-01f, 1.38820918e-02f, 6.15418537e-01f}, + {2.61182562e-01f, 1.33075156e-02f, 6.17911385e-01f}, + {2.67702993e-01f, 1.27162163e-02f, 6.20345997e-01f}, + {2.74190665e-01f, 1.21091423e-02f, 6.22721903e-01f}, + {2.80647969e-01f, 1.14875915e-02f, 6.25038468e-01f}, + {2.87076059e-01f, 1.08554862e-02f, 6.27294975e-01f}, + {2.93477695e-01f, 1.02128849e-02f, 6.29490490e-01f}, + {2.99855122e-01f, 9.56079551e-03f, 6.31623923e-01f}, + {3.06209825e-01f, 8.90185346e-03f, 6.33694102e-01f}, + {3.12543124e-01f, 8.23900704e-03f, 6.35699759e-01f}, + {3.18856183e-01f, 7.57551051e-03f, 6.37639537e-01f}, + {3.25150025e-01f, 6.91491734e-03f, 6.39512001e-01f}, + {3.31425547e-01f, 6.26107379e-03f, 6.41315649e-01f}, + {3.37683446e-01f, 5.61830889e-03f, 6.43048936e-01f}, + {3.43924591e-01f, 4.99053080e-03f, 6.44710195e-01f}, + {3.50149699e-01f, 4.38202557e-03f, 6.46297711e-01f}, + {3.56359209e-01f, 3.79781761e-03f, 6.47809772e-01f}, + {3.62553473e-01f, 3.24319591e-03f, 6.49244641e-01f}, + {3.68732762e-01f, 2.72370721e-03f, 6.50600561e-01f}, + {3.74897270e-01f, 2.24514897e-03f, 6.51875762e-01f}, + {3.81047116e-01f, 1.81356205e-03f, 6.53068467e-01f}, + {3.87182639e-01f, 1.43446923e-03f, 6.54176761e-01f}, + {3.93304010e-01f, 1.11388259e-03f, 6.55198755e-01f}, + {3.99410821e-01f, 8.59420809e-04f, 6.56132835e-01f}, + {4.05502914e-01f, 6.78091517e-04f, 6.56977276e-01f}, + {4.11580082e-01f, 5.77101735e-04f, 6.57730380e-01f}, + {4.17642063e-01f, 5.63847476e-04f, 6.58390492e-01f}, + {4.23688549e-01f, 6.45902780e-04f, 6.58956004e-01f}, + {4.29719186e-01f, 8.31008207e-04f, 6.59425363e-01f}, + {4.35733575e-01f, 1.12705875e-03f, 6.59797077e-01f}, + {4.41732123e-01f, 1.53984779e-03f, 6.60069009e-01f}, + {4.47713600e-01f, 2.07954744e-03f, 6.60240367e-01f}, + {4.53677394e-01f, 2.75470302e-03f, 6.60309966e-01f}, + {4.59622938e-01f, 3.57374415e-03f, 6.60276655e-01f}, + {4.65549631e-01f, 4.54518084e-03f, 6.60139383e-01f}, + {4.71456847e-01f, 5.67758762e-03f, 6.59897210e-01f}, + {4.77343929e-01f, 6.97958743e-03f, 6.59549311e-01f}, + {4.83210198e-01f, 8.45983494e-03f, 6.59094989e-01f}, + {4.89054951e-01f, 1.01269996e-02f, 6.58533677e-01f}, + {4.94877466e-01f, 1.19897486e-02f, 6.57864946e-01f}, + {5.00677687e-01f, 1.40550640e-02f, 6.57087561e-01f}, + {5.06454143e-01f, 1.63333443e-02f, 6.56202294e-01f}, + {5.12206035e-01f, 1.88332232e-02f, 6.55209222e-01f}, + {5.17932580e-01f, 2.15631918e-02f, 6.54108545e-01f}, + {5.23632990e-01f, 2.45316468e-02f, 6.52900629e-01f}, + {5.29306474e-01f, 2.77468735e-02f, 6.51586010e-01f}, + {5.34952244e-01f, 3.12170300e-02f, 6.50165396e-01f}, + {5.40569510e-01f, 3.49501310e-02f, 6.48639668e-01f}, + {5.46157494e-01f, 3.89540334e-02f, 6.47009884e-01f}, + {5.51715423e-01f, 4.31364795e-02f, 6.45277275e-01f}, + {5.57242538e-01f, 4.73307585e-02f, 6.43443250e-01f}, + {5.62738096e-01f, 5.15448092e-02f, 6.41509389e-01f}, + {5.68201372e-01f, 5.57776706e-02f, 6.39477440e-01f}, + {5.73631859e-01f, 6.00281369e-02f, 6.37348841e-01f}, + {5.79028682e-01f, 6.42955547e-02f, 6.35126108e-01f}, + {5.84391137e-01f, 6.85790261e-02f, 6.32811608e-01f}, + {5.89718606e-01f, 7.28775875e-02f, 6.30407727e-01f}, + {5.95010505e-01f, 7.71902878e-02f, 6.27916992e-01f}, + {6.00266283e-01f, 8.15161895e-02f, 6.25342058e-01f}, + {6.05485428e-01f, 8.58543713e-02f, 6.22685703e-01f}, + {6.10667469e-01f, 9.02039303e-02f, 6.19950811e-01f}, + {6.15811974e-01f, 9.45639838e-02f, 6.17140367e-01f}, + {6.20918555e-01f, 9.89336721e-02f, 6.14257440e-01f}, + {6.25986869e-01f, 1.03312160e-01f, 6.11305174e-01f}, + {6.31016615e-01f, 1.07698641e-01f, 6.08286774e-01f}, + {6.36007543e-01f, 1.12092335e-01f, 6.05205491e-01f}, + {6.40959444e-01f, 1.16492495e-01f, 6.02064611e-01f}, + {6.45872158e-01f, 1.20898405e-01f, 5.98867442e-01f}, + {6.50745571e-01f, 1.25309384e-01f, 5.95617300e-01f}, + {6.55579615e-01f, 1.29724785e-01f, 5.92317494e-01f}, + {6.60374266e-01f, 1.34143997e-01f, 5.88971318e-01f}, + {6.65129493e-01f, 1.38566428e-01f, 5.85582301e-01f}, + {6.69845385e-01f, 1.42991540e-01f, 5.82153572e-01f}, + {6.74522060e-01f, 1.47418835e-01f, 5.78688247e-01f}, + {6.79159664e-01f, 1.51847851e-01f, 5.75189431e-01f}, + {6.83758384e-01f, 1.56278163e-01f, 5.71660158e-01f}, + {6.88318440e-01f, 1.60709387e-01f, 5.68103380e-01f}, + {6.92840088e-01f, 1.65141174e-01f, 5.64521958e-01f}, + {6.97323615e-01f, 1.69573215e-01f, 5.60918659e-01f}, + {7.01769334e-01f, 1.74005236e-01f, 5.57296144e-01f}, + {7.06177590e-01f, 1.78437000e-01f, 5.53656970e-01f}, + {7.10548747e-01f, 1.82868306e-01f, 5.50003579e-01f}, + {7.14883195e-01f, 1.87298986e-01f, 5.46338299e-01f}, + {7.19181339e-01f, 1.91728906e-01f, 5.42663338e-01f}, + {7.23443604e-01f, 1.96157962e-01f, 5.38980786e-01f}, + {7.27670428e-01f, 2.00586086e-01f, 5.35292612e-01f}, + {7.31862231e-01f, 2.05013174e-01f, 5.31600995e-01f}, + {7.36019424e-01f, 2.09439071e-01f, 5.27908434e-01f}, + {7.40142557e-01f, 2.13863965e-01f, 5.24215533e-01f}, + {7.44232102e-01f, 2.18287899e-01f, 5.20523766e-01f}, + {7.48288533e-01f, 2.22710942e-01f, 5.16834495e-01f}, + {7.52312321e-01f, 2.27133187e-01f, 5.13148963e-01f}, + {7.56303937e-01f, 2.31554749e-01f, 5.09468305e-01f}, + {7.60263849e-01f, 2.35975765e-01f, 5.05793543e-01f}, + {7.64192516e-01f, 2.40396394e-01f, 5.02125599e-01f}, + {7.68090391e-01f, 2.44816813e-01f, 4.98465290e-01f}, + {7.71957916e-01f, 2.49237220e-01f, 4.94813338e-01f}, + {7.75795522e-01f, 2.53657797e-01f, 4.91170517e-01f}, + {7.79603614e-01f, 2.58078397e-01f, 4.87539124e-01f}, + {7.83382636e-01f, 2.62499662e-01f, 4.83917732e-01f}, + {7.87132978e-01f, 2.66921859e-01f, 4.80306702e-01f}, + {7.90855015e-01f, 2.71345267e-01f, 4.76706319e-01f}, + {7.94549101e-01f, 2.75770179e-01f, 4.73116798e-01f}, + {7.98215577e-01f, 2.80196901e-01f, 4.69538286e-01f}, + {8.01854758e-01f, 2.84625750e-01f, 4.65970871e-01f}, + {8.05466945e-01f, 2.89057057e-01f, 4.62414580e-01f}, + {8.09052419e-01f, 2.93491117e-01f, 4.58869577e-01f}, + {8.12611506e-01f, 2.97927865e-01f, 4.55337565e-01f}, + {8.16144382e-01f, 3.02368130e-01f, 4.51816385e-01f}, + {8.19651255e-01f, 3.06812282e-01f, 4.48305861e-01f}, + {8.23132309e-01f, 3.11260703e-01f, 4.44805781e-01f}, + {8.26587706e-01f, 3.15713782e-01f, 4.41315901e-01f}, + {8.30017584e-01f, 3.20171913e-01f, 4.37835947e-01f}, + {8.33422053e-01f, 3.24635499e-01f, 4.34365616e-01f}, + {8.36801237e-01f, 3.29104836e-01f, 4.30905052e-01f}, + {8.40155276e-01f, 3.33580106e-01f, 4.27454836e-01f}, + {8.43484103e-01f, 3.38062109e-01f, 4.24013059e-01f}, + {8.46787726e-01f, 3.42551272e-01f, 4.20579333e-01f}, + {8.50066132e-01f, 3.47048028e-01f, 4.17153264e-01f}, + {8.53319279e-01f, 3.51552815e-01f, 4.13734445e-01f}, + {8.56547103e-01f, 3.56066072e-01f, 4.10322469e-01f}, + {8.59749520e-01f, 3.60588229e-01f, 4.06916975e-01f}, + {8.62926559e-01f, 3.65119408e-01f, 4.03518809e-01f}, + {8.66077920e-01f, 3.69660446e-01f, 4.00126027e-01f}, + {8.69203436e-01f, 3.74211795e-01f, 3.96738211e-01f}, + {8.72302917e-01f, 3.78773910e-01f, 3.93354947e-01f}, + {8.75376149e-01f, 3.83347243e-01f, 3.89975832e-01f}, + {8.78422895e-01f, 3.87932249e-01f, 3.86600468e-01f}, + {8.81442916e-01f, 3.92529339e-01f, 3.83228622e-01f}, + {8.84435982e-01f, 3.97138877e-01f, 3.79860246e-01f}, + {8.87401682e-01f, 4.01761511e-01f, 3.76494232e-01f}, + {8.90339687e-01f, 4.06397694e-01f, 3.73130228e-01f}, + {8.93249647e-01f, 4.11047871e-01f, 3.69767893e-01f}, + {8.96131191e-01f, 4.15712489e-01f, 3.66406907e-01f}, + {8.98983931e-01f, 4.20391986e-01f, 3.63046965e-01f}, + {9.01807455e-01f, 4.25086807e-01f, 3.59687758e-01f}, + {9.04601295e-01f, 4.29797442e-01f, 3.56328796e-01f}, + {9.07364995e-01f, 4.34524335e-01f, 3.52969777e-01f}, + {9.10098088e-01f, 4.39267908e-01f, 3.49610469e-01f}, + {9.12800095e-01f, 4.44028574e-01f, 3.46250656e-01f}, + {9.15470518e-01f, 4.48806744e-01f, 3.42890148e-01f}, + {9.18108848e-01f, 4.53602818e-01f, 3.39528771e-01f}, + {9.20714383e-01f, 4.58417420e-01f, 3.36165582e-01f}, + {9.23286660e-01f, 4.63250828e-01f, 3.32800827e-01f}, + {9.25825146e-01f, 4.68103387e-01f, 3.29434512e-01f}, + {9.28329275e-01f, 4.72975465e-01f, 3.26066550e-01f}, + {9.30798469e-01f, 4.77867420e-01f, 3.22696876e-01f}, + {9.33232140e-01f, 4.82779603e-01f, 3.19325444e-01f}, + {9.35629684e-01f, 4.87712357e-01f, 3.15952211e-01f}, + {9.37990034e-01f, 4.92666544e-01f, 3.12575440e-01f}, + {9.40312939e-01f, 4.97642038e-01f, 3.09196628e-01f}, + {9.42597771e-01f, 5.02639147e-01f, 3.05815824e-01f}, + {9.44843893e-01f, 5.07658169e-01f, 3.02433101e-01f}, + {9.47050662e-01f, 5.12699390e-01f, 2.99048555e-01f}, + {9.49217427e-01f, 5.17763087e-01f, 2.95662308e-01f}, + {9.51343530e-01f, 5.22849522e-01f, 2.92274506e-01f}, + {9.53427725e-01f, 5.27959550e-01f, 2.88883445e-01f}, + {9.55469640e-01f, 5.33093083e-01f, 2.85490391e-01f}, + {9.57468770e-01f, 5.38250172e-01f, 2.82096149e-01f}, + {9.59424430e-01f, 5.43431038e-01f, 2.78700990e-01f}, + {9.61335930e-01f, 5.48635890e-01f, 2.75305214e-01f}, + {9.63202573e-01f, 5.53864931e-01f, 2.71909159e-01f}, + {9.65023656e-01f, 5.59118349e-01f, 2.68513200e-01f}, + {9.66798470e-01f, 5.64396327e-01f, 2.65117752e-01f}, + {9.68525639e-01f, 5.69699633e-01f, 2.61721488e-01f}, + {9.70204593e-01f, 5.75028270e-01f, 2.58325424e-01f}, + {9.71835007e-01f, 5.80382015e-01f, 2.54931256e-01f}, + {9.73416145e-01f, 5.85761012e-01f, 2.51539615e-01f}, + {9.74947262e-01f, 5.91165394e-01f, 2.48151200e-01f}, + {9.76427606e-01f, 5.96595287e-01f, 2.44766775e-01f}, + {9.77856416e-01f, 6.02050811e-01f, 2.41387186e-01f}, + {9.79232922e-01f, 6.07532077e-01f, 2.38013359e-01f}, + {9.80556344e-01f, 6.13039190e-01f, 2.34646316e-01f}, + {9.81825890e-01f, 6.18572250e-01f, 2.31287178e-01f}, + {9.83040742e-01f, 6.24131362e-01f, 2.27937141e-01f}, + {9.84198924e-01f, 6.29717516e-01f, 2.24595006e-01f}, + {9.85300760e-01f, 6.35329876e-01f, 2.21264889e-01f}, + {9.86345421e-01f, 6.40968508e-01f, 2.17948456e-01f}, + {9.87332067e-01f, 6.46633475e-01f, 2.14647532e-01f}, + {9.88259846e-01f, 6.52324832e-01f, 2.11364122e-01f}, + {9.89127893e-01f, 6.58042630e-01f, 2.08100426e-01f}, + {9.89935328e-01f, 6.63786914e-01f, 2.04858855e-01f}, + {9.90681261e-01f, 6.69557720e-01f, 2.01642049e-01f}, + {9.91364787e-01f, 6.75355082e-01f, 1.98452900e-01f}, + {9.91984990e-01f, 6.81179025e-01f, 1.95294567e-01f}, + {9.92540939e-01f, 6.87029567e-01f, 1.92170500e-01f}, + {9.93031693e-01f, 6.92906719e-01f, 1.89084459e-01f}, + {9.93456302e-01f, 6.98810484e-01f, 1.86040537e-01f}, + {9.93813802e-01f, 7.04740854e-01f, 1.83043180e-01f}, + {9.94103226e-01f, 7.10697814e-01f, 1.80097207e-01f}, + {9.94323596e-01f, 7.16681336e-01f, 1.77207826e-01f}, + {9.94473934e-01f, 7.22691379e-01f, 1.74380656e-01f}, + {9.94553260e-01f, 7.28727890e-01f, 1.71621733e-01f}, + {9.94560594e-01f, 7.34790799e-01f, 1.68937522e-01f}, + {9.94494964e-01f, 7.40880020e-01f, 1.66334918e-01f}, + {9.94355411e-01f, 7.46995448e-01f, 1.63821243e-01f}, + {9.94140989e-01f, 7.53136955e-01f, 1.61404226e-01f}, + {9.93850778e-01f, 7.59304390e-01f, 1.59091984e-01f}, + {9.93482190e-01f, 7.65498551e-01f, 1.56890625e-01f}, + {9.93033251e-01f, 7.71719833e-01f, 1.54807583e-01f}, + {9.92505214e-01f, 7.77966775e-01f, 1.52854862e-01f}, + {9.91897270e-01f, 7.84239120e-01f, 1.51041581e-01f}, + {9.91208680e-01f, 7.90536569e-01f, 1.49376885e-01f}, + {9.90438793e-01f, 7.96858775e-01f, 1.47869810e-01f}, + {9.89587065e-01f, 8.03205337e-01f, 1.46529128e-01f}, + {9.88647741e-01f, 8.09578605e-01f, 1.45357284e-01f}, + {9.87620557e-01f, 8.15977942e-01f, 1.44362644e-01f}, + {9.86509366e-01f, 8.22400620e-01f, 1.43556679e-01f}, + {9.85314198e-01f, 8.28845980e-01f, 1.42945116e-01f}, + {9.84031139e-01f, 8.35315360e-01f, 1.42528388e-01f}, + {9.82652820e-01f, 8.41811730e-01f, 1.42302653e-01f}, + {9.81190389e-01f, 8.48328902e-01f, 1.42278607e-01f}, + {9.79643637e-01f, 8.54866468e-01f, 1.42453425e-01f}, + {9.77994918e-01f, 8.61432314e-01f, 1.42808191e-01f}, + {9.76264977e-01f, 8.68015998e-01f, 1.43350944e-01f}, + {9.74443038e-01f, 8.74622194e-01f, 1.44061156e-01f}, + {9.72530009e-01f, 8.81250063e-01f, 1.44922913e-01f}, + {9.70532932e-01f, 8.87896125e-01f, 1.45918663e-01f}, + {9.68443477e-01f, 8.94563989e-01f, 1.47014438e-01f}, + {9.66271225e-01f, 9.01249365e-01f, 1.48179639e-01f}, + {9.64021057e-01f, 9.07950379e-01f, 1.49370428e-01f}, + {9.61681481e-01f, 9.14672479e-01f, 1.50520343e-01f}, + {9.59275646e-01f, 9.21406537e-01f, 1.51566019e-01f}, + {9.56808068e-01f, 9.28152065e-01f, 1.52409489e-01f}, + {9.54286813e-01f, 9.34907730e-01f, 1.52921158e-01f}, + {9.51726083e-01f, 9.41670605e-01f, 1.52925363e-01f}, + {9.49150533e-01f, 9.48434900e-01f, 1.52177604e-01f}, + {9.46602270e-01f, 9.55189860e-01f, 1.50327944e-01f}, + {9.44151742e-01f, 9.61916487e-01f, 1.46860789e-01f}, + {9.41896120e-01f, 9.68589814e-01f, 1.40955606e-01f}, + {9.40015097e-01f, 9.75158357e-01f, 1.31325517e-01f}}; + +//Viridis data from MatPlotLib +static std::vector> viridis_data = { + {0.26700401f, 0.00487433f, 0.32941519f}, + {0.26851048f, 0.00960483f, 0.33542652f}, + {0.26994384f, 0.01462494f, 0.34137895f}, + {0.27130489f, 0.01994186f, 0.34726862f}, + {0.27259384f, 0.02556309f, 0.35309303f}, + {0.27380934f, 0.03149748f, 0.35885256f}, + {0.27495242f, 0.03775181f, 0.36454323f}, + {0.27602238f, 0.04416723f, 0.37016418f}, + {0.2770184f , 0.05034437f, 0.37571452f}, + {0.27794143f, 0.05632444f, 0.38119074f}, + {0.27879067f, 0.06214536f, 0.38659204f}, + {0.2795655f , 0.06783587f, 0.39191723f}, + {0.28026658f, 0.07341724f, 0.39716349f}, + {0.28089358f, 0.07890703f, 0.40232944f}, + {0.28144581f, 0.0843197f , 0.40741404f}, + {0.28192358f, 0.08966622f, 0.41241521f}, + {0.28232739f, 0.09495545f, 0.41733086f}, + {0.28265633f, 0.10019576f, 0.42216032f}, + {0.28291049f, 0.10539345f, 0.42690202f}, + {0.28309095f, 0.11055307f, 0.43155375f}, + {0.28319704f, 0.11567966f, 0.43611482f}, + {0.28322882f, 0.12077701f, 0.44058404f}, + {0.28318684f, 0.12584799f, 0.44496f }, + {0.283072f , 0.13089477f, 0.44924127f}, + {0.28288389f, 0.13592005f, 0.45342734f}, + {0.28262297f, 0.14092556f, 0.45751726f}, + {0.28229037f, 0.14591233f, 0.46150995f}, + {0.28188676f, 0.15088147f, 0.46540474f}, + {0.28141228f, 0.15583425f, 0.46920128f}, + {0.28086773f, 0.16077132f, 0.47289909f}, + {0.28025468f, 0.16569272f, 0.47649762f}, + {0.27957399f, 0.17059884f, 0.47999675f}, + {0.27882618f, 0.1754902f , 0.48339654f}, + {0.27801236f, 0.18036684f, 0.48669702f}, + {0.27713437f, 0.18522836f, 0.48989831f}, + {0.27619376f, 0.19007447f, 0.49300074f}, + {0.27519116f, 0.1949054f , 0.49600488f}, + {0.27412802f, 0.19972086f, 0.49891131f}, + {0.27300596f, 0.20452049f, 0.50172076f}, + {0.27182812f, 0.20930306f, 0.50443413f}, + {0.27059473f, 0.21406899f, 0.50705243f}, + {0.26930756f, 0.21881782f, 0.50957678f}, + {0.26796846f, 0.22354911f, 0.5120084f }, + {0.26657984f, 0.2282621f , 0.5143487f }, + {0.2651445f , 0.23295593f, 0.5165993f }, + {0.2636632f , 0.23763078f, 0.51876163f}, + {0.26213801f, 0.24228619f, 0.52083736f}, + {0.26057103f, 0.2469217f , 0.52282822f}, + {0.25896451f, 0.25153685f, 0.52473609f}, + {0.25732244f, 0.2561304f , 0.52656332f}, + {0.25564519f, 0.26070284f, 0.52831152f}, + {0.25393498f, 0.26525384f, 0.52998273f}, + {0.25219404f, 0.26978306f, 0.53157905f}, + {0.25042462f, 0.27429024f, 0.53310261f}, + {0.24862899f, 0.27877509f, 0.53455561f}, + {0.2468114f , 0.28323662f, 0.53594093f}, + {0.24497208f, 0.28767547f, 0.53726018f}, + {0.24311324f, 0.29209154f, 0.53851561f}, + {0.24123708f, 0.29648471f, 0.53970946f}, + {0.23934575f, 0.30085494f, 0.54084398f}, + {0.23744138f, 0.30520222f, 0.5419214f }, + {0.23552606f, 0.30952657f, 0.54294396f}, + {0.23360277f, 0.31382773f, 0.54391424f}, + {0.2316735f , 0.3181058f , 0.54483444f}, + {0.22973926f, 0.32236127f, 0.54570633f}, + {0.22780192f, 0.32659432f, 0.546532f }, + {0.2258633f , 0.33080515f, 0.54731353f}, + {0.22392515f, 0.334994f , 0.54805291f}, + {0.22198915f, 0.33916114f, 0.54875211f}, + {0.22005691f, 0.34330688f, 0.54941304f}, + {0.21812995f, 0.34743154f, 0.55003755f}, + {0.21620971f, 0.35153548f, 0.55062743f}, + {0.21429757f, 0.35561907f, 0.5511844f }, + {0.21239477f, 0.35968273f, 0.55171011f}, + {0.2105031f , 0.36372671f, 0.55220646f}, + {0.20862342f, 0.36775151f, 0.55267486f}, + {0.20675628f, 0.37175775f, 0.55311653f}, + {0.20490257f, 0.37574589f, 0.55353282f}, + {0.20306309f, 0.37971644f, 0.55392505f}, + {0.20123854f, 0.38366989f, 0.55429441f}, + {0.1994295f , 0.38760678f, 0.55464205f}, + {0.1976365f , 0.39152762f, 0.55496905f}, + {0.19585993f, 0.39543297f, 0.55527637f}, + {0.19410009f, 0.39932336f, 0.55556494f}, + {0.19235719f, 0.40319934f, 0.55583559f}, + {0.19063135f, 0.40706148f, 0.55608907f}, + {0.18892259f, 0.41091033f, 0.55632606f}, + {0.18723083f, 0.41474645f, 0.55654717f}, + {0.18555593f, 0.4185704f , 0.55675292f}, + {0.18389763f, 0.42238275f, 0.55694377f}, + {0.18225561f, 0.42618405f, 0.5571201f }, + {0.18062949f, 0.42997486f, 0.55728221f}, + {0.17901879f, 0.43375572f, 0.55743035f}, + {0.17742298f, 0.4375272f , 0.55756466f}, + {0.17584148f, 0.44128981f, 0.55768526f}, + {0.17427363f, 0.4450441f , 0.55779216f}, + {0.17271876f, 0.4487906f , 0.55788532f}, + {0.17117615f, 0.4525298f , 0.55796464f}, + {0.16964573f, 0.45626209f, 0.55803034f}, + {0.16812641f, 0.45998802f, 0.55808199f}, + {0.1666171f , 0.46370813f, 0.55811913f}, + {0.16511703f, 0.4674229f , 0.55814141f}, + {0.16362543f, 0.47113278f, 0.55814842f}, + {0.16214155f, 0.47483821f, 0.55813967f}, + {0.16066467f, 0.47853961f, 0.55811466f}, + {0.15919413f, 0.4822374f , 0.5580728f }, + {0.15772933f, 0.48593197f, 0.55801347f}, + {0.15626973f, 0.4896237f , 0.557936f }, + {0.15481488f, 0.49331293f, 0.55783967f}, + {0.15336445f, 0.49700003f, 0.55772371f}, + {0.1519182f , 0.50068529f, 0.55758733f}, + {0.15047605f, 0.50436904f, 0.55742968f}, + {0.14903918f, 0.50805136f, 0.5572505f }, + {0.14760731f, 0.51173263f, 0.55704861f}, + {0.14618026f, 0.51541316f, 0.55682271f}, + {0.14475863f, 0.51909319f, 0.55657181f}, + {0.14334327f, 0.52277292f, 0.55629491f}, + {0.14193527f, 0.52645254f, 0.55599097f}, + {0.14053599f, 0.53013219f, 0.55565893f}, + {0.13914708f, 0.53381201f, 0.55529773f}, + {0.13777048f, 0.53749213f, 0.55490625f}, + {0.1364085f , 0.54117264f, 0.55448339f}, + {0.13506561f, 0.54485335f, 0.55402906f}, + {0.13374299f, 0.54853458f, 0.55354108f}, + {0.13244401f, 0.55221637f, 0.55301828f}, + {0.13117249f, 0.55589872f, 0.55245948f}, + {0.1299327f , 0.55958162f, 0.55186354f}, + {0.12872938f, 0.56326503f, 0.55122927f}, + {0.12756771f, 0.56694891f, 0.55055551f}, + {0.12645338f, 0.57063316f, 0.5498411f }, + {0.12539383f, 0.57431754f, 0.54908564f}, + {0.12439474f, 0.57800205f, 0.5482874f }, + {0.12346281f, 0.58168661f, 0.54744498f}, + {0.12260562f, 0.58537105f, 0.54655722f}, + {0.12183122f, 0.58905521f, 0.54562298f}, + {0.12114807f, 0.59273889f, 0.54464114f}, + {0.12056501f, 0.59642187f, 0.54361058f}, + {0.12009154f, 0.60010387f, 0.54253043f}, + {0.11973756f, 0.60378459f, 0.54139999f}, + {0.11951163f, 0.60746388f, 0.54021751f}, + {0.11942341f, 0.61114146f, 0.53898192f}, + {0.11948255f, 0.61481702f, 0.53769219f}, + {0.11969858f, 0.61849025f, 0.53634733f}, + {0.12008079f, 0.62216081f, 0.53494633f}, + {0.12063824f, 0.62582833f, 0.53348834f}, + {0.12137972f, 0.62949242f, 0.53197275f}, + {0.12231244f, 0.63315277f, 0.53039808f}, + {0.12344358f, 0.63680899f, 0.52876343f}, + {0.12477953f, 0.64046069f, 0.52706792f}, + {0.12632581f, 0.64410744f, 0.52531069f}, + {0.12808703f, 0.64774881f, 0.52349092f}, + {0.13006688f, 0.65138436f, 0.52160791f}, + {0.13226797f, 0.65501363f, 0.51966086f}, + {0.13469183f, 0.65863619f, 0.5176488f }, + {0.13733921f, 0.66225157f, 0.51557101f}, + {0.14020991f, 0.66585927f, 0.5134268f }, + {0.14330291f, 0.66945881f, 0.51121549f}, + {0.1466164f , 0.67304968f, 0.50893644f}, + {0.15014782f, 0.67663139f, 0.5065889f }, + {0.15389405f, 0.68020343f, 0.50417217f}, + {0.15785146f, 0.68376525f, 0.50168574f}, + {0.16201598f, 0.68731632f, 0.49912906f}, + {0.1663832f , 0.69085611f, 0.49650163f}, + {0.1709484f , 0.69438405f, 0.49380294f}, + {0.17570671f, 0.6978996f , 0.49103252f}, + {0.18065314f, 0.70140222f, 0.48818938f}, + {0.18578266f, 0.70489133f, 0.48527326f}, + {0.19109018f, 0.70836635f, 0.48228395f}, + {0.19657063f, 0.71182668f, 0.47922108f}, + {0.20221902f, 0.71527175f, 0.47608431f}, + {0.20803045f, 0.71870095f, 0.4728733f }, + {0.21400015f, 0.72211371f, 0.46958774f}, + {0.22012381f, 0.72550945f, 0.46622638f}, + {0.2263969f , 0.72888753f, 0.46278934f}, + {0.23281498f, 0.73224735f, 0.45927675f}, + {0.2393739f , 0.73558828f, 0.45568838f}, + {0.24606968f, 0.73890972f, 0.45202405f}, + {0.25289851f, 0.74221104f, 0.44828355f}, + {0.25985676f, 0.74549162f, 0.44446673f}, + {0.26694127f, 0.74875084f, 0.44057284f}, + {0.27414922f, 0.75198807f, 0.4366009f }, + {0.28147681f, 0.75520266f, 0.43255207f}, + {0.28892102f, 0.75839399f, 0.42842626f}, + {0.29647899f, 0.76156142f, 0.42422341f}, + {0.30414796f, 0.76470433f, 0.41994346f}, + {0.31192534f, 0.76782207f, 0.41558638f}, + {0.3198086f , 0.77091403f, 0.41115215f}, + {0.3277958f , 0.77397953f, 0.40664011f}, + {0.33588539f, 0.7770179f , 0.40204917f}, + {0.34407411f, 0.78002855f, 0.39738103f}, + {0.35235985f, 0.78301086f, 0.39263579f}, + {0.36074053f, 0.78596419f, 0.38781353f}, + {0.3692142f , 0.78888793f, 0.38291438f}, + {0.37777892f, 0.79178146f, 0.3779385f }, + {0.38643282f, 0.79464415f, 0.37288606f}, + {0.39517408f, 0.79747541f, 0.36775726f}, + {0.40400101f, 0.80027461f, 0.36255223f}, + {0.4129135f , 0.80304099f, 0.35726893f}, + {0.42190813f, 0.80577412f, 0.35191009f}, + {0.43098317f, 0.80847343f, 0.34647607f}, + {0.44013691f, 0.81113836f, 0.3409673f }, + {0.44936763f, 0.81376835f, 0.33538426f}, + {0.45867362f, 0.81636288f, 0.32972749f}, + {0.46805314f, 0.81892143f, 0.32399761f}, + {0.47750446f, 0.82144351f, 0.31819529f}, + {0.4870258f , 0.82392862f, 0.31232133f}, + {0.49661536f, 0.82637633f, 0.30637661f}, + {0.5062713f , 0.82878621f, 0.30036211f}, + {0.51599182f, 0.83115784f, 0.29427888f}, + {0.52577622f, 0.83349064f, 0.2881265f }, + {0.5356211f , 0.83578452f, 0.28190832f}, + {0.5455244f , 0.83803918f, 0.27562602f}, + {0.55548397f, 0.84025437f, 0.26928147f}, + {0.5654976f , 0.8424299f , 0.26287683f}, + {0.57556297f, 0.84456561f, 0.25641457f}, + {0.58567772f, 0.84666139f, 0.24989748f}, + {0.59583934f, 0.84871722f, 0.24332878f}, + {0.60604528f, 0.8507331f , 0.23671214f}, + {0.61629283f, 0.85270912f, 0.23005179f}, + {0.62657923f, 0.85464543f, 0.22335258f}, + {0.63690157f, 0.85654226f, 0.21662012f}, + {0.64725685f, 0.85839991f, 0.20986086f}, + {0.65764197f, 0.86021878f, 0.20308229f}, + {0.66805369f, 0.86199932f, 0.19629307f}, + {0.67848868f, 0.86374211f, 0.18950326f}, + {0.68894351f, 0.86544779f, 0.18272455f}, + {0.69941463f, 0.86711711f, 0.17597055f}, + {0.70989842f, 0.86875092f, 0.16925712f}, + {0.72039115f, 0.87035015f, 0.16260273f}, + {0.73088902f, 0.87191584f, 0.15602894f}, + {0.74138803f, 0.87344918f, 0.14956101f}, + {0.75188414f, 0.87495143f, 0.14322828f}, + {0.76237342f, 0.87642392f, 0.13706449f}, + {0.77285183f, 0.87786808f, 0.13110864f}, + {0.78331535f, 0.87928545f, 0.12540538f}, + {0.79375994f, 0.88067763f, 0.12000532f}, + {0.80418159f, 0.88204632f, 0.11496505f}, + {0.81457634f, 0.88339329f, 0.11034678f}, + {0.82494028f, 0.88472036f, 0.10621724f}, + {0.83526959f, 0.88602943f, 0.1026459f }, + {0.84556056f, 0.88732243f, 0.09970219f}, + {0.8558096f , 0.88860134f, 0.09745186f}, + {0.86601325f, 0.88986815f, 0.09595277f}, + {0.87616824f, 0.89112487f, 0.09525046f}, + {0.88627146f, 0.89237353f, 0.09537439f}, + {0.89632002f, 0.89361614f, 0.09633538f}, + {0.90631121f, 0.89485467f, 0.09812496f}, + {0.91624212f, 0.89609127f, 0.1007168f }, + {0.92610579f, 0.89732977f, 0.10407067f}, + {0.93590444f, 0.8985704f , 0.10813094f}, + {0.94563626f, 0.899815f , 0.11283773f}, + {0.95529972f, 0.90106534f, 0.11812832f}, + {0.96489353f, 0.90232311f, 0.12394051f}, + {0.97441665f, 0.90358991f, 0.13021494f}, + {0.98386829f, 0.90486726f, 0.13689671f}, + {0.99324789f, 0.90615657f, 0.1439362f }}; + +ColorMap::ColorMap(float min_val, float max_val, const std::vector>& color_data) + : min_(min_val) + , max_(max_val) + , color_data_(color_data) { + VTR_ASSERT(max_ >= min_); +} + +Color ColorMap::color(float value) const { + VTR_ASSERT(value >= min_); + VTR_ASSERT(value <= max_); + + float norm_value; + if (range() == 0) { + norm_value = 0; + } else { + norm_value = (value - min_) / range(); + } + + size_t color_idx = std::round(norm_value * (color_data_.size() - 1)); + + VTR_ASSERT(color_idx < color_data_.size()); + + return color_data_[color_idx]; +} + +float ColorMap::min() const { + return min_; +} + +float ColorMap::max() const { + return max_; +} + +float ColorMap::range() const { + return max() - min(); +} + +InfernoColorMap::InfernoColorMap(float min_val, float max_val) + : ColorMap(min_val, max_val, inferno_data) {} + +PlasmaColorMap::PlasmaColorMap(float min_val, float max_val) + : ColorMap(min_val, max_val, plasma_data) {} + +ViridisColorMap::ViridisColorMap(float min_val, float max_val) + : ColorMap(min_val, max_val, viridis_data) {} + +} //namespace diff --git a/libs/libvtrutil/src/vtr_color_map.h b/libs/libvtrutil/src/vtr_color_map.h new file mode 100644 index 000000000..93cee8405 --- /dev/null +++ b/libs/libvtrutil/src/vtr_color_map.h @@ -0,0 +1,45 @@ +#ifndef VTR_CMAP_H +#define VTR_CMAP_H +#include + +namespace vtr { + +template +struct Color { + T r; + T g; + T b; +}; + + +class ColorMap { + public: + ColorMap(float min, float max, const std::vector>& color_data); + virtual ~ColorMap() = default; + Color color(float value) const; + float min() const; + float max() const; + float range() const; + private: + float min_; + float max_; + std::vector> color_data_; +}; + +class InfernoColorMap : public ColorMap { + public: + InfernoColorMap(float min, float max); +}; + +class PlasmaColorMap : public ColorMap { + public: + PlasmaColorMap(float min, float max); +}; + +class ViridisColorMap : public ColorMap { + public: + ViridisColorMap(float min, float max); +}; + +} //namespace +#endif diff --git a/libs/libvtrutil/src/vtr_digest.cpp b/libs/libvtrutil/src/vtr_digest.cpp new file mode 100644 index 000000000..f139c97de --- /dev/null +++ b/libs/libvtrutil/src/vtr_digest.cpp @@ -0,0 +1,39 @@ +#include "vtr_digest.h" +#include "vtr_error.h" + +#include +#include +#include + +#include "picosha2.h" + +namespace vtr { + +std::string secure_digest_file(const std::string& filepath) { + std::ifstream is(filepath); + if (!is) { + throw VtrError("Failed to open file", filepath); + } + return secure_digest_stream(is); +} + +std::string secure_digest_stream(std::istream& is) { + //Read the stream in chunks and calculate the SHA256 digest + picosha2::hash256_one_by_one hasher; + + std::array buf; + while(!is.eof()) { + //Process a chunk + is.read(buf.data(), buf.size()); + hasher.process(buf.begin(), buf.begin() + is.gcount()); + } + hasher.finish(); + + //Return the digest as a hex string, prefixed with the hash type + // + //Prefixing with the hash type should allow us to differentiate if the + //hash type is ever changed in the future + return "SHA256:" + picosha2::get_hash_hex_string(hasher); +} + +} //namespace diff --git a/libs/libvtrutil/src/vtr_digest.h b/libs/libvtrutil/src/vtr_digest.h new file mode 100644 index 000000000..8d58f8d55 --- /dev/null +++ b/libs/libvtrutil/src/vtr_digest.h @@ -0,0 +1,16 @@ +#ifndef VTR_DIGEST_H +#define VTR_DIGEST_H +#include +#include + +namespace vtr { + +//Generate a secure hash of the file at filepath +std::string secure_digest_file(const std::string& filepath); + +//Generate a secure hash of a stream +std::string secure_digest_stream(std::istream& is); + +} //namespace + +#endif diff --git a/libs/libvtrutil/src/vtr_error.h b/libs/libvtrutil/src/vtr_error.h new file mode 100644 index 000000000..d5853bcde --- /dev/null +++ b/libs/libvtrutil/src/vtr_error.h @@ -0,0 +1,32 @@ +#ifndef VTR_ERROR_H +#define VTR_ERROR_H + +#include +#include + +namespace vtr { + +class VtrError : public std::runtime_error { + public: + VtrError(std::string msg="", std::string new_filename="", size_t new_linenumber=-1) + : std::runtime_error(msg) + , filename_(new_filename) + , linenumber_(new_linenumber) {} + + //Returns the filename associated with this error + //returns an empty string if none is specified + std::string filename() const { return filename_; } + const char* filename_c_str() const { return filename_.c_str(); } + + //Returns the line number associated with this error + //returns zero if none is specified + size_t line() const { return linenumber_; } + + private: + std::string filename_; + size_t linenumber_; +}; + +} + +#endif diff --git a/libs/libvtrutil/src/vtr_flat_map.h b/libs/libvtrutil/src/vtr_flat_map.h new file mode 100644 index 000000000..c26a76913 --- /dev/null +++ b/libs/libvtrutil/src/vtr_flat_map.h @@ -0,0 +1,349 @@ +#ifndef VTR_FLAT_MAP +#define VTR_FLAT_MAP +#include +#include +#include +#include + +#include "vtr_assert.h" + +namespace vtr { + +//Forward declaration +template> +class flat_map; + +template> +class flat_map2; + +//Helper function to create a flat map from a vector of pairs +//without haveing to explicity specify the key and value types +template +flat_map make_flat_map(std::vector> vec) { + return flat_map(vec); +} + +// +// flat_map is a (nearly) std::map compatible container which uses a vector +// as it's underlying storage. Internally the stored elements are kept sorted +// allowing efficient look-up in O(logN) time via binary search. +// +// This container is typically useful in the following scenarios: +// * Reduced memory usage if key/value are small (std::map needs to store pointers to +// other BST nodes which can add substantial overhead for small keys/values) +// * Faster search/iteration by exploiting data locality (all elments are in continguous +// memory enabling better spatial locality) +// +// The container deviates from the behaviour of std::map in the following important ways: +// * Insertion/erase takes O(N) instead of O(logN) time +// * Iterators may be invalidated on insertion/erase (i.e. if the vector is reallocated) +// +// The slow insertion/erase performance makes this container poorly suited to maps that +// frequently add/remove new keys. If this is required you likely want std::map or +// std::unordered_map. However if the map is constructed once and then repeatedly quieried, +// consider using the range or vector-based constructors which initializes the flat_map in +// O(NlogN) time. +// +template +class flat_map { + public: + typedef K key_type; + typedef T mapped_type; + typedef std::pair value_type; + typedef Compare key_compare; + typedef value_type& reference; + typedef const value_type& const_reference; + typedef typename std::vector::iterator iterator; + typedef typename std::vector::const_iterator const_iterator; + typedef typename std::vector::reverse_iterator reverse_iterator; + typedef typename std::vector::const_reverse_iterator const_reverse_iterator; + typedef typename std::vector::difference_type difference_type; + typedef typename std::vector::size_type size_type; + + class value_compare; + + public: + //Standard big 5 + flat_map() = default; + flat_map(const flat_map&) = default; + flat_map(flat_map&&) = default; + flat_map& operator=(const flat_map&) = default; + flat_map& operator=(flat_map&&) = default; + + //range constructor + template + flat_map(InputIterator first, InputIterator last) { + //Copy the values + std::copy(first, last, std::back_inserter(vec_)); + + sort(); + uniquify(); + } + + //direct vector constructor + flat_map(std::vector values) { + //By moving the values this should be more efficient + //than the range constructor which must copy each element + vec_ = std::move(values); + + sort(); + uniquify(); + } + + iterator begin() { return vec_.begin(); } + const_iterator begin() const { return vec_.begin(); } + iterator end() { return vec_.end(); } + const_iterator end() const { return vec_.end(); } + reverse_iterator rbegin() { return vec_.rbegin(); } + const_reverse_iterator rbegin() const { return vec_.rbegin(); } + reverse_iterator rend() { return vec_.rend(); } + const_reverse_iterator rend() const { return vec_.rend(); } + const_iterator cbegin() const { return vec_.begin(); } + const_iterator cend() const { return vec_.end(); } + const_reverse_iterator crbegin() const { return vec_.rbegin(); } + const_reverse_iterator crend() const { return vec_.rend(); } + + bool empty() const { return vec_.empty(); } + size_type size() const { return vec_.size(); } + size_type max_size() const { return vec_.max_size(); } + + const mapped_type& operator[](const key_type& key) const { + auto iter = find(key); + if(iter == end()) { + //Not found + throw std::out_of_range("Invalid key"); + } + + return iter->second; + } + + mapped_type& operator[](const key_type& key) { + auto iter = find(key); + if(iter == end()) { + //Not found + iter = insert(std::make_pair(key,mapped_type())).first; + } + + return iter->second; + } + + mapped_type& at(const key_type& key) { + return const_cast(const_cast(this)->at(key)); + } + + const mapped_type& at(const key_type& key) const { + auto iter = find(key); + if(iter == end()) { + throw std::out_of_range("Invalid key"); + } + return iter->second; + } + + //Insert value + std::pair insert(const value_type& value) { + auto iter = lower_bound(value.first); + if(iter != end() && keys_equivalent(iter->first, value.first)) { + //Found existing + return std::make_pair(iter, false); + } else { + //Insert + iter = insert(iter, value); + + return std::make_pair(iter, true); + } + } + + //Insert value with position hint + iterator insert(const_iterator position, const value_type& value) { + //In a legal position + VTR_ASSERT(position == begin() || value_comp()(*(position - 1), value)); + VTR_ASSERT(position == --end() || position == end() || !value_comp()(*(position + 1), value)); + + iterator iter = vec_.insert(position, value); + + return iter; + } + + //Insert range + template + void insert(InputIterator first, InputIterator last) { + vec_.insert(vec_.end(), first, last); + + //TODO: could be more efficient + sort(); + uniquify(); + } + + //Erase by key + void erase(const key_type& key) { + auto iter = find(key); + if(iter != end()) { + vec_.erase(iter); + } + } + + //Erase at iterator + void erase(const_iterator position) { + vec_.erase(position); + } + + //Erase range + void erase(const_iterator first, const_iterator last) { + vec_.erase(first,last); + } + + void swap(flat_map& other) { std::swap(*this, other); } + + void clear() { vec_.clear(); } + + template + iterator emplace(const key_type& key, Args&&... args) { + auto iter = lower_bound(key); + if(iter != end() && keys_equivalent(iter->first, key)) { + //Found + return std::make_pair(iter, false); + } else { + //Emplace + iter = emplace_hint(iter, key, std::forward(args)...); + return std::make_pair(iter, true); + } + } + + template + iterator emplace_hint(const_iterator position, Args&&... args) { + return vec_.emplace(position, std::forward(args)...); + } + + void reserve(size_type n) { vec_.reserve(n); } + void shrink_to_fit() { vec_.shrink_to_fit(); } + + key_compare key_comp() const { return key_compare(); } + value_compare value_comp() const { return value_compare(key_comp()); } + + iterator find(const key_type& key) { + const_iterator const_iter = const_cast(this)->find(key); + return convert_to_iterator(const_iter); + } + + const_iterator find(const key_type& key) const { + auto iter = lower_bound(key); + if(iter != end() && keys_equivalent(iter->first, key)) { + //Found + return iter; + } + return end(); + } + + size_type count(const key_type& key) const { + return (find(key) == end()) ? 0 : 1; + } + + iterator lower_bound(const key_type& key) { + const_iterator const_iter = const_cast(this)->lower_bound(key); + return convert_to_iterator(const_iter); + } + + const_iterator lower_bound(const key_type& key) const { + return std::lower_bound(begin(), end(), key, value_comp()); + } + + iterator upper_bound(const key_type& key) { + const_iterator const_iter = const_cast(this)->upper_bound(key); + return convert_to_iterator(const_iter); + } + + const_iterator upper_bound(const key_type& key) const { + return std::upper_bound(begin(), end(), key, key_compare()); + } + + std::pair equal_range(const key_type& key) { + auto const_iter_pair = const_cast(this)->equal_range(key); + return std::pair(iterator(const_iter_pair.first), iterator(const_iter_pair.second)); + } + + std::pair equal_range(const key_type& key) const { + return std::equal_range(begin(), end(), key); + } + + public: + friend void swap(flat_map& lhs, flat_map& rhs) { std::swap(lhs.vec_, rhs.vec_); } + + private: + bool keys_equivalent(const key_type& lhs, const key_type& rhs) const { + return !key_comp()(lhs, rhs) && !key_comp()(rhs, lhs); + } + + void sort() { + std::sort(vec_.begin(), vec_.end(), value_comp()); + } + + void uniquify() { + //Uniquify + auto key_equal_pred = [this](const value_type& lhs, const value_type& rhs) { + return !value_comp()(lhs, rhs) && !value_comp()(rhs, lhs); + }; + vec_.erase(std::unique(vec_.begin(), vec_.end(), key_equal_pred), vec_.end()); + } + + iterator convert_to_iterator(const_iterator const_iter) { + //This is a work around for the fact that there is no conversion between + //a const_iterator and iterator. + // + //We intiailize i to the start of the container and then advance it by + //the distance to const_iter. The resulting i points to the same element + //as const_iter + // + //Note that to be able to call std::distance with an iterator and + //const_iterator we need to specify the type as const_iterator (relying + //on the implicit conversion from iterator to const_iterator for i) + // + //Since the iterators are really vector (i.e. random-access) iterators + //this takes constant time + iterator i = begin(); + std::advance(i, std::distance(i, const_iter)); + return i; + } + + private: + std::vector vec_; +}; + +//Like flat_map, but operator[] never inserts and directly returns the mapped value +template +class flat_map2 : public flat_map { + public: + + const T& operator[](const K& key) const { + auto itr = this->find(key); + if (itr == this->end()) { + throw std::logic_error("Key not found"); + } + return itr->second; + } + + T& operator[](const K& key) { + return const_cast(const_cast(this)->operator[](key)); + } +}; + +template +class flat_map::value_compare { + friend class flat_map; + public: + + bool operator() (const value_type& x, const value_type& y) const { + return comp(x.first, y.first); + } + + //For std::lower_bound/std::upper_bound + bool operator() (const value_type& x, const key_type& y) const { + return comp(x.first, y); + } + private: + value_compare(Compare c) : comp(c) {} + + Compare comp; +}; + +} //namespace +#endif diff --git a/libs/libvtrutil/src/vtr_geometry.h b/libs/libvtrutil/src/vtr_geometry.h new file mode 100644 index 000000000..2ff45e6d0 --- /dev/null +++ b/libs/libvtrutil/src/vtr_geometry.h @@ -0,0 +1,167 @@ +#ifndef VTR_GEOMETRY_H +#define VTR_GEOMETRY_H + +#include +#include +#include + +#include "vtr_range.h" + +namespace vtr { + +/* + * Forward declarations + */ +template +class Point; + +template +class Rect; + +template +class Line; + +template +class RectUnion; + +template +bool operator==(Point lhs, Point rhs); +template +bool operator!=(Point lhs, Point rhs); +template +bool operator<(Point lhs, Point rhs); + +template +bool operator==(const Rect& lhs, const Rect& rhs); +template +bool operator!=(const Rect& lhs, const Rect& rhs); + +template +bool operator==(const RectUnion& lhs, const RectUnion& rhs); +template +bool operator!=(const RectUnion& lhs, const RectUnion& rhs); +/* + * Class Definitions + */ + +//A point in 2D space +template +class Point { + public: //Constructors + Point(T x_val, T y_val); + + public: //Accessors + + //Coordinates + T x() const; + T y() const; + + friend bool operator== <>(Point lhs, Point rhs); + friend bool operator!= <>(Point lhs, Point rhs); + friend bool operator< <>(Point lhs, Point rhs); + private: + T x_; + T y_; +}; + +//A 2D rectangle +template +class Rect { + public: //Constructors + Rect(T left_val, T bottom_val, T right_val, T top_val); + Rect(Point bottom_left_val, Point top_right_val); + Rect(); /* For RRGraph obj */ + + public: //Accessors + //Co-ordinates + T xmin() const; + T xmax() const; + T ymin() const; + T ymax() const; + + Point bottom_left() const; + Point top_right() const; + + T width() const; + T height() const; + + //Returns true if the point is fully contained within the rectangle (excluding the top-right edges) + bool contains(Point point) const; + + //Returns true if the point is strictly contained within the region (excluding all edges) + bool strictly_contains(Point point) const; + + //Returns true if the point is coincident with the rectangle (including the top-right edges) + bool coincident(Point point) const; + + friend bool operator== <>(const Rect& lhs, const Rect& rhs); + friend bool operator!= <>(const Rect& lhs, const Rect& rhs); + + private: + Point bottom_left_; + Point top_right_; +}; + +//A 2D line +template +class Line { + public: //Types + typedef typename std::vector>::const_iterator point_iter; + typedef vtr::Range point_range; + + public: //Constructors + Line(std::vector> line_points); + + public: //Accessors + + //Returns the bounding box + Rect bounding_box() const; + + //Returns a range of constituent points + point_range points() const; + + private: + std::vector> points_; +}; + +//A union of 2d rectangles +template +class RectUnion { + public: //Types + typedef typename std::vector>::const_iterator rect_iter; + typedef vtr::Range rect_range; + + public: //Constructors + + //Construct from a set of rectangles + RectUnion(std::vector> rects); + + public: //Accessors + //Returns the bounding box of all rectangles in the union + Rect bounding_box() const; + + //Returns true if the point is fully contained within the region (excluding top-right edges) + bool contains(Point point) const; + + //Returns true if the point is strictly contained within the region (excluding all edges) + bool strictly_contains(Point point) const; + + //Returns true if the point is coincident with the region (including the top-right edges) + bool coincident(Point point) const; + + //Returns a range of all constituent rectangles + rect_range rects() const; + + //Checks whether two RectUnions have identical representations + // Note: does not check whether the representations they are equivalent + friend bool operator== <>(const RectUnion& lhs, const RectUnion& rhs); + friend bool operator!= <>(const RectUnion& lhs, const RectUnion& rhs); + private: + //Note that a union of rectanges may have holes and may not be contiguous + std::vector> rects_; +}; + +} //namespace + +#include "vtr_geometry.tpp" +#endif diff --git a/libs/libvtrutil/src/vtr_geometry.tpp b/libs/libvtrutil/src/vtr_geometry.tpp new file mode 100644 index 000000000..658f564fe --- /dev/null +++ b/libs/libvtrutil/src/vtr_geometry.tpp @@ -0,0 +1,248 @@ +namespace vtr { + /* + * Point + */ + + template + Point::Point(T x_val, T y_val) + : x_(x_val) + , y_(y_val) { + //pass + } + + template + T Point::x() const { + return x_; + } + + template + T Point::y() const { + return y_; + } + + template + bool operator==(Point lhs, Point rhs) { + return lhs.x() == rhs.x() + && lhs.y() == rhs.y(); + } + + template + bool operator!=(Point lhs, Point rhs) { + return !(lhs == rhs); + } + + template + bool operator<(Point lhs, Point rhs) { + return std::make_tuple(lhs.x(), lhs.y()) < std::make_tuple(rhs.x(), rhs.y()); + } + + /* + * Rect + */ + template + Rect::Rect(T left_val, T bottom_val, T right_val, T top_val) + : Rect(Point(left_val, bottom_val), Point(right_val, top_val)) { + //pass + } + + template + Rect::Rect(Point bottom_left_val, Point top_right_val) + : bottom_left_(bottom_left_val) + , top_right_(top_right_val) { + //pass + } + + /* A new contructor which initialize everything to be zero*/ + template + Rect::Rect() + : Rect(Point(0, 0), Point(0, 0)) { + //pass + } + + template + T Rect::xmin() const { + return bottom_left_.x(); + } + + template + T Rect::xmax() const { + return top_right_.x(); + } + + template + T Rect::ymin() const { + return bottom_left_.y(); + } + + template + T Rect::ymax() const { + return top_right_.y(); + } + + template + Point Rect::bottom_left() const { + return bottom_left_; + } + + template + Point Rect::top_right() const { + return top_right_; + } + + template + T Rect::width() const { + return xmax() - xmin(); + } + + template + T Rect::height() const { + return ymax() - ymin(); + } + + template + bool Rect::contains(Point point) const { + //Up-to but not including right or top edges + return point.x() >= xmin() && point.x() < xmax() + && point.y() >= ymin() && point.y() < ymax(); + } + + template + bool Rect::strictly_contains(Point point) const { + //Excluding edges + return point.x() > xmin() && point.x() < xmax() + && point.y() > ymin() && point.y() < ymax(); + } + + template + bool Rect::coincident(Point point) const { + //Including right or top edges + return point.x() >= xmin() && point.x() <= xmax() + && point.y() >= ymin() && point.y() <= ymax(); + } + + template + bool operator==(const Rect& lhs, const Rect& rhs) { + return lhs.bottom_left() == rhs.bottom_left() + && lhs.top_right() == rhs.top_right(); + } + + template + bool operator!=(const Rect& lhs, const Rect& rhs) { + return !(lhs == rhs); + } + + /* + * Line + */ + template + Line::Line(std::vector> line_points) + : points_(line_points) { + //pass + } + + template + Rect Line::bounding_box() const { + T xmin = std::numeric_limits::max(); + T ymin = std::numeric_limits::max(); + T xmax = std::numeric_limits::min(); + T ymax = std::numeric_limits::min(); + + for (const auto& point : points()) { + xmin = std::min(xmin, point.x()); + ymin = std::min(ymin, point.y()); + xmax = std::max(xmax, point.x()); + ymax = std::max(ymax, point.y()); + } + + return Rect(xmin, ymin, xmax, ymax); + } + + template + typename Line::point_range Line::points() const { + return vtr::make_range(points_.begin(), points_.end()); + } + + /* + * RectUnion + */ + template + RectUnion::RectUnion(std::vector> rectangles) + : rects_(rectangles) { + //pass + } + + template + Rect RectUnion::bounding_box() const { + T xmin = std::numeric_limits::max(); + T ymin = std::numeric_limits::max(); + T xmax = std::numeric_limits::min(); + T ymax = std::numeric_limits::min(); + + for (const auto& rect : rects_) { + xmin = std::min(xmin, rect.xmin()); + ymin = std::min(ymin, rect.ymin()); + xmax = std::max(xmax, rect.xmax()); + ymax = std::max(ymax, rect.ymax()); + } + + return Rect(xmin, ymin, xmax, ymax); + } + + template + bool RectUnion::contains(Point point) const { + for(const auto& rect : rects()) { + if (rect.contains(point)) { + return true; + } + } + return false; + } + + template + bool RectUnion::strictly_contains(Point point) const { + for(const auto& rect : rects()) { + if (rect.strictly_contains(point)) { + return true; + } + } + return false; + } + + template + bool RectUnion::coincident(Point point) const { + for(const auto& rect : rects()) { + if (rect.coincident(point)) { + return true; + } + } + return false; + } + + template + typename RectUnion::rect_range RectUnion::rects() const { + return vtr::make_range(rects_.begin(), rects_.end()); + } + + template + bool operator==(const RectUnion& lhs, const RectUnion& rhs) { + //Currently checks for an identical *representation* (not whether the + //representations are equivalent) + + if (lhs.rects_.size() != rhs.rects_.size()) { + return false; + } + + for (size_t i = 0; i < lhs.rects_.size(); ++i) { + if (lhs.rects_[i] != rhs.rects_[i]) { + return false; + } + } + + return true; + } + + template + bool operator!=(const RectUnion& lhs, const RectUnion& rhs) { + return !(lhs == rhs); + } +} //namespace diff --git a/libs/libvtrutil/src/vtr_hash.h b/libs/libvtrutil/src/vtr_hash.h new file mode 100644 index 000000000..7385d1b6d --- /dev/null +++ b/libs/libvtrutil/src/vtr_hash.h @@ -0,0 +1,19 @@ +#ifndef VTR_HASH_H +#define VTR_HASH_H +#include + +namespace vtr { + +//Hashes v and combines it with seed (as in boost) +// +//This is typically used to implement std::hash for composite types. +template +inline void hash_combine(std::size_t& seed, const T& v) +{ + std::hash hasher; + seed ^= hasher(v) + 0x9e3779b9 + (seed<<6) + (seed>>2); +} + +} //namespace + +#endif diff --git a/libs/libvtrutil/src/vtr_linear_map.h b/libs/libvtrutil/src/vtr_linear_map.h new file mode 100644 index 000000000..7ae6c9a70 --- /dev/null +++ b/libs/libvtrutil/src/vtr_linear_map.h @@ -0,0 +1,263 @@ +#ifndef VTR_LINEAR_MAP_H +#define VTR_LINEAR_MAP_H +#include +#include + +#include "vtr_sentinels.h" + +namespace vtr { + +//A std::map-like container which is indexed by K +// +//The main use of this container is to behave like a std::map which is optimized to hold +//mappings between a dense linear range of keys (e.g. vtr::StrongId). +// +//Requires that K be convertable to size_t with the size_t operator (i.e. size_t()), and +//that the conversion results in a linearly increasing index into the underlying vector. +//Also requires that K() return the sentinel value used to mark invalid entries. +// +//If you only need to access the value associated with the key consider using vtr::vector_map +//instead, which provides a similar but more std::vector-like interface. +// +//Note that it is possible to use linear_map with sparse/non-contiguous keys, but this is typically +//memory inefficient as the underlying vector will allocate space for [0..size_t(max_key)-1], +//where max_key is the largest key that has been inserted. +// +//As with a std::vector, it is the caller's responsibility to ensure there is sufficient space +//when a given index/key before it is accessed. The exception to this are the find() and insert() +//methods which handle non-existing keys gracefully. +template> +class linear_map { + public: + typedef K key_type; + typedef T mapped_type; + typedef std::pair value_type; + typedef value_type& reference; + typedef const value_type& const_reference; + typedef typename std::vector::iterator iterator; + typedef typename std::vector::const_iterator const_iterator; + typedef typename std::vector::reverse_iterator reverse_iterator; + typedef typename std::vector::const_reverse_iterator const_reverse_iterator; + typedef typename std::vector::difference_type difference_type; + typedef typename std::vector::size_type size_type; + + public: + //Standard big 5 + linear_map() = default; + linear_map(const linear_map&) = default; + linear_map(linear_map&&) = default; + linear_map& operator=(const linear_map&) = default; + linear_map& operator=(linear_map&&) = default; + + linear_map(size_t num_keys) + : vec_(num_keys, std::make_pair(sentinel(), T())) //Initialize all with sentinel values + {} + + iterator begin() { return vec_.begin(); } + const_iterator begin() const { return vec_.begin(); } + iterator end() { return vec_.end(); } + const_iterator end() const { return vec_.end(); } + reverse_iterator rbegin() { return vec_.rbegin(); } + const_reverse_iterator rbegin() const { return vec_.rbegin(); } + reverse_iterator rend() { return vec_.rend(); } + const_reverse_iterator rend() const { return vec_.rend(); } + const_iterator cbegin() const { return vec_.begin(); } + const_iterator cend() const { return vec_.end(); } + const_reverse_iterator crbegin() const { return vec_.rbegin(); } + const_reverse_iterator crend() const { return vec_.rend(); } + + bool empty() const { return vec_.empty(); } + size_type size() const { return vec_.size(); } + size_type max_size() const { return vec_.max_size(); } + + mapped_type& operator[](const key_type& key) { + auto iter = find(key); + if(iter == end()) { + //Not found, create it + iter = insert(std::make_pair(key,mapped_type())).first; + } + + return iter->second; + } + + mapped_type& at(const key_type& key) { + return const_cast(const_cast(this)->at(key)); + } + + const mapped_type& at(const key_type& key) const { + auto iter = find(key); + if(iter == end()) { + throw std::out_of_range("Invalid key"); + } + return iter->second; + } + + //Insert value + std::pair insert(const value_type& value) { + auto iter = find(value.first); + if(iter != end()) { + //Found existing + return std::make_pair(iter, false); + } else { + //Insert + size_t index = size_t(value.first); + + if(index >= vec_.size()) { + //Make space, initialize empty slots with sentinel values + vec_.resize(index + 1, std::make_pair(sentinel(), T())); + } + + vec_[index] = value; + + return std::make_pair(vec_.begin() + index, true); + } + } + + //Insert range + template + void insert(InputIterator first, InputIterator last) { + for(InputIterator iter = first; iter != last; ++iter) { + insert(*iter); + } + } + + //Erase by key + void erase(const key_type& key) { + auto iter = find(key); + if(iter != end()) { + erase(iter); + } + } + + //Erase at iterator + void erase(const_iterator position) { + iterator pos = convert_to_iterator(position); + pos->first = sentinel(); //Mark invalid + } + + //Erase range + void erase(const_iterator first, const_iterator last) { + for(auto iter = first; iter != last; ++iter) { + erase(iter); + } + } + + void swap(linear_map& other) { std::swap(vec_, other.vec_); } + + void clear() { vec_.clear(); } + + template + std::pair emplace(const key_type& key, Args&&... args) { + auto iter = find(key); + if(iter != end()) { + //Found + return std::make_pair(iter, false); + } else { + //Emplace + size_t index = size_t(key); + + if(index >= vec_.size()) { + //Make space, initialize empty slots with sentinel values + vec_.resize(index + 1, value_type(sentinel(), T())); + } + + vec_[index] = value_type(key, std::forward(args)...); + + return std::make_pair(vec_.begin() + index, true); + } + } + + void reserve(size_type n) { vec_.reserve(n); } + void shrink_to_fit() { vec_.shrink_to_fit(); } + + iterator find(const key_type& key) { + const_iterator const_iter = const_cast(this)->find(key); + return convert_to_iterator(const_iter); + } + + const_iterator find(const key_type& key) const { + size_t index = size_t(key); + + if(index < vec_.size() && vec_[index].first != sentinel()) { + return vec_.begin() + index; + } + return end(); + } + + size_type count(const key_type& key) const { + return (find(key) == end()) ? 0 : 1; + } + + iterator lower_bound(const key_type& key) { + const_iterator const_iter = const_cast(this)->lower_bound(key); + return convert_to_iterator(const_iter); + } + + const_iterator lower_bound(const key_type& key) const { + return find(key); + } + + iterator upper_bound(const key_type& key) { + const_iterator const_iter = const_cast(this)->upper_bound(key); + return convert_to_iterator(const_iter); + } + + const_iterator upper_bound(const key_type& key) const { + auto iter = find(key); + return (iter != end()) ? iter + 1 : end(); + } + + std::pair equal_range(const key_type& key) { + auto const_iter_pair = const_cast(this)->equal_range(key); + return std::pair(iterator(const_iter_pair.first), iterator(const_iter_pair.second)); + } + + std::pair equal_range(const key_type& key) const { + auto lb_iter = lower_bound(key); + auto ub_iter = upper_bound(key); + return (lb_iter != end()) ? std::make_pair(lb_iter, ub_iter) : std::make_pair(ub_iter, ub_iter); + } + + size_type valid_size() const { + size_t valid_cnt = 0; + for(const auto& kv : vec_) { + if(kv.first != sentinel()) { + ++valid_cnt; + } + } + return valid_cnt; + } + + public: + friend void swap(linear_map& lhs, linear_map& rhs) { + std::swap(lhs.vec_, rhs.vec_); + } + + private: + iterator convert_to_iterator(const_iterator const_iter) { + //This is a work around for the fact that there is no conversion between + //a const_iterator and iterator. + // + //We intiailize i to the start of the container and then advance it by + //the distance to const_iter. The resulting i points to the same element + //as const_iter + // + //Note that to be able to call std::distance with an iterator and + //const_iterator we need to specify the type as const_iterator (relying + //on the implicit conversion from iterator to const_iterator for i) + // + //Since the iterators are really vector (i.e. random-access) iterators + //both distance and advance take constant time + iterator i = begin(); + std::advance(i, std::distance(i, const_iter)); + return i; + } + + constexpr K sentinel() const { return Sentinel::INVALID(); } + + private: + std::vector vec_; +}; + +} //namespace +#endif diff --git a/libs/libvtrutil/src/vtr_list.cpp b/libs/libvtrutil/src/vtr_list.cpp new file mode 100644 index 000000000..f64896974 --- /dev/null +++ b/libs/libvtrutil/src/vtr_list.cpp @@ -0,0 +1,34 @@ +#include + +#include "vtr_list.h" +#include "vtr_memory.h" + +namespace vtr { + +t_linked_vptr *insert_in_vptr_list(t_linked_vptr *head, void *vptr_to_add) { + + /* Inserts a new element at the head of a linked list of void pointers. * + * Returns the new head of the list. */ + + t_linked_vptr *linked_vptr; + + linked_vptr = (t_linked_vptr *) vtr::malloc(sizeof(t_linked_vptr)); + + linked_vptr->data_vptr = vptr_to_add; + linked_vptr->next = head; + return (linked_vptr); /* New head of the list */ +} + +/* Deletes the element at the head of a linked list of void pointers. * + * Returns the new head of the list. */ +t_linked_vptr *delete_in_vptr_list(t_linked_vptr *head) { + t_linked_vptr *linked_vptr; + + if (head == nullptr ) + return nullptr ; + linked_vptr = head->next; + free(head); + return linked_vptr; /* New head of the list */ +} + +} //namespace diff --git a/libs/libvtrutil/src/vtr_list.h b/libs/libvtrutil/src/vtr_list.h new file mode 100644 index 000000000..4ca8b1984 --- /dev/null +++ b/libs/libvtrutil/src/vtr_list.h @@ -0,0 +1,16 @@ +#ifndef VTR_LIST_H +#define VTR_LIST_H + +namespace vtr { + /* Linked lists of void pointers and integers, respectively. */ + + struct t_linked_vptr { + void *data_vptr; + struct t_linked_vptr *next; + }; + + t_linked_vptr *insert_in_vptr_list(t_linked_vptr *head, + void *vptr_to_add); + t_linked_vptr *delete_in_vptr_list(t_linked_vptr *head); +} +#endif diff --git a/libs/libvtrutil/src/vtr_log.cpp b/libs/libvtrutil/src/vtr_log.cpp new file mode 100644 index 000000000..8fb82df9c --- /dev/null +++ b/libs/libvtrutil/src/vtr_log.cpp @@ -0,0 +1,17 @@ +#include "vtr_log.h" + +#include "log.h" + +namespace vtr { +PrintHandlerInfo printf = log_print_info; +PrintHandlerInfo printf_info = log_print_info; +PrintHandlerWarning printf_warning = log_print_warning; +PrintHandlerError printf_error = log_print_error; +PrintHandlerDirect printf_direct = log_print_direct; + + +void set_log_file(const char* filename) { + log_set_output_file(filename); +} + +} //namespace diff --git a/libs/libvtrutil/src/vtr_log.h b/libs/libvtrutil/src/vtr_log.h new file mode 100644 index 000000000..c39cf56a4 --- /dev/null +++ b/libs/libvtrutil/src/vtr_log.h @@ -0,0 +1,133 @@ +#ifndef VTR_LOG_H +#define VTR_LOG_H +#include + +/* + * This header defines useful logging macros for VTR projects. + * + * Message Type + * ============ + * + * Three types of log message types are defined: + * VTR_LOG - The standard 'info' type log message + * VTR_LOG_WARN - A warning log message + * VTR_LOG_ERROR - An error log message + * + * For example: + * + * VTR_LOG("This produces a regular '%s' message\n", "info"); + * VTR_LOG_WARN("This produces a '%s' message\n", "warning"); + * VTR_LOG_ERROR("This produces an '%s' message\n", "error"); + * + * Conditional Logging + * =================== + * + * Each of the three message types also have a VTR_LOGV_* variant, + * which will cause the message to be logged if a user-defined condition + * is satisifed. + * + * For example: + * + * VTR_LOGV(verbosity > 5, "This message will be logged only if verbosity is greater than %d\n", 5); + * VTR_LOGV_WARN(verbose, "This warning message will be logged if verbose is true\n"); + * VTR_LOGV_ERROR(false, "This error message will never be logged\n"); + * + * Custom Location Logging + * ======================= + * + * Each of the three message types also have a VTR_LOGF_* variant, + * which will cause the message to be logged for a custom file and + * line loation. + * + * For example: + * + * VTR_LOGF("my_file.txt", 42 "This message will be logged from file 'my_file.txt' line %d\n", 42); + * + * Debug Logging + * ============= + * + * For debug purposes it may be useful to have additional logging. + * This is supported by VTR_LOG_DEBUG() and VTR_LOGV_DEBUG(). + * + * To avoid run-time overhead, these are only enabled if VTR_ENABLE_DEBUG_LOGGING + * is defined (disabled by default). + */ + +//Unconditional logging macros +#define VTR_LOG(...) VTR_LOGV(true, __VA_ARGS__) +#define VTR_LOG_WARN(...) VTR_LOGV_WARN(true, __VA_ARGS__) +#define VTR_LOG_ERROR(...) VTR_LOGV_ERROR(true, __VA_ARGS__) +#define VTR_LOG_NOP(...) VTR_LOGV_NOP(true, __VA_ARGS__) + +//Conditional logging macros +#define VTR_LOGV(expr, ...) VTR_LOGVF(expr, __FILE__, __LINE__, __VA_ARGS__) +#define VTR_LOGV_WARN(expr, ...) VTR_LOGVF_WARN(expr, __FILE__, __LINE__, __VA_ARGS__) +#define VTR_LOGV_ERROR(expr, ...) VTR_LOGVF_ERROR(expr, __FILE__, __LINE__, __VA_ARGS__) +#define VTR_LOGV_NOP(expr, ...) VTR_LOGVF_NOP(expr, __FILE__, __LINE__, __VA_ARGS__) + +//Custom file-line location logging macros +#define VTR_LOGF(file, line, ...) VTR_LOGVF(true, file, line, __VA_ARGS__) +#define VTR_LOGF_WARN(file, line, ...) VTR_LOGVF_WARN(true, file, line, __VA_ARGS__) +#define VTR_LOGF_ERROR(file, line, ...) VTR_LOGVF_ERROR(true, file, line, __VA_ARGS__) +#define VTR_LOGF_NOP(file, line, ...) VTR_LOGVF_NOP(true, file, line, __VA_ARGS__) + +//Conditional logging and custom file-line location macros +#define VTR_LOGVF(expr, file, line, ...) \ + do { \ + if (expr) vtr::printf(__VA_ARGS__); \ + } while (false) + +#define VTR_LOGVF_WARN(expr, file, line, ...) \ + do { \ + if (expr) vtr::printf_warning(file, line, __VA_ARGS__); \ + } while (false) + +#define VTR_LOGVF_ERROR(expr, file, line, ...) \ + do { \ + if (expr) vtr::printf_error(file, line, __VA_ARGS__); \ + } while (false) + +//No-op version of logging macro which avoids unused parameter warnings. +// +//Note that to avoid unused parameter warnings we call sizeof() and cast +//the result to void. sizeof is evaluated at compile time so there is no +//run-time overhead. +// +//Also note the use of std::make_tuple to ensure all arguments in VA_ARGS +//are used. +#define VTR_LOGVF_NOP(expr, file, line, ...) \ + do { \ + static_cast(sizeof(expr)); \ + static_cast(sizeof(file)); \ + static_cast(sizeof(line)); \ + static_cast(sizeof(std::make_tuple(__VA_ARGS__))); \ + } while (false) + +//Debug logging macros +#ifdef VTR_ENABLE_DEBUG_LOGGING //Enable +# define VTR_LOG_DEBUG(...) VTR_LOG(__VA_ARGS__) +# define VTR_LOGV_DEBUG(expr, ...) VTR_LOGV(expr, __VA_ARGS__) +#else //Disable +# define VTR_LOG_DEBUG(...) VTR_LOG_NOP(__VA_ARGS__) +# define VTR_LOGV_DEBUG(expr, ...) VTR_LOGV_NOP(expr, __VA_ARGS__) +#endif + +namespace vtr { + +typedef void (*PrintHandlerInfo)(const char* pszMessage, ... ); +typedef void (*PrintHandlerWarning)(const char* pszFileName, unsigned int lineNum, const char* pszMessage, ... ); +typedef void (*PrintHandlerError)(const char* pszFileName, unsigned int lineNum, const char* pszMessage, ... ); +typedef void (*PrintHandlerDirect)(const char* pszMessage, ... ); + +extern PrintHandlerInfo printf; //Same as printf_info +extern PrintHandlerInfo printf_info; +extern PrintHandlerWarning printf_warning; +extern PrintHandlerError printf_error; +extern PrintHandlerDirect printf_direct; + +void set_log_file(const char* filename); + +} //namespace + + +#endif diff --git a/libs/libvtrutil/src/vtr_logic.h b/libs/libvtrutil/src/vtr_logic.h new file mode 100644 index 000000000..bab170d12 --- /dev/null +++ b/libs/libvtrutil/src/vtr_logic.h @@ -0,0 +1,15 @@ +#ifndef VTR_LOGIC_H +#define VTR_LOGIC_H + +namespace vtr { + + enum class LogicValue { + FALSE = 0, + TRUE = 1, + DONT_CARE = 2, + UNKOWN = 3 + }; + +} //namespace + +#endif diff --git a/libs/libvtrutil/src/vtr_map_util.h b/libs/libvtrutil/src/vtr_map_util.h new file mode 100644 index 000000000..ec4e566e8 --- /dev/null +++ b/libs/libvtrutil/src/vtr_map_util.h @@ -0,0 +1,45 @@ +#ifndef VTR_MAP_UTIL_H +#define VTR_MAP_UTIL_H + +#include "vtr_pair_util.h" +#include "vtr_range.h" + +namespace vtr { + +//An iterator who wraps a std::map iterator to return it's key +template +using map_key_iter = pair_first_iter; + +//An iterator who wraps a std::map iterator to return it's value +template +using map_value_iter = pair_second_iter; + +//Returns a range iterating over a std::map's keys +template +auto make_key_range(T b, T e) { + using key_iter = map_key_iter; + return vtr::make_range(key_iter(b), key_iter(e)); +} + +//Returns a range iterating over a std::map's keys +template +auto make_key_range(const Container& c) { + return make_key_range(std::begin(c), std::end(c)); +} + +//Returns a range iterating over a std::map's values +template +auto make_value_range(T b, T e) { + using value_iter = map_value_iter; + return vtr::make_range(value_iter(b), value_iter(e)); +} + +//Returns a range iterating over a std::map's values +template +auto make_value_range(const Container& c) { + return make_value_range(std::begin(c), std::end(c)); +} + +} //namespace + +#endif diff --git a/libs/libvtrutil/src/vtr_math.cpp b/libs/libvtrutil/src/vtr_math.cpp new file mode 100644 index 000000000..71c39c090 --- /dev/null +++ b/libs/libvtrutil/src/vtr_math.cpp @@ -0,0 +1,86 @@ +#include + +#include "vtr_assert.h" +#include "vtr_error.h" +#include "vtr_math.h" + +namespace vtr { + +int ipow(int base, int exp) { + int result = 1; + + VTR_ASSERT(exp >= 0); + + while (exp) { + if (exp & 1) + result *= base; + exp >>= 1; + base *= base; + } + return result; +} + +/* Performs linear interpolation or extrapolation on the set of (x,y) values specified by the xy_map. + A requested x value is passed in, and we return the interpolated/extrapolated y value at this requested value of x. + Meant for maps where both key and element are numbers. + This is specifically enforced by the explicit instantiations below this function. i.e. only templates + using those types listed in the explicit instantiations below are allowed */ +template Y linear_interpolate_or_extrapolate(const std::map *xy_map, X requested_x){ + Y result; + + /* the intention of this function is to interpolate/extrapolate. we can't do so with less than 2 values in the xy_map */ + if (xy_map->size() < 2){ + throw VtrError("linear_interpolate_or_extrapolate: cannot interpolate/extrapolate based on less than 2 (x,y) pairs", __FILE__, __LINE__); + } + + auto itr = xy_map->find(requested_x); + if (itr != xy_map->end()){ + /* requested x already exists in the x,y map */ + result = itr->second; + } else { + /* requested x does not exist in the x,y map. need to interpolate/extrapolate */ + + typename std::map::const_iterator it; + double x_low, x_high, y_low, y_high; + double slope, reference_y, delta_x; + + /* get first x greater than the one requested */ + it = xy_map->upper_bound(requested_x); + + if(it == xy_map->end()){ + /* need to extrapolate to higher x. based on the y values at the two largest x values */ + it--; + x_high = (double)it->first; + y_high = (double)it->second; + it--; + x_low = (double)it->first; + y_low = (double)it->second; + } else if (it == xy_map->begin()){ + /* need to extrapolate to lower x. based on the y values at the two smallest x */ + x_low = (double)it->first; + y_low = (double)it->second; + it++; + x_high = (double)it->first; + y_high = (double)it->second; + } else { + /* need to interpolate. based on y values at x just above/below + the one we want */ + x_high = (double)it->first; + y_high = (double)it->second; + it--; + x_low = (double)it->first; + y_low = (double)it->second; + } + + slope = (y_high - y_low) / (x_high - x_low); + reference_y = y_low; + delta_x = (double)requested_x - x_low; + result = (Y)(reference_y + (slope * delta_x)); + } + + return result; +} +template double linear_interpolate_or_extrapolate(const std::map *xy_map, int requested_x); /* (int,double) */ +template double linear_interpolate_or_extrapolate(const std::map *xy_map, double requested_x); /* (double,double) */ + +} diff --git a/libs/libvtrutil/src/vtr_math.h b/libs/libvtrutil/src/vtr_math.h new file mode 100644 index 000000000..08473fa7c --- /dev/null +++ b/libs/libvtrutil/src/vtr_math.h @@ -0,0 +1,139 @@ +#ifndef VTR_MATH_H +#define VTR_MATH_H + +#include +#include + +#include "vtr_assert.h" + +namespace vtr { + /*********************** Math operations *************************************/ + int ipow(int base, int exp); + + template + Y linear_interpolate_or_extrapolate(const std::map *xy_map, X requested_x); + + constexpr int nint(float val) { return static_cast(val + 0.5); } + + //Returns a 'safe' ratio which evaluates to zero if the denominator is zero + template + T safe_ratio(T numerator, T denominator) { + if (denominator == T(0)) { + return 0; + } + return numerator / denominator; + } + + template + double median(InputIterator first, InputIterator last) { + auto len = std::distance(first, last); + auto iter = first + len / 2; + + if (len % 2 == 0) { + return (*iter + *(iter + 1)) / 2; + } else { + return *iter; + } + } + + template + double median(Container c) { + return median(std::begin(c), std::end(c)); + } + + + template + double geomean(InputIterator first, InputIterator last, double init=1.) { + //Compute the geometric mean of the elments in range [first, last) + // + //To avoid potential round-off issues we transform the standard formula: + // + // geomean = ( v_1 * v_2 * ... * v_n) ^ (1/n) + // + //by taking the log: + // + // geomean = exp( (1 / n) * (log(v_1) + log(v_2) + ... + log(v_n))) + + double log_sum = std::log(init); + size_t n = 0; + for(auto iter = first; iter != last; ++iter) { + log_sum += std::log(*iter); + n += 1; + } + + VTR_ASSERT(n > 0.); + + return std::exp( (1. / n) * log_sum ); + } + + template + double geomean(Container c) { + return geomean(std::begin(c), std::end(c)); + } + + + template + double arithmean(InputIterator first, InputIterator last, double init=0.) { + double sum = init; + size_t n = 0; + for (auto iter = first; iter != last; ++iter) { + sum += *iter; + n += 1; + } + + VTR_ASSERT(n > 0.); + return sum / n; + } + + template + double arithmean(Container c) { + return arithmean(std::begin(c), std::end(c)); + } + + + //Return the greatest common divisor of x and y + // Note that T should be an integral type + template + static T gcd(T x, T y){ + static_assert(std::is_integral::value, "T must be integral"); + //Euclidean algorithm + if (y == 0){ + return x; + } + return gcd(y, x % y); + } + + //Return the least common multiple of x and y + // Note that T should be an integral type + template + T lcm(T x, T y) { + static_assert(std::is_integral::value, "T must be integral"); + + if (x == 0 && y == 0) { + return 0; + } else { + return (x / gcd(x,y)) * y; + } + } + + constexpr double DEFAULT_REL_TOL = 1e-9; + constexpr double DEFAULT_ABS_TOL = 0; + + template + bool isclose(T a, T b, T rel_tol, T abs_tol) { + + if (std::isinf(a) && std::isinf(b)) return (std::signbit(a) == std::signbit(b)); + if (std::isnan(a) && std::isnan(b)) return false; + + T abs_largest = std::max(std::abs(a), std::abs(b)); + return std::abs(a - b) <= std::max(rel_tol * abs_largest, abs_tol); + } + + template + bool isclose(T a, T b) { + return isclose(a, b, DEFAULT_REL_TOL, DEFAULT_ABS_TOL); + } + +} + +#endif diff --git a/libs/libvtrutil/src/vtr_memory.cpp b/libs/libvtrutil/src/vtr_memory.cpp new file mode 100644 index 000000000..da93faaf0 --- /dev/null +++ b/libs/libvtrutil/src/vtr_memory.cpp @@ -0,0 +1,177 @@ +#include +#include + +#include "vtr_assert.h" +#include "vtr_list.h" +#include "vtr_memory.h" +#include "vtr_error.h" +#include "vtr_util.h" + +#ifndef __GLIBC__ + #include +#else + #include +#endif + + +namespace vtr { + + +#ifndef __GLIBC__ +int malloc_trim(size_t /*pad*/) { + return 0; +} +#else +int malloc_trim(size_t pad) { + return ::malloc_trim(pad); +} +#endif + +void* free(void *some){ + if(some){ + std::free(some); + some = nullptr; + } + return nullptr; +} + +void* calloc(size_t nelem, size_t size) { + void *ret; + if (nelem == 0) { + return nullptr ; + } + + if ((ret = std::calloc(nelem, size)) == nullptr ) { + throw VtrError("Unable to calloc memory.", __FILE__, __LINE__); + } + return (ret); +} + +void* malloc(size_t size) { + void *ret; + if (size == 0) { + return nullptr ; + } + + if ((ret = std::malloc(size)) == nullptr && size != 0) { + throw VtrError("Unable to malloc memory.", __FILE__, __LINE__); + } + return (ret); +} + +void* realloc(void *ptr, size_t size) { + void *ret; + + ret = std::realloc(ptr, size); + if (nullptr == ret && size != 0) { + throw VtrError(string_fmt("Unable to realloc memory (ptr=%p, size=%d).", ptr, size), + __FILE__, __LINE__); + } + return (ret); +} + +void* chunk_malloc(size_t size, t_chunk *chunk_info) { + + /* This routine should be used for allocating fairly small data * + * structures where memory-efficiency is crucial. This routine allocates * + * large "chunks" of data, and parcels them out as requested. Whenever * + * it mallocs a new chunk it adds it to the linked list pointed to by * + * chunk_info->chunk_ptr_head. This list can be used to free the * + * chunked memory. * + * Information about the currently open "chunk" must be stored by the * + * user program. chunk_info->mem_avail_ptr points to an int storing * + * how many bytes are left in the current chunk, while * + * chunk_info->next_mem_loc_ptr is the address of a pointer to the * + * next free bytes in the chunk. To start a new chunk, simply set * + * chunk_info->mem_avail_ptr = 0. Each independent set of data * + * structures should use a new chunk. */ + + /* To make sure the memory passed back is properly aligned, I must * + * only send back chunks in multiples of the worst-case alignment * + * restriction of the machine. On most machines this should be * + * a long, but on 64-bit machines it might be a long long or a * + * double. Change the typedef below if this is the case. */ + + typedef size_t Align; + + constexpr int CHUNK_SIZE = 32768; + constexpr int FRAGMENT_THRESHOLD = 100; + + char *tmp_ptr; + int aligned_size; + + VTR_ASSERT(chunk_info->mem_avail >= 0); + + if ((size_t) (chunk_info->mem_avail) < size) { /* Need to malloc more memory. */ + if (size > CHUNK_SIZE) { /* Too big, use standard routine. */ + tmp_ptr = (char *) vtr::malloc(size); + + /* When debugging, uncomment the code below to see if memory allocation size */ + /* makes sense */ + //#ifdef DEBUG + // vtr_printf("NB: my_chunk_malloc got a request for %d bytes.\n", size); + // vtr_printf("You should consider using vtr::malloc for such big requests.\n"); + // #endif + + VTR_ASSERT(chunk_info != nullptr); + chunk_info->chunk_ptr_head = insert_in_vptr_list( + chunk_info->chunk_ptr_head, tmp_ptr); + return (tmp_ptr); + } + + if (chunk_info->mem_avail < FRAGMENT_THRESHOLD) { /* Only a small scrap left. */ + chunk_info->next_mem_loc_ptr = (char *) vtr::malloc(CHUNK_SIZE); + chunk_info->mem_avail = CHUNK_SIZE; + VTR_ASSERT(chunk_info != nullptr); + chunk_info->chunk_ptr_head = insert_in_vptr_list( + chunk_info->chunk_ptr_head, chunk_info->next_mem_loc_ptr); + } + + /* Execute else clause only when the chunk we want is pretty big, * + * and would leave too big an unused fragment. Then we use malloc * + * to allocate normally. */ + + else { + tmp_ptr = (char *) vtr::malloc(size); + VTR_ASSERT(chunk_info != nullptr); + chunk_info->chunk_ptr_head = insert_in_vptr_list( + chunk_info->chunk_ptr_head, tmp_ptr); + return (tmp_ptr); + } + } + + /* Find the smallest distance to advance the memory pointer and keep * + * everything aligned. */ + + if (size % sizeof(Align) == 0) { + aligned_size = size; + } else { + aligned_size = size + sizeof(Align) - size % sizeof(Align); + } + + tmp_ptr = chunk_info->next_mem_loc_ptr; + chunk_info->next_mem_loc_ptr += aligned_size; + chunk_info->mem_avail -= aligned_size; + return (tmp_ptr); +} + +void free_chunk_memory(t_chunk *chunk_info) { + + /* Frees the memory allocated by a sequence of calls to my_chunk_malloc. */ + + t_linked_vptr *curr_ptr, *prev_ptr; + + curr_ptr = chunk_info->chunk_ptr_head; + + while (curr_ptr != nullptr ) { + free(curr_ptr->data_vptr); /* Free memory "chunk". */ + prev_ptr = curr_ptr; + curr_ptr = curr_ptr->next; + free(prev_ptr); /* Free memory used to track "chunk". */ + } + chunk_info->chunk_ptr_head = nullptr; + chunk_info->mem_avail = 0; + chunk_info->next_mem_loc_ptr = nullptr; +} + +} //namespace diff --git a/libs/libvtrutil/src/vtr_memory.h b/libs/libvtrutil/src/vtr_memory.h new file mode 100644 index 000000000..9bdb95262 --- /dev/null +++ b/libs/libvtrutil/src/vtr_memory.h @@ -0,0 +1,51 @@ +#ifndef VTR_MEMORY_H +#define VTR_MEMORY_H +#include + +namespace vtr { + struct t_linked_vptr; //Forward declaration + + /* This structure is to keep track of chunks of memory that is being * + * allocated to save overhead when allocating very small memory pieces. * + * For a complete description, please see the comment in chunk_malloc*/ + struct t_chunk { + t_linked_vptr *chunk_ptr_head = nullptr; + /* chunk_ptr_head->data_vptr: head of the entire linked + * list of allocated "chunk" memory; + * chunk_ptr_head->next: pointer to the next chunk on the linked list*/ + int mem_avail = 0; /* number of bytes left in the current chunk */ + char *next_mem_loc_ptr = nullptr;/* pointer to the first available (free) * + * byte in the current chunk */ + }; + + void* free(void *some); + void* calloc(size_t nelem, size_t size); + void* malloc(size_t size); + void* realloc(void *ptr, size_t size); + + void* chunk_malloc(size_t size, t_chunk *chunk_info); + void free_chunk_memory(t_chunk *chunk_info); + + //Like chunk_malloc, but with proper C++ object initialization + template + T* chunk_new(t_chunk *chunk_info) { + void* block = chunk_malloc(sizeof(T), chunk_info); + + return new (block) T(); //Placement new + } + + //Call the destructor of an obj which must have been allocated in the specified chunk + template + void chunk_delete(T* obj, t_chunk * /*chunk_info*/) { + if (obj) { + obj->~T(); //Manually call destructor + //Currently we don't mark the unused memory as free + } + } + + //Cross platform wrapper around GNU's malloc_trim() + // TODO: This is only used in one place within VPR, consider removing it + int malloc_trim(size_t pad); +} + +#endif diff --git a/libs/libvtrutil/src/vtr_ndmatrix.h b/libs/libvtrutil/src/vtr_ndmatrix.h new file mode 100644 index 000000000..8ff2c4019 --- /dev/null +++ b/libs/libvtrutil/src/vtr_ndmatrix.h @@ -0,0 +1,359 @@ +#ifndef VTR_ND_MATRIX_H +#define VTR_ND_MATRIX_H +#include +#include + +#include "vtr_assert.h" + +namespace vtr { + +//Proxy class for a sub-matrix of a NdMatrix class. +//This is used to allow chaining of array indexing [] operators in a natural way. +// +//Each instance of this class peels off one-dimension and returns a NdMatrixProxy representing +//the resulting sub-matrix. This is repeated recursively until we hit the 1-dimensional base-case. +// +//Since this expansion happens at compiler time all the proxy classes get optimized away, +//yielding both high performance and generality. +// +//Recursive case: N-dimensional array +template +class NdMatrixProxy { + public: + static_assert(N > 0, "Must have at least one dimension"); + + //Construct a matrix proxy object + // + // dim_sizes: Array of dimension sizes + // idim: The dimension associated with this proxy + // dim_stride: The stride of this dimension (i.e. how many element in memory between indicies of this dimension) + // start: Pointer to the start of the sub-matrix this proxy represents + NdMatrixProxy(const size_t* dim_sizes, size_t idim, size_t dim_stride, T* start) + : dim_sizes_(dim_sizes) + , idim_(idim) + , dim_stride_(dim_stride) + , start_(start) {} + + const NdMatrixProxy operator[](size_t index) const { + VTR_ASSERT_SAFE_MSG(index >= 0, "Index out of range (below dimension minimum)"); + VTR_ASSERT_SAFE_MSG(index < dim_sizes_[idim_], "Index out of range (above dimension maximum)"); + + size_t next_dim_size = dim_sizes_[idim_ + 1]; + VTR_ASSERT_SAFE_MSG(next_dim_size > 0, "Can not index into zero-sized dimension"); + + //Determine the stride of the next dimension + size_t next_dim_stride = dim_stride_ / next_dim_size; + + //Strip off one dimension + return NdMatrixProxy(dim_sizes_, //Pass the dimension information + idim_ + 1, //Pass the next dimension + next_dim_stride, //Pass the stride for the next dimension + start_ + dim_stride_*index); //Advance to index in this dimension + } + + NdMatrixProxy operator[](size_t index) { + //Call the const version and cast-away constness + return const_cast*>(this)->operator[](index); + } + + private: + const size_t* dim_sizes_; + const size_t idim_; + const size_t dim_stride_; + T* start_; +}; + +//Base case: 1-dimensional array +template +class NdMatrixProxy { + public: + NdMatrixProxy(const size_t* dim_sizes, size_t idim, size_t dim_stride, T* start) + : dim_sizes_(dim_sizes) + , idim_(idim) + , dim_stride_(dim_stride) + , start_(start) {} + + + const T& operator[](size_t index) const { + VTR_ASSERT_SAFE_MSG(dim_stride_ == 1, "Final dimension must have stride 1"); + VTR_ASSERT_SAFE_MSG(index >= 0, "Index out of range (below dimension minimum)"); + VTR_ASSERT_SAFE_MSG(index < dim_sizes_[idim_], "Index out of range (above dimension maximum)"); + + //Base case + return start_[index]; + } + + T& operator[](size_t index) { + //Call the const version and cast-away constness + return const_cast(const_cast*>(this)->operator[](index)); + } + + //For legacy compatibility (i.e. code expecting a pointer) we allow this base dimension + //case to retrieve a raw pointer to the last dimension elements. + // + //Note that it is the caller's responsibility to use this correctly; care must be taken + //not to clobber elements in other dimensions + const T* data() const { + return start_; + } + + T* data() { + //Call the const version and cast-away constness + return const_cast(const_cast*>(this)->data()); + } + + private: + const size_t* dim_sizes_; + const size_t idim_; + const size_t dim_stride_; + T* start_; +}; + +//Base class for an N-dimensional matrix supporting arbitrary index ranges per dimension. +//This class implements all of the matrix handling (lifetime etc.) except for indexing +//(which is implemented in the NdMatrix class). Indexing is split out to allows specialization +//of indexing for N = 1. +// +//Implementation: +// +//This class uses a single linear array to store the matrix in c-style (row major) +//order. That is, the right-most index is laid out contiguous memory. +// +//This should improve memory usage (no extra pointers to store for each dimension), +//and cache locality (less indirection via pointers, predictable strides). +// +//The indicies are calculated based on the dimensions to access the appropriate elements. +//Since the indexing calculations are visible to the compiler at compile time they can be +//optimized to be efficient. +template +class NdMatrixBase { + public: + static_assert(N >= 1, "Minimum dimension 1"); + + //An empty matrix (all dimensions size zero) + NdMatrixBase() { + clear(); + } + + //Specified dimension sizes: + // [0..dim_sizes[0]) + // [0..dim_sizes[1]) + // ... + //with optional fill value + NdMatrixBase(std::array dim_sizes, T value=T()) { + resize(dim_sizes, value); + } + + public: //Accessors + //Returns the size of the matrix (number of elements) + size_t size() const { + VTR_ASSERT_DEBUG_MSG(calc_size() == size_, "Calculated and current matrix size must be consistent"); + return size_; + } + + //Returns true if there are no elements in the matrix + bool empty() const { + return size() == 0; + } + + //Returns the number of dimensions (i.e. N) + size_t ndims() const { + return dim_sizes_.size(); + } + + //Returns the size of the ith dimension + size_t dim_size(size_t i) const { + VTR_ASSERT_SAFE(i < ndims()); + + return dim_sizes_[i]; + } + + //Returns the starting index of ith dimension + size_t begin_index(size_t i) const { + VTR_ASSERT_SAFE(i < ndims()); + + return 0; + } + + //Returns the one-past-the-end index of the ith dimension + size_t end_index(size_t i) const { + VTR_ASSERT_SAFE(i < ndims()); + + return dim_sizes_[i]; + } + + public: //Mutators + + //Set all elements to 'value' + void fill(T value) { + std::fill(data_.get(), data_.get() + size(), value); + } + + //Resize the matrix to the specified dimension ranges + // + //If 'value' is specified all elements will be initialized to it, + //otherwise they will be default constructed. + void resize(std::array dim_sizes, T value=T()) { + dim_sizes_ = dim_sizes; + size_ = calc_size(); + alloc(); + fill(value); + } + + //Reset the matrix to size zero + void clear() { + data_.reset(nullptr); + dim_sizes_.fill(0); + size_ = 0; + } + public: //Lifetime management + //Copy constructor + NdMatrixBase(const NdMatrixBase& other) + : NdMatrixBase(other.dim_sizes_) { + std::copy(other.data_.get(), other.data_.get() + other.size(), data_.get()); + } + + //Move constructor + NdMatrixBase(NdMatrixBase&& other) + : NdMatrixBase() { + swap(*this, other); + } + + //Copy/move assignment + // + //Note that rhs is taken by value (copy-swap idiom) + NdMatrixBase& operator=(NdMatrixBase rhs) { + swap(*this, rhs); + return *this; + } + + //Swap two NdMatrixBase objects + friend void swap(NdMatrixBase& m1, NdMatrixBase& m2) { + using std::swap; + swap(m1.size_, m2.size_); + swap(m1.dim_sizes_, m2.dim_sizes_); + swap(m1.data_, m2.data_); + } + + private: + //Allocate space for all the elements + void alloc() { + data_ = std::make_unique(size()); + } + + //Returns the size of the matrix (number of elements) calucated + //from the current dimensions + size_t calc_size() const { + //Size is the product of all dimension sizes + size_t cnt = dim_size(0); + for (size_t idim = 1; idim < ndims(); ++idim) { + cnt *= dim_size(idim); + } + return cnt; + } + protected: + size_t size_ = 0; + std::array dim_sizes_; + std::unique_ptr data_ = nullptr; +}; + +//An N-dimensional matrix supporting arbitrary (continuous) index ranges +//per dimension. +// +//Examples: +// +// //A 2-dimensional matrix with indicies [0..4][0..9] +// NdMatrix m1({5,10}); +// +// //Accessing an element +// int i = m1[3][5]; +// +// //Setting an element +// m1[2][8] = 0; +// +// //A 3-dimensional matrix with indicies [0..4][0..9][0..19] +// NdMatrix m2({5,10,20}); +// +// //A 2-dimensional matrix with indicies [0..4][0..9], with all entries +// //initialized to 42 +// NdMatrix m3({5,10}, 42); +// +// //Filling all entries with value 101 +// m3.fill(101); +// +// //Resizing an existing matrix (all values reset to default constucted value) +// m3.resize({5,5}) +// +// //Resizing an existing matrix (all elements set to value 88) +// m3.resize({15,55}, 88) +template +class NdMatrix : public NdMatrixBase { + //General case + static_assert(N >= 2, "Minimum dimension 2"); + + public: + //Use the base constructors + using NdMatrixBase::NdMatrixBase; + public: + //Access an element + // + //Returns a proxy-object to allow chained array-style indexing (N >= 2 case) + const NdMatrixProxy operator[](size_t index) const { + VTR_ASSERT_SAFE_MSG(this->dim_size(0) > 0, "Can not index into size zero dimension"); + VTR_ASSERT_SAFE_MSG(this->dim_size(1) > 0, "Can not index into size zero dimension"); + VTR_ASSERT_SAFE_MSG(index >= 0, "Index out of range (below dimension minimum)"); + VTR_ASSERT_SAFE_MSG(index < this->dim_sizes_[0], "Index out of range (above dimension maximum)"); + + //Calculate the stride for the current dimension + size_t dim_stride = this->size() / this->dim_size(0); + + //Calculate the stride for the next dimension + size_t next_dim_stride = dim_stride / this->dim_size(1); + + //Peel off the first dimension + return NdMatrixProxy(this->dim_sizes_.data(), //Pass the dimension information + 1, //Pass the next dimension + next_dim_stride, //Pass the stride for the next dimension + this->data_.get() + dim_stride*index); //Advance to index in this dimension + } + + //Access an element + // + //Returns a proxy-object to allow chained array-style indexing + NdMatrixProxy operator[](size_t index) { + //Call the const version, since returned by value don't need to worry about const + return const_cast*>(this)->operator[](index); + } +}; + +template +class NdMatrix : public NdMatrixBase { + //Specialization for N = 1 + public: + //Use the base constructors + using NdMatrixBase::NdMatrixBase; + public: + //Access an element (immutable) + const T& operator[](size_t index) const { + VTR_ASSERT_SAFE_MSG(this->dim_size(0) > 0, "Can not index into size zero dimension"); + VTR_ASSERT_SAFE_MSG(index >= 0, "Index out of range (below dimension minimum)"); + VTR_ASSERT_SAFE_MSG(index < this->dim_sizes_[0], "Index out of range (above dimension maximum)"); + + return this->data_[index]; + } + + //Access an element (mutable) + T& operator[](size_t index) { + //Call the const version, and cast away const-ness + return const_cast(const_cast*>(this)->operator[](index)); + } + +}; + + +//Convenient short forms for common NdMatricies +template +using Matrix = NdMatrix; + +} //namespace +#endif diff --git a/libs/libvtrutil/src/vtr_ndoffsetmatrix.h b/libs/libvtrutil/src/vtr_ndoffsetmatrix.h new file mode 100644 index 000000000..b0c9aec80 --- /dev/null +++ b/libs/libvtrutil/src/vtr_ndoffsetmatrix.h @@ -0,0 +1,400 @@ +#ifndef VTR_ND_OFFSET_MATRIX_H +#define VTR_ND_OFFSET_MATRIX_H +#include +#include + +#include "vtr_assert.h" + +namespace vtr { + +//A half-open range specification for a matrix dimension [begin_index, last_index), +//with valid indicies from [begin_index() ... end_index()-1], provided size() > 0. +class DimRange { + public: + DimRange() = default; + DimRange(size_t begin, size_t end) + : begin_index_(begin) + , end_index_(end) {} + + size_t begin_index() const { return begin_index_; } + size_t end_index() const { return end_index_; } + + size_t size() const { return end_index_ - begin_index_; } + private: + size_t begin_index_ = 0; + size_t end_index_ = 0; +}; + +//Proxy class for a sub-matrix of a NdOffsetMatrix class. +//This is used to allow chaining of array indexing [] operators in a natural way. +// +//Each instance of this class peels off one-dimension and returns a NdOffsetMatrixProxy representing +//the resulting sub-matrix. This is repeated recursively until we hit the 1-dimensional base-case. +// +//Since this expansion happens at compiler time all the proxy classes get optimized away, +//yielding both high performance and generality. +// +//Recursive case: N-dimensional array +template +class NdOffsetMatrixProxy { + public: + static_assert(N > 0, "Must have at least one dimension"); + + //Construct a matrix proxy object + // + // dim_ranges: Array of DimRange objects + // idim: The dimension associated with this proxy + // dim_stride: The stride of this dimension (i.e. how many element in memory between indicies of this dimension) + // start: Pointer to the start of the sub-matrix this proxy represents + NdOffsetMatrixProxy(const DimRange* dim_ranges, size_t idim, size_t dim_stride, T* start) + : dim_ranges_(dim_ranges) + , idim_(idim) + , dim_stride_(dim_stride) + , start_(start) {} + + const NdOffsetMatrixProxy operator[](size_t index) const { + VTR_ASSERT_SAFE_MSG(index >= dim_ranges_[idim_].begin_index(), "Index out of range (below dimension minimum)"); + VTR_ASSERT_SAFE_MSG(index < dim_ranges_[idim_].end_index(), "Index out of range (above dimension maximum)"); + + //The elements are stored in zero-indexed form, so we need to adjust + //for any non-zero minimum index + size_t effective_index = index - dim_ranges_[idim_].begin_index(); + + //Determine the stride of the next dimension + size_t next_dim_stride = dim_stride_ / dim_ranges_[idim_ + 1].size(); + + //Strip off one dimension + return NdOffsetMatrixProxy(dim_ranges_, //Pass the dimension information + idim_ + 1, //Pass the next dimension + next_dim_stride, //Pass the stride for the next dimension + start_ + dim_stride_*effective_index); //Advance to index in this dimension + } + + NdOffsetMatrixProxy operator[](size_t index) { + //Call the const version and cast-away constness + return const_cast*>(this)->operator[](index); + } + + private: + const DimRange* dim_ranges_; + const size_t idim_; + const size_t dim_stride_; + T* start_; +}; + +//Base case: 1-dimensional array +template +class NdOffsetMatrixProxy { + public: + NdOffsetMatrixProxy(const DimRange* dim_ranges, size_t idim, size_t dim_stride, T* start) + : dim_ranges_(dim_ranges) + , idim_(idim) + , dim_stride_(dim_stride) + , start_(start) {} + + + const T& operator[](size_t index) const { + VTR_ASSERT_SAFE_MSG(dim_stride_ == 1, "Final dimension must have stride 1"); + VTR_ASSERT_SAFE_MSG(index >= dim_ranges_[idim_].begin_index(), "Index out of range (below dimension minimum)"); + VTR_ASSERT_SAFE_MSG(index < dim_ranges_[idim_].end_index(), "Index out of range (above dimension maximum)"); + + //The elements are stored in zero-indexed form, so we need to adjust + //for any non-zero minimum index + size_t effective_index = index - dim_ranges_[idim_].begin_index(); + + //Base case + return start_[effective_index]; + } + + T& operator[](size_t index) { + //Call the const version and cast-away constness + return const_cast(const_cast*>(this)->operator[](index)); + } + + private: + const DimRange* dim_ranges_; + const size_t idim_; + const size_t dim_stride_; + T* start_; +}; + +//Base class for an N-dimensional matrix supporting arbitrary index ranges per dimension. +//This class implements all of the matrix handling (lifetime etc.) except for indexing +//(which is implemented in the NdOffsetMatrix class). Indexing is split out to allows specialization +//of indexing for N = 1. +// +//Implementation: +// +//This class uses a single linear array to store the matrix in c-style (row major) +//order. That is, the right-most index is laid out contiguous memory. +// +//This should improve memory usage (no extra pointers to store for each dimension), +//and cache locality (less indirection via pointers, predictable strides). +// +//The indicies are calculated based on the dimensions to access the appropriate elements. +//Since the indexing calculations are visible to the compiler at compile time they can be +//optimized to be efficient. +template +class NdOffsetMatrixBase { + public: + static_assert(N >= 1, "Minimum dimension 1"); + + //An empty matrix (all dimensions size zero) + NdOffsetMatrixBase() { + clear(); + } + + //Specified dimension sizes: + // [0..dim_sizes[0]) + // [0..dim_sizes[1]) + // ... + //with optional fill value + NdOffsetMatrixBase(std::array dim_sizes, T value=T()) { + resize(dim_sizes, value); + } + + //Specified dimension index ranges: + // [dim_ranges[0].begin_index() ... dim_ranges[1].end_index()) + // [dim_ranges[1].begin_index() ... dim_ranges[1].end_index()) + // ... + //with optional fill value + NdOffsetMatrixBase(std::array dim_ranges, T value=T()) { + resize(dim_ranges, value); + } + public: //Accessors + //Returns the size of the matrix (number of elements) + size_t size() const { + //Size is the product of all dimension sizes + size_t cnt = dim_size(0); + for (size_t idim = 1; idim < ndims(); ++idim) { + cnt *= dim_size(idim); + } + return cnt; + } + + //Returns true if there are no elements in the matrix + bool empty() const { + return size() == 0; + } + + //Returns the number of dimensions (i.e. N) + size_t ndims() const { + return dim_ranges_.size(); + } + + //Returns the size of the ith dimension + size_t dim_size(size_t i) const { + VTR_ASSERT_SAFE(i < ndims()); + + return dim_ranges_[i].size(); + } + + //Returns the starting index of ith dimension + size_t begin_index(size_t i) const { + VTR_ASSERT_SAFE(i < ndims()); + + return dim_ranges_[i].begin_index(); + } + + //Returns the one-past-the-end index of the ith dimension + size_t end_index(size_t i) const { + VTR_ASSERT_SAFE(i < ndims()); + + return dim_ranges_[i].end_index(); + } + + public: //Mutators + + //Set all elements to 'value' + void fill(T value) { + std::fill(data_.get(), data_.get() + size(), value); + } + + //Resize the matrix to the specified dimensions + // + //If 'value' is specified all elements will be initialized to it, + //otherwise they will be default constructed. + void resize(std::array dim_sizes, T value=T()) { + //Convert dimension to range [0..dim) + for(size_t i = 0; i < dim_sizes.size(); ++i) { + dim_ranges_[i] = {0, dim_sizes[i]}; + } + alloc(); + fill(value); + } + + //Resize the matrix to the specified dimension ranges + // + //If 'value' is specified all elements will be initialized to it, + //otherwise they will be default constructed. + void resize(std::array dim_ranges, T value=T()) { + dim_ranges_ = dim_ranges; + alloc(); + fill(value); + } + + //Reset the matrix to size zero + void clear() { + data_.reset(nullptr); + for(size_t i = 0; i < dim_ranges_.size(); ++i) { + dim_ranges_[i] = {0, 0}; + } + } + public: //Lifetime management + //Copy constructor + NdOffsetMatrixBase(const NdOffsetMatrixBase& other) + : NdOffsetMatrixBase(other.dim_ranges_) { + std::copy(other.data_.get(), other.data_.get() + other.size(), data_.get()); + } + + //Move constructor + NdOffsetMatrixBase(NdOffsetMatrixBase&& other) + : NdOffsetMatrixBase() { + swap(*this, other); + } + + //Copy/move assignment + // + //Note that rhs is taken by value (copy-swap idiom) + NdOffsetMatrixBase& operator=(NdOffsetMatrixBase rhs) { + swap(*this, rhs); + return *this; + } + + //Swap two NdOffsetMatrixBase objects + friend void swap(NdOffsetMatrixBase& m1, NdOffsetMatrixBase& m2) { + using std::swap; + swap(m1.dim_ranges_, m2.dim_ranges_); + swap(m1.data_, m2.data_); + } + + private: + //Allocate space for all the elements + void alloc() { + data_ = std::make_unique(size()); + } + + protected: + std::array dim_ranges_; + std::unique_ptr data_ = nullptr; +}; + +//An N-dimensional matrix supporting arbitrary (continuous) index ranges +//per dimension. +// +//If no second template parameter is provided defaults to a 2-dimensional +//matrix +// +//Examples: +// +// //A 2-dimensional matrix with indicies [0..4][0..9] +// NdOffsetMatrix m1({5,10}); +// +// //Accessing an element +// int i = m4[3][5]; +// +// //Setting an element +// m4[6][20] = 0; +// +// //A 2-dimensional matrix with indicies [2..6][5..9] +// // Note that C++ requires one more set of curly brace than you would expect +// NdOffsetMatrix m2({{{2,7},{5,10}}}); +// +// //A 3-dimensional matrix with indicies [0..4][0..9][0..19] +// NdOffsetMatrix m3({5,10,20}); +// +// //A 3-dimensional matrix with indicies [2..6][1..19][50..89] +// NdOffsetMatrix m4({{{2,7}, {1,20}, {50,90}}}); +// +// //A 2-dimensional matrix with indicies [2..6][1..20], with all entries +// //intialized to 42 +// NdOffsetMatrix m4({{{2,7}, {1,21}}}, 42); +// +// //A 2-dimensional matrix with indicies [0..4][0..9], with all entries +// //initialized to 42 +// NdOffsetMatrix m1({5,10}, 42); +// +// //Filling all entries with value 101 +// m1.fill(101); +// +// //Resizing an existing matrix (all values reset to default constucted value) +// m1.resize({5,5}) +// +// //Resizing an existing matrix (all elements set to value 88) +// m1.resize({15,55}, 88) +template +class NdOffsetMatrix : public NdOffsetMatrixBase { + //General case + static_assert(N >= 2, "Minimum dimension 2"); + + public: + //Use the base constructors + using NdOffsetMatrixBase::NdOffsetMatrixBase; + public: + //Access an element + // + //Returns a proxy-object to allow chained array-style indexing (N >= 2 case) + //template= 2>::type, typename T1=T> + const NdOffsetMatrixProxy operator[](size_t index) const { + VTR_ASSERT_SAFE_MSG(this->dim_size(0) > 0, "Can not index into size zero dimension"); + VTR_ASSERT_SAFE_MSG(this->dim_size(1) > 0, "Can not index into size zero dimension"); + VTR_ASSERT_SAFE_MSG(index >= this->dim_ranges_[0].begin_index(), "Index out of range (below dimension minimum)"); + VTR_ASSERT_SAFE_MSG(index < this->dim_ranges_[0].end_index(), "Index out of range (above dimension maximum)"); + + //The elements are stored in zero-indexed form, so adjust for any + //non-zero minimum index in this dimension + size_t effective_index = index - this->dim_ranges_[0].begin_index(); + + //Calculate the stride for the current dimension + size_t dim_stride = this->size() / this->dim_size(0); + + //Calculate the stride for the next dimension + size_t next_dim_stride = dim_stride / this->dim_size(1); + + //Peel off the first dimension + return NdOffsetMatrixProxy(this->dim_ranges_.data(), //Pass the dimension information + 1, //Pass the next dimension + next_dim_stride, //Pass the stride for the next dimension + this->data_.get() + dim_stride*effective_index); //Advance to index in this dimension + } + + //Access an element + // + //Returns a proxy-object to allow chained array-style indexing + NdOffsetMatrixProxy operator[](size_t index) { + //Call the const version, since returned by value don't need to worry about const + return const_cast*>(this)->operator[](index); + } +}; + +template +class NdOffsetMatrix : public NdOffsetMatrixBase { + //Specialization for N = 1 + public: + //Use the base constructors + using NdOffsetMatrixBase::NdOffsetMatrixBase; + public: + //Access an element (immutable) + const T& operator[](size_t index) const { + VTR_ASSERT_SAFE_MSG(this->dim_size(0) > 0, "Can not index into size zero dimension"); + VTR_ASSERT_SAFE_MSG(index >= this->dim_ranges_[0].begin_index(), "Index out of range (below dimension minimum)"); + VTR_ASSERT_SAFE_MSG(index < this->dim_ranges_[0].end_index(), "Index out of range (above dimension maximum)"); + + return this->data_[index]; + } + + //Access an element (mutable) + T& operator[](size_t index) { + //Call the const version, and cast away const-ness + return const_cast(const_cast*>(this)->operator[](index)); + } + +}; + + +//Convenient short forms for common NdMatricies +template +using OffsetMatrix = NdOffsetMatrix; + +} //namespace +#endif diff --git a/libs/libvtrutil/src/vtr_ostream_guard.h b/libs/libvtrutil/src/vtr_ostream_guard.h new file mode 100644 index 000000000..1ffe80e5d --- /dev/null +++ b/libs/libvtrutil/src/vtr_ostream_guard.h @@ -0,0 +1,39 @@ +#ifndef VTR_OSTREAM_GUARD_H +#define VTR_OSTREAM_GUARD_H + +namespace vtr { + +//A RAII guard class to ensure restoration of output stream format +class OsFormatGuard { + public: + explicit OsFormatGuard(std::ostream& os) + : os_(os) + , flags_(os_.flags()) //Save formatting flag state + , width_(os_.width()) + , precision_(os.precision()) + , fill_(os.fill()) + {} + + ~OsFormatGuard() { + os_.flags(flags_); //Restore + os_.width(width_); + os_.precision(precision_); + os_.fill(fill_); + } + + OsFormatGuard(const OsFormatGuard&) = delete; + OsFormatGuard& operator=(const OsFormatGuard&) = delete; + OsFormatGuard(const OsFormatGuard&&) = delete; + OsFormatGuard& operator=(const OsFormatGuard&&) = delete; + private: + std::ostream& os_; + std::ios::fmtflags flags_; + std::streamsize width_; + std::streamsize precision_; + char fill_; + +}; + +} //namespace + +#endif diff --git a/libs/libvtrutil/src/vtr_pair_util.h b/libs/libvtrutil/src/vtr_pair_util.h new file mode 100644 index 000000000..cfc511b84 --- /dev/null +++ b/libs/libvtrutil/src/vtr_pair_util.h @@ -0,0 +1,57 @@ +#ifndef VTR_PAIR_UTIL_H +#define VTR_PAIR_UTIL_H + +#include "vtr_range.h" + +namespace vtr { + +//Iterator which derefernces the 'first' element of a std::pair iterator +template +class pair_first_iter { + public: + + using iterator_category = std::bidirectional_iterator_tag; + using value_type = typename PairIter::value_type::first_type; + using difference_type = void; + using pointer = value_type*; + using reference = value_type&; + + pair_first_iter(PairIter init): iter_(init) {} + auto operator++() { iter_++; return *this; } + auto operator--() { iter_--; return *this; } + auto operator*() { return iter_->first; } + auto operator->() { return &iter_->first; } + + friend bool operator== (const pair_first_iter lhs, const pair_first_iter rhs) { return lhs.iter_ == rhs.iter_; } + friend bool operator!= (const pair_first_iter lhs, const pair_first_iter rhs) { return !(lhs == rhs); } + + private: + PairIter iter_; +}; + +//Iterator which derefernces the 'second' element of a std::pair iterator +template +class pair_second_iter { + public: + + using iterator_category = std::bidirectional_iterator_tag; + using value_type = typename PairIter::value_type::second_type; + using difference_type = void; + using pointer = value_type*; + using reference = value_type&; + + pair_second_iter(PairIter init): iter_(init) {} + auto operator++() { iter_++; return *this; } + auto operator--() { iter_--; return *this; } + auto operator*() { return iter_->second; } + auto operator->() { return &iter_->second; } + + friend bool operator== (const pair_second_iter lhs, const pair_second_iter rhs) { return lhs.iter_ == rhs.iter_; } + friend bool operator!= (const pair_second_iter lhs, const pair_second_iter rhs) { return !(lhs == rhs); } + + private: + PairIter iter_; +}; + +} //namespace +#endif diff --git a/libs/libvtrutil/src/vtr_path.cpp b/libs/libvtrutil/src/vtr_path.cpp new file mode 100644 index 000000000..f15d5cbe6 --- /dev/null +++ b/libs/libvtrutil/src/vtr_path.cpp @@ -0,0 +1,87 @@ +#include "vtr_path.h" + +#include "vtr_util.h" + +//TODO: currently this file assumes unix-like +// in the future support windows +#include + +#include + +namespace vtr { + +const std::string PATH_DELIM = "/"; + +//Splits off the name and extension (including ".") of the specified filename +std::array split_ext(const std::string& filename) { + std::array name_ext; + auto pos = filename.find_last_of('.'); + + if (pos == std::string::npos) { + //No extension + pos = filename.size(); + } + + name_ext[0] = std::string(filename, 0, pos); + name_ext[1] = std::string(filename, pos, filename.size() - pos); + + return name_ext; +} + +std::string basename(const std::string& path) { + auto elements = split(path, PATH_DELIM); + + std::string str; + if(elements.size() > 0) { + //Return the last path element + str = elements[elements.size() - 1]; + } + + return str; +} + +std::string dirname(const std::string& path) { + auto elements = split(path, PATH_DELIM); + + std::string str; + if(elements.size() > 0) { + //We need to start the dirname with a PATH_DELIM if path started with one + if(starts_with(path, PATH_DELIM)) { + str += PATH_DELIM; + } + + //Join all except the last path element + str += join(elements.begin(), elements.end() - 1, PATH_DELIM); + + //We append a final PATH_DELIM to allow clients to just append directly to the + //returned value + str += PATH_DELIM; + } + + return str; +} + +std::string getcwd() { + constexpr size_t BUF_SIZE = 500; + char buf [BUF_SIZE]; + + if (::getcwd(buf, BUF_SIZE)) { + return std::string(buf); + } + + //Check the global errno + int error = errno; + + switch (error) { + case EACCES: + throw std::runtime_error("Access denied"); + + default: { + std::stringstream str; + str << "Unrecognised error" << error; + throw std::runtime_error(str.str()); + } + } +} + +} //namespace diff --git a/libs/libvtrutil/src/vtr_path.h b/libs/libvtrutil/src/vtr_path.h new file mode 100644 index 000000000..06373f48a --- /dev/null +++ b/libs/libvtrutil/src/vtr_path.h @@ -0,0 +1,23 @@ +#ifndef VTR_PATH_H +#define VTR_PATH_H +#include +#include + +namespace vtr { + + //Splits off the name and extension (including ".") of the specified filename + std::array split_ext(const std::string& filename); + + //Returns the basename of path (i.e. the last filename component) + // For example, the path "/home/user/my_files/test.blif" -> "test.blif" + std::string basename(const std::string& path); + + //Returns the dirname of path (i.e. everything except the last filename component) + // For example, the path "/home/user/my_files/test.blif" -> "/home/user/my_files/" + std::string dirname(const std::string& path); + + //Returns the current working directory + std::string getcwd(); + +} +#endif diff --git a/libs/libvtrutil/src/vtr_random.cpp b/libs/libvtrutil/src/vtr_random.cpp new file mode 100644 index 000000000..6e669a599 --- /dev/null +++ b/libs/libvtrutil/src/vtr_random.cpp @@ -0,0 +1,76 @@ +#include + +#include "vtr_random.h" +#include "vtr_util.h" +#include "vtr_error.h" + +#define CHECK_RAND + +namespace vtr { +/* Portable random number generator defined below. Taken from ANSI C by * + * K & R. Not a great generator, but fast, and good enough for my needs. */ + +constexpr size_t IA = 1103515245u; +constexpr size_t IC = 12345u; +constexpr size_t IM = 2147483648u; + +static RandState random_state = 0; + +void srandom(int seed) { + random_state = (unsigned int) seed; +} + +/* returns the random_state value */ +RandState get_random_state() { + return random_state; +} + +int irand(int imax, RandState& state) { + + /* Creates a random integer between 0 and imax, inclusive. i.e. [0..imax] */ + int ival; + + /* state = (state * IA + IC) % IM; */ + state = state * IA + IC; /* Use overflow to wrap */ + ival = state & (IM - 1); /* Modulus */ + ival = (int) ((float) ival * (float) (imax + 0.999) / (float) IM); + +#ifdef CHECK_RAND + if ((ival < 0) || (ival > imax)) { + if (ival == imax + 1) { + /* Due to random floating point rounding, sometimes above calculation gives number greater than ival by 1 */ + ival = imax; + } else { + throw VtrError(string_fmt("Bad value in my_irand, imax = %d ival = %d", imax, ival), __FILE__, __LINE__); + } + } +#endif + + return ival; +} + +int irand(int imax) { + return irand(imax, random_state); +} + +float frand() { + + /* Creates a random float between 0 and 1. i.e. [0..1). */ + + float fval; + int ival; + + random_state = random_state * IA + IC; /* Use overflow to wrap */ + ival = random_state & (IM - 1); /* Modulus */ + fval = (float) ival / (float) IM; + +#ifdef CHECK_RAND + if ((fval < 0) || (fval > 1.)) { + throw VtrError(string_fmt("Bad value in my_frand, fval = %g", fval), __FILE__, __LINE__); + } +#endif + + return (fval); +} + +} //namespace diff --git a/libs/libvtrutil/src/vtr_random.h b/libs/libvtrutil/src/vtr_random.h new file mode 100644 index 000000000..55ed72fec --- /dev/null +++ b/libs/libvtrutil/src/vtr_random.h @@ -0,0 +1,32 @@ +#ifndef VTR_RANDOM_H +#define VTR_RANDOM_H +#include //For std::swap + +namespace vtr { + /*********************** Portable random number generators *******************/ + typedef unsigned RandState; + + void srandom(int seed); + RandState get_random_state(); + int irand(int imax); + int irand(int imax, RandState& rand_state); + float frand(); + + //Portable/invariant version of std::shuffle + // + //Note that std::shuffle relies on std::uniform_int_distribution + //which can produce different sequences accross different + //compilers/compiler versions. + // + //This version should be deterministic/invariant. However, since + //it uses vtr::irand(), may not be as well distributed as std::shuffle. + template + void shuffle(Iter first, Iter last, RandState& rand_state) { + for (auto i = (last - first) - 1; i > 0; --i) { + using std::swap; + swap(first[i], first[irand(i, rand_state)]); + } + } + +} //namespace +#endif diff --git a/libs/libvtrutil/src/vtr_range.h b/libs/libvtrutil/src/vtr_range.h new file mode 100644 index 000000000..7fd25f5fe --- /dev/null +++ b/libs/libvtrutil/src/vtr_range.h @@ -0,0 +1,73 @@ +#ifndef VTR_RANGE_H +#define VTR_RANGE_H +#include + +namespace vtr { +/* + * The vtr::Range template models a range defined by two iterators of type T. + * + * It allows conveniently returning a range from a single function call + * without having to explicity expose the underlying container, or make two + * explicit calls to retrieve the associated begin and end iterators. + * It also enables the easy use of range-based-for loops. + * + * For example: + * + * class My Data { + * public: + * typdef std::vector::const_iterator my_iter; + * vtr::Range data(); + * ... + * private: + * std::vector data_; + * }; + * + * ... + * + * MyDat my_data; + * + * //fill my_data + * + * for(int val : my_data.data()) { + * //work with values stored in my_data + * } + * + * The empty() and size() methods are convenience wrappers around the relevant + * iterator comparisons. + * + * Note that size() is only constant time if T is a random-access iterator! + */ +template +class Range { + public: + Range(T b, T e): begin_(b), end_(e) {} + T begin() { return begin_; } + T end() { return end_; } + bool empty() { return begin_ == end_; } + size_t size() { return std::distance(begin_, end_); } + private: + T begin_; + T end_; +}; + +/* + * Creates a vtr::Range from a pair of iterators. + * + * Unlike using the vtr::Range() constructor (which requires specifying + * the template type T, using vtr::make_range() infers T from the arguments. + * + * Example usage: + * auto my_range = vtr::make_range(my_vec.begin(), my_vec.end()); + */ +template +auto make_range(T b, T e) { return Range(b, e); } + +/* + * Creates a vtr::Range from a container + */ +template +auto make_range(const Container& c) { return make_range(std::begin(c), std::end(c)); } + +} //namespace + +#endif diff --git a/libs/libvtrutil/src/vtr_rusage.cpp b/libs/libvtrutil/src/vtr_rusage.cpp new file mode 100644 index 000000000..9dea8290f --- /dev/null +++ b/libs/libvtrutil/src/vtr_rusage.cpp @@ -0,0 +1,29 @@ +#include "vtr_rusage.h" + +#ifdef __unix__ +# include +# include +#endif + +namespace vtr { + +size_t get_max_rss() { + size_t max_rss = 0; + +#ifdef __unix__ + rusage usage; + int result = getrusage(RUSAGE_SELF, &usage); + + if (result == 0) { //Success + //ru_maxrss is in kilobytes, convert to bytes + max_rss = usage.ru_maxrss * 1024; + } +#else + //Do nothing, other platform specific code could be added here + //with appropriate defines +#endif + + return max_rss; +} + +} //namespace diff --git a/libs/libvtrutil/src/vtr_rusage.h b/libs/libvtrutil/src/vtr_rusage.h new file mode 100644 index 000000000..7ebd7868a --- /dev/null +++ b/libs/libvtrutil/src/vtr_rusage.h @@ -0,0 +1,12 @@ +#ifndef VTR_RUSAGE_H +#define VTR_RUSAGE_H +#include + +namespace vtr { + + //Returns the maximum resident set size in bytes, + //or zero if unable to determine. + size_t get_max_rss(); +} + +#endif diff --git a/libs/libvtrutil/src/vtr_sentinels.h b/libs/libvtrutil/src/vtr_sentinels.h new file mode 100644 index 000000000..b9574e288 --- /dev/null +++ b/libs/libvtrutil/src/vtr_sentinels.h @@ -0,0 +1,40 @@ +#ifndef VTR_SENTINELS_H +#define VTR_SENTINELS_H +namespace vtr { + +//Some specialized containers like vtr::linear_map and +//vtr::vector_map require sentinel values to mark invalid/uninitialized +//values. By convention, such containers query the sentinel objects static +//INVALID() member function to retrieve the sentinel value. +// +//These classes allows users to specify a custom sentinel value. +// +//Usually the containers default to DefaultSentinel + +//The sentinel value is the default constructed value of the type +template +class DefaultSentinel { + public: + constexpr static T INVALID() { return T(); } +}; + +//Specialization for pointer types +template +class DefaultSentinel { + public: + constexpr static T* INVALID() { return nullptr; } +}; + +//The sentile value is a specified value of the type +template +class CustomSentinel { + public: + constexpr static T INVALID() { return T(val); } +}; + +//The common case where -1 is used as the sentinel value +template +using MinusOneSentinel = CustomSentinel; + +} //namespace +#endif diff --git a/libs/libvtrutil/src/vtr_strong_id.h b/libs/libvtrutil/src/vtr_strong_id.h new file mode 100644 index 000000000..9c6887982 --- /dev/null +++ b/libs/libvtrutil/src/vtr_strong_id.h @@ -0,0 +1,233 @@ +#ifndef VTR_STRONG_ID_H +#define VTR_STRONG_ID_H +/* + * This header provides the StrongId class, a template which can be used to + * create strong Id's which avoid accidental type conversions (generating + * compiler errors when they occur). + * + * Motivation + * ========== + * It is common to use an Id (typically an integer) to identify and represent a component. + * A basic example (poor style): + * + * size_t count_net_terminals(int net_id); + * + * Where a plain int is used to represent the net identifier. + * Using a plain basic type is poor style since it makes it unclear that the parameter is + * an Id. + * + * A better example is to use a typedef: + * + * typedef int NetId; + * + * size_t count_net_teriminals(NetId net_id); + * + * It is now clear that the parameter is expecting an Id. + * + * However this approach has some limitations. In particular, typedef's only create type + * aliases, and still allow conversions. This is problematic if there are multiple types + * of Ids. For example: + * + * typedef int NetId; + * typedef int BlkId; + * + * size_t count_net_teriminals(NetId net_id); + * + * BlkId blk_id = 10; + * NetId net_id = 42; + * + * count_net_teriminals(net_id); //OK + * count_net_teriminals(blk_id); //Bug: passed a BlkId as a NetId + * + * Since typdefs are aliases the compiler issues no errors or warnings, and silently passes + * the BlkId where a NetId is expected. This results in hard to diagnose bugs. + * + * We can avoid this issue by using a StrongId: + * + * struct net_id_tag; //Phantom tag for NetId + * struct blk_id_tag; //Phantom tag for BlkId + * + * typedef StrongId NetId; + * typedef StrongId BlkId; + * + * size_t count_net_teriminals(NetId net_id); + * + * BlkId blk_id = 10; + * NetId net_id = 42; + * + * count_net_teriminals(net_id); //OK + * count_net_teriminals(blk_id); //Compiler Error: NetId expected! + * + * StrongId is a template which implements the basic features of an Id, but disallows silent conversions + * between different types of Ids. It uses another 'tag' type (passed as the first template parameter) + * to uniquely identify the type of the Id (preventing conversions between different types of Ids). + * + * Usage + * ===== + * + * The StrongId template class takes one required and three optional template parameters: + * + * 1) Tag - the unique type used to identify this type of Ids [Required] + * 2) T - the underlying integral id type (default: int) [Optional] + * 3) T sentinel - a value representing an invalid Id (default: -1) [Optional] + * + * If no value is supllied during construction the StrongId is initialized to the invalid/sentinel value. + * + * Example 1: default definition + * + * struct net_id_tag; + * typedef StrongId NetId; //Internally stores an integer Id, -1 represents invalid + * + * Example 2: definition with custom underlying type + * + * struct blk_id_tag; + * typedef StrongId BlkId; //Internally stores a size_t Id, -1 represents invalid + * + * Example 3: definition with custom underlying type and custom sentinel value + * + * struct pin_id_tag; + * typedef StrongId PinId; //Internally stores a size_t Id, 0 represents invalid + * + * Example 4: Creating Ids + * + * struct net_id_tag; + * typedef StrongId MyId; //Internally stores an integer Id, -1 represents invalid + * + * MyId my_id; //Defaults to the sentinel value (-1 by default) + * MyId my_other_id = 5; //Explicit construction + * MyId my_thrid_id(25); //Explicit construction + * + * Example 5: Comparing Ids + * + * struct net_id_tag; + * typedef StrongId MyId; //Internally stores an integer Id, -1 represents invalid + * + * MyId my_id; //Defaults to the sentinel value (-1 by default) + * MyId my_id_one = 1; + * MyId my_id_two = 2; + * MyId my_id_also_one = 1; + * + * my_id_one == my_id_also_one; //True + * my_id_one == my_id; //False + * my_id_one == my_id_two; //False + * my_id_one != my_id_two; //True + * + * Example 5: Checking for invalid Ids + * + * struct net_id_tag; + * typedef StrongId MyId; //Internally stores an integer Id, -1 represents invalid + * + * MyId my_id; //Defaults to the sentinel value + * MyId my_id_one = 1; + * + * //Comparison against a constructed invalid id + * my_id == MyId::INVALID(); //True + * my_id_one == MyId::INVALID(); //False + * my_id_one != MyId::INVALID(); //True + * + * //The Id can also be evaluated in a boolean context against the sentinel value + * if(my_id) //False, my_id is invalid + * if(!my_id) //True my_id is valid + * if(my_id_one) //True my_id_one is valid + * + * Example 6: Indexing data structures + * + * struct my_id_tag; + * typedef StrongId MyId; //Internally stores an integer Id, -1 represents invalid + * + * std::vector my_vec = {0, 1, 2, 3, 4, 5}; + * + * MyId my_id = 2; + * + * my_vec[size_t(my_id)]; //Access the third element via explicit conversion + */ +#include //for std::is_integral +#include //for std::size_t +#include //for std::hash + +namespace vtr { + +//Forward declare the class (needed for operator declarations) +template +class StrongId; + +//Forward declare the equality/inequality operators +// We need to do this before the class definition so the class can +// friend them +template +bool operator==(const StrongId& lhs, const StrongId& rhs); + +template +bool operator!=(const StrongId& lhs, const StrongId& rhs); + +template +bool operator<(const StrongId& lhs, const StrongId& rhs); + + +//Class template definition with default template parameters +template +class StrongId { + static_assert(std::is_integral::value, "T must be integral"); + + public: + //Gets the invalid Id + static constexpr StrongId INVALID() { return StrongId(); } + + //Default to the sentinel value + constexpr StrongId() : id_(sentinel) {} + + //Only allow explict constructions from a raw Id (no automatic conversions) + explicit constexpr StrongId(T id): id_(id) {} + + //Allow some explicit conversion to useful types + + //Allow explicit conversion to bool (e.g. if(id)) + explicit operator bool() const { return *this != INVALID(); } + + //Allow explicit conversion to size_t (e.g. my_vector[size_t(strong_id)]) + explicit operator std::size_t() const { return static_cast(id_); } + + + //To enable hasing Ids + friend std::hash>; + + //To enable comparisions between Ids + // Note that since these are templated functions we provide an empty set of template parameters + // after the function name (i.e. <>) + friend bool operator== <>(const StrongId& lhs, const StrongId& rhs); + friend bool operator!= <>(const StrongId& lhs, const StrongId& rhs); + friend bool operator< <>(const StrongId& lhs, const StrongId& rhs); + private: + T id_; +}; + +template +bool operator==(const StrongId& lhs, const StrongId& rhs) { + return lhs.id_ == rhs.id_; +} + +template +bool operator!=(const StrongId& lhs, const StrongId& rhs) { + return !(lhs == rhs); +} + +//Needed for std::map-like containers +template +bool operator<(const StrongId& lhs, const StrongId& rhs) { + return lhs.id_ < rhs.id_; +} + +} //namespace vtr + +//Specialize std::hash for StrongId's (needed for std::unordered_map-like containers) +namespace std { + template + struct hash> { + std::size_t operator()(const vtr::StrongId k) const noexcept { + return std::hash()(k.id_); //Hash with the underlying type + } + }; +} //namespace std + + +#endif diff --git a/libs/libvtrutil/src/vtr_time.cpp b/libs/libvtrutil/src/vtr_time.cpp new file mode 100644 index 000000000..9cfec95a1 --- /dev/null +++ b/libs/libvtrutil/src/vtr_time.cpp @@ -0,0 +1,85 @@ +#include "vtr_time.h" + +#include "vtr_log.h" +#include "vtr_rusage.h" + +namespace vtr { + +int f_timer_depth = 0; + +Timer::Timer() + : start_(clock::now()) + , initial_max_rss_(get_max_rss()) { +} + +float Timer::elapsed_sec() const { + return std::chrono::duration(clock::now() - start_).count(); +} + +float Timer::max_rss_mib() const { + return get_max_rss() / BYTE_TO_MIB; +} + +float Timer::delta_max_rss_mib() const { + return (get_max_rss() - initial_max_rss_) / BYTE_TO_MIB; +} + +ScopedActionTimer::ScopedActionTimer(std::string action_str) + : action_(action_str) + , depth_(f_timer_depth++) { +} + +ScopedActionTimer::~ScopedActionTimer() { + --f_timer_depth; +} + +void ScopedActionTimer::quiet(bool value) { + quiet_ = value; +} + +bool ScopedActionTimer::quiet() const { + return quiet_; +} + +std::string ScopedActionTimer::action() const { + return action_; +} + +std::string ScopedActionTimer::pad() const { + if (depth() == 0) { + return ""; + } + return std::string(depth(), '#') + " "; +} + +int ScopedActionTimer::depth() const { + return depth_; +} + + +ScopedFinishTimer::ScopedFinishTimer(std::string action_str) + : ScopedActionTimer(action_str) { +} + +ScopedFinishTimer::~ScopedFinishTimer() { + if (!quiet()) { + vtr::printf_info("%s%s took %.2f seconds (max_rss %.1f MiB)\n", + pad().c_str(), action().c_str(), elapsed_sec(), + max_rss_mib()); + } +} + +ScopedStartFinishTimer::ScopedStartFinishTimer(std::string action_str) + : ScopedActionTimer(action_str) { + vtr::printf_info("%s%s\n", pad().c_str(), action().c_str()); +} + +ScopedStartFinishTimer::~ScopedStartFinishTimer() { + if (!quiet()) { + vtr::printf_info("%s%s took %.2f seconds (max_rss %.1f MiB, delta_rss %+.1f MiB)\n", + pad().c_str(), action().c_str(), elapsed_sec(), + max_rss_mib(), delta_max_rss_mib()); + } +} + +} //namespace diff --git a/libs/libvtrutil/src/vtr_time.h b/libs/libvtrutil/src/vtr_time.h new file mode 100644 index 000000000..83792ca58 --- /dev/null +++ b/libs/libvtrutil/src/vtr_time.h @@ -0,0 +1,94 @@ +#ifndef VTR_TIME_H +#define VTR_TIME_H +#include +#include + +namespace vtr { + + //Class for tracking time elapsed since construction + class Timer { + public: + Timer(); + virtual ~Timer() = default; + + //No copy + Timer(Timer&) = delete; + Timer& operator=(Timer&) = delete; + + //No move + Timer(Timer&&) = delete; + Timer& operator=(Timer&&) = delete; + + //Return elapsed time in seconds + float elapsed_sec() const; + + //Return peak memory resident set size (in MiB) + float max_rss_mib() const; + + //Return change in peak memory resident set size (in MiB) + float delta_max_rss_mib() const; + private: + using clock = std::chrono::steady_clock; + std::chrono::time_point start_; + + size_t initial_max_rss_; //Maximum resident set size In bytes + constexpr static float BYTE_TO_MIB = 1024*1024; + }; + + class ScopedActionTimer : public Timer { + public: + ScopedActionTimer(const std::string action); + ~ScopedActionTimer(); + + void quiet(bool value); + bool quiet() const; + std::string action() const; + + protected: + int depth() const; + std::string pad() const; + + private: + const std::string action_; + bool quiet_ = false; + int depth_; + }; + + //Scoped elapsed time class which prints the time elapsed for + //the specified action when it is destructed. + // + //For example: + // + // { + // vtr::ScopedFinishTimer timer("my_action"); + // + // //Do other work + // + // //Will print: 'my_action took X.XX seconds' when out-of-scope + // } + class ScopedFinishTimer : public ScopedActionTimer { + public: + ScopedFinishTimer(const std::string action); + ~ScopedFinishTimer(); + }; + + //Scoped elapsed time class which prints out the action when + //initialized and again both the action and elapsed time + //when destructed. + //For example: + // + // { + // vtr::ScopedStartFinishTimer timer("my_action") //Will print: 'my_action' + // + // //Do other work + // + // //Will print 'my_action took X.XX seconds' when out of scope + // } + class ScopedStartFinishTimer : public ScopedActionTimer { + public: + ScopedStartFinishTimer(const std::string action); + ~ScopedStartFinishTimer(); + }; +} + +#endif diff --git a/libs/libvtrutil/src/vtr_util.cpp b/libs/libvtrutil/src/vtr_util.cpp new file mode 100644 index 000000000..161fa7943 --- /dev/null +++ b/libs/libvtrutil/src/vtr_util.cpp @@ -0,0 +1,387 @@ +#include +#include +#include //For errno +#include +#include +#include + +#include "vtr_util.h" +#include "vtr_assert.h" +#include "vtr_memory.h" +#include "vtr_error.h" + + +namespace vtr { + + +std::string out_file_prefix; /* used by fopen */ +static int file_line_number = 0; /* file in line number being parsed (used by fgets) */ +static int cont; /* line continued? (used by strtok)*/ + + +//Splits the string 'text' along the specified delimiter characters in 'delims' +//The split strings (excluding the delimiters) are returned +std::vector split(const char* text, const std::string delims) { + if(text) { + std::string text_str(text); + return split(text_str, delims); + } + return std::vector(); +} + +std::vector split(const std::string& text, const std::string delims) { + std::vector tokens; + + std::string curr_tok; + for(char c : text) { + if(delims.find(c) != std::string::npos) { + //Delimeter character + if(!curr_tok.empty()) { + //At the end of the token + + //Save it + tokens.push_back(curr_tok); + + //Reset token + curr_tok.clear(); + } else { + //Pass + } + } else { + //Non-delimeter append to token + curr_tok += c; + } + } + + //Add last token + if(!curr_tok.empty()) { + //Save it + tokens.push_back(curr_tok); + } + return tokens; +} + +std::string replace_first(const std::string& input, const std::string& search, const std::string& replace) { + auto pos = input.find(search); + + std::string output(input, 0, pos); + output += replace; + output += std::string(input, pos + search.size()); + + return output; +} + +std::string replace_all(const std::string& input, const std::string& search, const std::string& replace) { + + std::string output; + + size_t last = 0; + size_t pos = input.find(search, last); //Find the first instance of 'search' starting at or after 'last' + while(pos != std::string::npos) { + output += input.substr(last, pos - last); //Append anything in the input string between last and current match + output += replace; //Add the replacement + + last = pos + search.size(); //Advance past the current match + + pos = input.find(search, last); //Look for the next match + } + output += input.substr(last, pos - last); //Append anything in 'input' after the last match + + return output; +} + +//Retruns true if str starts with prefix +bool starts_with(std::string str, std::string prefix) { + return str.find(prefix) == 0; +} + +//Returns a std::string formatted using a printf-style format string +std::string string_fmt(const char* fmt, ...) { + // Make a variable argument list + va_list va_args; + + // Initialize variable argument list + va_start(va_args, fmt); + + //Format string + std::string str = vstring_fmt(fmt, va_args); + + // Reset variable argument list + va_end(va_args); + + return str; +} + +//Returns a std::string formatted using a printf-style format string taking +//an explicit va_list +std::string vstring_fmt(const char* fmt, va_list args) { + + // We need to copy the args so we don't change them before the true formating + va_list va_args_copy; + va_copy(va_args_copy, args); + + //Determine the formatted length using a copy of the args + int len = std::vsnprintf(nullptr, 0, fmt, va_args_copy); + + va_end(va_args_copy); //Clean-up + + //Negative if there is a problem with the format string + VTR_ASSERT_MSG(len >= 0, "Problem decoding format string"); + + size_t buf_size = len + 1; //For terminator + + //Allocate a buffer + // unique_ptr will free buffer automatically + std::unique_ptr buf(new char[buf_size]); + + //Format into the buffer using the original args + len = std::vsnprintf(buf.get(), buf_size, fmt, args); + + VTR_ASSERT_MSG(len >= 0, "Problem decoding format string"); + VTR_ASSERT(static_cast(len) == buf_size - 1); + + //Build the string from the buffer + return std::string(buf.get(), len); +} + +/* An alternate for strncpy since strncpy doesn't work as most + * people would expect. This ensures null termination */ +char* strncpy(char *dest, const char *src, size_t size) { + /* Find string's length */ + size_t len = std::strlen(src); + + /* Cap length at (num - 1) to leave room for \0 */ + if (size <= len) + len = (size - 1); + + /* Copy as much of string as we can fit */ + std::memcpy(dest, src, len); + + /* explicit null termination */ + dest[len] = '\0'; + + return dest; +} + +char* strdup(const char *str) { + + if (str == nullptr ) { + return nullptr ; + } + + size_t Len = std::strlen(str); + //use calloc to already make the last char '\0' + return (char *)std::memcpy(vtr::calloc(Len+1, sizeof(char)), str, Len);; +} + +template +T atoT(const std::string& value, const std::string& type_name) { + //The c version of atof doesn't catch errors. + // + //This version uses stringstream to detect conversion errors + std::istringstream ss(value); + + T val; + ss >> val; + + if(ss.fail() || !ss.eof()) { + //Failed to convert, or did not consume all input + std::stringstream msg; + msg << "Failed to convert string '" << value << "' to " << type_name; + throw VtrError(msg.str(), __FILE__, __LINE__); + } + + return val; +} + +int atoi(const std::string& value) { + return atoT(value, "int"); +} + +double atod(const std::string& value) { + return atoT(value, "double"); +} + +float atof(const std::string& value) { + return atoT(value, "float"); +} + +unsigned atou(const std::string& value) { + return atoT(value, "unsigned int"); +} + +char* strtok(char *ptr, const char *tokens, FILE * fp, char *buf) { + + /* Get next token, and wrap to next line if \ at end of line. * + * There is a bit of a "gotcha" in strtok. It does not make a * + * copy of the character array which you pass by pointer on the * + * first call. Thus, you must make sure this array exists for * + * as long as you are using strtok to parse that line. Don't * + * use local buffers in a bunch of subroutines calling each * + * other; the local buffer may be overwritten when the stack is * + * restored after return from the subroutine. */ + + char *val; + + val = std::strtok(ptr, tokens); + for (;;) { + if (val != nullptr || cont == 0) + return (val); + + /* return unless we have a null value and a continuation line */ + if (vtr::fgets(buf, bufsize, fp) == nullptr ) + return (nullptr ); + + val = std::strtok(buf, tokens); + } +} + +FILE* fopen(const char *fname, const char *flag) { + FILE *fp; + size_t Len; + char *new_fname = nullptr; + file_line_number = 0; + + /* Appends a prefix string for output files */ + if (!out_file_prefix.empty()) { + if (std::strchr(flag, 'w')) { + Len = 1; /* NULL char */ + Len += std::strlen(out_file_prefix.c_str()); + Len += std::strlen(fname); + new_fname = (char *) vtr::malloc(Len * sizeof(char)); + strcpy(new_fname, out_file_prefix.c_str()); + strcat(new_fname, fname); + fname = new_fname; + } + } + + if (nullptr == (fp = std::fopen(fname, flag))) { + throw VtrError(string_fmt("Error opening file %s for %s access: %s.\n", fname, flag, strerror(errno)), __FILE__, __LINE__); + } + + if (new_fname) + std::free(new_fname); + + return (fp); +} + +int fclose(FILE* f) { + return std::fclose(f); +} + +char* fgets(char *buf, int max_size, FILE * fp) { + /* Get an input line, update the line number and cut off * + * any comment part. A \ at the end of a line with no * + * comment part (#) means continue. vtr::fgets should give * + * identical results for Windows (\r\n) and Linux (\n) * + * newlines, since it replaces each carriage return \r * + * by a newline character \n. Returns NULL after EOF. */ + + int ch; + int i; + + cont = 0; /* line continued? */ + file_line_number++; /* global variable */ + + for (i = 0; i < max_size - 1; i++) { /* Keep going until the line finishes or the buffer is full */ + + ch = std::fgetc(fp); + + if (std::feof(fp)) { /* end of file */ + if (i == 0) { + return nullptr ; /* required so we can write while (vtr::fgets(...) != NULL) */ + } else { /* no newline before end of file - last line must be returned */ + buf[i] = '\0'; + return buf; + } + } + + if (ch == '#') { /* comment */ + buf[i] = '\0'; + while ((ch = std::fgetc(fp)) != '\n' && !std::feof(fp)) + ; /* skip the rest of the line */ + return buf; + } + + if (ch == '\r' || ch == '\n') { /* newline (cross-platform) */ + if (i != 0 && buf[i - 1] == '\\') { /* if \ at end of line, line continued */ + cont = 1; + buf[i - 1] = '\n'; /* May need this for tokens */ + buf[i] = '\0'; + } else { + buf[i] = '\n'; + buf[i + 1] = '\0'; + } + return buf; + } + + buf[i] = ch; /* copy character into the buffer */ + + } + + /* Buffer is full but line has not terminated, so error */ + throw VtrError(string_fmt("Error on line %d -- line is too long for input buffer.\n" + "All lines must be at most %d characters long.\n", bufsize - 2), + __FILE__, __LINE__); + return nullptr; +} + +/* + * Returns line number of last opened and read file + */ +int get_file_line_number_of_last_opened_file() { + return file_line_number; +} + + +bool file_exists(const char* filename) { + FILE * file; + + if (filename == nullptr ) { + return false; + } + + file = std::fopen(filename, "r"); + if (file) { + std::fclose(file); + return true; + } + return false; +} + +/* Date:July 17th, 2013 + * Author: Daniel Chen + * Purpose: Checks the file extension of an file to ensure + * correct file format. Returns true if format is + * correct, and false otherwise. + * Note: This is probably a fragile check, but at least + * should prevent common problems such as swapping + * architecture file and blif file on the VPR + * command line. + */ + +bool check_file_name_extension(const char* file_name, + const char* file_extension){ + const char* str; + int len_extension; + + len_extension = std::strlen(file_extension); + str = std::strstr(file_name, file_extension); + if(str == nullptr || (*(str + len_extension) != '\0')){ + return false; + } + + return true; +} + +std::vector ReadLineTokens(FILE * InFile, int *LineNum) { + std::unique_ptr buf(new char[vtr::bufsize]); + + const char* line = vtr::fgets(buf.get(), vtr::bufsize, InFile); + + ++(*LineNum); + + return vtr::split(line); +} + +} //namespace diff --git a/libs/libvtrutil/src/vtr_util.h b/libs/libvtrutil/src/vtr_util.h new file mode 100644 index 000000000..ed5b16e3a --- /dev/null +++ b/libs/libvtrutil/src/vtr_util.h @@ -0,0 +1,102 @@ +#ifndef VTR_UTIL_H +#define VTR_UTIL_H + +#include +#include +#include +#include + +namespace vtr { + + //Splits the string 'text' along the specified delimiter characters in 'delims' + //The split strings (excluding the delimiters) are returned + std::vector split(const char* text, const std::string delims=" \t\n"); + std::vector split(const std::string& text, const std::string delims=" \t\n"); + + //Returns 'input' with the first instance of 'search' replaced with 'replace' + std::string replace_first(const std::string& input, const std::string& search, const std::string& replace); + + //Returns 'input' with all instances of 'search' replaced with 'replace' + std::string replace_all(const std::string& input, const std::string& search, const std::string& replace); + + //Retruns true if str starts with prefix + bool starts_with(std::string str, std::string prefix); + + //Returns a std::string formatted using a printf-style format string + std::string string_fmt(const char* fmt, ...); + + //Returns a std::string formatted using a printf-style format string taking + //an explicit va_list + std::string vstring_fmt(const char* fmt, va_list args); + + //Joins a sequence by a specified delimeter + // For example the sequence {"home", "user", "my_files", "test.blif"} with delim="/" + // would return "home/user/my_files/test.blif" + template + std::string join(Iter begin, Iter end, std::string delim); + + template + std::string join(Container container, std::string delim); + + template + std::string join(std::initializer_list list, std::string delim); + + /* + * Legacy c-style function replacements, typically these add extra error checking + * and/or correct 'unexpected' behaviour of the standard c-functions + */ + constexpr size_t bufsize = 32768; /* Maximum line length for various parsing proc. */ + char* strncpy(char *dest, const char *src, size_t size); + char* strdup(const char *str); + char* strtok(char *ptr, const char *tokens, FILE * fp, char *buf); + FILE* fopen(const char *fname, const char *flag); + int fclose(FILE* f); + char* fgets(char *buf, int max_size, FILE * fp); + + int atoi(const std::string& value); + unsigned atou(const std::string& value); + float atof(const std::string& value); + double atod(const std::string& value); + + /* + * File utilities + */ + int get_file_line_number_of_last_opened_file(); + bool file_exists(const char * filename); + bool check_file_name_extension(const char* file_name, + const char* file_extension); + + extern std::string out_file_prefix; + + /* + * Legacy ReadLine Tokening + */ + std::vector ReadLineTokens(FILE * InFile, int *LineNum); + + /* + * Template implementations + */ + template + std::string join(Iter begin, Iter end, std::string delim) { + std::string joined_str; + for(auto iter = begin; iter != end; ++iter) { + joined_str += *iter; + if(iter != end - 1) { + joined_str += delim; + } + } + return joined_str; + } + + template + std::string join(Container container, std::string delim) { + return join(std::begin(container), std::end(container), delim); + } + + template + std::string join(std::initializer_list list, std::string delim) { + return join(list.begin(), list.end(), delim); + } +} + +#endif diff --git a/libs/libvtrutil/src/vtr_vector.h b/libs/libvtrutil/src/vtr_vector.h new file mode 100644 index 000000000..649432010 --- /dev/null +++ b/libs/libvtrutil/src/vtr_vector.h @@ -0,0 +1,146 @@ +#ifndef VTR_VECTOR +#define VTR_VECTOR +#include +#include +#include +#include "vtr_range.h" + +namespace vtr { + +//A std::vector container which is indexed by K (instead of size_t). +// +//The main use of this container is to behave like a std::vector which is +//indexed by a vtr::StrongId. It assumes that K is explicitly convertable to size_t +//(i.e. via operator size_t()), and can be explicitly constructed from a size_t. +// +//If you need more std::map-like (instead of std::vector-like) behaviour see +//vtr::vector_map. +template +class vector : private std::vector { + public: + typedef K key_type; + + class key_iterator; + typedef vtr::Range key_range; + + public: + //Pass through std::vector's types + using typename std::vector::value_type; + using typename std::vector::allocator_type; + using typename std::vector::reference; + using typename std::vector::const_reference; + using typename std::vector::pointer; + using typename std::vector::const_pointer; + using typename std::vector::iterator; + using typename std::vector::const_iterator; + using typename std::vector::reverse_iterator; + using typename std::vector::const_reverse_iterator; + using typename std::vector::difference_type; + using typename std::vector::size_type; + + //Pass through std::vector's methods + using std::vector::vector; + + using std::vector::begin; + using std::vector::end; + using std::vector::rbegin; + using std::vector::rend; + using std::vector::cbegin; + using std::vector::cend; + using std::vector::crbegin; + using std::vector::crend; + + using std::vector::size; + using std::vector::max_size; + using std::vector::resize; + using std::vector::capacity; + using std::vector::empty; + using std::vector::reserve; + using std::vector::shrink_to_fit; + + using std::vector::front; + using std::vector::back; + using std::vector::data; + + using std::vector::assign; + using std::vector::push_back; + using std::vector::pop_back; + using std::vector::insert; + using std::vector::erase; + using std::vector::swap; + using std::vector::clear; + using std::vector::emplace; + using std::vector::emplace_back; + using std::vector::get_allocator; + + + //Don't include operator[] and at() from std::vector, + //since we redine them to take key_type instead of size_t + reference operator[](const key_type id) { + auto i = size_t(id); + return std::vector::operator[](i); + } + const_reference operator[](const key_type id) const { + auto i = size_t(id); + return std::vector::operator[](i); + } + reference at(const key_type id) { + auto i = size_t(id); + return std::vector::at(i); + } + const_reference at(const key_type id) const { + auto i = size_t(id); + return std::vector::at(i); + } + + //Returns a range containing the keys + key_range keys() const { + return vtr::make_range(key_begin(), key_end()); + } + public: + + //Iterator class which is convertable to the key_type + //This allows end-users to call the parent class's keys() member + //to iterate through the keys with a range-based for loop + class key_iterator : public std::iterator { + public: + //We use the intermediate type my_iter to avoid a potential ambiguity for which + //clang generates errors and warnings + using my_iter = typename std::iterator; + using typename my_iter::value_type; + using typename my_iter::iterator; + using typename my_iter::pointer; + using typename my_iter::reference; + + key_iterator(key_iterator::value_type init): value_(init) {} + + //vtr::vector assumes that the key time is convertable to size_t and + //that all the underlying IDs are zero-based and contiguous. That means + //we can just increment the underlying Id to build the next key. + key_iterator operator++() { + value_ = value_type(size_t(value_) + 1); + return *this; + } + key_iterator operator--() { + value_ = value_type(size_t(value_) - 1); + return *this; + } + reference operator*() { return value_; } + pointer operator->() { return &value_; } + + friend bool operator== (const key_iterator lhs, const key_iterator rhs) { return lhs.value_ == rhs.value_; } + friend bool operator!= (const key_iterator lhs, const key_iterator rhs) { return !(lhs == rhs); } + + private: + value_type value_; + }; + + private: + key_iterator key_begin() const { return key_iterator(key_type(0)); } + key_iterator key_end() const { return key_iterator(key_type(size())); } + +}; + + +} //namespace +#endif diff --git a/libs/libvtrutil/src/vtr_vector_map.h b/libs/libvtrutil/src/vtr_vector_map.h new file mode 100644 index 000000000..8e2863585 --- /dev/null +++ b/libs/libvtrutil/src/vtr_vector_map.h @@ -0,0 +1,144 @@ +#ifndef VTR_VECTOR_MAP +#define VTR_VECTOR_MAP +#include + +#include "vtr_assert.h" +#include "vtr_sentinels.h" + +namespace vtr { + +//A vector-like container which is indexed by K (instead of size_t as in std::vector). +// +//The main use of this container is to behave like a std::vector which is indexed by +//vtr::StrongId. +// +//Requires that K be convertable to size_t with the size_t operator (i.e. size_t()), and +//that the conversion results in a linearly increasing index into the underlying vector. +// +//This results in a container that is somewhat similar to a std::map (i.e. converts from one +//type to another), but requires contiguously ascending (i.e. linear) keys. Unlike std::map +//only the values are stored (at the specified index/key), reducing memory usage and improving +//cache locality. Furthermore, operator[] and find() return the value or iterator directly +//associated with the value (like std::vector) rather than a std::pair (like std::map). +//insert() takes both the key and value as separate arguments and has no return value. +// +//Additionally, vector_map will silently create values for 'gaps' in the index range (i.e. +//those elements are initialized with Sentinel::INVALID()). +// +//If you need a fully featured std::map like container without the above differences see +//vtr::linear_map. +// +//If you do not need std::map-like features see vtr::vector. +// +//Note that it is possible to use vector_map with sparse/non-contiguous keys, but this is typically +//memory inefficient as the underlying vector will allocate space for [0..size_t(max_key)-1], +//where max_key is the largest key that has been inserted. +// +//As with a std::vector, it is the caller's responsibility to ensure there is sufficient space +//when a given index/key before it is accessed. The exception to this are the find(), insert() and +//update() methods which handle non-existing keys gracefully. +template> +class vector_map { + public: //Public types + typedef typename std::vector::const_reference const_reference; + typedef typename std::vector::reference reference; + + typedef typename std::vector::iterator iterator; + typedef typename std::vector::const_iterator const_iterator; + typedef typename std::vector::const_reverse_iterator const_reverse_iterator; + + public: //Constructor + template + vector_map(Args&&... args) + : vec_(std::forward(args)...) + { } + + public: //Accessors + + //Iterators + const_iterator begin() const { return vec_.begin(); } + const_iterator end() const { return vec_.end(); } + const_reverse_iterator rbegin() const { return vec_.rbegin(); } + const_reverse_iterator rend() const { return vec_.rend(); } + + //Indexing + const_reference operator[] (const K n) const { + size_t index = size_t(n); + VTR_ASSERT_SAFE_MSG(index >= 0 && index < vec_.size(), "Out-of-range index"); + return vec_[index]; + } + + const_iterator find(const K key) const { + if(size_t(key) < vec_.size()) { + return vec_.begin() + size_t(key); + } else { + return vec_.end(); + } + } + + std::size_t size() const { return vec_.size(); } + + bool empty() const { return vec_.empty(); } + + bool contains(const K key) const { return size_t(key) < vec_.size(); } + size_t count(const K key) const { return contains(key) ? 1 : 0; } + + public: //Mutators + + //Delegate potentially overloaded functions to the underlying vector with perfect + //forwarding + template + void push_back(Args&&... args) { vec_.push_back(std::forward(args)...); } + + template + void emplace_back(Args&&... args) { vec_.emplace_back(std::forward(args)...); } + + template + void resize(Args&&... args) { vec_.resize(std::forward(args)...); } + + void clear() { vec_.clear(); } + + size_t capacity() const { return vec_.capacity(); } + void shrink_to_fit() { vec_.shrink_to_fit(); } + + //Iterators + iterator begin() { return vec_.begin(); } + iterator end() { return vec_.end(); } + + //Indexing + reference operator[] (const K n) { + VTR_ASSERT_SAFE_MSG(size_t(n) < vec_.size(), "Out-of-range index"); + return vec_[size_t(n)]; + } + + iterator find(const K key) { + if(size_t(key) < vec_.size()) { + return vec_.begin() + size_t(key); + } else { + return vec_.end(); + } + } + + void insert(const K key, const V value) { + if(size_t(key) >= vec_.size()) { + //Resize so key is in range + vec_.resize(size_t(key) + 1, Sentinel::INVALID()); + } + + //Insert the value + operator[](key) = value; + } + + void update(const K key, const V value) { insert(key, value); } + + //Swap (this enables std::swap via ADL) + friend void swap(vector_map& x, vector_map& y) { + std::swap(x.vec_, y.vec_); + } + private: + std::vector vec_; +}; + + +} //namespace +#endif diff --git a/libs/libvtrutil/src/vtr_version.cpp.in b/libs/libvtrutil/src/vtr_version.cpp.in new file mode 100644 index 000000000..b347b6d4d --- /dev/null +++ b/libs/libvtrutil/src/vtr_version.cpp.in @@ -0,0 +1,18 @@ +#include "vtr_version.h" + +//This file is automatically processed by CMAKE and replaces +//the values between ampersand's with the releveant CMAKE variable +//before being compiled. +namespace vtr { + const char* VERSION = "@VTR_VERSION@"; + const char* VERSION_SHORT = "@VTR_VERSION_SHORT@"; + + const size_t VERSION_MAJOR = @VTR_VERSION_MAJOR@; + const size_t VERSION_MINOR = @VTR_VERSION_MINOR@; + const size_t VERSION_PATCH = @VTR_VERSION_PATCH@; + const char* VERSION_PRERELEASE = "@VTR_VERSION_PRERELEASE@"; + + const char* VCS_REVISION = "@VTR_VCS_REVISION@"; + const char* COMPILER = "@VTR_COMPILER_INFO@"; + const char* BUILD_TIMESTAMP = "@VTR_BUILD_TIMESTAMP@"; +} diff --git a/libs/libvtrutil/src/vtr_version.h b/libs/libvtrutil/src/vtr_version.h new file mode 100644 index 000000000..a4821a2c0 --- /dev/null +++ b/libs/libvtrutil/src/vtr_version.h @@ -0,0 +1,19 @@ +#ifndef VTR_VERSION_H +#define VTR_VERSION_H +#include + +namespace vtr { + extern const char* VERSION; + extern const char* VERSION_SHORT; + + extern const size_t VERSION_MAJOR; + extern const size_t VERSION_MINOR; + extern const size_t VERSION_PATCH; + + extern const char* VCS_REVISION; + extern const char* COMPILER; + extern const char* BUILD_TIMESTAMP; + extern const char* PRERELEASE_TAG; +} + +#endif diff --git a/vpr7_x2p/vpr/CMakeLists.txt b/vpr7_x2p/vpr/CMakeLists.txt index c614f2afc..4d97fcf0d 100644 --- a/vpr7_x2p/vpr/CMakeLists.txt +++ b/vpr7_x2p/vpr/CMakeLists.txt @@ -12,6 +12,8 @@ endif() project("vpr7_x2p" C CXX) # idenify if we need graphics +set(ENABLE_VPR_GRAPHIC_CXX_FLAG true) +message(STATUS "Checking VPR graphics option ${ENABLE_VPR_GRAPHICS}") if (ENABLE_VPR_GRAPHICS) # check for dependencies message(STATUS "VPR graphics is turned on, searching for dependencies") @@ -20,11 +22,13 @@ if (ENABLE_VPR_GRAPHICS) if (NOT X11_FOUND) message(WARNING "Failed to find required X11 library (on debian/ubuntu try 'sudo apt-get install libx11-dev' to install)") #Disable - set(ENABLE_VPR_GRAPHICS false) + set(ENABLE_VPR_GRAPHIC_CXX_FLAG false) endif() +else () + set(ENABLE_VPR_GRAPHIC_CXX_FLAG false) endif() -if (NOT ENABLE_VPR_GRAPHICS) +if (NOT ENABLE_VPR_GRAPHIC_CXX_FLAG) # Add a flag to notify compiler not to consider graphic-related source codes set (DISABLE_GRAPHIC_FLAGS "-DNO_GRAPHICS") set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${DISABLE_GRAPHIC_FLAGS}") @@ -56,14 +60,16 @@ target_include_directories(libvpr PUBLIC ${LIB_INCLUDE_DIRS}) set_target_properties(libvpr PROPERTIES PREFIX "") #Avoid extra 'lib' prefix#Create the executable #Specify link-time dependancies -if (ENABLE_VPR_GRAPHICS) +if (ENABLE_VPR_GRAPHIC_CXX_FLAG) target_link_libraries(libvpr libarchfpga X11 + libvtrutil readline) else () target_link_libraries(libvpr libarchfpga + libvtrutil readline) endif() diff --git a/vpr7_x2p/vpr/SRC/base/vpr_types.h b/vpr7_x2p/vpr/SRC/base/vpr_types.h index e70b3f24e..53b0cd303 100755 --- a/vpr7_x2p/vpr/SRC/base/vpr_types.h +++ b/vpr7_x2p/vpr/SRC/base/vpr_types.h @@ -35,6 +35,7 @@ #include "arch_types.h" #include +#include /******************************************************************************* * Global data types and constants @@ -946,6 +947,7 @@ struct s_rr_node { short yhigh; short ptc_num; + std::vector track_ids; /* Tileable arch support: Track indices in each GSB */ short cost_index; short occ; diff --git a/vpr7_x2p/vpr/SRC/device/rr_graph/chan_node_details.cpp b/vpr7_x2p/vpr/SRC/device/rr_graph/chan_node_details.cpp new file mode 100644 index 000000000..2e8c80708 --- /dev/null +++ b/vpr7_x2p/vpr/SRC/device/rr_graph/chan_node_details.cpp @@ -0,0 +1,268 @@ +/********************************************************** + * MIT License + * + * Copyright (c) 2018 LNIS - The University of Utah + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + ***********************************************************************/ + +/************************************************************************ + * Filename: chan_node_details.cpp + * Created by: Xifan Tang + * Change history: + * +-------------------------------------+ + * | Date | Author | Notes + * +-------------------------------------+ + * | 2019/06/14 | Xifan Tang | Created + * +-------------------------------------+ + ***********************************************************************/ +/************************************************************************ + * This file contains member functions for class ChanNodeDetails + ***********************************************************************/ +#include +#include +#include "chan_node_details.h" + +/************************************************************************ + * Constructors + ***********************************************************************/ +ChanNodeDetails::ChanNodeDetails(const ChanNodeDetails& src) { + /* duplicate */ + size_t chan_width = src.get_chan_width(); + this->reserve(chan_width); + for (size_t itrack = 0; itrack < chan_width; ++itrack) { + track_node_ids_.push_back(src.get_track_node_id(itrack)); + track_direction_.push_back(src.get_track_direction(itrack)); + seg_length_.push_back(src.get_track_segment_length(itrack)); + track_start_.push_back(src.is_track_start(itrack)); + track_end_.push_back(src.is_track_end(itrack)); + } +} + +ChanNodeDetails::ChanNodeDetails() { + this->clear(); +} + +/************************************************************************ + * Accessors + ***********************************************************************/ +size_t ChanNodeDetails::get_chan_width() const { + assert(validate_chan_width()); + return track_node_ids_.size(); +} + +size_t ChanNodeDetails::get_track_node_id(size_t track_id) const { + assert(validate_track_id(track_id)); + return track_node_ids_[track_id]; +} + +e_direction ChanNodeDetails::get_track_direction(size_t track_id) const { + assert(validate_track_id(track_id)); + return track_direction_[track_id]; +} + +size_t ChanNodeDetails::get_track_segment_length(size_t track_id) const { + assert(validate_track_id(track_id)); + return seg_length_[track_id]; +} + +bool ChanNodeDetails::is_track_start(size_t track_id) const { + assert(validate_track_id(track_id)); + return track_start_[track_id]; +} + +bool ChanNodeDetails::is_track_end(size_t track_id) const { + assert(validate_track_id(track_id)); + return track_end_[track_id]; +} + +/* Track_id is the starting point of group (whose is_start should be true) + * This function will try to find the track_ids with the same directionality as track_id and seg_length + * A group size is the number of such nodes between the starting points (include the 1st starting point) + */ +std::vector ChanNodeDetails::get_seg_group(size_t track_id) const { + assert(validate_chan_width()); + assert(validate_track_id(track_id)); + assert(is_track_start(track_id)); + + std::vector group; + /* Make sure a clean start */ + group.clear(); + + /* track_id is the first element */ + group.push_back(track_id); + + for (size_t itrack = track_id; itrack < get_chan_width(); ++itrack) { + if ( (get_track_direction(itrack) == get_track_direction(track_id) ) + && (get_track_segment_length(itrack) == get_track_segment_length(track_id)) ) { + if ( (false == is_track_start(itrack)) + || ( (true == is_track_start(itrack)) && (itrack == track_id)) ) { + group.push_back(itrack); + continue; + } + /* Stop if this another starting point */ + if (true == is_track_start(itrack)) { + break; + } + } + } + return group; +} + +/* Get a list of track_ids with the given list of track indices */ +std::vector ChanNodeDetails::get_seg_group_node_id(std::vector seg_group) const { + std::vector group; + /* Make sure a clean start */ + group.clear(); + + for (size_t id = 0; id < seg_group.size(); ++id) { + assert(validate_track_id(seg_group[id])); + group.push_back(get_track_node_id(seg_group[id])); + } + + return group; +} + +/* Get the number of tracks that starts in this routing channel */ +size_t ChanNodeDetails::get_num_starting_tracks() const { + size_t counter = 0; + for (size_t itrack = 0; itrack < get_chan_width(); ++itrack) { + if (false == is_track_start(itrack)) { + continue; + } + counter++; + } + return counter; +} + +/* Get the number of tracks that ends in this routing channel */ +size_t ChanNodeDetails::get_num_ending_tracks() const { + size_t counter = 0; + for (size_t itrack = 0; itrack < get_chan_width(); ++itrack) { + if (false == is_track_end(itrack)) { + continue; + } + counter++; + } + return counter; +} + +/************************************************************************ + * Mutators + ***********************************************************************/ +/* Reserve the capacitcy of vectors */ +void ChanNodeDetails::reserve(size_t chan_width) { + track_node_ids_.reserve(chan_width); + track_direction_.reserve(chan_width); + seg_length_.reserve(chan_width); + track_start_.reserve(chan_width); + track_end_.reserve(chan_width); +} + +/* Add a track to the channel */ +void ChanNodeDetails::add_track(size_t track_node_id, e_direction track_direction, size_t seg_length, size_t is_start, size_t is_end) { + track_node_ids_.push_back(track_node_id); + track_direction_.push_back(track_direction); + seg_length_.push_back(seg_length); + track_start_.push_back(is_start); + track_end_.push_back(is_end); +} + +/* Set tracks with a given direction to start */ +void ChanNodeDetails::set_tracks_start(e_direction track_direction) { + for (size_t inode = 0; inode < get_chan_width(); ++inode) { + /* Bypass non-match tracks */ + if (track_direction != get_track_direction(inode)) { + } + track_start_[inode] = true; + } +} + +/* Set tracks with a given direction to end */ +void ChanNodeDetails::set_tracks_end(e_direction track_direction) { + for (size_t inode = 0; inode < get_chan_width(); ++inode) { + /* Bypass non-match tracks */ + if (track_direction != get_track_direction(inode)) { + } + track_end_[inode] = true; + } +} + +/* rotate the track_node_id by an offset */ +void ChanNodeDetails::rotate_track_node_id(size_t offset, bool counter_rotate) { + /* Rotate the node_ids by groups + * A group begins from a track_start and ends before another track_start + */ + assert(validate_chan_width()); + for (size_t itrack = 0; itrack < get_chan_width(); ++itrack) { + /* Bypass non-start segment */ + if (false == is_track_start(itrack) ) { + continue; + } + /* Find the group nodes */ + std::vector track_group = get_seg_group(itrack); + /* Build a vector of the node ids of the tracks */ + std::vector track_group_node_id = get_seg_group_node_id(track_group); + /* Rotate or Counter rotate */ + if (true == counter_rotate) { + std::rotate(track_group_node_id.begin(), track_group_node_id.end() - offset, track_group_node_id.end()); + } else { + std::rotate(track_group_node_id.begin(), track_group_node_id.begin() + offset, track_group_node_id.end()); + } + /* Update the node_ids */ + for (size_t inode = 0; inode < track_group.size(); ++inode) { + track_node_ids_[track_group[inode]] = track_group_node_id[inode]; + } + } + return; +} + +void ChanNodeDetails::clear() { + track_node_ids_.clear(); + track_direction_.clear(); + seg_length_.clear(); + track_start_.clear(); + track_end_.clear(); +} + +/************************************************************************ + * Validators + ***********************************************************************/ +bool ChanNodeDetails::validate_chan_width() const { + size_t chan_width = track_node_ids_.size(); + if ( (chan_width == track_direction_.size()) + &&(chan_width == seg_length_.size()) + &&(chan_width == track_start_.size()) + &&(chan_width == track_end_.size()) ) { + return true; + } + return false; +} + +bool ChanNodeDetails::validate_track_id(size_t track_id) const { + if ( (track_id < track_node_ids_.size()) + && (track_id < track_direction_.size()) + && (track_id < seg_length_.size()) + && (track_id < track_start_.size()) + && (track_id < track_end_.size()) ) { + return true; + } + return false; +} + diff --git a/vpr7_x2p/vpr/SRC/device/rr_graph/chan_node_details.h b/vpr7_x2p/vpr/SRC/device/rr_graph/chan_node_details.h new file mode 100644 index 000000000..8b45ba25f --- /dev/null +++ b/vpr7_x2p/vpr/SRC/device/rr_graph/chan_node_details.h @@ -0,0 +1,105 @@ +/********************************************************** + * MIT License + * + * Copyright (c) 2018 LNIS - The University of Utah + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + ***********************************************************************/ + +/************************************************************************ + * Filename: chan_node_details.h + * Created by: Xifan Tang + * Change history: + * +-------------------------------------+ + * | Date | Author | Notes + * +-------------------------------------+ + * | 2019/06/11 | Xifan Tang | Created + * +-------------------------------------+ + ***********************************************************************/ +/************************************************************************ + * This file contains a class to model the details of routing node + * in a channel: + * 1. segment information: length, frequency etc. + * 2. starting point of segment + * 3. ending point of segment + * 4. potentail track_id(ptc_num) of each segment + ***********************************************************************/ + +/* IMPORTANT: + * The following preprocessing flags are added to + * avoid compilation error when this headers are included in more than 1 times + */ +#ifndef CHAN_NODE_DETAILS_H +#define CHAN_NODE_DETAILS_H + +/* + * Notes in include header files in a head file + * Only include the neccessary header files + * that is required by the data types in the function/class declarations! + */ +/* Header files should be included in a sequence */ +/* Standard header files required go first */ +#include +#include "vpr_types.h" + +/************************************************************************ + * ChanNodeDetails records segment length, directionality and starting of routing tracks + * +---------------------------------+ + * | Index | Direction | Start Point | + * +---------------------------------+ + * | 0 | --------> | Yes | + * +---------------------------------+ + ***********************************************************************/ + + +class ChanNodeDetails { + public : /* Constructor */ + ChanNodeDetails(const ChanNodeDetails&); /* Duplication */ + ChanNodeDetails(); /* Initilization */ + public: /* Accessors */ + size_t get_chan_width() const; + size_t get_track_node_id(size_t track_id) const; + e_direction get_track_direction(size_t track_id) const; + size_t get_track_segment_length(size_t track_id) const; + bool is_track_start(size_t track_id) const; + bool is_track_end(size_t track_id) const; + std::vector get_seg_group(size_t track_id) const; + std::vector get_seg_group_node_id(std::vector seg_group) const; + size_t get_num_starting_tracks() const; + size_t get_num_ending_tracks() const; + public: /* Mutators */ + void reserve(size_t chan_width); /* Reserve the capacitcy of vectors */ + void add_track(size_t track_node_id, e_direction track_direction, size_t seg_length, size_t is_start, size_t is_end); + void set_tracks_start(e_direction track_direction); + void set_tracks_end(e_direction track_direction); + void rotate_track_node_id(size_t offset, bool counter_rotate); /* rotate the track_node_id by an offset */ + void clear(); + private: /* validators */ + bool validate_chan_width() const; + bool validate_track_id(size_t track_id) const; + private: /* Internal data */ + std::vector track_node_ids_; /* indices of each track */ + std::vector track_direction_; /* direction of each track */ + std::vector seg_length_; /* Length of each segment */ + std::vector track_start_; /* flag to identify if this is the starting point of the track */ + std::vector track_end_; /* flag to identify if this is the ending point of the track */ +}; + +#endif + diff --git a/vpr7_x2p/vpr/SRC/device/rr_graph/gsb_graph.c b/vpr7_x2p/vpr/SRC/device/rr_graph/gsb_graph.c new file mode 100644 index 000000000..40159d05e --- /dev/null +++ b/vpr7_x2p/vpr/SRC/device/rr_graph/gsb_graph.c @@ -0,0 +1,66 @@ +/********************************************************** + * MIT License + * + * Copyright (c) 2018 LNIS - The University of Utah + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + ***********************************************************************/ + +/************************************************************************ + * Filename: rr_graph_gsb.c + * Created by: Xifan Tang + * Change history: + * +-------------------------------------+ + * | Date | Author | Notes + * +-------------------------------------+ + * | 2019/06/12 | Xifan Tang | Created + * +-------------------------------------+ + ***********************************************************************/ +/************************************************************************ + * This file constains the member functions for class GSBConn + ************************************************************************/ + +#include "gsb_graph.h" +#include "vtr_vector_map.h" + +/************************************************************************ + * Constructors for class GSBGraph + ************************************************************************/ +/* Duplicate a object */ +GSBGraph::GSBGraph(const GSBGraph& gsb_graph) { + +} + +GSBGraph::GSBGraph() { + coordinator_.clear(); + +} + + +/************************************************************************ + * Aggregators for class GSBGraph + ************************************************************************/ +/* Accessors: Aggregators */ +GSBGraph::node_range GSBGraph::nodes() const { + return vtr::make_range(node_ids_.begin(), node_ids_.end()); +} + +GSBGraph::edge_range GSBGraph::edges() const { + return vtr::make_range(edge_ids_.begin(), edge_ids_.end()); +} diff --git a/vpr7_x2p/vpr/SRC/device/rr_graph/gsb_graph.h b/vpr7_x2p/vpr/SRC/device/rr_graph/gsb_graph.h new file mode 100644 index 000000000..0ada48d32 --- /dev/null +++ b/vpr7_x2p/vpr/SRC/device/rr_graph/gsb_graph.h @@ -0,0 +1,166 @@ +/********************************************************** + * MIT License + * + * Copyright (c) 2018 LNIS - The University of Utah + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + ***********************************************************************/ + +/************************************************************************ + * Filename: gsb_graph.h + * Created by: Xifan Tang + * Change history: + * +-------------------------------------+ + * | Date | Author | Notes + * +-------------------------------------+ + * | 2019/06/12 | Xifan Tang | Created + * +-------------------------------------+ + ***********************************************************************/ +/************************************************************************ + * This file constains a class to model the connection of a + * General Switch Block (GSB), which is a unified block of Connection Blocks + * and Switch Blocks. + * This block contains + * 1. A switch block + * 2. A X-direction Connection block locates at the left side of the switch block + * 2. A Y-direction Connection block locates at the top side of the switch block + * + * +---------------------------------+ + * | Y-direction CB | + * | [x][y + 1] | + * +---------------------------------+ + * + * TOP SIDE + * +-------------+ +---------------------------------+ + * | | | OPIN_NODE CHAN_NODES OPIN_NODES | + * | | | | + * | | | OPIN_NODES OPIN_NODES | + * | X-direction | | | + * | CB | LEFT SIDE | Switch Block | RIGHT SIDE + * | [x][y] | | [x][y] | + * | | | | + * | | | CHAN_NODES CHAN_NODES | + * | | | | + * | | | OPIN_NODES OPIN_NODES | + * | | | | + * | | | OPIN_NODE CHAN_NODES OPIN_NODES | + * +-------------+ +---------------------------------+ + * BOTTOM SIDE + * + ***********************************************************************/ + +/* IMPORTANT: + * The following preprocessing flags are added to + * avoid compilation error when this headers are included in more than 1 times + */ +#ifndef GSB_GRAPH_H +#define GSB_GRAPH_H + +/* + * Notes in include header files in a head file + * Only include the neccessary header files + * that is required by the data types in the function/class declarations! + */ +/* Header files should be included in a sequence */ +/* Standard header files required go first */ +#include + +/* External library header files */ +#include "vtr_vector.h" +#include "vtr_range.h" + +#include "device_coordinator.h" +#include "vpr_types.h" +#include "rr_graph_fwd.h" + +/* Define open nodes */ +#define OPEN_NODE_ID RRNodeId(-1) +#define OPEN_EDGE_ID RREdgeId(-1) + +/*********************************************************************** + * This data structure focuses on modeling the internal pin-to-pin connections. + * It is basically a collection of nodes and edges. + * To make the data structure general, the nodes and edges are not linked to any another data + * structures. + * + * node_ids_: a collection of nodes (basically ids) modelling routing tracks + * which locate at each side of the GSB <0..num_nodes_per_side-1> + * + * node_directions_: Indicate if this node is an input or an output of the GSB + * <0..num_nodes_per_side-1> + * + * node_types_: specify the types of the node, CHANX|CHANY|IPIN|OPIN + * + * node_sides_: specify the sides of the node on a GSB, TOP|RIGHT|BOTTOM|LEFT + * + * node_grid_sides_: specify the side of the node on which side of a GRID + * for CHANX and CHANY, it is an invalid value + * <0..num_nodes_per_side-1> + * + * node_in_edges_: indcies of input edges of a node + * <0..num_nodes><0..num_input_edgess-1> + * + * node_out_edges_: indcies of output edges of a node + * <0..num_nodes><0..num_output_edges-1> + * + * edge_ids_: a collection of indices of edges, <0..num_edges-1>, which connects the nodes + * + * edge_src_nodes_: indcies of input nodes of an edge (driving nodes for each edge) + * <0..num_input_nodes-1> + * + * edge_sink_nodes_: indices of output nodes of an edge (fan-out nodes for each edge) + * <0..num_output_nodes-1> + * + ***********************************************************************/ + +class GSBGraph { + public: /* Types */ + typedef vtr::vector::const_iterator node_iterator; + typedef vtr::vector::const_iterator edge_iterator; + typedef vtr::Range node_range; + typedef vtr::Range edge_range; + public: /* Constructors */ + GSBGraph(const GSBGraph&); /* A constructor to duplicate */ + GSBGraph(); + public: /* Accessors */ + /* Aggregates */ + node_range nodes() const; + edge_range edges() const; + private: /* Internal Data */ + /* Coordinator of this GSB */ + DeviceCoordinator coordinator_; + + /* nodes on each side */ + vtr::vector node_ids_; + vtr::vector node_types_; + vtr::vector node_sides_; + vtr::vector node_directions_; + vtr::vector node_grid_sides_; + + vtr::vector> node_in_edges; + vtr::vector> node_out_edges; + + /* edges */ + vtr::vector edge_ids_; + vtr::vector edge_src_nodes_; /* each element is a node_id */ + vtr::vector edge_sink_nodes_; /* each element is a node_id */ +}; + +#endif + diff --git a/vpr7_x2p/vpr/SRC/device/rr_graph/rr_graph_fwd.h b/vpr7_x2p/vpr/SRC/device/rr_graph/rr_graph_fwd.h new file mode 100644 index 000000000..cd4351f34 --- /dev/null +++ b/vpr7_x2p/vpr/SRC/device/rr_graph/rr_graph_fwd.h @@ -0,0 +1,18 @@ +#ifndef RR_GRAPH_OBJ_FWD_H +#define RR_GRAPH_OBJ_FWD_H + +#include "vtr_strong_id.h" + +class RRGraph; + +struct rr_node_id_tag; +struct rr_edge_id_tag; +struct rr_switch_id_tag; +struct rr_segment_id_tag; + +typedef vtr::StrongId RRNodeId; +typedef vtr::StrongId RREdgeId; +typedef vtr::StrongId RRSwitchId; +typedef vtr::StrongId RRSegmentId; + +#endif diff --git a/vpr7_x2p/vpr/SRC/device/rr_graph/rr_graph_tileable_builder.c b/vpr7_x2p/vpr/SRC/device/rr_graph/rr_graph_tileable_builder.c index 2e9980c38..d5cbd8bfc 100644 --- a/vpr7_x2p/vpr/SRC/device/rr_graph/rr_graph_tileable_builder.c +++ b/vpr7_x2p/vpr/SRC/device/rr_graph/rr_graph_tileable_builder.c @@ -44,6 +44,7 @@ #include #include #include +#include #include "vpr_types.h" #include "globals.h" #include "vpr_utils.h" @@ -54,6 +55,391 @@ #include "fpga_x2p_types.h" #include "rr_graph_tileable_builder.h" +#include "chan_node_details.h" +#include "device_coordinator.h" + +/************************************************************************ + * Local function in the file + ***********************************************************************/ + +/************************************************************************ + * Generate the number of tracks for each types of routing segments + * w.r.t. the frequency of each of segments and channel width + * Note that if we dertermine the number of tracks per type using + * chan_width * segment_frequency / total_freq may cause + * The total track num may not match the chan_width, + * therefore, we assign tracks one by one until we meet the frequency requirement + * In this way, we can assign the number of tracks with repect to frequency + ***********************************************************************/ +static +std::vector get_num_tracks_per_seg_type(size_t chan_width, + std::vector segment_inf, + bool use_full_seg_groups) { + std::vector result; + std::vector demand; + /* Make sure a clean start */ + result.resize(segment_inf.size()); + demand.resize(segment_inf.size()); + + /* Scale factor so we can divide by any length + * and still use integers */ + /* Get the sum of frequency */ + size_t scale = 1; + size_t freq_sum = 0; + for (size_t iseg = 0; iseg < segment_inf.size(); ++iseg) { + scale *= segment_inf[iseg].length; + freq_sum += segment_inf[iseg].frequency; + } + size_t reduce = scale * freq_sum; + + /* Init assignments to 0 and set the demand values */ + /* Get the fraction of each segment type considering the frequency: + * num_track_per_seg = chan_width * (freq_of_seg / sum_freq) + */ + for (size_t iseg = 0; iseg < segment_inf.size(); ++iseg) { + result[iseg] = 0; + demand[iseg] = scale * chan_width * segment_inf[iseg].frequency; + if (true == use_full_seg_groups) { + demand[iseg] /= segment_inf[iseg].length; + } + } + + /* check if the sum of num_tracks, matches the chan_width */ + /* Keep assigning tracks until we use them up */ + size_t assigned = 0; + size_t size = 0; + size_t imax = 0; + while (assigned < chan_width) { + /* Find current maximum demand */ + double max = 0; + for (size_t iseg = 0; iseg < segment_inf.size(); ++iseg) { + if (demand[iseg] > max) { + imax = iseg; + } + max = std::max(demand[iseg], max); + } + + /* Assign tracks to the type and reduce the types demand */ + size = (use_full_seg_groups ? segment_inf[imax].length : 1); + demand[imax] -= reduce; + result[imax] += size; + assigned += size; + } + + /* Undo last assignment if we were closer to goal without it */ + if ((assigned - chan_width) > (size / 2)) { + result[imax] -= size; + } + + return result; +} + +/************************************************************************ + * Build details of routing tracks in a channel + * The function will + * 1. Assign the segments for each routing channel, + * To be specific, for each routing track, we assign a routing segment. + * The assignment is subject to users' specifications, such as + * a. length of each type of segment + * b. frequency of each type of segment. + * c. routing channel width + * + * 2. The starting point of each segment in the channel will be assigned + * For each segment group with same directionality (tracks have the same length), + * every L track will be a starting point (where L denotes the length of segments) + * In this case, if the number of tracks is not a multiple of L, + * indeed we may have some | Yes | + * +---------------------------------+ + * | 1 | <-------- | Yes | + * +---------------------------------+ + * | 2 | --------> | No | + * +---------------------------------+ + * | 3 | <-------- | No | + * +---------------------------------+ + * | 4 | --------> | No | + * +---------------------------------+ + * | 5 | <-------- | No | + * +---------------------------------+ + * | 7 | --------> | No | + * +---------------------------------+ + * | 8 | <-------- | No | + * +---------------------------------+ + * | 9 | --------> | Yes | + * +---------------------------------+ + * | 10 | <-------- | Yes | + * +---------------------------------+ + * | 11 | --------> | No | + * +---------------------------------+ + * | 12 | <-------- | No | + * +---------------------------------+ + * + * 3. SPECIAL for fringes: TOP|RIGHT|BOTTOM|RIGHT + * if device_side is NUM_SIDES, we assume this channel does not locate on borders + * All segments will start and ends with no exception + * + * 4. IMPORTANT: we should be aware that channel width maybe different + * in X-direction and Y-direction channels!!! + * So we will load segment details for different channels + ***********************************************************************/ +static +ChanNodeDetails build_unidir_chan_node_details(size_t chan_width, size_t max_seg_length, + enum e_side device_side, + std::vector segment_inf) { + ChanNodeDetails chan_node_details; + /* Correct the chan_width: it should be an even number */ + if (0 != chan_width % 2) { + chan_width++; /* increment it to be even */ + } + assert (0 == chan_width % 2); + + /* Reserve channel width */ + chan_node_details.reserve(chan_width); + /* Return if zero width is forced */ + if (0 == chan_width) { + return chan_node_details; + } + + /* Find the number of segments required by each group */ + std::vector num_tracks = get_num_tracks_per_seg_type(chan_width/2, segment_inf, TRUE); + + /* Add node to ChanNodeDetails */ + size_t cur_track = 0; + for (size_t iseg = 0; iseg < segment_inf.size(); ++iseg) { + /* segment length will be set to maxium segment length if this is a longwire */ + size_t seg_len = segment_inf[iseg].length; + if (TRUE == segment_inf[iseg].longline) { + seg_len = max_seg_length; + } + for (size_t itrack = 0; itrack < num_tracks[iseg]; ++itrack) { + bool seg_start = false; + /* Every length of wire, we set a starting point */ + if (0 == itrack % seg_len) { + seg_start = true; + } + /* Since this is a unidirectional routing architecture, + * Add a pair of tracks, 1 INC_DIRECTION track and 1 DEC_DIRECTION track + */ + chan_node_details.add_track(cur_track, INC_DIRECTION, seg_len, seg_start, false); + cur_track++; + chan_node_details.add_track(cur_track, DEC_DIRECTION, seg_len, seg_start, false); + cur_track++; + } + } + /* Check if all the tracks have been satisified */ + assert (cur_track == chan_width); + + /* If this is on the border of a device, segments should start */ + switch (device_side) { + case TOP: + case RIGHT: + /* INC_DIRECTION should all end */ + chan_node_details.set_tracks_end(INC_DIRECTION); + /* DEC_DIRECTION should all start */ + chan_node_details.set_tracks_start(DEC_DIRECTION); + break; + case BOTTOM: + case LEFT: + /* INC_DIRECTION should all start */ + chan_node_details.set_tracks_start(INC_DIRECTION); + /* DEC_DIRECTION should all end */ + chan_node_details.set_tracks_end(DEC_DIRECTION); + break; + case NUM_SIDES: + break; + default: + vpr_printf(TIO_MESSAGE_ERROR, + "(File:%s, [LINE%d]) Invalid device_side!\n", + __FILE__, __LINE__); + exit(1); + } + + return chan_node_details; +} + +/* Deteremine the side of a io grid */ +static +enum e_side determine_io_grid_pin_side(const DeviceCoordinator& device_size, + const DeviceCoordinator& grid_coordinator) { + /* TOP side IO of FPGA */ + if (device_size.get_y() == grid_coordinator.get_y()) { + return BOTTOM; /* Such I/O has only Bottom side pins */ + } else if (device_size.get_x() == grid_coordinator.get_x()) { /* RIGHT side IO of FPGA */ + return LEFT; /* Such I/O has only Left side pins */ + } else if (0 == grid_coordinator.get_y()) { /* BOTTOM side IO of FPGA */ + return TOP; /* Such I/O has only Top side pins */ + } else if (0 == grid_coordinator.get_x()) { /* LEFT side IO of FPGA */ + return RIGHT; /* Such I/O has only Right side pins */ + } else { + vpr_printf(TIO_MESSAGE_ERROR, "(File:%s, [LINE%d])I/O Grid is in the center part of FPGA! Currently unsupported!\n", + __FILE__, __LINE__); + exit(1); + } +} + +/************************************************************************ + * Get a list of pin_index for a grid (either OPIN or IPIN) + * For IO_TYPE, only one side will be used, we consider one side of pins + * For others, we consider all the sides + ***********************************************************************/ +static +std::vector get_grid_side_pins(const t_grid_tile& cur_grid, enum e_pin_type pin_type, enum e_side pin_side, int pin_height) { + std::vector pin_list; + /* Make sure a clear start */ + pin_list.clear(); + + for (int ipin = 0; ipin < cur_grid.type->num_pins; ++ipin) { + if ( (1 == cur_grid.type->pinloc[pin_height][pin_side][ipin]) + && (pin_type == cur_grid.type->pin_class[ipin]) ) { + pin_list.push_back(ipin); + } + } + return pin_list; +} + +/************************************************************************ + * Get the number of pins for a grid (either OPIN or IPIN) + * For IO_TYPE, only one side will be used, we consider one side of pins + * For others, we consider all the sides + ***********************************************************************/ +static +size_t get_grid_num_pins(const t_grid_tile& cur_grid, enum e_pin_type pin_type, enum e_side io_side) { + size_t num_pins = 0; + Side io_side_manager(io_side); + /* For IO_TYPE sides */ + for (size_t side = 0; side < NUM_SIDES; ++side) { + Side side_manager(side); + /* skip unwanted sides */ + if ( (IO_TYPE == cur_grid.type) + && (side != io_side_manager.to_size_t()) ) { + continue; + } + /* Get pin list */ + for (int height = 0; height < cur_grid.type->height; ++height) { + std::vector pin_list = get_grid_side_pins(cur_grid, pin_type, side_manager.get_side(), height); + num_pins += pin_list.size(); + } + } + + return num_pins; +} + +/************************************************************************ + * Estimate the number of rr_nodes per category: + * CHANX, CHANY, IPIN, OPIN, SOURCE, SINK + ***********************************************************************/ +static +std::vector estimate_num_rr_nodes_per_type(const DeviceCoordinator& device_size, + std::vector> grids, + std::vector chan_width, + std::vector segment_infs) { + std::vector num_rr_nodes_per_type; + /* reserve the vector: + * we have the follow type: + * SOURCE = 0, SINK, IPIN, OPIN, CHANX, CHANY, INTRA_CLUSTER_EDGE, NUM_RR_TYPES + * NUM_RR_TYPES and INTRA_CLUSTER_EDGE will be 0 + */ + num_rr_nodes_per_type.resize(NUM_RR_TYPES); + /* Make sure a clean start */ + for (size_t i = 0; i < NUM_RR_TYPES; ++i) { + num_rr_nodes_per_type[i] = 0; + } + + /************************************************************************ + * 1. Search the grid and find the number OPINs and IPINs per grid + * Note that the number of SOURCE nodes are the same as OPINs + * and the number of SINK nodes are the same as IPINs + ***********************************************************************/ + for (size_t ix = 0; ix < grids.size(); ++ix) { + for (size_t iy = 0; iy < grids[ix].size(); ++iy) { + /* Skip EMPTY tiles */ + if (EMPTY_TYPE == grids[ix][iy].type) { + continue; + } + /* Skip height>1 tiles (mostly heterogeneous blocks) */ + if (0 < grids[ix][iy].offset) { + continue; + } + enum e_side io_side = NUM_SIDES; + /* If this is the block on borders, we consider IO side */ + if (IO_TYPE == grid[ix][iy].type) { + DeviceCoordinator io_device_size(device_size.get_x() - 1, device_size.get_y() - 1); + DeviceCoordinator grid_coordinator(ix, iy); + io_side = determine_io_grid_pin_side(device_size, grid_coordinator); + } + /* get the number of OPINs */ + num_rr_nodes_per_type[OPIN] += get_grid_num_pins(grids[ix][iy], DRIVER, io_side); + /* get the number of IPINs */ + num_rr_nodes_per_type[IPIN] += get_grid_num_pins(grids[ix][iy], RECEIVER, io_side); + } + } + /* SOURCE and SINK */ + num_rr_nodes_per_type[SOURCE] = num_rr_nodes_per_type[OPIN]; + num_rr_nodes_per_type[SINK] = num_rr_nodes_per_type[IPIN]; + + /************************************************************************ + * 2. Assign the segments for each routing channel, + * To be specific, for each routing track, we assign a routing segment. + * The assignment is subject to users' specifications, such as + * a. length of each type of segment + * b. frequency of each type of segment. + * c. routing channel width + * + * SPECIAL for fringes: + * All segments will start and ends with no exception + * + * IMPORTANT: we should be aware that channel width maybe different + * in X-direction and Y-direction channels!!! + * So we will load segment details for different channels + ***********************************************************************/ + /* For X-direction Channel */ + /* For LEFT side of FPGA */ + ChanNodeDetails left_chanx_details = build_unidir_chan_node_details(chan_width[0], device_size.get_x() - 2, LEFT, segment_infs); + for (size_t iy = 0; iy < device_size.get_y() - 1; ++iy) { + num_rr_nodes_per_type[CHANX] += left_chanx_details.get_num_starting_tracks(); + } + /* For RIGHT side of FPGA */ + ChanNodeDetails right_chanx_details = build_unidir_chan_node_details(chan_width[0], device_size.get_x() - 2, RIGHT, segment_infs); + for (size_t iy = 0; iy < device_size.get_y() - 1; ++iy) { + num_rr_nodes_per_type[CHANX] += right_chanx_details.get_num_starting_tracks(); + } + /* For core of FPGA */ + ChanNodeDetails core_chanx_details = build_unidir_chan_node_details(chan_width[1], device_size.get_x() - 2, NUM_SIDES, segment_infs); + for (size_t ix = 1; ix < grids.size() - 2; ++ix) { + for (size_t iy = 1; iy < grids[ix].size() - 2; ++iy) { + num_rr_nodes_per_type[CHANX] += core_chanx_details.get_num_starting_tracks(); + } + } + + /* For Y-direction Channel */ + /* For TOP side of FPGA */ + ChanNodeDetails top_chany_details = build_unidir_chan_node_details(chan_width[1], device_size.get_y() - 2, TOP, segment_infs); + for (size_t ix = 0; ix < device_size.get_x() - 1; ++ix) { + num_rr_nodes_per_type[CHANY] += top_chany_details.get_num_starting_tracks(); + } + /* For BOTTOM side of FPGA */ + ChanNodeDetails bottom_chany_details = build_unidir_chan_node_details(chan_width[1], device_size.get_y() - 2, BOTTOM, segment_infs); + for (size_t ix = 0; ix < device_size.get_x() - 1; ++ix) { + num_rr_nodes_per_type[CHANY] += bottom_chany_details.get_num_starting_tracks(); + } + /* For core of FPGA */ + ChanNodeDetails core_chany_details = build_unidir_chan_node_details(chan_width[1], device_size.get_y() - 2, NUM_SIDES, segment_infs); + for (size_t ix = 1; ix < grids.size() - 2; ++ix) { + for (size_t iy = 1; iy < grids[ix].size() - 2; ++iy) { + num_rr_nodes_per_type[CHANY] += core_chany_details.get_num_starting_tracks(); + } + } + + return num_rr_nodes_per_type; +} + + /************************************************************************ * Main function of this file * Builder for a detailed uni-directional tileable rr_graph @@ -103,9 +489,9 @@ t_rr_graph build_tileable_unidir_rr_graph(INP int L_num_types, INP t_timing_inf timing_inf, INP int wire_to_ipin_switch, INP enum e_base_cost_type base_cost_type, INP t_direct_inf *directs, INP int num_directs, INP boolean ignore_Fc_0, OUTP int *Warnings, - /*Xifan TANG: Switch Segment Pattern Support*/ - INP int num_swseg_pattern, INP t_swseg_pattern_inf* swseg_patterns, - INP boolean opin_to_cb_fast_edges, INP boolean opin_logic_eq_edges) { + /*Xifan TANG: Switch Segment Pattern Support*/ + INP int num_swseg_pattern, INP t_swseg_pattern_inf* swseg_patterns, + INP boolean opin_to_cb_fast_edges, INP boolean opin_logic_eq_edges) { /* Create an empty graph */ t_rr_graph rr_graph; rr_graph.rr_node_indices = NULL; @@ -115,22 +501,55 @@ t_rr_graph build_tileable_unidir_rr_graph(INP int L_num_types, /* Reset warning flag */ *Warnings = RR_GRAPH_NO_WARN; + /* Create a matrix of grid */ + DeviceCoordinator device_size(L_nx + 2, L_ny + 2); + std::vector< std::vector > grids; + /* reserve vector capacity to be memory efficient */ + grids.resize(L_nx + 2); + for (int ix = 0; ix < (L_nx + 2); ++ix) { + grids[ix].resize(L_ny + 2); + for (int iy = 0; ix < (L_ny + 2); ++iy) { + grid[ix][iy] = L_grid[ix][iy]; + } + } + /* Create a vector of channel width, we support X-direction and Y-direction has different W */ + std::vector device_chan_width; + device_chan_width.push_back(chan_width); + device_chan_width.push_back(chan_width); + + /* Create a vector of segment_inf */ + std::vector segment_infs; + for (int iseg = 0; iseg < num_seg_types; ++iseg) { + segment_infs.push_back(segment_inf[iseg]); + } + /************************************************************************ - * 1. Assign the segments for each routing channel, - * To be specific, for each routing track, we assign a routing segment. - * The assignment is subject to users' specifications, such as - * a. length of each type of segment - * b. frequency of each type of segment. - * c. routing channel width + * 2. Estimate the number of nodes in the rr_graph + * This will estimate the number of + * a. IPINs, input pins of each grid + * b. OPINs, output pins of each grid + * c. SOURCE, virtual node which drives OPINs + * d. SINK, virtual node which is connected to IPINs + * e. CHANX and CHANY, routing segments of each channel + ***********************************************************************/ + std::vector num_rr_nodes_per_type = estimate_num_rr_nodes_per_type(device_size, grids, device_chan_width, segment_infs); + + /************************************************************************ + * 3. Allocate the rr_nodes + ***********************************************************************/ + rr_graph.num_rr_nodes = 0; + for (size_t i = 0; i < num_rr_nodes_per_type.size(); ++i) { + rr_graph.num_rr_nodes += num_rr_nodes_per_type[i]; + } + /* use calloc to initialize everything to be zero */ + rr_graph.rr_node = (t_rr_node*)my_calloc(rr_graph.num_rr_nodes, sizeof(t_rr_node)); + + /************************************************************************ + * 4. Initialize the basic information of rr_nodes: + * coordinators: xlow, ylow, xhigh, yhigh, + * features: capacity, track_ids, ptc_num, direction + * grid_info : pb_graph_pin ***********************************************************************/ - /* Check the channel width */ - int nodes_per_chan = chan_width; - assert(chan_width > 0); - t_seg_details *seg_details = NULL; - seg_details = alloc_and_load_seg_details(&nodes_per_chan, - std::max(L_nx, L_ny), - num_seg_types, segment_inf, - TRUE, FALSE, UNI_DIRECTIONAL); /************************************************************************ * 3. Create the connectivity of OPINs @@ -140,7 +559,7 @@ t_rr_graph build_tileable_unidir_rr_graph(INP int L_num_types, int **Fc_in = NULL; /* [0..num_types-1][0..num_pins-1] */ boolean Fc_clipped; Fc_clipped = FALSE; - Fc_in = alloc_and_load_actual_fc(L_num_types, types, nodes_per_chan, + Fc_in = alloc_and_load_actual_fc(L_num_types, types, chan_width, FALSE, UNI_DIRECTIONAL, &Fc_clipped, ignore_Fc_0); if (Fc_clipped) { *Warnings |= RR_GRAPH_WARN_FC_CLIPPED; @@ -153,7 +572,7 @@ t_rr_graph build_tileable_unidir_rr_graph(INP int L_num_types, ***********************************************************************/ int **Fc_out = NULL; /* [0..num_types-1][0..num_pins-1] */ Fc_clipped = FALSE; - Fc_out = alloc_and_load_actual_fc(L_num_types, types, nodes_per_chan, + Fc_out = alloc_and_load_actual_fc(L_num_types, types, chan_width, TRUE, UNI_DIRECTIONAL, &Fc_clipped, ignore_Fc_0); /************************************************************************ @@ -164,6 +583,7 @@ t_rr_graph build_tileable_unidir_rr_graph(INP int L_num_types, * c. handle direct-connections ***********************************************************************/ /* Alloc node lookups, count nodes, alloc rr nodes */ + /* rr_graph.num_rr_nodes = 0; rr_graph.rr_node_indices = alloc_and_load_rr_node_indices(nodes_per_chan, L_nx, L_ny, &(rr_graph.num_rr_nodes), seg_details); @@ -171,6 +591,7 @@ t_rr_graph build_tileable_unidir_rr_graph(INP int L_num_types, memset(rr_node, 0, sizeof(t_rr_node) * rr_graph.num_rr_nodes); boolean* L_rr_edge_done = (boolean *) my_malloc(sizeof(boolean) * rr_graph.num_rr_nodes); memset(L_rr_edge_done, 0, sizeof(boolean) * rr_graph.num_rr_nodes); + */ /* handle direct-connections */ t_clb_to_clb_directs* clb_to_clb_directs = NULL; @@ -183,7 +604,7 @@ t_rr_graph build_tileable_unidir_rr_graph(INP int L_num_types, * a. cost_index * b. RC tree ***********************************************************************/ - rr_graph_externals(timing_inf, segment_inf, num_seg_types, nodes_per_chan, + rr_graph_externals(timing_inf, segment_inf, num_seg_types, chan_width, wire_to_ipin_switch, base_cost_type); return rr_graph; diff --git a/vpr7_x2p/vpr/SRC/fpga_x2p/base/fpga_x2p_rr_graph_utils.c b/vpr7_x2p/vpr/SRC/fpga_x2p/base/fpga_x2p_rr_graph_utils.c index 711b6ddc0..e5312fcb4 100644 --- a/vpr7_x2p/vpr/SRC/fpga_x2p/base/fpga_x2p_rr_graph_utils.c +++ b/vpr7_x2p/vpr/SRC/fpga_x2p/base/fpga_x2p_rr_graph_utils.c @@ -403,7 +403,7 @@ void alloc_and_load_rr_graph_route_structs(t_rr_graph* local_rr_graph) { int inode; - local_rr_graph->rr_node_route_inf = (t_rr_node_route_inf *) my_malloc(local_rr_graph->num_rr_nodes * sizeof(t_rr_node_route_inf)); + local_rr_graph->rr_node_route_inf = (t_rr_node_route_inf *) my_calloc(local_rr_graph->num_rr_nodes, sizeof(t_rr_node_route_inf)); for (inode = 0; inode < local_rr_graph->num_rr_nodes; inode++) { local_rr_graph->rr_node_route_inf[inode].prev_node = NO_PREVIOUS; diff --git a/vpr7_x2p/vpr/SRC/fpga_x2p/base/rr_blocks.cpp b/vpr7_x2p/vpr/SRC/fpga_x2p/base/rr_blocks.cpp index a30aded14..cb8de00d3 100644 --- a/vpr7_x2p/vpr/SRC/fpga_x2p/base/rr_blocks.cpp +++ b/vpr7_x2p/vpr/SRC/fpga_x2p/base/rr_blocks.cpp @@ -1,5 +1,5 @@ #include -#include +#include #include #include @@ -1345,55 +1345,71 @@ const char* RRGSB::gen_cb_verilog_routing_track_name(t_rr_type cb_type, std::string y_str = std::to_string(get_cb_y(cb_type)); std::string track_id_str = std::to_string(track_id); - std::ostringstream oss; - oss << cb_name << "_" << x_str << "__" << y_str << "__midout_" << track_id_str << "_"; - std::string ret = oss.str(); - - return ret.c_str(); + char* ret = (char*)my_malloc(sizeof(char)* + ( cb_name.length() + 1 + + x_str.length() + 2 + + y_str.length() + 9 + + track_id_str.length() + 1 + + 1)); + sprintf (ret, "%s_%s__%s__midout_%s_", + cb_name.c_str(), x_str.c_str(), y_str.c_str(), track_id_str.c_str()); + return ret; } const char* RRGSB::gen_sb_verilog_module_name() const { std::string x_str = std::to_string(get_sb_x()); std::string y_str = std::to_string(get_sb_y()); - std::ostringstream oss; - oss << "sb_" << x_str << "__" << y_str << "_" ; - std::string ret = oss.str(); + char* ret = (char*)my_malloc(sizeof(char)* + ( 2 + 1 + + x_str.length() + 2 + + y_str.length() + 1 + + 1)); + sprintf (ret, "sb_%s__%s_", + x_str.c_str(), y_str.c_str()); - return ret.c_str(); + return ret; } const char* RRGSB::gen_sb_verilog_instance_name() const { - - std::ostringstream oss; - oss << gen_sb_verilog_module_name() << "_0_" ; - std::string ret = oss.str(); + char* ret = (char*)my_malloc(sizeof(char)* + ( strlen(gen_sb_verilog_module_name()) + 3 + + 1)); + sprintf (ret, "%s_0_", + gen_sb_verilog_module_name()); - return ret.c_str(); + return ret; } /* Public Accessors Verilog writer */ const char* RRGSB::gen_sb_verilog_side_module_name(enum e_side side, size_t seg_id) const { Side side_manager(side); + std::string prefix_str(gen_sb_verilog_module_name()); std::string seg_id_str(std::to_string(seg_id)); std::string side_str(side_manager.to_string()); - std::ostringstream oss; - oss << gen_sb_verilog_module_name() << "_" << side_str << "_seg_" << "_" << seg_id_str << "_" ; - std::string ret = oss.str(); + char* ret = (char*)my_malloc(sizeof(char)* + ( prefix_str.length() + 1 + + side_str.length() + 5 + + seg_id_str.length() + 1 + + 1)); + sprintf (ret, "%s_%s_seg_%s_", + prefix_str.c_str(), side_str.c_str(), seg_id_str.c_str()); - return ret.c_str(); + return ret; } const char* RRGSB::gen_sb_verilog_side_instance_name(enum e_side side, size_t seg_id) const { + std::string prefix_str = gen_sb_verilog_side_module_name(side, seg_id); + char* ret = (char*)my_malloc(sizeof(char)* + ( prefix_str.length() + 3 + + 1)); + sprintf (ret, "%s_0_", + prefix_str.c_str()); - std::ostringstream oss; - oss << gen_sb_verilog_side_module_name(side, seg_id) << "_0_" ; - std::string ret = oss.str(); - - return ret.c_str(); + return ret; } /* Public Accessors Verilog writer */ @@ -1405,23 +1421,29 @@ const char* RRGSB::gen_cb_verilog_module_name(t_rr_type cb_type) const { std::string x_str = std::to_string(get_cb_x(cb_type)); std::string y_str = std::to_string(get_cb_y(cb_type)); - std::ostringstream oss; - oss << prefix_str << "_" << x_str << "__" << y_str << "_" ; - std::string ret = oss.str(); + char* ret = (char*)my_malloc(sizeof(char)* + ( prefix_str.length() + 1 + + x_str.length() + 2 + + y_str.length() + 1 + + 1)); + sprintf (ret, "%s_%s__%s_", + prefix_str.c_str(), x_str.c_str(), y_str.c_str()); - return ret.c_str(); + return ret; } const char* RRGSB::gen_cb_verilog_instance_name(t_rr_type cb_type) const { /* check */ assert (validate_cb_type(cb_type)); - std::ostringstream oss; - oss << gen_cb_verilog_module_name(cb_type) << "_0_" ; - std::string ret = oss.str(); - - return ret.c_str(); + std::string prefix_str = gen_cb_verilog_module_name(cb_type); + char* ret = (char*)my_malloc(sizeof(char)* + (prefix_str.length() + 3 + + 1)); + sprintf (ret, "%s_0_", + prefix_str.c_str()); + return ret; } /* Public mutators */ diff --git a/vpr7_x2p/vpr/SRC/fpga_x2p/base/rr_blocks.h b/vpr7_x2p/vpr/SRC/fpga_x2p/base/rr_blocks.h index bebcda55e..475be8ab4 100644 --- a/vpr7_x2p/vpr/SRC/fpga_x2p/base/rr_blocks.h +++ b/vpr7_x2p/vpr/SRC/fpga_x2p/base/rr_blocks.h @@ -1,3 +1,38 @@ +/********************************************************** + * MIT License + * + * Copyright (c) 2018 LNIS - The University of Utah + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + ***********************************************************************/ + +/************************************************************************ + * Filename: rr_blocks.h + * Created by: Xifan Tang + * Change history: + * +-------------------------------------+ + * | Date | Author | Notes + * +-------------------------------------+ + * | 2019/06/12 | Xifan Tang | Created + * +-------------------------------------+ + ***********************************************************************/ + /* IMPORTANT: * The following preprocessing flags are added to * avoid compilation error when this headers are included in more than 1 times diff --git a/vpr7_x2p/vpr/SRC/fpga_x2p/base/rr_blocks_naming.cpp b/vpr7_x2p/vpr/SRC/fpga_x2p/base/rr_blocks_naming.cpp index 93530e5f6..2a7f86fb3 100644 --- a/vpr7_x2p/vpr/SRC/fpga_x2p/base/rr_blocks_naming.cpp +++ b/vpr7_x2p/vpr/SRC/fpga_x2p/base/rr_blocks_naming.cpp @@ -10,10 +10,8 @@ char* convert_cb_type_to_string(t_rr_type chan_type) { switch(chan_type) { case CHANX: return "cbx"; - break; case CHANY: return "cby"; - break; default: vpr_printf(TIO_MESSAGE_ERROR, "(File:%s, [LINE%d])Invalid type of channel!\n", @@ -26,10 +24,8 @@ char* convert_chan_type_to_string(t_rr_type chan_type) { switch(chan_type) { case CHANX: return "chanx"; - break; case CHANY: return "chany"; - break; default: vpr_printf(TIO_MESSAGE_ERROR, "(File:%s, [LINE%d])Invalid type of channel!\n", @@ -42,10 +38,8 @@ char* convert_chan_rr_node_direction_to_string(enum PORTS chan_rr_node_direction switch(chan_rr_node_direction) { case IN_PORT: return "in"; - break; case OUT_PORT: return "out"; - break; default: vpr_printf(TIO_MESSAGE_ERROR, "(File:%s, [LINE%d])Invalid type of port!\n", __FILE__, __LINE__); exit(1); diff --git a/vpr7_x2p/vpr/SRC/fpga_x2p/router/fpga_x2p_pb_rr_graph.c b/vpr7_x2p/vpr/SRC/fpga_x2p/router/fpga_x2p_pb_rr_graph.c index e0d4457d6..db283690c 100644 --- a/vpr7_x2p/vpr/SRC/fpga_x2p/router/fpga_x2p_pb_rr_graph.c +++ b/vpr7_x2p/vpr/SRC/fpga_x2p/router/fpga_x2p_pb_rr_graph.c @@ -33,6 +33,8 @@ #include "fpga_x2p_pbtypes_utils.h" #include "fpga_x2p_globals.h" +#include "fpga_x2p_pb_rr_graph.h" + /* Count the number of rr_graph nodes that should be allocated * (a) INPUT pins at the top-level pb_graph_node should be a local_rr_node and plus a SOURCE * (b) CLOCK pins at the top-level pb_graph_node should be a local_rr_node and plus a SOURCE @@ -104,6 +106,7 @@ void init_one_rr_node_pack_cost_for_phy_graph_node(INP t_pb_graph_pin* cur_pb_gr /* Override the fan-in and fan-out for a top/primitive pb_graph_node */ +static void override_one_rr_node_for_top_primitive_phy_pb_graph_node(INP t_pb_graph_pin* cur_pb_graph_pin, INOUTP t_rr_graph* local_rr_graph, int cur_rr_node_index, @@ -151,6 +154,7 @@ void override_one_rr_node_for_top_primitive_phy_pb_graph_node(INP t_pb_graph_pin } /* initialize a rr_node in a rr_graph of phyical pb_graph_node */ +static void init_one_rr_node_for_phy_pb_graph_node(INP t_pb_graph_pin* cur_pb_graph_pin, INOUTP t_rr_graph* local_rr_graph, int cur_rr_node_index, @@ -206,7 +210,9 @@ void init_one_rr_node_for_phy_pb_graph_node(INP t_pb_graph_pin* cur_pb_graph_pin local_rr_graph->rr_node[cur_rr_node_index].prev_edge = OPEN; local_rr_graph->rr_node[cur_rr_node_index].capacity = 1; + local_rr_graph->rr_node[cur_rr_node_index].occ = 0; local_rr_graph->rr_node[cur_rr_node_index].type = rr_node_type; + local_rr_graph->rr_node[cur_rr_node_index].cost_index = 0; return; } @@ -270,6 +276,7 @@ void connect_one_rr_node_for_phy_pb_graph_node(INP t_pb_graph_pin* cur_pb_graph_ /* Recursively configure all the rr_nodes in the rr_graph * Initialize the routing cost, fan-in rr_nodes and fan-out rr_nodes, and switches */ +static void rec_init_rr_graph_for_phy_pb_graph_node(INP t_pb_graph_node* cur_pb_graph_node, INOUTP t_rr_graph* local_rr_graph, int* cur_rr_node_index) { @@ -418,6 +425,7 @@ void rec_init_rr_graph_for_phy_pb_graph_node(INP t_pb_graph_node* cur_pb_graph_n /* Recursively connect all the rr_nodes in the rr_graph * output_edges, output_switches */ +static void rec_connect_rr_graph_for_phy_pb_graph_node(INP t_pb_graph_node* cur_pb_graph_node, INOUTP t_rr_graph* local_rr_graph, int* cur_rr_node_index) { @@ -598,6 +606,7 @@ void alloc_and_load_rr_graph_for_phy_pb_graph_node(INP t_pb_graph_node* top_pb_g /* Check the vpack_net_num of a rr_node mapped to a pb_graph_pin and * mark the used vpack_net_num in the list */ +static void mark_vpack_net_used_in_pb_pin(t_pb* cur_op_pb, t_pb_graph_pin* cur_pb_graph_pin, int L_num_vpack_nets, boolean* vpack_net_used_in_pb) { int inode; @@ -621,6 +630,7 @@ void mark_vpack_net_used_in_pb_pin(t_pb* cur_op_pb, t_pb_graph_pin* cur_pb_graph /* Recursively visit all the child pbs and * mark the used vpack_net_num in the list */ +static void mark_vpack_net_used_in_pb(t_pb* cur_op_pb, int L_num_vpack_nets, boolean* vpack_net_used_in_pb) { int mode_index, ipb, jpb; @@ -718,6 +728,7 @@ void alloc_and_load_phy_pb_rr_graph_nets(INP t_pb* cur_op_pb, } /* Find the rr_node in the primitive node of a pb_rr_graph*/ +static void sync_pb_graph_pin_vpack_net_num_to_phy_pb(t_rr_node* cur_op_pb_rr_graph, t_pb_graph_pin* cur_pb_graph_pin, t_rr_graph* local_rr_graph) { @@ -787,6 +798,7 @@ void sync_pb_graph_pin_vpack_net_num_to_phy_pb(t_rr_node* cur_op_pb_rr_graph, return; } +static void rec_sync_wired_pb_vpack_net_num_to_phy_pb_rr_graph(t_pb_graph_node* cur_pb_graph_node, t_rr_node* op_pb_rr_graph, t_rr_graph* local_rr_graph) { @@ -851,6 +863,7 @@ void rec_sync_wired_pb_vpack_net_num_to_phy_pb_rr_graph(t_pb_graph_node* cur_pb_ * synchronize the vpack_net_num of the top-level/primitive pb_graph_pin * to the physical pb rr_node nodes */ +static void rec_sync_pb_vpack_net_num_to_phy_pb_rr_graph(t_pb* cur_op_pb, t_rr_graph* local_rr_graph) { int mode_index, ipb, jpb; @@ -921,6 +934,7 @@ void rec_sync_pb_vpack_net_num_to_phy_pb_rr_graph(t_pb* cur_op_pb, * 3. Find the SOURCE and SINK rr_nodes related to the pb_graph_pin * 4. Configure the net_rr_terminals with the SINK/SOURCE rr_nodes */ +static void alloc_and_load_phy_pb_rr_graph_net_rr_terminals(INP t_pb* cur_op_pb, t_rr_graph* local_rr_graph) { int inet, inode, rr_node_net_name; @@ -1013,10 +1027,11 @@ void alloc_and_load_phy_pb_rr_graph_net_rr_terminals(INP t_pb* cur_op_pb, return; } +static void alloc_pb_rr_graph_rr_indexed_data(t_rr_graph* local_rr_graph) { /* inside a cluster, I do not consider rr_indexed_data cost, set to 1 since other costs are multiplied by it */ alloc_rr_graph_rr_indexed_data(local_rr_graph, 1); - local_rr_graph->rr_indexed_data[0].base_cost = 1; + local_rr_graph->rr_indexed_data[0].base_cost = 1.; return; } @@ -1025,8 +1040,8 @@ void alloc_pb_rr_graph_rr_indexed_data(t_rr_graph* local_rr_graph) { * Add an output edge to the rr_node of the used input * connect it to the rr_node of the used LUT output */ +static void add_rr_node_edge_to_one_wired_lut(t_pb_graph_node* cur_pb_graph_node, - t_pb_type* cur_pb_type, t_rr_node* op_pb_rr_graph, t_rr_graph* local_rr_graph) { int iport, ipin; @@ -1140,6 +1155,7 @@ void add_rr_node_edge_to_one_wired_lut(t_pb_graph_node* cur_pb_graph_node, /* Add rr edges connecting from an input of a LUT to its output * IMPORTANT: this is only applied to LUT which operates in wire mode (a buffer) */ +static void rec_add_unused_rr_graph_wired_lut_rr_edges(INP t_pb_graph_node* cur_op_pb_graph_node, INP t_rr_node* cur_op_pb_rr_graph, INOUTP t_rr_graph* local_rr_graph) { @@ -1162,7 +1178,6 @@ void rec_add_unused_rr_graph_wired_lut_rr_edges(INP t_pb_graph_node* cur_op_pb_g * connect it to the rr_node of the used LUT output */ add_rr_node_edge_to_one_wired_lut(cur_op_pb_graph_node, - cur_pb_type, cur_op_pb_rr_graph, local_rr_graph); } @@ -1194,6 +1209,7 @@ void rec_add_unused_rr_graph_wired_lut_rr_edges(INP t_pb_graph_node* cur_op_pb_g /* Add rr edges connecting from an input of a LUT to its output * IMPORTANT: this is only applied to LUT which operates in wire mode (a buffer) */ +static void rec_add_rr_graph_wired_lut_rr_edges(INP t_pb* cur_op_pb, INOUTP t_rr_graph* local_rr_graph) { int mode_index, ipb, jpb, imode; @@ -1214,7 +1230,6 @@ void rec_add_rr_graph_wired_lut_rr_edges(INP t_pb* cur_op_pb, * connect it to the rr_node of the used LUT output */ add_rr_node_edge_to_one_wired_lut(cur_op_pb->pb_graph_node, - cur_pb_type, cur_op_pb->rr_graph, local_rr_graph); } @@ -1253,6 +1268,7 @@ void rec_add_rr_graph_wired_lut_rr_edges(INP t_pb* cur_op_pb, * For each multiple-source net, I add a new source as the unique source in routing purpose * As so, edges have to be added to the decendents of sources */ +static int add_virtual_sources_to_rr_graph_multi_sources(t_rr_graph* local_rr_graph) { int inet, isrc; int unique_src_node; @@ -1271,6 +1287,8 @@ int add_virtual_sources_to_rr_graph_multi_sources(t_rr_graph* local_rr_graph) { unique_src_node = local_rr_graph->num_rr_nodes - 1; local_rr_graph->rr_node[unique_src_node].type = SOURCE; local_rr_graph->rr_node[unique_src_node].capacity = 1; + local_rr_graph->rr_node[unique_src_node].occ = 0; + local_rr_graph->rr_node[unique_src_node].cost_index = 0; local_rr_graph->rr_node[unique_src_node].fan_in = 0; local_rr_graph->rr_node[unique_src_node].num_drive_rr_nodes = 0; local_rr_graph->rr_node[unique_src_node].drive_rr_nodes = NULL; @@ -1314,6 +1332,9 @@ void alloc_and_load_rr_graph_for_phy_pb(INP t_pb* cur_op_pb, /* Allocate rr_graph*/ cur_phy_pb->rr_graph = (t_rr_graph*) my_calloc(1, sizeof(t_rr_graph)); + /* Allocate and initialize cost index */ + alloc_pb_rr_graph_rr_indexed_data(cur_phy_pb->rr_graph); + /* Create rr_graph */ alloc_and_load_rr_graph_for_phy_pb_graph_node(cur_phy_pb->pb_graph_node, cur_phy_pb->rr_graph); @@ -1332,8 +1353,6 @@ void alloc_and_load_rr_graph_for_phy_pb(INP t_pb* cur_op_pb, /* Allocate trace in rr_graph */ alloc_rr_graph_route_static_structs(cur_phy_pb->rr_graph, nx * ny); /* TODO: nx * ny should be reduced for pb-only routing */ - alloc_pb_rr_graph_rr_indexed_data(cur_phy_pb->rr_graph); - /* Fill the net_rr_terminals with * 1. pin-to-pin mapping in pb_graph_node in cur_op_pb * 2. rr_graph in the cur_op_pb diff --git a/vpr7_x2p/vpr/SRC/fpga_x2p/verilog/verilog_compact_netlist.c b/vpr7_x2p/vpr/SRC/fpga_x2p/verilog/verilog_compact_netlist.c index 7ce957522..059f023e9 100644 --- a/vpr7_x2p/vpr/SRC/fpga_x2p/verilog/verilog_compact_netlist.c +++ b/vpr7_x2p/vpr/SRC/fpga_x2p/verilog/verilog_compact_netlist.c @@ -419,11 +419,10 @@ void dump_compact_verilog_one_physical_block(t_sram_orgz_info* cur_sram_orgz_inf /* Local wires for memory configurations */ if (0 < temp_conf_bits_msb) { - dump_verilog_sram_config_bus_internal_wires(fp, cur_sram_orgz_info, - 0, temp_conf_bits_msb - 1); + dump_verilog_sram_config_bus_internal_wires(fp, cur_sram_orgz_info, + 0, temp_conf_bits_msb - 1); } - /* Quote all the sub blocks*/ for (iz = 0; iz < phy_block_type->capacity; iz++) { /* Local Vdd and Gnd, subckt name*/ diff --git a/vpr7_x2p/vpr/SRC/fpga_x2p/verilog/verilog_decoder.c b/vpr7_x2p/vpr/SRC/fpga_x2p/verilog/verilog_decoder.c index d4feaca75..3cb4f9a5a 100644 --- a/vpr7_x2p/vpr/SRC/fpga_x2p/verilog/verilog_decoder.c +++ b/vpr7_x2p/vpr/SRC/fpga_x2p/verilog/verilog_decoder.c @@ -464,16 +464,19 @@ void dump_verilog_membank_config_module(FILE* fp, case SPICE_MODEL_DESIGN_CMOS: assert( 0 == num_reserved_bl ); assert( 0 == num_reserved_wl ); + /* Declare normal BL / WL inputs */ - fprintf(fp, " output wire [%d:%d] %s%s, //---- Normal Bit lines \n", + fprintf(fp, " output wire [%d:%d] %s%s", 0, num_bl - 1, mem_model->prefix, top_netlist_normal_bl_port_postfix); + fprintf(fp, ", //---- Normal Bit lines \n"); + fprintf(fp, " output wire [%d:%d] %s%s", 0, num_wl - 1, mem_model->prefix, top_netlist_normal_wl_port_postfix); /* Declare inverted wires if needed */ if (1 == num_blb_ports) { fprintf(fp, ", //---- Normal Word lines \n"); } else { - fprintf(fp, " //---- Normal Word lines\n);\n"); + fprintf(fp, " //---- Normal Word lines\n"); } if (1 == num_blb_ports) { fprintf(fp, " output wire [%d:%d] %s%s", @@ -481,14 +484,14 @@ void dump_verilog_membank_config_module(FILE* fp, } if (1 == num_wlb_ports) { fprintf(fp, ", //---- Inverted Normal Bit lines \n"); - } else { - fprintf(fp, " //---- Inverted Normal Bit lines \n);\n"); + } else { + fprintf(fp, " //---- Inverted Normal Bit lines \n"); } if (1 == num_wlb_ports) { fprintf(fp, " output wire [%d:%d] %s%s //---- Inverted Normal Word lines \n", 0, num_wl - 1, mem_model->prefix, top_netlist_normal_wlb_port_postfix); - fprintf(fp, ");\n"); } + fprintf(fp, ");\n"); break; case SPICE_MODEL_DESIGN_RRAM: /* Check: there should be reserved BLs and WLs */ diff --git a/vpr7_x2p/vpr/SRC/fpga_x2p/verilog/verilog_pbtypes.c b/vpr7_x2p/vpr/SRC/fpga_x2p/verilog/verilog_pbtypes.c index 452928472..e3bdbf332 100644 --- a/vpr7_x2p/vpr/SRC/fpga_x2p/verilog/verilog_pbtypes.c +++ b/vpr7_x2p/vpr/SRC/fpga_x2p/verilog/verilog_pbtypes.c @@ -350,6 +350,51 @@ void stats_mux_verilog_model_pb_node_rec(t_llist** muxes_head, return; } +/* Print a port of pb_types, + * SRAM ports are not printed here!!! + * Important feature: manage the comma between ports + * Make sure there is no redundant comma and there is no comma after the last element if specified + */ +static +void dump_verilog_pb_type_one_bus_port(FILE* fp, + t_pb_type* cur_pb_type, + char* port_prefix, + char* port_type_str, + t_port* pb_type_port, + boolean dump_port_type, + boolean dump_explicit_port_map) { + if (TRUE == dump_port_type) { + fprintf(fp, "%s ", port_type_str); + fprintf(fp, "[0:%d] %s__%s ", + pb_type_port->num_pins - 1, + port_prefix, pb_type_port->name); + } else { + if ((NULL != cur_pb_type->spice_model) + && (TRUE == dump_explicit_port_map) + && (TRUE == cur_pb_type->spice_model->dump_explicit_port_map)) { + fprintf(fp, ".%s(", + pb_type_port->spice_model_port->lib_name); + } + fprintf(fp, "{"); + for (int ipin = 0; ipin < pb_type_port->num_pins; ++ipin) { + if (0 < ipin) { + fprintf(fp, ", "); + } + fprintf(fp, "%s", + gen_verilog_one_pb_type_pin_name(port_prefix, pb_type_port, ipin)); + } + fprintf(fp, "}"); + if ((NULL != cur_pb_type->spice_model) + && (TRUE == dump_explicit_port_map) + && (TRUE == cur_pb_type->spice_model->dump_explicit_port_map)) { + fprintf(fp, ")"); + } + } + + return; +} + + /* Print ports of pb_types, * SRAM ports are not printed here!!! * Important feature: manage the comma between ports @@ -360,7 +405,8 @@ void dump_verilog_pb_type_bus_ports(FILE* fp, int use_global_clock, t_pb_type* cur_pb_type, boolean dump_port_type, - boolean dump_last_comma) { + boolean dump_last_comma, + boolean dump_explicit_port_map) { int iport; int num_pb_type_input_port = 0; t_port** pb_type_input_ports = NULL; @@ -398,16 +444,9 @@ void dump_verilog_pb_type_bus_ports(FILE* fp, fprintf(fp, ", "); } } - if (TRUE == dump_port_type) { - fprintf(fp, "inout "); - fprintf(fp, "[0:%d] %s__%s ", - pb_type_inout_ports[iport]->num_pins - 1, - formatted_port_prefix, pb_type_inout_ports[iport]->name); - } else { - fprintf(fp, "%s__%s[0:%d] ", - formatted_port_prefix, pb_type_inout_ports[iport]->name, - pb_type_inout_ports[iport]->num_pins - 1); - } + dump_verilog_pb_type_one_bus_port(fp, cur_pb_type, formatted_port_prefix, "inout", + pb_type_inout_ports[iport], dump_port_type, dump_explicit_port_map); + /* Update the counter */ num_dumped_port++; } @@ -424,16 +463,8 @@ void dump_verilog_pb_type_bus_ports(FILE* fp, fprintf(fp, ", "); } } - if (TRUE == dump_port_type) { - fprintf(fp, "input "); - fprintf(fp, " [0:%d] %s__%s ", - pb_type_input_ports[iport]->num_pins - 1, - formatted_port_prefix, pb_type_input_ports[iport]->name); - } else { - fprintf(fp, " %s__%s[0:%d] ", - formatted_port_prefix, pb_type_input_ports[iport]->name, - pb_type_input_ports[iport]->num_pins - 1); - } + dump_verilog_pb_type_one_bus_port(fp, cur_pb_type, formatted_port_prefix, "input", + pb_type_input_ports[iport], dump_port_type, dump_explicit_port_map); /* Update the counter */ num_dumped_port++; } @@ -450,44 +481,30 @@ void dump_verilog_pb_type_bus_ports(FILE* fp, fprintf(fp, ", "); } } - if (TRUE == dump_port_type) { - fprintf(fp, "output "); - fprintf(fp, " %s__%s[0:%d]", - formatted_port_prefix, pb_type_output_ports[iport]->name, - pb_type_output_ports[iport]->num_pins - 1); - } else { - fprintf(fp, " %s__%s[0:%d]", - formatted_port_prefix, pb_type_output_ports[iport]->name, - pb_type_output_ports[iport]->num_pins - 1); - } + dump_verilog_pb_type_one_bus_port(fp, cur_pb_type, formatted_port_prefix, "output", + pb_type_output_ports[iport], dump_port_type, dump_explicit_port_map); /* Update the counter */ num_dumped_port++; } /* Clocks */ /* Find pb_type clock ports */ - pb_type_clk_ports = find_pb_type_ports_match_spice_model_port_type(cur_pb_type, SPICE_MODEL_PORT_CLOCK, &num_pb_type_clk_port); - /* Print all the clk ports */ - for (iport = 0; iport < num_pb_type_clk_port; iport++) { - if (0 < num_dumped_port) { - if (TRUE == dump_port_type) { - fprintf(fp, ",\n"); - } else { - fprintf(fp, ", "); + if (0 == use_global_clock) { + pb_type_clk_ports = find_pb_type_ports_match_spice_model_port_type(cur_pb_type, SPICE_MODEL_PORT_CLOCK, &num_pb_type_clk_port); + /* Print all the clk ports */ + for (iport = 0; iport < num_pb_type_clk_port; iport++) { + if (0 < num_dumped_port) { + if (TRUE == dump_port_type) { + fprintf(fp, ",\n"); + } else { + fprintf(fp, ", "); + } } + dump_verilog_pb_type_one_bus_port(fp, cur_pb_type, formatted_port_prefix, "input", + pb_type_clk_ports[iport], dump_port_type, dump_explicit_port_map); + /* Update the counter */ + num_dumped_port++; } - if (TRUE == dump_port_type) { - fprintf(fp, "input"); - fprintf(fp, " %s__%s[0:%d]", - formatted_port_prefix, pb_type_clk_ports[iport]->name, - pb_type_output_ports[iport]->num_pins - 1); - } else { - fprintf(fp, " %s__%s[0:%d]", - formatted_port_prefix, pb_type_clk_ports[iport]->name, - pb_type_output_ports[iport]->num_pins - 1); - } - /* Update the counter */ - num_dumped_port++; } /* Dump the last comma, when the option is enabled and there is something dumped */ diff --git a/vpr7_x2p/vpr/SRC/fpga_x2p/verilog/verilog_pbtypes.h b/vpr7_x2p/vpr/SRC/fpga_x2p/verilog/verilog_pbtypes.h index 2b2695336..42db03fb0 100644 --- a/vpr7_x2p/vpr/SRC/fpga_x2p/verilog/verilog_pbtypes.h +++ b/vpr7_x2p/vpr/SRC/fpga_x2p/verilog/verilog_pbtypes.h @@ -23,7 +23,8 @@ void dump_verilog_pb_type_bus_ports(FILE* fp, int use_global_clock, t_pb_type* cur_pb_type, boolean dump_port_type, - boolean dump_last_comma); + boolean dump_last_comma, + boolean dump_explicit_port_map); void dump_verilog_pb_type_ports(FILE* fp, char* port_prefix, diff --git a/vpr7_x2p/vpr/SRC/fpga_x2p/verilog/verilog_primitives.c b/vpr7_x2p/vpr/SRC/fpga_x2p/verilog/verilog_primitives.c index 22765eaed..46a9c69f1 100644 --- a/vpr7_x2p/vpr/SRC/fpga_x2p/verilog/verilog_primitives.c +++ b/vpr7_x2p/vpr/SRC/fpga_x2p/verilog/verilog_primitives.c @@ -235,7 +235,7 @@ void dump_verilog_pb_generic_primitive(t_sram_orgz_info* cur_sram_orgz_info, /* assert */ num_sram = count_num_sram_bits_one_spice_model(verilog_model, -1); /* print ports --> input ports */ - dump_verilog_pb_type_ports(fp, port_prefix, 0, prim_pb_type, FALSE, FALSE, TRUE); + dump_verilog_pb_type_bus_ports(fp, port_prefix, 1, prim_pb_type, FALSE, FALSE, verilog_model->dump_explicit_port_map); /* IOPADs requires a specical port to output */ if (SPICE_MODEL_IOPAD == verilog_model->type) { @@ -573,32 +573,6 @@ void dump_verilog_pb_primitive_lut(t_sram_orgz_info* cur_sram_orgz_info, fprintf(fp, ");\n"); /* Definition ends*/ - /* Specify inputs are wires */ - pb_type_input_ports = find_pb_type_ports_match_spice_model_port_type(cur_pb_type, SPICE_MODEL_PORT_INPUT, &num_pb_type_input_port); - assert(1 == num_pb_type_input_port); - fprintf(fp, "wire [0:%d] %s__%s;\n", - input_ports[0]->size - 1, port_prefix, pb_type_input_ports[0]->name); - for (i = 0; i < input_ports[0]->size; i++) { - fprintf(fp, "assign %s__%s[%d] = %s__%s_%d_;\n", - port_prefix, pb_type_input_ports[0]->name, i, - port_prefix, pb_type_input_ports[0]->name, i); - } - /* Specify outputs are wires */ - pb_type_output_ports = find_pb_type_ports_match_spice_model_port_type(cur_pb_type, SPICE_MODEL_PORT_OUTPUT, &num_pb_type_output_port); - for (i = 0; i < num_pb_type_output_port; i++) { - fprintf(fp, "wire [0:%d] %s__%s;\n", - output_ports[i]->size - 1, port_prefix, pb_type_output_ports[i]->name); - } - /* Make sure we have the same number outputs */ - assert (num_pb_type_output_port == num_output_port); - for (i = 0; i < num_output_port; i++) { - for (ipin = 0; ipin < output_ports[i]->size; ipin++) { - fprintf(fp, "assign %s__%s_%d_ = %s__%s[%d];\n", - port_prefix, pb_type_output_ports[i]->name, ipin, - port_prefix, pb_type_output_ports[i]->name, ipin); - } - } - /* Specify SRAM output are wires */ cur_num_sram = get_sram_orgz_info_num_mem_bit(cur_sram_orgz_info); dump_verilog_sram_config_bus_internal_wires(fp, cur_sram_orgz_info, cur_num_sram, cur_num_sram + num_sram - 1); @@ -640,7 +614,7 @@ void dump_verilog_pb_primitive_lut(t_sram_orgz_info* cur_sram_orgz_info, /* Connect inputs*/ /* Connect outputs*/ fprintf(fp, "//----- Input and output ports -----\n"); - dump_verilog_pb_type_bus_ports(fp, port_prefix, 0, cur_pb_type, FALSE, TRUE); + dump_verilog_pb_type_bus_ports(fp, port_prefix, 1, cur_pb_type, FALSE, TRUE, verilog_model->dump_explicit_port_map); fprintf(fp, "\n//----- SRAM ports -----\n"); /* check */ diff --git a/vpr7_x2p/vpr/SRC/fpga_x2p/verilog/verilog_routing.c b/vpr7_x2p/vpr/SRC/fpga_x2p/verilog/verilog_routing.c index a54edc7e0..9d049bc70 100644 --- a/vpr7_x2p/vpr/SRC/fpga_x2p/verilog/verilog_routing.c +++ b/vpr7_x2p/vpr/SRC/fpga_x2p/verilog/verilog_routing.c @@ -3029,7 +3029,7 @@ void dump_verilog_connection_box_mux(t_sram_orgz_info* cur_sram_orgz_info, verilog_model->prefix, mux_size, verilog_model->cnt, input_cnt); int drive_node_index = rr_gsb.get_cb_chan_node_index(cb_type, drive_rr_nodes[inode]); assert (-1 != drive_node_index); - fprintf(fp, "%s;", rr_gsb.gen_cb_verilog_routing_track_name(cb_type, drive_node_index)); + fprintf(fp, "%s;\n", rr_gsb.gen_cb_verilog_routing_track_name(cb_type, drive_node_index)); input_cnt++; } assert(input_cnt == mux_size); @@ -4076,13 +4076,9 @@ void dump_verilog_routing_resources(t_sram_orgz_info* cur_sram_orgz_info, DeviceCoordinator cb_range = device_rr_gsb.get_gsb_range(); /* X - channels [1...nx][0..ny]*/ - for (int iy = 0; iy < (ny + 1); iy++) { - for (int ix = 1; ix < (nx + 1); ix++) { - for (size_t icb = 0; icb < device_rr_gsb.get_num_cb_unique_module(CHANX); ++icb) { - const RRGSB& unique_mirror = device_rr_gsb.get_cb_unique_module(CHANX, icb); - dump_verilog_routing_connection_box_unique_module(cur_sram_orgz_info, verilog_dir, subckt_dir, unique_mirror, CHANX); - } - } + for (size_t icb = 0; icb < device_rr_gsb.get_num_cb_unique_module(CHANX); ++icb) { + const RRGSB& unique_mirror = device_rr_gsb.get_cb_unique_module(CHANX, icb); + dump_verilog_routing_connection_box_unique_module(cur_sram_orgz_info, verilog_dir, subckt_dir, unique_mirror, CHANX); } /* TODO: when we follow a tile organization, * updating the conf bits should follow a tile organization: CLB, SB and CBX, CBY */ @@ -4094,13 +4090,9 @@ void dump_verilog_routing_resources(t_sram_orgz_info* cur_sram_orgz_info, } /* Y - channels [1...ny][0..nx]*/ - for (int ix = 0; ix < (nx + 1); ix++) { - for (int iy = 1; iy < (ny + 1); iy++) { - for (size_t icb = 0; icb < device_rr_gsb.get_num_cb_unique_module(CHANY); ++icb) { - const RRGSB& unique_mirror = device_rr_gsb.get_cb_unique_module(CHANY, icb); - dump_verilog_routing_connection_box_unique_module(cur_sram_orgz_info, verilog_dir, subckt_dir, unique_mirror, CHANY); - } - } + for (size_t icb = 0; icb < device_rr_gsb.get_num_cb_unique_module(CHANY); ++icb) { + const RRGSB& unique_mirror = device_rr_gsb.get_cb_unique_module(CHANY, icb); + dump_verilog_routing_connection_box_unique_module(cur_sram_orgz_info, verilog_dir, subckt_dir, unique_mirror, CHANY); } for (size_t ix = 0; ix < cb_range.get_x(); ++ix) { diff --git a/vpr7_x2p/vpr/SRC/fpga_x2p/verilog/verilog_utils.c b/vpr7_x2p/vpr/SRC/fpga_x2p/verilog/verilog_utils.c index bbc7e75da..abdfbba53 100644 --- a/vpr7_x2p/vpr/SRC/fpga_x2p/verilog/verilog_utils.c +++ b/vpr7_x2p/vpr/SRC/fpga_x2p/verilog/verilog_utils.c @@ -1439,10 +1439,24 @@ void dump_verilog_formal_verification_sram_ports_wiring(FILE* fp, t_sram_orgz_info* cur_sram_orgz_info, int sram_lsb, int sram_msb) { fprintf(fp, "assign "); - - dump_verilog_sram_one_local_outport(fp, cur_sram_orgz_info, - sram_lsb, sram_msb, - 0, VERILOG_PORT_CONKT); + switch (cur_sram_orgz_info->type) { + case SPICE_SRAM_STANDALONE: + break; + case SPICE_SRAM_SCAN_CHAIN: + dump_verilog_sram_one_local_outport(fp, cur_sram_orgz_info, + sram_lsb, sram_msb, + 0, VERILOG_PORT_CONKT); + break; + case SPICE_SRAM_MEMORY_BANK: + dump_verilog_sram_one_outport(fp, cur_sram_orgz_info, + sram_lsb, sram_msb, + 0, VERILOG_PORT_CONKT); + break; + default: + vpr_printf(TIO_MESSAGE_ERROR, "(File:%s,[LINE%d])Invalid SRAM organization type!\n", + __FILE__, __LINE__); + exit(1); + } fprintf(fp, " = "); diff --git a/vpr7_x2p/vpr/SRC/power/power.c b/vpr7_x2p/vpr/SRC/power/power.c index 420b7e655..d185ffa9d 100644 --- a/vpr7_x2p/vpr/SRC/power/power.c +++ b/vpr7_x2p/vpr/SRC/power/power.c @@ -201,6 +201,7 @@ static void power_usage_primitive(t_power_usage * power_usage, t_pb * pb, } } +static void power_usage_local_pin_toggle(t_power_usage * power_usage, t_pb * pb, t_pb_graph_pin * pin) { float scale_factor; @@ -222,6 +223,7 @@ void power_usage_local_pin_toggle(t_power_usage * power_usage, t_pb * pb, / g_solution_inf.T_crit; } +static void power_usage_local_pin_buffer_and_wire(t_power_usage * power_usage, t_pb * pb, t_pb_graph_pin * pin) { t_power_usage sub_power_usage; @@ -1027,6 +1029,7 @@ static void power_usage_routing(t_power_usage * power_usage, } } +static void power_alloc_and_init_pb_pin(t_pb_graph_pin * pin) { int port_idx; t_port * port_to_find; @@ -1085,6 +1088,7 @@ void power_alloc_and_init_pb_pin(t_pb_graph_pin * pin) { } } +static void power_init_pb_pins_rec(t_pb_graph_node * pb_node) { int mode; int type; @@ -1131,6 +1135,7 @@ void power_init_pb_pins_rec(t_pb_graph_node * pb_node) { } } +static void power_pb_pins_init() { int type_idx; @@ -1141,6 +1146,7 @@ void power_pb_pins_init() { } } +static void power_routing_init(t_det_routing_arch * routing_arch) { int net_idx; int rr_node_idx; @@ -1367,7 +1373,7 @@ boolean power_uninit(void) { } delete mux_info; } - free(g_power_commonly_used); + delete g_power_commonly_used; if (g_power_output->out) { fclose(g_power_output->out); diff --git a/vpr7_x2p/vpr/VerilogNetlists/ff.v b/vpr7_x2p/vpr/VerilogNetlists/ff.v index b52ba4078..cf15dc440 100644 --- a/vpr7_x2p/vpr/VerilogNetlists/ff.v +++ b/vpr7_x2p/vpr/VerilogNetlists/ff.v @@ -5,7 +5,7 @@ // Coder : Xifan TANG //----------------------------------------------------- //------ Include defines: preproc flags ----- -`include "OPENFPGAPATHKEYWORD/vpr7_x2p/vpr/test_modes_Verilog/SRC/fpga_defines.v" +`include "GENERATED_DIR_KEYWORD/SRC/fpga_defines.v" module static_dff ( /* Global ports go first */ input set, // set input diff --git a/vpr7_x2p/vpr/regression_verilog.sh b/vpr7_x2p/vpr/regression_verilog.sh index 21602c821..293d9e551 100644 --- a/vpr7_x2p/vpr/regression_verilog.sh +++ b/vpr7_x2p/vpr/regression_verilog.sh @@ -16,21 +16,25 @@ act_file="${OpenFPGA_path}/fpga_flow/benchmarks/Blif/Test_Modes/$benchmark.act " verilog_reference="${OpenFPGA_path}/fpga_flow/benchmarks/Verilog/Test_Modes/$benchmark.v" vpr_route_chan_width="200" fpga_flow_script="${OpenFPGA_path}/fpga_flow/scripts" +ff_path="$vpr_path/VerilogNetlists/ff.v" +new_ff_path="$verilog_output_dirpath/$verilog_output_dirname/SRC/ff.v" +ff_keyword="GENERATED_DIR_KEYWORD" +ff_include_path="$verilog_output_dirpath/$verilog_output_dirname" +arch_ff_keyword="FFPATHKEYWORD" -# Step A: Make sure a clean start -# Recompile if needed -#make clean -#make -j32 # Remove previous designs rm -rf $verilog_output_dirpath/$verilog_output_dirname mkdir ${OpenFPGA_path}/fpga_flow/arch/generated cd $fpga_flow_scripts -perl arch_rewrite.pl -i $template_arch_xml_file -o $arch_xml_file +perl rewrite_path_in_file.pl -i $template_arch_xml_file -o $arch_xml_file +perl rewrite_path_in_file.pl -i $arch_xml_file -k $arch_ff_keyword $new_ff_path cd - # Run VPR -#valgrind ./vpr $arch_xml_file $blif_file --full_stats --nodisp --activity_file $act_file --fpga_verilog --fpga_verilog_dir $verilog_output_dirpath/$verilog_output_dirname --fpga_x2p_rename_illegal_port --fpga_bitstream_generator --fpga_verilog_print_top_testbench --fpga_verilog_print_input_blif_testbench --fpga_verilog_include_timing --fpga_verilog_include_signal_init --fpga_verilog_print_formal_verification_top_netlist --fpga_verilog_print_autocheck_top_testbench $verilog_reference --fpga_verilog_print_user_defined_template --route_chan_width $vpr_route_chan_width --fpga_verilog_include_icarus_simulator --fpga_verilog_print_report_timing_tcl --power --tech_properties $tech_file --fpga_verilog_print_sdc_pnr --fpga_verilog_print_sdc_analysis --fpga_x2p_compact_routing_hierarchy +cd $fpga_flow_scripts +perl rewrite_path_in_file.pl -i $ff_path -o $new_ff_path -k $ff_keyword $ff_include_path +cd -