From 1d8dead9d00a359e0116f0fd2c56206dcbf10d05 Mon Sep 17 00:00:00 2001 From: Marius Meyer Date: Wed, 22 Apr 2020 16:23:14 +0200 Subject: [PATCH] Initial commit --- .gitignore | 7 + .gitmodules | 9 + FFT/.gitignore | 4 + FFT/CHANGELOG | 9 + FFT/CMakeLists.txt | 36 + FFT/LICENSE | 21 + FFT/README.md | 114 ++++ FFT/performance/README.md | 90 +++ FFT/src/common/parameters.h.in | 27 + FFT/src/device/CMakeLists.txt | 35 + FFT/src/device/fft1d_float_8.cl | 258 +++++++ FFT/src/device/fft_8.cl | 347 ++++++++++ FFT/src/device/twid_radix4_8.cl | 90 +++ FFT/src/host/CMakeLists.txt | 12 + FFT/src/host/execution.h | 65 ++ FFT/src/host/execution_default.cpp | 92 +++ FFT/src/host/fft_functionality.cpp | 290 ++++++++ FFT/src/host/fft_functionality.hpp | 151 +++++ FFT/src/host/main.cpp | 48 ++ FFT/src/host/setup/fpga_setup.cpp | 273 ++++++++ FFT/src/host/setup/fpga_setup.hpp | 108 +++ FFT/synth-scripts/synth-fft-noctua.sh | 20 + FFT/tests/CMakeLists.txt | 15 + FFT/tests/test_execution_functionality.cpp | 240 +++++++ FFT/tests/test_fft_functionality.cpp | 105 +++ FFT/tests/test_fpga_setup.cpp | 34 + GEMM/.gitignore | 12 + GEMM/CMakeLists.txt | 38 ++ GEMM/Readme.md | 117 ++++ GEMM/performance/README.md | 159 +++++ GEMM/performance/performance_model_cannon.py | 43 ++ GEMM/performance/requirements.txt | 28 + GEMM/src/common/parameters.h.in | 26 + GEMM/src/device/CMakeLists.txt | 35 + GEMM/src/device/gemm_cannon.cl | 232 +++++++ GEMM/src/host/CMakeLists.txt | 12 + GEMM/src/host/execution.h | 80 +++ GEMM/src/host/execution_cannon.cpp | 122 ++++ GEMM/src/host/gemm_functionality.cpp | 227 +++++++ GEMM/src/host/gemm_functionality.hpp | 115 ++++ GEMM/src/host/main.cpp | 86 +++ GEMM/src/host/setup/fpga_setup.cpp | 273 ++++++++ GEMM/src/host/setup/fpga_setup.hpp | 108 +++ GEMM/tests/CMakeLists.txt | 15 + GEMM/tests/test_fpga_setup.cpp | 34 + ...nel_functionality_and_host_integration.cpp | 207 ++++++ LICENSE | 21 + LINPACK/.gitignore | 7 + LINPACK/CHANGELOG | 9 + LINPACK/CMakeLists.txt | 59 ++ LINPACK/LICENSE | 21 + LINPACK/Readme.md | 139 ++++ LINPACK/src/common/parameters.h.in | 28 + LINPACK/src/device/CMakeLists.txt | 101 +++ LINPACK/src/device/lu_blocked.cl | 371 ++++++++++ LINPACK/src/device/lu_blocked_pvt.cl | 580 ++++++++++++++++ LINPACK/src/device/lu_blocked_pvt_test.cl | 145 ++++ LINPACK/src/host/CMakeLists.txt | 24 + LINPACK/src/host/execution.h | 67 ++ LINPACK/src/host/execution_blocked.cpp | 163 +++++ LINPACK/src/host/execution_blocked_pvt.cpp | 114 ++++ LINPACK/src/host/linpack_functionality.cpp | 331 +++++++++ LINPACK/src/host/linpack_functionality.hpp | 140 ++++ LINPACK/src/host/main.cpp | 58 ++ LINPACK/src/host/setup/fpga_setup.cpp | 273 ++++++++ LINPACK/src/host/setup/fpga_setup.hpp | 108 +++ LINPACK/tests/CMakeLists.txt | 43 ++ LINPACK/tests/test_fpga_setup.cpp | 38 ++ ...nel_functionality_and_host_integration.cpp | 194 ++++++ ...st_kernel_functionality_separate_cores.cpp | 290 ++++++++ PTRANS/.gitignore | 3 + PTRANS/CMakeLists.txt | 36 + PTRANS/README.md | 116 ++++ PTRANS/src/common/parameters.h.in | 25 + PTRANS/src/device/CMakeLists.txt | 35 + PTRANS/src/device/transpose_default.cl | 28 + PTRANS/src/device/transpose_optimized.cl | 103 +++ PTRANS/src/host/CMakeLists.txt | 12 + PTRANS/src/host/execution.h | 67 ++ PTRANS/src/host/execution_default.cpp | 105 +++ PTRANS/src/host/main.cpp | 66 ++ PTRANS/src/host/setup/fpga_setup.cpp | 273 ++++++++ PTRANS/src/host/setup/fpga_setup.hpp | 108 +++ PTRANS/src/host/transpose_functionality.cpp | 205 ++++++ PTRANS/src/host/transpose_functionality.hpp | 121 ++++ PTRANS/tests/CMakeLists.txt | 15 + PTRANS/tests/test_fpga_setup.cpp | 34 + PTRANS/tests/test_host_functionality.cpp | 192 ++++++ ...nel_functionality_and_host_integration.cpp | 211 ++++++ README.md | 11 + RandomAccess/.gitignore | 6 + RandomAccess/CHANGELOG | 24 + RandomAccess/CMakeLists.txt | 54 ++ RandomAccess/LICENSE | 21 + RandomAccess/README.md | 111 +++ RandomAccess/results/README.md | 50 ++ .../results/frandom_single_results.csv | 7 + .../results/frandom_single_results.jpg | Bin 0 -> 60182 bytes .../settings/settings.compile.xilinx.ini | 4 + ...settings.link.xilinx.random_access.ddr.ini | 9 + ...random_access_kernels_single.generator.ini | 8 + RandomAccess/src/common/parameters.h.in | 35 + RandomAccess/src/device/CMakeLists.txt | 124 ++++ RandomAccess/src/device/generateKernels.cmake | 17 + .../src/device/generateXilinxSettings.cmake | 36 + .../device/random_access_kernels_ndrange.cl | 54 ++ .../device/random_access_kernels_single.cl | 111 +++ .../random_access_kernels_single_rnd.cl | 147 ++++ RandomAccess/src/host/CMakeLists.txt | 25 + RandomAccess/src/host/execution.h | 79 +++ RandomAccess/src/host/execution_ndrange.cpp | 191 ++++++ RandomAccess/src/host/execution_single.cpp | 163 +++++ .../src/host/execution_single_rnd.cpp | 195 ++++++ RandomAccess/src/host/main.cpp | 59 ++ .../src/host/random_access_functionality.cpp | 247 +++++++ .../src/host/random_access_functionality.hpp | 133 ++++ RandomAccess/src/host/setup/fpga_setup.cpp | 273 ++++++++ RandomAccess/src/host/setup/fpga_setup.hpp | 108 +++ RandomAccess/tests/CMakeLists.txt | 29 + RandomAccess/tests/test_fpga_setup.cpp | 38 ++ RandomAccess/tests/test_host_code.cpp | 31 + ...nel_functionality_and_host_integration.cpp | 88 +++ STREAM/.gitignore | 5 + STREAM/CHANGELOG | 45 ++ STREAM/CMakeLists.txt | 59 ++ STREAM/LICENSE | 21 + STREAM/README.md | 134 ++++ STREAM/csv_result_export/README.md | 28 + STREAM/csv_result_export/create_plots.py | 62 ++ .../dp_global_ring_plot.jpeg | Bin 0 -> 210389 bytes STREAM/csv_result_export/dp_plot.jpeg | Bin 0 -> 221249 bytes STREAM/csv_result_export/parse_data.py | 162 +++++ ...W+FPGA-hybrid-CPU+FPGA-Arria-10-GX1150.txt | 51 ++ ...tel-Arria-10-GX1150)-Buffer-Reorder_ni.txt | 52 ++ ..._Bittware-385A-(Intel-Arria-10-GX1150).txt | 51 ++ ...ttware-385A-(Intel-Arria-10-GX1150)_ni.txt | 52 ++ ...ittware-520N-(Intel-Stratix-10-GX2800).txt | 52 ++ ...ware-520N-(Intel-Stratix-10-GX2800)_ni.txt | 55 ++ ...ittware-520N-(Intel-Stratix-10-GX2800).txt | 52 ++ ...ware-520N-(Intel-Stratix-10-GX2800)_ni.txt | 54 ++ ...ittware-520N-(Intel-Stratix-10-GX2800).txt | 52 ++ ...ware-520N-(Intel-Stratix-10-GX2800)_ni.txt | 52 ++ ...ittware-520N-(Intel-Stratix-10-GX2800).txt | 53 ++ ...ware-520N-(Intel-Stratix-10-GX2800)_ni.txt | 53 ++ ...l-FPGA-PAC-D5005-(Intel-Stratix-10-SX).txt | 50 ++ ...PGA-PAC-D5005-(Intel-Stratix-10-SX)_ni.txt | 50 ++ ...ittware-520N-(Intel-Stratix-10-GX2800).txt | 52 ++ ...ware-520N-(Intel-Stratix-10-GX2800)_ni.txt | 52 ++ ...GA-PAC-D5005-(Intel-Stratix-10-SX)-SVM.txt | 61 ++ ...PAC-D5005-(Intel-Stratix-10-SX)-SVM_ni.txt | 61 ++ .../csv_result_export/raw_results/README.md | 21 + STREAM/csv_result_export/requirements.txt | 3 + .../sp_global_ring_plot.jpeg | Bin 0 -> 209487 bytes STREAM/csv_result_export/sp_plot.jpeg | Bin 0 -> 224552 bytes STREAM/results.txt | 506 ++++++++++++++ .../settings/settings.compile.xilinx.ddr.ini | 5 + STREAM/settings/settings.compile.xilinx.ini | 4 + ...ettings.link.xilinx.stream_kernels.ddr.ini | 24 + ...s.link.xilinx.stream_kernels.generator.ini | 17 + ....link.xilinx.stream_kernels_single.ddr.ini | 15 + ...xilinx.stream_kernels_single.generator.ini | 8 + STREAM/src/common/parameters.h.in | 37 + STREAM/src/device/CMakeLists.txt | 124 ++++ STREAM/src/device/generateKernels.cmake | 17 + .../src/device/generateXilinxSettings.cmake | 36 + STREAM/src/device/stream_kernels.cl | 59 ++ STREAM/src/device/stream_kernels_single.cl | 45 ++ STREAM/src/host/CMakeLists.txt | 27 + STREAM/src/host/execution.h | 88 +++ STREAM/src/host/execution_default.cpp | 632 ++++++++++++++++++ STREAM/src/host/main.cpp | 79 +++ STREAM/src/host/setup/fpga_setup.cpp | 273 ++++++++ STREAM/src/host/setup/fpga_setup.hpp | 108 +++ STREAM/src/host/stream_functionality.cpp | 271 ++++++++ STREAM/src/host/stream_functionality.hpp | 116 ++++ STREAM/tests/CMakeLists.txt | 29 + STREAM/tests/test_fpga_setup.cpp | 38 ++ ...nel_functionality_and_host_integration.cpp | 100 +++ b_eff/.gitignore | 3 + b_eff/CHANGELOG | 24 + b_eff/CMakeLists.txt | 55 ++ b_eff/LICENSE | 21 + b_eff/README.md | 164 +++++ b_eff/performance/README.md | 231 +++++++ b_eff/performance/bandwidth_compare.jpg | Bin 0 -> 30840 bytes b_eff/performance/bandwidth_model.jpg | Bin 0 -> 20021 bytes b_eff/src/common/parameters.h.in | 24 + b_eff/src/device/CMakeLists.txt | 35 + b_eff/src/device/communication_bw520n.cl | 138 ++++ .../communication_bw520n_combined_loops.cl | 118 ++++ ...communication_bw520n_disable_pipelining.cl | 122 ++++ b_eff/src/host/CMakeLists.txt | 13 + b_eff/src/host/execution.h | 67 ++ b_eff/src/host/execution_default.cpp | 84 +++ b_eff/src/host/main.cpp | 115 ++++ b_eff/src/host/network_functionality.cpp | 180 +++++ b_eff/src/host/network_functionality.hpp | 94 +++ b_eff/src/host/setup/fpga_setup.cpp | 295 ++++++++ b_eff/src/host/setup/fpga_setup.hpp | 108 +++ b_eff/tests/CMakeLists.txt | 16 + b_eff/tests/test_fpga_setup.cpp | 38 ++ ...nel_functionality_and_host_integration.cpp | 183 +++++ extern/cxxopts | 1 + extern/googletest | 1 + extern/hlslib | 1 + 205 files changed, 18320 insertions(+) create mode 100644 .gitignore create mode 100644 .gitmodules create mode 100644 FFT/.gitignore create mode 100644 FFT/CHANGELOG create mode 100755 FFT/CMakeLists.txt create mode 100644 FFT/LICENSE create mode 100644 FFT/README.md create mode 100644 FFT/performance/README.md create mode 100644 FFT/src/common/parameters.h.in create mode 100644 FFT/src/device/CMakeLists.txt create mode 100644 FFT/src/device/fft1d_float_8.cl create mode 100644 FFT/src/device/fft_8.cl create mode 100644 FFT/src/device/twid_radix4_8.cl create mode 100755 FFT/src/host/CMakeLists.txt create mode 100644 FFT/src/host/execution.h create mode 100644 FFT/src/host/execution_default.cpp create mode 100644 FFT/src/host/fft_functionality.cpp create mode 100644 FFT/src/host/fft_functionality.hpp create mode 100644 FFT/src/host/main.cpp create mode 100644 FFT/src/host/setup/fpga_setup.cpp create mode 100644 FFT/src/host/setup/fpga_setup.hpp create mode 100644 FFT/synth-scripts/synth-fft-noctua.sh create mode 100755 FFT/tests/CMakeLists.txt create mode 100644 FFT/tests/test_execution_functionality.cpp create mode 100644 FFT/tests/test_fft_functionality.cpp create mode 100644 FFT/tests/test_fpga_setup.cpp create mode 100644 GEMM/.gitignore create mode 100755 GEMM/CMakeLists.txt create mode 100644 GEMM/Readme.md create mode 100755 GEMM/performance/README.md create mode 100755 GEMM/performance/performance_model_cannon.py create mode 100755 GEMM/performance/requirements.txt create mode 100644 GEMM/src/common/parameters.h.in create mode 100644 GEMM/src/device/CMakeLists.txt create mode 100644 GEMM/src/device/gemm_cannon.cl create mode 100755 GEMM/src/host/CMakeLists.txt create mode 100644 GEMM/src/host/execution.h create mode 100644 GEMM/src/host/execution_cannon.cpp create mode 100644 GEMM/src/host/gemm_functionality.cpp create mode 100644 GEMM/src/host/gemm_functionality.hpp create mode 100644 GEMM/src/host/main.cpp create mode 100644 GEMM/src/host/setup/fpga_setup.cpp create mode 100644 GEMM/src/host/setup/fpga_setup.hpp create mode 100755 GEMM/tests/CMakeLists.txt create mode 100644 GEMM/tests/test_fpga_setup.cpp create mode 100644 GEMM/tests/test_kernel_functionality_and_host_integration.cpp create mode 100644 LICENSE create mode 100644 LINPACK/.gitignore create mode 100644 LINPACK/CHANGELOG create mode 100755 LINPACK/CMakeLists.txt create mode 100644 LINPACK/LICENSE create mode 100644 LINPACK/Readme.md create mode 100644 LINPACK/src/common/parameters.h.in create mode 100644 LINPACK/src/device/CMakeLists.txt create mode 100644 LINPACK/src/device/lu_blocked.cl create mode 100644 LINPACK/src/device/lu_blocked_pvt.cl create mode 100644 LINPACK/src/device/lu_blocked_pvt_test.cl create mode 100755 LINPACK/src/host/CMakeLists.txt create mode 100644 LINPACK/src/host/execution.h create mode 100644 LINPACK/src/host/execution_blocked.cpp create mode 100644 LINPACK/src/host/execution_blocked_pvt.cpp create mode 100644 LINPACK/src/host/linpack_functionality.cpp create mode 100644 LINPACK/src/host/linpack_functionality.hpp create mode 100644 LINPACK/src/host/main.cpp create mode 100644 LINPACK/src/host/setup/fpga_setup.cpp create mode 100644 LINPACK/src/host/setup/fpga_setup.hpp create mode 100755 LINPACK/tests/CMakeLists.txt create mode 100644 LINPACK/tests/test_fpga_setup.cpp create mode 100644 LINPACK/tests/test_kernel_functionality_and_host_integration.cpp create mode 100644 LINPACK/tests/test_kernel_functionality_separate_cores.cpp create mode 100644 PTRANS/.gitignore create mode 100755 PTRANS/CMakeLists.txt create mode 100644 PTRANS/README.md create mode 100644 PTRANS/src/common/parameters.h.in create mode 100644 PTRANS/src/device/CMakeLists.txt create mode 100644 PTRANS/src/device/transpose_default.cl create mode 100644 PTRANS/src/device/transpose_optimized.cl create mode 100755 PTRANS/src/host/CMakeLists.txt create mode 100644 PTRANS/src/host/execution.h create mode 100644 PTRANS/src/host/execution_default.cpp create mode 100644 PTRANS/src/host/main.cpp create mode 100644 PTRANS/src/host/setup/fpga_setup.cpp create mode 100644 PTRANS/src/host/setup/fpga_setup.hpp create mode 100644 PTRANS/src/host/transpose_functionality.cpp create mode 100644 PTRANS/src/host/transpose_functionality.hpp create mode 100755 PTRANS/tests/CMakeLists.txt create mode 100644 PTRANS/tests/test_fpga_setup.cpp create mode 100644 PTRANS/tests/test_host_functionality.cpp create mode 100644 PTRANS/tests/test_kernel_functionality_and_host_integration.cpp create mode 100755 README.md create mode 100644 RandomAccess/.gitignore create mode 100644 RandomAccess/CHANGELOG create mode 100755 RandomAccess/CMakeLists.txt create mode 100644 RandomAccess/LICENSE create mode 100644 RandomAccess/README.md create mode 100644 RandomAccess/results/README.md create mode 100644 RandomAccess/results/frandom_single_results.csv create mode 100644 RandomAccess/results/frandom_single_results.jpg create mode 100644 RandomAccess/settings/settings.compile.xilinx.ini create mode 100644 RandomAccess/settings/settings.link.xilinx.random_access.ddr.ini create mode 100644 RandomAccess/settings/settings.link.xilinx.random_access_kernels_single.generator.ini create mode 100644 RandomAccess/src/common/parameters.h.in create mode 100644 RandomAccess/src/device/CMakeLists.txt create mode 100644 RandomAccess/src/device/generateKernels.cmake create mode 100644 RandomAccess/src/device/generateXilinxSettings.cmake create mode 100644 RandomAccess/src/device/random_access_kernels_ndrange.cl create mode 100644 RandomAccess/src/device/random_access_kernels_single.cl create mode 100644 RandomAccess/src/device/random_access_kernels_single_rnd.cl create mode 100755 RandomAccess/src/host/CMakeLists.txt create mode 100644 RandomAccess/src/host/execution.h create mode 100644 RandomAccess/src/host/execution_ndrange.cpp create mode 100644 RandomAccess/src/host/execution_single.cpp create mode 100644 RandomAccess/src/host/execution_single_rnd.cpp create mode 100644 RandomAccess/src/host/main.cpp create mode 100644 RandomAccess/src/host/random_access_functionality.cpp create mode 100644 RandomAccess/src/host/random_access_functionality.hpp create mode 100644 RandomAccess/src/host/setup/fpga_setup.cpp create mode 100644 RandomAccess/src/host/setup/fpga_setup.hpp create mode 100755 RandomAccess/tests/CMakeLists.txt create mode 100644 RandomAccess/tests/test_fpga_setup.cpp create mode 100644 RandomAccess/tests/test_host_code.cpp create mode 100644 RandomAccess/tests/test_kernel_functionality_and_host_integration.cpp create mode 100644 STREAM/.gitignore create mode 100644 STREAM/CHANGELOG create mode 100755 STREAM/CMakeLists.txt create mode 100644 STREAM/LICENSE create mode 100644 STREAM/README.md create mode 100644 STREAM/csv_result_export/README.md create mode 100755 STREAM/csv_result_export/create_plots.py create mode 100644 STREAM/csv_result_export/dp_global_ring_plot.jpeg create mode 100644 STREAM/csv_result_export/dp_plot.jpeg create mode 100755 STREAM/csv_result_export/parse_data.py create mode 100644 STREAM/csv_result_export/raw_results/16-0-2_16_0_2_Intel-BDW+FPGA-hybrid-CPU+FPGA-Arria-10-GX1150.txt create mode 100644 STREAM/csv_result_export/raw_results/17-1-2_17-1-2_Bittware-385A-(Intel-Arria-10-GX1150)-Buffer-Reorder_ni.txt create mode 100644 STREAM/csv_result_export/raw_results/17-1-2_17-1-2_Bittware-385A-(Intel-Arria-10-GX1150).txt create mode 100644 STREAM/csv_result_export/raw_results/17-1-2_17-1-2_Bittware-385A-(Intel-Arria-10-GX1150)_ni.txt create mode 100644 STREAM/csv_result_export/raw_results/18-0-1_18-0-1_Bittware-520N-(Intel-Stratix-10-GX2800).txt create mode 100644 STREAM/csv_result_export/raw_results/18-0-1_18-0-1_Bittware-520N-(Intel-Stratix-10-GX2800)_ni.txt create mode 100644 STREAM/csv_result_export/raw_results/18-1-1_18-1-1_Bittware-520N-(Intel-Stratix-10-GX2800).txt create mode 100644 STREAM/csv_result_export/raw_results/18-1-1_18-1-1_Bittware-520N-(Intel-Stratix-10-GX2800)_ni.txt create mode 100644 STREAM/csv_result_export/raw_results/19-1_19-1_Bittware-520N-(Intel-Stratix-10-GX2800).txt create mode 100644 STREAM/csv_result_export/raw_results/19-1_19-1_Bittware-520N-(Intel-Stratix-10-GX2800)_ni.txt create mode 100644 STREAM/csv_result_export/raw_results/19-2_19-2_Bittware-520N-(Intel-Stratix-10-GX2800).txt create mode 100644 STREAM/csv_result_export/raw_results/19-2_19-2_Bittware-520N-(Intel-Stratix-10-GX2800)_ni.txt create mode 100644 STREAM/csv_result_export/raw_results/19-3_18-1-2_Intel-FPGA-PAC-D5005-(Intel-Stratix-10-SX).txt create mode 100644 STREAM/csv_result_export/raw_results/19-3_18-1-2_Intel-FPGA-PAC-D5005-(Intel-Stratix-10-SX)_ni.txt create mode 100644 STREAM/csv_result_export/raw_results/19-3_19-2_Bittware-520N-(Intel-Stratix-10-GX2800).txt create mode 100644 STREAM/csv_result_export/raw_results/19-3_19-2_Bittware-520N-(Intel-Stratix-10-GX2800)_ni.txt create mode 100644 STREAM/csv_result_export/raw_results/19-4_18-1-2_Intel-FPGA-PAC-D5005-(Intel-Stratix-10-SX)-SVM.txt create mode 100644 STREAM/csv_result_export/raw_results/19-4_18-1-2_Intel-FPGA-PAC-D5005-(Intel-Stratix-10-SX)-SVM_ni.txt create mode 100644 STREAM/csv_result_export/raw_results/README.md create mode 100644 STREAM/csv_result_export/requirements.txt create mode 100644 STREAM/csv_result_export/sp_global_ring_plot.jpeg create mode 100644 STREAM/csv_result_export/sp_plot.jpeg create mode 100644 STREAM/results.txt create mode 100644 STREAM/settings/settings.compile.xilinx.ddr.ini create mode 100644 STREAM/settings/settings.compile.xilinx.ini create mode 100644 STREAM/settings/settings.link.xilinx.stream_kernels.ddr.ini create mode 100644 STREAM/settings/settings.link.xilinx.stream_kernels.generator.ini create mode 100644 STREAM/settings/settings.link.xilinx.stream_kernels_single.ddr.ini create mode 100644 STREAM/settings/settings.link.xilinx.stream_kernels_single.generator.ini create mode 100644 STREAM/src/common/parameters.h.in create mode 100644 STREAM/src/device/CMakeLists.txt create mode 100644 STREAM/src/device/generateKernels.cmake create mode 100644 STREAM/src/device/generateXilinxSettings.cmake create mode 100644 STREAM/src/device/stream_kernels.cl create mode 100644 STREAM/src/device/stream_kernels_single.cl create mode 100755 STREAM/src/host/CMakeLists.txt create mode 100644 STREAM/src/host/execution.h create mode 100644 STREAM/src/host/execution_default.cpp create mode 100644 STREAM/src/host/main.cpp create mode 100644 STREAM/src/host/setup/fpga_setup.cpp create mode 100644 STREAM/src/host/setup/fpga_setup.hpp create mode 100644 STREAM/src/host/stream_functionality.cpp create mode 100644 STREAM/src/host/stream_functionality.hpp create mode 100755 STREAM/tests/CMakeLists.txt create mode 100644 STREAM/tests/test_fpga_setup.cpp create mode 100644 STREAM/tests/test_kernel_functionality_and_host_integration.cpp create mode 100644 b_eff/.gitignore create mode 100755 b_eff/CHANGELOG create mode 100755 b_eff/CMakeLists.txt create mode 100644 b_eff/LICENSE create mode 100644 b_eff/README.md create mode 100644 b_eff/performance/README.md create mode 100644 b_eff/performance/bandwidth_compare.jpg create mode 100644 b_eff/performance/bandwidth_model.jpg create mode 100644 b_eff/src/common/parameters.h.in create mode 100644 b_eff/src/device/CMakeLists.txt create mode 100644 b_eff/src/device/communication_bw520n.cl create mode 100644 b_eff/src/device/communication_bw520n_combined_loops.cl create mode 100644 b_eff/src/device/communication_bw520n_disable_pipelining.cl create mode 100755 b_eff/src/host/CMakeLists.txt create mode 100644 b_eff/src/host/execution.h create mode 100644 b_eff/src/host/execution_default.cpp create mode 100644 b_eff/src/host/main.cpp create mode 100644 b_eff/src/host/network_functionality.cpp create mode 100644 b_eff/src/host/network_functionality.hpp create mode 100644 b_eff/src/host/setup/fpga_setup.cpp create mode 100644 b_eff/src/host/setup/fpga_setup.hpp create mode 100755 b_eff/tests/CMakeLists.txt create mode 100644 b_eff/tests/test_fpga_setup.cpp create mode 100644 b_eff/tests/test_kernel_functionality_and_host_integration.cpp create mode 160000 extern/cxxopts create mode 160000 extern/googletest create mode 160000 extern/hlslib diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..ab49c00d --- /dev/null +++ b/.gitignore @@ -0,0 +1,7 @@ +*/.DS_Store +.DS_Store +cmake-* +.vscode +*._* +build-* +.idea diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 00000000..67257256 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,9 @@ +[submodule "extern/cxxopts"] + path = extern/cxxopts + url = https://github.com/jarro2783/cxxopts.git +[submodule "extern/hlslib"] + path = extern/hlslib + url = https://github.com/definelicht/hlslib.git +[submodule "extern/googletest"] + path = extern/googletest + url = https://github.com/google/googletest.git diff --git a/FFT/.gitignore b/FFT/.gitignore new file mode 100644 index 00000000..164bcc9c --- /dev/null +++ b/FFT/.gitignore @@ -0,0 +1,4 @@ +cmake-* +.DS_Store +build-* +.idea diff --git a/FFT/CHANGELOG b/FFT/CHANGELOG new file mode 100644 index 00000000..f7bafbe5 --- /dev/null +++ b/FFT/CHANGELOG @@ -0,0 +1,9 @@ +# Changelog + +This file contains all changes made to the source code for each release. + +## 1.0 + +#### Added: +- Host code and OpenCL kernel from Intel FPGA SDK AOC examples +- Execution result for the Bittware 520N board with brief performance model \ No newline at end of file diff --git a/FFT/CMakeLists.txt b/FFT/CMakeLists.txt new file mode 100755 index 00000000..bea267ff --- /dev/null +++ b/FFT/CMakeLists.txt @@ -0,0 +1,36 @@ +cmake_minimum_required(VERSION 2.8.12) +project(fFFT) + +set(VERSION 1.0) +set(DEFAULT_REPETITIONS 10 CACHE STRING "Default number of repetitions") +set(DEFAULT_ITERATIONS 100 CACHE STRING "Default number of iterations that is done with a single kernel execution") +set(HOST_DATA_TYPE cl_float CACHE STRING "Data type used by the host code. Should match the data type of the used FFT") +set(DEFAULT_DEVICE -1 CACHE STRING "Index of the default device to use") +set(DEFAULT_PLATFORM -1 CACHE STRING "Index of the default platform to use") +set(FPGA_BOARD_NAME p520_hpc_sg280l CACHE STRING "Name of the target FPGA board") + +set(AOC_FLAGS "-fpc -fp-relaxed" CACHE STRING "Used flags for the AOC compiler") +separate_arguments(AOC_FLAGS) + + +set(FFT_KERNEL_NAME fft1d CACHE STRING "Name of the kernel that is used for calculation") +set(FETCH_KERNEL_NAME fetch CACHE STRING "Name of the kernel that is used to fetch data from global memory") +set(LOG_FFT_SIZE 12 CACHE STRING "Log2 of the used FFT size") +set(FFT_UNROLL 8 CACHE STRING "Amount of global memory unrolling of the kernel. Will be used by the host to calculate NDRange sizes") + +set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_SOURCE_DIR}/../extern/hlslib/cmake) +set(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin) + +configure_file( + "${CMAKE_SOURCE_DIR}/src/common/parameters.h.in" + "${CMAKE_BINARY_DIR}/src/common/parameters.h" +) + +include_directories(${CMAKE_BINARY_DIR}/src/common) + +find_package(IntelFPGAOpenCL REQUIRED) + +add_subdirectory(src/device) +add_subdirectory(src/host) +add_subdirectory(tests) + diff --git a/FFT/LICENSE b/FFT/LICENSE new file mode 100644 index 00000000..fc47bf1c --- /dev/null +++ b/FFT/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020 pc2 + +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/FFT/README.md b/FFT/README.md new file mode 100644 index 00000000..ea0a054f --- /dev/null +++ b/FFT/README.md @@ -0,0 +1,114 @@ +# FFT Benchmark for FPGA + +This repository contains the FFT Benchmark for FPGA and its OpenCL kernels. +Currently only the Intel FPGA SDK for OpenCL utility is supported. + +It is based on the FFT benchmark of the [HPC Challenge Benchmark](https://icl.utk.edu/hpcc/) suite. +The FFT1D reference implementation is used for the kernel code. + +## Dependencies + +The benchmark comes with the following requirements for building and running: + +- CMake 2.8 +- GCC 4.9 +- Intel OpenCL FPGA SDK 19.3 + +It also contains submodules that will be automatically updated when running cmake: + +- cxxopts: A header only library to parse command line parameters +- googletest: A C++ test framework + +## Build + +CMake is used as the build system. +The targets below can be used to build the benchmark and its kernels: + + | Target | Description | + | -------- | ---------------------------------------------- | + | fFFT | Builds the host application | + | Google_Tests_run| Compile the tests and its dependencies | + + More over the are additional targets to generate kernel reports and bitstreams. + The provided kernel is optimized for Stratix 10 with 512bit LSUs. + The kernel targets are: + + | Target | Description | + | -------- | ---------------------------------------------- | + | fft1d_float_8 | Synthesizes the kernel (takes several hours!) | + | fft1d_float_8_report | Create an HTML report for the kernel | + | fft1d_float_8_emulate | Create a n emulation kernel | + + + You can build for example the host application by running + + mkdir build && cd build + cmake .. + make fFFT + +You will find all executables and kernel files in the `bin` +folder of your build directory. +You should always specify a target with make to reduce the build time! +You might want to specify predefined parameters before build: + +Name | Default | Description | +---------------- |-------------|--------------------------------------| +`DEFAULT_DEVICE` | -1 | Index of the default device (-1 = ask) | +`DEFAULT_PLATFORM`| -1 | Index of the default platform (-1 = ask) | +`FPGA_BOARD_NAME`| p520_hpc_sg280l | Name of the target board | +`DEFAULT_REPETITIONS`| 10 | Number of times the kernel will be executed | +`DEFAULT_ITERATIONS`| 100 | Default number of iterations that is done with a single kernel execution| +`LOG_FFT_SIZE` | 12 | Log2 of the FFT Size that has to be used i.e. 3 leads to a FFT Size of 2^3=8| +`AOC_FLAGS`| `-fpc -fp-relaxed` | Additional AOC compiler flags that are used for kernel compilation | + +Moreover the environment variable `INTELFPGAOCLSDKROOT` has to be set to the root +of the Intel FPGA SDK installation. + +Additionally it is possible to set the used compiler and other build tools +in the `CMakeCache.txt` located in the build directory after running cmake. + + + +## Execution + +For execution of the benchmark run: + + ./fFFT -f path_to_kernel.aocx + +For more information on available input parameters run + + ./fFFT -h + +To execute the unit and integration tests run + + ./Google_Tests_run + +in the `bin` folder within the build directory. +It will run an emulation of the kernel and execute some functionality tests. + +## Output Interpretation + +The benchmark will print the following two tables to standard output after execution: + + res. error mach. eps + 2.67000e-01 1.19209e-07 + + avg best + Time in s: 7.56801e-03 7.07241e-03 + GFLOPS: 3.24735e-02 3.47491e-02 + +The first table contains the maximum residual error of the calculation and the +machine epsilon that was used to calculate the residual error. +The benchmark will perform a FFT with the FPGA kernel on random input data. +In a second step the resulting data will be used as input for an iFFT using a CPU +reference implementation in double precision. +The residual error is then calculated with: + +![res=\frac{||x-x'||}{\epsilon*ld(n)}](https://latex.codecogs.com/gif.latex?res=\frac{||x-x'||}{\epsilon*ld(n)}) + +where `x` is the input data of the FFT, `x'` the resulting data from the iFFT, epsilon the machine epsilon and `n` the FFT size. + +In the second table the measured execution times and calculated FLOPs are given. +It gives the average and bast for both. +The time gives the averaged execution time for a single FFT in case of a batched execution (an execution with more than one iteration). +They are also used to calculate the FLOPs. diff --git a/FFT/performance/README.md b/FFT/performance/README.md new file mode 100644 index 00000000..10c44cbd --- /dev/null +++ b/FFT/performance/README.md @@ -0,0 +1,90 @@ +# Performance Evaluation + +## Performance Model + +FFT1d kernel modelled here can be found in the Intel OpenCL Design Samples. +The design follows the radix 22 FFT architecture, which consists of the following: + +1. ld(N) radix-2 butterflies +2. trivial rotations at every even stage +3. non-trivial rotations at every odd stage. This is the twiddle factor multiplication computed after the stage's butterfly. +4. shuffling using shift registers + +The FFT if fully pipelined and the FFT step is unrolled over all ld(N) stages. +Hence the performance is limited by the global memory to feed the pipeline with data. +We will focus on modeling the fetch kernel that is loading the data from memory. +The kernel pipeline can be expressed with the following equation: + +![t_{mempipeline}=\frac{\frac{s_{FFT}}{s_{bus}}}{f}](https://latex.codecogs.com/gif.latex?t_{mempipeline}=\frac{\frac{s_{FFT}}{s_{bus}}}{f}) + +where ![s_{FFT}](https://latex.codecogs.com/gif.latex?s_{block}) is the number of bytes needed to load from global memory for the FFT i.e. 4096 * 8B for a 4096 FFT with single precision complex values. +![s_{bus}](https://latex.codecogs.com/gif.latex?s_{bus}) the bus width of the global memory in bytes. +![f](https://latex.codecogs.com/gif.latex?f) is the kernel frequency. +Moreover latency will be added to this operation for every DRAM page that has to be activated: + +![t_{memoverhead}=\frac{s_{FFT}}{s_{page}}*\(t_{RCD}+t_{RP}\)](https://latex.codecogs.com/gif.latex?t_{memoverhead}=\frac{s_{FFT}}{s_{page}}*\(t_{RCD}+t_{RP}\)) + +where ![s_{page}](https://latex.codecogs.com/gif.latex?s_{page}) is the size of a DRAM page in bytes. +![t_{RCD}](https://latex.codecogs.com/gif.latex?t_{RCD}) and ![t_{RP}](https://latex.codecogs.com/gif.latex?t_{RP}) are the +row address to column address delay and the row precharge time. + +So the total time for the memory accesses for a the calculation of a single FFT is: + +![t_{mem}=t_{mempipeline}+t_{memoverhead}](https://latex.codecogs.com/gif.latex?t_{mem}=t_{mempipeline}+t_{memoverhead}) + +This model does not consider latencies of the calculation pipeline or of the memory but it holds for batched calculations where these latencies are hidden. +If memory interleaving is used, t_memoverhead is also hidden by the access to subsequent memory banks. + +## Synthesis Results + +The kernel was synthesized with the following configuration for the Bittware 520N board: + +Name | Default | Description | +---------------- |-------------|--------------------------------------| +`DEFAULT_DEVICE` | -1 | Index of the default device (-1 = ask) | +`DEFAULT_PLATFORM`| -1 | Index of the default platform (-1 = ask) | +`FPGA_BOARD_NAME`| p520_hpc_sg280l | Name of the target board | +`DEFAULT_REPETITIONS`| 10 | Number of times the kernel will be executed | +`DEFAULT_ITERATIONS`| 5000 | Default number of iterations that is done with a single kernel execution| +`LOG_FFT_SIZE` | 12 | Log2 of the FFT Size that has to be used i.e. 3 leads to a FFT Size of 2^3=8| +`AOC_FLAGS`| `-fpc -fp-relaxed` | Additional AOC compiler flags that are used for kernel compilation | + +The used tool versions: + +Tool | Version | +---------------- |---------| +Intel OpenCL SDK | 19.4.0 | +BSP | 19.2.0 | +GCC | 8.3.0 | + +The resulting output: + + ------------------------------------------------------------- + Implementation of the FFT benchmark proposed in the HPCC benchmark suite for FPGA. + Version: 1.0 + ------------------------------------------------------------- + Summary: + FFT Size: 4096 + Data Size: 5000 * FFT Size * sizeof(cl_float) = 8.19200e+07 Byte + Repetitions: 10 + Kernel file: fft1d_float_8.aocx + Device: p520_hpc_sg280l : BittWare Stratix 10 OpenCL platform (aclbitt_s10_pcie0) + ------------------------------------------------------------- + Start benchmark using the given configuration. + ------------------------------------------------------------- + res. error mach. eps + 3.17324e-01 1.19209e-07 + + avg best + Time in s: 1.81336e-06 1.81170e-06 + GFLOPS: 1.35528e+02 1.35652e+02 + +So the FFT implementation achieved 135.7 GFLOPs with a kernel frequency of 297.5MHz. +The kernel uses memory interleaving so the model simplifies to: + +![t_{mem}=\frac{\frac{4096}{8}}{297.5MHz}=1.72\mu&space;s](https://latex.codecogs.com/gif.latex?t_{mem}=\frac{\frac{4096}{8}}{297.5MHz}=1.72\mu&space;s) + +which shows an 5.2% difference to the measurement that resulted in 1.81µs. +The difference may be caused by the latencies of the global memory and the calculation pipeline. +Also the store of the FFT result may interfere with the load operations since they use the same memory banks. + diff --git a/FFT/src/common/parameters.h.in b/FFT/src/common/parameters.h.in new file mode 100644 index 00000000..a78481bb --- /dev/null +++ b/FFT/src/common/parameters.h.in @@ -0,0 +1,27 @@ +#ifndef SRC_COMMON_PARAMETERS_H_ +#define SRC_COMMON_PARAMETERS_H_ + +/** + * Host specific parameters + */ +#define VERSION "@VERSION@" +#define DEFAULT_REPETITIONS @DEFAULT_REPETITIONS@ +#define DEFAULT_ITERATIONS @DEFAULT_ITERATIONS@ +#define DEFAULT_PLATFORM @DEFAULT_PLATFORM@ +#define DEFAULT_DEVICE @DEFAULT_DEVICE@ +#define HOST_DATA_TYPE @HOST_DATA_TYPE@ +#define FFT_KERNEL_NAME "@FFT_KERNEL_NAME@" +#define FETCH_KERNEL_NAME "@FETCH_KERNEL_NAME@" + +/** + * Kernel Parameters + */ +#define LOG_FFT_SIZE @LOG_FFT_SIZE@ +#define FFT_UNROLL @FFT_UNROLL@ + +/** +Output separator +*/ +#define HLINE "-------------------------------------------------------------\n" + +#endif // SRC_COMMON_PARAMETERS_H_ \ No newline at end of file diff --git a/FFT/src/device/CMakeLists.txt b/FFT/src/device/CMakeLists.txt new file mode 100644 index 00000000..1067aef7 --- /dev/null +++ b/FFT/src/device/CMakeLists.txt @@ -0,0 +1,35 @@ + +set(AOC_INCLUDES "-I${CMAKE_CURRENT_BINARY_DIR}/../common") + +function(generate_kernel_targets) + foreach (kernel_file_name ${ARGN}) + set(source_f ${CMAKE_CURRENT_SOURCE_DIR}/${kernel_file_name}.cl) + set(report_f ${EXECUTABLE_OUTPUT_PATH}/${kernel_file_name}_report) + set(bitstream_emulate_f ${EXECUTABLE_OUTPUT_PATH}/${kernel_file_name}_emulate.aocx) + set(bitstream_f ${EXECUTABLE_OUTPUT_PATH}/${kernel_file_name}.aocx) + set(out_f "${CMAKE_CURRENT_BINARY_DIR}/${out_f}") + add_custom_command(OUTPUT ${bitstream_emulate_f} + COMMAND ${IntelFPGAOpenCL_AOC} ${source_f} ${AOC_INCLUDES} ${AOC_FLAGS} -legacy-emulator -march=emulator + -o ${bitstream_emulate_f} + ) + add_custom_command(OUTPUT ${bitstream_f} + COMMAND ${IntelFPGAOpenCL_AOC} ${source_f} ${AOC_INCLUDES} ${AOC_FLAGS} -board=${FPGA_BOARD_NAME} + -o ${bitstream_f} + ) + add_custom_command(OUTPUT ${report_f} + COMMAND ${IntelFPGAOpenCL_AOC} ${source_f} ${AOC_INCLUDES} ${AOC_FLAGS} -rtl -report -board=${FPGA_BOARD_NAME} + -o ${report_f} + ) + add_custom_target(${kernel_file_name}_report DEPENDS ${report_f} + DEPENDS ${source_f} ${CMAKE_BINARY_DIR}/src/common/parameters.h + SOURCES ${source_f} ${CMAKE_BINARY_DIR}/src/common/parameters.h) + add_custom_target(${kernel_file_name} DEPENDS ${bitstream_f} + DEPENDS ${source_f} ${CMAKE_BINARY_DIR}/src/common/parameters.h + SOURCES ${source_f} ${CMAKE_BINARY_DIR}/src/common/parameters.h) + add_custom_target(${kernel_file_name}_emulate DEPENDS ${bitstream_emulate_f} + DEPENDS ${source_f} ${CMAKE_BINARY_DIR}/src/common/parameters.h + SOURCES ${source_f} ${CMAKE_BINARY_DIR}/src/common/parameters.h) + endforeach () +endfunction() + +generate_kernel_targets(fft1d_float_8) diff --git a/FFT/src/device/fft1d_float_8.cl b/FFT/src/device/fft1d_float_8.cl new file mode 100644 index 00000000..9cf70034 --- /dev/null +++ b/FFT/src/device/fft1d_float_8.cl @@ -0,0 +1,258 @@ +// Copyright (C) 2013-2019 Altera Corporation, San Jose, California, USA. All rights reserved. +// 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. +// +// This agreement shall be governed in all respects by the laws of the State of California and +// by the laws of the United States of America. + +/* This is the top-level device source file for the fft1d example. The code is + * written as an OpenCL single work-item kernel. This coding style allows the + * compiler to extract loop-level parallelism from the source code and + * instantiate a hardware pipeline capable of executing concurrently a large + * number of loop iterations. The compiler analyses loop-carried dependencies, + * and these translate into data transfers across concurrently executed loop + * iterations. + * + * Careful coding ensures that all loop-carried dependencies are trivial, + * merely data transfers which span a single clock cycle. The FFT algorithm + * requires passing data forward across loop iterations. The code uses a + * sliding window to implement these data transfers. The advantage of using a + * sliding window is that dependencies across consecutive loop iterations have + * an invariant source and destination (pairs of constant offset array + * elements). Such transfer patterns can be implemented efficiently by the + * FPGA hardware. All this ensures an overall processing a throughput of one + * loop iteration per clock cycle. + * + * The size of the FFT transform can be customized via an argument to the FFT + * engine. This argument has to be a compile time constant to ensure that the + * compiler can propagate it throughout the function body and generate + * efficient hardware. + */ + +// Include source code for an engine that produces 8 points each step +#include "fft_8.cl" +#include "parameters.h" + +#pragma OPENCL EXTENSION cl_intel_channels : enable + +#define min(a,b) (a= 0) ? (LOG_CONT_FACTOR_LIMIT1) : 0) +#define LOG_CONT_FACTOR (((LOG_CONT_FACTOR_LIMIT2) <= 6) ? (LOG_CONT_FACTOR_LIMIT1) : 6) +#define CONT_FACTOR (1 << LOG_CONT_FACTOR) + +// Need some depth to our channels to accommodate their bursty filling. +channel float2 chanin[POINTS] __attribute__((depth(CONT_FACTOR*POINTS))); + +uint bit_reversed(uint x, uint bits) { + uint y = 0; + #pragma unroll + for (uint i = 0; i < bits; i++) { + y <<= 1; + y |= x & 1; + x >>= 1; + } + y &= ((1 << bits) - 1); + return y; +} + +// fetch N points as follows: +// - each thread will load 8 consecutive values +// - load CONT_FACTOR consecutive loads (8 values each), then jump by N/8, and load next +// CONT_FACTOR consecutive values. +// - Once load CONT_FACTOR values starting at 7N/8, send CONT_FACTOR values +// into the channel to the fft kernel. +// - start process again. +// This way, only need 8xCONT_FACTOR local memory buffer, instead of 8xN. +// +// Group index is used as follows ( 0 to CONT_FACTOR, iteration num ) +// +// 64K values = 2^16, num_fetches=2^13, 2^6 = CONT_FACTOR, 2^7=num_Fetches / cont_factor +// +// < C >< A > +// 5432109876543210 +// A -- fetch within contiguous block +// B -- B * N/8 region selector +// C -- num times fetch cont_factor * 8 values (or num times fill the buffer) + +// INPUT GID POINTS +// C_LEN must be at least 0. Can't be negative. +#define A_START 0 +#define A_END (LOG_CONT_FACTOR + LOGPOINTS - 1) + +#define B_START (A_END + 1) +#define B_END (B_START + LOGPOINTS - 1) + +#define C_START (B_END + 1) +#define C_END (LOGN - 1) + +#define D_START (C_END + 1) +#define D_END 31 + +#define A_LEN (A_END - A_START + 1) +#define B_LEN (B_END - B_START + 1) +#define C_LEN (C_END - C_START + 1) +#define D_LEN (D_END - D_START + 1) +#define EXTRACT(id,start,len) ((id >> start) & ((1 << len) - 1)) + +uint permute_gid (uint gid) { + uint result = 0; + // result[31:16]= gid[31:16] = D + // result[15:13] = gid[10:8] = C + // result[12:8] = gid[15:11] = B + // result[7:0] = gid[10:0] = A + + uint A = EXTRACT(gid, A_START, A_LEN); + uint B = EXTRACT(gid, B_START, B_LEN); + uint C = EXTRACT(gid, C_START, C_LEN); + uint D = EXTRACT(gid, D_START, D_LEN); + + // swap B and C + uint new_c_start = A_END + 1; + uint new_b_start = new_c_start + C_LEN; + result = (D << D_START) | (B << new_b_start) | (C << new_c_start) | (A << A_START); + return result; +} + +// group dimension (N/(8*CONT_FACTOR), num_iterations) +__attribute__((reqd_work_group_size(CONT_FACTOR * POINTS, 1, 1))) +kernel void fetch (global float2 * restrict src) { + + const int N = (1 << LOGN); + // Each thread will fetch POINTS points. Need POINTS times to pass to FFT. + const int BUF_SIZE = 1 << (LOG_CONT_FACTOR + LOGPOINTS + LOGPOINTS); + + // Local memory for CONT_FACTOR * POINTS points + local float2 buf[BUF_SIZE]; + + uint iteration = get_global_id(1); + uint group_per_iter = get_global_id(0); + + // permute global addr but not the local addr + uint global_addr = iteration * N + group_per_iter; + global_addr = permute_gid (global_addr << LOGPOINTS); + uint lid = get_local_id(0); + uint local_addr = lid << LOGPOINTS; + + #pragma unroll + for (uint k = 0; k < POINTS; k++) { + buf[local_addr + k] = src[global_addr + k]; + } + + barrier (CLK_LOCAL_MEM_FENCE); + + #pragma unroll + for (uint k = 0; k < POINTS; k++) { + uint buf_addr = bit_reversed(k,LOGPOINTS) * CONT_FACTOR * POINTS + lid; + write_channel_intel (chanin[k], buf[buf_addr]); + } +} + + +/* Attaching the attribute 'task' to the top level kernel to indicate + * that the host enqueues a task (a single work-item kernel) + * + * 'src' and 'dest' point to the input and output buffers in global memory; + * using restrict pointers as there are no dependencies between the buffers + * 'count' represents the number of 4k sets to process + * 'inverse' toggles between the direct and the inverse transform + */ + +__attribute__ ((max_global_work_dim(0))) +kernel void fft1d(global float2 * restrict dest, + int count, int inverse) { + + const int N = (1 << LOGN); + + /* The FFT engine requires a sliding window array for data reordering; data + * stored in this array is carried across loop iterations and shifted by one + * element every iteration; all loop dependencies derived from the uses of + * this array are simple transfers between adjacent array elements + */ + + float2 fft_delay_elements[N + POINTS * (LOGN - 2)]; + + /* This is the main loop. It runs 'count' back-to-back FFT transforms + * In addition to the 'count * (N / 8)' iterations, it runs 'N / 8 - 1' + * additional iterations to drain the last outputs + * (see comments attached to the FFT engine) + * + * The compiler leverages pipeline parallelism by overlapping the + * iterations of this loop - launching one iteration every clock cycle + */ + + for (unsigned i = 0; i < count * (N / POINTS) + N / POINTS - 1; i++) { + + /* As required by the FFT engine, gather input data from 8 distinct + * segments of the input buffer; for simplicity, this implementation + * does not attempt to coalesce memory accesses and this leads to + * higher resource utilization (see the fft2d example for advanced + * memory access techniques) + */ + + int base = (i / (N / POINTS)) * N; + int offset = i % (N / POINTS); + + float2x8 data; + // Perform memory transfers only when reading data in range + if (i < count * (N / POINTS)) { + data.i0 = read_channel_intel(chanin[0]); + data.i1 = read_channel_intel(chanin[1]); + data.i2 = read_channel_intel(chanin[2]); + data.i3 = read_channel_intel(chanin[3]); + data.i4 = read_channel_intel(chanin[4]); + data.i5 = read_channel_intel(chanin[5]); + data.i6 = read_channel_intel(chanin[6]); + data.i7 = read_channel_intel(chanin[7]); + } else { + data.i0 = data.i1 = data.i2 = data.i3 = + data.i4 = data.i5 = data.i6 = data.i7 = 0; + } + + // Perform one step of the FFT engine + data = fft_step(data, i % (N / POINTS), fft_delay_elements, inverse, LOGN); + + /* Store data back to memory. FFT engine outputs are delayed by + * N / 8 - 1 steps, hence gate writes accordingly + */ + + if (i >= N / POINTS - 1) { + int base = POINTS * (i - (N / POINTS - 1)); + + // These consecutive accesses will be coalesced by the compiler + dest[base] = data.i0; + dest[base + 1] = data.i1; + dest[base + 2] = data.i2; + dest[base + 3] = data.i3; + dest[base + 4] = data.i4; + dest[base + 5] = data.i5; + dest[base + 6] = data.i6; + dest[base + 7] = data.i7; + } + } +} + diff --git a/FFT/src/device/fft_8.cl b/FFT/src/device/fft_8.cl new file mode 100644 index 00000000..f767d7c1 --- /dev/null +++ b/FFT/src/device/fft_8.cl @@ -0,0 +1,347 @@ +// Copyright (C) 2013-2019 Altera Corporation, San Jose, California, USA. All rights reserved. +// 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. +// +// This agreement shall be governed in all respects by the laws of the State of California and +// by the laws of the United States of America. + +// Complex single-precision floating-point radix-4 feedforward FFT / iFFT engine +// +// See Mario Garrido, Jesús Grajal, M. A. Sanchez, Oscar Gustafsson: +// Pipeline Radix-2k Feedforward FFT Architectures. +// IEEE Trans. VLSI Syst. 21(1): 23-32 (2013)) +// +// The log(size) of the transform must be a compile-time constant argument. +// This FFT engine processes 8 points for each invocation. The inputs are eight +// ordered streams while the outputs are in bit reversed order. +// +// The entry point of the engine is the 'fft_step' function. This function +// passes 8 data points through a fixed sequence of processing blocks +// (butterfly, rotation, swap, reorder, multiplications, etc.) and produces +// 8 output points towards the overall FFT transform. +// +// The engine is designed to be invoked from a loop in a single work-item task. +// When compiling a single work-item task, the compiler leverages pipeline +// parallelism and overlaps the execution of multiple invocations of this +// function. A new instance can start processing every clock cycle + + +// Includes tabled twiddle factors - storing constants uses fewer resources +// than instantiating 'cos' or 'sin' hardware +#include "twid_radix4_8.cl" + +// Convenience struct representing the 8 data points processed each step +// Each member is a float2 representing a complex number +typedef struct { + float2 i0; + float2 i1; + float2 i2; + float2 i3; + float2 i4; + float2 i5; + float2 i6; + float2 i7; +} float2x8; + +// FFT butterfly building block +float2x8 butterfly(float2x8 data) { + float2x8 res; + res.i0 = data.i0 + data.i1; + res.i1 = data.i0 - data.i1; + res.i2 = data.i2 + data.i3; + res.i3 = data.i2 - data.i3; + res.i4 = data.i4 + data.i5; + res.i5 = data.i4 - data.i5; + res.i6 = data.i6 + data.i7; + res.i7 = data.i6 - data.i7; + return res; +} + +// Swap real and imaginary components in preparation for inverse transform +float2x8 swap_complex(float2x8 data) { + float2x8 res; + res.i0.x = data.i0.y; + res.i0.y = data.i0.x; + res.i1.x = data.i1.y; + res.i1.y = data.i1.x; + res.i2.x = data.i2.y; + res.i2.y = data.i2.x; + res.i3.x = data.i3.y; + res.i3.y = data.i3.x; + res.i4.x = data.i4.y; + res.i4.y = data.i4.x; + res.i5.x = data.i5.y; + res.i5.y = data.i5.x; + res.i6.x = data.i6.y; + res.i6.y = data.i6.x; + res.i7.x = data.i7.y; + res.i7.y = data.i7.x; + return res; +} + +// FFT trivial rotation building block +float2x8 trivial_rotate(float2x8 data) { + float2 tmp = data.i3; + data.i3.x = tmp.y; + data.i3.y = -tmp.x; + tmp = data.i7; + data.i7.x = tmp.y; + data.i7.y = -tmp.x; + return data; +} + +// FFT data swap building block associated with trivial rotations +float2x8 trivial_swap(float2x8 data) { + float2 tmp = data.i1; + data.i1 = data.i2; + data.i2 = tmp; + tmp = data.i5; + data.i5 = data.i6; + data.i6 = tmp; + return data; +} + +// FFT data swap building block associated with complex rotations +float2x8 swap(float2x8 data) { + float2 tmp = data.i1; + data.i1 = data.i4; + float2 tmp2 = data.i2; + data.i2 = tmp; + tmp = data.i3; + data.i3 = data.i5; + data.i4 = tmp2; + data.i5 = data.i6; + data.i6 = tmp; + return data; +} + +// This function "delays" the input by 'depth' steps +// Input 'data' from invocation N would be returned in invocation N + depth +// The 'shift_reg' sliding window is shifted by 1 element at every invocation +float2 delay(float2 data, const int depth, float2 *shift_reg) { + shift_reg[depth] = data; + return shift_reg[0]; +} + +// FFT data reordering building block. Implements the reordering depicted below +// (for depth = 2). The first valid outputs are in invocation 4 +// Invocation count: 0123... 01234567... +// data.i0 : GECA... ----> DBCA... +// data.i1 : HFDB... ----> HFGE... + +float2x8 reorder_data(float2x8 data, const int depth, float2 * shift_reg, bool toggle) { + // Use disconnected segments of length 'depth + 1' elements starting at + // 'shift_reg' to implement the delay elements. At the end of each FFT step, + // the contents of the entire buffer is shifted by 1 element + data.i1 = delay(data.i1, depth, shift_reg); + data.i3 = delay(data.i3, depth, shift_reg + depth + 1); + data.i5 = delay(data.i5, depth, shift_reg + 2 * (depth + 1)); + data.i7 = delay(data.i7, depth, shift_reg + 3 * (depth + 1)); + + if (toggle) { + float2 tmp = data.i0; + data.i0 = data.i1; + data.i1 = tmp; + tmp = data.i2; + data.i2 = data.i3; + data.i3 = tmp; + tmp = data.i4; + data.i4 = data.i5; + data.i5 = tmp; + tmp = data.i6; + data.i6 = data.i7; + data.i7 = tmp; + } + + data.i0 = delay(data.i0, depth, shift_reg + 4 * (depth + 1)); + data.i2 = delay(data.i2, depth, shift_reg + 5 * (depth + 1)); + data.i4 = delay(data.i4, depth, shift_reg + 6 * (depth + 1)); + data.i6 = delay(data.i6, depth, shift_reg + 7 * (depth + 1)); + + return data; +} + +// Implements a complex number multiplication +float2 comp_mult(float2 a, float2 b) { + float2 res; + res.x = a.x * b.x - a.y * b.y; + res.y = a.x * b.y + a.y * b.x; + return res; +} + +// Produces the twiddle factor associated with a processing stream 'stream', +// at a specified 'stage' during a step 'index' of the computation +// +// If there are precomputed twiddle factors for the given FFT size, uses them +// This saves hardware resources, because it avoids evaluating 'cos' and 'sin' +// functions + +float2 twiddle(int index, int stage, int size, int stream) { + float2 twid; + // Coalesces the twiddle tables for indexed access + constant float * twiddles_cos[TWID_STAGES][6] = { + {tc00, tc01, tc02, tc03, tc04, tc05}, + {tc10, tc11, tc12, tc13, tc14, tc15}, + {tc20, tc21, tc22, tc23, tc24, tc25}, + {tc30, tc31, tc32, tc33, tc34, tc35}, + {tc40, tc41, tc42, tc43, tc44, tc45} + }; + constant float * twiddles_sin[TWID_STAGES][6] = { + {ts00, ts01, ts02, ts03, ts04, ts05}, + {ts10, ts11, ts12, ts13, ts14, ts15}, + {ts20, ts21, ts22, ts23, ts24, ts25}, + {ts30, ts31, ts32, ts33, ts34, ts35}, + {ts40, ts41, ts42, ts43, ts44, ts45} + }; + + // Use the precomputed twiddle factors, if available - otherwise, compute them + int twid_stage = stage >> 1; + if (size <= (1 << (TWID_STAGES * 2 + 2))) { + twid.x = twiddles_cos[twid_stage][stream] + [index * ((1 << (TWID_STAGES * 2 + 2)) / size)]; + twid.y = twiddles_sin[twid_stage][stream] + [index * ((1 << (TWID_STAGES * 2 + 2)) / size)]; + } else { + // This would generate hardware consuming a large number of resources + // Instantiated only if precomputed twiddle factors are available + const float TWOPI = 2.0f * M_PI_F; + int multiplier; + + // The latter 3 streams will generate the second half of the elements + // In that case phase = 1 + + int phase = 0; + if (stream >= 3) { + stream -= 3; + phase = 1; + } + switch (stream) { + case 0: multiplier = 2; break; + case 1: multiplier = 1; break; + case 2: multiplier = 3; break; + default: multiplier = 0; + } + int pos = (1 << (stage - 1)) * multiplier * ((index + (size / 8) * phase) + & (size / 4 / (1 << (stage - 1)) - 1)); + float theta = -1.0f * TWOPI / size * (pos & (size - 1)); + twid.x = cos(theta); + twid.y = sin(theta); + } + return twid; +} + +// FFT complex rotation building block +float2x8 complex_rotate(float2x8 data, int index, int stage, int size) { + data.i1 = comp_mult(data.i1, twiddle(index, stage, size, 0)); + data.i2 = comp_mult(data.i2, twiddle(index, stage, size, 1)); + data.i3 = comp_mult(data.i3, twiddle(index, stage, size, 2)); + data.i5 = comp_mult(data.i5, twiddle(index, stage, size, 3)); + data.i6 = comp_mult(data.i6, twiddle(index, stage, size, 4)); + data.i7 = comp_mult(data.i7, twiddle(index, stage, size, 5)); + return data; +} + + +// Process 8 input points towards and a FFT/iFFT of size N, N >= 8 +// (in order input, bit reversed output). Apply all input points in N / 8 +// consecutive invocations. Obtain all outputs in N /8 consecutive invocations +// starting with invocation N /8 - 1 (outputs are delayed). Multiple back-to-back +// transforms can be executed +// +// 'data' encapsulates 8 complex single-precision floating-point input points +// 'step' specifies the index of the current invocation +// 'fft_delay_elements' is an array representing a sliding window of size N+8*(log(N)-2) +// 'inverse' toggles between the direct and inverse transform +// 'logN' should be a COMPILE TIME constant evaluating log(N) - the constant is +// propagated throughout the code to achieve efficient hardware +// +float2x8 fft_step(float2x8 data, int step, float2 *fft_delay_elements, + bool inverse, const int logN) { + const int size = 1 << logN; + // Swap real and imaginary components if doing an inverse transform + if (inverse) { + data = swap_complex(data); + } + + // Stage 0 of feed-forward FFT + data = butterfly(data); + data = trivial_rotate(data); + data = trivial_swap(data); + + // Stage 1 + data = butterfly(data); + data = complex_rotate(data, step & (size / 8 - 1), 1, size); + data = swap(data); + + // Next logN - 2 stages alternate two computation patterns - represented as + // a loop to avoid code duplication. Instruct the compiler to fully unroll + // the loop to increase the amount of pipeline parallelism and allow feed + // forward execution + + #pragma unroll + for (int stage = 2; stage < logN - 1; stage++) { + bool complex_stage = stage & 1; // stages 3, 5, ... + + // Figure out the index of the element processed at this stage + // Subtract (add modulo size / 8) the delay incurred as data travels + // from one stage to the next + int data_index = (step + ( 1 << (logN - 1 - stage))) & (size / 8 - 1); + + data = butterfly(data); + + if (complex_stage) { + data = complex_rotate(data, data_index, stage, size); + } + + data = swap(data); + + // Compute the delay of this stage + int delay = 1 << (logN - 2 - stage); + + // Reordering multiplexers must toggle every 'delay' steps + bool toggle = data_index & delay; + + // Assign unique sections of the buffer for the set of delay elements at + // each stage + float2 *head_buffer = fft_delay_elements + + size - (1 << (logN - stage + 2)) + 8 * (stage - 2); + data = reorder_data(data, delay, head_buffer, toggle); + + if (!complex_stage) { + data = trivial_rotate(data); + } + } + + // Stage logN - 1 + data = butterfly(data); + + // Shift the contents of the sliding window. The hardware is capable of + // shifting the entire contents in parallel if the loop is unrolled. More + // important, when unrolling this loop each transfer maps to a trivial + // loop-carried dependency + #pragma unroll + for (int ii = 0; ii < size + 8 * (logN - 2) - 1; ii++) { + fft_delay_elements[ii] = fft_delay_elements[ii + 1]; + } + + if (inverse) { + data = swap_complex(data); + } + + return data; +} + diff --git a/FFT/src/device/twid_radix4_8.cl b/FFT/src/device/twid_radix4_8.cl new file mode 100644 index 00000000..6765cb1f --- /dev/null +++ b/FFT/src/device/twid_radix4_8.cl @@ -0,0 +1,90 @@ +// Copyright (C) 2013-2019 Altera Corporation, San Jose, California, USA. All rights reserved. +// 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. +// +// This agreement shall be governed in all respects by the laws of the State of California and +// by the laws of the United States of America. + +// Twiddle factors for radix-4 FFTs +// Precomputed for FFT sizes between 8 and 4096 points + +#define TWID_STAGES 5 + +constant float tc00[512] = {1.0f, 0.9999952912f, 0.9999811649f, 0.9999576211f, 0.9999247193f, 0.9998823404f, 0.9998306036f, 0.9997693896f, 0.9996988177f, 0.9996188283f, 0.9995294213f, 0.9994305968f, 0.9993223548f, 0.9992047548f, 0.9990777373f, 0.9989413023f, 0.9987954497f, 0.9986402392f, 0.9984755516f, 0.9983015656f, 0.9981181026f, 0.9979252815f, 0.997723043f, 0.9975114465f, 0.9972904325f, 0.9970600605f, 0.996820271f, 0.9965711236f, 0.9963126183f, 0.9960446954f, 0.9957674146f, 0.9954807758f, 0.9951847196f, 0.9948793054f, 0.9945645928f, 0.9942404628f, 0.9939069748f, 0.9935641289f, 0.993211925f, 0.9928504229f, 0.9924795628f, 0.9920992851f, 0.9917097688f, 0.9913108349f, 0.9909026623f, 0.9904850721f, 0.9900581837f, 0.9896219969f, 0.9891765118f, 0.9887216687f, 0.988257587f, 0.9877841473f, 0.9873014092f, 0.9868093729f, 0.9863080978f, 0.9857975245f, 0.9852776527f, 0.9847484827f, 0.9842100739f, 0.9836624265f, 0.9831054807f, 0.9825392962f, 0.9819638729f, 0.9813792109f, 0.9807852507f, 0.9801821113f, 0.9795697927f, 0.9789481759f, 0.97831738f, 0.9776773453f, 0.9770281315f, 0.9763697386f, 0.975702107f, 0.9750253558f, 0.974339366f, 0.9736442566f, 0.9729399681f, 0.9722265005f, 0.9715039134f, 0.9707721472f, 0.9700312614f, 0.9692812562f, 0.9685220718f, 0.9677538276f, 0.9669764638f, 0.9661899805f, 0.9653944373f, 0.9645897746f, 0.963776052f, 0.9629532695f, 0.9621214271f, 0.9612804651f, 0.9604305029f, 0.9595715404f, 0.9587034583f, 0.9578264356f, 0.9569403529f, 0.95604527f, 0.9551411867f, 0.9542281032f, 0.9533060193f, 0.9523749948f, 0.9514350295f, 0.950486064f, 0.9495281577f, 0.9485613704f, 0.9475855827f, 0.946600914f, 0.9456073046f, 0.9446048141f, 0.9435934424f, 0.9425731897f, 0.9415440559f, 0.940506041f, 0.9394592047f, 0.9384035468f, 0.9373390079f, 0.9362656474f, 0.9351835251f, 0.9340925217f, 0.932992816f, 0.9318842888f, 0.9307669401f, 0.9296408892f, 0.9285060763f, 0.9273625016f, 0.9262102246f, 0.9250492454f, 0.9238795042f, 0.9227011204f, 0.9215140343f, 0.9203183055f, 0.9191138744f, 0.9179008007f, 0.9166790843f, 0.9154487252f, 0.9142097831f, 0.9129621983f, 0.9117060304f, 0.9104412794f, 0.909168005f, 0.9078860879f, 0.9065957069f, 0.9052967429f, 0.903989315f, 0.9026733041f, 0.9013488293f, 0.9000158906f, 0.8986744881f, 0.8973245621f, 0.8959662318f, 0.8945994973f, 0.893224299f, 0.8918406963f, 0.8904487491f, 0.8890483379f, 0.8876396418f, 0.8862225413f, 0.8847970963f, 0.8833633661f, 0.8819212914f, 0.8804708719f, 0.8790122271f, 0.8775452971f, 0.8760700822f, 0.8745866418f, 0.8730949759f, 0.8715950847f, 0.8700869679f, 0.8685706854f, 0.867046237f, 0.8655136228f, 0.8639728427f, 0.8624239564f, 0.8608669639f, 0.8593018055f, 0.8577286005f, 0.8561473489f, 0.854557991f, 0.8529605865f, 0.851355195f, 0.8497417569f, 0.8481203318f, 0.8464909196f, 0.84485358f, 0.8432082534f, 0.8415549994f, 0.8398938179f, 0.838224709f, 0.8365477324f, 0.8348628879f, 0.8331701756f, 0.8314695954f, 0.8297612071f, 0.8280450702f, 0.8263210654f, 0.8245893121f, 0.8228498101f, 0.8211025f, 0.8193475008f, 0.8175848126f, 0.8158144355f, 0.8140363097f, 0.8122506142f, 0.81045717f, 0.8086561561f, 0.8068475723f, 0.8050313592f, 0.8032075167f, 0.801376164f, 0.7995372415f, 0.7976908684f, 0.7958369255f, 0.7939754725f, 0.7921065688f, 0.7902302146f, 0.7883464098f, 0.786455214f, 0.7845565677f, 0.7826505899f, 0.7807372212f, 0.7788165212f, 0.7768884897f, 0.7749531269f, 0.7730104327f, 0.7710605264f, 0.7691033483f, 0.7671388984f, 0.7651672363f, 0.7631884217f, 0.761202395f, 0.7592092156f, 0.7572088242f, 0.7552013993f, 0.7531868219f, 0.7511651516f, 0.7491363883f, 0.7471005917f, 0.7450577617f, 0.7430079579f, 0.7409511209f, 0.73888731f, 0.7368165851f, 0.7347388864f, 0.7326542735f, 0.7305627465f, 0.728464365f, 0.726359129f, 0.724247098f, 0.7221282125f, 0.720002532f, 0.7178700566f, 0.7157308459f, 0.7135848403f, 0.7114322186f, 0.7092728019f, 0.7071067691f, 0.7049340606f, 0.7027547359f, 0.7005687952f, 0.6983762383f, 0.696177125f, 0.6939714551f, 0.6917592287f, 0.689540565f, 0.6873153448f, 0.6850836873f, 0.6828455329f, 0.6806010008f, 0.6783500314f, 0.6760926843f, 0.6738290191f, 0.6715589762f, 0.6692826152f, 0.6669999361f, 0.6647109985f, 0.6624158025f, 0.6601143479f, 0.6578066945f, 0.6554928422f, 0.6531728506f, 0.6508466601f, 0.64851439f, 0.6461760402f, 0.6438315511f, 0.6414810419f, 0.6391244531f, 0.6367618442f, 0.6343932748f, 0.6320187449f, 0.6296382546f, 0.6272518039f, 0.6248595119f, 0.6224612594f, 0.6200572252f, 0.6176472902f, 0.6152315736f, 0.6128100753f, 0.6103827953f, 0.6079497933f, 0.6055110693f, 0.6030666232f, 0.6006164551f, 0.5981606841f, 0.5956993103f, 0.5932322741f, 0.5907596946f, 0.5882815719f, 0.5857978463f, 0.5833086371f, 0.5808139443f, 0.5783137679f, 0.5758081675f, 0.573297143f, 0.5707807541f, 0.5682589412f, 0.5657318234f, 0.5631993413f, 0.5606615543f, 0.5581185222f, 0.5555702448f, 0.5530167222f, 0.5504579544f, 0.5478940606f, 0.5453249812f, 0.5427507758f, 0.5401714444f, 0.5375870466f, 0.534997642f, 0.5324031115f, 0.5298036337f, 0.5271991491f, 0.5245896578f, 0.5219752789f, 0.5193560123f, 0.5167317986f, 0.514102757f, 0.5114688277f, 0.5088301301f, 0.5061866641f, 0.5035383701f, 0.5008853674f, 0.4982276559f, 0.4955652654f, 0.492898196f, 0.4902264774f, 0.4875501692f, 0.4848692417f, 0.4821837842f, 0.479493767f, 0.4767992198f, 0.4741002023f, 0.4713967443f, 0.4686888158f, 0.4659765065f, 0.4632597864f, 0.4605387151f, 0.4578132927f, 0.4550835788f, 0.4523495734f, 0.449611336f, 0.4468688369f, 0.4441221356f, 0.4413712621f, 0.438616246f, 0.4358570874f, 0.433093816f, 0.4303264916f, 0.4275550842f, 0.4247796834f, 0.4220002592f, 0.4192169011f, 0.4164295495f, 0.4136383235f, 0.4108431637f, 0.4080441594f, 0.4052413106f, 0.4024346471f, 0.3996241987f, 0.3968099952f, 0.3939920366f, 0.3911703825f, 0.3883450329f, 0.3855160475f, 0.3826834261f, 0.3798471987f, 0.3770074248f, 0.3741640747f, 0.3713172078f, 0.3684668243f, 0.3656129837f, 0.3627557158f, 0.3598950505f, 0.3570309579f, 0.3541635275f, 0.3512927592f, 0.3484186828f, 0.3455413282f, 0.3426607251f, 0.3397768736f, 0.336889863f, 0.3339996636f, 0.3311063051f, 0.3282098472f, 0.3253102899f, 0.3224076927f, 0.3195020258f, 0.3165933788f, 0.3136817515f, 0.310767144f, 0.3078496456f, 0.3049292266f, 0.3020059466f, 0.2990798354f, 0.296150893f, 0.2932191491f, 0.2902846634f, 0.2873474658f, 0.2844075263f, 0.2814649343f, 0.27851969f, 0.2755718231f, 0.2726213634f, 0.2696683109f, 0.266712755f, 0.2637546659f, 0.2607941031f, 0.2578310966f, 0.2548656464f, 0.2518978119f, 0.2489276081f, 0.24595505f, 0.2429801822f, 0.2400030196f, 0.2370236069f, 0.234041959f, 0.2310581058f, 0.228072077f, 0.2250839174f, 0.2220936269f, 0.2191012353f, 0.2161068022f, 0.2131103128f, 0.2101118416f, 0.2071113735f, 0.2041089684f, 0.201104641f, 0.1980984062f, 0.1950903237f, 0.1920803934f, 0.1890686601f, 0.1860551536f, 0.1830398887f, 0.1800228953f, 0.1770042181f, 0.1739838719f, 0.1709618866f, 0.167938292f, 0.1649131179f, 0.161886394f, 0.1588581502f, 0.1558284014f, 0.1527971923f, 0.1497645378f, 0.1467304677f, 0.1436950266f, 0.1406582445f, 0.1376201212f, 0.1345807016f, 0.1315400302f, 0.1284981072f, 0.1254549772f, 0.1224106774f, 0.1193652153f, 0.1163186282f, 0.1132709533f, 0.1102222055f, 0.1071724221f, 0.1041216329f, 0.1010698602f, 0.09801714122f, 0.09496349841f, 0.09190895408f, 0.08885355294f, 0.08579730988f, 0.08274026215f, 0.07968243957f, 0.07662386447f, 0.07356456667f, 0.07050457597f, 0.06744392216f, 0.06438262761f, 0.061320737f, 0.05825826526f, 0.05519524589f, 0.05213170499f, 0.04906767607f, 0.04600318149f, 0.0429382585f, 0.03987292573f, 0.03680722415f, 0.0337411724f, 0.030674804f, 0.02760814503f, 0.02454122901f, 0.02147408016f, 0.01840673015f, 0.01533920597f, 0.01227153838f, 0.009203754365f, 0.006135884672f, 0.003067956772f}; +constant float tc03[512] = {6.123234263e-17f, -0.003067956772f, -0.006135884672f, -0.009203754365f, -0.01227153838f, -0.01533920597f, -0.01840673015f, -0.02147408016f, -0.02454122901f, -0.02760814503f, -0.030674804f, -0.0337411724f, -0.03680722415f, -0.03987292573f, -0.0429382585f, -0.04600318149f, -0.04906767607f, -0.05213170499f, -0.05519524589f, -0.05825826526f, -0.061320737f, -0.06438262761f, -0.06744392216f, -0.07050457597f, -0.07356456667f, -0.07662386447f, -0.07968243957f, -0.08274026215f, -0.08579730988f, -0.08885355294f, -0.09190895408f, -0.09496349841f, -0.09801714122f, -0.1010698602f, -0.1041216329f, -0.1071724221f, -0.1102222055f, -0.1132709533f, -0.1163186282f, -0.1193652153f, -0.1224106774f, -0.1254549772f, -0.1284981072f, -0.1315400302f, -0.1345807016f, -0.1376201212f, -0.1406582445f, -0.1436950266f, -0.1467304677f, -0.1497645378f, -0.1527971923f, -0.1558284014f, -0.1588581502f, -0.161886394f, -0.1649131179f, -0.167938292f, -0.1709618866f, -0.1739838719f, -0.1770042181f, -0.1800228953f, -0.1830398887f, -0.1860551536f, -0.1890686601f, -0.1920803934f, -0.1950903237f, -0.1980984062f, -0.201104641f, -0.2041089684f, -0.2071113735f, -0.2101118416f, -0.2131103128f, -0.2161068022f, -0.2191012353f, -0.2220936269f, -0.2250839174f, -0.228072077f, -0.2310581058f, -0.234041959f, -0.2370236069f, -0.2400030196f, -0.2429801822f, -0.24595505f, -0.2489276081f, -0.2518978119f, -0.2548656464f, -0.2578310966f, -0.2607941031f, -0.2637546659f, -0.266712755f, -0.2696683109f, -0.2726213634f, -0.2755718231f, -0.27851969f, -0.2814649343f, -0.2844075263f, -0.2873474658f, -0.2902846634f, -0.2932191491f, -0.296150893f, -0.2990798354f, -0.3020059466f, -0.3049292266f, -0.3078496456f, -0.310767144f, -0.3136817515f, -0.3165933788f, -0.3195020258f, -0.3224076927f, -0.3253102899f, -0.3282098472f, -0.3311063051f, -0.3339996636f, -0.336889863f, -0.3397768736f, -0.3426607251f, -0.3455413282f, -0.3484186828f, -0.3512927592f, -0.3541635275f, -0.3570309579f, -0.3598950505f, -0.3627557158f, -0.3656129837f, -0.3684668243f, -0.3713172078f, -0.3741640747f, -0.3770074248f, -0.3798471987f, -0.3826834261f, -0.3855160475f, -0.3883450329f, -0.3911703825f, -0.3939920366f, -0.3968099952f, -0.3996241987f, -0.4024346471f, -0.4052413106f, -0.4080441594f, -0.4108431637f, -0.4136383235f, -0.4164295495f, -0.4192169011f, -0.4220002592f, -0.4247796834f, -0.4275550842f, -0.4303264916f, -0.433093816f, -0.4358570874f, -0.438616246f, -0.4413712621f, -0.4441221356f, -0.4468688369f, -0.449611336f, -0.4523495734f, -0.4550835788f, -0.4578132927f, -0.4605387151f, -0.4632597864f, -0.4659765065f, -0.4686888158f, -0.4713967443f, -0.4741002023f, -0.4767992198f, -0.479493767f, -0.4821837842f, -0.4848692417f, -0.4875501692f, -0.4902264774f, -0.492898196f, -0.4955652654f, -0.4982276559f, -0.5008853674f, -0.5035383701f, -0.5061866641f, -0.5088301301f, -0.5114688277f, -0.514102757f, -0.5167317986f, -0.5193560123f, -0.5219752789f, -0.5245896578f, -0.5271991491f, -0.5298036337f, -0.5324031115f, -0.534997642f, -0.5375870466f, -0.5401714444f, -0.5427507758f, -0.5453249812f, -0.5478940606f, -0.5504579544f, -0.5530167222f, -0.5555702448f, -0.5581185222f, -0.5606615543f, -0.5631993413f, -0.5657318234f, -0.5682589412f, -0.5707807541f, -0.573297143f, -0.5758081675f, -0.5783137679f, -0.5808139443f, -0.5833086371f, -0.5857978463f, -0.5882815719f, -0.5907596946f, -0.5932322741f, -0.5956993103f, -0.5981606841f, -0.6006164551f, -0.6030666232f, -0.6055110693f, -0.6079497933f, -0.6103827953f, -0.6128100753f, -0.6152315736f, -0.6176472902f, -0.6200572252f, -0.6224612594f, -0.6248595119f, -0.6272518039f, -0.6296382546f, -0.6320187449f, -0.6343932748f, -0.6367618442f, -0.6391244531f, -0.6414810419f, -0.6438315511f, -0.6461760402f, -0.64851439f, -0.6508466601f, -0.6531728506f, -0.6554928422f, -0.6578066945f, -0.6601143479f, -0.6624158025f, -0.6647109985f, -0.6669999361f, -0.6692826152f, -0.6715589762f, -0.6738290191f, -0.6760926843f, -0.6783500314f, -0.6806010008f, -0.6828455329f, -0.6850836873f, -0.6873153448f, -0.689540565f, -0.6917592287f, -0.6939714551f, -0.696177125f, -0.6983762383f, -0.7005687952f, -0.7027547359f, -0.7049340606f, -0.7071067691f, -0.7092728019f, -0.7114322186f, -0.7135848403f, -0.7157308459f, -0.7178700566f, -0.720002532f, -0.7221282125f, -0.724247098f, -0.726359129f, -0.728464365f, -0.7305627465f, -0.7326542735f, -0.7347388864f, -0.7368165851f, -0.73888731f, -0.7409511209f, -0.7430079579f, -0.7450577617f, -0.7471005917f, -0.7491363883f, -0.7511651516f, -0.7531868219f, -0.7552013993f, -0.7572088242f, -0.7592092156f, -0.761202395f, -0.7631884217f, -0.7651672363f, -0.7671388984f, -0.7691033483f, -0.7710605264f, -0.7730104327f, -0.7749531269f, -0.7768884897f, -0.7788165212f, -0.7807372212f, -0.7826505899f, -0.7845565677f, -0.786455214f, -0.7883464098f, -0.7902302146f, -0.7921065688f, -0.7939754725f, -0.7958369255f, -0.7976908684f, -0.7995372415f, -0.801376164f, -0.8032075167f, -0.8050313592f, -0.8068475723f, -0.8086561561f, -0.81045717f, -0.8122506142f, -0.8140363097f, -0.8158144355f, -0.8175848126f, -0.8193475008f, -0.8211025f, -0.8228498101f, -0.8245893121f, -0.8263210654f, -0.8280450702f, -0.8297612071f, -0.8314695954f, -0.8331701756f, -0.8348628879f, -0.8365477324f, -0.838224709f, -0.8398938179f, -0.8415549994f, -0.8432082534f, -0.84485358f, -0.8464909196f, -0.8481203318f, -0.8497417569f, -0.851355195f, -0.8529605865f, -0.854557991f, -0.8561473489f, -0.8577286005f, -0.8593018055f, -0.8608669639f, -0.8624239564f, -0.8639728427f, -0.8655136228f, -0.867046237f, -0.8685706854f, -0.8700869679f, -0.8715950847f, -0.8730949759f, -0.8745866418f, -0.8760700822f, -0.8775452971f, -0.8790122271f, -0.8804708719f, -0.8819212914f, -0.8833633661f, -0.8847970963f, -0.8862225413f, -0.8876396418f, -0.8890483379f, -0.8904487491f, -0.8918406963f, -0.893224299f, -0.8945994973f, -0.8959662318f, -0.8973245621f, -0.8986744881f, -0.9000158906f, -0.9013488293f, -0.9026733041f, -0.903989315f, -0.9052967429f, -0.9065957069f, -0.9078860879f, -0.909168005f, -0.9104412794f, -0.9117060304f, -0.9129621983f, -0.9142097831f, -0.9154487252f, -0.9166790843f, -0.9179008007f, -0.9191138744f, -0.9203183055f, -0.9215140343f, -0.9227011204f, -0.9238795042f, -0.9250492454f, -0.9262102246f, -0.9273625016f, -0.9285060763f, -0.9296408892f, -0.9307669401f, -0.9318842888f, -0.932992816f, -0.9340925217f, -0.9351835251f, -0.9362656474f, -0.9373390079f, -0.9384035468f, -0.9394592047f, -0.940506041f, -0.9415440559f, -0.9425731897f, -0.9435934424f, -0.9446048141f, -0.9456073046f, -0.946600914f, -0.9475855827f, -0.9485613704f, -0.9495281577f, -0.950486064f, -0.9514350295f, -0.9523749948f, -0.9533060193f, -0.9542281032f, -0.9551411867f, -0.95604527f, -0.9569403529f, -0.9578264356f, -0.9587034583f, -0.9595715404f, -0.9604305029f, -0.9612804651f, -0.9621214271f, -0.9629532695f, -0.963776052f, -0.9645897746f, -0.9653944373f, -0.9661899805f, -0.9669764638f, -0.9677538276f, -0.9685220718f, -0.9692812562f, -0.9700312614f, -0.9707721472f, -0.9715039134f, -0.9722265005f, -0.9729399681f, -0.9736442566f, -0.974339366f, -0.9750253558f, -0.975702107f, -0.9763697386f, -0.9770281315f, -0.9776773453f, -0.97831738f, -0.9789481759f, -0.9795697927f, -0.9801821113f, -0.9807852507f, -0.9813792109f, -0.9819638729f, -0.9825392962f, -0.9831054807f, -0.9836624265f, -0.9842100739f, -0.9847484827f, -0.9852776527f, -0.9857975245f, -0.9863080978f, -0.9868093729f, -0.9873014092f, -0.9877841473f, -0.988257587f, -0.9887216687f, -0.9891765118f, -0.9896219969f, -0.9900581837f, -0.9904850721f, -0.9909026623f, -0.9913108349f, -0.9917097688f, -0.9920992851f, -0.9924795628f, -0.9928504229f, -0.993211925f, -0.9935641289f, -0.9939069748f, -0.9942404628f, -0.9945645928f, -0.9948793054f, -0.9951847196f, -0.9954807758f, -0.9957674146f, -0.9960446954f, -0.9963126183f, -0.9965711236f, -0.996820271f, -0.9970600605f, -0.9972904325f, -0.9975114465f, -0.997723043f, -0.9979252815f, -0.9981181026f, -0.9983015656f, -0.9984755516f, -0.9986402392f, -0.9987954497f, -0.9989413023f, -0.9990777373f, -0.9992047548f, -0.9993223548f, -0.9994305968f, -0.9995294213f, -0.9996188283f, -0.9996988177f, -0.9997693896f, -0.9998306036f, -0.9998823404f, -0.9999247193f, -0.9999576211f, -0.9999811649f, -0.9999952912f}; +constant float tc01[512] = {1.0f, 0.9999988079f, 0.9999952912f, 0.9999893904f, 0.9999811649f, 0.9999706149f, 0.9999576211f, 0.9999423623f, 0.9999247193f, 0.9999046922f, 0.9998823404f, 0.9998576641f, 0.9998306036f, 0.9998011589f, 0.9997693896f, 0.9997352958f, 0.9996988177f, 0.9996600151f, 0.9996188283f, 0.9995753169f, 0.9995294213f, 0.9994812012f, 0.9994305968f, 0.9993776679f, 0.9993223548f, 0.9992647767f, 0.9992047548f, 0.9991424084f, 0.9990777373f, 0.9990106821f, 0.9989413023f, 0.9988695383f, 0.9987954497f, 0.9987190366f, 0.9986402392f, 0.9985590577f, 0.9984755516f, 0.9983897209f, 0.9983015656f, 0.9982110262f, 0.9981181026f, 0.9980228543f, 0.9979252815f, 0.9978253245f, 0.997723043f, 0.9976184368f, 0.9975114465f, 0.9974021316f, 0.9972904325f, 0.9971764088f, 0.9970600605f, 0.996941328f, 0.996820271f, 0.9966968894f, 0.9965711236f, 0.9964430332f, 0.9963126183f, 0.9961798191f, 0.9960446954f, 0.9959072471f, 0.9957674146f, 0.9956252575f, 0.9954807758f, 0.99533391f, 0.9951847196f, 0.9950332046f, 0.9948793054f, 0.9947231412f, 0.9945645928f, 0.9944036603f, 0.9942404628f, 0.9940748811f, 0.9939069748f, 0.9937367439f, 0.9935641289f, 0.9933891892f, 0.993211925f, 0.9930323362f, 0.9928504229f, 0.9926661253f, 0.9924795628f, 0.992290616f, 0.9920992851f, 0.9919056892f, 0.9917097688f, 0.9915114641f, 0.9913108349f, 0.9911079407f, 0.9909026623f, 0.9906949997f, 0.9904850721f, 0.99027282f, 0.9900581837f, 0.9898412824f, 0.9896219969f, 0.9894004464f, 0.9891765118f, 0.9889502525f, 0.9887216687f, 0.9884908199f, 0.988257587f, 0.9880220294f, 0.9877841473f, 0.9875439405f, 0.9873014092f, 0.9870565534f, 0.9868093729f, 0.9865599275f, 0.9863080978f, 0.9860539436f, 0.9857975245f, 0.9855387211f, 0.9852776527f, 0.9850142598f, 0.9847484827f, 0.9844804406f, 0.9842100739f, 0.9839374423f, 0.9836624265f, 0.9833850861f, 0.9831054807f, 0.9828235507f, 0.9825392962f, 0.982252717f, 0.9819638729f, 0.9816727042f, 0.9813792109f, 0.9810833931f, 0.9807852507f, 0.9804848433f, 0.9801821113f, 0.9798771143f, 0.9795697927f, 0.9792601466f, 0.9789481759f, 0.9786339402f, 0.97831738f, 0.9779984951f, 0.9776773453f, 0.9773538709f, 0.9770281315f, 0.9767000675f, 0.9763697386f, 0.9760370851f, 0.975702107f, 0.9753648639f, 0.9750253558f, 0.9746835232f, 0.974339366f, 0.9739929438f, 0.9736442566f, 0.9732932448f, 0.9729399681f, 0.9725843668f, 0.9722265005f, 0.9718663096f, 0.9715039134f, 0.971139133f, 0.9707721472f, 0.9704028368f, 0.9700312614f, 0.9696573615f, 0.9692812562f, 0.9689028263f, 0.9685220718f, 0.968139112f, 0.9677538276f, 0.9673662782f, 0.9669764638f, 0.9665843844f, 0.9661899805f, 0.9657933712f, 0.9653944373f, 0.9649932384f, 0.9645897746f, 0.9641840458f, 0.963776052f, 0.9633657932f, 0.9629532695f, 0.9625384808f, 0.9621214271f, 0.9617020488f, 0.9612804651f, 0.9608566165f, 0.9604305029f, 0.9600021243f, 0.9595715404f, 0.9591386318f, 0.9587034583f, 0.9582660794f, 0.9578264356f, 0.9573845267f, 0.9569403529f, 0.9564939141f, 0.95604527f, 0.9555943608f, 0.9551411867f, 0.9546857476f, 0.9542281032f, 0.9537681937f, 0.9533060193f, 0.9528416395f, 0.9523749948f, 0.9519061446f, 0.9514350295f, 0.9509616494f, 0.950486064f, 0.9500082731f, 0.9495281577f, 0.9490458965f, 0.9485613704f, 0.9480745792f, 0.9475855827f, 0.9470943809f, 0.946600914f, 0.9461052418f, 0.9456073046f, 0.9451072216f, 0.9446048141f, 0.9441002607f, 0.9435934424f, 0.9430844188f, 0.9425731897f, 0.9420597553f, 0.9415440559f, 0.9410261512f, 0.940506041f, 0.9399837255f, 0.9394592047f, 0.9389324784f, 0.9384035468f, 0.9378723502f, 0.9373390079f, 0.9368034601f, 0.9362656474f, 0.9357256889f, 0.9351835251f, 0.9346391559f, 0.9340925217f, 0.9335438013f, 0.932992816f, 0.9324396253f, 0.9318842888f, 0.9313266873f, 0.9307669401f, 0.9302050471f, 0.9296408892f, 0.9290745854f, 0.9285060763f, 0.9279354215f, 0.9273625016f, 0.9267874956f, 0.9262102246f, 0.9256308079f, 0.9250492454f, 0.9244654775f, 0.9238795042f, 0.9232914448f, 0.9227011204f, 0.9221086502f, 0.9215140343f, 0.920917213f, 0.9203183055f, 0.919717133f, 0.9191138744f, 0.9185084105f, 0.9179008007f, 0.9172909856f, 0.9166790843f, 0.9160649776f, 0.9154487252f, 0.914830327f, 0.9142097831f, 0.9135870337f, 0.9129621983f, 0.9123351574f, 0.9117060304f, 0.9110747576f, 0.9104412794f, 0.9098057151f, 0.909168005f, 0.9085280895f, 0.9078860879f, 0.9072420001f, 0.9065957069f, 0.905947268f, 0.9052967429f, 0.9046440721f, 0.903989315f, 0.9033323526f, 0.9026733041f, 0.9020121694f, 0.9013488293f, 0.900683403f, 0.9000158906f, 0.8993462324f, 0.8986744881f, 0.898000598f, 0.8973245621f, 0.8966464996f, 0.8959662318f, 0.8952839375f, 0.8945994973f, 0.893912971f, 0.893224299f, 0.8925335407f, 0.8918406963f, 0.8911457658f, 0.8904487491f, 0.8897495866f, 0.8890483379f, 0.8883450627f, 0.8876396418f, 0.8869321346f, 0.8862225413f, 0.8855108619f, 0.8847970963f, 0.8840812445f, 0.8833633661f, 0.882643342f, 0.8819212914f, 0.8811970949f, 0.8804708719f, 0.8797426224f, 0.8790122271f, 0.8782798052f, 0.8775452971f, 0.8768087029f, 0.8760700822f, 0.8753293753f, 0.8745866418f, 0.8738418221f, 0.8730949759f, 0.8723460436f, 0.8715950847f, 0.8708420396f, 0.8700869679f, 0.8693298697f, 0.8685706854f, 0.8678094745f, 0.867046237f, 0.866280973f, 0.8655136228f, 0.864744246f, 0.8639728427f, 0.8631994128f, 0.8624239564f, 0.8616464734f, 0.8608669639f, 0.8600853682f, 0.8593018055f, 0.8585162163f, 0.8577286005f, 0.8569389582f, 0.8561473489f, 0.8553536534f, 0.854557991f, 0.8537603021f, 0.8529605865f, 0.8521589041f, 0.851355195f, 0.8505494595f, 0.8497417569f, 0.8489320278f, 0.8481203318f, 0.8473066092f, 0.8464909196f, 0.8456732631f, 0.84485358f, 0.8440318704f, 0.8432082534f, 0.8423826098f, 0.8415549994f, 0.8407253623f, 0.8398938179f, 0.8390602469f, 0.838224709f, 0.8373872042f, 0.8365477324f, 0.8357062936f, 0.8348628879f, 0.8340175152f, 0.8331701756f, 0.832320869f, 0.8314695954f, 0.8306164145f, 0.8297612071f, 0.8289040923f, 0.8280450702f, 0.8271840215f, 0.8263210654f, 0.8254561424f, 0.8245893121f, 0.8237205148f, 0.8228498101f, 0.8219771385f, 0.8211025f, 0.8202259541f, 0.8193475008f, 0.8184671402f, 0.8175848126f, 0.8167005777f, 0.8158144355f, 0.8149263263f, 0.8140363097f, 0.8131443858f, 0.8122506142f, 0.8113548756f, 0.81045717f, 0.8095576167f, 0.8086561561f, 0.8077528477f, 0.8068475723f, 0.8059403896f, 0.8050313592f, 0.8041203618f, 0.8032075167f, 0.8022928238f, 0.801376164f, 0.8004576564f, 0.7995372415f, 0.7986149788f, 0.7976908684f, 0.796764791f, 0.7958369255f, 0.7949071527f, 0.7939754725f, 0.7930419445f, 0.7921065688f, 0.7911693454f, 0.7902302146f, 0.7892892361f, 0.7883464098f, 0.7874017358f, 0.786455214f, 0.7855068445f, 0.7845565677f, 0.7836045027f, 0.7826505899f, 0.7816948295f, 0.7807372212f, 0.7797777653f, 0.7788165212f, 0.7778534293f, 0.7768884897f, 0.7759217024f, 0.7749531269f, 0.7739827037f, 0.7730104327f, 0.7720363736f, 0.7710605264f, 0.7700828314f, 0.7691033483f, 0.7681220174f, 0.7671388984f, 0.7661539912f, 0.7651672363f, 0.7641787529f, 0.7631884217f, 0.7621963024f, 0.761202395f, 0.7602066994f, 0.7592092156f, 0.7582098842f, 0.7572088242f, 0.756205976f, 0.7552013993f, 0.7541949749f, 0.7531868219f, 0.7521768212f, 0.7511651516f, 0.7501516342f, 0.7491363883f, 0.7481193542f, 0.7471005917f, 0.7460801005f, 0.7450577617f, 0.7440337539f, 0.7430079579f, 0.7419804335f, 0.7409511209f, 0.7399200797f, 0.73888731f, 0.7378528118f, 0.7368165851f, 0.7357785702f, 0.7347388864f, 0.7336974144f, 0.7326542735f, 0.7316094041f, 0.7305627465f, 0.72951442f, 0.728464365f, 0.727412641f, 0.726359129f, 0.7253039479f, 0.724247098f, 0.7231884599f, 0.7221282125f, 0.7210661769f, 0.720002532f, 0.718937099f, 0.7178700566f, 0.7168012857f, 0.7157308459f, 0.7146586776f, 0.7135848403f, 0.7125093937f, 0.7114322186f, 0.7103533745f, 0.7092728019f, 0.7081906199f}; +constant float tc04[512] = {0.7071067691f, 0.7060212493f, 0.7049340606f, 0.7038452625f, 0.7027547359f, 0.7016626f, 0.7005687952f, 0.6994733214f, 0.6983762383f, 0.6972774863f, 0.696177125f, 0.6950750947f, 0.6939714551f, 0.6928661466f, 0.6917592287f, 0.6906507015f, 0.689540565f, 0.6884287596f, 0.6873153448f, 0.6862003207f, 0.6850836873f, 0.683965385f, 0.6828455329f, 0.6817240715f, 0.6806010008f, 0.6794763207f, 0.6783500314f, 0.6772221923f, 0.6760926843f, 0.6749616265f, 0.6738290191f, 0.6726947427f, 0.6715589762f, 0.6704215407f, 0.6692826152f, 0.6681420207f, 0.6669999361f, 0.6658562422f, 0.6647109985f, 0.6635641456f, 0.6624158025f, 0.6612658501f, 0.6601143479f, 0.6589612961f, 0.6578066945f, 0.6566505432f, 0.6554928422f, 0.6543335915f, 0.6531728506f, 0.65201056f, 0.6508466601f, 0.6496813297f, 0.64851439f, 0.6473459601f, 0.6461760402f, 0.6450045109f, 0.6438315511f, 0.6426570415f, 0.6414810419f, 0.6403034925f, 0.6391244531f, 0.6379439235f, 0.6367618442f, 0.6355783343f, 0.6343932748f, 0.6332067847f, 0.6320187449f, 0.630829215f, 0.6296382546f, 0.6284457445f, 0.6272518039f, 0.6260563731f, 0.6248595119f, 0.6236611009f, 0.6224612594f, 0.6212599874f, 0.6200572252f, 0.618852973f, 0.6176472902f, 0.616440177f, 0.6152315736f, 0.6140215397f, 0.6128100753f, 0.6115971804f, 0.6103827953f, 0.6091670394f, 0.6079497933f, 0.6067311168f, 0.6055110693f, 0.6042895317f, 0.6030666232f, 0.6018422246f, 0.6006164551f, 0.5993893147f, 0.5981606841f, 0.5969306827f, 0.5956993103f, 0.5944665074f, 0.5932322741f, 0.5919966698f, 0.5907596946f, 0.5895212889f, 0.5882815719f, 0.5870403647f, 0.5857978463f, 0.584553957f, 0.5833086371f, 0.582062006f, 0.5808139443f, 0.5795645714f, 0.5783137679f, 0.5770616531f, 0.5758081675f, 0.5745533705f, 0.573297143f, 0.5720396042f, 0.5707807541f, 0.5695205331f, 0.5682589412f, 0.566996038f, 0.5657318234f, 0.564466238f, 0.5631993413f, 0.5619311333f, 0.5606615543f, 0.5593907237f, 0.5581185222f, 0.5568450093f, 0.5555702448f, 0.5542941093f, 0.5530167222f, 0.5517379642f, 0.5504579544f, 0.5491766334f, 0.5478940606f, 0.5466101766f, 0.5453249812f, 0.5440385342f, 0.5427507758f, 0.5414617658f, 0.5401714444f, 0.538879931f, 0.5375870466f, 0.5362929702f, 0.534997642f, 0.5337010026f, 0.5324031115f, 0.5311040282f, 0.5298036337f, 0.5285019875f, 0.5271991491f, 0.5258949995f, 0.5245896578f, 0.523283124f, 0.5219752789f, 0.5206662416f, 0.5193560123f, 0.5180445313f, 0.5167317986f, 0.5154178739f, 0.514102757f, 0.5127863884f, 0.5114688277f, 0.510150075f, 0.5088301301f, 0.5075089931f, 0.5061866641f, 0.5048630834f, 0.5035383701f, 0.5022124648f, 0.5008853674f, 0.4995571077f, 0.4982276559f, 0.4968970418f, 0.4955652654f, 0.4942322969f, 0.492898196f, 0.4915629029f, 0.4902264774f, 0.4888888896f, 0.4875501692f, 0.4862102866f, 0.4848692417f, 0.4835270643f, 0.4821837842f, 0.4808393419f, 0.479493767f, 0.4781470597f, 0.4767992198f, 0.4754502773f, 0.4741002023f, 0.4727490246f, 0.4713967443f, 0.4700433314f, 0.4686888158f, 0.4673331976f, 0.4659765065f, 0.4646186829f, 0.4632597864f, 0.4618997872f, 0.4605387151f, 0.4591765404f, 0.4578132927f, 0.4564489722f, 0.4550835788f, 0.4537171125f, 0.4523495734f, 0.4509809911f, 0.449611336f, 0.448240608f, 0.4468688369f, 0.4454960227f, 0.4441221356f, 0.4427472353f, 0.4413712621f, 0.4399942756f, 0.438616246f, 0.4372371733f, 0.4358570874f, 0.4344759583f, 0.433093816f, 0.4317106605f, 0.4303264916f, 0.4289412796f, 0.4275550842f, 0.4261678755f, 0.4247796834f, 0.4233904779f, 0.4220002592f, 0.4206090868f, 0.4192169011f, 0.4178237021f, 0.4164295495f, 0.4150344133f, 0.4136383235f, 0.4122412205f, 0.4108431637f, 0.4094441533f, 0.4080441594f, 0.4066432118f, 0.4052413106f, 0.4038384557f, 0.4024346471f, 0.4010298848f, 0.3996241987f, 0.3982175589f, 0.3968099952f, 0.3954014778f, 0.3939920366f, 0.3925816715f, 0.3911703825f, 0.3897581697f, 0.3883450329f, 0.3869310021f, 0.3855160475f, 0.3841001987f, 0.3826834261f, 0.3812657595f, 0.3798471987f, 0.3784277439f, 0.3770074248f, 0.3755861819f, 0.3741640747f, 0.3727410734f, 0.3713172078f, 0.3698924482f, 0.3684668243f, 0.3670403361f, 0.3656129837f, 0.3641847968f, 0.3627557158f, 0.3613258004f, 0.3598950505f, 0.3584634066f, 0.3570309579f, 0.3555976748f, 0.3541635275f, 0.3527285457f, 0.3512927592f, 0.3498561382f, 0.3484186828f, 0.3469804227f, 0.3455413282f, 0.344101429f, 0.3426607251f, 0.3412192166f, 0.3397768736f, 0.3383337557f, 0.336889863f, 0.3354451358f, 0.3339996636f, 0.3325533569f, 0.3311063051f, 0.3296584487f, 0.3282098472f, 0.3267604411f, 0.3253102899f, 0.3238593638f, 0.3224076927f, 0.3209552467f, 0.3195020258f, 0.3180480897f, 0.3165933788f, 0.3151379228f, 0.3136817515f, 0.3122248054f, 0.310767144f, 0.3093087673f, 0.3078496456f, 0.3063898087f, 0.3049292266f, 0.3034679592f, 0.3020059466f, 0.3005432487f, 0.2990798354f, 0.2976157069f, 0.296150893f, 0.2946853638f, 0.2932191491f, 0.291752249f, 0.2902846634f, 0.2888164222f, 0.2873474658f, 0.2858778238f, 0.2844075263f, 0.282936573f, 0.2814649343f, 0.27999264f, 0.27851969f, 0.2770460844f, 0.2755718231f, 0.2740969062f, 0.2726213634f, 0.271145165f, 0.2696683109f, 0.2681908607f, 0.266712755f, 0.2652340233f, 0.2637546659f, 0.2622747123f, 0.2607941031f, 0.2593129277f, 0.2578310966f, 0.2563486695f, 0.2548656464f, 0.2533820271f, 0.2518978119f, 0.2504130006f, 0.2489276081f, 0.2474416196f, 0.24595505f, 0.2444678992f, 0.2429801822f, 0.241491884f, 0.2400030196f, 0.2385135889f, 0.2370236069f, 0.2355330586f, 0.234041959f, 0.2325503081f, 0.2310581058f, 0.2295653671f, 0.228072077f, 0.2265782654f, 0.2250839174f, 0.2235890329f, 0.2220936269f, 0.2205976844f, 0.2191012353f, 0.2176042795f, 0.2161068022f, 0.2146088183f, 0.2131103128f, 0.2116113305f, 0.2101118416f, 0.208611846f, 0.2071113735f, 0.2056104094f, 0.2041089684f, 0.2026070356f, 0.201104641f, 0.1996017545f, 0.1980984062f, 0.1965945959f, 0.1950903237f, 0.1935855895f, 0.1920803934f, 0.1905747503f, 0.1890686601f, 0.1875621229f, 0.1860551536f, 0.1845477372f, 0.1830398887f, 0.1815316081f, 0.1800228953f, 0.1785137653f, 0.1770042181f, 0.1754942536f, 0.1739838719f, 0.1724730879f, 0.1709618866f, 0.169450298f, 0.167938292f, 0.1664258987f, 0.1649131179f, 0.1633999497f, 0.161886394f, 0.1603724509f, 0.1588581502f, 0.1573434621f, 0.1558284014f, 0.1543129683f, 0.1527971923f, 0.1512810439f, 0.1497645378f, 0.1482476741f, 0.1467304677f, 0.1452129185f, 0.1436950266f, 0.1421768069f, 0.1406582445f, 0.1391393393f, 0.1376201212f, 0.1361005753f, 0.1345807016f, 0.1330605298f, 0.1315400302f, 0.1300192177f, 0.1284981072f, 0.1269766986f, 0.1254549772f, 0.1239329726f, 0.1224106774f, 0.1208880842f, 0.1193652153f, 0.1178420633f, 0.1163186282f, 0.1147949249f, 0.1132709533f, 0.1117467135f, 0.1102222055f, 0.1086974442f, 0.1071724221f, 0.1056471542f, 0.1041216329f, 0.1025958657f, 0.1010698602f, 0.09954361618f, 0.09801714122f, 0.09649042785f, 0.09496349841f, 0.09343633801f, 0.09190895408f, 0.09038136154f, 0.08885355294f, 0.08732553571f, 0.08579730988f, 0.08426889032f, 0.08274026215f, 0.08121144772f, 0.07968243957f, 0.07815324515f, 0.07662386447f, 0.07509429753f, 0.07356456667f, 0.07203464955f, 0.07050457597f, 0.06897433102f, 0.06744392216f, 0.06591334939f, 0.06438262761f, 0.06285175681f, 0.061320737f, 0.05978957191f, 0.05825826526f, 0.05672682077f, 0.05519524589f, 0.05366353691f, 0.05213170499f, 0.05059975013f, 0.04906767607f, 0.04753548279f, 0.04600318149f, 0.04447077215f, 0.0429382585f, 0.04140564054f, 0.03987292573f, 0.03834012151f, 0.03680722415f, 0.03527423739f, 0.0337411724f, 0.03220802546f, 0.030674804f, 0.02914150804f, 0.02760814503f, 0.02607471868f, 0.02454122901f, 0.02300768159f, 0.02147408016f, 0.01994042844f, 0.01840673015f, 0.01687298715f, 0.01533920597f, 0.01380538847f, 0.01227153838f, 0.01073765941f, 0.009203754365f, 0.007669828832f, 0.006135884672f, 0.004601926077f, 0.003067956772f, 0.001533980132f}; +constant float tc02[512] = {1.0f, 0.9999893904f, 0.9999576211f, 0.9999046922f, 0.9998306036f, 0.9997352958f, 0.9996188283f, 0.9994812012f, 0.9993223548f, 0.9991424084f, 0.9989413023f, 0.9987190366f, 0.9984755516f, 0.9982110262f, 0.9979252815f, 0.9976184368f, 0.9972904325f, 0.996941328f, 0.9965711236f, 0.9961798191f, 0.9957674146f, 0.99533391f, 0.9948793054f, 0.9944036603f, 0.9939069748f, 0.9933891892f, 0.9928504229f, 0.992290616f, 0.9917097688f, 0.9911079407f, 0.9904850721f, 0.9898412824f, 0.9891765118f, 0.9884908199f, 0.9877841473f, 0.9870565534f, 0.9863080978f, 0.9855387211f, 0.9847484827f, 0.9839374423f, 0.9831054807f, 0.982252717f, 0.9813792109f, 0.9804848433f, 0.9795697927f, 0.9786339402f, 0.9776773453f, 0.9767000675f, 0.975702107f, 0.9746835232f, 0.9736442566f, 0.9725843668f, 0.9715039134f, 0.9704028368f, 0.9692812562f, 0.968139112f, 0.9669764638f, 0.9657933712f, 0.9645897746f, 0.9633657932f, 0.9621214271f, 0.9608566165f, 0.9595715404f, 0.9582660794f, 0.9569403529f, 0.9555943608f, 0.9542281032f, 0.9528416395f, 0.9514350295f, 0.9500082731f, 0.9485613704f, 0.9470943809f, 0.9456073046f, 0.9441002607f, 0.9425731897f, 0.9410261512f, 0.9394592047f, 0.9378723502f, 0.9362656474f, 0.9346391559f, 0.932992816f, 0.9313266873f, 0.9296408892f, 0.9279354215f, 0.9262102246f, 0.9244654775f, 0.9227011204f, 0.920917213f, 0.9191138744f, 0.9172909856f, 0.9154487252f, 0.9135870337f, 0.9117060304f, 0.9098057151f, 0.9078860879f, 0.905947268f, 0.903989315f, 0.9020121694f, 0.9000158906f, 0.898000598f, 0.8959662318f, 0.893912971f, 0.8918406963f, 0.8897495866f, 0.8876396418f, 0.8855108619f, 0.8833633661f, 0.8811970949f, 0.8790122271f, 0.8768087029f, 0.8745866418f, 0.8723460436f, 0.8700869679f, 0.8678094745f, 0.8655136228f, 0.8631994128f, 0.8608669639f, 0.8585162163f, 0.8561473489f, 0.8537603021f, 0.851355195f, 0.8489320278f, 0.8464909196f, 0.8440318704f, 0.8415549994f, 0.8390602469f, 0.8365477324f, 0.8340175152f, 0.8314695954f, 0.8289040923f, 0.8263210654f, 0.8237205148f, 0.8211025f, 0.8184671402f, 0.8158144355f, 0.8131443858f, 0.81045717f, 0.8077528477f, 0.8050313592f, 0.8022928238f, 0.7995372415f, 0.796764791f, 0.7939754725f, 0.7911693454f, 0.7883464098f, 0.7855068445f, 0.7826505899f, 0.7797777653f, 0.7768884897f, 0.7739827037f, 0.7710605264f, 0.7681220174f, 0.7651672363f, 0.7621963024f, 0.7592092156f, 0.756205976f, 0.7531868219f, 0.7501516342f, 0.7471005917f, 0.7440337539f, 0.7409511209f, 0.7378528118f, 0.7347388864f, 0.7316094041f, 0.728464365f, 0.7253039479f, 0.7221282125f, 0.718937099f, 0.7157308459f, 0.7125093937f, 0.7092728019f, 0.7060212493f, 0.7027547359f, 0.6994733214f, 0.696177125f, 0.6928661466f, 0.689540565f, 0.6862003207f, 0.6828455329f, 0.6794763207f, 0.6760926843f, 0.6726947427f, 0.6692826152f, 0.6658562422f, 0.6624158025f, 0.6589612961f, 0.6554928422f, 0.65201056f, 0.64851439f, 0.6450045109f, 0.6414810419f, 0.6379439235f, 0.6343932748f, 0.630829215f, 0.6272518039f, 0.6236611009f, 0.6200572252f, 0.616440177f, 0.6128100753f, 0.6091670394f, 0.6055110693f, 0.6018422246f, 0.5981606841f, 0.5944665074f, 0.5907596946f, 0.5870403647f, 0.5833086371f, 0.5795645714f, 0.5758081675f, 0.5720396042f, 0.5682589412f, 0.564466238f, 0.5606615543f, 0.5568450093f, 0.5530167222f, 0.5491766334f, 0.5453249812f, 0.5414617658f, 0.5375870466f, 0.5337010026f, 0.5298036337f, 0.5258949995f, 0.5219752789f, 0.5180445313f, 0.514102757f, 0.510150075f, 0.5061866641f, 0.5022124648f, 0.4982276559f, 0.4942322969f, 0.4902264774f, 0.4862102866f, 0.4821837842f, 0.4781470597f, 0.4741002023f, 0.4700433314f, 0.4659765065f, 0.4618997872f, 0.4578132927f, 0.4537171125f, 0.449611336f, 0.4454960227f, 0.4413712621f, 0.4372371733f, 0.433093816f, 0.4289412796f, 0.4247796834f, 0.4206090868f, 0.4164295495f, 0.4122412205f, 0.4080441594f, 0.4038384557f, 0.3996241987f, 0.3954014778f, 0.3911703825f, 0.3869310021f, 0.3826834261f, 0.3784277439f, 0.3741640747f, 0.3698924482f, 0.3656129837f, 0.3613258004f, 0.3570309579f, 0.3527285457f, 0.3484186828f, 0.344101429f, 0.3397768736f, 0.3354451358f, 0.3311063051f, 0.3267604411f, 0.3224076927f, 0.3180480897f, 0.3136817515f, 0.3093087673f, 0.3049292266f, 0.3005432487f, 0.296150893f, 0.291752249f, 0.2873474658f, 0.282936573f, 0.27851969f, 0.2740969062f, 0.2696683109f, 0.2652340233f, 0.2607941031f, 0.2563486695f, 0.2518978119f, 0.2474416196f, 0.2429801822f, 0.2385135889f, 0.234041959f, 0.2295653671f, 0.2250839174f, 0.2205976844f, 0.2161068022f, 0.2116113305f, 0.2071113735f, 0.2026070356f, 0.1980984062f, 0.1935855895f, 0.1890686601f, 0.1845477372f, 0.1800228953f, 0.1754942536f, 0.1709618866f, 0.1664258987f, 0.161886394f, 0.1573434621f, 0.1527971923f, 0.1482476741f, 0.1436950266f, 0.1391393393f, 0.1345807016f, 0.1300192177f, 0.1254549772f, 0.1208880842f, 0.1163186282f, 0.1117467135f, 0.1071724221f, 0.1025958657f, 0.09801714122f, 0.09343633801f, 0.08885355294f, 0.08426889032f, 0.07968243957f, 0.07509429753f, 0.07050457597f, 0.06591334939f, 0.061320737f, 0.05672682077f, 0.05213170499f, 0.04753548279f, 0.0429382585f, 0.03834012151f, 0.0337411724f, 0.02914150804f, 0.02454122901f, 0.01994042844f, 0.01533920597f, 0.01073765941f, 0.006135884672f, 0.001533980132f, -0.003067956772f, -0.007669828832f, -0.01227153838f, -0.01687298715f, -0.02147408016f, -0.02607471868f, -0.030674804f, -0.03527423739f, -0.03987292573f, -0.04447077215f, -0.04906767607f, -0.05366353691f, -0.05825826526f, -0.06285175681f, -0.06744392216f, -0.07203464955f, -0.07662386447f, -0.08121144772f, -0.08579730988f, -0.09038136154f, -0.09496349841f, -0.09954361618f, -0.1041216329f, -0.1086974442f, -0.1132709533f, -0.1178420633f, -0.1224106774f, -0.1269766986f, -0.1315400302f, -0.1361005753f, -0.1406582445f, -0.1452129185f, -0.1497645378f, -0.1543129683f, -0.1588581502f, -0.1633999497f, -0.167938292f, -0.1724730879f, -0.1770042181f, -0.1815316081f, -0.1860551536f, -0.1905747503f, -0.1950903237f, -0.1996017545f, -0.2041089684f, -0.208611846f, -0.2131103128f, -0.2176042795f, -0.2220936269f, -0.2265782654f, -0.2310581058f, -0.2355330586f, -0.2400030196f, -0.2444678992f, -0.2489276081f, -0.2533820271f, -0.2578310966f, -0.2622747123f, -0.266712755f, -0.271145165f, -0.2755718231f, -0.27999264f, -0.2844075263f, -0.2888164222f, -0.2932191491f, -0.2976157069f, -0.3020059466f, -0.3063898087f, -0.310767144f, -0.3151379228f, -0.3195020258f, -0.3238593638f, -0.3282098472f, -0.3325533569f, -0.336889863f, -0.3412192166f, -0.3455413282f, -0.3498561382f, -0.3541635275f, -0.3584634066f, -0.3627557158f, -0.3670403361f, -0.3713172078f, -0.3755861819f, -0.3798471987f, -0.3841001987f, -0.3883450329f, -0.3925816715f, -0.3968099952f, -0.4010298848f, -0.4052413106f, -0.4094441533f, -0.4136383235f, -0.4178237021f, -0.4220002592f, -0.4261678755f, -0.4303264916f, -0.4344759583f, -0.438616246f, -0.4427472353f, -0.4468688369f, -0.4509809911f, -0.4550835788f, -0.4591765404f, -0.4632597864f, -0.4673331976f, -0.4713967443f, -0.4754502773f, -0.479493767f, -0.4835270643f, -0.4875501692f, -0.4915629029f, -0.4955652654f, -0.4995571077f, -0.5035383701f, -0.5075089931f, -0.5114688277f, -0.5154178739f, -0.5193560123f, -0.523283124f, -0.5271991491f, -0.5311040282f, -0.534997642f, -0.538879931f, -0.5427507758f, -0.5466101766f, -0.5504579544f, -0.5542941093f, -0.5581185222f, -0.5619311333f, -0.5657318234f, -0.5695205331f, -0.573297143f, -0.5770616531f, -0.5808139443f, -0.584553957f, -0.5882815719f, -0.5919966698f, -0.5956993103f, -0.5993893147f, -0.6030666232f, -0.6067311168f, -0.6103827953f, -0.6140215397f, -0.6176472902f, -0.6212599874f, -0.6248595119f, -0.6284457445f, -0.6320187449f, -0.6355783343f, -0.6391244531f, -0.6426570415f, -0.6461760402f, -0.6496813297f, -0.6531728506f, -0.6566505432f, -0.6601143479f, -0.6635641456f, -0.6669999361f, -0.6704215407f, -0.6738290191f, -0.6772221923f, -0.6806010008f, -0.683965385f, -0.6873153448f, -0.6906507015f, -0.6939714551f, -0.6972774863f, -0.7005687952f, -0.7038452625f}; +constant float tc05[512] = {-0.7071067691f, -0.7103533745f, -0.7135848403f, -0.7168012857f, -0.720002532f, -0.7231884599f, -0.726359129f, -0.72951442f, -0.7326542735f, -0.7357785702f, -0.73888731f, -0.7419804335f, -0.7450577617f, -0.7481193542f, -0.7511651516f, -0.7541949749f, -0.7572088242f, -0.7602066994f, -0.7631884217f, -0.7661539912f, -0.7691033483f, -0.7720363736f, -0.7749531269f, -0.7778534293f, -0.7807372212f, -0.7836045027f, -0.786455214f, -0.7892892361f, -0.7921065688f, -0.7949071527f, -0.7976908684f, -0.8004576564f, -0.8032075167f, -0.8059403896f, -0.8086561561f, -0.8113548756f, -0.8140363097f, -0.8167005777f, -0.8193475008f, -0.8219771385f, -0.8245893121f, -0.8271840215f, -0.8297612071f, -0.832320869f, -0.8348628879f, -0.8373872042f, -0.8398938179f, -0.8423826098f, -0.84485358f, -0.8473066092f, -0.8497417569f, -0.8521589041f, -0.854557991f, -0.8569389582f, -0.8593018055f, -0.8616464734f, -0.8639728427f, -0.866280973f, -0.8685706854f, -0.8708420396f, -0.8730949759f, -0.8753293753f, -0.8775452971f, -0.8797426224f, -0.8819212914f, -0.8840812445f, -0.8862225413f, -0.8883450627f, -0.8904487491f, -0.8925335407f, -0.8945994973f, -0.8966464996f, -0.8986744881f, -0.900683403f, -0.9026733041f, -0.9046440721f, -0.9065957069f, -0.9085280895f, -0.9104412794f, -0.9123351574f, -0.9142097831f, -0.9160649776f, -0.9179008007f, -0.919717133f, -0.9215140343f, -0.9232914448f, -0.9250492454f, -0.9267874956f, -0.9285060763f, -0.9302050471f, -0.9318842888f, -0.9335438013f, -0.9351835251f, -0.9368034601f, -0.9384035468f, -0.9399837255f, -0.9415440559f, -0.9430844188f, -0.9446048141f, -0.9461052418f, -0.9475855827f, -0.9490458965f, -0.950486064f, -0.9519061446f, -0.9533060193f, -0.9546857476f, -0.95604527f, -0.9573845267f, -0.9587034583f, -0.9600021243f, -0.9612804651f, -0.9625384808f, -0.963776052f, -0.9649932384f, -0.9661899805f, -0.9673662782f, -0.9685220718f, -0.9696573615f, -0.9707721472f, -0.9718663096f, -0.9729399681f, -0.9739929438f, -0.9750253558f, -0.9760370851f, -0.9770281315f, -0.9779984951f, -0.9789481759f, -0.9798771143f, -0.9807852507f, -0.9816727042f, -0.9825392962f, -0.9833850861f, -0.9842100739f, -0.9850142598f, -0.9857975245f, -0.9865599275f, -0.9873014092f, -0.9880220294f, -0.9887216687f, -0.9894004464f, -0.9900581837f, -0.9906949997f, -0.9913108349f, -0.9919056892f, -0.9924795628f, -0.9930323362f, -0.9935641289f, -0.9940748811f, -0.9945645928f, -0.9950332046f, -0.9954807758f, -0.9959072471f, -0.9963126183f, -0.9966968894f, -0.9970600605f, -0.9974021316f, -0.997723043f, -0.9980228543f, -0.9983015656f, -0.9985590577f, -0.9987954497f, -0.9990106821f, -0.9992047548f, -0.9993776679f, -0.9995294213f, -0.9996600151f, -0.9997693896f, -0.9998576641f, -0.9999247193f, -0.9999706149f, -0.9999952912f, -0.9999988079f, -0.9999811649f, -0.9999423623f, -0.9998823404f, -0.9998011589f, -0.9996988177f, -0.9995753169f, -0.9994305968f, -0.9992647767f, -0.9990777373f, -0.9988695383f, -0.9986402392f, -0.9983897209f, -0.9981181026f, -0.9978253245f, -0.9975114465f, -0.9971764088f, -0.996820271f, -0.9964430332f, -0.9960446954f, -0.9956252575f, -0.9951847196f, -0.9947231412f, -0.9942404628f, -0.9937367439f, -0.993211925f, -0.9926661253f, -0.9920992851f, -0.9915114641f, -0.9909026623f, -0.99027282f, -0.9896219969f, -0.9889502525f, -0.988257587f, -0.9875439405f, -0.9868093729f, -0.9860539436f, -0.9852776527f, -0.9844804406f, -0.9836624265f, -0.9828235507f, -0.9819638729f, -0.9810833931f, -0.9801821113f, -0.9792601466f, -0.97831738f, -0.9773538709f, -0.9763697386f, -0.9753648639f, -0.974339366f, -0.9732932448f, -0.9722265005f, -0.971139133f, -0.9700312614f, -0.9689028263f, -0.9677538276f, -0.9665843844f, -0.9653944373f, -0.9641840458f, -0.9629532695f, -0.9617020488f, -0.9604305029f, -0.9591386318f, -0.9578264356f, -0.9564939141f, -0.9551411867f, -0.9537681937f, -0.9523749948f, -0.9509616494f, -0.9495281577f, -0.9480745792f, -0.946600914f, -0.9451072216f, -0.9435934424f, -0.9420597553f, -0.940506041f, -0.9389324784f, -0.9373390079f, -0.9357256889f, -0.9340925217f, -0.9324396253f, -0.9307669401f, -0.9290745854f, -0.9273625016f, -0.9256308079f, -0.9238795042f, -0.9221086502f, -0.9203183055f, -0.9185084105f, -0.9166790843f, -0.914830327f, -0.9129621983f, -0.9110747576f, -0.909168005f, -0.9072420001f, -0.9052967429f, -0.9033323526f, -0.9013488293f, -0.8993462324f, -0.8973245621f, -0.8952839375f, -0.893224299f, -0.8911457658f, -0.8890483379f, -0.8869321346f, -0.8847970963f, -0.882643342f, -0.8804708719f, -0.8782798052f, -0.8760700822f, -0.8738418221f, -0.8715950847f, -0.8693298697f, -0.867046237f, -0.864744246f, -0.8624239564f, -0.8600853682f, -0.8577286005f, -0.8553536534f, -0.8529605865f, -0.8505494595f, -0.8481203318f, -0.8456732631f, -0.8432082534f, -0.8407253623f, -0.838224709f, -0.8357062936f, -0.8331701756f, -0.8306164145f, -0.8280450702f, -0.8254561424f, -0.8228498101f, -0.8202259541f, -0.8175848126f, -0.8149263263f, -0.8122506142f, -0.8095576167f, -0.8068475723f, -0.8041203618f, -0.801376164f, -0.7986149788f, -0.7958369255f, -0.7930419445f, -0.7902302146f, -0.7874017358f, -0.7845565677f, -0.7816948295f, -0.7788165212f, -0.7759217024f, -0.7730104327f, -0.7700828314f, -0.7671388984f, -0.7641787529f, -0.761202395f, -0.7582098842f, -0.7552013993f, -0.7521768212f, -0.7491363883f, -0.7460801005f, -0.7430079579f, -0.7399200797f, -0.7368165851f, -0.7336974144f, -0.7305627465f, -0.727412641f, -0.724247098f, -0.7210661769f, -0.7178700566f, -0.7146586776f, -0.7114322186f, -0.7081906199f, -0.7049340606f, -0.7016626f, -0.6983762383f, -0.6950750947f, -0.6917592287f, -0.6884287596f, -0.6850836873f, -0.6817240715f, -0.6783500314f, -0.6749616265f, -0.6715589762f, -0.6681420207f, -0.6647109985f, -0.6612658501f, -0.6578066945f, -0.6543335915f, -0.6508466601f, -0.6473459601f, -0.6438315511f, -0.6403034925f, -0.6367618442f, -0.6332067847f, -0.6296382546f, -0.6260563731f, -0.6224612594f, -0.618852973f, -0.6152315736f, -0.6115971804f, -0.6079497933f, -0.6042895317f, -0.6006164551f, -0.5969306827f, -0.5932322741f, -0.5895212889f, -0.5857978463f, -0.582062006f, -0.5783137679f, -0.5745533705f, -0.5707807541f, -0.566996038f, -0.5631993413f, -0.5593907237f, -0.5555702448f, -0.5517379642f, -0.5478940606f, -0.5440385342f, -0.5401714444f, -0.5362929702f, -0.5324031115f, -0.5285019875f, -0.5245896578f, -0.5206662416f, -0.5167317986f, -0.5127863884f, -0.5088301301f, -0.5048630834f, -0.5008853674f, -0.4968970418f, -0.492898196f, -0.4888888896f, -0.4848692417f, -0.4808393419f, -0.4767992198f, -0.4727490246f, -0.4686888158f, -0.4646186829f, -0.4605387151f, -0.4564489722f, -0.4523495734f, -0.448240608f, -0.4441221356f, -0.4399942756f, -0.4358570874f, -0.4317106605f, -0.4275550842f, -0.4233904779f, -0.4192169011f, -0.4150344133f, -0.4108431637f, -0.4066432118f, -0.4024346471f, -0.3982175589f, -0.3939920366f, -0.3897581697f, -0.3855160475f, -0.3812657595f, -0.3770074248f, -0.3727410734f, -0.3684668243f, -0.3641847968f, -0.3598950505f, -0.3555976748f, -0.3512927592f, -0.3469804227f, -0.3426607251f, -0.3383337557f, -0.3339996636f, -0.3296584487f, -0.3253102899f, -0.3209552467f, -0.3165933788f, -0.3122248054f, -0.3078496456f, -0.3034679592f, -0.2990798354f, -0.2946853638f, -0.2902846634f, -0.2858778238f, -0.2814649343f, -0.2770460844f, -0.2726213634f, -0.2681908607f, -0.2637546659f, -0.2593129277f, -0.2548656464f, -0.2504130006f, -0.24595505f, -0.241491884f, -0.2370236069f, -0.2325503081f, -0.228072077f, -0.2235890329f, -0.2191012353f, -0.2146088183f, -0.2101118416f, -0.2056104094f, -0.201104641f, -0.1965945959f, -0.1920803934f, -0.1875621229f, -0.1830398887f, -0.1785137653f, -0.1739838719f, -0.169450298f, -0.1649131179f, -0.1603724509f, -0.1558284014f, -0.1512810439f, -0.1467304677f, -0.1421768069f, -0.1376201212f, -0.1330605298f, -0.1284981072f, -0.1239329726f, -0.1193652153f, -0.1147949249f, -0.1102222055f, -0.1056471542f, -0.1010698602f, -0.09649042785f, -0.09190895408f, -0.08732553571f, -0.08274026215f, -0.07815324515f, -0.07356456667f, -0.06897433102f, -0.06438262761f, -0.05978957191f, -0.05519524589f, -0.05059975013f, -0.04600318149f, -0.04140564054f, -0.03680722415f, -0.03220802546f, -0.02760814503f, -0.02300768159f, -0.01840673015f, -0.01380538847f, -0.009203754365f, -0.004601926077f}; +constant float tc10[512] = {1.0f, 0.9999247193f, 0.9996988177f, 0.9993223548f, 0.9987954497f, 0.9981181026f, 0.9972904325f, 0.9963126183f, 0.9951847196f, 0.9939069748f, 0.9924795628f, 0.9909026623f, 0.9891765118f, 0.9873014092f, 0.9852776527f, 0.9831054807f, 0.9807852507f, 0.97831738f, 0.975702107f, 0.9729399681f, 0.9700312614f, 0.9669764638f, 0.963776052f, 0.9604305029f, 0.9569403529f, 0.9533060193f, 0.9495281577f, 0.9456073046f, 0.9415440559f, 0.9373390079f, 0.932992816f, 0.9285060763f, 0.9238795042f, 0.9191138744f, 0.9142097831f, 0.909168005f, 0.903989315f, 0.8986744881f, 0.893224299f, 0.8876396418f, 0.8819212914f, 0.8760700822f, 0.8700869679f, 0.8639728427f, 0.8577286005f, 0.851355195f, 0.84485358f, 0.838224709f, 0.8314695954f, 0.8245893121f, 0.8175848126f, 0.81045717f, 0.8032075167f, 0.7958369255f, 0.7883464098f, 0.7807372212f, 0.7730104327f, 0.7651672363f, 0.7572088242f, 0.7491363883f, 0.7409511209f, 0.7326542735f, 0.724247098f, 0.7157308459f, 0.7071067691f, 0.6983762383f, 0.689540565f, 0.6806010008f, 0.6715589762f, 0.6624158025f, 0.6531728506f, 0.6438315511f, 0.6343932748f, 0.6248595119f, 0.6152315736f, 0.6055110693f, 0.5956993103f, 0.5857978463f, 0.5758081675f, 0.5657318234f, 0.5555702448f, 0.5453249812f, 0.534997642f, 0.5245896578f, 0.514102757f, 0.5035383701f, 0.492898196f, 0.4821837842f, 0.4713967443f, 0.4605387151f, 0.449611336f, 0.438616246f, 0.4275550842f, 0.4164295495f, 0.4052413106f, 0.3939920366f, 0.3826834261f, 0.3713172078f, 0.3598950505f, 0.3484186828f, 0.336889863f, 0.3253102899f, 0.3136817515f, 0.3020059466f, 0.2902846634f, 0.27851969f, 0.266712755f, 0.2548656464f, 0.2429801822f, 0.2310581058f, 0.2191012353f, 0.2071113735f, 0.1950903237f, 0.1830398887f, 0.1709618866f, 0.1588581502f, 0.1467304677f, 0.1345807016f, 0.1224106774f, 0.1102222055f, 0.09801714122f, 0.08579730988f, 0.07356456667f, 0.061320737f, 0.04906767607f, 0.03680722415f, 0.02454122901f, 0.01227153838f, 6.123234263e-17f, -0.01227153838f, -0.02454122901f, -0.03680722415f, -0.04906767607f, -0.061320737f, -0.07356456667f, -0.08579730988f, -0.09801714122f, -0.1102222055f, -0.1224106774f, -0.1345807016f, -0.1467304677f, -0.1588581502f, -0.1709618866f, -0.1830398887f, -0.1950903237f, -0.2071113735f, -0.2191012353f, -0.2310581058f, -0.2429801822f, -0.2548656464f, -0.266712755f, -0.27851969f, -0.2902846634f, -0.3020059466f, -0.3136817515f, -0.3253102899f, -0.336889863f, -0.3484186828f, -0.3598950505f, -0.3713172078f, -0.3826834261f, -0.3939920366f, -0.4052413106f, -0.4164295495f, -0.4275550842f, -0.438616246f, -0.449611336f, -0.4605387151f, -0.4713967443f, -0.4821837842f, -0.492898196f, -0.5035383701f, -0.514102757f, -0.5245896578f, -0.534997642f, -0.5453249812f, -0.5555702448f, -0.5657318234f, -0.5758081675f, -0.5857978463f, -0.5956993103f, -0.6055110693f, -0.6152315736f, -0.6248595119f, -0.6343932748f, -0.6438315511f, -0.6531728506f, -0.6624158025f, -0.6715589762f, -0.6806010008f, -0.689540565f, -0.6983762383f, -0.7071067691f, -0.7157308459f, -0.724247098f, -0.7326542735f, -0.7409511209f, -0.7491363883f, -0.7572088242f, -0.7651672363f, -0.7730104327f, -0.7807372212f, -0.7883464098f, -0.7958369255f, -0.8032075167f, -0.81045717f, -0.8175848126f, -0.8245893121f, -0.8314695954f, -0.838224709f, -0.84485358f, -0.851355195f, -0.8577286005f, -0.8639728427f, -0.8700869679f, -0.8760700822f, -0.8819212914f, -0.8876396418f, -0.893224299f, -0.8986744881f, -0.903989315f, -0.909168005f, -0.9142097831f, -0.9191138744f, -0.9238795042f, -0.9285060763f, -0.932992816f, -0.9373390079f, -0.9415440559f, -0.9456073046f, -0.9495281577f, -0.9533060193f, -0.9569403529f, -0.9604305029f, -0.963776052f, -0.9669764638f, -0.9700312614f, -0.9729399681f, -0.975702107f, -0.97831738f, -0.9807852507f, -0.9831054807f, -0.9852776527f, -0.9873014092f, -0.9891765118f, -0.9909026623f, -0.9924795628f, -0.9939069748f, -0.9951847196f, -0.9963126183f, -0.9972904325f, -0.9981181026f, -0.9987954497f, -0.9993223548f, -0.9996988177f, -0.9999247193f, 1.0f, 0.9999247193f, 0.9996988177f, 0.9993223548f, 0.9987954497f, 0.9981181026f, 0.9972904325f, 0.9963126183f, 0.9951847196f, 0.9939069748f, 0.9924795628f, 0.9909026623f, 0.9891765118f, 0.9873014092f, 0.9852776527f, 0.9831054807f, 0.9807852507f, 0.97831738f, 0.975702107f, 0.9729399681f, 0.9700312614f, 0.9669764638f, 0.963776052f, 0.9604305029f, 0.9569403529f, 0.9533060193f, 0.9495281577f, 0.9456073046f, 0.9415440559f, 0.9373390079f, 0.932992816f, 0.9285060763f, 0.9238795042f, 0.9191138744f, 0.9142097831f, 0.909168005f, 0.903989315f, 0.8986744881f, 0.893224299f, 0.8876396418f, 0.8819212914f, 0.8760700822f, 0.8700869679f, 0.8639728427f, 0.8577286005f, 0.851355195f, 0.84485358f, 0.838224709f, 0.8314695954f, 0.8245893121f, 0.8175848126f, 0.81045717f, 0.8032075167f, 0.7958369255f, 0.7883464098f, 0.7807372212f, 0.7730104327f, 0.7651672363f, 0.7572088242f, 0.7491363883f, 0.7409511209f, 0.7326542735f, 0.724247098f, 0.7157308459f, 0.7071067691f, 0.6983762383f, 0.689540565f, 0.6806010008f, 0.6715589762f, 0.6624158025f, 0.6531728506f, 0.6438315511f, 0.6343932748f, 0.6248595119f, 0.6152315736f, 0.6055110693f, 0.5956993103f, 0.5857978463f, 0.5758081675f, 0.5657318234f, 0.5555702448f, 0.5453249812f, 0.534997642f, 0.5245896578f, 0.514102757f, 0.5035383701f, 0.492898196f, 0.4821837842f, 0.4713967443f, 0.4605387151f, 0.449611336f, 0.438616246f, 0.4275550842f, 0.4164295495f, 0.4052413106f, 0.3939920366f, 0.3826834261f, 0.3713172078f, 0.3598950505f, 0.3484186828f, 0.336889863f, 0.3253102899f, 0.3136817515f, 0.3020059466f, 0.2902846634f, 0.27851969f, 0.266712755f, 0.2548656464f, 0.2429801822f, 0.2310581058f, 0.2191012353f, 0.2071113735f, 0.1950903237f, 0.1830398887f, 0.1709618866f, 0.1588581502f, 0.1467304677f, 0.1345807016f, 0.1224106774f, 0.1102222055f, 0.09801714122f, 0.08579730988f, 0.07356456667f, 0.061320737f, 0.04906767607f, 0.03680722415f, 0.02454122901f, 0.01227153838f, 6.123234263e-17f, -0.01227153838f, -0.02454122901f, -0.03680722415f, -0.04906767607f, -0.061320737f, -0.07356456667f, -0.08579730988f, -0.09801714122f, -0.1102222055f, -0.1224106774f, -0.1345807016f, -0.1467304677f, -0.1588581502f, -0.1709618866f, -0.1830398887f, -0.1950903237f, -0.2071113735f, -0.2191012353f, -0.2310581058f, -0.2429801822f, -0.2548656464f, -0.266712755f, -0.27851969f, -0.2902846634f, -0.3020059466f, -0.3136817515f, -0.3253102899f, -0.336889863f, -0.3484186828f, -0.3598950505f, -0.3713172078f, -0.3826834261f, -0.3939920366f, -0.4052413106f, -0.4164295495f, -0.4275550842f, -0.438616246f, -0.449611336f, -0.4605387151f, -0.4713967443f, -0.4821837842f, -0.492898196f, -0.5035383701f, -0.514102757f, -0.5245896578f, -0.534997642f, -0.5453249812f, -0.5555702448f, -0.5657318234f, -0.5758081675f, -0.5857978463f, -0.5956993103f, -0.6055110693f, -0.6152315736f, -0.6248595119f, -0.6343932748f, -0.6438315511f, -0.6531728506f, -0.6624158025f, -0.6715589762f, -0.6806010008f, -0.689540565f, -0.6983762383f, -0.7071067691f, -0.7157308459f, -0.724247098f, -0.7326542735f, -0.7409511209f, -0.7491363883f, -0.7572088242f, -0.7651672363f, -0.7730104327f, -0.7807372212f, -0.7883464098f, -0.7958369255f, -0.8032075167f, -0.81045717f, -0.8175848126f, -0.8245893121f, -0.8314695954f, -0.838224709f, -0.84485358f, -0.851355195f, -0.8577286005f, -0.8639728427f, -0.8700869679f, -0.8760700822f, -0.8819212914f, -0.8876396418f, -0.893224299f, -0.8986744881f, -0.903989315f, -0.909168005f, -0.9142097831f, -0.9191138744f, -0.9238795042f, -0.9285060763f, -0.932992816f, -0.9373390079f, -0.9415440559f, -0.9456073046f, -0.9495281577f, -0.9533060193f, -0.9569403529f, -0.9604305029f, -0.963776052f, -0.9669764638f, -0.9700312614f, -0.9729399681f, -0.975702107f, -0.97831738f, -0.9807852507f, -0.9831054807f, -0.9852776527f, -0.9873014092f, -0.9891765118f, -0.9909026623f, -0.9924795628f, -0.9939069748f, -0.9951847196f, -0.9963126183f, -0.9972904325f, -0.9981181026f, -0.9987954497f, -0.9993223548f, -0.9996988177f, -0.9999247193f}; +constant float tc13[512] = {1.0f, 0.9999247193f, 0.9996988177f, 0.9993223548f, 0.9987954497f, 0.9981181026f, 0.9972904325f, 0.9963126183f, 0.9951847196f, 0.9939069748f, 0.9924795628f, 0.9909026623f, 0.9891765118f, 0.9873014092f, 0.9852776527f, 0.9831054807f, 0.9807852507f, 0.97831738f, 0.975702107f, 0.9729399681f, 0.9700312614f, 0.9669764638f, 0.963776052f, 0.9604305029f, 0.9569403529f, 0.9533060193f, 0.9495281577f, 0.9456073046f, 0.9415440559f, 0.9373390079f, 0.932992816f, 0.9285060763f, 0.9238795042f, 0.9191138744f, 0.9142097831f, 0.909168005f, 0.903989315f, 0.8986744881f, 0.893224299f, 0.8876396418f, 0.8819212914f, 0.8760700822f, 0.8700869679f, 0.8639728427f, 0.8577286005f, 0.851355195f, 0.84485358f, 0.838224709f, 0.8314695954f, 0.8245893121f, 0.8175848126f, 0.81045717f, 0.8032075167f, 0.7958369255f, 0.7883464098f, 0.7807372212f, 0.7730104327f, 0.7651672363f, 0.7572088242f, 0.7491363883f, 0.7409511209f, 0.7326542735f, 0.724247098f, 0.7157308459f, 0.7071067691f, 0.6983762383f, 0.689540565f, 0.6806010008f, 0.6715589762f, 0.6624158025f, 0.6531728506f, 0.6438315511f, 0.6343932748f, 0.6248595119f, 0.6152315736f, 0.6055110693f, 0.5956993103f, 0.5857978463f, 0.5758081675f, 0.5657318234f, 0.5555702448f, 0.5453249812f, 0.534997642f, 0.5245896578f, 0.514102757f, 0.5035383701f, 0.492898196f, 0.4821837842f, 0.4713967443f, 0.4605387151f, 0.449611336f, 0.438616246f, 0.4275550842f, 0.4164295495f, 0.4052413106f, 0.3939920366f, 0.3826834261f, 0.3713172078f, 0.3598950505f, 0.3484186828f, 0.336889863f, 0.3253102899f, 0.3136817515f, 0.3020059466f, 0.2902846634f, 0.27851969f, 0.266712755f, 0.2548656464f, 0.2429801822f, 0.2310581058f, 0.2191012353f, 0.2071113735f, 0.1950903237f, 0.1830398887f, 0.1709618866f, 0.1588581502f, 0.1467304677f, 0.1345807016f, 0.1224106774f, 0.1102222055f, 0.09801714122f, 0.08579730988f, 0.07356456667f, 0.061320737f, 0.04906767607f, 0.03680722415f, 0.02454122901f, 0.01227153838f, 6.123234263e-17f, -0.01227153838f, -0.02454122901f, -0.03680722415f, -0.04906767607f, -0.061320737f, -0.07356456667f, -0.08579730988f, -0.09801714122f, -0.1102222055f, -0.1224106774f, -0.1345807016f, -0.1467304677f, -0.1588581502f, -0.1709618866f, -0.1830398887f, -0.1950903237f, -0.2071113735f, -0.2191012353f, -0.2310581058f, -0.2429801822f, -0.2548656464f, -0.266712755f, -0.27851969f, -0.2902846634f, -0.3020059466f, -0.3136817515f, -0.3253102899f, -0.336889863f, -0.3484186828f, -0.3598950505f, -0.3713172078f, -0.3826834261f, -0.3939920366f, -0.4052413106f, -0.4164295495f, -0.4275550842f, -0.438616246f, -0.449611336f, -0.4605387151f, -0.4713967443f, -0.4821837842f, -0.492898196f, -0.5035383701f, -0.514102757f, -0.5245896578f, -0.534997642f, -0.5453249812f, -0.5555702448f, -0.5657318234f, -0.5758081675f, -0.5857978463f, -0.5956993103f, -0.6055110693f, -0.6152315736f, -0.6248595119f, -0.6343932748f, -0.6438315511f, -0.6531728506f, -0.6624158025f, -0.6715589762f, -0.6806010008f, -0.689540565f, -0.6983762383f, -0.7071067691f, -0.7157308459f, -0.724247098f, -0.7326542735f, -0.7409511209f, -0.7491363883f, -0.7572088242f, -0.7651672363f, -0.7730104327f, -0.7807372212f, -0.7883464098f, -0.7958369255f, -0.8032075167f, -0.81045717f, -0.8175848126f, -0.8245893121f, -0.8314695954f, -0.838224709f, -0.84485358f, -0.851355195f, -0.8577286005f, -0.8639728427f, -0.8700869679f, -0.8760700822f, -0.8819212914f, -0.8876396418f, -0.893224299f, -0.8986744881f, -0.903989315f, -0.909168005f, -0.9142097831f, -0.9191138744f, -0.9238795042f, -0.9285060763f, -0.932992816f, -0.9373390079f, -0.9415440559f, -0.9456073046f, -0.9495281577f, -0.9533060193f, -0.9569403529f, -0.9604305029f, -0.963776052f, -0.9669764638f, -0.9700312614f, -0.9729399681f, -0.975702107f, -0.97831738f, -0.9807852507f, -0.9831054807f, -0.9852776527f, -0.9873014092f, -0.9891765118f, -0.9909026623f, -0.9924795628f, -0.9939069748f, -0.9951847196f, -0.9963126183f, -0.9972904325f, -0.9981181026f, -0.9987954497f, -0.9993223548f, -0.9996988177f, -0.9999247193f, 1.0f, 0.9999247193f, 0.9996988177f, 0.9993223548f, 0.9987954497f, 0.9981181026f, 0.9972904325f, 0.9963126183f, 0.9951847196f, 0.9939069748f, 0.9924795628f, 0.9909026623f, 0.9891765118f, 0.9873014092f, 0.9852776527f, 0.9831054807f, 0.9807852507f, 0.97831738f, 0.975702107f, 0.9729399681f, 0.9700312614f, 0.9669764638f, 0.963776052f, 0.9604305029f, 0.9569403529f, 0.9533060193f, 0.9495281577f, 0.9456073046f, 0.9415440559f, 0.9373390079f, 0.932992816f, 0.9285060763f, 0.9238795042f, 0.9191138744f, 0.9142097831f, 0.909168005f, 0.903989315f, 0.8986744881f, 0.893224299f, 0.8876396418f, 0.8819212914f, 0.8760700822f, 0.8700869679f, 0.8639728427f, 0.8577286005f, 0.851355195f, 0.84485358f, 0.838224709f, 0.8314695954f, 0.8245893121f, 0.8175848126f, 0.81045717f, 0.8032075167f, 0.7958369255f, 0.7883464098f, 0.7807372212f, 0.7730104327f, 0.7651672363f, 0.7572088242f, 0.7491363883f, 0.7409511209f, 0.7326542735f, 0.724247098f, 0.7157308459f, 0.7071067691f, 0.6983762383f, 0.689540565f, 0.6806010008f, 0.6715589762f, 0.6624158025f, 0.6531728506f, 0.6438315511f, 0.6343932748f, 0.6248595119f, 0.6152315736f, 0.6055110693f, 0.5956993103f, 0.5857978463f, 0.5758081675f, 0.5657318234f, 0.5555702448f, 0.5453249812f, 0.534997642f, 0.5245896578f, 0.514102757f, 0.5035383701f, 0.492898196f, 0.4821837842f, 0.4713967443f, 0.4605387151f, 0.449611336f, 0.438616246f, 0.4275550842f, 0.4164295495f, 0.4052413106f, 0.3939920366f, 0.3826834261f, 0.3713172078f, 0.3598950505f, 0.3484186828f, 0.336889863f, 0.3253102899f, 0.3136817515f, 0.3020059466f, 0.2902846634f, 0.27851969f, 0.266712755f, 0.2548656464f, 0.2429801822f, 0.2310581058f, 0.2191012353f, 0.2071113735f, 0.1950903237f, 0.1830398887f, 0.1709618866f, 0.1588581502f, 0.1467304677f, 0.1345807016f, 0.1224106774f, 0.1102222055f, 0.09801714122f, 0.08579730988f, 0.07356456667f, 0.061320737f, 0.04906767607f, 0.03680722415f, 0.02454122901f, 0.01227153838f, 6.123234263e-17f, -0.01227153838f, -0.02454122901f, -0.03680722415f, -0.04906767607f, -0.061320737f, -0.07356456667f, -0.08579730988f, -0.09801714122f, -0.1102222055f, -0.1224106774f, -0.1345807016f, -0.1467304677f, -0.1588581502f, -0.1709618866f, -0.1830398887f, -0.1950903237f, -0.2071113735f, -0.2191012353f, -0.2310581058f, -0.2429801822f, -0.2548656464f, -0.266712755f, -0.27851969f, -0.2902846634f, -0.3020059466f, -0.3136817515f, -0.3253102899f, -0.336889863f, -0.3484186828f, -0.3598950505f, -0.3713172078f, -0.3826834261f, -0.3939920366f, -0.4052413106f, -0.4164295495f, -0.4275550842f, -0.438616246f, -0.449611336f, -0.4605387151f, -0.4713967443f, -0.4821837842f, -0.492898196f, -0.5035383701f, -0.514102757f, -0.5245896578f, -0.534997642f, -0.5453249812f, -0.5555702448f, -0.5657318234f, -0.5758081675f, -0.5857978463f, -0.5956993103f, -0.6055110693f, -0.6152315736f, -0.6248595119f, -0.6343932748f, -0.6438315511f, -0.6531728506f, -0.6624158025f, -0.6715589762f, -0.6806010008f, -0.689540565f, -0.6983762383f, -0.7071067691f, -0.7157308459f, -0.724247098f, -0.7326542735f, -0.7409511209f, -0.7491363883f, -0.7572088242f, -0.7651672363f, -0.7730104327f, -0.7807372212f, -0.7883464098f, -0.7958369255f, -0.8032075167f, -0.81045717f, -0.8175848126f, -0.8245893121f, -0.8314695954f, -0.838224709f, -0.84485358f, -0.851355195f, -0.8577286005f, -0.8639728427f, -0.8700869679f, -0.8760700822f, -0.8819212914f, -0.8876396418f, -0.893224299f, -0.8986744881f, -0.903989315f, -0.909168005f, -0.9142097831f, -0.9191138744f, -0.9238795042f, -0.9285060763f, -0.932992816f, -0.9373390079f, -0.9415440559f, -0.9456073046f, -0.9495281577f, -0.9533060193f, -0.9569403529f, -0.9604305029f, -0.963776052f, -0.9669764638f, -0.9700312614f, -0.9729399681f, -0.975702107f, -0.97831738f, -0.9807852507f, -0.9831054807f, -0.9852776527f, -0.9873014092f, -0.9891765118f, -0.9909026623f, -0.9924795628f, -0.9939069748f, -0.9951847196f, -0.9963126183f, -0.9972904325f, -0.9981181026f, -0.9987954497f, -0.9993223548f, -0.9996988177f, -0.9999247193f}; +constant float tc11[512] = {1.0f, 0.9999811649f, 0.9999247193f, 0.9998306036f, 0.9996988177f, 0.9995294213f, 0.9993223548f, 0.9990777373f, 0.9987954497f, 0.9984755516f, 0.9981181026f, 0.997723043f, 0.9972904325f, 0.996820271f, 0.9963126183f, 0.9957674146f, 0.9951847196f, 0.9945645928f, 0.9939069748f, 0.993211925f, 0.9924795628f, 0.9917097688f, 0.9909026623f, 0.9900581837f, 0.9891765118f, 0.988257587f, 0.9873014092f, 0.9863080978f, 0.9852776527f, 0.9842100739f, 0.9831054807f, 0.9819638729f, 0.9807852507f, 0.9795697927f, 0.97831738f, 0.9770281315f, 0.975702107f, 0.974339366f, 0.9729399681f, 0.9715039134f, 0.9700312614f, 0.9685220718f, 0.9669764638f, 0.9653944373f, 0.963776052f, 0.9621214271f, 0.9604305029f, 0.9587034583f, 0.9569403529f, 0.9551411867f, 0.9533060193f, 0.9514350295f, 0.9495281577f, 0.9475855827f, 0.9456073046f, 0.9435934424f, 0.9415440559f, 0.9394592047f, 0.9373390079f, 0.9351835251f, 0.932992816f, 0.9307669401f, 0.9285060763f, 0.9262102246f, 0.9238795042f, 0.9215140343f, 0.9191138744f, 0.9166790843f, 0.9142097831f, 0.9117060304f, 0.909168005f, 0.9065957069f, 0.903989315f, 0.9013488293f, 0.8986744881f, 0.8959662318f, 0.893224299f, 0.8904487491f, 0.8876396418f, 0.8847970963f, 0.8819212914f, 0.8790122271f, 0.8760700822f, 0.8730949759f, 0.8700869679f, 0.867046237f, 0.8639728427f, 0.8608669639f, 0.8577286005f, 0.854557991f, 0.851355195f, 0.8481203318f, 0.84485358f, 0.8415549994f, 0.838224709f, 0.8348628879f, 0.8314695954f, 0.8280450702f, 0.8245893121f, 0.8211025f, 0.8175848126f, 0.8140363097f, 0.81045717f, 0.8068475723f, 0.8032075167f, 0.7995372415f, 0.7958369255f, 0.7921065688f, 0.7883464098f, 0.7845565677f, 0.7807372212f, 0.7768884897f, 0.7730104327f, 0.7691033483f, 0.7651672363f, 0.761202395f, 0.7572088242f, 0.7531868219f, 0.7491363883f, 0.7450577617f, 0.7409511209f, 0.7368165851f, 0.7326542735f, 0.728464365f, 0.724247098f, 0.720002532f, 0.7157308459f, 0.7114322186f, 0.7071067691f, 0.7027547359f, 0.6983762383f, 0.6939714551f, 0.689540565f, 0.6850836873f, 0.6806010008f, 0.6760926843f, 0.6715589762f, 0.6669999361f, 0.6624158025f, 0.6578066945f, 0.6531728506f, 0.64851439f, 0.6438315511f, 0.6391244531f, 0.6343932748f, 0.6296382546f, 0.6248595119f, 0.6200572252f, 0.6152315736f, 0.6103827953f, 0.6055110693f, 0.6006164551f, 0.5956993103f, 0.5907596946f, 0.5857978463f, 0.5808139443f, 0.5758081675f, 0.5707807541f, 0.5657318234f, 0.5606615543f, 0.5555702448f, 0.5504579544f, 0.5453249812f, 0.5401714444f, 0.534997642f, 0.5298036337f, 0.5245896578f, 0.5193560123f, 0.514102757f, 0.5088301301f, 0.5035383701f, 0.4982276559f, 0.492898196f, 0.4875501692f, 0.4821837842f, 0.4767992198f, 0.4713967443f, 0.4659765065f, 0.4605387151f, 0.4550835788f, 0.449611336f, 0.4441221356f, 0.438616246f, 0.433093816f, 0.4275550842f, 0.4220002592f, 0.4164295495f, 0.4108431637f, 0.4052413106f, 0.3996241987f, 0.3939920366f, 0.3883450329f, 0.3826834261f, 0.3770074248f, 0.3713172078f, 0.3656129837f, 0.3598950505f, 0.3541635275f, 0.3484186828f, 0.3426607251f, 0.336889863f, 0.3311063051f, 0.3253102899f, 0.3195020258f, 0.3136817515f, 0.3078496456f, 0.3020059466f, 0.296150893f, 0.2902846634f, 0.2844075263f, 0.27851969f, 0.2726213634f, 0.266712755f, 0.2607941031f, 0.2548656464f, 0.2489276081f, 0.2429801822f, 0.2370236069f, 0.2310581058f, 0.2250839174f, 0.2191012353f, 0.2131103128f, 0.2071113735f, 0.201104641f, 0.1950903237f, 0.1890686601f, 0.1830398887f, 0.1770042181f, 0.1709618866f, 0.1649131179f, 0.1588581502f, 0.1527971923f, 0.1467304677f, 0.1406582445f, 0.1345807016f, 0.1284981072f, 0.1224106774f, 0.1163186282f, 0.1102222055f, 0.1041216329f, 0.09801714122f, 0.09190895408f, 0.08579730988f, 0.07968243957f, 0.07356456667f, 0.06744392216f, 0.061320737f, 0.05519524589f, 0.04906767607f, 0.0429382585f, 0.03680722415f, 0.030674804f, 0.02454122901f, 0.01840673015f, 0.01227153838f, 0.006135884672f, 1.0f, 0.9999811649f, 0.9999247193f, 0.9998306036f, 0.9996988177f, 0.9995294213f, 0.9993223548f, 0.9990777373f, 0.9987954497f, 0.9984755516f, 0.9981181026f, 0.997723043f, 0.9972904325f, 0.996820271f, 0.9963126183f, 0.9957674146f, 0.9951847196f, 0.9945645928f, 0.9939069748f, 0.993211925f, 0.9924795628f, 0.9917097688f, 0.9909026623f, 0.9900581837f, 0.9891765118f, 0.988257587f, 0.9873014092f, 0.9863080978f, 0.9852776527f, 0.9842100739f, 0.9831054807f, 0.9819638729f, 0.9807852507f, 0.9795697927f, 0.97831738f, 0.9770281315f, 0.975702107f, 0.974339366f, 0.9729399681f, 0.9715039134f, 0.9700312614f, 0.9685220718f, 0.9669764638f, 0.9653944373f, 0.963776052f, 0.9621214271f, 0.9604305029f, 0.9587034583f, 0.9569403529f, 0.9551411867f, 0.9533060193f, 0.9514350295f, 0.9495281577f, 0.9475855827f, 0.9456073046f, 0.9435934424f, 0.9415440559f, 0.9394592047f, 0.9373390079f, 0.9351835251f, 0.932992816f, 0.9307669401f, 0.9285060763f, 0.9262102246f, 0.9238795042f, 0.9215140343f, 0.9191138744f, 0.9166790843f, 0.9142097831f, 0.9117060304f, 0.909168005f, 0.9065957069f, 0.903989315f, 0.9013488293f, 0.8986744881f, 0.8959662318f, 0.893224299f, 0.8904487491f, 0.8876396418f, 0.8847970963f, 0.8819212914f, 0.8790122271f, 0.8760700822f, 0.8730949759f, 0.8700869679f, 0.867046237f, 0.8639728427f, 0.8608669639f, 0.8577286005f, 0.854557991f, 0.851355195f, 0.8481203318f, 0.84485358f, 0.8415549994f, 0.838224709f, 0.8348628879f, 0.8314695954f, 0.8280450702f, 0.8245893121f, 0.8211025f, 0.8175848126f, 0.8140363097f, 0.81045717f, 0.8068475723f, 0.8032075167f, 0.7995372415f, 0.7958369255f, 0.7921065688f, 0.7883464098f, 0.7845565677f, 0.7807372212f, 0.7768884897f, 0.7730104327f, 0.7691033483f, 0.7651672363f, 0.761202395f, 0.7572088242f, 0.7531868219f, 0.7491363883f, 0.7450577617f, 0.7409511209f, 0.7368165851f, 0.7326542735f, 0.728464365f, 0.724247098f, 0.720002532f, 0.7157308459f, 0.7114322186f, 0.7071067691f, 0.7027547359f, 0.6983762383f, 0.6939714551f, 0.689540565f, 0.6850836873f, 0.6806010008f, 0.6760926843f, 0.6715589762f, 0.6669999361f, 0.6624158025f, 0.6578066945f, 0.6531728506f, 0.64851439f, 0.6438315511f, 0.6391244531f, 0.6343932748f, 0.6296382546f, 0.6248595119f, 0.6200572252f, 0.6152315736f, 0.6103827953f, 0.6055110693f, 0.6006164551f, 0.5956993103f, 0.5907596946f, 0.5857978463f, 0.5808139443f, 0.5758081675f, 0.5707807541f, 0.5657318234f, 0.5606615543f, 0.5555702448f, 0.5504579544f, 0.5453249812f, 0.5401714444f, 0.534997642f, 0.5298036337f, 0.5245896578f, 0.5193560123f, 0.514102757f, 0.5088301301f, 0.5035383701f, 0.4982276559f, 0.492898196f, 0.4875501692f, 0.4821837842f, 0.4767992198f, 0.4713967443f, 0.4659765065f, 0.4605387151f, 0.4550835788f, 0.449611336f, 0.4441221356f, 0.438616246f, 0.433093816f, 0.4275550842f, 0.4220002592f, 0.4164295495f, 0.4108431637f, 0.4052413106f, 0.3996241987f, 0.3939920366f, 0.3883450329f, 0.3826834261f, 0.3770074248f, 0.3713172078f, 0.3656129837f, 0.3598950505f, 0.3541635275f, 0.3484186828f, 0.3426607251f, 0.336889863f, 0.3311063051f, 0.3253102899f, 0.3195020258f, 0.3136817515f, 0.3078496456f, 0.3020059466f, 0.296150893f, 0.2902846634f, 0.2844075263f, 0.27851969f, 0.2726213634f, 0.266712755f, 0.2607941031f, 0.2548656464f, 0.2489276081f, 0.2429801822f, 0.2370236069f, 0.2310581058f, 0.2250839174f, 0.2191012353f, 0.2131103128f, 0.2071113735f, 0.201104641f, 0.1950903237f, 0.1890686601f, 0.1830398887f, 0.1770042181f, 0.1709618866f, 0.1649131179f, 0.1588581502f, 0.1527971923f, 0.1467304677f, 0.1406582445f, 0.1345807016f, 0.1284981072f, 0.1224106774f, 0.1163186282f, 0.1102222055f, 0.1041216329f, 0.09801714122f, 0.09190895408f, 0.08579730988f, 0.07968243957f, 0.07356456667f, 0.06744392216f, 0.061320737f, 0.05519524589f, 0.04906767607f, 0.0429382585f, 0.03680722415f, 0.030674804f, 0.02454122901f, 0.01840673015f, 0.01227153838f, 0.006135884672f}; +constant float tc14[512] = {1.0f, 0.9999811649f, 0.9999247193f, 0.9998306036f, 0.9996988177f, 0.9995294213f, 0.9993223548f, 0.9990777373f, 0.9987954497f, 0.9984755516f, 0.9981181026f, 0.997723043f, 0.9972904325f, 0.996820271f, 0.9963126183f, 0.9957674146f, 0.9951847196f, 0.9945645928f, 0.9939069748f, 0.993211925f, 0.9924795628f, 0.9917097688f, 0.9909026623f, 0.9900581837f, 0.9891765118f, 0.988257587f, 0.9873014092f, 0.9863080978f, 0.9852776527f, 0.9842100739f, 0.9831054807f, 0.9819638729f, 0.9807852507f, 0.9795697927f, 0.97831738f, 0.9770281315f, 0.975702107f, 0.974339366f, 0.9729399681f, 0.9715039134f, 0.9700312614f, 0.9685220718f, 0.9669764638f, 0.9653944373f, 0.963776052f, 0.9621214271f, 0.9604305029f, 0.9587034583f, 0.9569403529f, 0.9551411867f, 0.9533060193f, 0.9514350295f, 0.9495281577f, 0.9475855827f, 0.9456073046f, 0.9435934424f, 0.9415440559f, 0.9394592047f, 0.9373390079f, 0.9351835251f, 0.932992816f, 0.9307669401f, 0.9285060763f, 0.9262102246f, 0.9238795042f, 0.9215140343f, 0.9191138744f, 0.9166790843f, 0.9142097831f, 0.9117060304f, 0.909168005f, 0.9065957069f, 0.903989315f, 0.9013488293f, 0.8986744881f, 0.8959662318f, 0.893224299f, 0.8904487491f, 0.8876396418f, 0.8847970963f, 0.8819212914f, 0.8790122271f, 0.8760700822f, 0.8730949759f, 0.8700869679f, 0.867046237f, 0.8639728427f, 0.8608669639f, 0.8577286005f, 0.854557991f, 0.851355195f, 0.8481203318f, 0.84485358f, 0.8415549994f, 0.838224709f, 0.8348628879f, 0.8314695954f, 0.8280450702f, 0.8245893121f, 0.8211025f, 0.8175848126f, 0.8140363097f, 0.81045717f, 0.8068475723f, 0.8032075167f, 0.7995372415f, 0.7958369255f, 0.7921065688f, 0.7883464098f, 0.7845565677f, 0.7807372212f, 0.7768884897f, 0.7730104327f, 0.7691033483f, 0.7651672363f, 0.761202395f, 0.7572088242f, 0.7531868219f, 0.7491363883f, 0.7450577617f, 0.7409511209f, 0.7368165851f, 0.7326542735f, 0.728464365f, 0.724247098f, 0.720002532f, 0.7157308459f, 0.7114322186f, 0.7071067691f, 0.7027547359f, 0.6983762383f, 0.6939714551f, 0.689540565f, 0.6850836873f, 0.6806010008f, 0.6760926843f, 0.6715589762f, 0.6669999361f, 0.6624158025f, 0.6578066945f, 0.6531728506f, 0.64851439f, 0.6438315511f, 0.6391244531f, 0.6343932748f, 0.6296382546f, 0.6248595119f, 0.6200572252f, 0.6152315736f, 0.6103827953f, 0.6055110693f, 0.6006164551f, 0.5956993103f, 0.5907596946f, 0.5857978463f, 0.5808139443f, 0.5758081675f, 0.5707807541f, 0.5657318234f, 0.5606615543f, 0.5555702448f, 0.5504579544f, 0.5453249812f, 0.5401714444f, 0.534997642f, 0.5298036337f, 0.5245896578f, 0.5193560123f, 0.514102757f, 0.5088301301f, 0.5035383701f, 0.4982276559f, 0.492898196f, 0.4875501692f, 0.4821837842f, 0.4767992198f, 0.4713967443f, 0.4659765065f, 0.4605387151f, 0.4550835788f, 0.449611336f, 0.4441221356f, 0.438616246f, 0.433093816f, 0.4275550842f, 0.4220002592f, 0.4164295495f, 0.4108431637f, 0.4052413106f, 0.3996241987f, 0.3939920366f, 0.3883450329f, 0.3826834261f, 0.3770074248f, 0.3713172078f, 0.3656129837f, 0.3598950505f, 0.3541635275f, 0.3484186828f, 0.3426607251f, 0.336889863f, 0.3311063051f, 0.3253102899f, 0.3195020258f, 0.3136817515f, 0.3078496456f, 0.3020059466f, 0.296150893f, 0.2902846634f, 0.2844075263f, 0.27851969f, 0.2726213634f, 0.266712755f, 0.2607941031f, 0.2548656464f, 0.2489276081f, 0.2429801822f, 0.2370236069f, 0.2310581058f, 0.2250839174f, 0.2191012353f, 0.2131103128f, 0.2071113735f, 0.201104641f, 0.1950903237f, 0.1890686601f, 0.1830398887f, 0.1770042181f, 0.1709618866f, 0.1649131179f, 0.1588581502f, 0.1527971923f, 0.1467304677f, 0.1406582445f, 0.1345807016f, 0.1284981072f, 0.1224106774f, 0.1163186282f, 0.1102222055f, 0.1041216329f, 0.09801714122f, 0.09190895408f, 0.08579730988f, 0.07968243957f, 0.07356456667f, 0.06744392216f, 0.061320737f, 0.05519524589f, 0.04906767607f, 0.0429382585f, 0.03680722415f, 0.030674804f, 0.02454122901f, 0.01840673015f, 0.01227153838f, 0.006135884672f, 1.0f, 0.9999811649f, 0.9999247193f, 0.9998306036f, 0.9996988177f, 0.9995294213f, 0.9993223548f, 0.9990777373f, 0.9987954497f, 0.9984755516f, 0.9981181026f, 0.997723043f, 0.9972904325f, 0.996820271f, 0.9963126183f, 0.9957674146f, 0.9951847196f, 0.9945645928f, 0.9939069748f, 0.993211925f, 0.9924795628f, 0.9917097688f, 0.9909026623f, 0.9900581837f, 0.9891765118f, 0.988257587f, 0.9873014092f, 0.9863080978f, 0.9852776527f, 0.9842100739f, 0.9831054807f, 0.9819638729f, 0.9807852507f, 0.9795697927f, 0.97831738f, 0.9770281315f, 0.975702107f, 0.974339366f, 0.9729399681f, 0.9715039134f, 0.9700312614f, 0.9685220718f, 0.9669764638f, 0.9653944373f, 0.963776052f, 0.9621214271f, 0.9604305029f, 0.9587034583f, 0.9569403529f, 0.9551411867f, 0.9533060193f, 0.9514350295f, 0.9495281577f, 0.9475855827f, 0.9456073046f, 0.9435934424f, 0.9415440559f, 0.9394592047f, 0.9373390079f, 0.9351835251f, 0.932992816f, 0.9307669401f, 0.9285060763f, 0.9262102246f, 0.9238795042f, 0.9215140343f, 0.9191138744f, 0.9166790843f, 0.9142097831f, 0.9117060304f, 0.909168005f, 0.9065957069f, 0.903989315f, 0.9013488293f, 0.8986744881f, 0.8959662318f, 0.893224299f, 0.8904487491f, 0.8876396418f, 0.8847970963f, 0.8819212914f, 0.8790122271f, 0.8760700822f, 0.8730949759f, 0.8700869679f, 0.867046237f, 0.8639728427f, 0.8608669639f, 0.8577286005f, 0.854557991f, 0.851355195f, 0.8481203318f, 0.84485358f, 0.8415549994f, 0.838224709f, 0.8348628879f, 0.8314695954f, 0.8280450702f, 0.8245893121f, 0.8211025f, 0.8175848126f, 0.8140363097f, 0.81045717f, 0.8068475723f, 0.8032075167f, 0.7995372415f, 0.7958369255f, 0.7921065688f, 0.7883464098f, 0.7845565677f, 0.7807372212f, 0.7768884897f, 0.7730104327f, 0.7691033483f, 0.7651672363f, 0.761202395f, 0.7572088242f, 0.7531868219f, 0.7491363883f, 0.7450577617f, 0.7409511209f, 0.7368165851f, 0.7326542735f, 0.728464365f, 0.724247098f, 0.720002532f, 0.7157308459f, 0.7114322186f, 0.7071067691f, 0.7027547359f, 0.6983762383f, 0.6939714551f, 0.689540565f, 0.6850836873f, 0.6806010008f, 0.6760926843f, 0.6715589762f, 0.6669999361f, 0.6624158025f, 0.6578066945f, 0.6531728506f, 0.64851439f, 0.6438315511f, 0.6391244531f, 0.6343932748f, 0.6296382546f, 0.6248595119f, 0.6200572252f, 0.6152315736f, 0.6103827953f, 0.6055110693f, 0.6006164551f, 0.5956993103f, 0.5907596946f, 0.5857978463f, 0.5808139443f, 0.5758081675f, 0.5707807541f, 0.5657318234f, 0.5606615543f, 0.5555702448f, 0.5504579544f, 0.5453249812f, 0.5401714444f, 0.534997642f, 0.5298036337f, 0.5245896578f, 0.5193560123f, 0.514102757f, 0.5088301301f, 0.5035383701f, 0.4982276559f, 0.492898196f, 0.4875501692f, 0.4821837842f, 0.4767992198f, 0.4713967443f, 0.4659765065f, 0.4605387151f, 0.4550835788f, 0.449611336f, 0.4441221356f, 0.438616246f, 0.433093816f, 0.4275550842f, 0.4220002592f, 0.4164295495f, 0.4108431637f, 0.4052413106f, 0.3996241987f, 0.3939920366f, 0.3883450329f, 0.3826834261f, 0.3770074248f, 0.3713172078f, 0.3656129837f, 0.3598950505f, 0.3541635275f, 0.3484186828f, 0.3426607251f, 0.336889863f, 0.3311063051f, 0.3253102899f, 0.3195020258f, 0.3136817515f, 0.3078496456f, 0.3020059466f, 0.296150893f, 0.2902846634f, 0.2844075263f, 0.27851969f, 0.2726213634f, 0.266712755f, 0.2607941031f, 0.2548656464f, 0.2489276081f, 0.2429801822f, 0.2370236069f, 0.2310581058f, 0.2250839174f, 0.2191012353f, 0.2131103128f, 0.2071113735f, 0.201104641f, 0.1950903237f, 0.1890686601f, 0.1830398887f, 0.1770042181f, 0.1709618866f, 0.1649131179f, 0.1588581502f, 0.1527971923f, 0.1467304677f, 0.1406582445f, 0.1345807016f, 0.1284981072f, 0.1224106774f, 0.1163186282f, 0.1102222055f, 0.1041216329f, 0.09801714122f, 0.09190895408f, 0.08579730988f, 0.07968243957f, 0.07356456667f, 0.06744392216f, 0.061320737f, 0.05519524589f, 0.04906767607f, 0.0429382585f, 0.03680722415f, 0.030674804f, 0.02454122901f, 0.01840673015f, 0.01227153838f, 0.006135884672f}; +constant float tc12[512] = {1.0f, 0.9998306036f, 0.9993223548f, 0.9984755516f, 0.9972904325f, 0.9957674146f, 0.9939069748f, 0.9917097688f, 0.9891765118f, 0.9863080978f, 0.9831054807f, 0.9795697927f, 0.975702107f, 0.9715039134f, 0.9669764638f, 0.9621214271f, 0.9569403529f, 0.9514350295f, 0.9456073046f, 0.9394592047f, 0.932992816f, 0.9262102246f, 0.9191138744f, 0.9117060304f, 0.903989315f, 0.8959662318f, 0.8876396418f, 0.8790122271f, 0.8700869679f, 0.8608669639f, 0.851355195f, 0.8415549994f, 0.8314695954f, 0.8211025f, 0.81045717f, 0.7995372415f, 0.7883464098f, 0.7768884897f, 0.7651672363f, 0.7531868219f, 0.7409511209f, 0.728464365f, 0.7157308459f, 0.7027547359f, 0.689540565f, 0.6760926843f, 0.6624158025f, 0.64851439f, 0.6343932748f, 0.6200572252f, 0.6055110693f, 0.5907596946f, 0.5758081675f, 0.5606615543f, 0.5453249812f, 0.5298036337f, 0.514102757f, 0.4982276559f, 0.4821837842f, 0.4659765065f, 0.449611336f, 0.433093816f, 0.4164295495f, 0.3996241987f, 0.3826834261f, 0.3656129837f, 0.3484186828f, 0.3311063051f, 0.3136817515f, 0.296150893f, 0.27851969f, 0.2607941031f, 0.2429801822f, 0.2250839174f, 0.2071113735f, 0.1890686601f, 0.1709618866f, 0.1527971923f, 0.1345807016f, 0.1163186282f, 0.09801714122f, 0.07968243957f, 0.061320737f, 0.0429382585f, 0.02454122901f, 0.006135884672f, -0.01227153838f, -0.030674804f, -0.04906767607f, -0.06744392216f, -0.08579730988f, -0.1041216329f, -0.1224106774f, -0.1406582445f, -0.1588581502f, -0.1770042181f, -0.1950903237f, -0.2131103128f, -0.2310581058f, -0.2489276081f, -0.266712755f, -0.2844075263f, -0.3020059466f, -0.3195020258f, -0.336889863f, -0.3541635275f, -0.3713172078f, -0.3883450329f, -0.4052413106f, -0.4220002592f, -0.438616246f, -0.4550835788f, -0.4713967443f, -0.4875501692f, -0.5035383701f, -0.5193560123f, -0.534997642f, -0.5504579544f, -0.5657318234f, -0.5808139443f, -0.5956993103f, -0.6103827953f, -0.6248595119f, -0.6391244531f, -0.6531728506f, -0.6669999361f, -0.6806010008f, -0.6939714551f, -0.7071067691f, -0.720002532f, -0.7326542735f, -0.7450577617f, -0.7572088242f, -0.7691033483f, -0.7807372212f, -0.7921065688f, -0.8032075167f, -0.8140363097f, -0.8245893121f, -0.8348628879f, -0.84485358f, -0.854557991f, -0.8639728427f, -0.8730949759f, -0.8819212914f, -0.8904487491f, -0.8986744881f, -0.9065957069f, -0.9142097831f, -0.9215140343f, -0.9285060763f, -0.9351835251f, -0.9415440559f, -0.9475855827f, -0.9533060193f, -0.9587034583f, -0.963776052f, -0.9685220718f, -0.9729399681f, -0.9770281315f, -0.9807852507f, -0.9842100739f, -0.9873014092f, -0.9900581837f, -0.9924795628f, -0.9945645928f, -0.9963126183f, -0.997723043f, -0.9987954497f, -0.9995294213f, -0.9999247193f, -0.9999811649f, -0.9996988177f, -0.9990777373f, -0.9981181026f, -0.996820271f, -0.9951847196f, -0.993211925f, -0.9909026623f, -0.988257587f, -0.9852776527f, -0.9819638729f, -0.97831738f, -0.974339366f, -0.9700312614f, -0.9653944373f, -0.9604305029f, -0.9551411867f, -0.9495281577f, -0.9435934424f, -0.9373390079f, -0.9307669401f, -0.9238795042f, -0.9166790843f, -0.909168005f, -0.9013488293f, -0.893224299f, -0.8847970963f, -0.8760700822f, -0.867046237f, -0.8577286005f, -0.8481203318f, -0.838224709f, -0.8280450702f, -0.8175848126f, -0.8068475723f, -0.7958369255f, -0.7845565677f, -0.7730104327f, -0.761202395f, -0.7491363883f, -0.7368165851f, -0.724247098f, -0.7114322186f, -0.6983762383f, -0.6850836873f, -0.6715589762f, -0.6578066945f, -0.6438315511f, -0.6296382546f, -0.6152315736f, -0.6006164551f, -0.5857978463f, -0.5707807541f, -0.5555702448f, -0.5401714444f, -0.5245896578f, -0.5088301301f, -0.492898196f, -0.4767992198f, -0.4605387151f, -0.4441221356f, -0.4275550842f, -0.4108431637f, -0.3939920366f, -0.3770074248f, -0.3598950505f, -0.3426607251f, -0.3253102899f, -0.3078496456f, -0.2902846634f, -0.2726213634f, -0.2548656464f, -0.2370236069f, -0.2191012353f, -0.201104641f, -0.1830398887f, -0.1649131179f, -0.1467304677f, -0.1284981072f, -0.1102222055f, -0.09190895408f, -0.07356456667f, -0.05519524589f, -0.03680722415f, -0.01840673015f, 1.0f, 0.9998306036f, 0.9993223548f, 0.9984755516f, 0.9972904325f, 0.9957674146f, 0.9939069748f, 0.9917097688f, 0.9891765118f, 0.9863080978f, 0.9831054807f, 0.9795697927f, 0.975702107f, 0.9715039134f, 0.9669764638f, 0.9621214271f, 0.9569403529f, 0.9514350295f, 0.9456073046f, 0.9394592047f, 0.932992816f, 0.9262102246f, 0.9191138744f, 0.9117060304f, 0.903989315f, 0.8959662318f, 0.8876396418f, 0.8790122271f, 0.8700869679f, 0.8608669639f, 0.851355195f, 0.8415549994f, 0.8314695954f, 0.8211025f, 0.81045717f, 0.7995372415f, 0.7883464098f, 0.7768884897f, 0.7651672363f, 0.7531868219f, 0.7409511209f, 0.728464365f, 0.7157308459f, 0.7027547359f, 0.689540565f, 0.6760926843f, 0.6624158025f, 0.64851439f, 0.6343932748f, 0.6200572252f, 0.6055110693f, 0.5907596946f, 0.5758081675f, 0.5606615543f, 0.5453249812f, 0.5298036337f, 0.514102757f, 0.4982276559f, 0.4821837842f, 0.4659765065f, 0.449611336f, 0.433093816f, 0.4164295495f, 0.3996241987f, 0.3826834261f, 0.3656129837f, 0.3484186828f, 0.3311063051f, 0.3136817515f, 0.296150893f, 0.27851969f, 0.2607941031f, 0.2429801822f, 0.2250839174f, 0.2071113735f, 0.1890686601f, 0.1709618866f, 0.1527971923f, 0.1345807016f, 0.1163186282f, 0.09801714122f, 0.07968243957f, 0.061320737f, 0.0429382585f, 0.02454122901f, 0.006135884672f, -0.01227153838f, -0.030674804f, -0.04906767607f, -0.06744392216f, -0.08579730988f, -0.1041216329f, -0.1224106774f, -0.1406582445f, -0.1588581502f, -0.1770042181f, -0.1950903237f, -0.2131103128f, -0.2310581058f, -0.2489276081f, -0.266712755f, -0.2844075263f, -0.3020059466f, -0.3195020258f, -0.336889863f, -0.3541635275f, -0.3713172078f, -0.3883450329f, -0.4052413106f, -0.4220002592f, -0.438616246f, -0.4550835788f, -0.4713967443f, -0.4875501692f, -0.5035383701f, -0.5193560123f, -0.534997642f, -0.5504579544f, -0.5657318234f, -0.5808139443f, -0.5956993103f, -0.6103827953f, -0.6248595119f, -0.6391244531f, -0.6531728506f, -0.6669999361f, -0.6806010008f, -0.6939714551f, -0.7071067691f, -0.720002532f, -0.7326542735f, -0.7450577617f, -0.7572088242f, -0.7691033483f, -0.7807372212f, -0.7921065688f, -0.8032075167f, -0.8140363097f, -0.8245893121f, -0.8348628879f, -0.84485358f, -0.854557991f, -0.8639728427f, -0.8730949759f, -0.8819212914f, -0.8904487491f, -0.8986744881f, -0.9065957069f, -0.9142097831f, -0.9215140343f, -0.9285060763f, -0.9351835251f, -0.9415440559f, -0.9475855827f, -0.9533060193f, -0.9587034583f, -0.963776052f, -0.9685220718f, -0.9729399681f, -0.9770281315f, -0.9807852507f, -0.9842100739f, -0.9873014092f, -0.9900581837f, -0.9924795628f, -0.9945645928f, -0.9963126183f, -0.997723043f, -0.9987954497f, -0.9995294213f, -0.9999247193f, -0.9999811649f, -0.9996988177f, -0.9990777373f, -0.9981181026f, -0.996820271f, -0.9951847196f, -0.993211925f, -0.9909026623f, -0.988257587f, -0.9852776527f, -0.9819638729f, -0.97831738f, -0.974339366f, -0.9700312614f, -0.9653944373f, -0.9604305029f, -0.9551411867f, -0.9495281577f, -0.9435934424f, -0.9373390079f, -0.9307669401f, -0.9238795042f, -0.9166790843f, -0.909168005f, -0.9013488293f, -0.893224299f, -0.8847970963f, -0.8760700822f, -0.867046237f, -0.8577286005f, -0.8481203318f, -0.838224709f, -0.8280450702f, -0.8175848126f, -0.8068475723f, -0.7958369255f, -0.7845565677f, -0.7730104327f, -0.761202395f, -0.7491363883f, -0.7368165851f, -0.724247098f, -0.7114322186f, -0.6983762383f, -0.6850836873f, -0.6715589762f, -0.6578066945f, -0.6438315511f, -0.6296382546f, -0.6152315736f, -0.6006164551f, -0.5857978463f, -0.5707807541f, -0.5555702448f, -0.5401714444f, -0.5245896578f, -0.5088301301f, -0.492898196f, -0.4767992198f, -0.4605387151f, -0.4441221356f, -0.4275550842f, -0.4108431637f, -0.3939920366f, -0.3770074248f, -0.3598950505f, -0.3426607251f, -0.3253102899f, -0.3078496456f, -0.2902846634f, -0.2726213634f, -0.2548656464f, -0.2370236069f, -0.2191012353f, -0.201104641f, -0.1830398887f, -0.1649131179f, -0.1467304677f, -0.1284981072f, -0.1102222055f, -0.09190895408f, -0.07356456667f, -0.05519524589f, -0.03680722415f, -0.01840673015f}; +constant float tc15[512] = { 1.0f, 0.9998306036f, 0.9993223548f, 0.9984755516f, 0.9972904325f, 0.9957674146f, 0.9939069748f, 0.9917097688f, 0.9891765118f, 0.9863080978f, 0.9831054807f, 0.9795697927f, 0.975702107f, 0.9715039134f, 0.9669764638f, 0.9621214271f, 0.9569403529f, 0.9514350295f, 0.9456073046f, 0.9394592047f, 0.932992816f, 0.9262102246f, 0.9191138744f, 0.9117060304f, 0.903989315f, 0.8959662318f, 0.8876396418f, 0.8790122271f, 0.8700869679f, 0.8608669639f, 0.851355195f, 0.8415549994f, 0.8314695954f, 0.8211025f, 0.81045717f, 0.7995372415f, 0.7883464098f, 0.7768884897f, 0.7651672363f, 0.7531868219f, 0.7409511209f, 0.728464365f, 0.7157308459f, 0.7027547359f, 0.689540565f, 0.6760926843f, 0.6624158025f, 0.64851439f, 0.6343932748f, 0.6200572252f, 0.6055110693f, 0.5907596946f, 0.5758081675f, 0.5606615543f, 0.5453249812f, 0.5298036337f, 0.514102757f, 0.4982276559f, 0.4821837842f, 0.4659765065f, 0.449611336f, 0.433093816f, 0.4164295495f, 0.3996241987f, 0.3826834261f, 0.3656129837f, 0.3484186828f, 0.3311063051f, 0.3136817515f, 0.296150893f, 0.27851969f, 0.2607941031f, 0.2429801822f, 0.2250839174f, 0.2071113735f, 0.1890686601f, 0.1709618866f, 0.1527971923f, 0.1345807016f, 0.1163186282f, 0.09801714122f, 0.07968243957f, 0.061320737f, 0.0429382585f, 0.02454122901f, 0.006135884672f, -0.01227153838f, -0.030674804f, -0.04906767607f, -0.06744392216f, -0.08579730988f, -0.1041216329f, -0.1224106774f, -0.1406582445f, -0.1588581502f, -0.1770042181f, -0.1950903237f, -0.2131103128f, -0.2310581058f, -0.2489276081f, -0.266712755f, -0.2844075263f, -0.3020059466f, -0.3195020258f, -0.336889863f, -0.3541635275f, -0.3713172078f, -0.3883450329f, -0.4052413106f, -0.4220002592f, -0.438616246f, -0.4550835788f, -0.4713967443f, -0.4875501692f, -0.5035383701f, -0.5193560123f, -0.534997642f, -0.5504579544f, -0.5657318234f, -0.5808139443f, -0.5956993103f, -0.6103827953f, -0.6248595119f, -0.6391244531f, -0.6531728506f, -0.6669999361f, -0.6806010008f, -0.6939714551f, -0.7071067691f, -0.720002532f, -0.7326542735f, -0.7450577617f, -0.7572088242f, -0.7691033483f, -0.7807372212f, -0.7921065688f, -0.8032075167f, -0.8140363097f, -0.8245893121f, -0.8348628879f, -0.84485358f, -0.854557991f, -0.8639728427f, -0.8730949759f, -0.8819212914f, -0.8904487491f, -0.8986744881f, -0.9065957069f, -0.9142097831f, -0.9215140343f, -0.9285060763f, -0.9351835251f, -0.9415440559f, -0.9475855827f, -0.9533060193f, -0.9587034583f, -0.963776052f, -0.9685220718f, -0.9729399681f, -0.9770281315f, -0.9807852507f, -0.9842100739f, -0.9873014092f, -0.9900581837f, -0.9924795628f, -0.9945645928f, -0.9963126183f, -0.997723043f, -0.9987954497f, -0.9995294213f, -0.9999247193f, -0.9999811649f, -0.9996988177f, -0.9990777373f, -0.9981181026f, -0.996820271f, -0.9951847196f, -0.993211925f, -0.9909026623f, -0.988257587f, -0.9852776527f, -0.9819638729f, -0.97831738f, -0.974339366f, -0.9700312614f, -0.9653944373f, -0.9604305029f, -0.9551411867f, -0.9495281577f, -0.9435934424f, -0.9373390079f, -0.9307669401f, -0.9238795042f, -0.9166790843f, -0.909168005f, -0.9013488293f, -0.893224299f, -0.8847970963f, -0.8760700822f, -0.867046237f, -0.8577286005f, -0.8481203318f, -0.838224709f, -0.8280450702f, -0.8175848126f, -0.8068475723f, -0.7958369255f, -0.7845565677f, -0.7730104327f, -0.761202395f, -0.7491363883f, -0.7368165851f, -0.724247098f, -0.7114322186f, -0.6983762383f, -0.6850836873f, -0.6715589762f, -0.6578066945f, -0.6438315511f, -0.6296382546f, -0.6152315736f, -0.6006164551f, -0.5857978463f, -0.5707807541f, -0.5555702448f, -0.5401714444f, -0.5245896578f, -0.5088301301f, -0.492898196f, -0.4767992198f, -0.4605387151f, -0.4441221356f, -0.4275550842f, -0.4108431637f, -0.3939920366f, -0.3770074248f, -0.3598950505f, -0.3426607251f, -0.3253102899f, -0.3078496456f, -0.2902846634f, -0.2726213634f, -0.2548656464f, -0.2370236069f, -0.2191012353f, -0.201104641f, -0.1830398887f, -0.1649131179f, -0.1467304677f, -0.1284981072f, -0.1102222055f, -0.09190895408f, -0.07356456667f, -0.05519524589f, -0.03680722415f, -0.01840673015f, 1.0f, 0.9998306036f, 0.9993223548f, 0.9984755516f, 0.9972904325f, 0.9957674146f, 0.9939069748f, 0.9917097688f, 0.9891765118f, 0.9863080978f, 0.9831054807f, 0.9795697927f, 0.975702107f, 0.9715039134f, 0.9669764638f, 0.9621214271f, 0.9569403529f, 0.9514350295f, 0.9456073046f, 0.9394592047f, 0.932992816f, 0.9262102246f, 0.9191138744f, 0.9117060304f, 0.903989315f, 0.8959662318f, 0.8876396418f, 0.8790122271f, 0.8700869679f, 0.8608669639f, 0.851355195f, 0.8415549994f, 0.8314695954f, 0.8211025f, 0.81045717f, 0.7995372415f, 0.7883464098f, 0.7768884897f, 0.7651672363f, 0.7531868219f, 0.7409511209f, 0.728464365f, 0.7157308459f, 0.7027547359f, 0.689540565f, 0.6760926843f, 0.6624158025f, 0.64851439f, 0.6343932748f, 0.6200572252f, 0.6055110693f, 0.5907596946f, 0.5758081675f, 0.5606615543f, 0.5453249812f, 0.5298036337f, 0.514102757f, 0.4982276559f, 0.4821837842f, 0.4659765065f, 0.449611336f, 0.433093816f, 0.4164295495f, 0.3996241987f, 0.3826834261f, 0.3656129837f, 0.3484186828f, 0.3311063051f, 0.3136817515f, 0.296150893f, 0.27851969f, 0.2607941031f, 0.2429801822f, 0.2250839174f, 0.2071113735f, 0.1890686601f, 0.1709618866f, 0.1527971923f, 0.1345807016f, 0.1163186282f, 0.09801714122f, 0.07968243957f, 0.061320737f, 0.0429382585f, 0.02454122901f, 0.006135884672f, -0.01227153838f, -0.030674804f, -0.04906767607f, -0.06744392216f, -0.08579730988f, -0.1041216329f, -0.1224106774f, -0.1406582445f, -0.1588581502f, -0.1770042181f, -0.1950903237f, -0.2131103128f, -0.2310581058f, -0.2489276081f, -0.266712755f, -0.2844075263f, -0.3020059466f, -0.3195020258f, -0.336889863f, -0.3541635275f, -0.3713172078f, -0.3883450329f, -0.4052413106f, -0.4220002592f, -0.438616246f, -0.4550835788f, -0.4713967443f, -0.4875501692f, -0.5035383701f, -0.5193560123f, -0.534997642f, -0.5504579544f, -0.5657318234f, -0.5808139443f, -0.5956993103f, -0.6103827953f, -0.6248595119f, -0.6391244531f, -0.6531728506f, -0.6669999361f, -0.6806010008f, -0.6939714551f, -0.7071067691f, -0.720002532f, -0.7326542735f, -0.7450577617f, -0.7572088242f, -0.7691033483f, -0.7807372212f, -0.7921065688f, -0.8032075167f, -0.8140363097f, -0.8245893121f, -0.8348628879f, -0.84485358f, -0.854557991f, -0.8639728427f, -0.8730949759f, -0.8819212914f, -0.8904487491f, -0.8986744881f, -0.9065957069f, -0.9142097831f, -0.9215140343f, -0.9285060763f, -0.9351835251f, -0.9415440559f, -0.9475855827f, -0.9533060193f, -0.9587034583f, -0.963776052f, -0.9685220718f, -0.9729399681f, -0.9770281315f, -0.9807852507f, -0.9842100739f, -0.9873014092f, -0.9900581837f, -0.9924795628f, -0.9945645928f, -0.9963126183f, -0.997723043f, -0.9987954497f, -0.9995294213f, -0.9999247193f, -0.9999811649f, -0.9996988177f, -0.9990777373f, -0.9981181026f, -0.996820271f, -0.9951847196f, -0.993211925f, -0.9909026623f, -0.988257587f, -0.9852776527f, -0.9819638729f, -0.97831738f, -0.974339366f, -0.9700312614f, -0.9653944373f, -0.9604305029f, -0.9551411867f, -0.9495281577f, -0.9435934424f, -0.9373390079f, -0.9307669401f, -0.9238795042f, -0.9166790843f, -0.909168005f, -0.9013488293f, -0.893224299f, -0.8847970963f, -0.8760700822f, -0.867046237f, -0.8577286005f, -0.8481203318f, -0.838224709f, -0.8280450702f, -0.8175848126f, -0.8068475723f, -0.7958369255f, -0.7845565677f, -0.7730104327f, -0.761202395f, -0.7491363883f, -0.7368165851f, -0.724247098f, -0.7114322186f, -0.6983762383f, -0.6850836873f, -0.6715589762f, -0.6578066945f, -0.6438315511f, -0.6296382546f, -0.6152315736f, -0.6006164551f, -0.5857978463f, -0.5707807541f, -0.5555702448f, -0.5401714444f, -0.5245896578f, -0.5088301301f, -0.492898196f, -0.4767992198f, -0.4605387151f, -0.4441221356f, -0.4275550842f, -0.4108431637f, -0.3939920366f, -0.3770074248f, -0.3598950505f, -0.3426607251f, -0.3253102899f, -0.3078496456f, -0.2902846634f, -0.2726213634f, -0.2548656464f, -0.2370236069f, -0.2191012353f, -0.201104641f, -0.1830398887f, -0.1649131179f, -0.1467304677f, -0.1284981072f, -0.1102222055f, -0.09190895408f, -0.07356456667f, -0.05519524589f, -0.03680722415f, -0.01840673015f}; +constant float tc20[512] = {1.0f, 0.9987954497f, 0.9951847196f, 0.9891765118f, 0.9807852507f, 0.9700312614f, 0.9569403529f, 0.9415440559f, 0.9238795042f, 0.903989315f, 0.8819212914f, 0.8577286005f, 0.8314695954f, 0.8032075167f, 0.7730104327f, 0.7409511209f, 0.7071067691f, 0.6715589762f, 0.6343932748f, 0.5956993103f, 0.5555702448f, 0.514102757f, 0.4713967443f, 0.4275550842f, 0.3826834261f, 0.336889863f, 0.2902846634f, 0.2429801822f, 0.1950903237f, 0.1467304677f, 0.09801714122f, 0.04906767607f, 6.123234263e-17f, -0.04906767607f, -0.09801714122f, -0.1467304677f, -0.1950903237f, -0.2429801822f, -0.2902846634f, -0.336889863f, -0.3826834261f, -0.4275550842f, -0.4713967443f, -0.514102757f, -0.5555702448f, -0.5956993103f, -0.6343932748f, -0.6715589762f, -0.7071067691f, -0.7409511209f, -0.7730104327f, -0.8032075167f, -0.8314695954f, -0.8577286005f, -0.8819212914f, -0.903989315f, -0.9238795042f, -0.9415440559f, -0.9569403529f, -0.9700312614f, -0.9807852507f, -0.9891765118f, -0.9951847196f, -0.9987954497f, 1.0f, 0.9987954497f, 0.9951847196f, 0.9891765118f, 0.9807852507f, 0.9700312614f, 0.9569403529f, 0.9415440559f, 0.9238795042f, 0.903989315f, 0.8819212914f, 0.8577286005f, 0.8314695954f, 0.8032075167f, 0.7730104327f, 0.7409511209f, 0.7071067691f, 0.6715589762f, 0.6343932748f, 0.5956993103f, 0.5555702448f, 0.514102757f, 0.4713967443f, 0.4275550842f, 0.3826834261f, 0.336889863f, 0.2902846634f, 0.2429801822f, 0.1950903237f, 0.1467304677f, 0.09801714122f, 0.04906767607f, 6.123234263e-17f, -0.04906767607f, -0.09801714122f, -0.1467304677f, -0.1950903237f, -0.2429801822f, -0.2902846634f, -0.336889863f, -0.3826834261f, -0.4275550842f, -0.4713967443f, -0.514102757f, -0.5555702448f, -0.5956993103f, -0.6343932748f, -0.6715589762f, -0.7071067691f, -0.7409511209f, -0.7730104327f, -0.8032075167f, -0.8314695954f, -0.8577286005f, -0.8819212914f, -0.903989315f, -0.9238795042f, -0.9415440559f, -0.9569403529f, -0.9700312614f, -0.9807852507f, -0.9891765118f, -0.9951847196f, -0.9987954497f, 1.0f, 0.9987954497f, 0.9951847196f, 0.9891765118f, 0.9807852507f, 0.9700312614f, 0.9569403529f, 0.9415440559f, 0.9238795042f, 0.903989315f, 0.8819212914f, 0.8577286005f, 0.8314695954f, 0.8032075167f, 0.7730104327f, 0.7409511209f, 0.7071067691f, 0.6715589762f, 0.6343932748f, 0.5956993103f, 0.5555702448f, 0.514102757f, 0.4713967443f, 0.4275550842f, 0.3826834261f, 0.336889863f, 0.2902846634f, 0.2429801822f, 0.1950903237f, 0.1467304677f, 0.09801714122f, 0.04906767607f, 6.123234263e-17f, -0.04906767607f, -0.09801714122f, -0.1467304677f, -0.1950903237f, -0.2429801822f, -0.2902846634f, -0.336889863f, -0.3826834261f, -0.4275550842f, -0.4713967443f, -0.514102757f, -0.5555702448f, -0.5956993103f, -0.6343932748f, -0.6715589762f, -0.7071067691f, -0.7409511209f, -0.7730104327f, -0.8032075167f, -0.8314695954f, -0.8577286005f, -0.8819212914f, -0.903989315f, -0.9238795042f, -0.9415440559f, -0.9569403529f, -0.9700312614f, -0.9807852507f, -0.9891765118f, -0.9951847196f, -0.9987954497f, 1.0f, 0.9987954497f, 0.9951847196f, 0.9891765118f, 0.9807852507f, 0.9700312614f, 0.9569403529f, 0.9415440559f, 0.9238795042f, 0.903989315f, 0.8819212914f, 0.8577286005f, 0.8314695954f, 0.8032075167f, 0.7730104327f, 0.7409511209f, 0.7071067691f, 0.6715589762f, 0.6343932748f, 0.5956993103f, 0.5555702448f, 0.514102757f, 0.4713967443f, 0.4275550842f, 0.3826834261f, 0.336889863f, 0.2902846634f, 0.2429801822f, 0.1950903237f, 0.1467304677f, 0.09801714122f, 0.04906767607f, 6.123234263e-17f, -0.04906767607f, -0.09801714122f, -0.1467304677f, -0.1950903237f, -0.2429801822f, -0.2902846634f, -0.336889863f, -0.3826834261f, -0.4275550842f, -0.4713967443f, -0.514102757f, -0.5555702448f, -0.5956993103f, -0.6343932748f, -0.6715589762f, -0.7071067691f, -0.7409511209f, -0.7730104327f, -0.8032075167f, -0.8314695954f, -0.8577286005f, -0.8819212914f, -0.903989315f, -0.9238795042f, -0.9415440559f, -0.9569403529f, -0.9700312614f, -0.9807852507f, -0.9891765118f, -0.9951847196f, -0.9987954497f, 1.0f, 0.9987954497f, 0.9951847196f, 0.9891765118f, 0.9807852507f, 0.9700312614f, 0.9569403529f, 0.9415440559f, 0.9238795042f, 0.903989315f, 0.8819212914f, 0.8577286005f, 0.8314695954f, 0.8032075167f, 0.7730104327f, 0.7409511209f, 0.7071067691f, 0.6715589762f, 0.6343932748f, 0.5956993103f, 0.5555702448f, 0.514102757f, 0.4713967443f, 0.4275550842f, 0.3826834261f, 0.336889863f, 0.2902846634f, 0.2429801822f, 0.1950903237f, 0.1467304677f, 0.09801714122f, 0.04906767607f, 6.123234263e-17f, -0.04906767607f, -0.09801714122f, -0.1467304677f, -0.1950903237f, -0.2429801822f, -0.2902846634f, -0.336889863f, -0.3826834261f, -0.4275550842f, -0.4713967443f, -0.514102757f, -0.5555702448f, -0.5956993103f, -0.6343932748f, -0.6715589762f, -0.7071067691f, -0.7409511209f, -0.7730104327f, -0.8032075167f, -0.8314695954f, -0.8577286005f, -0.8819212914f, -0.903989315f, -0.9238795042f, -0.9415440559f, -0.9569403529f, -0.9700312614f, -0.9807852507f, -0.9891765118f, -0.9951847196f, -0.9987954497f, 1.0f, 0.9987954497f, 0.9951847196f, 0.9891765118f, 0.9807852507f, 0.9700312614f, 0.9569403529f, 0.9415440559f, 0.9238795042f, 0.903989315f, 0.8819212914f, 0.8577286005f, 0.8314695954f, 0.8032075167f, 0.7730104327f, 0.7409511209f, 0.7071067691f, 0.6715589762f, 0.6343932748f, 0.5956993103f, 0.5555702448f, 0.514102757f, 0.4713967443f, 0.4275550842f, 0.3826834261f, 0.336889863f, 0.2902846634f, 0.2429801822f, 0.1950903237f, 0.1467304677f, 0.09801714122f, 0.04906767607f, 6.123234263e-17f, -0.04906767607f, -0.09801714122f, -0.1467304677f, -0.1950903237f, -0.2429801822f, -0.2902846634f, -0.336889863f, -0.3826834261f, -0.4275550842f, -0.4713967443f, -0.514102757f, -0.5555702448f, -0.5956993103f, -0.6343932748f, -0.6715589762f, -0.7071067691f, -0.7409511209f, -0.7730104327f, -0.8032075167f, -0.8314695954f, -0.8577286005f, -0.8819212914f, -0.903989315f, -0.9238795042f, -0.9415440559f, -0.9569403529f, -0.9700312614f, -0.9807852507f, -0.9891765118f, -0.9951847196f, -0.9987954497f, 1.0f, 0.9987954497f, 0.9951847196f, 0.9891765118f, 0.9807852507f, 0.9700312614f, 0.9569403529f, 0.9415440559f, 0.9238795042f, 0.903989315f, 0.8819212914f, 0.8577286005f, 0.8314695954f, 0.8032075167f, 0.7730104327f, 0.7409511209f, 0.7071067691f, 0.6715589762f, 0.6343932748f, 0.5956993103f, 0.5555702448f, 0.514102757f, 0.4713967443f, 0.4275550842f, 0.3826834261f, 0.336889863f, 0.2902846634f, 0.2429801822f, 0.1950903237f, 0.1467304677f, 0.09801714122f, 0.04906767607f, 6.123234263e-17f, -0.04906767607f, -0.09801714122f, -0.1467304677f, -0.1950903237f, -0.2429801822f, -0.2902846634f, -0.336889863f, -0.3826834261f, -0.4275550842f, -0.4713967443f, -0.514102757f, -0.5555702448f, -0.5956993103f, -0.6343932748f, -0.6715589762f, -0.7071067691f, -0.7409511209f, -0.7730104327f, -0.8032075167f, -0.8314695954f, -0.8577286005f, -0.8819212914f, -0.903989315f, -0.9238795042f, -0.9415440559f, -0.9569403529f, -0.9700312614f, -0.9807852507f, -0.9891765118f, -0.9951847196f, -0.9987954497f, 1.0f, 0.9987954497f, 0.9951847196f, 0.9891765118f, 0.9807852507f, 0.9700312614f, 0.9569403529f, 0.9415440559f, 0.9238795042f, 0.903989315f, 0.8819212914f, 0.8577286005f, 0.8314695954f, 0.8032075167f, 0.7730104327f, 0.7409511209f, 0.7071067691f, 0.6715589762f, 0.6343932748f, 0.5956993103f, 0.5555702448f, 0.514102757f, 0.4713967443f, 0.4275550842f, 0.3826834261f, 0.336889863f, 0.2902846634f, 0.2429801822f, 0.1950903237f, 0.1467304677f, 0.09801714122f, 0.04906767607f, 6.123234263e-17f, -0.04906767607f, -0.09801714122f, -0.1467304677f, -0.1950903237f, -0.2429801822f, -0.2902846634f, -0.336889863f, -0.3826834261f, -0.4275550842f, -0.4713967443f, -0.514102757f, -0.5555702448f, -0.5956993103f, -0.6343932748f, -0.6715589762f, -0.7071067691f, -0.7409511209f, -0.7730104327f, -0.8032075167f, -0.8314695954f, -0.8577286005f, -0.8819212914f, -0.903989315f, -0.9238795042f, -0.9415440559f, -0.9569403529f, -0.9700312614f, -0.9807852507f, -0.9891765118f, -0.9951847196f, -0.9987954497f}; +constant float tc23[512] = {1.0f, 0.9987954497f, 0.9951847196f, 0.9891765118f, 0.9807852507f, 0.9700312614f, 0.9569403529f, 0.9415440559f, 0.9238795042f, 0.903989315f, 0.8819212914f, 0.8577286005f, 0.8314695954f, 0.8032075167f, 0.7730104327f, 0.7409511209f, 0.7071067691f, 0.6715589762f, 0.6343932748f, 0.5956993103f, 0.5555702448f, 0.514102757f, 0.4713967443f, 0.4275550842f, 0.3826834261f, 0.336889863f, 0.2902846634f, 0.2429801822f, 0.1950903237f, 0.1467304677f, 0.09801714122f, 0.04906767607f, 6.123234263e-17f, -0.04906767607f, -0.09801714122f, -0.1467304677f, -0.1950903237f, -0.2429801822f, -0.2902846634f, -0.336889863f, -0.3826834261f, -0.4275550842f, -0.4713967443f, -0.514102757f, -0.5555702448f, -0.5956993103f, -0.6343932748f, -0.6715589762f, -0.7071067691f, -0.7409511209f, -0.7730104327f, -0.8032075167f, -0.8314695954f, -0.8577286005f, -0.8819212914f, -0.903989315f, -0.9238795042f, -0.9415440559f, -0.9569403529f, -0.9700312614f, -0.9807852507f, -0.9891765118f, -0.9951847196f, -0.9987954497f, 1.0f, 0.9987954497f, 0.9951847196f, 0.9891765118f, 0.9807852507f, 0.9700312614f, 0.9569403529f, 0.9415440559f, 0.9238795042f, 0.903989315f, 0.8819212914f, 0.8577286005f, 0.8314695954f, 0.8032075167f, 0.7730104327f, 0.7409511209f, 0.7071067691f, 0.6715589762f, 0.6343932748f, 0.5956993103f, 0.5555702448f, 0.514102757f, 0.4713967443f, 0.4275550842f, 0.3826834261f, 0.336889863f, 0.2902846634f, 0.2429801822f, 0.1950903237f, 0.1467304677f, 0.09801714122f, 0.04906767607f, 6.123234263e-17f, -0.04906767607f, -0.09801714122f, -0.1467304677f, -0.1950903237f, -0.2429801822f, -0.2902846634f, -0.336889863f, -0.3826834261f, -0.4275550842f, -0.4713967443f, -0.514102757f, -0.5555702448f, -0.5956993103f, -0.6343932748f, -0.6715589762f, -0.7071067691f, -0.7409511209f, -0.7730104327f, -0.8032075167f, -0.8314695954f, -0.8577286005f, -0.8819212914f, -0.903989315f, -0.9238795042f, -0.9415440559f, -0.9569403529f, -0.9700312614f, -0.9807852507f, -0.9891765118f, -0.9951847196f, -0.9987954497f, 1.0f, 0.9987954497f, 0.9951847196f, 0.9891765118f, 0.9807852507f, 0.9700312614f, 0.9569403529f, 0.9415440559f, 0.9238795042f, 0.903989315f, 0.8819212914f, 0.8577286005f, 0.8314695954f, 0.8032075167f, 0.7730104327f, 0.7409511209f, 0.7071067691f, 0.6715589762f, 0.6343932748f, 0.5956993103f, 0.5555702448f, 0.514102757f, 0.4713967443f, 0.4275550842f, 0.3826834261f, 0.336889863f, 0.2902846634f, 0.2429801822f, 0.1950903237f, 0.1467304677f, 0.09801714122f, 0.04906767607f, 6.123234263e-17f, -0.04906767607f, -0.09801714122f, -0.1467304677f, -0.1950903237f, -0.2429801822f, -0.2902846634f, -0.336889863f, -0.3826834261f, -0.4275550842f, -0.4713967443f, -0.514102757f, -0.5555702448f, -0.5956993103f, -0.6343932748f, -0.6715589762f, -0.7071067691f, -0.7409511209f, -0.7730104327f, -0.8032075167f, -0.8314695954f, -0.8577286005f, -0.8819212914f, -0.903989315f, -0.9238795042f, -0.9415440559f, -0.9569403529f, -0.9700312614f, -0.9807852507f, -0.9891765118f, -0.9951847196f, -0.9987954497f, 1.0f, 0.9987954497f, 0.9951847196f, 0.9891765118f, 0.9807852507f, 0.9700312614f, 0.9569403529f, 0.9415440559f, 0.9238795042f, 0.903989315f, 0.8819212914f, 0.8577286005f, 0.8314695954f, 0.8032075167f, 0.7730104327f, 0.7409511209f, 0.7071067691f, 0.6715589762f, 0.6343932748f, 0.5956993103f, 0.5555702448f, 0.514102757f, 0.4713967443f, 0.4275550842f, 0.3826834261f, 0.336889863f, 0.2902846634f, 0.2429801822f, 0.1950903237f, 0.1467304677f, 0.09801714122f, 0.04906767607f, 6.123234263e-17f, -0.04906767607f, -0.09801714122f, -0.1467304677f, -0.1950903237f, -0.2429801822f, -0.2902846634f, -0.336889863f, -0.3826834261f, -0.4275550842f, -0.4713967443f, -0.514102757f, -0.5555702448f, -0.5956993103f, -0.6343932748f, -0.6715589762f, -0.7071067691f, -0.7409511209f, -0.7730104327f, -0.8032075167f, -0.8314695954f, -0.8577286005f, -0.8819212914f, -0.903989315f, -0.9238795042f, -0.9415440559f, -0.9569403529f, -0.9700312614f, -0.9807852507f, -0.9891765118f, -0.9951847196f, -0.9987954497f, 1.0f, 0.9987954497f, 0.9951847196f, 0.9891765118f, 0.9807852507f, 0.9700312614f, 0.9569403529f, 0.9415440559f, 0.9238795042f, 0.903989315f, 0.8819212914f, 0.8577286005f, 0.8314695954f, 0.8032075167f, 0.7730104327f, 0.7409511209f, 0.7071067691f, 0.6715589762f, 0.6343932748f, 0.5956993103f, 0.5555702448f, 0.514102757f, 0.4713967443f, 0.4275550842f, 0.3826834261f, 0.336889863f, 0.2902846634f, 0.2429801822f, 0.1950903237f, 0.1467304677f, 0.09801714122f, 0.04906767607f, 6.123234263e-17f, -0.04906767607f, -0.09801714122f, -0.1467304677f, -0.1950903237f, -0.2429801822f, -0.2902846634f, -0.336889863f, -0.3826834261f, -0.4275550842f, -0.4713967443f, -0.514102757f, -0.5555702448f, -0.5956993103f, -0.6343932748f, -0.6715589762f, -0.7071067691f, -0.7409511209f, -0.7730104327f, -0.8032075167f, -0.8314695954f, -0.8577286005f, -0.8819212914f, -0.903989315f, -0.9238795042f, -0.9415440559f, -0.9569403529f, -0.9700312614f, -0.9807852507f, -0.9891765118f, -0.9951847196f, -0.9987954497f, 1.0f, 0.9987954497f, 0.9951847196f, 0.9891765118f, 0.9807852507f, 0.9700312614f, 0.9569403529f, 0.9415440559f, 0.9238795042f, 0.903989315f, 0.8819212914f, 0.8577286005f, 0.8314695954f, 0.8032075167f, 0.7730104327f, 0.7409511209f, 0.7071067691f, 0.6715589762f, 0.6343932748f, 0.5956993103f, 0.5555702448f, 0.514102757f, 0.4713967443f, 0.4275550842f, 0.3826834261f, 0.336889863f, 0.2902846634f, 0.2429801822f, 0.1950903237f, 0.1467304677f, 0.09801714122f, 0.04906767607f, 6.123234263e-17f, -0.04906767607f, -0.09801714122f, -0.1467304677f, -0.1950903237f, -0.2429801822f, -0.2902846634f, -0.336889863f, -0.3826834261f, -0.4275550842f, -0.4713967443f, -0.514102757f, -0.5555702448f, -0.5956993103f, -0.6343932748f, -0.6715589762f, -0.7071067691f, -0.7409511209f, -0.7730104327f, -0.8032075167f, -0.8314695954f, -0.8577286005f, -0.8819212914f, -0.903989315f, -0.9238795042f, -0.9415440559f, -0.9569403529f, -0.9700312614f, -0.9807852507f, -0.9891765118f, -0.9951847196f, -0.9987954497f, 1.0f, 0.9987954497f, 0.9951847196f, 0.9891765118f, 0.9807852507f, 0.9700312614f, 0.9569403529f, 0.9415440559f, 0.9238795042f, 0.903989315f, 0.8819212914f, 0.8577286005f, 0.8314695954f, 0.8032075167f, 0.7730104327f, 0.7409511209f, 0.7071067691f, 0.6715589762f, 0.6343932748f, 0.5956993103f, 0.5555702448f, 0.514102757f, 0.4713967443f, 0.4275550842f, 0.3826834261f, 0.336889863f, 0.2902846634f, 0.2429801822f, 0.1950903237f, 0.1467304677f, 0.09801714122f, 0.04906767607f, 6.123234263e-17f, -0.04906767607f, -0.09801714122f, -0.1467304677f, -0.1950903237f, -0.2429801822f, -0.2902846634f, -0.336889863f, -0.3826834261f, -0.4275550842f, -0.4713967443f, -0.514102757f, -0.5555702448f, -0.5956993103f, -0.6343932748f, -0.6715589762f, -0.7071067691f, -0.7409511209f, -0.7730104327f, -0.8032075167f, -0.8314695954f, -0.8577286005f, -0.8819212914f, -0.903989315f, -0.9238795042f, -0.9415440559f, -0.9569403529f, -0.9700312614f, -0.9807852507f, -0.9891765118f, -0.9951847196f, -0.9987954497f, 1.0f, 0.9987954497f, 0.9951847196f, 0.9891765118f, 0.9807852507f, 0.9700312614f, 0.9569403529f, 0.9415440559f, 0.9238795042f, 0.903989315f, 0.8819212914f, 0.8577286005f, 0.8314695954f, 0.8032075167f, 0.7730104327f, 0.7409511209f, 0.7071067691f, 0.6715589762f, 0.6343932748f, 0.5956993103f, 0.5555702448f, 0.514102757f, 0.4713967443f, 0.4275550842f, 0.3826834261f, 0.336889863f, 0.2902846634f, 0.2429801822f, 0.1950903237f, 0.1467304677f, 0.09801714122f, 0.04906767607f, 6.123234263e-17f, -0.04906767607f, -0.09801714122f, -0.1467304677f, -0.1950903237f, -0.2429801822f, -0.2902846634f, -0.336889863f, -0.3826834261f, -0.4275550842f, -0.4713967443f, -0.514102757f, -0.5555702448f, -0.5956993103f, -0.6343932748f, -0.6715589762f, -0.7071067691f, -0.7409511209f, -0.7730104327f, -0.8032075167f, -0.8314695954f, -0.8577286005f, -0.8819212914f, -0.903989315f, -0.9238795042f, -0.9415440559f, -0.9569403529f, -0.9700312614f, -0.9807852507f, -0.9891765118f, -0.9951847196f, -0.9987954497f}; +constant float tc21[512] = {1.0f, 0.9996988177f, 0.9987954497f, 0.9972904325f, 0.9951847196f, 0.9924795628f, 0.9891765118f, 0.9852776527f, 0.9807852507f, 0.975702107f, 0.9700312614f, 0.963776052f, 0.9569403529f, 0.9495281577f, 0.9415440559f, 0.932992816f, 0.9238795042f, 0.9142097831f, 0.903989315f, 0.893224299f, 0.8819212914f, 0.8700869679f, 0.8577286005f, 0.84485358f, 0.8314695954f, 0.8175848126f, 0.8032075167f, 0.7883464098f, 0.7730104327f, 0.7572088242f, 0.7409511209f, 0.724247098f, 0.7071067691f, 0.689540565f, 0.6715589762f, 0.6531728506f, 0.6343932748f, 0.6152315736f, 0.5956993103f, 0.5758081675f, 0.5555702448f, 0.534997642f, 0.514102757f, 0.492898196f, 0.4713967443f, 0.449611336f, 0.4275550842f, 0.4052413106f, 0.3826834261f, 0.3598950505f, 0.336889863f, 0.3136817515f, 0.2902846634f, 0.266712755f, 0.2429801822f, 0.2191012353f, 0.1950903237f, 0.1709618866f, 0.1467304677f, 0.1224106774f, 0.09801714122f, 0.07356456667f, 0.04906767607f, 0.02454122901f, 1.0f, 0.9996988177f, 0.9987954497f, 0.9972904325f, 0.9951847196f, 0.9924795628f, 0.9891765118f, 0.9852776527f, 0.9807852507f, 0.975702107f, 0.9700312614f, 0.963776052f, 0.9569403529f, 0.9495281577f, 0.9415440559f, 0.932992816f, 0.9238795042f, 0.9142097831f, 0.903989315f, 0.893224299f, 0.8819212914f, 0.8700869679f, 0.8577286005f, 0.84485358f, 0.8314695954f, 0.8175848126f, 0.8032075167f, 0.7883464098f, 0.7730104327f, 0.7572088242f, 0.7409511209f, 0.724247098f, 0.7071067691f, 0.689540565f, 0.6715589762f, 0.6531728506f, 0.6343932748f, 0.6152315736f, 0.5956993103f, 0.5758081675f, 0.5555702448f, 0.534997642f, 0.514102757f, 0.492898196f, 0.4713967443f, 0.449611336f, 0.4275550842f, 0.4052413106f, 0.3826834261f, 0.3598950505f, 0.336889863f, 0.3136817515f, 0.2902846634f, 0.266712755f, 0.2429801822f, 0.2191012353f, 0.1950903237f, 0.1709618866f, 0.1467304677f, 0.1224106774f, 0.09801714122f, 0.07356456667f, 0.04906767607f, 0.02454122901f, 1.0f, 0.9996988177f, 0.9987954497f, 0.9972904325f, 0.9951847196f, 0.9924795628f, 0.9891765118f, 0.9852776527f, 0.9807852507f, 0.975702107f, 0.9700312614f, 0.963776052f, 0.9569403529f, 0.9495281577f, 0.9415440559f, 0.932992816f, 0.9238795042f, 0.9142097831f, 0.903989315f, 0.893224299f, 0.8819212914f, 0.8700869679f, 0.8577286005f, 0.84485358f, 0.8314695954f, 0.8175848126f, 0.8032075167f, 0.7883464098f, 0.7730104327f, 0.7572088242f, 0.7409511209f, 0.724247098f, 0.7071067691f, 0.689540565f, 0.6715589762f, 0.6531728506f, 0.6343932748f, 0.6152315736f, 0.5956993103f, 0.5758081675f, 0.5555702448f, 0.534997642f, 0.514102757f, 0.492898196f, 0.4713967443f, 0.449611336f, 0.4275550842f, 0.4052413106f, 0.3826834261f, 0.3598950505f, 0.336889863f, 0.3136817515f, 0.2902846634f, 0.266712755f, 0.2429801822f, 0.2191012353f, 0.1950903237f, 0.1709618866f, 0.1467304677f, 0.1224106774f, 0.09801714122f, 0.07356456667f, 0.04906767607f, 0.02454122901f, 1.0f, 0.9996988177f, 0.9987954497f, 0.9972904325f, 0.9951847196f, 0.9924795628f, 0.9891765118f, 0.9852776527f, 0.9807852507f, 0.975702107f, 0.9700312614f, 0.963776052f, 0.9569403529f, 0.9495281577f, 0.9415440559f, 0.932992816f, 0.9238795042f, 0.9142097831f, 0.903989315f, 0.893224299f, 0.8819212914f, 0.8700869679f, 0.8577286005f, 0.84485358f, 0.8314695954f, 0.8175848126f, 0.8032075167f, 0.7883464098f, 0.7730104327f, 0.7572088242f, 0.7409511209f, 0.724247098f, 0.7071067691f, 0.689540565f, 0.6715589762f, 0.6531728506f, 0.6343932748f, 0.6152315736f, 0.5956993103f, 0.5758081675f, 0.5555702448f, 0.534997642f, 0.514102757f, 0.492898196f, 0.4713967443f, 0.449611336f, 0.4275550842f, 0.4052413106f, 0.3826834261f, 0.3598950505f, 0.336889863f, 0.3136817515f, 0.2902846634f, 0.266712755f, 0.2429801822f, 0.2191012353f, 0.1950903237f, 0.1709618866f, 0.1467304677f, 0.1224106774f, 0.09801714122f, 0.07356456667f, 0.04906767607f, 0.02454122901f, 1.0f, 0.9996988177f, 0.9987954497f, 0.9972904325f, 0.9951847196f, 0.9924795628f, 0.9891765118f, 0.9852776527f, 0.9807852507f, 0.975702107f, 0.9700312614f, 0.963776052f, 0.9569403529f, 0.9495281577f, 0.9415440559f, 0.932992816f, 0.9238795042f, 0.9142097831f, 0.903989315f, 0.893224299f, 0.8819212914f, 0.8700869679f, 0.8577286005f, 0.84485358f, 0.8314695954f, 0.8175848126f, 0.8032075167f, 0.7883464098f, 0.7730104327f, 0.7572088242f, 0.7409511209f, 0.724247098f, 0.7071067691f, 0.689540565f, 0.6715589762f, 0.6531728506f, 0.6343932748f, 0.6152315736f, 0.5956993103f, 0.5758081675f, 0.5555702448f, 0.534997642f, 0.514102757f, 0.492898196f, 0.4713967443f, 0.449611336f, 0.4275550842f, 0.4052413106f, 0.3826834261f, 0.3598950505f, 0.336889863f, 0.3136817515f, 0.2902846634f, 0.266712755f, 0.2429801822f, 0.2191012353f, 0.1950903237f, 0.1709618866f, 0.1467304677f, 0.1224106774f, 0.09801714122f, 0.07356456667f, 0.04906767607f, 0.02454122901f, 1.0f, 0.9996988177f, 0.9987954497f, 0.9972904325f, 0.9951847196f, 0.9924795628f, 0.9891765118f, 0.9852776527f, 0.9807852507f, 0.975702107f, 0.9700312614f, 0.963776052f, 0.9569403529f, 0.9495281577f, 0.9415440559f, 0.932992816f, 0.9238795042f, 0.9142097831f, 0.903989315f, 0.893224299f, 0.8819212914f, 0.8700869679f, 0.8577286005f, 0.84485358f, 0.8314695954f, 0.8175848126f, 0.8032075167f, 0.7883464098f, 0.7730104327f, 0.7572088242f, 0.7409511209f, 0.724247098f, 0.7071067691f, 0.689540565f, 0.6715589762f, 0.6531728506f, 0.6343932748f, 0.6152315736f, 0.5956993103f, 0.5758081675f, 0.5555702448f, 0.534997642f, 0.514102757f, 0.492898196f, 0.4713967443f, 0.449611336f, 0.4275550842f, 0.4052413106f, 0.3826834261f, 0.3598950505f, 0.336889863f, 0.3136817515f, 0.2902846634f, 0.266712755f, 0.2429801822f, 0.2191012353f, 0.1950903237f, 0.1709618866f, 0.1467304677f, 0.1224106774f, 0.09801714122f, 0.07356456667f, 0.04906767607f, 0.02454122901f, 1.0f, 0.9996988177f, 0.9987954497f, 0.9972904325f, 0.9951847196f, 0.9924795628f, 0.9891765118f, 0.9852776527f, 0.9807852507f, 0.975702107f, 0.9700312614f, 0.963776052f, 0.9569403529f, 0.9495281577f, 0.9415440559f, 0.932992816f, 0.9238795042f, 0.9142097831f, 0.903989315f, 0.893224299f, 0.8819212914f, 0.8700869679f, 0.8577286005f, 0.84485358f, 0.8314695954f, 0.8175848126f, 0.8032075167f, 0.7883464098f, 0.7730104327f, 0.7572088242f, 0.7409511209f, 0.724247098f, 0.7071067691f, 0.689540565f, 0.6715589762f, 0.6531728506f, 0.6343932748f, 0.6152315736f, 0.5956993103f, 0.5758081675f, 0.5555702448f, 0.534997642f, 0.514102757f, 0.492898196f, 0.4713967443f, 0.449611336f, 0.4275550842f, 0.4052413106f, 0.3826834261f, 0.3598950505f, 0.336889863f, 0.3136817515f, 0.2902846634f, 0.266712755f, 0.2429801822f, 0.2191012353f, 0.1950903237f, 0.1709618866f, 0.1467304677f, 0.1224106774f, 0.09801714122f, 0.07356456667f, 0.04906767607f, 0.02454122901f, 1.0f, 0.9996988177f, 0.9987954497f, 0.9972904325f, 0.9951847196f, 0.9924795628f, 0.9891765118f, 0.9852776527f, 0.9807852507f, 0.975702107f, 0.9700312614f, 0.963776052f, 0.9569403529f, 0.9495281577f, 0.9415440559f, 0.932992816f, 0.9238795042f, 0.9142097831f, 0.903989315f, 0.893224299f, 0.8819212914f, 0.8700869679f, 0.8577286005f, 0.84485358f, 0.8314695954f, 0.8175848126f, 0.8032075167f, 0.7883464098f, 0.7730104327f, 0.7572088242f, 0.7409511209f, 0.724247098f, 0.7071067691f, 0.689540565f, 0.6715589762f, 0.6531728506f, 0.6343932748f, 0.6152315736f, 0.5956993103f, 0.5758081675f, 0.5555702448f, 0.534997642f, 0.514102757f, 0.492898196f, 0.4713967443f, 0.449611336f, 0.4275550842f, 0.4052413106f, 0.3826834261f, 0.3598950505f, 0.336889863f, 0.3136817515f, 0.2902846634f, 0.266712755f, 0.2429801822f, 0.2191012353f, 0.1950903237f, 0.1709618866f, 0.1467304677f, 0.1224106774f, 0.09801714122f, 0.07356456667f, 0.04906767607f, 0.02454122901f}; +constant float tc24[512] = {1.0f, 0.9996988177f, 0.9987954497f, 0.9972904325f, 0.9951847196f, 0.9924795628f, 0.9891765118f, 0.9852776527f, 0.9807852507f, 0.975702107f, 0.9700312614f, 0.963776052f, 0.9569403529f, 0.9495281577f, 0.9415440559f, 0.932992816f, 0.9238795042f, 0.9142097831f, 0.903989315f, 0.893224299f, 0.8819212914f, 0.8700869679f, 0.8577286005f, 0.84485358f, 0.8314695954f, 0.8175848126f, 0.8032075167f, 0.7883464098f, 0.7730104327f, 0.7572088242f, 0.7409511209f, 0.724247098f, 0.7071067691f, 0.689540565f, 0.6715589762f, 0.6531728506f, 0.6343932748f, 0.6152315736f, 0.5956993103f, 0.5758081675f, 0.5555702448f, 0.534997642f, 0.514102757f, 0.492898196f, 0.4713967443f, 0.449611336f, 0.4275550842f, 0.4052413106f, 0.3826834261f, 0.3598950505f, 0.336889863f, 0.3136817515f, 0.2902846634f, 0.266712755f, 0.2429801822f, 0.2191012353f, 0.1950903237f, 0.1709618866f, 0.1467304677f, 0.1224106774f, 0.09801714122f, 0.07356456667f, 0.04906767607f, 0.02454122901f, 1.0f, 0.9996988177f, 0.9987954497f, 0.9972904325f, 0.9951847196f, 0.9924795628f, 0.9891765118f, 0.9852776527f, 0.9807852507f, 0.975702107f, 0.9700312614f, 0.963776052f, 0.9569403529f, 0.9495281577f, 0.9415440559f, 0.932992816f, 0.9238795042f, 0.9142097831f, 0.903989315f, 0.893224299f, 0.8819212914f, 0.8700869679f, 0.8577286005f, 0.84485358f, 0.8314695954f, 0.8175848126f, 0.8032075167f, 0.7883464098f, 0.7730104327f, 0.7572088242f, 0.7409511209f, 0.724247098f, 0.7071067691f, 0.689540565f, 0.6715589762f, 0.6531728506f, 0.6343932748f, 0.6152315736f, 0.5956993103f, 0.5758081675f, 0.5555702448f, 0.534997642f, 0.514102757f, 0.492898196f, 0.4713967443f, 0.449611336f, 0.4275550842f, 0.4052413106f, 0.3826834261f, 0.3598950505f, 0.336889863f, 0.3136817515f, 0.2902846634f, 0.266712755f, 0.2429801822f, 0.2191012353f, 0.1950903237f, 0.1709618866f, 0.1467304677f, 0.1224106774f, 0.09801714122f, 0.07356456667f, 0.04906767607f, 0.02454122901f, 1.0f, 0.9996988177f, 0.9987954497f, 0.9972904325f, 0.9951847196f, 0.9924795628f, 0.9891765118f, 0.9852776527f, 0.9807852507f, 0.975702107f, 0.9700312614f, 0.963776052f, 0.9569403529f, 0.9495281577f, 0.9415440559f, 0.932992816f, 0.9238795042f, 0.9142097831f, 0.903989315f, 0.893224299f, 0.8819212914f, 0.8700869679f, 0.8577286005f, 0.84485358f, 0.8314695954f, 0.8175848126f, 0.8032075167f, 0.7883464098f, 0.7730104327f, 0.7572088242f, 0.7409511209f, 0.724247098f, 0.7071067691f, 0.689540565f, 0.6715589762f, 0.6531728506f, 0.6343932748f, 0.6152315736f, 0.5956993103f, 0.5758081675f, 0.5555702448f, 0.534997642f, 0.514102757f, 0.492898196f, 0.4713967443f, 0.449611336f, 0.4275550842f, 0.4052413106f, 0.3826834261f, 0.3598950505f, 0.336889863f, 0.3136817515f, 0.2902846634f, 0.266712755f, 0.2429801822f, 0.2191012353f, 0.1950903237f, 0.1709618866f, 0.1467304677f, 0.1224106774f, 0.09801714122f, 0.07356456667f, 0.04906767607f, 0.02454122901f, 1.0f, 0.9996988177f, 0.9987954497f, 0.9972904325f, 0.9951847196f, 0.9924795628f, 0.9891765118f, 0.9852776527f, 0.9807852507f, 0.975702107f, 0.9700312614f, 0.963776052f, 0.9569403529f, 0.9495281577f, 0.9415440559f, 0.932992816f, 0.9238795042f, 0.9142097831f, 0.903989315f, 0.893224299f, 0.8819212914f, 0.8700869679f, 0.8577286005f, 0.84485358f, 0.8314695954f, 0.8175848126f, 0.8032075167f, 0.7883464098f, 0.7730104327f, 0.7572088242f, 0.7409511209f, 0.724247098f, 0.7071067691f, 0.689540565f, 0.6715589762f, 0.6531728506f, 0.6343932748f, 0.6152315736f, 0.5956993103f, 0.5758081675f, 0.5555702448f, 0.534997642f, 0.514102757f, 0.492898196f, 0.4713967443f, 0.449611336f, 0.4275550842f, 0.4052413106f, 0.3826834261f, 0.3598950505f, 0.336889863f, 0.3136817515f, 0.2902846634f, 0.266712755f, 0.2429801822f, 0.2191012353f, 0.1950903237f, 0.1709618866f, 0.1467304677f, 0.1224106774f, 0.09801714122f, 0.07356456667f, 0.04906767607f, 0.02454122901f, 1.0f, 0.9996988177f, 0.9987954497f, 0.9972904325f, 0.9951847196f, 0.9924795628f, 0.9891765118f, 0.9852776527f, 0.9807852507f, 0.975702107f, 0.9700312614f, 0.963776052f, 0.9569403529f, 0.9495281577f, 0.9415440559f, 0.932992816f, 0.9238795042f, 0.9142097831f, 0.903989315f, 0.893224299f, 0.8819212914f, 0.8700869679f, 0.8577286005f, 0.84485358f, 0.8314695954f, 0.8175848126f, 0.8032075167f, 0.7883464098f, 0.7730104327f, 0.7572088242f, 0.7409511209f, 0.724247098f, 0.7071067691f, 0.689540565f, 0.6715589762f, 0.6531728506f, 0.6343932748f, 0.6152315736f, 0.5956993103f, 0.5758081675f, 0.5555702448f, 0.534997642f, 0.514102757f, 0.492898196f, 0.4713967443f, 0.449611336f, 0.4275550842f, 0.4052413106f, 0.3826834261f, 0.3598950505f, 0.336889863f, 0.3136817515f, 0.2902846634f, 0.266712755f, 0.2429801822f, 0.2191012353f, 0.1950903237f, 0.1709618866f, 0.1467304677f, 0.1224106774f, 0.09801714122f, 0.07356456667f, 0.04906767607f, 0.02454122901f, 1.0f, 0.9996988177f, 0.9987954497f, 0.9972904325f, 0.9951847196f, 0.9924795628f, 0.9891765118f, 0.9852776527f, 0.9807852507f, 0.975702107f, 0.9700312614f, 0.963776052f, 0.9569403529f, 0.9495281577f, 0.9415440559f, 0.932992816f, 0.9238795042f, 0.9142097831f, 0.903989315f, 0.893224299f, 0.8819212914f, 0.8700869679f, 0.8577286005f, 0.84485358f, 0.8314695954f, 0.8175848126f, 0.8032075167f, 0.7883464098f, 0.7730104327f, 0.7572088242f, 0.7409511209f, 0.724247098f, 0.7071067691f, 0.689540565f, 0.6715589762f, 0.6531728506f, 0.6343932748f, 0.6152315736f, 0.5956993103f, 0.5758081675f, 0.5555702448f, 0.534997642f, 0.514102757f, 0.492898196f, 0.4713967443f, 0.449611336f, 0.4275550842f, 0.4052413106f, 0.3826834261f, 0.3598950505f, 0.336889863f, 0.3136817515f, 0.2902846634f, 0.266712755f, 0.2429801822f, 0.2191012353f, 0.1950903237f, 0.1709618866f, 0.1467304677f, 0.1224106774f, 0.09801714122f, 0.07356456667f, 0.04906767607f, 0.02454122901f, 1.0f, 0.9996988177f, 0.9987954497f, 0.9972904325f, 0.9951847196f, 0.9924795628f, 0.9891765118f, 0.9852776527f, 0.9807852507f, 0.975702107f, 0.9700312614f, 0.963776052f, 0.9569403529f, 0.9495281577f, 0.9415440559f, 0.932992816f, 0.9238795042f, 0.9142097831f, 0.903989315f, 0.893224299f, 0.8819212914f, 0.8700869679f, 0.8577286005f, 0.84485358f, 0.8314695954f, 0.8175848126f, 0.8032075167f, 0.7883464098f, 0.7730104327f, 0.7572088242f, 0.7409511209f, 0.724247098f, 0.7071067691f, 0.689540565f, 0.6715589762f, 0.6531728506f, 0.6343932748f, 0.6152315736f, 0.5956993103f, 0.5758081675f, 0.5555702448f, 0.534997642f, 0.514102757f, 0.492898196f, 0.4713967443f, 0.449611336f, 0.4275550842f, 0.4052413106f, 0.3826834261f, 0.3598950505f, 0.336889863f, 0.3136817515f, 0.2902846634f, 0.266712755f, 0.2429801822f, 0.2191012353f, 0.1950903237f, 0.1709618866f, 0.1467304677f, 0.1224106774f, 0.09801714122f, 0.07356456667f, 0.04906767607f, 0.02454122901f, 1.0f, 0.9996988177f, 0.9987954497f, 0.9972904325f, 0.9951847196f, 0.9924795628f, 0.9891765118f, 0.9852776527f, 0.9807852507f, 0.975702107f, 0.9700312614f, 0.963776052f, 0.9569403529f, 0.9495281577f, 0.9415440559f, 0.932992816f, 0.9238795042f, 0.9142097831f, 0.903989315f, 0.893224299f, 0.8819212914f, 0.8700869679f, 0.8577286005f, 0.84485358f, 0.8314695954f, 0.8175848126f, 0.8032075167f, 0.7883464098f, 0.7730104327f, 0.7572088242f, 0.7409511209f, 0.724247098f, 0.7071067691f, 0.689540565f, 0.6715589762f, 0.6531728506f, 0.6343932748f, 0.6152315736f, 0.5956993103f, 0.5758081675f, 0.5555702448f, 0.534997642f, 0.514102757f, 0.492898196f, 0.4713967443f, 0.449611336f, 0.4275550842f, 0.4052413106f, 0.3826834261f, 0.3598950505f, 0.336889863f, 0.3136817515f, 0.2902846634f, 0.266712755f, 0.2429801822f, 0.2191012353f, 0.1950903237f, 0.1709618866f, 0.1467304677f, 0.1224106774f, 0.09801714122f, 0.07356456667f, 0.04906767607f, 0.02454122901f}; +constant float tc22[512] = {1.0f, 0.9972904325f, 0.9891765118f, 0.975702107f, 0.9569403529f, 0.932992816f, 0.903989315f, 0.8700869679f, 0.8314695954f, 0.7883464098f, 0.7409511209f, 0.689540565f, 0.6343932748f, 0.5758081675f, 0.514102757f, 0.449611336f, 0.3826834261f, 0.3136817515f, 0.2429801822f, 0.1709618866f, 0.09801714122f, 0.02454122901f, -0.04906767607f, -0.1224106774f, -0.1950903237f, -0.266712755f, -0.336889863f, -0.4052413106f, -0.4713967443f, -0.534997642f, -0.5956993103f, -0.6531728506f, -0.7071067691f, -0.7572088242f, -0.8032075167f, -0.84485358f, -0.8819212914f, -0.9142097831f, -0.9415440559f, -0.963776052f, -0.9807852507f, -0.9924795628f, -0.9987954497f, -0.9996988177f, -0.9951847196f, -0.9852776527f, -0.9700312614f, -0.9495281577f, -0.9238795042f, -0.893224299f, -0.8577286005f, -0.8175848126f, -0.7730104327f, -0.724247098f, -0.6715589762f, -0.6152315736f, -0.5555702448f, -0.492898196f, -0.4275550842f, -0.3598950505f, -0.2902846634f, -0.2191012353f, -0.1467304677f, -0.07356456667f, 1.0f, 0.9972904325f, 0.9891765118f, 0.975702107f, 0.9569403529f, 0.932992816f, 0.903989315f, 0.8700869679f, 0.8314695954f, 0.7883464098f, 0.7409511209f, 0.689540565f, 0.6343932748f, 0.5758081675f, 0.514102757f, 0.449611336f, 0.3826834261f, 0.3136817515f, 0.2429801822f, 0.1709618866f, 0.09801714122f, 0.02454122901f, -0.04906767607f, -0.1224106774f, -0.1950903237f, -0.266712755f, -0.336889863f, -0.4052413106f, -0.4713967443f, -0.534997642f, -0.5956993103f, -0.6531728506f, -0.7071067691f, -0.7572088242f, -0.8032075167f, -0.84485358f, -0.8819212914f, -0.9142097831f, -0.9415440559f, -0.963776052f, -0.9807852507f, -0.9924795628f, -0.9987954497f, -0.9996988177f, -0.9951847196f, -0.9852776527f, -0.9700312614f, -0.9495281577f, -0.9238795042f, -0.893224299f, -0.8577286005f, -0.8175848126f, -0.7730104327f, -0.724247098f, -0.6715589762f, -0.6152315736f, -0.5555702448f, -0.492898196f, -0.4275550842f, -0.3598950505f, -0.2902846634f, -0.2191012353f, -0.1467304677f, -0.07356456667f, 1.0f, 0.9972904325f, 0.9891765118f, 0.975702107f, 0.9569403529f, 0.932992816f, 0.903989315f, 0.8700869679f, 0.8314695954f, 0.7883464098f, 0.7409511209f, 0.689540565f, 0.6343932748f, 0.5758081675f, 0.514102757f, 0.449611336f, 0.3826834261f, 0.3136817515f, 0.2429801822f, 0.1709618866f, 0.09801714122f, 0.02454122901f, -0.04906767607f, -0.1224106774f, -0.1950903237f, -0.266712755f, -0.336889863f, -0.4052413106f, -0.4713967443f, -0.534997642f, -0.5956993103f, -0.6531728506f, -0.7071067691f, -0.7572088242f, -0.8032075167f, -0.84485358f, -0.8819212914f, -0.9142097831f, -0.9415440559f, -0.963776052f, -0.9807852507f, -0.9924795628f, -0.9987954497f, -0.9996988177f, -0.9951847196f, -0.9852776527f, -0.9700312614f, -0.9495281577f, -0.9238795042f, -0.893224299f, -0.8577286005f, -0.8175848126f, -0.7730104327f, -0.724247098f, -0.6715589762f, -0.6152315736f, -0.5555702448f, -0.492898196f, -0.4275550842f, -0.3598950505f, -0.2902846634f, -0.2191012353f, -0.1467304677f, -0.07356456667f, 1.0f, 0.9972904325f, 0.9891765118f, 0.975702107f, 0.9569403529f, 0.932992816f, 0.903989315f, 0.8700869679f, 0.8314695954f, 0.7883464098f, 0.7409511209f, 0.689540565f, 0.6343932748f, 0.5758081675f, 0.514102757f, 0.449611336f, 0.3826834261f, 0.3136817515f, 0.2429801822f, 0.1709618866f, 0.09801714122f, 0.02454122901f, -0.04906767607f, -0.1224106774f, -0.1950903237f, -0.266712755f, -0.336889863f, -0.4052413106f, -0.4713967443f, -0.534997642f, -0.5956993103f, -0.6531728506f, -0.7071067691f, -0.7572088242f, -0.8032075167f, -0.84485358f, -0.8819212914f, -0.9142097831f, -0.9415440559f, -0.963776052f, -0.9807852507f, -0.9924795628f, -0.9987954497f, -0.9996988177f, -0.9951847196f, -0.9852776527f, -0.9700312614f, -0.9495281577f, -0.9238795042f, -0.893224299f, -0.8577286005f, -0.8175848126f, -0.7730104327f, -0.724247098f, -0.6715589762f, -0.6152315736f, -0.5555702448f, -0.492898196f, -0.4275550842f, -0.3598950505f, -0.2902846634f, -0.2191012353f, -0.1467304677f, -0.07356456667f, 1.0f, 0.9972904325f, 0.9891765118f, 0.975702107f, 0.9569403529f, 0.932992816f, 0.903989315f, 0.8700869679f, 0.8314695954f, 0.7883464098f, 0.7409511209f, 0.689540565f, 0.6343932748f, 0.5758081675f, 0.514102757f, 0.449611336f, 0.3826834261f, 0.3136817515f, 0.2429801822f, 0.1709618866f, 0.09801714122f, 0.02454122901f, -0.04906767607f, -0.1224106774f, -0.1950903237f, -0.266712755f, -0.336889863f, -0.4052413106f, -0.4713967443f, -0.534997642f, -0.5956993103f, -0.6531728506f, -0.7071067691f, -0.7572088242f, -0.8032075167f, -0.84485358f, -0.8819212914f, -0.9142097831f, -0.9415440559f, -0.963776052f, -0.9807852507f, -0.9924795628f, -0.9987954497f, -0.9996988177f, -0.9951847196f, -0.9852776527f, -0.9700312614f, -0.9495281577f, -0.9238795042f, -0.893224299f, -0.8577286005f, -0.8175848126f, -0.7730104327f, -0.724247098f, -0.6715589762f, -0.6152315736f, -0.5555702448f, -0.492898196f, -0.4275550842f, -0.3598950505f, -0.2902846634f, -0.2191012353f, -0.1467304677f, -0.07356456667f, 1.0f, 0.9972904325f, 0.9891765118f, 0.975702107f, 0.9569403529f, 0.932992816f, 0.903989315f, 0.8700869679f, 0.8314695954f, 0.7883464098f, 0.7409511209f, 0.689540565f, 0.6343932748f, 0.5758081675f, 0.514102757f, 0.449611336f, 0.3826834261f, 0.3136817515f, 0.2429801822f, 0.1709618866f, 0.09801714122f, 0.02454122901f, -0.04906767607f, -0.1224106774f, -0.1950903237f, -0.266712755f, -0.336889863f, -0.4052413106f, -0.4713967443f, -0.534997642f, -0.5956993103f, -0.6531728506f, -0.7071067691f, -0.7572088242f, -0.8032075167f, -0.84485358f, -0.8819212914f, -0.9142097831f, -0.9415440559f, -0.963776052f, -0.9807852507f, -0.9924795628f, -0.9987954497f, -0.9996988177f, -0.9951847196f, -0.9852776527f, -0.9700312614f, -0.9495281577f, -0.9238795042f, -0.893224299f, -0.8577286005f, -0.8175848126f, -0.7730104327f, -0.724247098f, -0.6715589762f, -0.6152315736f, -0.5555702448f, -0.492898196f, -0.4275550842f, -0.3598950505f, -0.2902846634f, -0.2191012353f, -0.1467304677f, -0.07356456667f, 1.0f, 0.9972904325f, 0.9891765118f, 0.975702107f, 0.9569403529f, 0.932992816f, 0.903989315f, 0.8700869679f, 0.8314695954f, 0.7883464098f, 0.7409511209f, 0.689540565f, 0.6343932748f, 0.5758081675f, 0.514102757f, 0.449611336f, 0.3826834261f, 0.3136817515f, 0.2429801822f, 0.1709618866f, 0.09801714122f, 0.02454122901f, -0.04906767607f, -0.1224106774f, -0.1950903237f, -0.266712755f, -0.336889863f, -0.4052413106f, -0.4713967443f, -0.534997642f, -0.5956993103f, -0.6531728506f, -0.7071067691f, -0.7572088242f, -0.8032075167f, -0.84485358f, -0.8819212914f, -0.9142097831f, -0.9415440559f, -0.963776052f, -0.9807852507f, -0.9924795628f, -0.9987954497f, -0.9996988177f, -0.9951847196f, -0.9852776527f, -0.9700312614f, -0.9495281577f, -0.9238795042f, -0.893224299f, -0.8577286005f, -0.8175848126f, -0.7730104327f, -0.724247098f, -0.6715589762f, -0.6152315736f, -0.5555702448f, -0.492898196f, -0.4275550842f, -0.3598950505f, -0.2902846634f, -0.2191012353f, -0.1467304677f, -0.07356456667f, 1.0f, 0.9972904325f, 0.9891765118f, 0.975702107f, 0.9569403529f, 0.932992816f, 0.903989315f, 0.8700869679f, 0.8314695954f, 0.7883464098f, 0.7409511209f, 0.689540565f, 0.6343932748f, 0.5758081675f, 0.514102757f, 0.449611336f, 0.3826834261f, 0.3136817515f, 0.2429801822f, 0.1709618866f, 0.09801714122f, 0.02454122901f, -0.04906767607f, -0.1224106774f, -0.1950903237f, -0.266712755f, -0.336889863f, -0.4052413106f, -0.4713967443f, -0.534997642f, -0.5956993103f, -0.6531728506f, -0.7071067691f, -0.7572088242f, -0.8032075167f, -0.84485358f, -0.8819212914f, -0.9142097831f, -0.9415440559f, -0.963776052f, -0.9807852507f, -0.9924795628f, -0.9987954497f, -0.9996988177f, -0.9951847196f, -0.9852776527f, -0.9700312614f, -0.9495281577f, -0.9238795042f, -0.893224299f, -0.8577286005f, -0.8175848126f, -0.7730104327f, -0.724247098f, -0.6715589762f, -0.6152315736f, -0.5555702448f, -0.492898196f, -0.4275550842f, -0.3598950505f, -0.2902846634f, -0.2191012353f, -0.1467304677f, -0.07356456667f}; +constant float tc25[512] = {1.0f, 0.9972904325f, 0.9891765118f, 0.975702107f, 0.9569403529f, 0.932992816f, 0.903989315f, 0.8700869679f, 0.8314695954f, 0.7883464098f, 0.7409511209f, 0.689540565f, 0.6343932748f, 0.5758081675f, 0.514102757f, 0.449611336f, 0.3826834261f, 0.3136817515f, 0.2429801822f, 0.1709618866f, 0.09801714122f, 0.02454122901f, -0.04906767607f, -0.1224106774f, -0.1950903237f, -0.266712755f, -0.336889863f, -0.4052413106f, -0.4713967443f, -0.534997642f, -0.5956993103f, -0.6531728506f, -0.7071067691f, -0.7572088242f, -0.8032075167f, -0.84485358f, -0.8819212914f, -0.9142097831f, -0.9415440559f, -0.963776052f, -0.9807852507f, -0.9924795628f, -0.9987954497f, -0.9996988177f, -0.9951847196f, -0.9852776527f, -0.9700312614f, -0.9495281577f, -0.9238795042f, -0.893224299f, -0.8577286005f, -0.8175848126f, -0.7730104327f, -0.724247098f, -0.6715589762f, -0.6152315736f, -0.5555702448f, -0.492898196f, -0.4275550842f, -0.3598950505f, -0.2902846634f, -0.2191012353f, -0.1467304677f, -0.07356456667f, 1.0f, 0.9972904325f, 0.9891765118f, 0.975702107f, 0.9569403529f, 0.932992816f, 0.903989315f, 0.8700869679f, 0.8314695954f, 0.7883464098f, 0.7409511209f, 0.689540565f, 0.6343932748f, 0.5758081675f, 0.514102757f, 0.449611336f, 0.3826834261f, 0.3136817515f, 0.2429801822f, 0.1709618866f, 0.09801714122f, 0.02454122901f, -0.04906767607f, -0.1224106774f, -0.1950903237f, -0.266712755f, -0.336889863f, -0.4052413106f, -0.4713967443f, -0.534997642f, -0.5956993103f, -0.6531728506f, -0.7071067691f, -0.7572088242f, -0.8032075167f, -0.84485358f, -0.8819212914f, -0.9142097831f, -0.9415440559f, -0.963776052f, -0.9807852507f, -0.9924795628f, -0.9987954497f, -0.9996988177f, -0.9951847196f, -0.9852776527f, -0.9700312614f, -0.9495281577f, -0.9238795042f, -0.893224299f, -0.8577286005f, -0.8175848126f, -0.7730104327f, -0.724247098f, -0.6715589762f, -0.6152315736f, -0.5555702448f, -0.492898196f, -0.4275550842f, -0.3598950505f, -0.2902846634f, -0.2191012353f, -0.1467304677f, -0.07356456667f, 1.0f, 0.9972904325f, 0.9891765118f, 0.975702107f, 0.9569403529f, 0.932992816f, 0.903989315f, 0.8700869679f, 0.8314695954f, 0.7883464098f, 0.7409511209f, 0.689540565f, 0.6343932748f, 0.5758081675f, 0.514102757f, 0.449611336f, 0.3826834261f, 0.3136817515f, 0.2429801822f, 0.1709618866f, 0.09801714122f, 0.02454122901f, -0.04906767607f, -0.1224106774f, -0.1950903237f, -0.266712755f, -0.336889863f, -0.4052413106f, -0.4713967443f, -0.534997642f, -0.5956993103f, -0.6531728506f, -0.7071067691f, -0.7572088242f, -0.8032075167f, -0.84485358f, -0.8819212914f, -0.9142097831f, -0.9415440559f, -0.963776052f, -0.9807852507f, -0.9924795628f, -0.9987954497f, -0.9996988177f, -0.9951847196f, -0.9852776527f, -0.9700312614f, -0.9495281577f, -0.9238795042f, -0.893224299f, -0.8577286005f, -0.8175848126f, -0.7730104327f, -0.724247098f, -0.6715589762f, -0.6152315736f, -0.5555702448f, -0.492898196f, -0.4275550842f, -0.3598950505f, -0.2902846634f, -0.2191012353f, -0.1467304677f, -0.07356456667f, 1.0f, 0.9972904325f, 0.9891765118f, 0.975702107f, 0.9569403529f, 0.932992816f, 0.903989315f, 0.8700869679f, 0.8314695954f, 0.7883464098f, 0.7409511209f, 0.689540565f, 0.6343932748f, 0.5758081675f, 0.514102757f, 0.449611336f, 0.3826834261f, 0.3136817515f, 0.2429801822f, 0.1709618866f, 0.09801714122f, 0.02454122901f, -0.04906767607f, -0.1224106774f, -0.1950903237f, -0.266712755f, -0.336889863f, -0.4052413106f, -0.4713967443f, -0.534997642f, -0.5956993103f, -0.6531728506f, -0.7071067691f, -0.7572088242f, -0.8032075167f, -0.84485358f, -0.8819212914f, -0.9142097831f, -0.9415440559f, -0.963776052f, -0.9807852507f, -0.9924795628f, -0.9987954497f, -0.9996988177f, -0.9951847196f, -0.9852776527f, -0.9700312614f, -0.9495281577f, -0.9238795042f, -0.893224299f, -0.8577286005f, -0.8175848126f, -0.7730104327f, -0.724247098f, -0.6715589762f, -0.6152315736f, -0.5555702448f, -0.492898196f, -0.4275550842f, -0.3598950505f, -0.2902846634f, -0.2191012353f, -0.1467304677f, -0.07356456667f, 1.0f, 0.9972904325f, 0.9891765118f, 0.975702107f, 0.9569403529f, 0.932992816f, 0.903989315f, 0.8700869679f, 0.8314695954f, 0.7883464098f, 0.7409511209f, 0.689540565f, 0.6343932748f, 0.5758081675f, 0.514102757f, 0.449611336f, 0.3826834261f, 0.3136817515f, 0.2429801822f, 0.1709618866f, 0.09801714122f, 0.02454122901f, -0.04906767607f, -0.1224106774f, -0.1950903237f, -0.266712755f, -0.336889863f, -0.4052413106f, -0.4713967443f, -0.534997642f, -0.5956993103f, -0.6531728506f, -0.7071067691f, -0.7572088242f, -0.8032075167f, -0.84485358f, -0.8819212914f, -0.9142097831f, -0.9415440559f, -0.963776052f, -0.9807852507f, -0.9924795628f, -0.9987954497f, -0.9996988177f, -0.9951847196f, -0.9852776527f, -0.9700312614f, -0.9495281577f, -0.9238795042f, -0.893224299f, -0.8577286005f, -0.8175848126f, -0.7730104327f, -0.724247098f, -0.6715589762f, -0.6152315736f, -0.5555702448f, -0.492898196f, -0.4275550842f, -0.3598950505f, -0.2902846634f, -0.2191012353f, -0.1467304677f, -0.07356456667f, 1.0f, 0.9972904325f, 0.9891765118f, 0.975702107f, 0.9569403529f, 0.932992816f, 0.903989315f, 0.8700869679f, 0.8314695954f, 0.7883464098f, 0.7409511209f, 0.689540565f, 0.6343932748f, 0.5758081675f, 0.514102757f, 0.449611336f, 0.3826834261f, 0.3136817515f, 0.2429801822f, 0.1709618866f, 0.09801714122f, 0.02454122901f, -0.04906767607f, -0.1224106774f, -0.1950903237f, -0.266712755f, -0.336889863f, -0.4052413106f, -0.4713967443f, -0.534997642f, -0.5956993103f, -0.6531728506f, -0.7071067691f, -0.7572088242f, -0.8032075167f, -0.84485358f, -0.8819212914f, -0.9142097831f, -0.9415440559f, -0.963776052f, -0.9807852507f, -0.9924795628f, -0.9987954497f, -0.9996988177f, -0.9951847196f, -0.9852776527f, -0.9700312614f, -0.9495281577f, -0.9238795042f, -0.893224299f, -0.8577286005f, -0.8175848126f, -0.7730104327f, -0.724247098f, -0.6715589762f, -0.6152315736f, -0.5555702448f, -0.492898196f, -0.4275550842f, -0.3598950505f, -0.2902846634f, -0.2191012353f, -0.1467304677f, -0.07356456667f, 1.0f, 0.9972904325f, 0.9891765118f, 0.975702107f, 0.9569403529f, 0.932992816f, 0.903989315f, 0.8700869679f, 0.8314695954f, 0.7883464098f, 0.7409511209f, 0.689540565f, 0.6343932748f, 0.5758081675f, 0.514102757f, 0.449611336f, 0.3826834261f, 0.3136817515f, 0.2429801822f, 0.1709618866f, 0.09801714122f, 0.02454122901f, -0.04906767607f, -0.1224106774f, -0.1950903237f, -0.266712755f, -0.336889863f, -0.4052413106f, -0.4713967443f, -0.534997642f, -0.5956993103f, -0.6531728506f, -0.7071067691f, -0.7572088242f, -0.8032075167f, -0.84485358f, -0.8819212914f, -0.9142097831f, -0.9415440559f, -0.963776052f, -0.9807852507f, -0.9924795628f, -0.9987954497f, -0.9996988177f, -0.9951847196f, -0.9852776527f, -0.9700312614f, -0.9495281577f, -0.9238795042f, -0.893224299f, -0.8577286005f, -0.8175848126f, -0.7730104327f, -0.724247098f, -0.6715589762f, -0.6152315736f, -0.5555702448f, -0.492898196f, -0.4275550842f, -0.3598950505f, -0.2902846634f, -0.2191012353f, -0.1467304677f, -0.07356456667f, 1.0f, 0.9972904325f, 0.9891765118f, 0.975702107f, 0.9569403529f, 0.932992816f, 0.903989315f, 0.8700869679f, 0.8314695954f, 0.7883464098f, 0.7409511209f, 0.689540565f, 0.6343932748f, 0.5758081675f, 0.514102757f, 0.449611336f, 0.3826834261f, 0.3136817515f, 0.2429801822f, 0.1709618866f, 0.09801714122f, 0.02454122901f, -0.04906767607f, -0.1224106774f, -0.1950903237f, -0.266712755f, -0.336889863f, -0.4052413106f, -0.4713967443f, -0.534997642f, -0.5956993103f, -0.6531728506f, -0.7071067691f, -0.7572088242f, -0.8032075167f, -0.84485358f, -0.8819212914f, -0.9142097831f, -0.9415440559f, -0.963776052f, -0.9807852507f, -0.9924795628f, -0.9987954497f, -0.9996988177f, -0.9951847196f, -0.9852776527f, -0.9700312614f, -0.9495281577f, -0.9238795042f, -0.893224299f, -0.8577286005f, -0.8175848126f, -0.7730104327f, -0.724247098f, -0.6715589762f, -0.6152315736f, -0.5555702448f, -0.492898196f, -0.4275550842f, -0.3598950505f, -0.2902846634f, -0.2191012353f, -0.1467304677f, -0.07356456667f}; +constant float tc30[512] = {1.0f, 0.9807852507f, 0.9238795042f, 0.8314695954f, 0.7071067691f, 0.5555702448f, 0.3826834261f, 0.1950903237f, 6.123234263e-17f, -0.1950903237f, -0.3826834261f, -0.5555702448f, -0.7071067691f, -0.8314695954f, -0.9238795042f, -0.9807852507f, 1.0f, 0.9807852507f, 0.9238795042f, 0.8314695954f, 0.7071067691f, 0.5555702448f, 0.3826834261f, 0.1950903237f, 6.123234263e-17f, -0.1950903237f, -0.3826834261f, -0.5555702448f, -0.7071067691f, -0.8314695954f, -0.9238795042f, -0.9807852507f, 1.0f, 0.9807852507f, 0.9238795042f, 0.8314695954f, 0.7071067691f, 0.5555702448f, 0.3826834261f, 0.1950903237f, 6.123234263e-17f, -0.1950903237f, -0.3826834261f, -0.5555702448f, -0.7071067691f, -0.8314695954f, -0.9238795042f, -0.9807852507f, 1.0f, 0.9807852507f, 0.9238795042f, 0.8314695954f, 0.7071067691f, 0.5555702448f, 0.3826834261f, 0.1950903237f, 6.123234263e-17f, -0.1950903237f, -0.3826834261f, -0.5555702448f, -0.7071067691f, -0.8314695954f, -0.9238795042f, -0.9807852507f, 1.0f, 0.9807852507f, 0.9238795042f, 0.8314695954f, 0.7071067691f, 0.5555702448f, 0.3826834261f, 0.1950903237f, 6.123234263e-17f, -0.1950903237f, -0.3826834261f, -0.5555702448f, -0.7071067691f, -0.8314695954f, -0.9238795042f, -0.9807852507f, 1.0f, 0.9807852507f, 0.9238795042f, 0.8314695954f, 0.7071067691f, 0.5555702448f, 0.3826834261f, 0.1950903237f, 6.123234263e-17f, -0.1950903237f, -0.3826834261f, -0.5555702448f, -0.7071067691f, -0.8314695954f, -0.9238795042f, -0.9807852507f, 1.0f, 0.9807852507f, 0.9238795042f, 0.8314695954f, 0.7071067691f, 0.5555702448f, 0.3826834261f, 0.1950903237f, 6.123234263e-17f, -0.1950903237f, -0.3826834261f, -0.5555702448f, -0.7071067691f, -0.8314695954f, -0.9238795042f, -0.9807852507f, 1.0f, 0.9807852507f, 0.9238795042f, 0.8314695954f, 0.7071067691f, 0.5555702448f, 0.3826834261f, 0.1950903237f, 6.123234263e-17f, -0.1950903237f, -0.3826834261f, -0.5555702448f, -0.7071067691f, -0.8314695954f, -0.9238795042f, -0.9807852507f, 1.0f, 0.9807852507f, 0.9238795042f, 0.8314695954f, 0.7071067691f, 0.5555702448f, 0.3826834261f, 0.1950903237f, 6.123234263e-17f, -0.1950903237f, -0.3826834261f, -0.5555702448f, -0.7071067691f, -0.8314695954f, -0.9238795042f, -0.9807852507f, 1.0f, 0.9807852507f, 0.9238795042f, 0.8314695954f, 0.7071067691f, 0.5555702448f, 0.3826834261f, 0.1950903237f, 6.123234263e-17f, -0.1950903237f, -0.3826834261f, -0.5555702448f, -0.7071067691f, -0.8314695954f, -0.9238795042f, -0.9807852507f, 1.0f, 0.9807852507f, 0.9238795042f, 0.8314695954f, 0.7071067691f, 0.5555702448f, 0.3826834261f, 0.1950903237f, 6.123234263e-17f, -0.1950903237f, -0.3826834261f, -0.5555702448f, -0.7071067691f, -0.8314695954f, -0.9238795042f, -0.9807852507f, 1.0f, 0.9807852507f, 0.9238795042f, 0.8314695954f, 0.7071067691f, 0.5555702448f, 0.3826834261f, 0.1950903237f, 6.123234263e-17f, -0.1950903237f, -0.3826834261f, -0.5555702448f, -0.7071067691f, -0.8314695954f, -0.9238795042f, -0.9807852507f, 1.0f, 0.9807852507f, 0.9238795042f, 0.8314695954f, 0.7071067691f, 0.5555702448f, 0.3826834261f, 0.1950903237f, 6.123234263e-17f, -0.1950903237f, -0.3826834261f, -0.5555702448f, -0.7071067691f, -0.8314695954f, -0.9238795042f, -0.9807852507f, 1.0f, 0.9807852507f, 0.9238795042f, 0.8314695954f, 0.7071067691f, 0.5555702448f, 0.3826834261f, 0.1950903237f, 6.123234263e-17f, -0.1950903237f, -0.3826834261f, -0.5555702448f, -0.7071067691f, -0.8314695954f, -0.9238795042f, -0.9807852507f, 1.0f, 0.9807852507f, 0.9238795042f, 0.8314695954f, 0.7071067691f, 0.5555702448f, 0.3826834261f, 0.1950903237f, 6.123234263e-17f, -0.1950903237f, -0.3826834261f, -0.5555702448f, -0.7071067691f, -0.8314695954f, -0.9238795042f, -0.9807852507f, 1.0f, 0.9807852507f, 0.9238795042f, 0.8314695954f, 0.7071067691f, 0.5555702448f, 0.3826834261f, 0.1950903237f, 6.123234263e-17f, -0.1950903237f, -0.3826834261f, -0.5555702448f, -0.7071067691f, -0.8314695954f, -0.9238795042f, -0.9807852507f, 1.0f, 0.9807852507f, 0.9238795042f, 0.8314695954f, 0.7071067691f, 0.5555702448f, 0.3826834261f, 0.1950903237f, 6.123234263e-17f, -0.1950903237f, -0.3826834261f, -0.5555702448f, -0.7071067691f, -0.8314695954f, -0.9238795042f, -0.9807852507f, 1.0f, 0.9807852507f, 0.9238795042f, 0.8314695954f, 0.7071067691f, 0.5555702448f, 0.3826834261f, 0.1950903237f, 6.123234263e-17f, -0.1950903237f, -0.3826834261f, -0.5555702448f, -0.7071067691f, -0.8314695954f, -0.9238795042f, -0.9807852507f, 1.0f, 0.9807852507f, 0.9238795042f, 0.8314695954f, 0.7071067691f, 0.5555702448f, 0.3826834261f, 0.1950903237f, 6.123234263e-17f, -0.1950903237f, -0.3826834261f, -0.5555702448f, -0.7071067691f, -0.8314695954f, -0.9238795042f, -0.9807852507f, 1.0f, 0.9807852507f, 0.9238795042f, 0.8314695954f, 0.7071067691f, 0.5555702448f, 0.3826834261f, 0.1950903237f, 6.123234263e-17f, -0.1950903237f, -0.3826834261f, -0.5555702448f, -0.7071067691f, -0.8314695954f, -0.9238795042f, -0.9807852507f, 1.0f, 0.9807852507f, 0.9238795042f, 0.8314695954f, 0.7071067691f, 0.5555702448f, 0.3826834261f, 0.1950903237f, 6.123234263e-17f, -0.1950903237f, -0.3826834261f, -0.5555702448f, -0.7071067691f, -0.8314695954f, -0.9238795042f, -0.9807852507f, 1.0f, 0.9807852507f, 0.9238795042f, 0.8314695954f, 0.7071067691f, 0.5555702448f, 0.3826834261f, 0.1950903237f, 6.123234263e-17f, -0.1950903237f, -0.3826834261f, -0.5555702448f, -0.7071067691f, -0.8314695954f, -0.9238795042f, -0.9807852507f, 1.0f, 0.9807852507f, 0.9238795042f, 0.8314695954f, 0.7071067691f, 0.5555702448f, 0.3826834261f, 0.1950903237f, 6.123234263e-17f, -0.1950903237f, -0.3826834261f, -0.5555702448f, -0.7071067691f, -0.8314695954f, -0.9238795042f, -0.9807852507f, 1.0f, 0.9807852507f, 0.9238795042f, 0.8314695954f, 0.7071067691f, 0.5555702448f, 0.3826834261f, 0.1950903237f, 6.123234263e-17f, -0.1950903237f, -0.3826834261f, -0.5555702448f, -0.7071067691f, -0.8314695954f, -0.9238795042f, -0.9807852507f, 1.0f, 0.9807852507f, 0.9238795042f, 0.8314695954f, 0.7071067691f, 0.5555702448f, 0.3826834261f, 0.1950903237f, 6.123234263e-17f, -0.1950903237f, -0.3826834261f, -0.5555702448f, -0.7071067691f, -0.8314695954f, -0.9238795042f, -0.9807852507f, 1.0f, 0.9807852507f, 0.9238795042f, 0.8314695954f, 0.7071067691f, 0.5555702448f, 0.3826834261f, 0.1950903237f, 6.123234263e-17f, -0.1950903237f, -0.3826834261f, -0.5555702448f, -0.7071067691f, -0.8314695954f, -0.9238795042f, -0.9807852507f, 1.0f, 0.9807852507f, 0.9238795042f, 0.8314695954f, 0.7071067691f, 0.5555702448f, 0.3826834261f, 0.1950903237f, 6.123234263e-17f, -0.1950903237f, -0.3826834261f, -0.5555702448f, -0.7071067691f, -0.8314695954f, -0.9238795042f, -0.9807852507f, 1.0f, 0.9807852507f, 0.9238795042f, 0.8314695954f, 0.7071067691f, 0.5555702448f, 0.3826834261f, 0.1950903237f, 6.123234263e-17f, -0.1950903237f, -0.3826834261f, -0.5555702448f, -0.7071067691f, -0.8314695954f, -0.9238795042f, -0.9807852507f, 1.0f, 0.9807852507f, 0.9238795042f, 0.8314695954f, 0.7071067691f, 0.5555702448f, 0.3826834261f, 0.1950903237f, 6.123234263e-17f, -0.1950903237f, -0.3826834261f, -0.5555702448f, -0.7071067691f, -0.8314695954f, -0.9238795042f, -0.9807852507f, 1.0f, 0.9807852507f, 0.9238795042f, 0.8314695954f, 0.7071067691f, 0.5555702448f, 0.3826834261f, 0.1950903237f, 6.123234263e-17f, -0.1950903237f, -0.3826834261f, -0.5555702448f, -0.7071067691f, -0.8314695954f, -0.9238795042f, -0.9807852507f, 1.0f, 0.9807852507f, 0.9238795042f, 0.8314695954f, 0.7071067691f, 0.5555702448f, 0.3826834261f, 0.1950903237f, 6.123234263e-17f, -0.1950903237f, -0.3826834261f, -0.5555702448f, -0.7071067691f, -0.8314695954f, -0.9238795042f, -0.9807852507f, 1.0f, 0.9807852507f, 0.9238795042f, 0.8314695954f, 0.7071067691f, 0.5555702448f, 0.3826834261f, 0.1950903237f, 6.123234263e-17f, -0.1950903237f, -0.3826834261f, -0.5555702448f, -0.7071067691f, -0.8314695954f, -0.9238795042f, -0.9807852507f}; +constant float tc33[512] = {1.0f, 0.9807852507f, 0.9238795042f, 0.8314695954f, 0.7071067691f, 0.5555702448f, 0.3826834261f, 0.1950903237f, 6.123234263e-17f, -0.1950903237f, -0.3826834261f, -0.5555702448f, -0.7071067691f, -0.8314695954f, -0.9238795042f, -0.9807852507f, 1.0f, 0.9807852507f, 0.9238795042f, 0.8314695954f, 0.7071067691f, 0.5555702448f, 0.3826834261f, 0.1950903237f, 6.123234263e-17f, -0.1950903237f, -0.3826834261f, -0.5555702448f, -0.7071067691f, -0.8314695954f, -0.9238795042f, -0.9807852507f, 1.0f, 0.9807852507f, 0.9238795042f, 0.8314695954f, 0.7071067691f, 0.5555702448f, 0.3826834261f, 0.1950903237f, 6.123234263e-17f, -0.1950903237f, -0.3826834261f, -0.5555702448f, -0.7071067691f, -0.8314695954f, -0.9238795042f, -0.9807852507f, 1.0f, 0.9807852507f, 0.9238795042f, 0.8314695954f, 0.7071067691f, 0.5555702448f, 0.3826834261f, 0.1950903237f, 6.123234263e-17f, -0.1950903237f, -0.3826834261f, -0.5555702448f, -0.7071067691f, -0.8314695954f, -0.9238795042f, -0.9807852507f, 1.0f, 0.9807852507f, 0.9238795042f, 0.8314695954f, 0.7071067691f, 0.5555702448f, 0.3826834261f, 0.1950903237f, 6.123234263e-17f, -0.1950903237f, -0.3826834261f, -0.5555702448f, -0.7071067691f, -0.8314695954f, -0.9238795042f, -0.9807852507f, 1.0f, 0.9807852507f, 0.9238795042f, 0.8314695954f, 0.7071067691f, 0.5555702448f, 0.3826834261f, 0.1950903237f, 6.123234263e-17f, -0.1950903237f, -0.3826834261f, -0.5555702448f, -0.7071067691f, -0.8314695954f, -0.9238795042f, -0.9807852507f, 1.0f, 0.9807852507f, 0.9238795042f, 0.8314695954f, 0.7071067691f, 0.5555702448f, 0.3826834261f, 0.1950903237f, 6.123234263e-17f, -0.1950903237f, -0.3826834261f, -0.5555702448f, -0.7071067691f, -0.8314695954f, -0.9238795042f, -0.9807852507f, 1.0f, 0.9807852507f, 0.9238795042f, 0.8314695954f, 0.7071067691f, 0.5555702448f, 0.3826834261f, 0.1950903237f, 6.123234263e-17f, -0.1950903237f, -0.3826834261f, -0.5555702448f, -0.7071067691f, -0.8314695954f, -0.9238795042f, -0.9807852507f, 1.0f, 0.9807852507f, 0.9238795042f, 0.8314695954f, 0.7071067691f, 0.5555702448f, 0.3826834261f, 0.1950903237f, 6.123234263e-17f, -0.1950903237f, -0.3826834261f, -0.5555702448f, -0.7071067691f, -0.8314695954f, -0.9238795042f, -0.9807852507f, 1.0f, 0.9807852507f, 0.9238795042f, 0.8314695954f, 0.7071067691f, 0.5555702448f, 0.3826834261f, 0.1950903237f, 6.123234263e-17f, -0.1950903237f, -0.3826834261f, -0.5555702448f, -0.7071067691f, -0.8314695954f, -0.9238795042f, -0.9807852507f, 1.0f, 0.9807852507f, 0.9238795042f, 0.8314695954f, 0.7071067691f, 0.5555702448f, 0.3826834261f, 0.1950903237f, 6.123234263e-17f, -0.1950903237f, -0.3826834261f, -0.5555702448f, -0.7071067691f, -0.8314695954f, -0.9238795042f, -0.9807852507f, 1.0f, 0.9807852507f, 0.9238795042f, 0.8314695954f, 0.7071067691f, 0.5555702448f, 0.3826834261f, 0.1950903237f, 6.123234263e-17f, -0.1950903237f, -0.3826834261f, -0.5555702448f, -0.7071067691f, -0.8314695954f, -0.9238795042f, -0.9807852507f, 1.0f, 0.9807852507f, 0.9238795042f, 0.8314695954f, 0.7071067691f, 0.5555702448f, 0.3826834261f, 0.1950903237f, 6.123234263e-17f, -0.1950903237f, -0.3826834261f, -0.5555702448f, -0.7071067691f, -0.8314695954f, -0.9238795042f, -0.9807852507f, 1.0f, 0.9807852507f, 0.9238795042f, 0.8314695954f, 0.7071067691f, 0.5555702448f, 0.3826834261f, 0.1950903237f, 6.123234263e-17f, -0.1950903237f, -0.3826834261f, -0.5555702448f, -0.7071067691f, -0.8314695954f, -0.9238795042f, -0.9807852507f, 1.0f, 0.9807852507f, 0.9238795042f, 0.8314695954f, 0.7071067691f, 0.5555702448f, 0.3826834261f, 0.1950903237f, 6.123234263e-17f, -0.1950903237f, -0.3826834261f, -0.5555702448f, -0.7071067691f, -0.8314695954f, -0.9238795042f, -0.9807852507f, 1.0f, 0.9807852507f, 0.9238795042f, 0.8314695954f, 0.7071067691f, 0.5555702448f, 0.3826834261f, 0.1950903237f, 6.123234263e-17f, -0.1950903237f, -0.3826834261f, -0.5555702448f, -0.7071067691f, -0.8314695954f, -0.9238795042f, -0.9807852507f, 1.0f, 0.9807852507f, 0.9238795042f, 0.8314695954f, 0.7071067691f, 0.5555702448f, 0.3826834261f, 0.1950903237f, 6.123234263e-17f, -0.1950903237f, -0.3826834261f, -0.5555702448f, -0.7071067691f, -0.8314695954f, -0.9238795042f, -0.9807852507f, 1.0f, 0.9807852507f, 0.9238795042f, 0.8314695954f, 0.7071067691f, 0.5555702448f, 0.3826834261f, 0.1950903237f, 6.123234263e-17f, -0.1950903237f, -0.3826834261f, -0.5555702448f, -0.7071067691f, -0.8314695954f, -0.9238795042f, -0.9807852507f, 1.0f, 0.9807852507f, 0.9238795042f, 0.8314695954f, 0.7071067691f, 0.5555702448f, 0.3826834261f, 0.1950903237f, 6.123234263e-17f, -0.1950903237f, -0.3826834261f, -0.5555702448f, -0.7071067691f, -0.8314695954f, -0.9238795042f, -0.9807852507f, 1.0f, 0.9807852507f, 0.9238795042f, 0.8314695954f, 0.7071067691f, 0.5555702448f, 0.3826834261f, 0.1950903237f, 6.123234263e-17f, -0.1950903237f, -0.3826834261f, -0.5555702448f, -0.7071067691f, -0.8314695954f, -0.9238795042f, -0.9807852507f, 1.0f, 0.9807852507f, 0.9238795042f, 0.8314695954f, 0.7071067691f, 0.5555702448f, 0.3826834261f, 0.1950903237f, 6.123234263e-17f, -0.1950903237f, -0.3826834261f, -0.5555702448f, -0.7071067691f, -0.8314695954f, -0.9238795042f, -0.9807852507f, 1.0f, 0.9807852507f, 0.9238795042f, 0.8314695954f, 0.7071067691f, 0.5555702448f, 0.3826834261f, 0.1950903237f, 6.123234263e-17f, -0.1950903237f, -0.3826834261f, -0.5555702448f, -0.7071067691f, -0.8314695954f, -0.9238795042f, -0.9807852507f, 1.0f, 0.9807852507f, 0.9238795042f, 0.8314695954f, 0.7071067691f, 0.5555702448f, 0.3826834261f, 0.1950903237f, 6.123234263e-17f, -0.1950903237f, -0.3826834261f, -0.5555702448f, -0.7071067691f, -0.8314695954f, -0.9238795042f, -0.9807852507f, 1.0f, 0.9807852507f, 0.9238795042f, 0.8314695954f, 0.7071067691f, 0.5555702448f, 0.3826834261f, 0.1950903237f, 6.123234263e-17f, -0.1950903237f, -0.3826834261f, -0.5555702448f, -0.7071067691f, -0.8314695954f, -0.9238795042f, -0.9807852507f, 1.0f, 0.9807852507f, 0.9238795042f, 0.8314695954f, 0.7071067691f, 0.5555702448f, 0.3826834261f, 0.1950903237f, 6.123234263e-17f, -0.1950903237f, -0.3826834261f, -0.5555702448f, -0.7071067691f, -0.8314695954f, -0.9238795042f, -0.9807852507f, 1.0f, 0.9807852507f, 0.9238795042f, 0.8314695954f, 0.7071067691f, 0.5555702448f, 0.3826834261f, 0.1950903237f, 6.123234263e-17f, -0.1950903237f, -0.3826834261f, -0.5555702448f, -0.7071067691f, -0.8314695954f, -0.9238795042f, -0.9807852507f, 1.0f, 0.9807852507f, 0.9238795042f, 0.8314695954f, 0.7071067691f, 0.5555702448f, 0.3826834261f, 0.1950903237f, 6.123234263e-17f, -0.1950903237f, -0.3826834261f, -0.5555702448f, -0.7071067691f, -0.8314695954f, -0.9238795042f, -0.9807852507f, 1.0f, 0.9807852507f, 0.9238795042f, 0.8314695954f, 0.7071067691f, 0.5555702448f, 0.3826834261f, 0.1950903237f, 6.123234263e-17f, -0.1950903237f, -0.3826834261f, -0.5555702448f, -0.7071067691f, -0.8314695954f, -0.9238795042f, -0.9807852507f, 1.0f, 0.9807852507f, 0.9238795042f, 0.8314695954f, 0.7071067691f, 0.5555702448f, 0.3826834261f, 0.1950903237f, 6.123234263e-17f, -0.1950903237f, -0.3826834261f, -0.5555702448f, -0.7071067691f, -0.8314695954f, -0.9238795042f, -0.9807852507f, 1.0f, 0.9807852507f, 0.9238795042f, 0.8314695954f, 0.7071067691f, 0.5555702448f, 0.3826834261f, 0.1950903237f, 6.123234263e-17f, -0.1950903237f, -0.3826834261f, -0.5555702448f, -0.7071067691f, -0.8314695954f, -0.9238795042f, -0.9807852507f, 1.0f, 0.9807852507f, 0.9238795042f, 0.8314695954f, 0.7071067691f, 0.5555702448f, 0.3826834261f, 0.1950903237f, 6.123234263e-17f, -0.1950903237f, -0.3826834261f, -0.5555702448f, -0.7071067691f, -0.8314695954f, -0.9238795042f, -0.9807852507f, 1.0f, 0.9807852507f, 0.9238795042f, 0.8314695954f, 0.7071067691f, 0.5555702448f, 0.3826834261f, 0.1950903237f, 6.123234263e-17f, -0.1950903237f, -0.3826834261f, -0.5555702448f, -0.7071067691f, -0.8314695954f, -0.9238795042f, -0.9807852507f}; +constant float tc31[512] = {1.0f, 0.9951847196f, 0.9807852507f, 0.9569403529f, 0.9238795042f, 0.8819212914f, 0.8314695954f, 0.7730104327f, 0.7071067691f, 0.6343932748f, 0.5555702448f, 0.4713967443f, 0.3826834261f, 0.2902846634f, 0.1950903237f, 0.09801714122f, 1.0f, 0.9951847196f, 0.9807852507f, 0.9569403529f, 0.9238795042f, 0.8819212914f, 0.8314695954f, 0.7730104327f, 0.7071067691f, 0.6343932748f, 0.5555702448f, 0.4713967443f, 0.3826834261f, 0.2902846634f, 0.1950903237f, 0.09801714122f, 1.0f, 0.9951847196f, 0.9807852507f, 0.9569403529f, 0.9238795042f, 0.8819212914f, 0.8314695954f, 0.7730104327f, 0.7071067691f, 0.6343932748f, 0.5555702448f, 0.4713967443f, 0.3826834261f, 0.2902846634f, 0.1950903237f, 0.09801714122f, 1.0f, 0.9951847196f, 0.9807852507f, 0.9569403529f, 0.9238795042f, 0.8819212914f, 0.8314695954f, 0.7730104327f, 0.7071067691f, 0.6343932748f, 0.5555702448f, 0.4713967443f, 0.3826834261f, 0.2902846634f, 0.1950903237f, 0.09801714122f, 1.0f, 0.9951847196f, 0.9807852507f, 0.9569403529f, 0.9238795042f, 0.8819212914f, 0.8314695954f, 0.7730104327f, 0.7071067691f, 0.6343932748f, 0.5555702448f, 0.4713967443f, 0.3826834261f, 0.2902846634f, 0.1950903237f, 0.09801714122f, 1.0f, 0.9951847196f, 0.9807852507f, 0.9569403529f, 0.9238795042f, 0.8819212914f, 0.8314695954f, 0.7730104327f, 0.7071067691f, 0.6343932748f, 0.5555702448f, 0.4713967443f, 0.3826834261f, 0.2902846634f, 0.1950903237f, 0.09801714122f, 1.0f, 0.9951847196f, 0.9807852507f, 0.9569403529f, 0.9238795042f, 0.8819212914f, 0.8314695954f, 0.7730104327f, 0.7071067691f, 0.6343932748f, 0.5555702448f, 0.4713967443f, 0.3826834261f, 0.2902846634f, 0.1950903237f, 0.09801714122f, 1.0f, 0.9951847196f, 0.9807852507f, 0.9569403529f, 0.9238795042f, 0.8819212914f, 0.8314695954f, 0.7730104327f, 0.7071067691f, 0.6343932748f, 0.5555702448f, 0.4713967443f, 0.3826834261f, 0.2902846634f, 0.1950903237f, 0.09801714122f, 1.0f, 0.9951847196f, 0.9807852507f, 0.9569403529f, 0.9238795042f, 0.8819212914f, 0.8314695954f, 0.7730104327f, 0.7071067691f, 0.6343932748f, 0.5555702448f, 0.4713967443f, 0.3826834261f, 0.2902846634f, 0.1950903237f, 0.09801714122f, 1.0f, 0.9951847196f, 0.9807852507f, 0.9569403529f, 0.9238795042f, 0.8819212914f, 0.8314695954f, 0.7730104327f, 0.7071067691f, 0.6343932748f, 0.5555702448f, 0.4713967443f, 0.3826834261f, 0.2902846634f, 0.1950903237f, 0.09801714122f, 1.0f, 0.9951847196f, 0.9807852507f, 0.9569403529f, 0.9238795042f, 0.8819212914f, 0.8314695954f, 0.7730104327f, 0.7071067691f, 0.6343932748f, 0.5555702448f, 0.4713967443f, 0.3826834261f, 0.2902846634f, 0.1950903237f, 0.09801714122f, 1.0f, 0.9951847196f, 0.9807852507f, 0.9569403529f, 0.9238795042f, 0.8819212914f, 0.8314695954f, 0.7730104327f, 0.7071067691f, 0.6343932748f, 0.5555702448f, 0.4713967443f, 0.3826834261f, 0.2902846634f, 0.1950903237f, 0.09801714122f, 1.0f, 0.9951847196f, 0.9807852507f, 0.9569403529f, 0.9238795042f, 0.8819212914f, 0.8314695954f, 0.7730104327f, 0.7071067691f, 0.6343932748f, 0.5555702448f, 0.4713967443f, 0.3826834261f, 0.2902846634f, 0.1950903237f, 0.09801714122f, 1.0f, 0.9951847196f, 0.9807852507f, 0.9569403529f, 0.9238795042f, 0.8819212914f, 0.8314695954f, 0.7730104327f, 0.7071067691f, 0.6343932748f, 0.5555702448f, 0.4713967443f, 0.3826834261f, 0.2902846634f, 0.1950903237f, 0.09801714122f, 1.0f, 0.9951847196f, 0.9807852507f, 0.9569403529f, 0.9238795042f, 0.8819212914f, 0.8314695954f, 0.7730104327f, 0.7071067691f, 0.6343932748f, 0.5555702448f, 0.4713967443f, 0.3826834261f, 0.2902846634f, 0.1950903237f, 0.09801714122f, 1.0f, 0.9951847196f, 0.9807852507f, 0.9569403529f, 0.9238795042f, 0.8819212914f, 0.8314695954f, 0.7730104327f, 0.7071067691f, 0.6343932748f, 0.5555702448f, 0.4713967443f, 0.3826834261f, 0.2902846634f, 0.1950903237f, 0.09801714122f, 1.0f, 0.9951847196f, 0.9807852507f, 0.9569403529f, 0.9238795042f, 0.8819212914f, 0.8314695954f, 0.7730104327f, 0.7071067691f, 0.6343932748f, 0.5555702448f, 0.4713967443f, 0.3826834261f, 0.2902846634f, 0.1950903237f, 0.09801714122f, 1.0f, 0.9951847196f, 0.9807852507f, 0.9569403529f, 0.9238795042f, 0.8819212914f, 0.8314695954f, 0.7730104327f, 0.7071067691f, 0.6343932748f, 0.5555702448f, 0.4713967443f, 0.3826834261f, 0.2902846634f, 0.1950903237f, 0.09801714122f, 1.0f, 0.9951847196f, 0.9807852507f, 0.9569403529f, 0.9238795042f, 0.8819212914f, 0.8314695954f, 0.7730104327f, 0.7071067691f, 0.6343932748f, 0.5555702448f, 0.4713967443f, 0.3826834261f, 0.2902846634f, 0.1950903237f, 0.09801714122f, 1.0f, 0.9951847196f, 0.9807852507f, 0.9569403529f, 0.9238795042f, 0.8819212914f, 0.8314695954f, 0.7730104327f, 0.7071067691f, 0.6343932748f, 0.5555702448f, 0.4713967443f, 0.3826834261f, 0.2902846634f, 0.1950903237f, 0.09801714122f, 1.0f, 0.9951847196f, 0.9807852507f, 0.9569403529f, 0.9238795042f, 0.8819212914f, 0.8314695954f, 0.7730104327f, 0.7071067691f, 0.6343932748f, 0.5555702448f, 0.4713967443f, 0.3826834261f, 0.2902846634f, 0.1950903237f, 0.09801714122f, 1.0f, 0.9951847196f, 0.9807852507f, 0.9569403529f, 0.9238795042f, 0.8819212914f, 0.8314695954f, 0.7730104327f, 0.7071067691f, 0.6343932748f, 0.5555702448f, 0.4713967443f, 0.3826834261f, 0.2902846634f, 0.1950903237f, 0.09801714122f, 1.0f, 0.9951847196f, 0.9807852507f, 0.9569403529f, 0.9238795042f, 0.8819212914f, 0.8314695954f, 0.7730104327f, 0.7071067691f, 0.6343932748f, 0.5555702448f, 0.4713967443f, 0.3826834261f, 0.2902846634f, 0.1950903237f, 0.09801714122f, 1.0f, 0.9951847196f, 0.9807852507f, 0.9569403529f, 0.9238795042f, 0.8819212914f, 0.8314695954f, 0.7730104327f, 0.7071067691f, 0.6343932748f, 0.5555702448f, 0.4713967443f, 0.3826834261f, 0.2902846634f, 0.1950903237f, 0.09801714122f, 1.0f, 0.9951847196f, 0.9807852507f, 0.9569403529f, 0.9238795042f, 0.8819212914f, 0.8314695954f, 0.7730104327f, 0.7071067691f, 0.6343932748f, 0.5555702448f, 0.4713967443f, 0.3826834261f, 0.2902846634f, 0.1950903237f, 0.09801714122f, 1.0f, 0.9951847196f, 0.9807852507f, 0.9569403529f, 0.9238795042f, 0.8819212914f, 0.8314695954f, 0.7730104327f, 0.7071067691f, 0.6343932748f, 0.5555702448f, 0.4713967443f, 0.3826834261f, 0.2902846634f, 0.1950903237f, 0.09801714122f, 1.0f, 0.9951847196f, 0.9807852507f, 0.9569403529f, 0.9238795042f, 0.8819212914f, 0.8314695954f, 0.7730104327f, 0.7071067691f, 0.6343932748f, 0.5555702448f, 0.4713967443f, 0.3826834261f, 0.2902846634f, 0.1950903237f, 0.09801714122f, 1.0f, 0.9951847196f, 0.9807852507f, 0.9569403529f, 0.9238795042f, 0.8819212914f, 0.8314695954f, 0.7730104327f, 0.7071067691f, 0.6343932748f, 0.5555702448f, 0.4713967443f, 0.3826834261f, 0.2902846634f, 0.1950903237f, 0.09801714122f, 1.0f, 0.9951847196f, 0.9807852507f, 0.9569403529f, 0.9238795042f, 0.8819212914f, 0.8314695954f, 0.7730104327f, 0.7071067691f, 0.6343932748f, 0.5555702448f, 0.4713967443f, 0.3826834261f, 0.2902846634f, 0.1950903237f, 0.09801714122f, 1.0f, 0.9951847196f, 0.9807852507f, 0.9569403529f, 0.9238795042f, 0.8819212914f, 0.8314695954f, 0.7730104327f, 0.7071067691f, 0.6343932748f, 0.5555702448f, 0.4713967443f, 0.3826834261f, 0.2902846634f, 0.1950903237f, 0.09801714122f, 1.0f, 0.9951847196f, 0.9807852507f, 0.9569403529f, 0.9238795042f, 0.8819212914f, 0.8314695954f, 0.7730104327f, 0.7071067691f, 0.6343932748f, 0.5555702448f, 0.4713967443f, 0.3826834261f, 0.2902846634f, 0.1950903237f, 0.09801714122f, 1.0f, 0.9951847196f, 0.9807852507f, 0.9569403529f, 0.9238795042f, 0.8819212914f, 0.8314695954f, 0.7730104327f, 0.7071067691f, 0.6343932748f, 0.5555702448f, 0.4713967443f, 0.3826834261f, 0.2902846634f, 0.1950903237f, 0.09801714122f}; +constant float tc34[512] = {1.0f, 0.9951847196f, 0.9807852507f, 0.9569403529f, 0.9238795042f, 0.8819212914f, 0.8314695954f, 0.7730104327f, 0.7071067691f, 0.6343932748f, 0.5555702448f, 0.4713967443f, 0.3826834261f, 0.2902846634f, 0.1950903237f, 0.09801714122f, 1.0f, 0.9951847196f, 0.9807852507f, 0.9569403529f, 0.9238795042f, 0.8819212914f, 0.8314695954f, 0.7730104327f, 0.7071067691f, 0.6343932748f, 0.5555702448f, 0.4713967443f, 0.3826834261f, 0.2902846634f, 0.1950903237f, 0.09801714122f, 1.0f, 0.9951847196f, 0.9807852507f, 0.9569403529f, 0.9238795042f, 0.8819212914f, 0.8314695954f, 0.7730104327f, 0.7071067691f, 0.6343932748f, 0.5555702448f, 0.4713967443f, 0.3826834261f, 0.2902846634f, 0.1950903237f, 0.09801714122f, 1.0f, 0.9951847196f, 0.9807852507f, 0.9569403529f, 0.9238795042f, 0.8819212914f, 0.8314695954f, 0.7730104327f, 0.7071067691f, 0.6343932748f, 0.5555702448f, 0.4713967443f, 0.3826834261f, 0.2902846634f, 0.1950903237f, 0.09801714122f, 1.0f, 0.9951847196f, 0.9807852507f, 0.9569403529f, 0.9238795042f, 0.8819212914f, 0.8314695954f, 0.7730104327f, 0.7071067691f, 0.6343932748f, 0.5555702448f, 0.4713967443f, 0.3826834261f, 0.2902846634f, 0.1950903237f, 0.09801714122f, 1.0f, 0.9951847196f, 0.9807852507f, 0.9569403529f, 0.9238795042f, 0.8819212914f, 0.8314695954f, 0.7730104327f, 0.7071067691f, 0.6343932748f, 0.5555702448f, 0.4713967443f, 0.3826834261f, 0.2902846634f, 0.1950903237f, 0.09801714122f, 1.0f, 0.9951847196f, 0.9807852507f, 0.9569403529f, 0.9238795042f, 0.8819212914f, 0.8314695954f, 0.7730104327f, 0.7071067691f, 0.6343932748f, 0.5555702448f, 0.4713967443f, 0.3826834261f, 0.2902846634f, 0.1950903237f, 0.09801714122f, 1.0f, 0.9951847196f, 0.9807852507f, 0.9569403529f, 0.9238795042f, 0.8819212914f, 0.8314695954f, 0.7730104327f, 0.7071067691f, 0.6343932748f, 0.5555702448f, 0.4713967443f, 0.3826834261f, 0.2902846634f, 0.1950903237f, 0.09801714122f, 1.0f, 0.9951847196f, 0.9807852507f, 0.9569403529f, 0.9238795042f, 0.8819212914f, 0.8314695954f, 0.7730104327f, 0.7071067691f, 0.6343932748f, 0.5555702448f, 0.4713967443f, 0.3826834261f, 0.2902846634f, 0.1950903237f, 0.09801714122f, 1.0f, 0.9951847196f, 0.9807852507f, 0.9569403529f, 0.9238795042f, 0.8819212914f, 0.8314695954f, 0.7730104327f, 0.7071067691f, 0.6343932748f, 0.5555702448f, 0.4713967443f, 0.3826834261f, 0.2902846634f, 0.1950903237f, 0.09801714122f, 1.0f, 0.9951847196f, 0.9807852507f, 0.9569403529f, 0.9238795042f, 0.8819212914f, 0.8314695954f, 0.7730104327f, 0.7071067691f, 0.6343932748f, 0.5555702448f, 0.4713967443f, 0.3826834261f, 0.2902846634f, 0.1950903237f, 0.09801714122f, 1.0f, 0.9951847196f, 0.9807852507f, 0.9569403529f, 0.9238795042f, 0.8819212914f, 0.8314695954f, 0.7730104327f, 0.7071067691f, 0.6343932748f, 0.5555702448f, 0.4713967443f, 0.3826834261f, 0.2902846634f, 0.1950903237f, 0.09801714122f, 1.0f, 0.9951847196f, 0.9807852507f, 0.9569403529f, 0.9238795042f, 0.8819212914f, 0.8314695954f, 0.7730104327f, 0.7071067691f, 0.6343932748f, 0.5555702448f, 0.4713967443f, 0.3826834261f, 0.2902846634f, 0.1950903237f, 0.09801714122f, 1.0f, 0.9951847196f, 0.9807852507f, 0.9569403529f, 0.9238795042f, 0.8819212914f, 0.8314695954f, 0.7730104327f, 0.7071067691f, 0.6343932748f, 0.5555702448f, 0.4713967443f, 0.3826834261f, 0.2902846634f, 0.1950903237f, 0.09801714122f, 1.0f, 0.9951847196f, 0.9807852507f, 0.9569403529f, 0.9238795042f, 0.8819212914f, 0.8314695954f, 0.7730104327f, 0.7071067691f, 0.6343932748f, 0.5555702448f, 0.4713967443f, 0.3826834261f, 0.2902846634f, 0.1950903237f, 0.09801714122f, 1.0f, 0.9951847196f, 0.9807852507f, 0.9569403529f, 0.9238795042f, 0.8819212914f, 0.8314695954f, 0.7730104327f, 0.7071067691f, 0.6343932748f, 0.5555702448f, 0.4713967443f, 0.3826834261f, 0.2902846634f, 0.1950903237f, 0.09801714122f, 1.0f, 0.9951847196f, 0.9807852507f, 0.9569403529f, 0.9238795042f, 0.8819212914f, 0.8314695954f, 0.7730104327f, 0.7071067691f, 0.6343932748f, 0.5555702448f, 0.4713967443f, 0.3826834261f, 0.2902846634f, 0.1950903237f, 0.09801714122f, 1.0f, 0.9951847196f, 0.9807852507f, 0.9569403529f, 0.9238795042f, 0.8819212914f, 0.8314695954f, 0.7730104327f, 0.7071067691f, 0.6343932748f, 0.5555702448f, 0.4713967443f, 0.3826834261f, 0.2902846634f, 0.1950903237f, 0.09801714122f, 1.0f, 0.9951847196f, 0.9807852507f, 0.9569403529f, 0.9238795042f, 0.8819212914f, 0.8314695954f, 0.7730104327f, 0.7071067691f, 0.6343932748f, 0.5555702448f, 0.4713967443f, 0.3826834261f, 0.2902846634f, 0.1950903237f, 0.09801714122f, 1.0f, 0.9951847196f, 0.9807852507f, 0.9569403529f, 0.9238795042f, 0.8819212914f, 0.8314695954f, 0.7730104327f, 0.7071067691f, 0.6343932748f, 0.5555702448f, 0.4713967443f, 0.3826834261f, 0.2902846634f, 0.1950903237f, 0.09801714122f, 1.0f, 0.9951847196f, 0.9807852507f, 0.9569403529f, 0.9238795042f, 0.8819212914f, 0.8314695954f, 0.7730104327f, 0.7071067691f, 0.6343932748f, 0.5555702448f, 0.4713967443f, 0.3826834261f, 0.2902846634f, 0.1950903237f, 0.09801714122f, 1.0f, 0.9951847196f, 0.9807852507f, 0.9569403529f, 0.9238795042f, 0.8819212914f, 0.8314695954f, 0.7730104327f, 0.7071067691f, 0.6343932748f, 0.5555702448f, 0.4713967443f, 0.3826834261f, 0.2902846634f, 0.1950903237f, 0.09801714122f, 1.0f, 0.9951847196f, 0.9807852507f, 0.9569403529f, 0.9238795042f, 0.8819212914f, 0.8314695954f, 0.7730104327f, 0.7071067691f, 0.6343932748f, 0.5555702448f, 0.4713967443f, 0.3826834261f, 0.2902846634f, 0.1950903237f, 0.09801714122f, 1.0f, 0.9951847196f, 0.9807852507f, 0.9569403529f, 0.9238795042f, 0.8819212914f, 0.8314695954f, 0.7730104327f, 0.7071067691f, 0.6343932748f, 0.5555702448f, 0.4713967443f, 0.3826834261f, 0.2902846634f, 0.1950903237f, 0.09801714122f, 1.0f, 0.9951847196f, 0.9807852507f, 0.9569403529f, 0.9238795042f, 0.8819212914f, 0.8314695954f, 0.7730104327f, 0.7071067691f, 0.6343932748f, 0.5555702448f, 0.4713967443f, 0.3826834261f, 0.2902846634f, 0.1950903237f, 0.09801714122f, 1.0f, 0.9951847196f, 0.9807852507f, 0.9569403529f, 0.9238795042f, 0.8819212914f, 0.8314695954f, 0.7730104327f, 0.7071067691f, 0.6343932748f, 0.5555702448f, 0.4713967443f, 0.3826834261f, 0.2902846634f, 0.1950903237f, 0.09801714122f, 1.0f, 0.9951847196f, 0.9807852507f, 0.9569403529f, 0.9238795042f, 0.8819212914f, 0.8314695954f, 0.7730104327f, 0.7071067691f, 0.6343932748f, 0.5555702448f, 0.4713967443f, 0.3826834261f, 0.2902846634f, 0.1950903237f, 0.09801714122f, 1.0f, 0.9951847196f, 0.9807852507f, 0.9569403529f, 0.9238795042f, 0.8819212914f, 0.8314695954f, 0.7730104327f, 0.7071067691f, 0.6343932748f, 0.5555702448f, 0.4713967443f, 0.3826834261f, 0.2902846634f, 0.1950903237f, 0.09801714122f, 1.0f, 0.9951847196f, 0.9807852507f, 0.9569403529f, 0.9238795042f, 0.8819212914f, 0.8314695954f, 0.7730104327f, 0.7071067691f, 0.6343932748f, 0.5555702448f, 0.4713967443f, 0.3826834261f, 0.2902846634f, 0.1950903237f, 0.09801714122f, 1.0f, 0.9951847196f, 0.9807852507f, 0.9569403529f, 0.9238795042f, 0.8819212914f, 0.8314695954f, 0.7730104327f, 0.7071067691f, 0.6343932748f, 0.5555702448f, 0.4713967443f, 0.3826834261f, 0.2902846634f, 0.1950903237f, 0.09801714122f, 1.0f, 0.9951847196f, 0.9807852507f, 0.9569403529f, 0.9238795042f, 0.8819212914f, 0.8314695954f, 0.7730104327f, 0.7071067691f, 0.6343932748f, 0.5555702448f, 0.4713967443f, 0.3826834261f, 0.2902846634f, 0.1950903237f, 0.09801714122f, 1.0f, 0.9951847196f, 0.9807852507f, 0.9569403529f, 0.9238795042f, 0.8819212914f, 0.8314695954f, 0.7730104327f, 0.7071067691f, 0.6343932748f, 0.5555702448f, 0.4713967443f, 0.3826834261f, 0.2902846634f, 0.1950903237f, 0.09801714122f}; +constant float tc32[512] = {1.0f, 0.9569403529f, 0.8314695954f, 0.6343932748f, 0.3826834261f, 0.09801714122f, -0.1950903237f, -0.4713967443f, -0.7071067691f, -0.8819212914f, -0.9807852507f, -0.9951847196f, -0.9238795042f, -0.7730104327f, -0.5555702448f, -0.2902846634f, 1.0f, 0.9569403529f, 0.8314695954f, 0.6343932748f, 0.3826834261f, 0.09801714122f, -0.1950903237f, -0.4713967443f, -0.7071067691f, -0.8819212914f, -0.9807852507f, -0.9951847196f, -0.9238795042f, -0.7730104327f, -0.5555702448f, -0.2902846634f, 1.0f, 0.9569403529f, 0.8314695954f, 0.6343932748f, 0.3826834261f, 0.09801714122f, -0.1950903237f, -0.4713967443f, -0.7071067691f, -0.8819212914f, -0.9807852507f, -0.9951847196f, -0.9238795042f, -0.7730104327f, -0.5555702448f, -0.2902846634f, 1.0f, 0.9569403529f, 0.8314695954f, 0.6343932748f, 0.3826834261f, 0.09801714122f, -0.1950903237f, -0.4713967443f, -0.7071067691f, -0.8819212914f, -0.9807852507f, -0.9951847196f, -0.9238795042f, -0.7730104327f, -0.5555702448f, -0.2902846634f, 1.0f, 0.9569403529f, 0.8314695954f, 0.6343932748f, 0.3826834261f, 0.09801714122f, -0.1950903237f, -0.4713967443f, -0.7071067691f, -0.8819212914f, -0.9807852507f, -0.9951847196f, -0.9238795042f, -0.7730104327f, -0.5555702448f, -0.2902846634f, 1.0f, 0.9569403529f, 0.8314695954f, 0.6343932748f, 0.3826834261f, 0.09801714122f, -0.1950903237f, -0.4713967443f, -0.7071067691f, -0.8819212914f, -0.9807852507f, -0.9951847196f, -0.9238795042f, -0.7730104327f, -0.5555702448f, -0.2902846634f, 1.0f, 0.9569403529f, 0.8314695954f, 0.6343932748f, 0.3826834261f, 0.09801714122f, -0.1950903237f, -0.4713967443f, -0.7071067691f, -0.8819212914f, -0.9807852507f, -0.9951847196f, -0.9238795042f, -0.7730104327f, -0.5555702448f, -0.2902846634f, 1.0f, 0.9569403529f, 0.8314695954f, 0.6343932748f, 0.3826834261f, 0.09801714122f, -0.1950903237f, -0.4713967443f, -0.7071067691f, -0.8819212914f, -0.9807852507f, -0.9951847196f, -0.9238795042f, -0.7730104327f, -0.5555702448f, -0.2902846634f, 1.0f, 0.9569403529f, 0.8314695954f, 0.6343932748f, 0.3826834261f, 0.09801714122f, -0.1950903237f, -0.4713967443f, -0.7071067691f, -0.8819212914f, -0.9807852507f, -0.9951847196f, -0.9238795042f, -0.7730104327f, -0.5555702448f, -0.2902846634f, 1.0f, 0.9569403529f, 0.8314695954f, 0.6343932748f, 0.3826834261f, 0.09801714122f, -0.1950903237f, -0.4713967443f, -0.7071067691f, -0.8819212914f, -0.9807852507f, -0.9951847196f, -0.9238795042f, -0.7730104327f, -0.5555702448f, -0.2902846634f, 1.0f, 0.9569403529f, 0.8314695954f, 0.6343932748f, 0.3826834261f, 0.09801714122f, -0.1950903237f, -0.4713967443f, -0.7071067691f, -0.8819212914f, -0.9807852507f, -0.9951847196f, -0.9238795042f, -0.7730104327f, -0.5555702448f, -0.2902846634f, 1.0f, 0.9569403529f, 0.8314695954f, 0.6343932748f, 0.3826834261f, 0.09801714122f, -0.1950903237f, -0.4713967443f, -0.7071067691f, -0.8819212914f, -0.9807852507f, -0.9951847196f, -0.9238795042f, -0.7730104327f, -0.5555702448f, -0.2902846634f, 1.0f, 0.9569403529f, 0.8314695954f, 0.6343932748f, 0.3826834261f, 0.09801714122f, -0.1950903237f, -0.4713967443f, -0.7071067691f, -0.8819212914f, -0.9807852507f, -0.9951847196f, -0.9238795042f, -0.7730104327f, -0.5555702448f, -0.2902846634f, 1.0f, 0.9569403529f, 0.8314695954f, 0.6343932748f, 0.3826834261f, 0.09801714122f, -0.1950903237f, -0.4713967443f, -0.7071067691f, -0.8819212914f, -0.9807852507f, -0.9951847196f, -0.9238795042f, -0.7730104327f, -0.5555702448f, -0.2902846634f, 1.0f, 0.9569403529f, 0.8314695954f, 0.6343932748f, 0.3826834261f, 0.09801714122f, -0.1950903237f, -0.4713967443f, -0.7071067691f, -0.8819212914f, -0.9807852507f, -0.9951847196f, -0.9238795042f, -0.7730104327f, -0.5555702448f, -0.2902846634f, 1.0f, 0.9569403529f, 0.8314695954f, 0.6343932748f, 0.3826834261f, 0.09801714122f, -0.1950903237f, -0.4713967443f, -0.7071067691f, -0.8819212914f, -0.9807852507f, -0.9951847196f, -0.9238795042f, -0.7730104327f, -0.5555702448f, -0.2902846634f, 1.0f, 0.9569403529f, 0.8314695954f, 0.6343932748f, 0.3826834261f, 0.09801714122f, -0.1950903237f, -0.4713967443f, -0.7071067691f, -0.8819212914f, -0.9807852507f, -0.9951847196f, -0.9238795042f, -0.7730104327f, -0.5555702448f, -0.2902846634f, 1.0f, 0.9569403529f, 0.8314695954f, 0.6343932748f, 0.3826834261f, 0.09801714122f, -0.1950903237f, -0.4713967443f, -0.7071067691f, -0.8819212914f, -0.9807852507f, -0.9951847196f, -0.9238795042f, -0.7730104327f, -0.5555702448f, -0.2902846634f, 1.0f, 0.9569403529f, 0.8314695954f, 0.6343932748f, 0.3826834261f, 0.09801714122f, -0.1950903237f, -0.4713967443f, -0.7071067691f, -0.8819212914f, -0.9807852507f, -0.9951847196f, -0.9238795042f, -0.7730104327f, -0.5555702448f, -0.2902846634f, 1.0f, 0.9569403529f, 0.8314695954f, 0.6343932748f, 0.3826834261f, 0.09801714122f, -0.1950903237f, -0.4713967443f, -0.7071067691f, -0.8819212914f, -0.9807852507f, -0.9951847196f, -0.9238795042f, -0.7730104327f, -0.5555702448f, -0.2902846634f, 1.0f, 0.9569403529f, 0.8314695954f, 0.6343932748f, 0.3826834261f, 0.09801714122f, -0.1950903237f, -0.4713967443f, -0.7071067691f, -0.8819212914f, -0.9807852507f, -0.9951847196f, -0.9238795042f, -0.7730104327f, -0.5555702448f, -0.2902846634f, 1.0f, 0.9569403529f, 0.8314695954f, 0.6343932748f, 0.3826834261f, 0.09801714122f, -0.1950903237f, -0.4713967443f, -0.7071067691f, -0.8819212914f, -0.9807852507f, -0.9951847196f, -0.9238795042f, -0.7730104327f, -0.5555702448f, -0.2902846634f, 1.0f, 0.9569403529f, 0.8314695954f, 0.6343932748f, 0.3826834261f, 0.09801714122f, -0.1950903237f, -0.4713967443f, -0.7071067691f, -0.8819212914f, -0.9807852507f, -0.9951847196f, -0.9238795042f, -0.7730104327f, -0.5555702448f, -0.2902846634f, 1.0f, 0.9569403529f, 0.8314695954f, 0.6343932748f, 0.3826834261f, 0.09801714122f, -0.1950903237f, -0.4713967443f, -0.7071067691f, -0.8819212914f, -0.9807852507f, -0.9951847196f, -0.9238795042f, -0.7730104327f, -0.5555702448f, -0.2902846634f, 1.0f, 0.9569403529f, 0.8314695954f, 0.6343932748f, 0.3826834261f, 0.09801714122f, -0.1950903237f, -0.4713967443f, -0.7071067691f, -0.8819212914f, -0.9807852507f, -0.9951847196f, -0.9238795042f, -0.7730104327f, -0.5555702448f, -0.2902846634f, 1.0f, 0.9569403529f, 0.8314695954f, 0.6343932748f, 0.3826834261f, 0.09801714122f, -0.1950903237f, -0.4713967443f, -0.7071067691f, -0.8819212914f, -0.9807852507f, -0.9951847196f, -0.9238795042f, -0.7730104327f, -0.5555702448f, -0.2902846634f, 1.0f, 0.9569403529f, 0.8314695954f, 0.6343932748f, 0.3826834261f, 0.09801714122f, -0.1950903237f, -0.4713967443f, -0.7071067691f, -0.8819212914f, -0.9807852507f, -0.9951847196f, -0.9238795042f, -0.7730104327f, -0.5555702448f, -0.2902846634f, 1.0f, 0.9569403529f, 0.8314695954f, 0.6343932748f, 0.3826834261f, 0.09801714122f, -0.1950903237f, -0.4713967443f, -0.7071067691f, -0.8819212914f, -0.9807852507f, -0.9951847196f, -0.9238795042f, -0.7730104327f, -0.5555702448f, -0.2902846634f, 1.0f, 0.9569403529f, 0.8314695954f, 0.6343932748f, 0.3826834261f, 0.09801714122f, -0.1950903237f, -0.4713967443f, -0.7071067691f, -0.8819212914f, -0.9807852507f, -0.9951847196f, -0.9238795042f, -0.7730104327f, -0.5555702448f, -0.2902846634f, 1.0f, 0.9569403529f, 0.8314695954f, 0.6343932748f, 0.3826834261f, 0.09801714122f, -0.1950903237f, -0.4713967443f, -0.7071067691f, -0.8819212914f, -0.9807852507f, -0.9951847196f, -0.9238795042f, -0.7730104327f, -0.5555702448f, -0.2902846634f, 1.0f, 0.9569403529f, 0.8314695954f, 0.6343932748f, 0.3826834261f, 0.09801714122f, -0.1950903237f, -0.4713967443f, -0.7071067691f, -0.8819212914f, -0.9807852507f, -0.9951847196f, -0.9238795042f, -0.7730104327f, -0.5555702448f, -0.2902846634f, 1.0f, 0.9569403529f, 0.8314695954f, 0.6343932748f, 0.3826834261f, 0.09801714122f, -0.1950903237f, -0.4713967443f, -0.7071067691f, -0.8819212914f, -0.9807852507f, -0.9951847196f, -0.9238795042f, -0.7730104327f, -0.5555702448f, -0.2902846634f}; +constant float tc35[512] = {1.0f, 0.9569403529f, 0.8314695954f, 0.6343932748f, 0.3826834261f, 0.09801714122f, -0.1950903237f, -0.4713967443f, -0.7071067691f, -0.8819212914f, -0.9807852507f, -0.9951847196f, -0.9238795042f, -0.7730104327f, -0.5555702448f, -0.2902846634f, 1.0f, 0.9569403529f, 0.8314695954f, 0.6343932748f, 0.3826834261f, 0.09801714122f, -0.1950903237f, -0.4713967443f, -0.7071067691f, -0.8819212914f, -0.9807852507f, -0.9951847196f, -0.9238795042f, -0.7730104327f, -0.5555702448f, -0.2902846634f, 1.0f, 0.9569403529f, 0.8314695954f, 0.6343932748f, 0.3826834261f, 0.09801714122f, -0.1950903237f, -0.4713967443f, -0.7071067691f, -0.8819212914f, -0.9807852507f, -0.9951847196f, -0.9238795042f, -0.7730104327f, -0.5555702448f, -0.2902846634f, 1.0f, 0.9569403529f, 0.8314695954f, 0.6343932748f, 0.3826834261f, 0.09801714122f, -0.1950903237f, -0.4713967443f, -0.7071067691f, -0.8819212914f, -0.9807852507f, -0.9951847196f, -0.9238795042f, -0.7730104327f, -0.5555702448f, -0.2902846634f, 1.0f, 0.9569403529f, 0.8314695954f, 0.6343932748f, 0.3826834261f, 0.09801714122f, -0.1950903237f, -0.4713967443f, -0.7071067691f, -0.8819212914f, -0.9807852507f, -0.9951847196f, -0.9238795042f, -0.7730104327f, -0.5555702448f, -0.2902846634f, 1.0f, 0.9569403529f, 0.8314695954f, 0.6343932748f, 0.3826834261f, 0.09801714122f, -0.1950903237f, -0.4713967443f, -0.7071067691f, -0.8819212914f, -0.9807852507f, -0.9951847196f, -0.9238795042f, -0.7730104327f, -0.5555702448f, -0.2902846634f, 1.0f, 0.9569403529f, 0.8314695954f, 0.6343932748f, 0.3826834261f, 0.09801714122f, -0.1950903237f, -0.4713967443f, -0.7071067691f, -0.8819212914f, -0.9807852507f, -0.9951847196f, -0.9238795042f, -0.7730104327f, -0.5555702448f, -0.2902846634f, 1.0f, 0.9569403529f, 0.8314695954f, 0.6343932748f, 0.3826834261f, 0.09801714122f, -0.1950903237f, -0.4713967443f, -0.7071067691f, -0.8819212914f, -0.9807852507f, -0.9951847196f, -0.9238795042f, -0.7730104327f, -0.5555702448f, -0.2902846634f, 1.0f, 0.9569403529f, 0.8314695954f, 0.6343932748f, 0.3826834261f, 0.09801714122f, -0.1950903237f, -0.4713967443f, -0.7071067691f, -0.8819212914f, -0.9807852507f, -0.9951847196f, -0.9238795042f, -0.7730104327f, -0.5555702448f, -0.2902846634f, 1.0f, 0.9569403529f, 0.8314695954f, 0.6343932748f, 0.3826834261f, 0.09801714122f, -0.1950903237f, -0.4713967443f, -0.7071067691f, -0.8819212914f, -0.9807852507f, -0.9951847196f, -0.9238795042f, -0.7730104327f, -0.5555702448f, -0.2902846634f, 1.0f, 0.9569403529f, 0.8314695954f, 0.6343932748f, 0.3826834261f, 0.09801714122f, -0.1950903237f, -0.4713967443f, -0.7071067691f, -0.8819212914f, -0.9807852507f, -0.9951847196f, -0.9238795042f, -0.7730104327f, -0.5555702448f, -0.2902846634f, 1.0f, 0.9569403529f, 0.8314695954f, 0.6343932748f, 0.3826834261f, 0.09801714122f, -0.1950903237f, -0.4713967443f, -0.7071067691f, -0.8819212914f, -0.9807852507f, -0.9951847196f, -0.9238795042f, -0.7730104327f, -0.5555702448f, -0.2902846634f, 1.0f, 0.9569403529f, 0.8314695954f, 0.6343932748f, 0.3826834261f, 0.09801714122f, -0.1950903237f, -0.4713967443f, -0.7071067691f, -0.8819212914f, -0.9807852507f, -0.9951847196f, -0.9238795042f, -0.7730104327f, -0.5555702448f, -0.2902846634f, 1.0f, 0.9569403529f, 0.8314695954f, 0.6343932748f, 0.3826834261f, 0.09801714122f, -0.1950903237f, -0.4713967443f, -0.7071067691f, -0.8819212914f, -0.9807852507f, -0.9951847196f, -0.9238795042f, -0.7730104327f, -0.5555702448f, -0.2902846634f, 1.0f, 0.9569403529f, 0.8314695954f, 0.6343932748f, 0.3826834261f, 0.09801714122f, -0.1950903237f, -0.4713967443f, -0.7071067691f, -0.8819212914f, -0.9807852507f, -0.9951847196f, -0.9238795042f, -0.7730104327f, -0.5555702448f, -0.2902846634f, 1.0f, 0.9569403529f, 0.8314695954f, 0.6343932748f, 0.3826834261f, 0.09801714122f, -0.1950903237f, -0.4713967443f, -0.7071067691f, -0.8819212914f, -0.9807852507f, -0.9951847196f, -0.9238795042f, -0.7730104327f, -0.5555702448f, -0.2902846634f, 1.0f, 0.9569403529f, 0.8314695954f, 0.6343932748f, 0.3826834261f, 0.09801714122f, -0.1950903237f, -0.4713967443f, -0.7071067691f, -0.8819212914f, -0.9807852507f, -0.9951847196f, -0.9238795042f, -0.7730104327f, -0.5555702448f, -0.2902846634f, 1.0f, 0.9569403529f, 0.8314695954f, 0.6343932748f, 0.3826834261f, 0.09801714122f, -0.1950903237f, -0.4713967443f, -0.7071067691f, -0.8819212914f, -0.9807852507f, -0.9951847196f, -0.9238795042f, -0.7730104327f, -0.5555702448f, -0.2902846634f, 1.0f, 0.9569403529f, 0.8314695954f, 0.6343932748f, 0.3826834261f, 0.09801714122f, -0.1950903237f, -0.4713967443f, -0.7071067691f, -0.8819212914f, -0.9807852507f, -0.9951847196f, -0.9238795042f, -0.7730104327f, -0.5555702448f, -0.2902846634f, 1.0f, 0.9569403529f, 0.8314695954f, 0.6343932748f, 0.3826834261f, 0.09801714122f, -0.1950903237f, -0.4713967443f, -0.7071067691f, -0.8819212914f, -0.9807852507f, -0.9951847196f, -0.9238795042f, -0.7730104327f, -0.5555702448f, -0.2902846634f, 1.0f, 0.9569403529f, 0.8314695954f, 0.6343932748f, 0.3826834261f, 0.09801714122f, -0.1950903237f, -0.4713967443f, -0.7071067691f, -0.8819212914f, -0.9807852507f, -0.9951847196f, -0.9238795042f, -0.7730104327f, -0.5555702448f, -0.2902846634f, 1.0f, 0.9569403529f, 0.8314695954f, 0.6343932748f, 0.3826834261f, 0.09801714122f, -0.1950903237f, -0.4713967443f, -0.7071067691f, -0.8819212914f, -0.9807852507f, -0.9951847196f, -0.9238795042f, -0.7730104327f, -0.5555702448f, -0.2902846634f, 1.0f, 0.9569403529f, 0.8314695954f, 0.6343932748f, 0.3826834261f, 0.09801714122f, -0.1950903237f, -0.4713967443f, -0.7071067691f, -0.8819212914f, -0.9807852507f, -0.9951847196f, -0.9238795042f, -0.7730104327f, -0.5555702448f, -0.2902846634f, 1.0f, 0.9569403529f, 0.8314695954f, 0.6343932748f, 0.3826834261f, 0.09801714122f, -0.1950903237f, -0.4713967443f, -0.7071067691f, -0.8819212914f, -0.9807852507f, -0.9951847196f, -0.9238795042f, -0.7730104327f, -0.5555702448f, -0.2902846634f, 1.0f, 0.9569403529f, 0.8314695954f, 0.6343932748f, 0.3826834261f, 0.09801714122f, -0.1950903237f, -0.4713967443f, -0.7071067691f, -0.8819212914f, -0.9807852507f, -0.9951847196f, -0.9238795042f, -0.7730104327f, -0.5555702448f, -0.2902846634f, 1.0f, 0.9569403529f, 0.8314695954f, 0.6343932748f, 0.3826834261f, 0.09801714122f, -0.1950903237f, -0.4713967443f, -0.7071067691f, -0.8819212914f, -0.9807852507f, -0.9951847196f, -0.9238795042f, -0.7730104327f, -0.5555702448f, -0.2902846634f, 1.0f, 0.9569403529f, 0.8314695954f, 0.6343932748f, 0.3826834261f, 0.09801714122f, -0.1950903237f, -0.4713967443f, -0.7071067691f, -0.8819212914f, -0.9807852507f, -0.9951847196f, -0.9238795042f, -0.7730104327f, -0.5555702448f, -0.2902846634f, 1.0f, 0.9569403529f, 0.8314695954f, 0.6343932748f, 0.3826834261f, 0.09801714122f, -0.1950903237f, -0.4713967443f, -0.7071067691f, -0.8819212914f, -0.9807852507f, -0.9951847196f, -0.9238795042f, -0.7730104327f, -0.5555702448f, -0.2902846634f, 1.0f, 0.9569403529f, 0.8314695954f, 0.6343932748f, 0.3826834261f, 0.09801714122f, -0.1950903237f, -0.4713967443f, -0.7071067691f, -0.8819212914f, -0.9807852507f, -0.9951847196f, -0.9238795042f, -0.7730104327f, -0.5555702448f, -0.2902846634f, 1.0f, 0.9569403529f, 0.8314695954f, 0.6343932748f, 0.3826834261f, 0.09801714122f, -0.1950903237f, -0.4713967443f, -0.7071067691f, -0.8819212914f, -0.9807852507f, -0.9951847196f, -0.9238795042f, -0.7730104327f, -0.5555702448f, -0.2902846634f, 1.0f, 0.9569403529f, 0.8314695954f, 0.6343932748f, 0.3826834261f, 0.09801714122f, -0.1950903237f, -0.4713967443f, -0.7071067691f, -0.8819212914f, -0.9807852507f, -0.9951847196f, -0.9238795042f, -0.7730104327f, -0.5555702448f, -0.2902846634f, 1.0f, 0.9569403529f, 0.8314695954f, 0.6343932748f, 0.3826834261f, 0.09801714122f, -0.1950903237f, -0.4713967443f, -0.7071067691f, -0.8819212914f, -0.9807852507f, -0.9951847196f, -0.9238795042f, -0.7730104327f, -0.5555702448f, -0.2902846634f}; +constant float tc40[512] = {1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f}; +constant float tc43[512] = {1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f, 1.0f, 0.7071067691f, 6.123234263e-17f, -0.7071067691f}; +constant float tc41[512] = {1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f}; +constant float tc44[512] = {1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f, 1.0f, 0.9238795042f, 0.7071067691f, 0.3826834261f}; +constant float tc42[512] = {1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f}; +constant float tc45[512] = {1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f, 1.0f, 0.3826834261f, -0.7071067691f, -0.9238795042f}; + + + +constant float ts00[512] = {-0.0f, -0.003067956772f, -0.006135884672f, -0.009203754365f, -0.01227153838f, -0.01533920597f, -0.01840673015f, -0.02147408016f, -0.02454122901f, -0.02760814503f, -0.030674804f, -0.0337411724f, -0.03680722415f, -0.03987292573f, -0.0429382585f, -0.04600318149f, -0.04906767607f, -0.05213170499f, -0.05519524589f, -0.05825826526f, -0.061320737f, -0.06438262761f, -0.06744392216f, -0.07050457597f, -0.07356456667f, -0.07662386447f, -0.07968243957f, -0.08274026215f, -0.08579730988f, -0.08885355294f, -0.09190895408f, -0.09496349841f, -0.09801714122f, -0.1010698602f, -0.1041216329f, -0.1071724221f, -0.1102222055f, -0.1132709533f, -0.1163186282f, -0.1193652153f, -0.1224106774f, -0.1254549772f, -0.1284981072f, -0.1315400302f, -0.1345807016f, -0.1376201212f, -0.1406582445f, -0.1436950266f, -0.1467304677f, -0.1497645378f, -0.1527971923f, -0.1558284014f, -0.1588581502f, -0.161886394f, -0.1649131179f, -0.167938292f, -0.1709618866f, -0.1739838719f, -0.1770042181f, -0.1800228953f, -0.1830398887f, -0.1860551536f, -0.1890686601f, -0.1920803934f, -0.1950903237f, -0.1980984062f, -0.201104641f, -0.2041089684f, -0.2071113735f, -0.2101118416f, -0.2131103128f, -0.2161068022f, -0.2191012353f, -0.2220936269f, -0.2250839174f, -0.228072077f, -0.2310581058f, -0.234041959f, -0.2370236069f, -0.2400030196f, -0.2429801822f, -0.24595505f, -0.2489276081f, -0.2518978119f, -0.2548656464f, -0.2578310966f, -0.2607941031f, -0.2637546659f, -0.266712755f, -0.2696683109f, -0.2726213634f, -0.2755718231f, -0.27851969f, -0.2814649343f, -0.2844075263f, -0.2873474658f, -0.2902846634f, -0.2932191491f, -0.296150893f, -0.2990798354f, -0.3020059466f, -0.3049292266f, -0.3078496456f, -0.310767144f, -0.3136817515f, -0.3165933788f, -0.3195020258f, -0.3224076927f, -0.3253102899f, -0.3282098472f, -0.3311063051f, -0.3339996636f, -0.336889863f, -0.3397768736f, -0.3426607251f, -0.3455413282f, -0.3484186828f, -0.3512927592f, -0.3541635275f, -0.3570309579f, -0.3598950505f, -0.3627557158f, -0.3656129837f, -0.3684668243f, -0.3713172078f, -0.3741640747f, -0.3770074248f, -0.3798471987f, -0.3826834261f, -0.3855160475f, -0.3883450329f, -0.3911703825f, -0.3939920366f, -0.3968099952f, -0.3996241987f, -0.4024346471f, -0.4052413106f, -0.4080441594f, -0.4108431637f, -0.4136383235f, -0.4164295495f, -0.4192169011f, -0.4220002592f, -0.4247796834f, -0.4275550842f, -0.4303264916f, -0.433093816f, -0.4358570874f, -0.438616246f, -0.4413712621f, -0.4441221356f, -0.4468688369f, -0.449611336f, -0.4523495734f, -0.4550835788f, -0.4578132927f, -0.4605387151f, -0.4632597864f, -0.4659765065f, -0.4686888158f, -0.4713967443f, -0.4741002023f, -0.4767992198f, -0.479493767f, -0.4821837842f, -0.4848692417f, -0.4875501692f, -0.4902264774f, -0.492898196f, -0.4955652654f, -0.4982276559f, -0.5008853674f, -0.5035383701f, -0.5061866641f, -0.5088301301f, -0.5114688277f, -0.514102757f, -0.5167317986f, -0.5193560123f, -0.5219752789f, -0.5245896578f, -0.5271991491f, -0.5298036337f, -0.5324031115f, -0.534997642f, -0.5375870466f, -0.5401714444f, -0.5427507758f, -0.5453249812f, -0.5478940606f, -0.5504579544f, -0.5530167222f, -0.5555702448f, -0.5581185222f, -0.5606615543f, -0.5631993413f, -0.5657318234f, -0.5682589412f, -0.5707807541f, -0.573297143f, -0.5758081675f, -0.5783137679f, -0.5808139443f, -0.5833086371f, -0.5857978463f, -0.5882815719f, -0.5907596946f, -0.5932322741f, -0.5956993103f, -0.5981606841f, -0.6006164551f, -0.6030666232f, -0.6055110693f, -0.6079497933f, -0.6103827953f, -0.6128100753f, -0.6152315736f, -0.6176472902f, -0.6200572252f, -0.6224612594f, -0.6248595119f, -0.6272518039f, -0.6296382546f, -0.6320187449f, -0.6343932748f, -0.6367618442f, -0.6391244531f, -0.6414810419f, -0.6438315511f, -0.6461760402f, -0.64851439f, -0.6508466601f, -0.6531728506f, -0.6554928422f, -0.6578066945f, -0.6601143479f, -0.6624158025f, -0.6647109985f, -0.6669999361f, -0.6692826152f, -0.6715589762f, -0.6738290191f, -0.6760926843f, -0.6783500314f, -0.6806010008f, -0.6828455329f, -0.6850836873f, -0.6873153448f, -0.689540565f, -0.6917592287f, -0.6939714551f, -0.696177125f, -0.6983762383f, -0.7005687952f, -0.7027547359f, -0.7049340606f, -0.7071067691f, -0.7092728019f, -0.7114322186f, -0.7135848403f, -0.7157308459f, -0.7178700566f, -0.720002532f, -0.7221282125f, -0.724247098f, -0.726359129f, -0.728464365f, -0.7305627465f, -0.7326542735f, -0.7347388864f, -0.7368165851f, -0.73888731f, -0.7409511209f, -0.7430079579f, -0.7450577617f, -0.7471005917f, -0.7491363883f, -0.7511651516f, -0.7531868219f, -0.7552013993f, -0.7572088242f, -0.7592092156f, -0.761202395f, -0.7631884217f, -0.7651672363f, -0.7671388984f, -0.7691033483f, -0.7710605264f, -0.7730104327f, -0.7749531269f, -0.7768884897f, -0.7788165212f, -0.7807372212f, -0.7826505899f, -0.7845565677f, -0.786455214f, -0.7883464098f, -0.7902302146f, -0.7921065688f, -0.7939754725f, -0.7958369255f, -0.7976908684f, -0.7995372415f, -0.801376164f, -0.8032075167f, -0.8050313592f, -0.8068475723f, -0.8086561561f, -0.81045717f, -0.8122506142f, -0.8140363097f, -0.8158144355f, -0.8175848126f, -0.8193475008f, -0.8211025f, -0.8228498101f, -0.8245893121f, -0.8263210654f, -0.8280450702f, -0.8297612071f, -0.8314695954f, -0.8331701756f, -0.8348628879f, -0.8365477324f, -0.838224709f, -0.8398938179f, -0.8415549994f, -0.8432082534f, -0.84485358f, -0.8464909196f, -0.8481203318f, -0.8497417569f, -0.851355195f, -0.8529605865f, -0.854557991f, -0.8561473489f, -0.8577286005f, -0.8593018055f, -0.8608669639f, -0.8624239564f, -0.8639728427f, -0.8655136228f, -0.867046237f, -0.8685706854f, -0.8700869679f, -0.8715950847f, -0.8730949759f, -0.8745866418f, -0.8760700822f, -0.8775452971f, -0.8790122271f, -0.8804708719f, -0.8819212914f, -0.8833633661f, -0.8847970963f, -0.8862225413f, -0.8876396418f, -0.8890483379f, -0.8904487491f, -0.8918406963f, -0.893224299f, -0.8945994973f, -0.8959662318f, -0.8973245621f, -0.8986744881f, -0.9000158906f, -0.9013488293f, -0.9026733041f, -0.903989315f, -0.9052967429f, -0.9065957069f, -0.9078860879f, -0.909168005f, -0.9104412794f, -0.9117060304f, -0.9129621983f, -0.9142097831f, -0.9154487252f, -0.9166790843f, -0.9179008007f, -0.9191138744f, -0.9203183055f, -0.9215140343f, -0.9227011204f, -0.9238795042f, -0.9250492454f, -0.9262102246f, -0.9273625016f, -0.9285060763f, -0.9296408892f, -0.9307669401f, -0.9318842888f, -0.932992816f, -0.9340925217f, -0.9351835251f, -0.9362656474f, -0.9373390079f, -0.9384035468f, -0.9394592047f, -0.940506041f, -0.9415440559f, -0.9425731897f, -0.9435934424f, -0.9446048141f, -0.9456073046f, -0.946600914f, -0.9475855827f, -0.9485613704f, -0.9495281577f, -0.950486064f, -0.9514350295f, -0.9523749948f, -0.9533060193f, -0.9542281032f, -0.9551411867f, -0.95604527f, -0.9569403529f, -0.9578264356f, -0.9587034583f, -0.9595715404f, -0.9604305029f, -0.9612804651f, -0.9621214271f, -0.9629532695f, -0.963776052f, -0.9645897746f, -0.9653944373f, -0.9661899805f, -0.9669764638f, -0.9677538276f, -0.9685220718f, -0.9692812562f, -0.9700312614f, -0.9707721472f, -0.9715039134f, -0.9722265005f, -0.9729399681f, -0.9736442566f, -0.974339366f, -0.9750253558f, -0.975702107f, -0.9763697386f, -0.9770281315f, -0.9776773453f, -0.97831738f, -0.9789481759f, -0.9795697927f, -0.9801821113f, -0.9807852507f, -0.9813792109f, -0.9819638729f, -0.9825392962f, -0.9831054807f, -0.9836624265f, -0.9842100739f, -0.9847484827f, -0.9852776527f, -0.9857975245f, -0.9863080978f, -0.9868093729f, -0.9873014092f, -0.9877841473f, -0.988257587f, -0.9887216687f, -0.9891765118f, -0.9896219969f, -0.9900581837f, -0.9904850721f, -0.9909026623f, -0.9913108349f, -0.9917097688f, -0.9920992851f, -0.9924795628f, -0.9928504229f, -0.993211925f, -0.9935641289f, -0.9939069748f, -0.9942404628f, -0.9945645928f, -0.9948793054f, -0.9951847196f, -0.9954807758f, -0.9957674146f, -0.9960446954f, -0.9963126183f, -0.9965711236f, -0.996820271f, -0.9970600605f, -0.9972904325f, -0.9975114465f, -0.997723043f, -0.9979252815f, -0.9981181026f, -0.9983015656f, -0.9984755516f, -0.9986402392f, -0.9987954497f, -0.9989413023f, -0.9990777373f, -0.9992047548f, -0.9993223548f, -0.9994305968f, -0.9995294213f, -0.9996188283f, -0.9996988177f, -0.9997693896f, -0.9998306036f, -0.9998823404f, -0.9999247193f, -0.9999576211f, -0.9999811649f, -0.9999952912f}; +constant float ts03[512] = {-1.0f, -0.9999952912f, -0.9999811649f, -0.9999576211f, -0.9999247193f, -0.9998823404f, -0.9998306036f, -0.9997693896f, -0.9996988177f, -0.9996188283f, -0.9995294213f, -0.9994305968f, -0.9993223548f, -0.9992047548f, -0.9990777373f, -0.9989413023f, -0.9987954497f, -0.9986402392f, -0.9984755516f, -0.9983015656f, -0.9981181026f, -0.9979252815f, -0.997723043f, -0.9975114465f, -0.9972904325f, -0.9970600605f, -0.996820271f, -0.9965711236f, -0.9963126183f, -0.9960446954f, -0.9957674146f, -0.9954807758f, -0.9951847196f, -0.9948793054f, -0.9945645928f, -0.9942404628f, -0.9939069748f, -0.9935641289f, -0.993211925f, -0.9928504229f, -0.9924795628f, -0.9920992851f, -0.9917097688f, -0.9913108349f, -0.9909026623f, -0.9904850721f, -0.9900581837f, -0.9896219969f, -0.9891765118f, -0.9887216687f, -0.988257587f, -0.9877841473f, -0.9873014092f, -0.9868093729f, -0.9863080978f, -0.9857975245f, -0.9852776527f, -0.9847484827f, -0.9842100739f, -0.9836624265f, -0.9831054807f, -0.9825392962f, -0.9819638729f, -0.9813792109f, -0.9807852507f, -0.9801821113f, -0.9795697927f, -0.9789481759f, -0.97831738f, -0.9776773453f, -0.9770281315f, -0.9763697386f, -0.975702107f, -0.9750253558f, -0.974339366f, -0.9736442566f, -0.9729399681f, -0.9722265005f, -0.9715039134f, -0.9707721472f, -0.9700312614f, -0.9692812562f, -0.9685220718f, -0.9677538276f, -0.9669764638f, -0.9661899805f, -0.9653944373f, -0.9645897746f, -0.963776052f, -0.9629532695f, -0.9621214271f, -0.9612804651f, -0.9604305029f, -0.9595715404f, -0.9587034583f, -0.9578264356f, -0.9569403529f, -0.95604527f, -0.9551411867f, -0.9542281032f, -0.9533060193f, -0.9523749948f, -0.9514350295f, -0.950486064f, -0.9495281577f, -0.9485613704f, -0.9475855827f, -0.946600914f, -0.9456073046f, -0.9446048141f, -0.9435934424f, -0.9425731897f, -0.9415440559f, -0.940506041f, -0.9394592047f, -0.9384035468f, -0.9373390079f, -0.9362656474f, -0.9351835251f, -0.9340925217f, -0.932992816f, -0.9318842888f, -0.9307669401f, -0.9296408892f, -0.9285060763f, -0.9273625016f, -0.9262102246f, -0.9250492454f, -0.9238795042f, -0.9227011204f, -0.9215140343f, -0.9203183055f, -0.9191138744f, -0.9179008007f, -0.9166790843f, -0.9154487252f, -0.9142097831f, -0.9129621983f, -0.9117060304f, -0.9104412794f, -0.909168005f, -0.9078860879f, -0.9065957069f, -0.9052967429f, -0.903989315f, -0.9026733041f, -0.9013488293f, -0.9000158906f, -0.8986744881f, -0.8973245621f, -0.8959662318f, -0.8945994973f, -0.893224299f, -0.8918406963f, -0.8904487491f, -0.8890483379f, -0.8876396418f, -0.8862225413f, -0.8847970963f, -0.8833633661f, -0.8819212914f, -0.8804708719f, -0.8790122271f, -0.8775452971f, -0.8760700822f, -0.8745866418f, -0.8730949759f, -0.8715950847f, -0.8700869679f, -0.8685706854f, -0.867046237f, -0.8655136228f, -0.8639728427f, -0.8624239564f, -0.8608669639f, -0.8593018055f, -0.8577286005f, -0.8561473489f, -0.854557991f, -0.8529605865f, -0.851355195f, -0.8497417569f, -0.8481203318f, -0.8464909196f, -0.84485358f, -0.8432082534f, -0.8415549994f, -0.8398938179f, -0.838224709f, -0.8365477324f, -0.8348628879f, -0.8331701756f, -0.8314695954f, -0.8297612071f, -0.8280450702f, -0.8263210654f, -0.8245893121f, -0.8228498101f, -0.8211025f, -0.8193475008f, -0.8175848126f, -0.8158144355f, -0.8140363097f, -0.8122506142f, -0.81045717f, -0.8086561561f, -0.8068475723f, -0.8050313592f, -0.8032075167f, -0.801376164f, -0.7995372415f, -0.7976908684f, -0.7958369255f, -0.7939754725f, -0.7921065688f, -0.7902302146f, -0.7883464098f, -0.786455214f, -0.7845565677f, -0.7826505899f, -0.7807372212f, -0.7788165212f, -0.7768884897f, -0.7749531269f, -0.7730104327f, -0.7710605264f, -0.7691033483f, -0.7671388984f, -0.7651672363f, -0.7631884217f, -0.761202395f, -0.7592092156f, -0.7572088242f, -0.7552013993f, -0.7531868219f, -0.7511651516f, -0.7491363883f, -0.7471005917f, -0.7450577617f, -0.7430079579f, -0.7409511209f, -0.73888731f, -0.7368165851f, -0.7347388864f, -0.7326542735f, -0.7305627465f, -0.728464365f, -0.726359129f, -0.724247098f, -0.7221282125f, -0.720002532f, -0.7178700566f, -0.7157308459f, -0.7135848403f, -0.7114322186f, -0.7092728019f, -0.7071067691f, -0.7049340606f, -0.7027547359f, -0.7005687952f, -0.6983762383f, -0.696177125f, -0.6939714551f, -0.6917592287f, -0.689540565f, -0.6873153448f, -0.6850836873f, -0.6828455329f, -0.6806010008f, -0.6783500314f, -0.6760926843f, -0.6738290191f, -0.6715589762f, -0.6692826152f, -0.6669999361f, -0.6647109985f, -0.6624158025f, -0.6601143479f, -0.6578066945f, -0.6554928422f, -0.6531728506f, -0.6508466601f, -0.64851439f, -0.6461760402f, -0.6438315511f, -0.6414810419f, -0.6391244531f, -0.6367618442f, -0.6343932748f, -0.6320187449f, -0.6296382546f, -0.6272518039f, -0.6248595119f, -0.6224612594f, -0.6200572252f, -0.6176472902f, -0.6152315736f, -0.6128100753f, -0.6103827953f, -0.6079497933f, -0.6055110693f, -0.6030666232f, -0.6006164551f, -0.5981606841f, -0.5956993103f, -0.5932322741f, -0.5907596946f, -0.5882815719f, -0.5857978463f, -0.5833086371f, -0.5808139443f, -0.5783137679f, -0.5758081675f, -0.573297143f, -0.5707807541f, -0.5682589412f, -0.5657318234f, -0.5631993413f, -0.5606615543f, -0.5581185222f, -0.5555702448f, -0.5530167222f, -0.5504579544f, -0.5478940606f, -0.5453249812f, -0.5427507758f, -0.5401714444f, -0.5375870466f, -0.534997642f, -0.5324031115f, -0.5298036337f, -0.5271991491f, -0.5245896578f, -0.5219752789f, -0.5193560123f, -0.5167317986f, -0.514102757f, -0.5114688277f, -0.5088301301f, -0.5061866641f, -0.5035383701f, -0.5008853674f, -0.4982276559f, -0.4955652654f, -0.492898196f, -0.4902264774f, -0.4875501692f, -0.4848692417f, -0.4821837842f, -0.479493767f, -0.4767992198f, -0.4741002023f, -0.4713967443f, -0.4686888158f, -0.4659765065f, -0.4632597864f, -0.4605387151f, -0.4578132927f, -0.4550835788f, -0.4523495734f, -0.449611336f, -0.4468688369f, -0.4441221356f, -0.4413712621f, -0.438616246f, -0.4358570874f, -0.433093816f, -0.4303264916f, -0.4275550842f, -0.4247796834f, -0.4220002592f, -0.4192169011f, -0.4164295495f, -0.4136383235f, -0.4108431637f, -0.4080441594f, -0.4052413106f, -0.4024346471f, -0.3996241987f, -0.3968099952f, -0.3939920366f, -0.3911703825f, -0.3883450329f, -0.3855160475f, -0.3826834261f, -0.3798471987f, -0.3770074248f, -0.3741640747f, -0.3713172078f, -0.3684668243f, -0.3656129837f, -0.3627557158f, -0.3598950505f, -0.3570309579f, -0.3541635275f, -0.3512927592f, -0.3484186828f, -0.3455413282f, -0.3426607251f, -0.3397768736f, -0.336889863f, -0.3339996636f, -0.3311063051f, -0.3282098472f, -0.3253102899f, -0.3224076927f, -0.3195020258f, -0.3165933788f, -0.3136817515f, -0.310767144f, -0.3078496456f, -0.3049292266f, -0.3020059466f, -0.2990798354f, -0.296150893f, -0.2932191491f, -0.2902846634f, -0.2873474658f, -0.2844075263f, -0.2814649343f, -0.27851969f, -0.2755718231f, -0.2726213634f, -0.2696683109f, -0.266712755f, -0.2637546659f, -0.2607941031f, -0.2578310966f, -0.2548656464f, -0.2518978119f, -0.2489276081f, -0.24595505f, -0.2429801822f, -0.2400030196f, -0.2370236069f, -0.234041959f, -0.2310581058f, -0.228072077f, -0.2250839174f, -0.2220936269f, -0.2191012353f, -0.2161068022f, -0.2131103128f, -0.2101118416f, -0.2071113735f, -0.2041089684f, -0.201104641f, -0.1980984062f, -0.1950903237f, -0.1920803934f, -0.1890686601f, -0.1860551536f, -0.1830398887f, -0.1800228953f, -0.1770042181f, -0.1739838719f, -0.1709618866f, -0.167938292f, -0.1649131179f, -0.161886394f, -0.1588581502f, -0.1558284014f, -0.1527971923f, -0.1497645378f, -0.1467304677f, -0.1436950266f, -0.1406582445f, -0.1376201212f, -0.1345807016f, -0.1315400302f, -0.1284981072f, -0.1254549772f, -0.1224106774f, -0.1193652153f, -0.1163186282f, -0.1132709533f, -0.1102222055f, -0.1071724221f, -0.1041216329f, -0.1010698602f, -0.09801714122f, -0.09496349841f, -0.09190895408f, -0.08885355294f, -0.08579730988f, -0.08274026215f, -0.07968243957f, -0.07662386447f, -0.07356456667f, -0.07050457597f, -0.06744392216f, -0.06438262761f, -0.061320737f, -0.05825826526f, -0.05519524589f, -0.05213170499f, -0.04906767607f, -0.04600318149f, -0.0429382585f, -0.03987292573f, -0.03680722415f, -0.0337411724f, -0.030674804f, -0.02760814503f, -0.02454122901f, -0.02147408016f, -0.01840673015f, -0.01533920597f, -0.01227153838f, -0.009203754365f, -0.006135884672f, -0.003067956772f}; +constant float ts01[512] = {-0.0f, -0.001533980132f, -0.003067956772f, -0.004601926077f, -0.006135884672f, -0.007669828832f, -0.009203754365f, -0.01073765941f, -0.01227153838f, -0.01380538847f, -0.01533920597f, -0.01687298715f, -0.01840673015f, -0.01994042844f, -0.02147408016f, -0.02300768159f, -0.02454122901f, -0.02607471868f, -0.02760814503f, -0.02914150804f, -0.030674804f, -0.03220802546f, -0.0337411724f, -0.03527423739f, -0.03680722415f, -0.03834012151f, -0.03987292573f, -0.04140564054f, -0.0429382585f, -0.04447077215f, -0.04600318149f, -0.04753548279f, -0.04906767607f, -0.05059975013f, -0.05213170499f, -0.05366353691f, -0.05519524589f, -0.05672682077f, -0.05825826526f, -0.05978957191f, -0.061320737f, -0.06285175681f, -0.06438262761f, -0.06591334939f, -0.06744392216f, -0.06897433102f, -0.07050457597f, -0.07203464955f, -0.07356456667f, -0.07509429753f, -0.07662386447f, -0.07815324515f, -0.07968243957f, -0.08121144772f, -0.08274026215f, -0.08426889032f, -0.08579730988f, -0.08732553571f, -0.08885355294f, -0.09038136154f, -0.09190895408f, -0.09343633801f, -0.09496349841f, -0.09649042785f, -0.09801714122f, -0.09954361618f, -0.1010698602f, -0.1025958657f, -0.1041216329f, -0.1056471542f, -0.1071724221f, -0.1086974442f, -0.1102222055f, -0.1117467135f, -0.1132709533f, -0.1147949249f, -0.1163186282f, -0.1178420633f, -0.1193652153f, -0.1208880842f, -0.1224106774f, -0.1239329726f, -0.1254549772f, -0.1269766986f, -0.1284981072f, -0.1300192177f, -0.1315400302f, -0.1330605298f, -0.1345807016f, -0.1361005753f, -0.1376201212f, -0.1391393393f, -0.1406582445f, -0.1421768069f, -0.1436950266f, -0.1452129185f, -0.1467304677f, -0.1482476741f, -0.1497645378f, -0.1512810439f, -0.1527971923f, -0.1543129683f, -0.1558284014f, -0.1573434621f, -0.1588581502f, -0.1603724509f, -0.161886394f, -0.1633999497f, -0.1649131179f, -0.1664258987f, -0.167938292f, -0.169450298f, -0.1709618866f, -0.1724730879f, -0.1739838719f, -0.1754942536f, -0.1770042181f, -0.1785137653f, -0.1800228953f, -0.1815316081f, -0.1830398887f, -0.1845477372f, -0.1860551536f, -0.1875621229f, -0.1890686601f, -0.1905747503f, -0.1920803934f, -0.1935855895f, -0.1950903237f, -0.1965945959f, -0.1980984062f, -0.1996017545f, -0.201104641f, -0.2026070356f, -0.2041089684f, -0.2056104094f, -0.2071113735f, -0.208611846f, -0.2101118416f, -0.2116113305f, -0.2131103128f, -0.2146088183f, -0.2161068022f, -0.2176042795f, -0.2191012353f, -0.2205976844f, -0.2220936269f, -0.2235890329f, -0.2250839174f, -0.2265782654f, -0.228072077f, -0.2295653671f, -0.2310581058f, -0.2325503081f, -0.234041959f, -0.2355330586f, -0.2370236069f, -0.2385135889f, -0.2400030196f, -0.241491884f, -0.2429801822f, -0.2444678992f, -0.24595505f, -0.2474416196f, -0.2489276081f, -0.2504130006f, -0.2518978119f, -0.2533820271f, -0.2548656464f, -0.2563486695f, -0.2578310966f, -0.2593129277f, -0.2607941031f, -0.2622747123f, -0.2637546659f, -0.2652340233f, -0.266712755f, -0.2681908607f, -0.2696683109f, -0.271145165f, -0.2726213634f, -0.2740969062f, -0.2755718231f, -0.2770460844f, -0.27851969f, -0.27999264f, -0.2814649343f, -0.282936573f, -0.2844075263f, -0.2858778238f, -0.2873474658f, -0.2888164222f, -0.2902846634f, -0.291752249f, -0.2932191491f, -0.2946853638f, -0.296150893f, -0.2976157069f, -0.2990798354f, -0.3005432487f, -0.3020059466f, -0.3034679592f, -0.3049292266f, -0.3063898087f, -0.3078496456f, -0.3093087673f, -0.310767144f, -0.3122248054f, -0.3136817515f, -0.3151379228f, -0.3165933788f, -0.3180480897f, -0.3195020258f, -0.3209552467f, -0.3224076927f, -0.3238593638f, -0.3253102899f, -0.3267604411f, -0.3282098472f, -0.3296584487f, -0.3311063051f, -0.3325533569f, -0.3339996636f, -0.3354451358f, -0.336889863f, -0.3383337557f, -0.3397768736f, -0.3412192166f, -0.3426607251f, -0.344101429f, -0.3455413282f, -0.3469804227f, -0.3484186828f, -0.3498561382f, -0.3512927592f, -0.3527285457f, -0.3541635275f, -0.3555976748f, -0.3570309579f, -0.3584634066f, -0.3598950505f, -0.3613258004f, -0.3627557158f, -0.3641847968f, -0.3656129837f, -0.3670403361f, -0.3684668243f, -0.3698924482f, -0.3713172078f, -0.3727410734f, -0.3741640747f, -0.3755861819f, -0.3770074248f, -0.3784277439f, -0.3798471987f, -0.3812657595f, -0.3826834261f, -0.3841001987f, -0.3855160475f, -0.3869310021f, -0.3883450329f, -0.3897581697f, -0.3911703825f, -0.3925816715f, -0.3939920366f, -0.3954014778f, -0.3968099952f, -0.3982175589f, -0.3996241987f, -0.4010298848f, -0.4024346471f, -0.4038384557f, -0.4052413106f, -0.4066432118f, -0.4080441594f, -0.4094441533f, -0.4108431637f, -0.4122412205f, -0.4136383235f, -0.4150344133f, -0.4164295495f, -0.4178237021f, -0.4192169011f, -0.4206090868f, -0.4220002592f, -0.4233904779f, -0.4247796834f, -0.4261678755f, -0.4275550842f, -0.4289412796f, -0.4303264916f, -0.4317106605f, -0.433093816f, -0.4344759583f, -0.4358570874f, -0.4372371733f, -0.438616246f, -0.4399942756f, -0.4413712621f, -0.4427472353f, -0.4441221356f, -0.4454960227f, -0.4468688369f, -0.448240608f, -0.449611336f, -0.4509809911f, -0.4523495734f, -0.4537171125f, -0.4550835788f, -0.4564489722f, -0.4578132927f, -0.4591765404f, -0.4605387151f, -0.4618997872f, -0.4632597864f, -0.4646186829f, -0.4659765065f, -0.4673331976f, -0.4686888158f, -0.4700433314f, -0.4713967443f, -0.4727490246f, -0.4741002023f, -0.4754502773f, -0.4767992198f, -0.4781470597f, -0.479493767f, -0.4808393419f, -0.4821837842f, -0.4835270643f, -0.4848692417f, -0.4862102866f, -0.4875501692f, -0.4888888896f, -0.4902264774f, -0.4915629029f, -0.492898196f, -0.4942322969f, -0.4955652654f, -0.4968970418f, -0.4982276559f, -0.4995571077f, -0.5008853674f, -0.5022124648f, -0.5035383701f, -0.5048630834f, -0.5061866641f, -0.5075089931f, -0.5088301301f, -0.510150075f, -0.5114688277f, -0.5127863884f, -0.514102757f, -0.5154178739f, -0.5167317986f, -0.5180445313f, -0.5193560123f, -0.5206662416f, -0.5219752789f, -0.523283124f, -0.5245896578f, -0.5258949995f, -0.5271991491f, -0.5285019875f, -0.5298036337f, -0.5311040282f, -0.5324031115f, -0.5337010026f, -0.534997642f, -0.5362929702f, -0.5375870466f, -0.538879931f, -0.5401714444f, -0.5414617658f, -0.5427507758f, -0.5440385342f, -0.5453249812f, -0.5466101766f, -0.5478940606f, -0.5491766334f, -0.5504579544f, -0.5517379642f, -0.5530167222f, -0.5542941093f, -0.5555702448f, -0.5568450093f, -0.5581185222f, -0.5593907237f, -0.5606615543f, -0.5619311333f, -0.5631993413f, -0.564466238f, -0.5657318234f, -0.566996038f, -0.5682589412f, -0.5695205331f, -0.5707807541f, -0.5720396042f, -0.573297143f, -0.5745533705f, -0.5758081675f, -0.5770616531f, -0.5783137679f, -0.5795645714f, -0.5808139443f, -0.582062006f, -0.5833086371f, -0.584553957f, -0.5857978463f, -0.5870403647f, -0.5882815719f, -0.5895212889f, -0.5907596946f, -0.5919966698f, -0.5932322741f, -0.5944665074f, -0.5956993103f, -0.5969306827f, -0.5981606841f, -0.5993893147f, -0.6006164551f, -0.6018422246f, -0.6030666232f, -0.6042895317f, -0.6055110693f, -0.6067311168f, -0.6079497933f, -0.6091670394f, -0.6103827953f, -0.6115971804f, -0.6128100753f, -0.6140215397f, -0.6152315736f, -0.616440177f, -0.6176472902f, -0.618852973f, -0.6200572252f, -0.6212599874f, -0.6224612594f, -0.6236611009f, -0.6248595119f, -0.6260563731f, -0.6272518039f, -0.6284457445f, -0.6296382546f, -0.630829215f, -0.6320187449f, -0.6332067847f, -0.6343932748f, -0.6355783343f, -0.6367618442f, -0.6379439235f, -0.6391244531f, -0.6403034925f, -0.6414810419f, -0.6426570415f, -0.6438315511f, -0.6450045109f, -0.6461760402f, -0.6473459601f, -0.64851439f, -0.6496813297f, -0.6508466601f, -0.65201056f, -0.6531728506f, -0.6543335915f, -0.6554928422f, -0.6566505432f, -0.6578066945f, -0.6589612961f, -0.6601143479f, -0.6612658501f, -0.6624158025f, -0.6635641456f, -0.6647109985f, -0.6658562422f, -0.6669999361f, -0.6681420207f, -0.6692826152f, -0.6704215407f, -0.6715589762f, -0.6726947427f, -0.6738290191f, -0.6749616265f, -0.6760926843f, -0.6772221923f, -0.6783500314f, -0.6794763207f, -0.6806010008f, -0.6817240715f, -0.6828455329f, -0.683965385f, -0.6850836873f, -0.6862003207f, -0.6873153448f, -0.6884287596f, -0.689540565f, -0.6906507015f, -0.6917592287f, -0.6928661466f, -0.6939714551f, -0.6950750947f, -0.696177125f, -0.6972774863f, -0.6983762383f, -0.6994733214f, -0.7005687952f, -0.7016626f, -0.7027547359f, -0.7038452625f, -0.7049340606f, -0.7060212493f}; +constant float ts04[512] = {-0.7071067691f, -0.7081906199f, -0.7092728019f, -0.7103533745f, -0.7114322186f, -0.7125093937f, -0.7135848403f, -0.7146586776f, -0.7157308459f, -0.7168012857f, -0.7178700566f, -0.718937099f, -0.720002532f, -0.7210661769f, -0.7221282125f, -0.7231884599f, -0.724247098f, -0.7253039479f, -0.726359129f, -0.727412641f, -0.728464365f, -0.72951442f, -0.7305627465f, -0.7316094041f, -0.7326542735f, -0.7336974144f, -0.7347388864f, -0.7357785702f, -0.7368165851f, -0.7378528118f, -0.73888731f, -0.7399200797f, -0.7409511209f, -0.7419804335f, -0.7430079579f, -0.7440337539f, -0.7450577617f, -0.7460801005f, -0.7471005917f, -0.7481193542f, -0.7491363883f, -0.7501516342f, -0.7511651516f, -0.7521768212f, -0.7531868219f, -0.7541949749f, -0.7552013993f, -0.756205976f, -0.7572088242f, -0.7582098842f, -0.7592092156f, -0.7602066994f, -0.761202395f, -0.7621963024f, -0.7631884217f, -0.7641787529f, -0.7651672363f, -0.7661539912f, -0.7671388984f, -0.7681220174f, -0.7691033483f, -0.7700828314f, -0.7710605264f, -0.7720363736f, -0.7730104327f, -0.7739827037f, -0.7749531269f, -0.7759217024f, -0.7768884897f, -0.7778534293f, -0.7788165212f, -0.7797777653f, -0.7807372212f, -0.7816948295f, -0.7826505899f, -0.7836045027f, -0.7845565677f, -0.7855068445f, -0.786455214f, -0.7874017358f, -0.7883464098f, -0.7892892361f, -0.7902302146f, -0.7911693454f, -0.7921065688f, -0.7930419445f, -0.7939754725f, -0.7949071527f, -0.7958369255f, -0.796764791f, -0.7976908684f, -0.7986149788f, -0.7995372415f, -0.8004576564f, -0.801376164f, -0.8022928238f, -0.8032075167f, -0.8041203618f, -0.8050313592f, -0.8059403896f, -0.8068475723f, -0.8077528477f, -0.8086561561f, -0.8095576167f, -0.81045717f, -0.8113548756f, -0.8122506142f, -0.8131443858f, -0.8140363097f, -0.8149263263f, -0.8158144355f, -0.8167005777f, -0.8175848126f, -0.8184671402f, -0.8193475008f, -0.8202259541f, -0.8211025f, -0.8219771385f, -0.8228498101f, -0.8237205148f, -0.8245893121f, -0.8254561424f, -0.8263210654f, -0.8271840215f, -0.8280450702f, -0.8289040923f, -0.8297612071f, -0.8306164145f, -0.8314695954f, -0.832320869f, -0.8331701756f, -0.8340175152f, -0.8348628879f, -0.8357062936f, -0.8365477324f, -0.8373872042f, -0.838224709f, -0.8390602469f, -0.8398938179f, -0.8407253623f, -0.8415549994f, -0.8423826098f, -0.8432082534f, -0.8440318704f, -0.84485358f, -0.8456732631f, -0.8464909196f, -0.8473066092f, -0.8481203318f, -0.8489320278f, -0.8497417569f, -0.8505494595f, -0.851355195f, -0.8521589041f, -0.8529605865f, -0.8537603021f, -0.854557991f, -0.8553536534f, -0.8561473489f, -0.8569389582f, -0.8577286005f, -0.8585162163f, -0.8593018055f, -0.8600853682f, -0.8608669639f, -0.8616464734f, -0.8624239564f, -0.8631994128f, -0.8639728427f, -0.864744246f, -0.8655136228f, -0.866280973f, -0.867046237f, -0.8678094745f, -0.8685706854f, -0.8693298697f, -0.8700869679f, -0.8708420396f, -0.8715950847f, -0.8723460436f, -0.8730949759f, -0.8738418221f, -0.8745866418f, -0.8753293753f, -0.8760700822f, -0.8768087029f, -0.8775452971f, -0.8782798052f, -0.8790122271f, -0.8797426224f, -0.8804708719f, -0.8811970949f, -0.8819212914f, -0.882643342f, -0.8833633661f, -0.8840812445f, -0.8847970963f, -0.8855108619f, -0.8862225413f, -0.8869321346f, -0.8876396418f, -0.8883450627f, -0.8890483379f, -0.8897495866f, -0.8904487491f, -0.8911457658f, -0.8918406963f, -0.8925335407f, -0.893224299f, -0.893912971f, -0.8945994973f, -0.8952839375f, -0.8959662318f, -0.8966464996f, -0.8973245621f, -0.898000598f, -0.8986744881f, -0.8993462324f, -0.9000158906f, -0.900683403f, -0.9013488293f, -0.9020121694f, -0.9026733041f, -0.9033323526f, -0.903989315f, -0.9046440721f, -0.9052967429f, -0.905947268f, -0.9065957069f, -0.9072420001f, -0.9078860879f, -0.9085280895f, -0.909168005f, -0.9098057151f, -0.9104412794f, -0.9110747576f, -0.9117060304f, -0.9123351574f, -0.9129621983f, -0.9135870337f, -0.9142097831f, -0.914830327f, -0.9154487252f, -0.9160649776f, -0.9166790843f, -0.9172909856f, -0.9179008007f, -0.9185084105f, -0.9191138744f, -0.919717133f, -0.9203183055f, -0.920917213f, -0.9215140343f, -0.9221086502f, -0.9227011204f, -0.9232914448f, -0.9238795042f, -0.9244654775f, -0.9250492454f, -0.9256308079f, -0.9262102246f, -0.9267874956f, -0.9273625016f, -0.9279354215f, -0.9285060763f, -0.9290745854f, -0.9296408892f, -0.9302050471f, -0.9307669401f, -0.9313266873f, -0.9318842888f, -0.9324396253f, -0.932992816f, -0.9335438013f, -0.9340925217f, -0.9346391559f, -0.9351835251f, -0.9357256889f, -0.9362656474f, -0.9368034601f, -0.9373390079f, -0.9378723502f, -0.9384035468f, -0.9389324784f, -0.9394592047f, -0.9399837255f, -0.940506041f, -0.9410261512f, -0.9415440559f, -0.9420597553f, -0.9425731897f, -0.9430844188f, -0.9435934424f, -0.9441002607f, -0.9446048141f, -0.9451072216f, -0.9456073046f, -0.9461052418f, -0.946600914f, -0.9470943809f, -0.9475855827f, -0.9480745792f, -0.9485613704f, -0.9490458965f, -0.9495281577f, -0.9500082731f, -0.950486064f, -0.9509616494f, -0.9514350295f, -0.9519061446f, -0.9523749948f, -0.9528416395f, -0.9533060193f, -0.9537681937f, -0.9542281032f, -0.9546857476f, -0.9551411867f, -0.9555943608f, -0.95604527f, -0.9564939141f, -0.9569403529f, -0.9573845267f, -0.9578264356f, -0.9582660794f, -0.9587034583f, -0.9591386318f, -0.9595715404f, -0.9600021243f, -0.9604305029f, -0.9608566165f, -0.9612804651f, -0.9617020488f, -0.9621214271f, -0.9625384808f, -0.9629532695f, -0.9633657932f, -0.963776052f, -0.9641840458f, -0.9645897746f, -0.9649932384f, -0.9653944373f, -0.9657933712f, -0.9661899805f, -0.9665843844f, -0.9669764638f, -0.9673662782f, -0.9677538276f, -0.968139112f, -0.9685220718f, -0.9689028263f, -0.9692812562f, -0.9696573615f, -0.9700312614f, -0.9704028368f, -0.9707721472f, -0.971139133f, -0.9715039134f, -0.9718663096f, -0.9722265005f, -0.9725843668f, -0.9729399681f, -0.9732932448f, -0.9736442566f, -0.9739929438f, -0.974339366f, -0.9746835232f, -0.9750253558f, -0.9753648639f, -0.975702107f, -0.9760370851f, -0.9763697386f, -0.9767000675f, -0.9770281315f, -0.9773538709f, -0.9776773453f, -0.9779984951f, -0.97831738f, -0.9786339402f, -0.9789481759f, -0.9792601466f, -0.9795697927f, -0.9798771143f, -0.9801821113f, -0.9804848433f, -0.9807852507f, -0.9810833931f, -0.9813792109f, -0.9816727042f, -0.9819638729f, -0.982252717f, -0.9825392962f, -0.9828235507f, -0.9831054807f, -0.9833850861f, -0.9836624265f, -0.9839374423f, -0.9842100739f, -0.9844804406f, -0.9847484827f, -0.9850142598f, -0.9852776527f, -0.9855387211f, -0.9857975245f, -0.9860539436f, -0.9863080978f, -0.9865599275f, -0.9868093729f, -0.9870565534f, -0.9873014092f, -0.9875439405f, -0.9877841473f, -0.9880220294f, -0.988257587f, -0.9884908199f, -0.9887216687f, -0.9889502525f, -0.9891765118f, -0.9894004464f, -0.9896219969f, -0.9898412824f, -0.9900581837f, -0.99027282f, -0.9904850721f, -0.9906949997f, -0.9909026623f, -0.9911079407f, -0.9913108349f, -0.9915114641f, -0.9917097688f, -0.9919056892f, -0.9920992851f, -0.992290616f, -0.9924795628f, -0.9926661253f, -0.9928504229f, -0.9930323362f, -0.993211925f, -0.9933891892f, -0.9935641289f, -0.9937367439f, -0.9939069748f, -0.9940748811f, -0.9942404628f, -0.9944036603f, -0.9945645928f, -0.9947231412f, -0.9948793054f, -0.9950332046f, -0.9951847196f, -0.99533391f, -0.9954807758f, -0.9956252575f, -0.9957674146f, -0.9959072471f, -0.9960446954f, -0.9961798191f, -0.9963126183f, -0.9964430332f, -0.9965711236f, -0.9966968894f, -0.996820271f, -0.996941328f, -0.9970600605f, -0.9971764088f, -0.9972904325f, -0.9974021316f, -0.9975114465f, -0.9976184368f, -0.997723043f, -0.9978253245f, -0.9979252815f, -0.9980228543f, -0.9981181026f, -0.9982110262f, -0.9983015656f, -0.9983897209f, -0.9984755516f, -0.9985590577f, -0.9986402392f, -0.9987190366f, -0.9987954497f, -0.9988695383f, -0.9989413023f, -0.9990106821f, -0.9990777373f, -0.9991424084f, -0.9992047548f, -0.9992647767f, -0.9993223548f, -0.9993776679f, -0.9994305968f, -0.9994812012f, -0.9995294213f, -0.9995753169f, -0.9996188283f, -0.9996600151f, -0.9996988177f, -0.9997352958f, -0.9997693896f, -0.9998011589f, -0.9998306036f, -0.9998576641f, -0.9998823404f, -0.9999046922f, -0.9999247193f, -0.9999423623f, -0.9999576211f, -0.9999706149f, -0.9999811649f, -0.9999893904f, -0.9999952912f, -0.9999988079f}; +constant float ts02[512] = {-0.0f, -0.004601926077f, -0.009203754365f, -0.01380538847f, -0.01840673015f, -0.02300768159f, -0.02760814503f, -0.03220802546f, -0.03680722415f, -0.04140564054f, -0.04600318149f, -0.05059975013f, -0.05519524589f, -0.05978957191f, -0.06438262761f, -0.06897433102f, -0.07356456667f, -0.07815324515f, -0.08274026215f, -0.08732553571f, -0.09190895408f, -0.09649042785f, -0.1010698602f, -0.1056471542f, -0.1102222055f, -0.1147949249f, -0.1193652153f, -0.1239329726f, -0.1284981072f, -0.1330605298f, -0.1376201212f, -0.1421768069f, -0.1467304677f, -0.1512810439f, -0.1558284014f, -0.1603724509f, -0.1649131179f, -0.169450298f, -0.1739838719f, -0.1785137653f, -0.1830398887f, -0.1875621229f, -0.1920803934f, -0.1965945959f, -0.201104641f, -0.2056104094f, -0.2101118416f, -0.2146088183f, -0.2191012353f, -0.2235890329f, -0.228072077f, -0.2325503081f, -0.2370236069f, -0.241491884f, -0.24595505f, -0.2504130006f, -0.2548656464f, -0.2593129277f, -0.2637546659f, -0.2681908607f, -0.2726213634f, -0.2770460844f, -0.2814649343f, -0.2858778238f, -0.2902846634f, -0.2946853638f, -0.2990798354f, -0.3034679592f, -0.3078496456f, -0.3122248054f, -0.3165933788f, -0.3209552467f, -0.3253102899f, -0.3296584487f, -0.3339996636f, -0.3383337557f, -0.3426607251f, -0.3469804227f, -0.3512927592f, -0.3555976748f, -0.3598950505f, -0.3641847968f, -0.3684668243f, -0.3727410734f, -0.3770074248f, -0.3812657595f, -0.3855160475f, -0.3897581697f, -0.3939920366f, -0.3982175589f, -0.4024346471f, -0.4066432118f, -0.4108431637f, -0.4150344133f, -0.4192169011f, -0.4233904779f, -0.4275550842f, -0.4317106605f, -0.4358570874f, -0.4399942756f, -0.4441221356f, -0.448240608f, -0.4523495734f, -0.4564489722f, -0.4605387151f, -0.4646186829f, -0.4686888158f, -0.4727490246f, -0.4767992198f, -0.4808393419f, -0.4848692417f, -0.4888888896f, -0.492898196f, -0.4968970418f, -0.5008853674f, -0.5048630834f, -0.5088301301f, -0.5127863884f, -0.5167317986f, -0.5206662416f, -0.5245896578f, -0.5285019875f, -0.5324031115f, -0.5362929702f, -0.5401714444f, -0.5440385342f, -0.5478940606f, -0.5517379642f, -0.5555702448f, -0.5593907237f, -0.5631993413f, -0.566996038f, -0.5707807541f, -0.5745533705f, -0.5783137679f, -0.582062006f, -0.5857978463f, -0.5895212889f, -0.5932322741f, -0.5969306827f, -0.6006164551f, -0.6042895317f, -0.6079497933f, -0.6115971804f, -0.6152315736f, -0.618852973f, -0.6224612594f, -0.6260563731f, -0.6296382546f, -0.6332067847f, -0.6367618442f, -0.6403034925f, -0.6438315511f, -0.6473459601f, -0.6508466601f, -0.6543335915f, -0.6578066945f, -0.6612658501f, -0.6647109985f, -0.6681420207f, -0.6715589762f, -0.6749616265f, -0.6783500314f, -0.6817240715f, -0.6850836873f, -0.6884287596f, -0.6917592287f, -0.6950750947f, -0.6983762383f, -0.7016626f, -0.7049340606f, -0.7081906199f, -0.7114322186f, -0.7146586776f, -0.7178700566f, -0.7210661769f, -0.724247098f, -0.727412641f, -0.7305627465f, -0.7336974144f, -0.7368165851f, -0.7399200797f, -0.7430079579f, -0.7460801005f, -0.7491363883f, -0.7521768212f, -0.7552013993f, -0.7582098842f, -0.761202395f, -0.7641787529f, -0.7671388984f, -0.7700828314f, -0.7730104327f, -0.7759217024f, -0.7788165212f, -0.7816948295f, -0.7845565677f, -0.7874017358f, -0.7902302146f, -0.7930419445f, -0.7958369255f, -0.7986149788f, -0.801376164f, -0.8041203618f, -0.8068475723f, -0.8095576167f, -0.8122506142f, -0.8149263263f, -0.8175848126f, -0.8202259541f, -0.8228498101f, -0.8254561424f, -0.8280450702f, -0.8306164145f, -0.8331701756f, -0.8357062936f, -0.838224709f, -0.8407253623f, -0.8432082534f, -0.8456732631f, -0.8481203318f, -0.8505494595f, -0.8529605865f, -0.8553536534f, -0.8577286005f, -0.8600853682f, -0.8624239564f, -0.864744246f, -0.867046237f, -0.8693298697f, -0.8715950847f, -0.8738418221f, -0.8760700822f, -0.8782798052f, -0.8804708719f, -0.882643342f, -0.8847970963f, -0.8869321346f, -0.8890483379f, -0.8911457658f, -0.893224299f, -0.8952839375f, -0.8973245621f, -0.8993462324f, -0.9013488293f, -0.9033323526f, -0.9052967429f, -0.9072420001f, -0.909168005f, -0.9110747576f, -0.9129621983f, -0.914830327f, -0.9166790843f, -0.9185084105f, -0.9203183055f, -0.9221086502f, -0.9238795042f, -0.9256308079f, -0.9273625016f, -0.9290745854f, -0.9307669401f, -0.9324396253f, -0.9340925217f, -0.9357256889f, -0.9373390079f, -0.9389324784f, -0.940506041f, -0.9420597553f, -0.9435934424f, -0.9451072216f, -0.946600914f, -0.9480745792f, -0.9495281577f, -0.9509616494f, -0.9523749948f, -0.9537681937f, -0.9551411867f, -0.9564939141f, -0.9578264356f, -0.9591386318f, -0.9604305029f, -0.9617020488f, -0.9629532695f, -0.9641840458f, -0.9653944373f, -0.9665843844f, -0.9677538276f, -0.9689028263f, -0.9700312614f, -0.971139133f, -0.9722265005f, -0.9732932448f, -0.974339366f, -0.9753648639f, -0.9763697386f, -0.9773538709f, -0.97831738f, -0.9792601466f, -0.9801821113f, -0.9810833931f, -0.9819638729f, -0.9828235507f, -0.9836624265f, -0.9844804406f, -0.9852776527f, -0.9860539436f, -0.9868093729f, -0.9875439405f, -0.988257587f, -0.9889502525f, -0.9896219969f, -0.99027282f, -0.9909026623f, -0.9915114641f, -0.9920992851f, -0.9926661253f, -0.993211925f, -0.9937367439f, -0.9942404628f, -0.9947231412f, -0.9951847196f, -0.9956252575f, -0.9960446954f, -0.9964430332f, -0.996820271f, -0.9971764088f, -0.9975114465f, -0.9978253245f, -0.9981181026f, -0.9983897209f, -0.9986402392f, -0.9988695383f, -0.9990777373f, -0.9992647767f, -0.9994305968f, -0.9995753169f, -0.9996988177f, -0.9998011589f, -0.9998823404f, -0.9999423623f, -0.9999811649f, -0.9999988079f, -0.9999952912f, -0.9999706149f, -0.9999247193f, -0.9998576641f, -0.9997693896f, -0.9996600151f, -0.9995294213f, -0.9993776679f, -0.9992047548f, -0.9990106821f, -0.9987954497f, -0.9985590577f, -0.9983015656f, -0.9980228543f, -0.997723043f, -0.9974021316f, -0.9970600605f, -0.9966968894f, -0.9963126183f, -0.9959072471f, -0.9954807758f, -0.9950332046f, -0.9945645928f, -0.9940748811f, -0.9935641289f, -0.9930323362f, -0.9924795628f, -0.9919056892f, -0.9913108349f, -0.9906949997f, -0.9900581837f, -0.9894004464f, -0.9887216687f, -0.9880220294f, -0.9873014092f, -0.9865599275f, -0.9857975245f, -0.9850142598f, -0.9842100739f, -0.9833850861f, -0.9825392962f, -0.9816727042f, -0.9807852507f, -0.9798771143f, -0.9789481759f, -0.9779984951f, -0.9770281315f, -0.9760370851f, -0.9750253558f, -0.9739929438f, -0.9729399681f, -0.9718663096f, -0.9707721472f, -0.9696573615f, -0.9685220718f, -0.9673662782f, -0.9661899805f, -0.9649932384f, -0.963776052f, -0.9625384808f, -0.9612804651f, -0.9600021243f, -0.9587034583f, -0.9573845267f, -0.95604527f, -0.9546857476f, -0.9533060193f, -0.9519061446f, -0.950486064f, -0.9490458965f, -0.9475855827f, -0.9461052418f, -0.9446048141f, -0.9430844188f, -0.9415440559f, -0.9399837255f, -0.9384035468f, -0.9368034601f, -0.9351835251f, -0.9335438013f, -0.9318842888f, -0.9302050471f, -0.9285060763f, -0.9267874956f, -0.9250492454f, -0.9232914448f, -0.9215140343f, -0.919717133f, -0.9179008007f, -0.9160649776f, -0.9142097831f, -0.9123351574f, -0.9104412794f, -0.9085280895f, -0.9065957069f, -0.9046440721f, -0.9026733041f, -0.900683403f, -0.8986744881f, -0.8966464996f, -0.8945994973f, -0.8925335407f, -0.8904487491f, -0.8883450627f, -0.8862225413f, -0.8840812445f, -0.8819212914f, -0.8797426224f, -0.8775452971f, -0.8753293753f, -0.8730949759f, -0.8708420396f, -0.8685706854f, -0.866280973f, -0.8639728427f, -0.8616464734f, -0.8593018055f, -0.8569389582f, -0.854557991f, -0.8521589041f, -0.8497417569f, -0.8473066092f, -0.84485358f, -0.8423826098f, -0.8398938179f, -0.8373872042f, -0.8348628879f, -0.832320869f, -0.8297612071f, -0.8271840215f, -0.8245893121f, -0.8219771385f, -0.8193475008f, -0.8167005777f, -0.8140363097f, -0.8113548756f, -0.8086561561f, -0.8059403896f, -0.8032075167f, -0.8004576564f, -0.7976908684f, -0.7949071527f, -0.7921065688f, -0.7892892361f, -0.786455214f, -0.7836045027f, -0.7807372212f, -0.7778534293f, -0.7749531269f, -0.7720363736f, -0.7691033483f, -0.7661539912f, -0.7631884217f, -0.7602066994f, -0.7572088242f, -0.7541949749f, -0.7511651516f, -0.7481193542f, -0.7450577617f, -0.7419804335f, -0.73888731f, -0.7357785702f, -0.7326542735f, -0.72951442f, -0.726359129f, -0.7231884599f, -0.720002532f, -0.7168012857f, -0.7135848403f, -0.7103533745f}; +constant float ts05[512] = {-0.7071067691f, -0.7038452625f, -0.7005687952f, -0.6972774863f, -0.6939714551f, -0.6906507015f, -0.6873153448f, -0.683965385f, -0.6806010008f, -0.6772221923f, -0.6738290191f, -0.6704215407f, -0.6669999361f, -0.6635641456f, -0.6601143479f, -0.6566505432f, -0.6531728506f, -0.6496813297f, -0.6461760402f, -0.6426570415f, -0.6391244531f, -0.6355783343f, -0.6320187449f, -0.6284457445f, -0.6248595119f, -0.6212599874f, -0.6176472902f, -0.6140215397f, -0.6103827953f, -0.6067311168f, -0.6030666232f, -0.5993893147f, -0.5956993103f, -0.5919966698f, -0.5882815719f, -0.584553957f, -0.5808139443f, -0.5770616531f, -0.573297143f, -0.5695205331f, -0.5657318234f, -0.5619311333f, -0.5581185222f, -0.5542941093f, -0.5504579544f, -0.5466101766f, -0.5427507758f, -0.538879931f, -0.534997642f, -0.5311040282f, -0.5271991491f, -0.523283124f, -0.5193560123f, -0.5154178739f, -0.5114688277f, -0.5075089931f, -0.5035383701f, -0.4995571077f, -0.4955652654f, -0.4915629029f, -0.4875501692f, -0.4835270643f, -0.479493767f, -0.4754502773f, -0.4713967443f, -0.4673331976f, -0.4632597864f, -0.4591765404f, -0.4550835788f, -0.4509809911f, -0.4468688369f, -0.4427472353f, -0.438616246f, -0.4344759583f, -0.4303264916f, -0.4261678755f, -0.4220002592f, -0.4178237021f, -0.4136383235f, -0.4094441533f, -0.4052413106f, -0.4010298848f, -0.3968099952f, -0.3925816715f, -0.3883450329f, -0.3841001987f, -0.3798471987f, -0.3755861819f, -0.3713172078f, -0.3670403361f, -0.3627557158f, -0.3584634066f, -0.3541635275f, -0.3498561382f, -0.3455413282f, -0.3412192166f, -0.336889863f, -0.3325533569f, -0.3282098472f, -0.3238593638f, -0.3195020258f, -0.3151379228f, -0.310767144f, -0.3063898087f, -0.3020059466f, -0.2976157069f, -0.2932191491f, -0.2888164222f, -0.2844075263f, -0.27999264f, -0.2755718231f, -0.271145165f, -0.266712755f, -0.2622747123f, -0.2578310966f, -0.2533820271f, -0.2489276081f, -0.2444678992f, -0.2400030196f, -0.2355330586f, -0.2310581058f, -0.2265782654f, -0.2220936269f, -0.2176042795f, -0.2131103128f, -0.208611846f, -0.2041089684f, -0.1996017545f, -0.1950903237f, -0.1905747503f, -0.1860551536f, -0.1815316081f, -0.1770042181f, -0.1724730879f, -0.167938292f, -0.1633999497f, -0.1588581502f, -0.1543129683f, -0.1497645378f, -0.1452129185f, -0.1406582445f, -0.1361005753f, -0.1315400302f, -0.1269766986f, -0.1224106774f, -0.1178420633f, -0.1132709533f, -0.1086974442f, -0.1041216329f, -0.09954361618f, -0.09496349841f, -0.09038136154f, -0.08579730988f, -0.08121144772f, -0.07662386447f, -0.07203464955f, -0.06744392216f, -0.06285175681f, -0.05825826526f, -0.05366353691f, -0.04906767607f, -0.04447077215f, -0.03987292573f, -0.03527423739f, -0.030674804f, -0.02607471868f, -0.02147408016f, -0.01687298715f, -0.01227153838f, -0.007669828832f, -0.003067956772f, 0.001533980132f, 0.006135884672f, 0.01073765941f, 0.01533920597f, 0.01994042844f, 0.02454122901f, 0.02914150804f, 0.0337411724f, 0.03834012151f, 0.0429382585f, 0.04753548279f, 0.05213170499f, 0.05672682077f, 0.061320737f, 0.06591334939f, 0.07050457597f, 0.07509429753f, 0.07968243957f, 0.08426889032f, 0.08885355294f, 0.09343633801f, 0.09801714122f, 0.1025958657f, 0.1071724221f, 0.1117467135f, 0.1163186282f, 0.1208880842f, 0.1254549772f, 0.1300192177f, 0.1345807016f, 0.1391393393f, 0.1436950266f, 0.1482476741f, 0.1527971923f, 0.1573434621f, 0.161886394f, 0.1664258987f, 0.1709618866f, 0.1754942536f, 0.1800228953f, 0.1845477372f, 0.1890686601f, 0.1935855895f, 0.1980984062f, 0.2026070356f, 0.2071113735f, 0.2116113305f, 0.2161068022f, 0.2205976844f, 0.2250839174f, 0.2295653671f, 0.234041959f, 0.2385135889f, 0.2429801822f, 0.2474416196f, 0.2518978119f, 0.2563486695f, 0.2607941031f, 0.2652340233f, 0.2696683109f, 0.2740969062f, 0.27851969f, 0.282936573f, 0.2873474658f, 0.291752249f, 0.296150893f, 0.3005432487f, 0.3049292266f, 0.3093087673f, 0.3136817515f, 0.3180480897f, 0.3224076927f, 0.3267604411f, 0.3311063051f, 0.3354451358f, 0.3397768736f, 0.344101429f, 0.3484186828f, 0.3527285457f, 0.3570309579f, 0.3613258004f, 0.3656129837f, 0.3698924482f, 0.3741640747f, 0.3784277439f, 0.3826834261f, 0.3869310021f, 0.3911703825f, 0.3954014778f, 0.3996241987f, 0.4038384557f, 0.4080441594f, 0.4122412205f, 0.4164295495f, 0.4206090868f, 0.4247796834f, 0.4289412796f, 0.433093816f, 0.4372371733f, 0.4413712621f, 0.4454960227f, 0.449611336f, 0.4537171125f, 0.4578132927f, 0.4618997872f, 0.4659765065f, 0.4700433314f, 0.4741002023f, 0.4781470597f, 0.4821837842f, 0.4862102866f, 0.4902264774f, 0.4942322969f, 0.4982276559f, 0.5022124648f, 0.5061866641f, 0.510150075f, 0.514102757f, 0.5180445313f, 0.5219752789f, 0.5258949995f, 0.5298036337f, 0.5337010026f, 0.5375870466f, 0.5414617658f, 0.5453249812f, 0.5491766334f, 0.5530167222f, 0.5568450093f, 0.5606615543f, 0.564466238f, 0.5682589412f, 0.5720396042f, 0.5758081675f, 0.5795645714f, 0.5833086371f, 0.5870403647f, 0.5907596946f, 0.5944665074f, 0.5981606841f, 0.6018422246f, 0.6055110693f, 0.6091670394f, 0.6128100753f, 0.616440177f, 0.6200572252f, 0.6236611009f, 0.6272518039f, 0.630829215f, 0.6343932748f, 0.6379439235f, 0.6414810419f, 0.6450045109f, 0.64851439f, 0.65201056f, 0.6554928422f, 0.6589612961f, 0.6624158025f, 0.6658562422f, 0.6692826152f, 0.6726947427f, 0.6760926843f, 0.6794763207f, 0.6828455329f, 0.6862003207f, 0.689540565f, 0.6928661466f, 0.696177125f, 0.6994733214f, 0.7027547359f, 0.7060212493f, 0.7092728019f, 0.7125093937f, 0.7157308459f, 0.718937099f, 0.7221282125f, 0.7253039479f, 0.728464365f, 0.7316094041f, 0.7347388864f, 0.7378528118f, 0.7409511209f, 0.7440337539f, 0.7471005917f, 0.7501516342f, 0.7531868219f, 0.756205976f, 0.7592092156f, 0.7621963024f, 0.7651672363f, 0.7681220174f, 0.7710605264f, 0.7739827037f, 0.7768884897f, 0.7797777653f, 0.7826505899f, 0.7855068445f, 0.7883464098f, 0.7911693454f, 0.7939754725f, 0.796764791f, 0.7995372415f, 0.8022928238f, 0.8050313592f, 0.8077528477f, 0.81045717f, 0.8131443858f, 0.8158144355f, 0.8184671402f, 0.8211025f, 0.8237205148f, 0.8263210654f, 0.8289040923f, 0.8314695954f, 0.8340175152f, 0.8365477324f, 0.8390602469f, 0.8415549994f, 0.8440318704f, 0.8464909196f, 0.8489320278f, 0.851355195f, 0.8537603021f, 0.8561473489f, 0.8585162163f, 0.8608669639f, 0.8631994128f, 0.8655136228f, 0.8678094745f, 0.8700869679f, 0.8723460436f, 0.8745866418f, 0.8768087029f, 0.8790122271f, 0.8811970949f, 0.8833633661f, 0.8855108619f, 0.8876396418f, 0.8897495866f, 0.8918406963f, 0.893912971f, 0.8959662318f, 0.898000598f, 0.9000158906f, 0.9020121694f, 0.903989315f, 0.905947268f, 0.9078860879f, 0.9098057151f, 0.9117060304f, 0.9135870337f, 0.9154487252f, 0.9172909856f, 0.9191138744f, 0.920917213f, 0.9227011204f, 0.9244654775f, 0.9262102246f, 0.9279354215f, 0.9296408892f, 0.9313266873f, 0.932992816f, 0.9346391559f, 0.9362656474f, 0.9378723502f, 0.9394592047f, 0.9410261512f, 0.9425731897f, 0.9441002607f, 0.9456073046f, 0.9470943809f, 0.9485613704f, 0.9500082731f, 0.9514350295f, 0.9528416395f, 0.9542281032f, 0.9555943608f, 0.9569403529f, 0.9582660794f, 0.9595715404f, 0.9608566165f, 0.9621214271f, 0.9633657932f, 0.9645897746f, 0.9657933712f, 0.9669764638f, 0.968139112f, 0.9692812562f, 0.9704028368f, 0.9715039134f, 0.9725843668f, 0.9736442566f, 0.9746835232f, 0.975702107f, 0.9767000675f, 0.9776773453f, 0.9786339402f, 0.9795697927f, 0.9804848433f, 0.9813792109f, 0.982252717f, 0.9831054807f, 0.9839374423f, 0.9847484827f, 0.9855387211f, 0.9863080978f, 0.9870565534f, 0.9877841473f, 0.9884908199f, 0.9891765118f, 0.9898412824f, 0.9904850721f, 0.9911079407f, 0.9917097688f, 0.992290616f, 0.9928504229f, 0.9933891892f, 0.9939069748f, 0.9944036603f, 0.9948793054f, 0.99533391f, 0.9957674146f, 0.9961798191f, 0.9965711236f, 0.996941328f, 0.9972904325f, 0.9976184368f, 0.9979252815f, 0.9982110262f, 0.9984755516f, 0.9987190366f, 0.9989413023f, 0.9991424084f, 0.9993223548f, 0.9994812012f, 0.9996188283f, 0.9997352958f, 0.9998306036f, 0.9999046922f, 0.9999576211f, 0.9999893904f}; +constant float ts10[512] = {-0.0f, -0.01227153838f, -0.02454122901f, -0.03680722415f, -0.04906767607f, -0.061320737f, -0.07356456667f, -0.08579730988f, -0.09801714122f, -0.1102222055f, -0.1224106774f, -0.1345807016f, -0.1467304677f, -0.1588581502f, -0.1709618866f, -0.1830398887f, -0.1950903237f, -0.2071113735f, -0.2191012353f, -0.2310581058f, -0.2429801822f, -0.2548656464f, -0.266712755f, -0.27851969f, -0.2902846634f, -0.3020059466f, -0.3136817515f, -0.3253102899f, -0.336889863f, -0.3484186828f, -0.3598950505f, -0.3713172078f, -0.3826834261f, -0.3939920366f, -0.4052413106f, -0.4164295495f, -0.4275550842f, -0.438616246f, -0.449611336f, -0.4605387151f, -0.4713967443f, -0.4821837842f, -0.492898196f, -0.5035383701f, -0.514102757f, -0.5245896578f, -0.534997642f, -0.5453249812f, -0.5555702448f, -0.5657318234f, -0.5758081675f, -0.5857978463f, -0.5956993103f, -0.6055110693f, -0.6152315736f, -0.6248595119f, -0.6343932748f, -0.6438315511f, -0.6531728506f, -0.6624158025f, -0.6715589762f, -0.6806010008f, -0.689540565f, -0.6983762383f, -0.7071067691f, -0.7157308459f, -0.724247098f, -0.7326542735f, -0.7409511209f, -0.7491363883f, -0.7572088242f, -0.7651672363f, -0.7730104327f, -0.7807372212f, -0.7883464098f, -0.7958369255f, -0.8032075167f, -0.81045717f, -0.8175848126f, -0.8245893121f, -0.8314695954f, -0.838224709f, -0.84485358f, -0.851355195f, -0.8577286005f, -0.8639728427f, -0.8700869679f, -0.8760700822f, -0.8819212914f, -0.8876396418f, -0.893224299f, -0.8986744881f, -0.903989315f, -0.909168005f, -0.9142097831f, -0.9191138744f, -0.9238795042f, -0.9285060763f, -0.932992816f, -0.9373390079f, -0.9415440559f, -0.9456073046f, -0.9495281577f, -0.9533060193f, -0.9569403529f, -0.9604305029f, -0.963776052f, -0.9669764638f, -0.9700312614f, -0.9729399681f, -0.975702107f, -0.97831738f, -0.9807852507f, -0.9831054807f, -0.9852776527f, -0.9873014092f, -0.9891765118f, -0.9909026623f, -0.9924795628f, -0.9939069748f, -0.9951847196f, -0.9963126183f, -0.9972904325f, -0.9981181026f, -0.9987954497f, -0.9993223548f, -0.9996988177f, -0.9999247193f, -1.0f, -0.9999247193f, -0.9996988177f, -0.9993223548f, -0.9987954497f, -0.9981181026f, -0.9972904325f, -0.9963126183f, -0.9951847196f, -0.9939069748f, -0.9924795628f, -0.9909026623f, -0.9891765118f, -0.9873014092f, -0.9852776527f, -0.9831054807f, -0.9807852507f, -0.97831738f, -0.975702107f, -0.9729399681f, -0.9700312614f, -0.9669764638f, -0.963776052f, -0.9604305029f, -0.9569403529f, -0.9533060193f, -0.9495281577f, -0.9456073046f, -0.9415440559f, -0.9373390079f, -0.932992816f, -0.9285060763f, -0.9238795042f, -0.9191138744f, -0.9142097831f, -0.909168005f, -0.903989315f, -0.8986744881f, -0.893224299f, -0.8876396418f, -0.8819212914f, -0.8760700822f, -0.8700869679f, -0.8639728427f, -0.8577286005f, -0.851355195f, -0.84485358f, -0.838224709f, -0.8314695954f, -0.8245893121f, -0.8175848126f, -0.81045717f, -0.8032075167f, -0.7958369255f, -0.7883464098f, -0.7807372212f, -0.7730104327f, -0.7651672363f, -0.7572088242f, -0.7491363883f, -0.7409511209f, -0.7326542735f, -0.724247098f, -0.7157308459f, -0.7071067691f, -0.6983762383f, -0.689540565f, -0.6806010008f, -0.6715589762f, -0.6624158025f, -0.6531728506f, -0.6438315511f, -0.6343932748f, -0.6248595119f, -0.6152315736f, -0.6055110693f, -0.5956993103f, -0.5857978463f, -0.5758081675f, -0.5657318234f, -0.5555702448f, -0.5453249812f, -0.534997642f, -0.5245896578f, -0.514102757f, -0.5035383701f, -0.492898196f, -0.4821837842f, -0.4713967443f, -0.4605387151f, -0.449611336f, -0.438616246f, -0.4275550842f, -0.4164295495f, -0.4052413106f, -0.3939920366f, -0.3826834261f, -0.3713172078f, -0.3598950505f, -0.3484186828f, -0.336889863f, -0.3253102899f, -0.3136817515f, -0.3020059466f, -0.2902846634f, -0.27851969f, -0.266712755f, -0.2548656464f, -0.2429801822f, -0.2310581058f, -0.2191012353f, -0.2071113735f, -0.1950903237f, -0.1830398887f, -0.1709618866f, -0.1588581502f, -0.1467304677f, -0.1345807016f, -0.1224106774f, -0.1102222055f, -0.09801714122f, -0.08579730988f, -0.07356456667f, -0.061320737f, -0.04906767607f, -0.03680722415f, -0.02454122901f, -0.01227153838f, -0.0f, -0.01227153838f, -0.02454122901f, -0.03680722415f, -0.04906767607f, -0.061320737f, -0.07356456667f, -0.08579730988f, -0.09801714122f, -0.1102222055f, -0.1224106774f, -0.1345807016f, -0.1467304677f, -0.1588581502f, -0.1709618866f, -0.1830398887f, -0.1950903237f, -0.2071113735f, -0.2191012353f, -0.2310581058f, -0.2429801822f, -0.2548656464f, -0.266712755f, -0.27851969f, -0.2902846634f, -0.3020059466f, -0.3136817515f, -0.3253102899f, -0.336889863f, -0.3484186828f, -0.3598950505f, -0.3713172078f, -0.3826834261f, -0.3939920366f, -0.4052413106f, -0.4164295495f, -0.4275550842f, -0.438616246f, -0.449611336f, -0.4605387151f, -0.4713967443f, -0.4821837842f, -0.492898196f, -0.5035383701f, -0.514102757f, -0.5245896578f, -0.534997642f, -0.5453249812f, -0.5555702448f, -0.5657318234f, -0.5758081675f, -0.5857978463f, -0.5956993103f, -0.6055110693f, -0.6152315736f, -0.6248595119f, -0.6343932748f, -0.6438315511f, -0.6531728506f, -0.6624158025f, -0.6715589762f, -0.6806010008f, -0.689540565f, -0.6983762383f, -0.7071067691f, -0.7157308459f, -0.724247098f, -0.7326542735f, -0.7409511209f, -0.7491363883f, -0.7572088242f, -0.7651672363f, -0.7730104327f, -0.7807372212f, -0.7883464098f, -0.7958369255f, -0.8032075167f, -0.81045717f, -0.8175848126f, -0.8245893121f, -0.8314695954f, -0.838224709f, -0.84485358f, -0.851355195f, -0.8577286005f, -0.8639728427f, -0.8700869679f, -0.8760700822f, -0.8819212914f, -0.8876396418f, -0.893224299f, -0.8986744881f, -0.903989315f, -0.909168005f, -0.9142097831f, -0.9191138744f, -0.9238795042f, -0.9285060763f, -0.932992816f, -0.9373390079f, -0.9415440559f, -0.9456073046f, -0.9495281577f, -0.9533060193f, -0.9569403529f, -0.9604305029f, -0.963776052f, -0.9669764638f, -0.9700312614f, -0.9729399681f, -0.975702107f, -0.97831738f, -0.9807852507f, -0.9831054807f, -0.9852776527f, -0.9873014092f, -0.9891765118f, -0.9909026623f, -0.9924795628f, -0.9939069748f, -0.9951847196f, -0.9963126183f, -0.9972904325f, -0.9981181026f, -0.9987954497f, -0.9993223548f, -0.9996988177f, -0.9999247193f, -1.0f, -0.9999247193f, -0.9996988177f, -0.9993223548f, -0.9987954497f, -0.9981181026f, -0.9972904325f, -0.9963126183f, -0.9951847196f, -0.9939069748f, -0.9924795628f, -0.9909026623f, -0.9891765118f, -0.9873014092f, -0.9852776527f, -0.9831054807f, -0.9807852507f, -0.97831738f, -0.975702107f, -0.9729399681f, -0.9700312614f, -0.9669764638f, -0.963776052f, -0.9604305029f, -0.9569403529f, -0.9533060193f, -0.9495281577f, -0.9456073046f, -0.9415440559f, -0.9373390079f, -0.932992816f, -0.9285060763f, -0.9238795042f, -0.9191138744f, -0.9142097831f, -0.909168005f, -0.903989315f, -0.8986744881f, -0.893224299f, -0.8876396418f, -0.8819212914f, -0.8760700822f, -0.8700869679f, -0.8639728427f, -0.8577286005f, -0.851355195f, -0.84485358f, -0.838224709f, -0.8314695954f, -0.8245893121f, -0.8175848126f, -0.81045717f, -0.8032075167f, -0.7958369255f, -0.7883464098f, -0.7807372212f, -0.7730104327f, -0.7651672363f, -0.7572088242f, -0.7491363883f, -0.7409511209f, -0.7326542735f, -0.724247098f, -0.7157308459f, -0.7071067691f, -0.6983762383f, -0.689540565f, -0.6806010008f, -0.6715589762f, -0.6624158025f, -0.6531728506f, -0.6438315511f, -0.6343932748f, -0.6248595119f, -0.6152315736f, -0.6055110693f, -0.5956993103f, -0.5857978463f, -0.5758081675f, -0.5657318234f, -0.5555702448f, -0.5453249812f, -0.534997642f, -0.5245896578f, -0.514102757f, -0.5035383701f, -0.492898196f, -0.4821837842f, -0.4713967443f, -0.4605387151f, -0.449611336f, -0.438616246f, -0.4275550842f, -0.4164295495f, -0.4052413106f, -0.3939920366f, -0.3826834261f, -0.3713172078f, -0.3598950505f, -0.3484186828f, -0.336889863f, -0.3253102899f, -0.3136817515f, -0.3020059466f, -0.2902846634f, -0.27851969f, -0.266712755f, -0.2548656464f, -0.2429801822f, -0.2310581058f, -0.2191012353f, -0.2071113735f, -0.1950903237f, -0.1830398887f, -0.1709618866f, -0.1588581502f, -0.1467304677f, -0.1345807016f, -0.1224106774f, -0.1102222055f, -0.09801714122f, -0.08579730988f, -0.07356456667f, -0.061320737f, -0.04906767607f, -0.03680722415f, -0.02454122901f, -0.01227153838f}; +constant float ts13[512] = {-0.0f, -0.01227153838f, -0.02454122901f, -0.03680722415f, -0.04906767607f, -0.061320737f, -0.07356456667f, -0.08579730988f, -0.09801714122f, -0.1102222055f, -0.1224106774f, -0.1345807016f, -0.1467304677f, -0.1588581502f, -0.1709618866f, -0.1830398887f, -0.1950903237f, -0.2071113735f, -0.2191012353f, -0.2310581058f, -0.2429801822f, -0.2548656464f, -0.266712755f, -0.27851969f, -0.2902846634f, -0.3020059466f, -0.3136817515f, -0.3253102899f, -0.336889863f, -0.3484186828f, -0.3598950505f, -0.3713172078f, -0.3826834261f, -0.3939920366f, -0.4052413106f, -0.4164295495f, -0.4275550842f, -0.438616246f, -0.449611336f, -0.4605387151f, -0.4713967443f, -0.4821837842f, -0.492898196f, -0.5035383701f, -0.514102757f, -0.5245896578f, -0.534997642f, -0.5453249812f, -0.5555702448f, -0.5657318234f, -0.5758081675f, -0.5857978463f, -0.5956993103f, -0.6055110693f, -0.6152315736f, -0.6248595119f, -0.6343932748f, -0.6438315511f, -0.6531728506f, -0.6624158025f, -0.6715589762f, -0.6806010008f, -0.689540565f, -0.6983762383f, -0.7071067691f, -0.7157308459f, -0.724247098f, -0.7326542735f, -0.7409511209f, -0.7491363883f, -0.7572088242f, -0.7651672363f, -0.7730104327f, -0.7807372212f, -0.7883464098f, -0.7958369255f, -0.8032075167f, -0.81045717f, -0.8175848126f, -0.8245893121f, -0.8314695954f, -0.838224709f, -0.84485358f, -0.851355195f, -0.8577286005f, -0.8639728427f, -0.8700869679f, -0.8760700822f, -0.8819212914f, -0.8876396418f, -0.893224299f, -0.8986744881f, -0.903989315f, -0.909168005f, -0.9142097831f, -0.9191138744f, -0.9238795042f, -0.9285060763f, -0.932992816f, -0.9373390079f, -0.9415440559f, -0.9456073046f, -0.9495281577f, -0.9533060193f, -0.9569403529f, -0.9604305029f, -0.963776052f, -0.9669764638f, -0.9700312614f, -0.9729399681f, -0.975702107f, -0.97831738f, -0.9807852507f, -0.9831054807f, -0.9852776527f, -0.9873014092f, -0.9891765118f, -0.9909026623f, -0.9924795628f, -0.9939069748f, -0.9951847196f, -0.9963126183f, -0.9972904325f, -0.9981181026f, -0.9987954497f, -0.9993223548f, -0.9996988177f, -0.9999247193f, -1.0f, -0.9999247193f, -0.9996988177f, -0.9993223548f, -0.9987954497f, -0.9981181026f, -0.9972904325f, -0.9963126183f, -0.9951847196f, -0.9939069748f, -0.9924795628f, -0.9909026623f, -0.9891765118f, -0.9873014092f, -0.9852776527f, -0.9831054807f, -0.9807852507f, -0.97831738f, -0.975702107f, -0.9729399681f, -0.9700312614f, -0.9669764638f, -0.963776052f, -0.9604305029f, -0.9569403529f, -0.9533060193f, -0.9495281577f, -0.9456073046f, -0.9415440559f, -0.9373390079f, -0.932992816f, -0.9285060763f, -0.9238795042f, -0.9191138744f, -0.9142097831f, -0.909168005f, -0.903989315f, -0.8986744881f, -0.893224299f, -0.8876396418f, -0.8819212914f, -0.8760700822f, -0.8700869679f, -0.8639728427f, -0.8577286005f, -0.851355195f, -0.84485358f, -0.838224709f, -0.8314695954f, -0.8245893121f, -0.8175848126f, -0.81045717f, -0.8032075167f, -0.7958369255f, -0.7883464098f, -0.7807372212f, -0.7730104327f, -0.7651672363f, -0.7572088242f, -0.7491363883f, -0.7409511209f, -0.7326542735f, -0.724247098f, -0.7157308459f, -0.7071067691f, -0.6983762383f, -0.689540565f, -0.6806010008f, -0.6715589762f, -0.6624158025f, -0.6531728506f, -0.6438315511f, -0.6343932748f, -0.6248595119f, -0.6152315736f, -0.6055110693f, -0.5956993103f, -0.5857978463f, -0.5758081675f, -0.5657318234f, -0.5555702448f, -0.5453249812f, -0.534997642f, -0.5245896578f, -0.514102757f, -0.5035383701f, -0.492898196f, -0.4821837842f, -0.4713967443f, -0.4605387151f, -0.449611336f, -0.438616246f, -0.4275550842f, -0.4164295495f, -0.4052413106f, -0.3939920366f, -0.3826834261f, -0.3713172078f, -0.3598950505f, -0.3484186828f, -0.336889863f, -0.3253102899f, -0.3136817515f, -0.3020059466f, -0.2902846634f, -0.27851969f, -0.266712755f, -0.2548656464f, -0.2429801822f, -0.2310581058f, -0.2191012353f, -0.2071113735f, -0.1950903237f, -0.1830398887f, -0.1709618866f, -0.1588581502f, -0.1467304677f, -0.1345807016f, -0.1224106774f, -0.1102222055f, -0.09801714122f, -0.08579730988f, -0.07356456667f, -0.061320737f, -0.04906767607f, -0.03680722415f, -0.02454122901f, -0.01227153838f, -0.0f, -0.01227153838f, -0.02454122901f, -0.03680722415f, -0.04906767607f, -0.061320737f, -0.07356456667f, -0.08579730988f, -0.09801714122f, -0.1102222055f, -0.1224106774f, -0.1345807016f, -0.1467304677f, -0.1588581502f, -0.1709618866f, -0.1830398887f, -0.1950903237f, -0.2071113735f, -0.2191012353f, -0.2310581058f, -0.2429801822f, -0.2548656464f, -0.266712755f, -0.27851969f, -0.2902846634f, -0.3020059466f, -0.3136817515f, -0.3253102899f, -0.336889863f, -0.3484186828f, -0.3598950505f, -0.3713172078f, -0.3826834261f, -0.3939920366f, -0.4052413106f, -0.4164295495f, -0.4275550842f, -0.438616246f, -0.449611336f, -0.4605387151f, -0.4713967443f, -0.4821837842f, -0.492898196f, -0.5035383701f, -0.514102757f, -0.5245896578f, -0.534997642f, -0.5453249812f, -0.5555702448f, -0.5657318234f, -0.5758081675f, -0.5857978463f, -0.5956993103f, -0.6055110693f, -0.6152315736f, -0.6248595119f, -0.6343932748f, -0.6438315511f, -0.6531728506f, -0.6624158025f, -0.6715589762f, -0.6806010008f, -0.689540565f, -0.6983762383f, -0.7071067691f, -0.7157308459f, -0.724247098f, -0.7326542735f, -0.7409511209f, -0.7491363883f, -0.7572088242f, -0.7651672363f, -0.7730104327f, -0.7807372212f, -0.7883464098f, -0.7958369255f, -0.8032075167f, -0.81045717f, -0.8175848126f, -0.8245893121f, -0.8314695954f, -0.838224709f, -0.84485358f, -0.851355195f, -0.8577286005f, -0.8639728427f, -0.8700869679f, -0.8760700822f, -0.8819212914f, -0.8876396418f, -0.893224299f, -0.8986744881f, -0.903989315f, -0.909168005f, -0.9142097831f, -0.9191138744f, -0.9238795042f, -0.9285060763f, -0.932992816f, -0.9373390079f, -0.9415440559f, -0.9456073046f, -0.9495281577f, -0.9533060193f, -0.9569403529f, -0.9604305029f, -0.963776052f, -0.9669764638f, -0.9700312614f, -0.9729399681f, -0.975702107f, -0.97831738f, -0.9807852507f, -0.9831054807f, -0.9852776527f, -0.9873014092f, -0.9891765118f, -0.9909026623f, -0.9924795628f, -0.9939069748f, -0.9951847196f, -0.9963126183f, -0.9972904325f, -0.9981181026f, -0.9987954497f, -0.9993223548f, -0.9996988177f, -0.9999247193f, -1.0f, -0.9999247193f, -0.9996988177f, -0.9993223548f, -0.9987954497f, -0.9981181026f, -0.9972904325f, -0.9963126183f, -0.9951847196f, -0.9939069748f, -0.9924795628f, -0.9909026623f, -0.9891765118f, -0.9873014092f, -0.9852776527f, -0.9831054807f, -0.9807852507f, -0.97831738f, -0.975702107f, -0.9729399681f, -0.9700312614f, -0.9669764638f, -0.963776052f, -0.9604305029f, -0.9569403529f, -0.9533060193f, -0.9495281577f, -0.9456073046f, -0.9415440559f, -0.9373390079f, -0.932992816f, -0.9285060763f, -0.9238795042f, -0.9191138744f, -0.9142097831f, -0.909168005f, -0.903989315f, -0.8986744881f, -0.893224299f, -0.8876396418f, -0.8819212914f, -0.8760700822f, -0.8700869679f, -0.8639728427f, -0.8577286005f, -0.851355195f, -0.84485358f, -0.838224709f, -0.8314695954f, -0.8245893121f, -0.8175848126f, -0.81045717f, -0.8032075167f, -0.7958369255f, -0.7883464098f, -0.7807372212f, -0.7730104327f, -0.7651672363f, -0.7572088242f, -0.7491363883f, -0.7409511209f, -0.7326542735f, -0.724247098f, -0.7157308459f, -0.7071067691f, -0.6983762383f, -0.689540565f, -0.6806010008f, -0.6715589762f, -0.6624158025f, -0.6531728506f, -0.6438315511f, -0.6343932748f, -0.6248595119f, -0.6152315736f, -0.6055110693f, -0.5956993103f, -0.5857978463f, -0.5758081675f, -0.5657318234f, -0.5555702448f, -0.5453249812f, -0.534997642f, -0.5245896578f, -0.514102757f, -0.5035383701f, -0.492898196f, -0.4821837842f, -0.4713967443f, -0.4605387151f, -0.449611336f, -0.438616246f, -0.4275550842f, -0.4164295495f, -0.4052413106f, -0.3939920366f, -0.3826834261f, -0.3713172078f, -0.3598950505f, -0.3484186828f, -0.336889863f, -0.3253102899f, -0.3136817515f, -0.3020059466f, -0.2902846634f, -0.27851969f, -0.266712755f, -0.2548656464f, -0.2429801822f, -0.2310581058f, -0.2191012353f, -0.2071113735f, -0.1950903237f, -0.1830398887f, -0.1709618866f, -0.1588581502f, -0.1467304677f, -0.1345807016f, -0.1224106774f, -0.1102222055f, -0.09801714122f, -0.08579730988f, -0.07356456667f, -0.061320737f, -0.04906767607f, -0.03680722415f, -0.02454122901f, -0.01227153838f}; +constant float ts11[512] = {-0.0f, -0.006135884672f, -0.01227153838f, -0.01840673015f, -0.02454122901f, -0.030674804f, -0.03680722415f, -0.0429382585f, -0.04906767607f, -0.05519524589f, -0.061320737f, -0.06744392216f, -0.07356456667f, -0.07968243957f, -0.08579730988f, -0.09190895408f, -0.09801714122f, -0.1041216329f, -0.1102222055f, -0.1163186282f, -0.1224106774f, -0.1284981072f, -0.1345807016f, -0.1406582445f, -0.1467304677f, -0.1527971923f, -0.1588581502f, -0.1649131179f, -0.1709618866f, -0.1770042181f, -0.1830398887f, -0.1890686601f, -0.1950903237f, -0.201104641f, -0.2071113735f, -0.2131103128f, -0.2191012353f, -0.2250839174f, -0.2310581058f, -0.2370236069f, -0.2429801822f, -0.2489276081f, -0.2548656464f, -0.2607941031f, -0.266712755f, -0.2726213634f, -0.27851969f, -0.2844075263f, -0.2902846634f, -0.296150893f, -0.3020059466f, -0.3078496456f, -0.3136817515f, -0.3195020258f, -0.3253102899f, -0.3311063051f, -0.336889863f, -0.3426607251f, -0.3484186828f, -0.3541635275f, -0.3598950505f, -0.3656129837f, -0.3713172078f, -0.3770074248f, -0.3826834261f, -0.3883450329f, -0.3939920366f, -0.3996241987f, -0.4052413106f, -0.4108431637f, -0.4164295495f, -0.4220002592f, -0.4275550842f, -0.433093816f, -0.438616246f, -0.4441221356f, -0.449611336f, -0.4550835788f, -0.4605387151f, -0.4659765065f, -0.4713967443f, -0.4767992198f, -0.4821837842f, -0.4875501692f, -0.492898196f, -0.4982276559f, -0.5035383701f, -0.5088301301f, -0.514102757f, -0.5193560123f, -0.5245896578f, -0.5298036337f, -0.534997642f, -0.5401714444f, -0.5453249812f, -0.5504579544f, -0.5555702448f, -0.5606615543f, -0.5657318234f, -0.5707807541f, -0.5758081675f, -0.5808139443f, -0.5857978463f, -0.5907596946f, -0.5956993103f, -0.6006164551f, -0.6055110693f, -0.6103827953f, -0.6152315736f, -0.6200572252f, -0.6248595119f, -0.6296382546f, -0.6343932748f, -0.6391244531f, -0.6438315511f, -0.64851439f, -0.6531728506f, -0.6578066945f, -0.6624158025f, -0.6669999361f, -0.6715589762f, -0.6760926843f, -0.6806010008f, -0.6850836873f, -0.689540565f, -0.6939714551f, -0.6983762383f, -0.7027547359f, -0.7071067691f, -0.7114322186f, -0.7157308459f, -0.720002532f, -0.724247098f, -0.728464365f, -0.7326542735f, -0.7368165851f, -0.7409511209f, -0.7450577617f, -0.7491363883f, -0.7531868219f, -0.7572088242f, -0.761202395f, -0.7651672363f, -0.7691033483f, -0.7730104327f, -0.7768884897f, -0.7807372212f, -0.7845565677f, -0.7883464098f, -0.7921065688f, -0.7958369255f, -0.7995372415f, -0.8032075167f, -0.8068475723f, -0.81045717f, -0.8140363097f, -0.8175848126f, -0.8211025f, -0.8245893121f, -0.8280450702f, -0.8314695954f, -0.8348628879f, -0.838224709f, -0.8415549994f, -0.84485358f, -0.8481203318f, -0.851355195f, -0.854557991f, -0.8577286005f, -0.8608669639f, -0.8639728427f, -0.867046237f, -0.8700869679f, -0.8730949759f, -0.8760700822f, -0.8790122271f, -0.8819212914f, -0.8847970963f, -0.8876396418f, -0.8904487491f, -0.893224299f, -0.8959662318f, -0.8986744881f, -0.9013488293f, -0.903989315f, -0.9065957069f, -0.909168005f, -0.9117060304f, -0.9142097831f, -0.9166790843f, -0.9191138744f, -0.9215140343f, -0.9238795042f, -0.9262102246f, -0.9285060763f, -0.9307669401f, -0.932992816f, -0.9351835251f, -0.9373390079f, -0.9394592047f, -0.9415440559f, -0.9435934424f, -0.9456073046f, -0.9475855827f, -0.9495281577f, -0.9514350295f, -0.9533060193f, -0.9551411867f, -0.9569403529f, -0.9587034583f, -0.9604305029f, -0.9621214271f, -0.963776052f, -0.9653944373f, -0.9669764638f, -0.9685220718f, -0.9700312614f, -0.9715039134f, -0.9729399681f, -0.974339366f, -0.975702107f, -0.9770281315f, -0.97831738f, -0.9795697927f, -0.9807852507f, -0.9819638729f, -0.9831054807f, -0.9842100739f, -0.9852776527f, -0.9863080978f, -0.9873014092f, -0.988257587f, -0.9891765118f, -0.9900581837f, -0.9909026623f, -0.9917097688f, -0.9924795628f, -0.993211925f, -0.9939069748f, -0.9945645928f, -0.9951847196f, -0.9957674146f, -0.9963126183f, -0.996820271f, -0.9972904325f, -0.997723043f, -0.9981181026f, -0.9984755516f, -0.9987954497f, -0.9990777373f, -0.9993223548f, -0.9995294213f, -0.9996988177f, -0.9998306036f, -0.9999247193f, -0.9999811649f, -0.0f, -0.006135884672f, -0.01227153838f, -0.01840673015f, -0.02454122901f, -0.030674804f, -0.03680722415f, -0.0429382585f, -0.04906767607f, -0.05519524589f, -0.061320737f, -0.06744392216f, -0.07356456667f, -0.07968243957f, -0.08579730988f, -0.09190895408f, -0.09801714122f, -0.1041216329f, -0.1102222055f, -0.1163186282f, -0.1224106774f, -0.1284981072f, -0.1345807016f, -0.1406582445f, -0.1467304677f, -0.1527971923f, -0.1588581502f, -0.1649131179f, -0.1709618866f, -0.1770042181f, -0.1830398887f, -0.1890686601f, -0.1950903237f, -0.201104641f, -0.2071113735f, -0.2131103128f, -0.2191012353f, -0.2250839174f, -0.2310581058f, -0.2370236069f, -0.2429801822f, -0.2489276081f, -0.2548656464f, -0.2607941031f, -0.266712755f, -0.2726213634f, -0.27851969f, -0.2844075263f, -0.2902846634f, -0.296150893f, -0.3020059466f, -0.3078496456f, -0.3136817515f, -0.3195020258f, -0.3253102899f, -0.3311063051f, -0.336889863f, -0.3426607251f, -0.3484186828f, -0.3541635275f, -0.3598950505f, -0.3656129837f, -0.3713172078f, -0.3770074248f, -0.3826834261f, -0.3883450329f, -0.3939920366f, -0.3996241987f, -0.4052413106f, -0.4108431637f, -0.4164295495f, -0.4220002592f, -0.4275550842f, -0.433093816f, -0.438616246f, -0.4441221356f, -0.449611336f, -0.4550835788f, -0.4605387151f, -0.4659765065f, -0.4713967443f, -0.4767992198f, -0.4821837842f, -0.4875501692f, -0.492898196f, -0.4982276559f, -0.5035383701f, -0.5088301301f, -0.514102757f, -0.5193560123f, -0.5245896578f, -0.5298036337f, -0.534997642f, -0.5401714444f, -0.5453249812f, -0.5504579544f, -0.5555702448f, -0.5606615543f, -0.5657318234f, -0.5707807541f, -0.5758081675f, -0.5808139443f, -0.5857978463f, -0.5907596946f, -0.5956993103f, -0.6006164551f, -0.6055110693f, -0.6103827953f, -0.6152315736f, -0.6200572252f, -0.6248595119f, -0.6296382546f, -0.6343932748f, -0.6391244531f, -0.6438315511f, -0.64851439f, -0.6531728506f, -0.6578066945f, -0.6624158025f, -0.6669999361f, -0.6715589762f, -0.6760926843f, -0.6806010008f, -0.6850836873f, -0.689540565f, -0.6939714551f, -0.6983762383f, -0.7027547359f, -0.7071067691f, -0.7114322186f, -0.7157308459f, -0.720002532f, -0.724247098f, -0.728464365f, -0.7326542735f, -0.7368165851f, -0.7409511209f, -0.7450577617f, -0.7491363883f, -0.7531868219f, -0.7572088242f, -0.761202395f, -0.7651672363f, -0.7691033483f, -0.7730104327f, -0.7768884897f, -0.7807372212f, -0.7845565677f, -0.7883464098f, -0.7921065688f, -0.7958369255f, -0.7995372415f, -0.8032075167f, -0.8068475723f, -0.81045717f, -0.8140363097f, -0.8175848126f, -0.8211025f, -0.8245893121f, -0.8280450702f, -0.8314695954f, -0.8348628879f, -0.838224709f, -0.8415549994f, -0.84485358f, -0.8481203318f, -0.851355195f, -0.854557991f, -0.8577286005f, -0.8608669639f, -0.8639728427f, -0.867046237f, -0.8700869679f, -0.8730949759f, -0.8760700822f, -0.8790122271f, -0.8819212914f, -0.8847970963f, -0.8876396418f, -0.8904487491f, -0.893224299f, -0.8959662318f, -0.8986744881f, -0.9013488293f, -0.903989315f, -0.9065957069f, -0.909168005f, -0.9117060304f, -0.9142097831f, -0.9166790843f, -0.9191138744f, -0.9215140343f, -0.9238795042f, -0.9262102246f, -0.9285060763f, -0.9307669401f, -0.932992816f, -0.9351835251f, -0.9373390079f, -0.9394592047f, -0.9415440559f, -0.9435934424f, -0.9456073046f, -0.9475855827f, -0.9495281577f, -0.9514350295f, -0.9533060193f, -0.9551411867f, -0.9569403529f, -0.9587034583f, -0.9604305029f, -0.9621214271f, -0.963776052f, -0.9653944373f, -0.9669764638f, -0.9685220718f, -0.9700312614f, -0.9715039134f, -0.9729399681f, -0.974339366f, -0.975702107f, -0.9770281315f, -0.97831738f, -0.9795697927f, -0.9807852507f, -0.9819638729f, -0.9831054807f, -0.9842100739f, -0.9852776527f, -0.9863080978f, -0.9873014092f, -0.988257587f, -0.9891765118f, -0.9900581837f, -0.9909026623f, -0.9917097688f, -0.9924795628f, -0.993211925f, -0.9939069748f, -0.9945645928f, -0.9951847196f, -0.9957674146f, -0.9963126183f, -0.996820271f, -0.9972904325f, -0.997723043f, -0.9981181026f, -0.9984755516f, -0.9987954497f, -0.9990777373f, -0.9993223548f, -0.9995294213f, -0.9996988177f, -0.9998306036f, -0.9999247193f, -0.9999811649f}; +constant float ts14[512] = {-0.0f, -0.006135884672f, -0.01227153838f, -0.01840673015f, -0.02454122901f, -0.030674804f, -0.03680722415f, -0.0429382585f, -0.04906767607f, -0.05519524589f, -0.061320737f, -0.06744392216f, -0.07356456667f, -0.07968243957f, -0.08579730988f, -0.09190895408f, -0.09801714122f, -0.1041216329f, -0.1102222055f, -0.1163186282f, -0.1224106774f, -0.1284981072f, -0.1345807016f, -0.1406582445f, -0.1467304677f, -0.1527971923f, -0.1588581502f, -0.1649131179f, -0.1709618866f, -0.1770042181f, -0.1830398887f, -0.1890686601f, -0.1950903237f, -0.201104641f, -0.2071113735f, -0.2131103128f, -0.2191012353f, -0.2250839174f, -0.2310581058f, -0.2370236069f, -0.2429801822f, -0.2489276081f, -0.2548656464f, -0.2607941031f, -0.266712755f, -0.2726213634f, -0.27851969f, -0.2844075263f, -0.2902846634f, -0.296150893f, -0.3020059466f, -0.3078496456f, -0.3136817515f, -0.3195020258f, -0.3253102899f, -0.3311063051f, -0.336889863f, -0.3426607251f, -0.3484186828f, -0.3541635275f, -0.3598950505f, -0.3656129837f, -0.3713172078f, -0.3770074248f, -0.3826834261f, -0.3883450329f, -0.3939920366f, -0.3996241987f, -0.4052413106f, -0.4108431637f, -0.4164295495f, -0.4220002592f, -0.4275550842f, -0.433093816f, -0.438616246f, -0.4441221356f, -0.449611336f, -0.4550835788f, -0.4605387151f, -0.4659765065f, -0.4713967443f, -0.4767992198f, -0.4821837842f, -0.4875501692f, -0.492898196f, -0.4982276559f, -0.5035383701f, -0.5088301301f, -0.514102757f, -0.5193560123f, -0.5245896578f, -0.5298036337f, -0.534997642f, -0.5401714444f, -0.5453249812f, -0.5504579544f, -0.5555702448f, -0.5606615543f, -0.5657318234f, -0.5707807541f, -0.5758081675f, -0.5808139443f, -0.5857978463f, -0.5907596946f, -0.5956993103f, -0.6006164551f, -0.6055110693f, -0.6103827953f, -0.6152315736f, -0.6200572252f, -0.6248595119f, -0.6296382546f, -0.6343932748f, -0.6391244531f, -0.6438315511f, -0.64851439f, -0.6531728506f, -0.6578066945f, -0.6624158025f, -0.6669999361f, -0.6715589762f, -0.6760926843f, -0.6806010008f, -0.6850836873f, -0.689540565f, -0.6939714551f, -0.6983762383f, -0.7027547359f, -0.7071067691f, -0.7114322186f, -0.7157308459f, -0.720002532f, -0.724247098f, -0.728464365f, -0.7326542735f, -0.7368165851f, -0.7409511209f, -0.7450577617f, -0.7491363883f, -0.7531868219f, -0.7572088242f, -0.761202395f, -0.7651672363f, -0.7691033483f, -0.7730104327f, -0.7768884897f, -0.7807372212f, -0.7845565677f, -0.7883464098f, -0.7921065688f, -0.7958369255f, -0.7995372415f, -0.8032075167f, -0.8068475723f, -0.81045717f, -0.8140363097f, -0.8175848126f, -0.8211025f, -0.8245893121f, -0.8280450702f, -0.8314695954f, -0.8348628879f, -0.838224709f, -0.8415549994f, -0.84485358f, -0.8481203318f, -0.851355195f, -0.854557991f, -0.8577286005f, -0.8608669639f, -0.8639728427f, -0.867046237f, -0.8700869679f, -0.8730949759f, -0.8760700822f, -0.8790122271f, -0.8819212914f, -0.8847970963f, -0.8876396418f, -0.8904487491f, -0.893224299f, -0.8959662318f, -0.8986744881f, -0.9013488293f, -0.903989315f, -0.9065957069f, -0.909168005f, -0.9117060304f, -0.9142097831f, -0.9166790843f, -0.9191138744f, -0.9215140343f, -0.9238795042f, -0.9262102246f, -0.9285060763f, -0.9307669401f, -0.932992816f, -0.9351835251f, -0.9373390079f, -0.9394592047f, -0.9415440559f, -0.9435934424f, -0.9456073046f, -0.9475855827f, -0.9495281577f, -0.9514350295f, -0.9533060193f, -0.9551411867f, -0.9569403529f, -0.9587034583f, -0.9604305029f, -0.9621214271f, -0.963776052f, -0.9653944373f, -0.9669764638f, -0.9685220718f, -0.9700312614f, -0.9715039134f, -0.9729399681f, -0.974339366f, -0.975702107f, -0.9770281315f, -0.97831738f, -0.9795697927f, -0.9807852507f, -0.9819638729f, -0.9831054807f, -0.9842100739f, -0.9852776527f, -0.9863080978f, -0.9873014092f, -0.988257587f, -0.9891765118f, -0.9900581837f, -0.9909026623f, -0.9917097688f, -0.9924795628f, -0.993211925f, -0.9939069748f, -0.9945645928f, -0.9951847196f, -0.9957674146f, -0.9963126183f, -0.996820271f, -0.9972904325f, -0.997723043f, -0.9981181026f, -0.9984755516f, -0.9987954497f, -0.9990777373f, -0.9993223548f, -0.9995294213f, -0.9996988177f, -0.9998306036f, -0.9999247193f, -0.9999811649f, -0.0f, -0.006135884672f, -0.01227153838f, -0.01840673015f, -0.02454122901f, -0.030674804f, -0.03680722415f, -0.0429382585f, -0.04906767607f, -0.05519524589f, -0.061320737f, -0.06744392216f, -0.07356456667f, -0.07968243957f, -0.08579730988f, -0.09190895408f, -0.09801714122f, -0.1041216329f, -0.1102222055f, -0.1163186282f, -0.1224106774f, -0.1284981072f, -0.1345807016f, -0.1406582445f, -0.1467304677f, -0.1527971923f, -0.1588581502f, -0.1649131179f, -0.1709618866f, -0.1770042181f, -0.1830398887f, -0.1890686601f, -0.1950903237f, -0.201104641f, -0.2071113735f, -0.2131103128f, -0.2191012353f, -0.2250839174f, -0.2310581058f, -0.2370236069f, -0.2429801822f, -0.2489276081f, -0.2548656464f, -0.2607941031f, -0.266712755f, -0.2726213634f, -0.27851969f, -0.2844075263f, -0.2902846634f, -0.296150893f, -0.3020059466f, -0.3078496456f, -0.3136817515f, -0.3195020258f, -0.3253102899f, -0.3311063051f, -0.336889863f, -0.3426607251f, -0.3484186828f, -0.3541635275f, -0.3598950505f, -0.3656129837f, -0.3713172078f, -0.3770074248f, -0.3826834261f, -0.3883450329f, -0.3939920366f, -0.3996241987f, -0.4052413106f, -0.4108431637f, -0.4164295495f, -0.4220002592f, -0.4275550842f, -0.433093816f, -0.438616246f, -0.4441221356f, -0.449611336f, -0.4550835788f, -0.4605387151f, -0.4659765065f, -0.4713967443f, -0.4767992198f, -0.4821837842f, -0.4875501692f, -0.492898196f, -0.4982276559f, -0.5035383701f, -0.5088301301f, -0.514102757f, -0.5193560123f, -0.5245896578f, -0.5298036337f, -0.534997642f, -0.5401714444f, -0.5453249812f, -0.5504579544f, -0.5555702448f, -0.5606615543f, -0.5657318234f, -0.5707807541f, -0.5758081675f, -0.5808139443f, -0.5857978463f, -0.5907596946f, -0.5956993103f, -0.6006164551f, -0.6055110693f, -0.6103827953f, -0.6152315736f, -0.6200572252f, -0.6248595119f, -0.6296382546f, -0.6343932748f, -0.6391244531f, -0.6438315511f, -0.64851439f, -0.6531728506f, -0.6578066945f, -0.6624158025f, -0.6669999361f, -0.6715589762f, -0.6760926843f, -0.6806010008f, -0.6850836873f, -0.689540565f, -0.6939714551f, -0.6983762383f, -0.7027547359f, -0.7071067691f, -0.7114322186f, -0.7157308459f, -0.720002532f, -0.724247098f, -0.728464365f, -0.7326542735f, -0.7368165851f, -0.7409511209f, -0.7450577617f, -0.7491363883f, -0.7531868219f, -0.7572088242f, -0.761202395f, -0.7651672363f, -0.7691033483f, -0.7730104327f, -0.7768884897f, -0.7807372212f, -0.7845565677f, -0.7883464098f, -0.7921065688f, -0.7958369255f, -0.7995372415f, -0.8032075167f, -0.8068475723f, -0.81045717f, -0.8140363097f, -0.8175848126f, -0.8211025f, -0.8245893121f, -0.8280450702f, -0.8314695954f, -0.8348628879f, -0.838224709f, -0.8415549994f, -0.84485358f, -0.8481203318f, -0.851355195f, -0.854557991f, -0.8577286005f, -0.8608669639f, -0.8639728427f, -0.867046237f, -0.8700869679f, -0.8730949759f, -0.8760700822f, -0.8790122271f, -0.8819212914f, -0.8847970963f, -0.8876396418f, -0.8904487491f, -0.893224299f, -0.8959662318f, -0.8986744881f, -0.9013488293f, -0.903989315f, -0.9065957069f, -0.909168005f, -0.9117060304f, -0.9142097831f, -0.9166790843f, -0.9191138744f, -0.9215140343f, -0.9238795042f, -0.9262102246f, -0.9285060763f, -0.9307669401f, -0.932992816f, -0.9351835251f, -0.9373390079f, -0.9394592047f, -0.9415440559f, -0.9435934424f, -0.9456073046f, -0.9475855827f, -0.9495281577f, -0.9514350295f, -0.9533060193f, -0.9551411867f, -0.9569403529f, -0.9587034583f, -0.9604305029f, -0.9621214271f, -0.963776052f, -0.9653944373f, -0.9669764638f, -0.9685220718f, -0.9700312614f, -0.9715039134f, -0.9729399681f, -0.974339366f, -0.975702107f, -0.9770281315f, -0.97831738f, -0.9795697927f, -0.9807852507f, -0.9819638729f, -0.9831054807f, -0.9842100739f, -0.9852776527f, -0.9863080978f, -0.9873014092f, -0.988257587f, -0.9891765118f, -0.9900581837f, -0.9909026623f, -0.9917097688f, -0.9924795628f, -0.993211925f, -0.9939069748f, -0.9945645928f, -0.9951847196f, -0.9957674146f, -0.9963126183f, -0.996820271f, -0.9972904325f, -0.997723043f, -0.9981181026f, -0.9984755516f, -0.9987954497f, -0.9990777373f, -0.9993223548f, -0.9995294213f, -0.9996988177f, -0.9998306036f, -0.9999247193f, -0.9999811649f}; +constant float ts12[512] = {-0.0f, -0.01840673015f, -0.03680722415f, -0.05519524589f, -0.07356456667f, -0.09190895408f, -0.1102222055f, -0.1284981072f, -0.1467304677f, -0.1649131179f, -0.1830398887f, -0.201104641f, -0.2191012353f, -0.2370236069f, -0.2548656464f, -0.2726213634f, -0.2902846634f, -0.3078496456f, -0.3253102899f, -0.3426607251f, -0.3598950505f, -0.3770074248f, -0.3939920366f, -0.4108431637f, -0.4275550842f, -0.4441221356f, -0.4605387151f, -0.4767992198f, -0.492898196f, -0.5088301301f, -0.5245896578f, -0.5401714444f, -0.5555702448f, -0.5707807541f, -0.5857978463f, -0.6006164551f, -0.6152315736f, -0.6296382546f, -0.6438315511f, -0.6578066945f, -0.6715589762f, -0.6850836873f, -0.6983762383f, -0.7114322186f, -0.724247098f, -0.7368165851f, -0.7491363883f, -0.761202395f, -0.7730104327f, -0.7845565677f, -0.7958369255f, -0.8068475723f, -0.8175848126f, -0.8280450702f, -0.838224709f, -0.8481203318f, -0.8577286005f, -0.867046237f, -0.8760700822f, -0.8847970963f, -0.893224299f, -0.9013488293f, -0.909168005f, -0.9166790843f, -0.9238795042f, -0.9307669401f, -0.9373390079f, -0.9435934424f, -0.9495281577f, -0.9551411867f, -0.9604305029f, -0.9653944373f, -0.9700312614f, -0.974339366f, -0.97831738f, -0.9819638729f, -0.9852776527f, -0.988257587f, -0.9909026623f, -0.993211925f, -0.9951847196f, -0.996820271f, -0.9981181026f, -0.9990777373f, -0.9996988177f, -0.9999811649f, -0.9999247193f, -0.9995294213f, -0.9987954497f, -0.997723043f, -0.9963126183f, -0.9945645928f, -0.9924795628f, -0.9900581837f, -0.9873014092f, -0.9842100739f, -0.9807852507f, -0.9770281315f, -0.9729399681f, -0.9685220718f, -0.963776052f, -0.9587034583f, -0.9533060193f, -0.9475855827f, -0.9415440559f, -0.9351835251f, -0.9285060763f, -0.9215140343f, -0.9142097831f, -0.9065957069f, -0.8986744881f, -0.8904487491f, -0.8819212914f, -0.8730949759f, -0.8639728427f, -0.854557991f, -0.84485358f, -0.8348628879f, -0.8245893121f, -0.8140363097f, -0.8032075167f, -0.7921065688f, -0.7807372212f, -0.7691033483f, -0.7572088242f, -0.7450577617f, -0.7326542735f, -0.720002532f, -0.7071067691f, -0.6939714551f, -0.6806010008f, -0.6669999361f, -0.6531728506f, -0.6391244531f, -0.6248595119f, -0.6103827953f, -0.5956993103f, -0.5808139443f, -0.5657318234f, -0.5504579544f, -0.534997642f, -0.5193560123f, -0.5035383701f, -0.4875501692f, -0.4713967443f, -0.4550835788f, -0.438616246f, -0.4220002592f, -0.4052413106f, -0.3883450329f, -0.3713172078f, -0.3541635275f, -0.336889863f, -0.3195020258f, -0.3020059466f, -0.2844075263f, -0.266712755f, -0.2489276081f, -0.2310581058f, -0.2131103128f, -0.1950903237f, -0.1770042181f, -0.1588581502f, -0.1406582445f, -0.1224106774f, -0.1041216329f, -0.08579730988f, -0.06744392216f, -0.04906767607f, -0.030674804f, -0.01227153838f, 0.006135884672f, 0.02454122901f, 0.0429382585f, 0.061320737f, 0.07968243957f, 0.09801714122f, 0.1163186282f, 0.1345807016f, 0.1527971923f, 0.1709618866f, 0.1890686601f, 0.2071113735f, 0.2250839174f, 0.2429801822f, 0.2607941031f, 0.27851969f, 0.296150893f, 0.3136817515f, 0.3311063051f, 0.3484186828f, 0.3656129837f, 0.3826834261f, 0.3996241987f, 0.4164295495f, 0.433093816f, 0.449611336f, 0.4659765065f, 0.4821837842f, 0.4982276559f, 0.514102757f, 0.5298036337f, 0.5453249812f, 0.5606615543f, 0.5758081675f, 0.5907596946f, 0.6055110693f, 0.6200572252f, 0.6343932748f, 0.64851439f, 0.6624158025f, 0.6760926843f, 0.689540565f, 0.7027547359f, 0.7157308459f, 0.728464365f, 0.7409511209f, 0.7531868219f, 0.7651672363f, 0.7768884897f, 0.7883464098f, 0.7995372415f, 0.81045717f, 0.8211025f, 0.8314695954f, 0.8415549994f, 0.851355195f, 0.8608669639f, 0.8700869679f, 0.8790122271f, 0.8876396418f, 0.8959662318f, 0.903989315f, 0.9117060304f, 0.9191138744f, 0.9262102246f, 0.932992816f, 0.9394592047f, 0.9456073046f, 0.9514350295f, 0.9569403529f, 0.9621214271f, 0.9669764638f, 0.9715039134f, 0.975702107f, 0.9795697927f, 0.9831054807f, 0.9863080978f, 0.9891765118f, 0.9917097688f, 0.9939069748f, 0.9957674146f, 0.9972904325f, 0.9984755516f, 0.9993223548f, 0.9998306036f, -0.0f, -0.01840673015f, -0.03680722415f, -0.05519524589f, -0.07356456667f, -0.09190895408f, -0.1102222055f, -0.1284981072f, -0.1467304677f, -0.1649131179f, -0.1830398887f, -0.201104641f, -0.2191012353f, -0.2370236069f, -0.2548656464f, -0.2726213634f, -0.2902846634f, -0.3078496456f, -0.3253102899f, -0.3426607251f, -0.3598950505f, -0.3770074248f, -0.3939920366f, -0.4108431637f, -0.4275550842f, -0.4441221356f, -0.4605387151f, -0.4767992198f, -0.492898196f, -0.5088301301f, -0.5245896578f, -0.5401714444f, -0.5555702448f, -0.5707807541f, -0.5857978463f, -0.6006164551f, -0.6152315736f, -0.6296382546f, -0.6438315511f, -0.6578066945f, -0.6715589762f, -0.6850836873f, -0.6983762383f, -0.7114322186f, -0.724247098f, -0.7368165851f, -0.7491363883f, -0.761202395f, -0.7730104327f, -0.7845565677f, -0.7958369255f, -0.8068475723f, -0.8175848126f, -0.8280450702f, -0.838224709f, -0.8481203318f, -0.8577286005f, -0.867046237f, -0.8760700822f, -0.8847970963f, -0.893224299f, -0.9013488293f, -0.909168005f, -0.9166790843f, -0.9238795042f, -0.9307669401f, -0.9373390079f, -0.9435934424f, -0.9495281577f, -0.9551411867f, -0.9604305029f, -0.9653944373f, -0.9700312614f, -0.974339366f, -0.97831738f, -0.9819638729f, -0.9852776527f, -0.988257587f, -0.9909026623f, -0.993211925f, -0.9951847196f, -0.996820271f, -0.9981181026f, -0.9990777373f, -0.9996988177f, -0.9999811649f, -0.9999247193f, -0.9995294213f, -0.9987954497f, -0.997723043f, -0.9963126183f, -0.9945645928f, -0.9924795628f, -0.9900581837f, -0.9873014092f, -0.9842100739f, -0.9807852507f, -0.9770281315f, -0.9729399681f, -0.9685220718f, -0.963776052f, -0.9587034583f, -0.9533060193f, -0.9475855827f, -0.9415440559f, -0.9351835251f, -0.9285060763f, -0.9215140343f, -0.9142097831f, -0.9065957069f, -0.8986744881f, -0.8904487491f, -0.8819212914f, -0.8730949759f, -0.8639728427f, -0.854557991f, -0.84485358f, -0.8348628879f, -0.8245893121f, -0.8140363097f, -0.8032075167f, -0.7921065688f, -0.7807372212f, -0.7691033483f, -0.7572088242f, -0.7450577617f, -0.7326542735f, -0.720002532f, -0.7071067691f, -0.6939714551f, -0.6806010008f, -0.6669999361f, -0.6531728506f, -0.6391244531f, -0.6248595119f, -0.6103827953f, -0.5956993103f, -0.5808139443f, -0.5657318234f, -0.5504579544f, -0.534997642f, -0.5193560123f, -0.5035383701f, -0.4875501692f, -0.4713967443f, -0.4550835788f, -0.438616246f, -0.4220002592f, -0.4052413106f, -0.3883450329f, -0.3713172078f, -0.3541635275f, -0.336889863f, -0.3195020258f, -0.3020059466f, -0.2844075263f, -0.266712755f, -0.2489276081f, -0.2310581058f, -0.2131103128f, -0.1950903237f, -0.1770042181f, -0.1588581502f, -0.1406582445f, -0.1224106774f, -0.1041216329f, -0.08579730988f, -0.06744392216f, -0.04906767607f, -0.030674804f, -0.01227153838f, 0.006135884672f, 0.02454122901f, 0.0429382585f, 0.061320737f, 0.07968243957f, 0.09801714122f, 0.1163186282f, 0.1345807016f, 0.1527971923f, 0.1709618866f, 0.1890686601f, 0.2071113735f, 0.2250839174f, 0.2429801822f, 0.2607941031f, 0.27851969f, 0.296150893f, 0.3136817515f, 0.3311063051f, 0.3484186828f, 0.3656129837f, 0.3826834261f, 0.3996241987f, 0.4164295495f, 0.433093816f, 0.449611336f, 0.4659765065f, 0.4821837842f, 0.4982276559f, 0.514102757f, 0.5298036337f, 0.5453249812f, 0.5606615543f, 0.5758081675f, 0.5907596946f, 0.6055110693f, 0.6200572252f, 0.6343932748f, 0.64851439f, 0.6624158025f, 0.6760926843f, 0.689540565f, 0.7027547359f, 0.7157308459f, 0.728464365f, 0.7409511209f, 0.7531868219f, 0.7651672363f, 0.7768884897f, 0.7883464098f, 0.7995372415f, 0.81045717f, 0.8211025f, 0.8314695954f, 0.8415549994f, 0.851355195f, 0.8608669639f, 0.8700869679f, 0.8790122271f, 0.8876396418f, 0.8959662318f, 0.903989315f, 0.9117060304f, 0.9191138744f, 0.9262102246f, 0.932992816f, 0.9394592047f, 0.9456073046f, 0.9514350295f, 0.9569403529f, 0.9621214271f, 0.9669764638f, 0.9715039134f, 0.975702107f, 0.9795697927f, 0.9831054807f, 0.9863080978f, 0.9891765118f, 0.9917097688f, 0.9939069748f, 0.9957674146f, 0.9972904325f, 0.9984755516f, 0.9993223548f, 0.9998306036f}; +constant float ts15[512] = {-0.0f, -0.01840673015f, -0.03680722415f, -0.05519524589f, -0.07356456667f, -0.09190895408f, -0.1102222055f, -0.1284981072f, -0.1467304677f, -0.1649131179f, -0.1830398887f, -0.201104641f, -0.2191012353f, -0.2370236069f, -0.2548656464f, -0.2726213634f, -0.2902846634f, -0.3078496456f, -0.3253102899f, -0.3426607251f, -0.3598950505f, -0.3770074248f, -0.3939920366f, -0.4108431637f, -0.4275550842f, -0.4441221356f, -0.4605387151f, -0.4767992198f, -0.492898196f, -0.5088301301f, -0.5245896578f, -0.5401714444f, -0.5555702448f, -0.5707807541f, -0.5857978463f, -0.6006164551f, -0.6152315736f, -0.6296382546f, -0.6438315511f, -0.6578066945f, -0.6715589762f, -0.6850836873f, -0.6983762383f, -0.7114322186f, -0.724247098f, -0.7368165851f, -0.7491363883f, -0.761202395f, -0.7730104327f, -0.7845565677f, -0.7958369255f, -0.8068475723f, -0.8175848126f, -0.8280450702f, -0.838224709f, -0.8481203318f, -0.8577286005f, -0.867046237f, -0.8760700822f, -0.8847970963f, -0.893224299f, -0.9013488293f, -0.909168005f, -0.9166790843f, -0.9238795042f, -0.9307669401f, -0.9373390079f, -0.9435934424f, -0.9495281577f, -0.9551411867f, -0.9604305029f, -0.9653944373f, -0.9700312614f, -0.974339366f, -0.97831738f, -0.9819638729f, -0.9852776527f, -0.988257587f, -0.9909026623f, -0.993211925f, -0.9951847196f, -0.996820271f, -0.9981181026f, -0.9990777373f, -0.9996988177f, -0.9999811649f, -0.9999247193f, -0.9995294213f, -0.9987954497f, -0.997723043f, -0.9963126183f, -0.9945645928f, -0.9924795628f, -0.9900581837f, -0.9873014092f, -0.9842100739f, -0.9807852507f, -0.9770281315f, -0.9729399681f, -0.9685220718f, -0.963776052f, -0.9587034583f, -0.9533060193f, -0.9475855827f, -0.9415440559f, -0.9351835251f, -0.9285060763f, -0.9215140343f, -0.9142097831f, -0.9065957069f, -0.8986744881f, -0.8904487491f, -0.8819212914f, -0.8730949759f, -0.8639728427f, -0.854557991f, -0.84485358f, -0.8348628879f, -0.8245893121f, -0.8140363097f, -0.8032075167f, -0.7921065688f, -0.7807372212f, -0.7691033483f, -0.7572088242f, -0.7450577617f, -0.7326542735f, -0.720002532f, -0.7071067691f, -0.6939714551f, -0.6806010008f, -0.6669999361f, -0.6531728506f, -0.6391244531f, -0.6248595119f, -0.6103827953f, -0.5956993103f, -0.5808139443f, -0.5657318234f, -0.5504579544f, -0.534997642f, -0.5193560123f, -0.5035383701f, -0.4875501692f, -0.4713967443f, -0.4550835788f, -0.438616246f, -0.4220002592f, -0.4052413106f, -0.3883450329f, -0.3713172078f, -0.3541635275f, -0.336889863f, -0.3195020258f, -0.3020059466f, -0.2844075263f, -0.266712755f, -0.2489276081f, -0.2310581058f, -0.2131103128f, -0.1950903237f, -0.1770042181f, -0.1588581502f, -0.1406582445f, -0.1224106774f, -0.1041216329f, -0.08579730988f, -0.06744392216f, -0.04906767607f, -0.030674804f, -0.01227153838f, 0.006135884672f, 0.02454122901f, 0.0429382585f, 0.061320737f, 0.07968243957f, 0.09801714122f, 0.1163186282f, 0.1345807016f, 0.1527971923f, 0.1709618866f, 0.1890686601f, 0.2071113735f, 0.2250839174f, 0.2429801822f, 0.2607941031f, 0.27851969f, 0.296150893f, 0.3136817515f, 0.3311063051f, 0.3484186828f, 0.3656129837f, 0.3826834261f, 0.3996241987f, 0.4164295495f, 0.433093816f, 0.449611336f, 0.4659765065f, 0.4821837842f, 0.4982276559f, 0.514102757f, 0.5298036337f, 0.5453249812f, 0.5606615543f, 0.5758081675f, 0.5907596946f, 0.6055110693f, 0.6200572252f, 0.6343932748f, 0.64851439f, 0.6624158025f, 0.6760926843f, 0.689540565f, 0.7027547359f, 0.7157308459f, 0.728464365f, 0.7409511209f, 0.7531868219f, 0.7651672363f, 0.7768884897f, 0.7883464098f, 0.7995372415f, 0.81045717f, 0.8211025f, 0.8314695954f, 0.8415549994f, 0.851355195f, 0.8608669639f, 0.8700869679f, 0.8790122271f, 0.8876396418f, 0.8959662318f, 0.903989315f, 0.9117060304f, 0.9191138744f, 0.9262102246f, 0.932992816f, 0.9394592047f, 0.9456073046f, 0.9514350295f, 0.9569403529f, 0.9621214271f, 0.9669764638f, 0.9715039134f, 0.975702107f, 0.9795697927f, 0.9831054807f, 0.9863080978f, 0.9891765118f, 0.9917097688f, 0.9939069748f, 0.9957674146f, 0.9972904325f, 0.9984755516f, 0.9993223548f, 0.9998306036f, -0.0f, -0.01840673015f, -0.03680722415f, -0.05519524589f, -0.07356456667f, -0.09190895408f, -0.1102222055f, -0.1284981072f, -0.1467304677f, -0.1649131179f, -0.1830398887f, -0.201104641f, -0.2191012353f, -0.2370236069f, -0.2548656464f, -0.2726213634f, -0.2902846634f, -0.3078496456f, -0.3253102899f, -0.3426607251f, -0.3598950505f, -0.3770074248f, -0.3939920366f, -0.4108431637f, -0.4275550842f, -0.4441221356f, -0.4605387151f, -0.4767992198f, -0.492898196f, -0.5088301301f, -0.5245896578f, -0.5401714444f, -0.5555702448f, -0.5707807541f, -0.5857978463f, -0.6006164551f, -0.6152315736f, -0.6296382546f, -0.6438315511f, -0.6578066945f, -0.6715589762f, -0.6850836873f, -0.6983762383f, -0.7114322186f, -0.724247098f, -0.7368165851f, -0.7491363883f, -0.761202395f, -0.7730104327f, -0.7845565677f, -0.7958369255f, -0.8068475723f, -0.8175848126f, -0.8280450702f, -0.838224709f, -0.8481203318f, -0.8577286005f, -0.867046237f, -0.8760700822f, -0.8847970963f, -0.893224299f, -0.9013488293f, -0.909168005f, -0.9166790843f, -0.9238795042f, -0.9307669401f, -0.9373390079f, -0.9435934424f, -0.9495281577f, -0.9551411867f, -0.9604305029f, -0.9653944373f, -0.9700312614f, -0.974339366f, -0.97831738f, -0.9819638729f, -0.9852776527f, -0.988257587f, -0.9909026623f, -0.993211925f, -0.9951847196f, -0.996820271f, -0.9981181026f, -0.9990777373f, -0.9996988177f, -0.9999811649f, -0.9999247193f, -0.9995294213f, -0.9987954497f, -0.997723043f, -0.9963126183f, -0.9945645928f, -0.9924795628f, -0.9900581837f, -0.9873014092f, -0.9842100739f, -0.9807852507f, -0.9770281315f, -0.9729399681f, -0.9685220718f, -0.963776052f, -0.9587034583f, -0.9533060193f, -0.9475855827f, -0.9415440559f, -0.9351835251f, -0.9285060763f, -0.9215140343f, -0.9142097831f, -0.9065957069f, -0.8986744881f, -0.8904487491f, -0.8819212914f, -0.8730949759f, -0.8639728427f, -0.854557991f, -0.84485358f, -0.8348628879f, -0.8245893121f, -0.8140363097f, -0.8032075167f, -0.7921065688f, -0.7807372212f, -0.7691033483f, -0.7572088242f, -0.7450577617f, -0.7326542735f, -0.720002532f, -0.7071067691f, -0.6939714551f, -0.6806010008f, -0.6669999361f, -0.6531728506f, -0.6391244531f, -0.6248595119f, -0.6103827953f, -0.5956993103f, -0.5808139443f, -0.5657318234f, -0.5504579544f, -0.534997642f, -0.5193560123f, -0.5035383701f, -0.4875501692f, -0.4713967443f, -0.4550835788f, -0.438616246f, -0.4220002592f, -0.4052413106f, -0.3883450329f, -0.3713172078f, -0.3541635275f, -0.336889863f, -0.3195020258f, -0.3020059466f, -0.2844075263f, -0.266712755f, -0.2489276081f, -0.2310581058f, -0.2131103128f, -0.1950903237f, -0.1770042181f, -0.1588581502f, -0.1406582445f, -0.1224106774f, -0.1041216329f, -0.08579730988f, -0.06744392216f, -0.04906767607f, -0.030674804f, -0.01227153838f, 0.006135884672f, 0.02454122901f, 0.0429382585f, 0.061320737f, 0.07968243957f, 0.09801714122f, 0.1163186282f, 0.1345807016f, 0.1527971923f, 0.1709618866f, 0.1890686601f, 0.2071113735f, 0.2250839174f, 0.2429801822f, 0.2607941031f, 0.27851969f, 0.296150893f, 0.3136817515f, 0.3311063051f, 0.3484186828f, 0.3656129837f, 0.3826834261f, 0.3996241987f, 0.4164295495f, 0.433093816f, 0.449611336f, 0.4659765065f, 0.4821837842f, 0.4982276559f, 0.514102757f, 0.5298036337f, 0.5453249812f, 0.5606615543f, 0.5758081675f, 0.5907596946f, 0.6055110693f, 0.6200572252f, 0.6343932748f, 0.64851439f, 0.6624158025f, 0.6760926843f, 0.689540565f, 0.7027547359f, 0.7157308459f, 0.728464365f, 0.7409511209f, 0.7531868219f, 0.7651672363f, 0.7768884897f, 0.7883464098f, 0.7995372415f, 0.81045717f, 0.8211025f, 0.8314695954f, 0.8415549994f, 0.851355195f, 0.8608669639f, 0.8700869679f, 0.8790122271f, 0.8876396418f, 0.8959662318f, 0.903989315f, 0.9117060304f, 0.9191138744f, 0.9262102246f, 0.932992816f, 0.9394592047f, 0.9456073046f, 0.9514350295f, 0.9569403529f, 0.9621214271f, 0.9669764638f, 0.9715039134f, 0.975702107f, 0.9795697927f, 0.9831054807f, 0.9863080978f, 0.9891765118f, 0.9917097688f, 0.9939069748f, 0.9957674146f, 0.9972904325f, 0.9984755516f, 0.9993223548f, 0.9998306036f}; +constant float ts20[512] = {-0.0f, -0.04906767607f, -0.09801714122f, -0.1467304677f, -0.1950903237f, -0.2429801822f, -0.2902846634f, -0.336889863f, -0.3826834261f, -0.4275550842f, -0.4713967443f, -0.514102757f, -0.5555702448f, -0.5956993103f, -0.6343932748f, -0.6715589762f, -0.7071067691f, -0.7409511209f, -0.7730104327f, -0.8032075167f, -0.8314695954f, -0.8577286005f, -0.8819212914f, -0.903989315f, -0.9238795042f, -0.9415440559f, -0.9569403529f, -0.9700312614f, -0.9807852507f, -0.9891765118f, -0.9951847196f, -0.9987954497f, -1.0f, -0.9987954497f, -0.9951847196f, -0.9891765118f, -0.9807852507f, -0.9700312614f, -0.9569403529f, -0.9415440559f, -0.9238795042f, -0.903989315f, -0.8819212914f, -0.8577286005f, -0.8314695954f, -0.8032075167f, -0.7730104327f, -0.7409511209f, -0.7071067691f, -0.6715589762f, -0.6343932748f, -0.5956993103f, -0.5555702448f, -0.514102757f, -0.4713967443f, -0.4275550842f, -0.3826834261f, -0.336889863f, -0.2902846634f, -0.2429801822f, -0.1950903237f, -0.1467304677f, -0.09801714122f, -0.04906767607f, -0.0f, -0.04906767607f, -0.09801714122f, -0.1467304677f, -0.1950903237f, -0.2429801822f, -0.2902846634f, -0.336889863f, -0.3826834261f, -0.4275550842f, -0.4713967443f, -0.514102757f, -0.5555702448f, -0.5956993103f, -0.6343932748f, -0.6715589762f, -0.7071067691f, -0.7409511209f, -0.7730104327f, -0.8032075167f, -0.8314695954f, -0.8577286005f, -0.8819212914f, -0.903989315f, -0.9238795042f, -0.9415440559f, -0.9569403529f, -0.9700312614f, -0.9807852507f, -0.9891765118f, -0.9951847196f, -0.9987954497f, -1.0f, -0.9987954497f, -0.9951847196f, -0.9891765118f, -0.9807852507f, -0.9700312614f, -0.9569403529f, -0.9415440559f, -0.9238795042f, -0.903989315f, -0.8819212914f, -0.8577286005f, -0.8314695954f, -0.8032075167f, -0.7730104327f, -0.7409511209f, -0.7071067691f, -0.6715589762f, -0.6343932748f, -0.5956993103f, -0.5555702448f, -0.514102757f, -0.4713967443f, -0.4275550842f, -0.3826834261f, -0.336889863f, -0.2902846634f, -0.2429801822f, -0.1950903237f, -0.1467304677f, -0.09801714122f, -0.04906767607f, -0.0f, -0.04906767607f, -0.09801714122f, -0.1467304677f, -0.1950903237f, -0.2429801822f, -0.2902846634f, -0.336889863f, -0.3826834261f, -0.4275550842f, -0.4713967443f, -0.514102757f, -0.5555702448f, -0.5956993103f, -0.6343932748f, -0.6715589762f, -0.7071067691f, -0.7409511209f, -0.7730104327f, -0.8032075167f, -0.8314695954f, -0.8577286005f, -0.8819212914f, -0.903989315f, -0.9238795042f, -0.9415440559f, -0.9569403529f, -0.9700312614f, -0.9807852507f, -0.9891765118f, -0.9951847196f, -0.9987954497f, -1.0f, -0.9987954497f, -0.9951847196f, -0.9891765118f, -0.9807852507f, -0.9700312614f, -0.9569403529f, -0.9415440559f, -0.9238795042f, -0.903989315f, -0.8819212914f, -0.8577286005f, -0.8314695954f, -0.8032075167f, -0.7730104327f, -0.7409511209f, -0.7071067691f, -0.6715589762f, -0.6343932748f, -0.5956993103f, -0.5555702448f, -0.514102757f, -0.4713967443f, -0.4275550842f, -0.3826834261f, -0.336889863f, -0.2902846634f, -0.2429801822f, -0.1950903237f, -0.1467304677f, -0.09801714122f, -0.04906767607f, -0.0f, -0.04906767607f, -0.09801714122f, -0.1467304677f, -0.1950903237f, -0.2429801822f, -0.2902846634f, -0.336889863f, -0.3826834261f, -0.4275550842f, -0.4713967443f, -0.514102757f, -0.5555702448f, -0.5956993103f, -0.6343932748f, -0.6715589762f, -0.7071067691f, -0.7409511209f, -0.7730104327f, -0.8032075167f, -0.8314695954f, -0.8577286005f, -0.8819212914f, -0.903989315f, -0.9238795042f, -0.9415440559f, -0.9569403529f, -0.9700312614f, -0.9807852507f, -0.9891765118f, -0.9951847196f, -0.9987954497f, -1.0f, -0.9987954497f, -0.9951847196f, -0.9891765118f, -0.9807852507f, -0.9700312614f, -0.9569403529f, -0.9415440559f, -0.9238795042f, -0.903989315f, -0.8819212914f, -0.8577286005f, -0.8314695954f, -0.8032075167f, -0.7730104327f, -0.7409511209f, -0.7071067691f, -0.6715589762f, -0.6343932748f, -0.5956993103f, -0.5555702448f, -0.514102757f, -0.4713967443f, -0.4275550842f, -0.3826834261f, -0.336889863f, -0.2902846634f, -0.2429801822f, -0.1950903237f, -0.1467304677f, -0.09801714122f, -0.04906767607f, -0.0f, -0.04906767607f, -0.09801714122f, -0.1467304677f, -0.1950903237f, -0.2429801822f, -0.2902846634f, -0.336889863f, -0.3826834261f, -0.4275550842f, -0.4713967443f, -0.514102757f, -0.5555702448f, -0.5956993103f, -0.6343932748f, -0.6715589762f, -0.7071067691f, -0.7409511209f, -0.7730104327f, -0.8032075167f, -0.8314695954f, -0.8577286005f, -0.8819212914f, -0.903989315f, -0.9238795042f, -0.9415440559f, -0.9569403529f, -0.9700312614f, -0.9807852507f, -0.9891765118f, -0.9951847196f, -0.9987954497f, -1.0f, -0.9987954497f, -0.9951847196f, -0.9891765118f, -0.9807852507f, -0.9700312614f, -0.9569403529f, -0.9415440559f, -0.9238795042f, -0.903989315f, -0.8819212914f, -0.8577286005f, -0.8314695954f, -0.8032075167f, -0.7730104327f, -0.7409511209f, -0.7071067691f, -0.6715589762f, -0.6343932748f, -0.5956993103f, -0.5555702448f, -0.514102757f, -0.4713967443f, -0.4275550842f, -0.3826834261f, -0.336889863f, -0.2902846634f, -0.2429801822f, -0.1950903237f, -0.1467304677f, -0.09801714122f, -0.04906767607f, -0.0f, -0.04906767607f, -0.09801714122f, -0.1467304677f, -0.1950903237f, -0.2429801822f, -0.2902846634f, -0.336889863f, -0.3826834261f, -0.4275550842f, -0.4713967443f, -0.514102757f, -0.5555702448f, -0.5956993103f, -0.6343932748f, -0.6715589762f, -0.7071067691f, -0.7409511209f, -0.7730104327f, -0.8032075167f, -0.8314695954f, -0.8577286005f, -0.8819212914f, -0.903989315f, -0.9238795042f, -0.9415440559f, -0.9569403529f, -0.9700312614f, -0.9807852507f, -0.9891765118f, -0.9951847196f, -0.9987954497f, -1.0f, -0.9987954497f, -0.9951847196f, -0.9891765118f, -0.9807852507f, -0.9700312614f, -0.9569403529f, -0.9415440559f, -0.9238795042f, -0.903989315f, -0.8819212914f, -0.8577286005f, -0.8314695954f, -0.8032075167f, -0.7730104327f, -0.7409511209f, -0.7071067691f, -0.6715589762f, -0.6343932748f, -0.5956993103f, -0.5555702448f, -0.514102757f, -0.4713967443f, -0.4275550842f, -0.3826834261f, -0.336889863f, -0.2902846634f, -0.2429801822f, -0.1950903237f, -0.1467304677f, -0.09801714122f, -0.04906767607f, -0.0f, -0.04906767607f, -0.09801714122f, -0.1467304677f, -0.1950903237f, -0.2429801822f, -0.2902846634f, -0.336889863f, -0.3826834261f, -0.4275550842f, -0.4713967443f, -0.514102757f, -0.5555702448f, -0.5956993103f, -0.6343932748f, -0.6715589762f, -0.7071067691f, -0.7409511209f, -0.7730104327f, -0.8032075167f, -0.8314695954f, -0.8577286005f, -0.8819212914f, -0.903989315f, -0.9238795042f, -0.9415440559f, -0.9569403529f, -0.9700312614f, -0.9807852507f, -0.9891765118f, -0.9951847196f, -0.9987954497f, -1.0f, -0.9987954497f, -0.9951847196f, -0.9891765118f, -0.9807852507f, -0.9700312614f, -0.9569403529f, -0.9415440559f, -0.9238795042f, -0.903989315f, -0.8819212914f, -0.8577286005f, -0.8314695954f, -0.8032075167f, -0.7730104327f, -0.7409511209f, -0.7071067691f, -0.6715589762f, -0.6343932748f, -0.5956993103f, -0.5555702448f, -0.514102757f, -0.4713967443f, -0.4275550842f, -0.3826834261f, -0.336889863f, -0.2902846634f, -0.2429801822f, -0.1950903237f, -0.1467304677f, -0.09801714122f, -0.04906767607f, -0.0f, -0.04906767607f, -0.09801714122f, -0.1467304677f, -0.1950903237f, -0.2429801822f, -0.2902846634f, -0.336889863f, -0.3826834261f, -0.4275550842f, -0.4713967443f, -0.514102757f, -0.5555702448f, -0.5956993103f, -0.6343932748f, -0.6715589762f, -0.7071067691f, -0.7409511209f, -0.7730104327f, -0.8032075167f, -0.8314695954f, -0.8577286005f, -0.8819212914f, -0.903989315f, -0.9238795042f, -0.9415440559f, -0.9569403529f, -0.9700312614f, -0.9807852507f, -0.9891765118f, -0.9951847196f, -0.9987954497f, -1.0f, -0.9987954497f, -0.9951847196f, -0.9891765118f, -0.9807852507f, -0.9700312614f, -0.9569403529f, -0.9415440559f, -0.9238795042f, -0.903989315f, -0.8819212914f, -0.8577286005f, -0.8314695954f, -0.8032075167f, -0.7730104327f, -0.7409511209f, -0.7071067691f, -0.6715589762f, -0.6343932748f, -0.5956993103f, -0.5555702448f, -0.514102757f, -0.4713967443f, -0.4275550842f, -0.3826834261f, -0.336889863f, -0.2902846634f, -0.2429801822f, -0.1950903237f, -0.1467304677f, -0.09801714122f, -0.04906767607f}; +constant float ts23[512] = {-0.0f, -0.04906767607f, -0.09801714122f, -0.1467304677f, -0.1950903237f, -0.2429801822f, -0.2902846634f, -0.336889863f, -0.3826834261f, -0.4275550842f, -0.4713967443f, -0.514102757f, -0.5555702448f, -0.5956993103f, -0.6343932748f, -0.6715589762f, -0.7071067691f, -0.7409511209f, -0.7730104327f, -0.8032075167f, -0.8314695954f, -0.8577286005f, -0.8819212914f, -0.903989315f, -0.9238795042f, -0.9415440559f, -0.9569403529f, -0.9700312614f, -0.9807852507f, -0.9891765118f, -0.9951847196f, -0.9987954497f, -1.0f, -0.9987954497f, -0.9951847196f, -0.9891765118f, -0.9807852507f, -0.9700312614f, -0.9569403529f, -0.9415440559f, -0.9238795042f, -0.903989315f, -0.8819212914f, -0.8577286005f, -0.8314695954f, -0.8032075167f, -0.7730104327f, -0.7409511209f, -0.7071067691f, -0.6715589762f, -0.6343932748f, -0.5956993103f, -0.5555702448f, -0.514102757f, -0.4713967443f, -0.4275550842f, -0.3826834261f, -0.336889863f, -0.2902846634f, -0.2429801822f, -0.1950903237f, -0.1467304677f, -0.09801714122f, -0.04906767607f, -0.0f, -0.04906767607f, -0.09801714122f, -0.1467304677f, -0.1950903237f, -0.2429801822f, -0.2902846634f, -0.336889863f, -0.3826834261f, -0.4275550842f, -0.4713967443f, -0.514102757f, -0.5555702448f, -0.5956993103f, -0.6343932748f, -0.6715589762f, -0.7071067691f, -0.7409511209f, -0.7730104327f, -0.8032075167f, -0.8314695954f, -0.8577286005f, -0.8819212914f, -0.903989315f, -0.9238795042f, -0.9415440559f, -0.9569403529f, -0.9700312614f, -0.9807852507f, -0.9891765118f, -0.9951847196f, -0.9987954497f, -1.0f, -0.9987954497f, -0.9951847196f, -0.9891765118f, -0.9807852507f, -0.9700312614f, -0.9569403529f, -0.9415440559f, -0.9238795042f, -0.903989315f, -0.8819212914f, -0.8577286005f, -0.8314695954f, -0.8032075167f, -0.7730104327f, -0.7409511209f, -0.7071067691f, -0.6715589762f, -0.6343932748f, -0.5956993103f, -0.5555702448f, -0.514102757f, -0.4713967443f, -0.4275550842f, -0.3826834261f, -0.336889863f, -0.2902846634f, -0.2429801822f, -0.1950903237f, -0.1467304677f, -0.09801714122f, -0.04906767607f, -0.0f, -0.04906767607f, -0.09801714122f, -0.1467304677f, -0.1950903237f, -0.2429801822f, -0.2902846634f, -0.336889863f, -0.3826834261f, -0.4275550842f, -0.4713967443f, -0.514102757f, -0.5555702448f, -0.5956993103f, -0.6343932748f, -0.6715589762f, -0.7071067691f, -0.7409511209f, -0.7730104327f, -0.8032075167f, -0.8314695954f, -0.8577286005f, -0.8819212914f, -0.903989315f, -0.9238795042f, -0.9415440559f, -0.9569403529f, -0.9700312614f, -0.9807852507f, -0.9891765118f, -0.9951847196f, -0.9987954497f, -1.0f, -0.9987954497f, -0.9951847196f, -0.9891765118f, -0.9807852507f, -0.9700312614f, -0.9569403529f, -0.9415440559f, -0.9238795042f, -0.903989315f, -0.8819212914f, -0.8577286005f, -0.8314695954f, -0.8032075167f, -0.7730104327f, -0.7409511209f, -0.7071067691f, -0.6715589762f, -0.6343932748f, -0.5956993103f, -0.5555702448f, -0.514102757f, -0.4713967443f, -0.4275550842f, -0.3826834261f, -0.336889863f, -0.2902846634f, -0.2429801822f, -0.1950903237f, -0.1467304677f, -0.09801714122f, -0.04906767607f, -0.0f, -0.04906767607f, -0.09801714122f, -0.1467304677f, -0.1950903237f, -0.2429801822f, -0.2902846634f, -0.336889863f, -0.3826834261f, -0.4275550842f, -0.4713967443f, -0.514102757f, -0.5555702448f, -0.5956993103f, -0.6343932748f, -0.6715589762f, -0.7071067691f, -0.7409511209f, -0.7730104327f, -0.8032075167f, -0.8314695954f, -0.8577286005f, -0.8819212914f, -0.903989315f, -0.9238795042f, -0.9415440559f, -0.9569403529f, -0.9700312614f, -0.9807852507f, -0.9891765118f, -0.9951847196f, -0.9987954497f, -1.0f, -0.9987954497f, -0.9951847196f, -0.9891765118f, -0.9807852507f, -0.9700312614f, -0.9569403529f, -0.9415440559f, -0.9238795042f, -0.903989315f, -0.8819212914f, -0.8577286005f, -0.8314695954f, -0.8032075167f, -0.7730104327f, -0.7409511209f, -0.7071067691f, -0.6715589762f, -0.6343932748f, -0.5956993103f, -0.5555702448f, -0.514102757f, -0.4713967443f, -0.4275550842f, -0.3826834261f, -0.336889863f, -0.2902846634f, -0.2429801822f, -0.1950903237f, -0.1467304677f, -0.09801714122f, -0.04906767607f, -0.0f, -0.04906767607f, -0.09801714122f, -0.1467304677f, -0.1950903237f, -0.2429801822f, -0.2902846634f, -0.336889863f, -0.3826834261f, -0.4275550842f, -0.4713967443f, -0.514102757f, -0.5555702448f, -0.5956993103f, -0.6343932748f, -0.6715589762f, -0.7071067691f, -0.7409511209f, -0.7730104327f, -0.8032075167f, -0.8314695954f, -0.8577286005f, -0.8819212914f, -0.903989315f, -0.9238795042f, -0.9415440559f, -0.9569403529f, -0.9700312614f, -0.9807852507f, -0.9891765118f, -0.9951847196f, -0.9987954497f, -1.0f, -0.9987954497f, -0.9951847196f, -0.9891765118f, -0.9807852507f, -0.9700312614f, -0.9569403529f, -0.9415440559f, -0.9238795042f, -0.903989315f, -0.8819212914f, -0.8577286005f, -0.8314695954f, -0.8032075167f, -0.7730104327f, -0.7409511209f, -0.7071067691f, -0.6715589762f, -0.6343932748f, -0.5956993103f, -0.5555702448f, -0.514102757f, -0.4713967443f, -0.4275550842f, -0.3826834261f, -0.336889863f, -0.2902846634f, -0.2429801822f, -0.1950903237f, -0.1467304677f, -0.09801714122f, -0.04906767607f, -0.0f, -0.04906767607f, -0.09801714122f, -0.1467304677f, -0.1950903237f, -0.2429801822f, -0.2902846634f, -0.336889863f, -0.3826834261f, -0.4275550842f, -0.4713967443f, -0.514102757f, -0.5555702448f, -0.5956993103f, -0.6343932748f, -0.6715589762f, -0.7071067691f, -0.7409511209f, -0.7730104327f, -0.8032075167f, -0.8314695954f, -0.8577286005f, -0.8819212914f, -0.903989315f, -0.9238795042f, -0.9415440559f, -0.9569403529f, -0.9700312614f, -0.9807852507f, -0.9891765118f, -0.9951847196f, -0.9987954497f, -1.0f, -0.9987954497f, -0.9951847196f, -0.9891765118f, -0.9807852507f, -0.9700312614f, -0.9569403529f, -0.9415440559f, -0.9238795042f, -0.903989315f, -0.8819212914f, -0.8577286005f, -0.8314695954f, -0.8032075167f, -0.7730104327f, -0.7409511209f, -0.7071067691f, -0.6715589762f, -0.6343932748f, -0.5956993103f, -0.5555702448f, -0.514102757f, -0.4713967443f, -0.4275550842f, -0.3826834261f, -0.336889863f, -0.2902846634f, -0.2429801822f, -0.1950903237f, -0.1467304677f, -0.09801714122f, -0.04906767607f, -0.0f, -0.04906767607f, -0.09801714122f, -0.1467304677f, -0.1950903237f, -0.2429801822f, -0.2902846634f, -0.336889863f, -0.3826834261f, -0.4275550842f, -0.4713967443f, -0.514102757f, -0.5555702448f, -0.5956993103f, -0.6343932748f, -0.6715589762f, -0.7071067691f, -0.7409511209f, -0.7730104327f, -0.8032075167f, -0.8314695954f, -0.8577286005f, -0.8819212914f, -0.903989315f, -0.9238795042f, -0.9415440559f, -0.9569403529f, -0.9700312614f, -0.9807852507f, -0.9891765118f, -0.9951847196f, -0.9987954497f, -1.0f, -0.9987954497f, -0.9951847196f, -0.9891765118f, -0.9807852507f, -0.9700312614f, -0.9569403529f, -0.9415440559f, -0.9238795042f, -0.903989315f, -0.8819212914f, -0.8577286005f, -0.8314695954f, -0.8032075167f, -0.7730104327f, -0.7409511209f, -0.7071067691f, -0.6715589762f, -0.6343932748f, -0.5956993103f, -0.5555702448f, -0.514102757f, -0.4713967443f, -0.4275550842f, -0.3826834261f, -0.336889863f, -0.2902846634f, -0.2429801822f, -0.1950903237f, -0.1467304677f, -0.09801714122f, -0.04906767607f, -0.0f, -0.04906767607f, -0.09801714122f, -0.1467304677f, -0.1950903237f, -0.2429801822f, -0.2902846634f, -0.336889863f, -0.3826834261f, -0.4275550842f, -0.4713967443f, -0.514102757f, -0.5555702448f, -0.5956993103f, -0.6343932748f, -0.6715589762f, -0.7071067691f, -0.7409511209f, -0.7730104327f, -0.8032075167f, -0.8314695954f, -0.8577286005f, -0.8819212914f, -0.903989315f, -0.9238795042f, -0.9415440559f, -0.9569403529f, -0.9700312614f, -0.9807852507f, -0.9891765118f, -0.9951847196f, -0.9987954497f, -1.0f, -0.9987954497f, -0.9951847196f, -0.9891765118f, -0.9807852507f, -0.9700312614f, -0.9569403529f, -0.9415440559f, -0.9238795042f, -0.903989315f, -0.8819212914f, -0.8577286005f, -0.8314695954f, -0.8032075167f, -0.7730104327f, -0.7409511209f, -0.7071067691f, -0.6715589762f, -0.6343932748f, -0.5956993103f, -0.5555702448f, -0.514102757f, -0.4713967443f, -0.4275550842f, -0.3826834261f, -0.336889863f, -0.2902846634f, -0.2429801822f, -0.1950903237f, -0.1467304677f, -0.09801714122f, -0.04906767607f}; +constant float ts21[512] = {-0.0f, -0.02454122901f, -0.04906767607f, -0.07356456667f, -0.09801714122f, -0.1224106774f, -0.1467304677f, -0.1709618866f, -0.1950903237f, -0.2191012353f, -0.2429801822f, -0.266712755f, -0.2902846634f, -0.3136817515f, -0.336889863f, -0.3598950505f, -0.3826834261f, -0.4052413106f, -0.4275550842f, -0.449611336f, -0.4713967443f, -0.492898196f, -0.514102757f, -0.534997642f, -0.5555702448f, -0.5758081675f, -0.5956993103f, -0.6152315736f, -0.6343932748f, -0.6531728506f, -0.6715589762f, -0.689540565f, -0.7071067691f, -0.724247098f, -0.7409511209f, -0.7572088242f, -0.7730104327f, -0.7883464098f, -0.8032075167f, -0.8175848126f, -0.8314695954f, -0.84485358f, -0.8577286005f, -0.8700869679f, -0.8819212914f, -0.893224299f, -0.903989315f, -0.9142097831f, -0.9238795042f, -0.932992816f, -0.9415440559f, -0.9495281577f, -0.9569403529f, -0.963776052f, -0.9700312614f, -0.975702107f, -0.9807852507f, -0.9852776527f, -0.9891765118f, -0.9924795628f, -0.9951847196f, -0.9972904325f, -0.9987954497f, -0.9996988177f, -0.0f, -0.02454122901f, -0.04906767607f, -0.07356456667f, -0.09801714122f, -0.1224106774f, -0.1467304677f, -0.1709618866f, -0.1950903237f, -0.2191012353f, -0.2429801822f, -0.266712755f, -0.2902846634f, -0.3136817515f, -0.336889863f, -0.3598950505f, -0.3826834261f, -0.4052413106f, -0.4275550842f, -0.449611336f, -0.4713967443f, -0.492898196f, -0.514102757f, -0.534997642f, -0.5555702448f, -0.5758081675f, -0.5956993103f, -0.6152315736f, -0.6343932748f, -0.6531728506f, -0.6715589762f, -0.689540565f, -0.7071067691f, -0.724247098f, -0.7409511209f, -0.7572088242f, -0.7730104327f, -0.7883464098f, -0.8032075167f, -0.8175848126f, -0.8314695954f, -0.84485358f, -0.8577286005f, -0.8700869679f, -0.8819212914f, -0.893224299f, -0.903989315f, -0.9142097831f, -0.9238795042f, -0.932992816f, -0.9415440559f, -0.9495281577f, -0.9569403529f, -0.963776052f, -0.9700312614f, -0.975702107f, -0.9807852507f, -0.9852776527f, -0.9891765118f, -0.9924795628f, -0.9951847196f, -0.9972904325f, -0.9987954497f, -0.9996988177f, -0.0f, -0.02454122901f, -0.04906767607f, -0.07356456667f, -0.09801714122f, -0.1224106774f, -0.1467304677f, -0.1709618866f, -0.1950903237f, -0.2191012353f, -0.2429801822f, -0.266712755f, -0.2902846634f, -0.3136817515f, -0.336889863f, -0.3598950505f, -0.3826834261f, -0.4052413106f, -0.4275550842f, -0.449611336f, -0.4713967443f, -0.492898196f, -0.514102757f, -0.534997642f, -0.5555702448f, -0.5758081675f, -0.5956993103f, -0.6152315736f, -0.6343932748f, -0.6531728506f, -0.6715589762f, -0.689540565f, -0.7071067691f, -0.724247098f, -0.7409511209f, -0.7572088242f, -0.7730104327f, -0.7883464098f, -0.8032075167f, -0.8175848126f, -0.8314695954f, -0.84485358f, -0.8577286005f, -0.8700869679f, -0.8819212914f, -0.893224299f, -0.903989315f, -0.9142097831f, -0.9238795042f, -0.932992816f, -0.9415440559f, -0.9495281577f, -0.9569403529f, -0.963776052f, -0.9700312614f, -0.975702107f, -0.9807852507f, -0.9852776527f, -0.9891765118f, -0.9924795628f, -0.9951847196f, -0.9972904325f, -0.9987954497f, -0.9996988177f, -0.0f, -0.02454122901f, -0.04906767607f, -0.07356456667f, -0.09801714122f, -0.1224106774f, -0.1467304677f, -0.1709618866f, -0.1950903237f, -0.2191012353f, -0.2429801822f, -0.266712755f, -0.2902846634f, -0.3136817515f, -0.336889863f, -0.3598950505f, -0.3826834261f, -0.4052413106f, -0.4275550842f, -0.449611336f, -0.4713967443f, -0.492898196f, -0.514102757f, -0.534997642f, -0.5555702448f, -0.5758081675f, -0.5956993103f, -0.6152315736f, -0.6343932748f, -0.6531728506f, -0.6715589762f, -0.689540565f, -0.7071067691f, -0.724247098f, -0.7409511209f, -0.7572088242f, -0.7730104327f, -0.7883464098f, -0.8032075167f, -0.8175848126f, -0.8314695954f, -0.84485358f, -0.8577286005f, -0.8700869679f, -0.8819212914f, -0.893224299f, -0.903989315f, -0.9142097831f, -0.9238795042f, -0.932992816f, -0.9415440559f, -0.9495281577f, -0.9569403529f, -0.963776052f, -0.9700312614f, -0.975702107f, -0.9807852507f, -0.9852776527f, -0.9891765118f, -0.9924795628f, -0.9951847196f, -0.9972904325f, -0.9987954497f, -0.9996988177f, -0.0f, -0.02454122901f, -0.04906767607f, -0.07356456667f, -0.09801714122f, -0.1224106774f, -0.1467304677f, -0.1709618866f, -0.1950903237f, -0.2191012353f, -0.2429801822f, -0.266712755f, -0.2902846634f, -0.3136817515f, -0.336889863f, -0.3598950505f, -0.3826834261f, -0.4052413106f, -0.4275550842f, -0.449611336f, -0.4713967443f, -0.492898196f, -0.514102757f, -0.534997642f, -0.5555702448f, -0.5758081675f, -0.5956993103f, -0.6152315736f, -0.6343932748f, -0.6531728506f, -0.6715589762f, -0.689540565f, -0.7071067691f, -0.724247098f, -0.7409511209f, -0.7572088242f, -0.7730104327f, -0.7883464098f, -0.8032075167f, -0.8175848126f, -0.8314695954f, -0.84485358f, -0.8577286005f, -0.8700869679f, -0.8819212914f, -0.893224299f, -0.903989315f, -0.9142097831f, -0.9238795042f, -0.932992816f, -0.9415440559f, -0.9495281577f, -0.9569403529f, -0.963776052f, -0.9700312614f, -0.975702107f, -0.9807852507f, -0.9852776527f, -0.9891765118f, -0.9924795628f, -0.9951847196f, -0.9972904325f, -0.9987954497f, -0.9996988177f, -0.0f, -0.02454122901f, -0.04906767607f, -0.07356456667f, -0.09801714122f, -0.1224106774f, -0.1467304677f, -0.1709618866f, -0.1950903237f, -0.2191012353f, -0.2429801822f, -0.266712755f, -0.2902846634f, -0.3136817515f, -0.336889863f, -0.3598950505f, -0.3826834261f, -0.4052413106f, -0.4275550842f, -0.449611336f, -0.4713967443f, -0.492898196f, -0.514102757f, -0.534997642f, -0.5555702448f, -0.5758081675f, -0.5956993103f, -0.6152315736f, -0.6343932748f, -0.6531728506f, -0.6715589762f, -0.689540565f, -0.7071067691f, -0.724247098f, -0.7409511209f, -0.7572088242f, -0.7730104327f, -0.7883464098f, -0.8032075167f, -0.8175848126f, -0.8314695954f, -0.84485358f, -0.8577286005f, -0.8700869679f, -0.8819212914f, -0.893224299f, -0.903989315f, -0.9142097831f, -0.9238795042f, -0.932992816f, -0.9415440559f, -0.9495281577f, -0.9569403529f, -0.963776052f, -0.9700312614f, -0.975702107f, -0.9807852507f, -0.9852776527f, -0.9891765118f, -0.9924795628f, -0.9951847196f, -0.9972904325f, -0.9987954497f, -0.9996988177f, -0.0f, -0.02454122901f, -0.04906767607f, -0.07356456667f, -0.09801714122f, -0.1224106774f, -0.1467304677f, -0.1709618866f, -0.1950903237f, -0.2191012353f, -0.2429801822f, -0.266712755f, -0.2902846634f, -0.3136817515f, -0.336889863f, -0.3598950505f, -0.3826834261f, -0.4052413106f, -0.4275550842f, -0.449611336f, -0.4713967443f, -0.492898196f, -0.514102757f, -0.534997642f, -0.5555702448f, -0.5758081675f, -0.5956993103f, -0.6152315736f, -0.6343932748f, -0.6531728506f, -0.6715589762f, -0.689540565f, -0.7071067691f, -0.724247098f, -0.7409511209f, -0.7572088242f, -0.7730104327f, -0.7883464098f, -0.8032075167f, -0.8175848126f, -0.8314695954f, -0.84485358f, -0.8577286005f, -0.8700869679f, -0.8819212914f, -0.893224299f, -0.903989315f, -0.9142097831f, -0.9238795042f, -0.932992816f, -0.9415440559f, -0.9495281577f, -0.9569403529f, -0.963776052f, -0.9700312614f, -0.975702107f, -0.9807852507f, -0.9852776527f, -0.9891765118f, -0.9924795628f, -0.9951847196f, -0.9972904325f, -0.9987954497f, -0.9996988177f, -0.0f, -0.02454122901f, -0.04906767607f, -0.07356456667f, -0.09801714122f, -0.1224106774f, -0.1467304677f, -0.1709618866f, -0.1950903237f, -0.2191012353f, -0.2429801822f, -0.266712755f, -0.2902846634f, -0.3136817515f, -0.336889863f, -0.3598950505f, -0.3826834261f, -0.4052413106f, -0.4275550842f, -0.449611336f, -0.4713967443f, -0.492898196f, -0.514102757f, -0.534997642f, -0.5555702448f, -0.5758081675f, -0.5956993103f, -0.6152315736f, -0.6343932748f, -0.6531728506f, -0.6715589762f, -0.689540565f, -0.7071067691f, -0.724247098f, -0.7409511209f, -0.7572088242f, -0.7730104327f, -0.7883464098f, -0.8032075167f, -0.8175848126f, -0.8314695954f, -0.84485358f, -0.8577286005f, -0.8700869679f, -0.8819212914f, -0.893224299f, -0.903989315f, -0.9142097831f, -0.9238795042f, -0.932992816f, -0.9415440559f, -0.9495281577f, -0.9569403529f, -0.963776052f, -0.9700312614f, -0.975702107f, -0.9807852507f, -0.9852776527f, -0.9891765118f, -0.9924795628f, -0.9951847196f, -0.9972904325f, -0.9987954497f, -0.9996988177f}; +constant float ts24[512] = {-0.0f, -0.02454122901f, -0.04906767607f, -0.07356456667f, -0.09801714122f, -0.1224106774f, -0.1467304677f, -0.1709618866f, -0.1950903237f, -0.2191012353f, -0.2429801822f, -0.266712755f, -0.2902846634f, -0.3136817515f, -0.336889863f, -0.3598950505f, -0.3826834261f, -0.4052413106f, -0.4275550842f, -0.449611336f, -0.4713967443f, -0.492898196f, -0.514102757f, -0.534997642f, -0.5555702448f, -0.5758081675f, -0.5956993103f, -0.6152315736f, -0.6343932748f, -0.6531728506f, -0.6715589762f, -0.689540565f, -0.7071067691f, -0.724247098f, -0.7409511209f, -0.7572088242f, -0.7730104327f, -0.7883464098f, -0.8032075167f, -0.8175848126f, -0.8314695954f, -0.84485358f, -0.8577286005f, -0.8700869679f, -0.8819212914f, -0.893224299f, -0.903989315f, -0.9142097831f, -0.9238795042f, -0.932992816f, -0.9415440559f, -0.9495281577f, -0.9569403529f, -0.963776052f, -0.9700312614f, -0.975702107f, -0.9807852507f, -0.9852776527f, -0.9891765118f, -0.9924795628f, -0.9951847196f, -0.9972904325f, -0.9987954497f, -0.9996988177f, -0.0f, -0.02454122901f, -0.04906767607f, -0.07356456667f, -0.09801714122f, -0.1224106774f, -0.1467304677f, -0.1709618866f, -0.1950903237f, -0.2191012353f, -0.2429801822f, -0.266712755f, -0.2902846634f, -0.3136817515f, -0.336889863f, -0.3598950505f, -0.3826834261f, -0.4052413106f, -0.4275550842f, -0.449611336f, -0.4713967443f, -0.492898196f, -0.514102757f, -0.534997642f, -0.5555702448f, -0.5758081675f, -0.5956993103f, -0.6152315736f, -0.6343932748f, -0.6531728506f, -0.6715589762f, -0.689540565f, -0.7071067691f, -0.724247098f, -0.7409511209f, -0.7572088242f, -0.7730104327f, -0.7883464098f, -0.8032075167f, -0.8175848126f, -0.8314695954f, -0.84485358f, -0.8577286005f, -0.8700869679f, -0.8819212914f, -0.893224299f, -0.903989315f, -0.9142097831f, -0.9238795042f, -0.932992816f, -0.9415440559f, -0.9495281577f, -0.9569403529f, -0.963776052f, -0.9700312614f, -0.975702107f, -0.9807852507f, -0.9852776527f, -0.9891765118f, -0.9924795628f, -0.9951847196f, -0.9972904325f, -0.9987954497f, -0.9996988177f, -0.0f, -0.02454122901f, -0.04906767607f, -0.07356456667f, -0.09801714122f, -0.1224106774f, -0.1467304677f, -0.1709618866f, -0.1950903237f, -0.2191012353f, -0.2429801822f, -0.266712755f, -0.2902846634f, -0.3136817515f, -0.336889863f, -0.3598950505f, -0.3826834261f, -0.4052413106f, -0.4275550842f, -0.449611336f, -0.4713967443f, -0.492898196f, -0.514102757f, -0.534997642f, -0.5555702448f, -0.5758081675f, -0.5956993103f, -0.6152315736f, -0.6343932748f, -0.6531728506f, -0.6715589762f, -0.689540565f, -0.7071067691f, -0.724247098f, -0.7409511209f, -0.7572088242f, -0.7730104327f, -0.7883464098f, -0.8032075167f, -0.8175848126f, -0.8314695954f, -0.84485358f, -0.8577286005f, -0.8700869679f, -0.8819212914f, -0.893224299f, -0.903989315f, -0.9142097831f, -0.9238795042f, -0.932992816f, -0.9415440559f, -0.9495281577f, -0.9569403529f, -0.963776052f, -0.9700312614f, -0.975702107f, -0.9807852507f, -0.9852776527f, -0.9891765118f, -0.9924795628f, -0.9951847196f, -0.9972904325f, -0.9987954497f, -0.9996988177f, -0.0f, -0.02454122901f, -0.04906767607f, -0.07356456667f, -0.09801714122f, -0.1224106774f, -0.1467304677f, -0.1709618866f, -0.1950903237f, -0.2191012353f, -0.2429801822f, -0.266712755f, -0.2902846634f, -0.3136817515f, -0.336889863f, -0.3598950505f, -0.3826834261f, -0.4052413106f, -0.4275550842f, -0.449611336f, -0.4713967443f, -0.492898196f, -0.514102757f, -0.534997642f, -0.5555702448f, -0.5758081675f, -0.5956993103f, -0.6152315736f, -0.6343932748f, -0.6531728506f, -0.6715589762f, -0.689540565f, -0.7071067691f, -0.724247098f, -0.7409511209f, -0.7572088242f, -0.7730104327f, -0.7883464098f, -0.8032075167f, -0.8175848126f, -0.8314695954f, -0.84485358f, -0.8577286005f, -0.8700869679f, -0.8819212914f, -0.893224299f, -0.903989315f, -0.9142097831f, -0.9238795042f, -0.932992816f, -0.9415440559f, -0.9495281577f, -0.9569403529f, -0.963776052f, -0.9700312614f, -0.975702107f, -0.9807852507f, -0.9852776527f, -0.9891765118f, -0.9924795628f, -0.9951847196f, -0.9972904325f, -0.9987954497f, -0.9996988177f, -0.0f, -0.02454122901f, -0.04906767607f, -0.07356456667f, -0.09801714122f, -0.1224106774f, -0.1467304677f, -0.1709618866f, -0.1950903237f, -0.2191012353f, -0.2429801822f, -0.266712755f, -0.2902846634f, -0.3136817515f, -0.336889863f, -0.3598950505f, -0.3826834261f, -0.4052413106f, -0.4275550842f, -0.449611336f, -0.4713967443f, -0.492898196f, -0.514102757f, -0.534997642f, -0.5555702448f, -0.5758081675f, -0.5956993103f, -0.6152315736f, -0.6343932748f, -0.6531728506f, -0.6715589762f, -0.689540565f, -0.7071067691f, -0.724247098f, -0.7409511209f, -0.7572088242f, -0.7730104327f, -0.7883464098f, -0.8032075167f, -0.8175848126f, -0.8314695954f, -0.84485358f, -0.8577286005f, -0.8700869679f, -0.8819212914f, -0.893224299f, -0.903989315f, -0.9142097831f, -0.9238795042f, -0.932992816f, -0.9415440559f, -0.9495281577f, -0.9569403529f, -0.963776052f, -0.9700312614f, -0.975702107f, -0.9807852507f, -0.9852776527f, -0.9891765118f, -0.9924795628f, -0.9951847196f, -0.9972904325f, -0.9987954497f, -0.9996988177f, -0.0f, -0.02454122901f, -0.04906767607f, -0.07356456667f, -0.09801714122f, -0.1224106774f, -0.1467304677f, -0.1709618866f, -0.1950903237f, -0.2191012353f, -0.2429801822f, -0.266712755f, -0.2902846634f, -0.3136817515f, -0.336889863f, -0.3598950505f, -0.3826834261f, -0.4052413106f, -0.4275550842f, -0.449611336f, -0.4713967443f, -0.492898196f, -0.514102757f, -0.534997642f, -0.5555702448f, -0.5758081675f, -0.5956993103f, -0.6152315736f, -0.6343932748f, -0.6531728506f, -0.6715589762f, -0.689540565f, -0.7071067691f, -0.724247098f, -0.7409511209f, -0.7572088242f, -0.7730104327f, -0.7883464098f, -0.8032075167f, -0.8175848126f, -0.8314695954f, -0.84485358f, -0.8577286005f, -0.8700869679f, -0.8819212914f, -0.893224299f, -0.903989315f, -0.9142097831f, -0.9238795042f, -0.932992816f, -0.9415440559f, -0.9495281577f, -0.9569403529f, -0.963776052f, -0.9700312614f, -0.975702107f, -0.9807852507f, -0.9852776527f, -0.9891765118f, -0.9924795628f, -0.9951847196f, -0.9972904325f, -0.9987954497f, -0.9996988177f, -0.0f, -0.02454122901f, -0.04906767607f, -0.07356456667f, -0.09801714122f, -0.1224106774f, -0.1467304677f, -0.1709618866f, -0.1950903237f, -0.2191012353f, -0.2429801822f, -0.266712755f, -0.2902846634f, -0.3136817515f, -0.336889863f, -0.3598950505f, -0.3826834261f, -0.4052413106f, -0.4275550842f, -0.449611336f, -0.4713967443f, -0.492898196f, -0.514102757f, -0.534997642f, -0.5555702448f, -0.5758081675f, -0.5956993103f, -0.6152315736f, -0.6343932748f, -0.6531728506f, -0.6715589762f, -0.689540565f, -0.7071067691f, -0.724247098f, -0.7409511209f, -0.7572088242f, -0.7730104327f, -0.7883464098f, -0.8032075167f, -0.8175848126f, -0.8314695954f, -0.84485358f, -0.8577286005f, -0.8700869679f, -0.8819212914f, -0.893224299f, -0.903989315f, -0.9142097831f, -0.9238795042f, -0.932992816f, -0.9415440559f, -0.9495281577f, -0.9569403529f, -0.963776052f, -0.9700312614f, -0.975702107f, -0.9807852507f, -0.9852776527f, -0.9891765118f, -0.9924795628f, -0.9951847196f, -0.9972904325f, -0.9987954497f, -0.9996988177f, -0.0f, -0.02454122901f, -0.04906767607f, -0.07356456667f, -0.09801714122f, -0.1224106774f, -0.1467304677f, -0.1709618866f, -0.1950903237f, -0.2191012353f, -0.2429801822f, -0.266712755f, -0.2902846634f, -0.3136817515f, -0.336889863f, -0.3598950505f, -0.3826834261f, -0.4052413106f, -0.4275550842f, -0.449611336f, -0.4713967443f, -0.492898196f, -0.514102757f, -0.534997642f, -0.5555702448f, -0.5758081675f, -0.5956993103f, -0.6152315736f, -0.6343932748f, -0.6531728506f, -0.6715589762f, -0.689540565f, -0.7071067691f, -0.724247098f, -0.7409511209f, -0.7572088242f, -0.7730104327f, -0.7883464098f, -0.8032075167f, -0.8175848126f, -0.8314695954f, -0.84485358f, -0.8577286005f, -0.8700869679f, -0.8819212914f, -0.893224299f, -0.903989315f, -0.9142097831f, -0.9238795042f, -0.932992816f, -0.9415440559f, -0.9495281577f, -0.9569403529f, -0.963776052f, -0.9700312614f, -0.975702107f, -0.9807852507f, -0.9852776527f, -0.9891765118f, -0.9924795628f, -0.9951847196f, -0.9972904325f, -0.9987954497f, -0.9996988177f}; +constant float ts22[512] = {-0.0f, -0.07356456667f, -0.1467304677f, -0.2191012353f, -0.2902846634f, -0.3598950505f, -0.4275550842f, -0.492898196f, -0.5555702448f, -0.6152315736f, -0.6715589762f, -0.724247098f, -0.7730104327f, -0.8175848126f, -0.8577286005f, -0.893224299f, -0.9238795042f, -0.9495281577f, -0.9700312614f, -0.9852776527f, -0.9951847196f, -0.9996988177f, -0.9987954497f, -0.9924795628f, -0.9807852507f, -0.963776052f, -0.9415440559f, -0.9142097831f, -0.8819212914f, -0.84485358f, -0.8032075167f, -0.7572088242f, -0.7071067691f, -0.6531728506f, -0.5956993103f, -0.534997642f, -0.4713967443f, -0.4052413106f, -0.336889863f, -0.266712755f, -0.1950903237f, -0.1224106774f, -0.04906767607f, 0.02454122901f, 0.09801714122f, 0.1709618866f, 0.2429801822f, 0.3136817515f, 0.3826834261f, 0.449611336f, 0.514102757f, 0.5758081675f, 0.6343932748f, 0.689540565f, 0.7409511209f, 0.7883464098f, 0.8314695954f, 0.8700869679f, 0.903989315f, 0.932992816f, 0.9569403529f, 0.975702107f, 0.9891765118f, 0.9972904325f, -0.0f, -0.07356456667f, -0.1467304677f, -0.2191012353f, -0.2902846634f, -0.3598950505f, -0.4275550842f, -0.492898196f, -0.5555702448f, -0.6152315736f, -0.6715589762f, -0.724247098f, -0.7730104327f, -0.8175848126f, -0.8577286005f, -0.893224299f, -0.9238795042f, -0.9495281577f, -0.9700312614f, -0.9852776527f, -0.9951847196f, -0.9996988177f, -0.9987954497f, -0.9924795628f, -0.9807852507f, -0.963776052f, -0.9415440559f, -0.9142097831f, -0.8819212914f, -0.84485358f, -0.8032075167f, -0.7572088242f, -0.7071067691f, -0.6531728506f, -0.5956993103f, -0.534997642f, -0.4713967443f, -0.4052413106f, -0.336889863f, -0.266712755f, -0.1950903237f, -0.1224106774f, -0.04906767607f, 0.02454122901f, 0.09801714122f, 0.1709618866f, 0.2429801822f, 0.3136817515f, 0.3826834261f, 0.449611336f, 0.514102757f, 0.5758081675f, 0.6343932748f, 0.689540565f, 0.7409511209f, 0.7883464098f, 0.8314695954f, 0.8700869679f, 0.903989315f, 0.932992816f, 0.9569403529f, 0.975702107f, 0.9891765118f, 0.9972904325f, -0.0f, -0.07356456667f, -0.1467304677f, -0.2191012353f, -0.2902846634f, -0.3598950505f, -0.4275550842f, -0.492898196f, -0.5555702448f, -0.6152315736f, -0.6715589762f, -0.724247098f, -0.7730104327f, -0.8175848126f, -0.8577286005f, -0.893224299f, -0.9238795042f, -0.9495281577f, -0.9700312614f, -0.9852776527f, -0.9951847196f, -0.9996988177f, -0.9987954497f, -0.9924795628f, -0.9807852507f, -0.963776052f, -0.9415440559f, -0.9142097831f, -0.8819212914f, -0.84485358f, -0.8032075167f, -0.7572088242f, -0.7071067691f, -0.6531728506f, -0.5956993103f, -0.534997642f, -0.4713967443f, -0.4052413106f, -0.336889863f, -0.266712755f, -0.1950903237f, -0.1224106774f, -0.04906767607f, 0.02454122901f, 0.09801714122f, 0.1709618866f, 0.2429801822f, 0.3136817515f, 0.3826834261f, 0.449611336f, 0.514102757f, 0.5758081675f, 0.6343932748f, 0.689540565f, 0.7409511209f, 0.7883464098f, 0.8314695954f, 0.8700869679f, 0.903989315f, 0.932992816f, 0.9569403529f, 0.975702107f, 0.9891765118f, 0.9972904325f, -0.0f, -0.07356456667f, -0.1467304677f, -0.2191012353f, -0.2902846634f, -0.3598950505f, -0.4275550842f, -0.492898196f, -0.5555702448f, -0.6152315736f, -0.6715589762f, -0.724247098f, -0.7730104327f, -0.8175848126f, -0.8577286005f, -0.893224299f, -0.9238795042f, -0.9495281577f, -0.9700312614f, -0.9852776527f, -0.9951847196f, -0.9996988177f, -0.9987954497f, -0.9924795628f, -0.9807852507f, -0.963776052f, -0.9415440559f, -0.9142097831f, -0.8819212914f, -0.84485358f, -0.8032075167f, -0.7572088242f, -0.7071067691f, -0.6531728506f, -0.5956993103f, -0.534997642f, -0.4713967443f, -0.4052413106f, -0.336889863f, -0.266712755f, -0.1950903237f, -0.1224106774f, -0.04906767607f, 0.02454122901f, 0.09801714122f, 0.1709618866f, 0.2429801822f, 0.3136817515f, 0.3826834261f, 0.449611336f, 0.514102757f, 0.5758081675f, 0.6343932748f, 0.689540565f, 0.7409511209f, 0.7883464098f, 0.8314695954f, 0.8700869679f, 0.903989315f, 0.932992816f, 0.9569403529f, 0.975702107f, 0.9891765118f, 0.9972904325f, -0.0f, -0.07356456667f, -0.1467304677f, -0.2191012353f, -0.2902846634f, -0.3598950505f, -0.4275550842f, -0.492898196f, -0.5555702448f, -0.6152315736f, -0.6715589762f, -0.724247098f, -0.7730104327f, -0.8175848126f, -0.8577286005f, -0.893224299f, -0.9238795042f, -0.9495281577f, -0.9700312614f, -0.9852776527f, -0.9951847196f, -0.9996988177f, -0.9987954497f, -0.9924795628f, -0.9807852507f, -0.963776052f, -0.9415440559f, -0.9142097831f, -0.8819212914f, -0.84485358f, -0.8032075167f, -0.7572088242f, -0.7071067691f, -0.6531728506f, -0.5956993103f, -0.534997642f, -0.4713967443f, -0.4052413106f, -0.336889863f, -0.266712755f, -0.1950903237f, -0.1224106774f, -0.04906767607f, 0.02454122901f, 0.09801714122f, 0.1709618866f, 0.2429801822f, 0.3136817515f, 0.3826834261f, 0.449611336f, 0.514102757f, 0.5758081675f, 0.6343932748f, 0.689540565f, 0.7409511209f, 0.7883464098f, 0.8314695954f, 0.8700869679f, 0.903989315f, 0.932992816f, 0.9569403529f, 0.975702107f, 0.9891765118f, 0.9972904325f, -0.0f, -0.07356456667f, -0.1467304677f, -0.2191012353f, -0.2902846634f, -0.3598950505f, -0.4275550842f, -0.492898196f, -0.5555702448f, -0.6152315736f, -0.6715589762f, -0.724247098f, -0.7730104327f, -0.8175848126f, -0.8577286005f, -0.893224299f, -0.9238795042f, -0.9495281577f, -0.9700312614f, -0.9852776527f, -0.9951847196f, -0.9996988177f, -0.9987954497f, -0.9924795628f, -0.9807852507f, -0.963776052f, -0.9415440559f, -0.9142097831f, -0.8819212914f, -0.84485358f, -0.8032075167f, -0.7572088242f, -0.7071067691f, -0.6531728506f, -0.5956993103f, -0.534997642f, -0.4713967443f, -0.4052413106f, -0.336889863f, -0.266712755f, -0.1950903237f, -0.1224106774f, -0.04906767607f, 0.02454122901f, 0.09801714122f, 0.1709618866f, 0.2429801822f, 0.3136817515f, 0.3826834261f, 0.449611336f, 0.514102757f, 0.5758081675f, 0.6343932748f, 0.689540565f, 0.7409511209f, 0.7883464098f, 0.8314695954f, 0.8700869679f, 0.903989315f, 0.932992816f, 0.9569403529f, 0.975702107f, 0.9891765118f, 0.9972904325f, -0.0f, -0.07356456667f, -0.1467304677f, -0.2191012353f, -0.2902846634f, -0.3598950505f, -0.4275550842f, -0.492898196f, -0.5555702448f, -0.6152315736f, -0.6715589762f, -0.724247098f, -0.7730104327f, -0.8175848126f, -0.8577286005f, -0.893224299f, -0.9238795042f, -0.9495281577f, -0.9700312614f, -0.9852776527f, -0.9951847196f, -0.9996988177f, -0.9987954497f, -0.9924795628f, -0.9807852507f, -0.963776052f, -0.9415440559f, -0.9142097831f, -0.8819212914f, -0.84485358f, -0.8032075167f, -0.7572088242f, -0.7071067691f, -0.6531728506f, -0.5956993103f, -0.534997642f, -0.4713967443f, -0.4052413106f, -0.336889863f, -0.266712755f, -0.1950903237f, -0.1224106774f, -0.04906767607f, 0.02454122901f, 0.09801714122f, 0.1709618866f, 0.2429801822f, 0.3136817515f, 0.3826834261f, 0.449611336f, 0.514102757f, 0.5758081675f, 0.6343932748f, 0.689540565f, 0.7409511209f, 0.7883464098f, 0.8314695954f, 0.8700869679f, 0.903989315f, 0.932992816f, 0.9569403529f, 0.975702107f, 0.9891765118f, 0.9972904325f, -0.0f, -0.07356456667f, -0.1467304677f, -0.2191012353f, -0.2902846634f, -0.3598950505f, -0.4275550842f, -0.492898196f, -0.5555702448f, -0.6152315736f, -0.6715589762f, -0.724247098f, -0.7730104327f, -0.8175848126f, -0.8577286005f, -0.893224299f, -0.9238795042f, -0.9495281577f, -0.9700312614f, -0.9852776527f, -0.9951847196f, -0.9996988177f, -0.9987954497f, -0.9924795628f, -0.9807852507f, -0.963776052f, -0.9415440559f, -0.9142097831f, -0.8819212914f, -0.84485358f, -0.8032075167f, -0.7572088242f, -0.7071067691f, -0.6531728506f, -0.5956993103f, -0.534997642f, -0.4713967443f, -0.4052413106f, -0.336889863f, -0.266712755f, -0.1950903237f, -0.1224106774f, -0.04906767607f, 0.02454122901f, 0.09801714122f, 0.1709618866f, 0.2429801822f, 0.3136817515f, 0.3826834261f, 0.449611336f, 0.514102757f, 0.5758081675f, 0.6343932748f, 0.689540565f, 0.7409511209f, 0.7883464098f, 0.8314695954f, 0.8700869679f, 0.903989315f, 0.932992816f, 0.9569403529f, 0.975702107f, 0.9891765118f, 0.9972904325f}; +constant float ts25[512] = {-0.0f, -0.07356456667f, -0.1467304677f, -0.2191012353f, -0.2902846634f, -0.3598950505f, -0.4275550842f, -0.492898196f, -0.5555702448f, -0.6152315736f, -0.6715589762f, -0.724247098f, -0.7730104327f, -0.8175848126f, -0.8577286005f, -0.893224299f, -0.9238795042f, -0.9495281577f, -0.9700312614f, -0.9852776527f, -0.9951847196f, -0.9996988177f, -0.9987954497f, -0.9924795628f, -0.9807852507f, -0.963776052f, -0.9415440559f, -0.9142097831f, -0.8819212914f, -0.84485358f, -0.8032075167f, -0.7572088242f, -0.7071067691f, -0.6531728506f, -0.5956993103f, -0.534997642f, -0.4713967443f, -0.4052413106f, -0.336889863f, -0.266712755f, -0.1950903237f, -0.1224106774f, -0.04906767607f, 0.02454122901f, 0.09801714122f, 0.1709618866f, 0.2429801822f, 0.3136817515f, 0.3826834261f, 0.449611336f, 0.514102757f, 0.5758081675f, 0.6343932748f, 0.689540565f, 0.7409511209f, 0.7883464098f, 0.8314695954f, 0.8700869679f, 0.903989315f, 0.932992816f, 0.9569403529f, 0.975702107f, 0.9891765118f, 0.9972904325f, -0.0f, -0.07356456667f, -0.1467304677f, -0.2191012353f, -0.2902846634f, -0.3598950505f, -0.4275550842f, -0.492898196f, -0.5555702448f, -0.6152315736f, -0.6715589762f, -0.724247098f, -0.7730104327f, -0.8175848126f, -0.8577286005f, -0.893224299f, -0.9238795042f, -0.9495281577f, -0.9700312614f, -0.9852776527f, -0.9951847196f, -0.9996988177f, -0.9987954497f, -0.9924795628f, -0.9807852507f, -0.963776052f, -0.9415440559f, -0.9142097831f, -0.8819212914f, -0.84485358f, -0.8032075167f, -0.7572088242f, -0.7071067691f, -0.6531728506f, -0.5956993103f, -0.534997642f, -0.4713967443f, -0.4052413106f, -0.336889863f, -0.266712755f, -0.1950903237f, -0.1224106774f, -0.04906767607f, 0.02454122901f, 0.09801714122f, 0.1709618866f, 0.2429801822f, 0.3136817515f, 0.3826834261f, 0.449611336f, 0.514102757f, 0.5758081675f, 0.6343932748f, 0.689540565f, 0.7409511209f, 0.7883464098f, 0.8314695954f, 0.8700869679f, 0.903989315f, 0.932992816f, 0.9569403529f, 0.975702107f, 0.9891765118f, 0.9972904325f, -0.0f, -0.07356456667f, -0.1467304677f, -0.2191012353f, -0.2902846634f, -0.3598950505f, -0.4275550842f, -0.492898196f, -0.5555702448f, -0.6152315736f, -0.6715589762f, -0.724247098f, -0.7730104327f, -0.8175848126f, -0.8577286005f, -0.893224299f, -0.9238795042f, -0.9495281577f, -0.9700312614f, -0.9852776527f, -0.9951847196f, -0.9996988177f, -0.9987954497f, -0.9924795628f, -0.9807852507f, -0.963776052f, -0.9415440559f, -0.9142097831f, -0.8819212914f, -0.84485358f, -0.8032075167f, -0.7572088242f, -0.7071067691f, -0.6531728506f, -0.5956993103f, -0.534997642f, -0.4713967443f, -0.4052413106f, -0.336889863f, -0.266712755f, -0.1950903237f, -0.1224106774f, -0.04906767607f, 0.02454122901f, 0.09801714122f, 0.1709618866f, 0.2429801822f, 0.3136817515f, 0.3826834261f, 0.449611336f, 0.514102757f, 0.5758081675f, 0.6343932748f, 0.689540565f, 0.7409511209f, 0.7883464098f, 0.8314695954f, 0.8700869679f, 0.903989315f, 0.932992816f, 0.9569403529f, 0.975702107f, 0.9891765118f, 0.9972904325f, -0.0f, -0.07356456667f, -0.1467304677f, -0.2191012353f, -0.2902846634f, -0.3598950505f, -0.4275550842f, -0.492898196f, -0.5555702448f, -0.6152315736f, -0.6715589762f, -0.724247098f, -0.7730104327f, -0.8175848126f, -0.8577286005f, -0.893224299f, -0.9238795042f, -0.9495281577f, -0.9700312614f, -0.9852776527f, -0.9951847196f, -0.9996988177f, -0.9987954497f, -0.9924795628f, -0.9807852507f, -0.963776052f, -0.9415440559f, -0.9142097831f, -0.8819212914f, -0.84485358f, -0.8032075167f, -0.7572088242f, -0.7071067691f, -0.6531728506f, -0.5956993103f, -0.534997642f, -0.4713967443f, -0.4052413106f, -0.336889863f, -0.266712755f, -0.1950903237f, -0.1224106774f, -0.04906767607f, 0.02454122901f, 0.09801714122f, 0.1709618866f, 0.2429801822f, 0.3136817515f, 0.3826834261f, 0.449611336f, 0.514102757f, 0.5758081675f, 0.6343932748f, 0.689540565f, 0.7409511209f, 0.7883464098f, 0.8314695954f, 0.8700869679f, 0.903989315f, 0.932992816f, 0.9569403529f, 0.975702107f, 0.9891765118f, 0.9972904325f, -0.0f, -0.07356456667f, -0.1467304677f, -0.2191012353f, -0.2902846634f, -0.3598950505f, -0.4275550842f, -0.492898196f, -0.5555702448f, -0.6152315736f, -0.6715589762f, -0.724247098f, -0.7730104327f, -0.8175848126f, -0.8577286005f, -0.893224299f, -0.9238795042f, -0.9495281577f, -0.9700312614f, -0.9852776527f, -0.9951847196f, -0.9996988177f, -0.9987954497f, -0.9924795628f, -0.9807852507f, -0.963776052f, -0.9415440559f, -0.9142097831f, -0.8819212914f, -0.84485358f, -0.8032075167f, -0.7572088242f, -0.7071067691f, -0.6531728506f, -0.5956993103f, -0.534997642f, -0.4713967443f, -0.4052413106f, -0.336889863f, -0.266712755f, -0.1950903237f, -0.1224106774f, -0.04906767607f, 0.02454122901f, 0.09801714122f, 0.1709618866f, 0.2429801822f, 0.3136817515f, 0.3826834261f, 0.449611336f, 0.514102757f, 0.5758081675f, 0.6343932748f, 0.689540565f, 0.7409511209f, 0.7883464098f, 0.8314695954f, 0.8700869679f, 0.903989315f, 0.932992816f, 0.9569403529f, 0.975702107f, 0.9891765118f, 0.9972904325f, -0.0f, -0.07356456667f, -0.1467304677f, -0.2191012353f, -0.2902846634f, -0.3598950505f, -0.4275550842f, -0.492898196f, -0.5555702448f, -0.6152315736f, -0.6715589762f, -0.724247098f, -0.7730104327f, -0.8175848126f, -0.8577286005f, -0.893224299f, -0.9238795042f, -0.9495281577f, -0.9700312614f, -0.9852776527f, -0.9951847196f, -0.9996988177f, -0.9987954497f, -0.9924795628f, -0.9807852507f, -0.963776052f, -0.9415440559f, -0.9142097831f, -0.8819212914f, -0.84485358f, -0.8032075167f, -0.7572088242f, -0.7071067691f, -0.6531728506f, -0.5956993103f, -0.534997642f, -0.4713967443f, -0.4052413106f, -0.336889863f, -0.266712755f, -0.1950903237f, -0.1224106774f, -0.04906767607f, 0.02454122901f, 0.09801714122f, 0.1709618866f, 0.2429801822f, 0.3136817515f, 0.3826834261f, 0.449611336f, 0.514102757f, 0.5758081675f, 0.6343932748f, 0.689540565f, 0.7409511209f, 0.7883464098f, 0.8314695954f, 0.8700869679f, 0.903989315f, 0.932992816f, 0.9569403529f, 0.975702107f, 0.9891765118f, 0.9972904325f, -0.0f, -0.07356456667f, -0.1467304677f, -0.2191012353f, -0.2902846634f, -0.3598950505f, -0.4275550842f, -0.492898196f, -0.5555702448f, -0.6152315736f, -0.6715589762f, -0.724247098f, -0.7730104327f, -0.8175848126f, -0.8577286005f, -0.893224299f, -0.9238795042f, -0.9495281577f, -0.9700312614f, -0.9852776527f, -0.9951847196f, -0.9996988177f, -0.9987954497f, -0.9924795628f, -0.9807852507f, -0.963776052f, -0.9415440559f, -0.9142097831f, -0.8819212914f, -0.84485358f, -0.8032075167f, -0.7572088242f, -0.7071067691f, -0.6531728506f, -0.5956993103f, -0.534997642f, -0.4713967443f, -0.4052413106f, -0.336889863f, -0.266712755f, -0.1950903237f, -0.1224106774f, -0.04906767607f, 0.02454122901f, 0.09801714122f, 0.1709618866f, 0.2429801822f, 0.3136817515f, 0.3826834261f, 0.449611336f, 0.514102757f, 0.5758081675f, 0.6343932748f, 0.689540565f, 0.7409511209f, 0.7883464098f, 0.8314695954f, 0.8700869679f, 0.903989315f, 0.932992816f, 0.9569403529f, 0.975702107f, 0.9891765118f, 0.9972904325f, -0.0f, -0.07356456667f, -0.1467304677f, -0.2191012353f, -0.2902846634f, -0.3598950505f, -0.4275550842f, -0.492898196f, -0.5555702448f, -0.6152315736f, -0.6715589762f, -0.724247098f, -0.7730104327f, -0.8175848126f, -0.8577286005f, -0.893224299f, -0.9238795042f, -0.9495281577f, -0.9700312614f, -0.9852776527f, -0.9951847196f, -0.9996988177f, -0.9987954497f, -0.9924795628f, -0.9807852507f, -0.963776052f, -0.9415440559f, -0.9142097831f, -0.8819212914f, -0.84485358f, -0.8032075167f, -0.7572088242f, -0.7071067691f, -0.6531728506f, -0.5956993103f, -0.534997642f, -0.4713967443f, -0.4052413106f, -0.336889863f, -0.266712755f, -0.1950903237f, -0.1224106774f, -0.04906767607f, 0.02454122901f, 0.09801714122f, 0.1709618866f, 0.2429801822f, 0.3136817515f, 0.3826834261f, 0.449611336f, 0.514102757f, 0.5758081675f, 0.6343932748f, 0.689540565f, 0.7409511209f, 0.7883464098f, 0.8314695954f, 0.8700869679f, 0.903989315f, 0.932992816f, 0.9569403529f, 0.975702107f, 0.9891765118f, 0.9972904325f}; +constant float ts30[512] = {-0.0f, -0.1950903237f, -0.3826834261f, -0.5555702448f, -0.7071067691f, -0.8314695954f, -0.9238795042f, -0.9807852507f, -1.0f, -0.9807852507f, -0.9238795042f, -0.8314695954f, -0.7071067691f, -0.5555702448f, -0.3826834261f, -0.1950903237f, -0.0f, -0.1950903237f, -0.3826834261f, -0.5555702448f, -0.7071067691f, -0.8314695954f, -0.9238795042f, -0.9807852507f, -1.0f, -0.9807852507f, -0.9238795042f, -0.8314695954f, -0.7071067691f, -0.5555702448f, -0.3826834261f, -0.1950903237f, -0.0f, -0.1950903237f, -0.3826834261f, -0.5555702448f, -0.7071067691f, -0.8314695954f, -0.9238795042f, -0.9807852507f, -1.0f, -0.9807852507f, -0.9238795042f, -0.8314695954f, -0.7071067691f, -0.5555702448f, -0.3826834261f, -0.1950903237f, -0.0f, -0.1950903237f, -0.3826834261f, -0.5555702448f, -0.7071067691f, -0.8314695954f, -0.9238795042f, -0.9807852507f, -1.0f, -0.9807852507f, -0.9238795042f, -0.8314695954f, -0.7071067691f, -0.5555702448f, -0.3826834261f, -0.1950903237f, -0.0f, -0.1950903237f, -0.3826834261f, -0.5555702448f, -0.7071067691f, -0.8314695954f, -0.9238795042f, -0.9807852507f, -1.0f, -0.9807852507f, -0.9238795042f, -0.8314695954f, -0.7071067691f, -0.5555702448f, -0.3826834261f, -0.1950903237f, -0.0f, -0.1950903237f, -0.3826834261f, -0.5555702448f, -0.7071067691f, -0.8314695954f, -0.9238795042f, -0.9807852507f, -1.0f, -0.9807852507f, -0.9238795042f, -0.8314695954f, -0.7071067691f, -0.5555702448f, -0.3826834261f, -0.1950903237f, -0.0f, -0.1950903237f, -0.3826834261f, -0.5555702448f, -0.7071067691f, -0.8314695954f, -0.9238795042f, -0.9807852507f, -1.0f, -0.9807852507f, -0.9238795042f, -0.8314695954f, -0.7071067691f, -0.5555702448f, -0.3826834261f, -0.1950903237f, -0.0f, -0.1950903237f, -0.3826834261f, -0.5555702448f, -0.7071067691f, -0.8314695954f, -0.9238795042f, -0.9807852507f, -1.0f, -0.9807852507f, -0.9238795042f, -0.8314695954f, -0.7071067691f, -0.5555702448f, -0.3826834261f, -0.1950903237f, -0.0f, -0.1950903237f, -0.3826834261f, -0.5555702448f, -0.7071067691f, -0.8314695954f, -0.9238795042f, -0.9807852507f, -1.0f, -0.9807852507f, -0.9238795042f, -0.8314695954f, -0.7071067691f, -0.5555702448f, -0.3826834261f, -0.1950903237f, -0.0f, -0.1950903237f, -0.3826834261f, -0.5555702448f, -0.7071067691f, -0.8314695954f, -0.9238795042f, -0.9807852507f, -1.0f, -0.9807852507f, -0.9238795042f, -0.8314695954f, -0.7071067691f, -0.5555702448f, -0.3826834261f, -0.1950903237f, -0.0f, -0.1950903237f, -0.3826834261f, -0.5555702448f, -0.7071067691f, -0.8314695954f, -0.9238795042f, -0.9807852507f, -1.0f, -0.9807852507f, -0.9238795042f, -0.8314695954f, -0.7071067691f, -0.5555702448f, -0.3826834261f, -0.1950903237f, -0.0f, -0.1950903237f, -0.3826834261f, -0.5555702448f, -0.7071067691f, -0.8314695954f, -0.9238795042f, -0.9807852507f, -1.0f, -0.9807852507f, -0.9238795042f, -0.8314695954f, -0.7071067691f, -0.5555702448f, -0.3826834261f, -0.1950903237f, -0.0f, -0.1950903237f, -0.3826834261f, -0.5555702448f, -0.7071067691f, -0.8314695954f, -0.9238795042f, -0.9807852507f, -1.0f, -0.9807852507f, -0.9238795042f, -0.8314695954f, -0.7071067691f, -0.5555702448f, -0.3826834261f, -0.1950903237f, -0.0f, -0.1950903237f, -0.3826834261f, -0.5555702448f, -0.7071067691f, -0.8314695954f, -0.9238795042f, -0.9807852507f, -1.0f, -0.9807852507f, -0.9238795042f, -0.8314695954f, -0.7071067691f, -0.5555702448f, -0.3826834261f, -0.1950903237f, -0.0f, -0.1950903237f, -0.3826834261f, -0.5555702448f, -0.7071067691f, -0.8314695954f, -0.9238795042f, -0.9807852507f, -1.0f, -0.9807852507f, -0.9238795042f, -0.8314695954f, -0.7071067691f, -0.5555702448f, -0.3826834261f, -0.1950903237f, -0.0f, -0.1950903237f, -0.3826834261f, -0.5555702448f, -0.7071067691f, -0.8314695954f, -0.9238795042f, -0.9807852507f, -1.0f, -0.9807852507f, -0.9238795042f, -0.8314695954f, -0.7071067691f, -0.5555702448f, -0.3826834261f, -0.1950903237f, -0.0f, -0.1950903237f, -0.3826834261f, -0.5555702448f, -0.7071067691f, -0.8314695954f, -0.9238795042f, -0.9807852507f, -1.0f, -0.9807852507f, -0.9238795042f, -0.8314695954f, -0.7071067691f, -0.5555702448f, -0.3826834261f, -0.1950903237f, -0.0f, -0.1950903237f, -0.3826834261f, -0.5555702448f, -0.7071067691f, -0.8314695954f, -0.9238795042f, -0.9807852507f, -1.0f, -0.9807852507f, -0.9238795042f, -0.8314695954f, -0.7071067691f, -0.5555702448f, -0.3826834261f, -0.1950903237f, -0.0f, -0.1950903237f, -0.3826834261f, -0.5555702448f, -0.7071067691f, -0.8314695954f, -0.9238795042f, -0.9807852507f, -1.0f, -0.9807852507f, -0.9238795042f, -0.8314695954f, -0.7071067691f, -0.5555702448f, -0.3826834261f, -0.1950903237f, -0.0f, -0.1950903237f, -0.3826834261f, -0.5555702448f, -0.7071067691f, -0.8314695954f, -0.9238795042f, -0.9807852507f, -1.0f, -0.9807852507f, -0.9238795042f, -0.8314695954f, -0.7071067691f, -0.5555702448f, -0.3826834261f, -0.1950903237f, -0.0f, -0.1950903237f, -0.3826834261f, -0.5555702448f, -0.7071067691f, -0.8314695954f, -0.9238795042f, -0.9807852507f, -1.0f, -0.9807852507f, -0.9238795042f, -0.8314695954f, -0.7071067691f, -0.5555702448f, -0.3826834261f, -0.1950903237f, -0.0f, -0.1950903237f, -0.3826834261f, -0.5555702448f, -0.7071067691f, -0.8314695954f, -0.9238795042f, -0.9807852507f, -1.0f, -0.9807852507f, -0.9238795042f, -0.8314695954f, -0.7071067691f, -0.5555702448f, -0.3826834261f, -0.1950903237f, -0.0f, -0.1950903237f, -0.3826834261f, -0.5555702448f, -0.7071067691f, -0.8314695954f, -0.9238795042f, -0.9807852507f, -1.0f, -0.9807852507f, -0.9238795042f, -0.8314695954f, -0.7071067691f, -0.5555702448f, -0.3826834261f, -0.1950903237f, -0.0f, -0.1950903237f, -0.3826834261f, -0.5555702448f, -0.7071067691f, -0.8314695954f, -0.9238795042f, -0.9807852507f, -1.0f, -0.9807852507f, -0.9238795042f, -0.8314695954f, -0.7071067691f, -0.5555702448f, -0.3826834261f, -0.1950903237f, -0.0f, -0.1950903237f, -0.3826834261f, -0.5555702448f, -0.7071067691f, -0.8314695954f, -0.9238795042f, -0.9807852507f, -1.0f, -0.9807852507f, -0.9238795042f, -0.8314695954f, -0.7071067691f, -0.5555702448f, -0.3826834261f, -0.1950903237f, -0.0f, -0.1950903237f, -0.3826834261f, -0.5555702448f, -0.7071067691f, -0.8314695954f, -0.9238795042f, -0.9807852507f, -1.0f, -0.9807852507f, -0.9238795042f, -0.8314695954f, -0.7071067691f, -0.5555702448f, -0.3826834261f, -0.1950903237f, -0.0f, -0.1950903237f, -0.3826834261f, -0.5555702448f, -0.7071067691f, -0.8314695954f, -0.9238795042f, -0.9807852507f, -1.0f, -0.9807852507f, -0.9238795042f, -0.8314695954f, -0.7071067691f, -0.5555702448f, -0.3826834261f, -0.1950903237f, -0.0f, -0.1950903237f, -0.3826834261f, -0.5555702448f, -0.7071067691f, -0.8314695954f, -0.9238795042f, -0.9807852507f, -1.0f, -0.9807852507f, -0.9238795042f, -0.8314695954f, -0.7071067691f, -0.5555702448f, -0.3826834261f, -0.1950903237f, -0.0f, -0.1950903237f, -0.3826834261f, -0.5555702448f, -0.7071067691f, -0.8314695954f, -0.9238795042f, -0.9807852507f, -1.0f, -0.9807852507f, -0.9238795042f, -0.8314695954f, -0.7071067691f, -0.5555702448f, -0.3826834261f, -0.1950903237f, -0.0f, -0.1950903237f, -0.3826834261f, -0.5555702448f, -0.7071067691f, -0.8314695954f, -0.9238795042f, -0.9807852507f, -1.0f, -0.9807852507f, -0.9238795042f, -0.8314695954f, -0.7071067691f, -0.5555702448f, -0.3826834261f, -0.1950903237f, -0.0f, -0.1950903237f, -0.3826834261f, -0.5555702448f, -0.7071067691f, -0.8314695954f, -0.9238795042f, -0.9807852507f, -1.0f, -0.9807852507f, -0.9238795042f, -0.8314695954f, -0.7071067691f, -0.5555702448f, -0.3826834261f, -0.1950903237f, -0.0f, -0.1950903237f, -0.3826834261f, -0.5555702448f, -0.7071067691f, -0.8314695954f, -0.9238795042f, -0.9807852507f, -1.0f, -0.9807852507f, -0.9238795042f, -0.8314695954f, -0.7071067691f, -0.5555702448f, -0.3826834261f, -0.1950903237f}; +constant float ts33[512] = {-0.0f, -0.1950903237f, -0.3826834261f, -0.5555702448f, -0.7071067691f, -0.8314695954f, -0.9238795042f, -0.9807852507f, -1.0f, -0.9807852507f, -0.9238795042f, -0.8314695954f, -0.7071067691f, -0.5555702448f, -0.3826834261f, -0.1950903237f, -0.0f, -0.1950903237f, -0.3826834261f, -0.5555702448f, -0.7071067691f, -0.8314695954f, -0.9238795042f, -0.9807852507f, -1.0f, -0.9807852507f, -0.9238795042f, -0.8314695954f, -0.7071067691f, -0.5555702448f, -0.3826834261f, -0.1950903237f, -0.0f, -0.1950903237f, -0.3826834261f, -0.5555702448f, -0.7071067691f, -0.8314695954f, -0.9238795042f, -0.9807852507f, -1.0f, -0.9807852507f, -0.9238795042f, -0.8314695954f, -0.7071067691f, -0.5555702448f, -0.3826834261f, -0.1950903237f, -0.0f, -0.1950903237f, -0.3826834261f, -0.5555702448f, -0.7071067691f, -0.8314695954f, -0.9238795042f, -0.9807852507f, -1.0f, -0.9807852507f, -0.9238795042f, -0.8314695954f, -0.7071067691f, -0.5555702448f, -0.3826834261f, -0.1950903237f, -0.0f, -0.1950903237f, -0.3826834261f, -0.5555702448f, -0.7071067691f, -0.8314695954f, -0.9238795042f, -0.9807852507f, -1.0f, -0.9807852507f, -0.9238795042f, -0.8314695954f, -0.7071067691f, -0.5555702448f, -0.3826834261f, -0.1950903237f, -0.0f, -0.1950903237f, -0.3826834261f, -0.5555702448f, -0.7071067691f, -0.8314695954f, -0.9238795042f, -0.9807852507f, -1.0f, -0.9807852507f, -0.9238795042f, -0.8314695954f, -0.7071067691f, -0.5555702448f, -0.3826834261f, -0.1950903237f, -0.0f, -0.1950903237f, -0.3826834261f, -0.5555702448f, -0.7071067691f, -0.8314695954f, -0.9238795042f, -0.9807852507f, -1.0f, -0.9807852507f, -0.9238795042f, -0.8314695954f, -0.7071067691f, -0.5555702448f, -0.3826834261f, -0.1950903237f, -0.0f, -0.1950903237f, -0.3826834261f, -0.5555702448f, -0.7071067691f, -0.8314695954f, -0.9238795042f, -0.9807852507f, -1.0f, -0.9807852507f, -0.9238795042f, -0.8314695954f, -0.7071067691f, -0.5555702448f, -0.3826834261f, -0.1950903237f, -0.0f, -0.1950903237f, -0.3826834261f, -0.5555702448f, -0.7071067691f, -0.8314695954f, -0.9238795042f, -0.9807852507f, -1.0f, -0.9807852507f, -0.9238795042f, -0.8314695954f, -0.7071067691f, -0.5555702448f, -0.3826834261f, -0.1950903237f, -0.0f, -0.1950903237f, -0.3826834261f, -0.5555702448f, -0.7071067691f, -0.8314695954f, -0.9238795042f, -0.9807852507f, -1.0f, -0.9807852507f, -0.9238795042f, -0.8314695954f, -0.7071067691f, -0.5555702448f, -0.3826834261f, -0.1950903237f, -0.0f, -0.1950903237f, -0.3826834261f, -0.5555702448f, -0.7071067691f, -0.8314695954f, -0.9238795042f, -0.9807852507f, -1.0f, -0.9807852507f, -0.9238795042f, -0.8314695954f, -0.7071067691f, -0.5555702448f, -0.3826834261f, -0.1950903237f, -0.0f, -0.1950903237f, -0.3826834261f, -0.5555702448f, -0.7071067691f, -0.8314695954f, -0.9238795042f, -0.9807852507f, -1.0f, -0.9807852507f, -0.9238795042f, -0.8314695954f, -0.7071067691f, -0.5555702448f, -0.3826834261f, -0.1950903237f, -0.0f, -0.1950903237f, -0.3826834261f, -0.5555702448f, -0.7071067691f, -0.8314695954f, -0.9238795042f, -0.9807852507f, -1.0f, -0.9807852507f, -0.9238795042f, -0.8314695954f, -0.7071067691f, -0.5555702448f, -0.3826834261f, -0.1950903237f, -0.0f, -0.1950903237f, -0.3826834261f, -0.5555702448f, -0.7071067691f, -0.8314695954f, -0.9238795042f, -0.9807852507f, -1.0f, -0.9807852507f, -0.9238795042f, -0.8314695954f, -0.7071067691f, -0.5555702448f, -0.3826834261f, -0.1950903237f, -0.0f, -0.1950903237f, -0.3826834261f, -0.5555702448f, -0.7071067691f, -0.8314695954f, -0.9238795042f, -0.9807852507f, -1.0f, -0.9807852507f, -0.9238795042f, -0.8314695954f, -0.7071067691f, -0.5555702448f, -0.3826834261f, -0.1950903237f, -0.0f, -0.1950903237f, -0.3826834261f, -0.5555702448f, -0.7071067691f, -0.8314695954f, -0.9238795042f, -0.9807852507f, -1.0f, -0.9807852507f, -0.9238795042f, -0.8314695954f, -0.7071067691f, -0.5555702448f, -0.3826834261f, -0.1950903237f, -0.0f, -0.1950903237f, -0.3826834261f, -0.5555702448f, -0.7071067691f, -0.8314695954f, -0.9238795042f, -0.9807852507f, -1.0f, -0.9807852507f, -0.9238795042f, -0.8314695954f, -0.7071067691f, -0.5555702448f, -0.3826834261f, -0.1950903237f, -0.0f, -0.1950903237f, -0.3826834261f, -0.5555702448f, -0.7071067691f, -0.8314695954f, -0.9238795042f, -0.9807852507f, -1.0f, -0.9807852507f, -0.9238795042f, -0.8314695954f, -0.7071067691f, -0.5555702448f, -0.3826834261f, -0.1950903237f, -0.0f, -0.1950903237f, -0.3826834261f, -0.5555702448f, -0.7071067691f, -0.8314695954f, -0.9238795042f, -0.9807852507f, -1.0f, -0.9807852507f, -0.9238795042f, -0.8314695954f, -0.7071067691f, -0.5555702448f, -0.3826834261f, -0.1950903237f, -0.0f, -0.1950903237f, -0.3826834261f, -0.5555702448f, -0.7071067691f, -0.8314695954f, -0.9238795042f, -0.9807852507f, -1.0f, -0.9807852507f, -0.9238795042f, -0.8314695954f, -0.7071067691f, -0.5555702448f, -0.3826834261f, -0.1950903237f, -0.0f, -0.1950903237f, -0.3826834261f, -0.5555702448f, -0.7071067691f, -0.8314695954f, -0.9238795042f, -0.9807852507f, -1.0f, -0.9807852507f, -0.9238795042f, -0.8314695954f, -0.7071067691f, -0.5555702448f, -0.3826834261f, -0.1950903237f, -0.0f, -0.1950903237f, -0.3826834261f, -0.5555702448f, -0.7071067691f, -0.8314695954f, -0.9238795042f, -0.9807852507f, -1.0f, -0.9807852507f, -0.9238795042f, -0.8314695954f, -0.7071067691f, -0.5555702448f, -0.3826834261f, -0.1950903237f, -0.0f, -0.1950903237f, -0.3826834261f, -0.5555702448f, -0.7071067691f, -0.8314695954f, -0.9238795042f, -0.9807852507f, -1.0f, -0.9807852507f, -0.9238795042f, -0.8314695954f, -0.7071067691f, -0.5555702448f, -0.3826834261f, -0.1950903237f, -0.0f, -0.1950903237f, -0.3826834261f, -0.5555702448f, -0.7071067691f, -0.8314695954f, -0.9238795042f, -0.9807852507f, -1.0f, -0.9807852507f, -0.9238795042f, -0.8314695954f, -0.7071067691f, -0.5555702448f, -0.3826834261f, -0.1950903237f, -0.0f, -0.1950903237f, -0.3826834261f, -0.5555702448f, -0.7071067691f, -0.8314695954f, -0.9238795042f, -0.9807852507f, -1.0f, -0.9807852507f, -0.9238795042f, -0.8314695954f, -0.7071067691f, -0.5555702448f, -0.3826834261f, -0.1950903237f, -0.0f, -0.1950903237f, -0.3826834261f, -0.5555702448f, -0.7071067691f, -0.8314695954f, -0.9238795042f, -0.9807852507f, -1.0f, -0.9807852507f, -0.9238795042f, -0.8314695954f, -0.7071067691f, -0.5555702448f, -0.3826834261f, -0.1950903237f, -0.0f, -0.1950903237f, -0.3826834261f, -0.5555702448f, -0.7071067691f, -0.8314695954f, -0.9238795042f, -0.9807852507f, -1.0f, -0.9807852507f, -0.9238795042f, -0.8314695954f, -0.7071067691f, -0.5555702448f, -0.3826834261f, -0.1950903237f, -0.0f, -0.1950903237f, -0.3826834261f, -0.5555702448f, -0.7071067691f, -0.8314695954f, -0.9238795042f, -0.9807852507f, -1.0f, -0.9807852507f, -0.9238795042f, -0.8314695954f, -0.7071067691f, -0.5555702448f, -0.3826834261f, -0.1950903237f, -0.0f, -0.1950903237f, -0.3826834261f, -0.5555702448f, -0.7071067691f, -0.8314695954f, -0.9238795042f, -0.9807852507f, -1.0f, -0.9807852507f, -0.9238795042f, -0.8314695954f, -0.7071067691f, -0.5555702448f, -0.3826834261f, -0.1950903237f, -0.0f, -0.1950903237f, -0.3826834261f, -0.5555702448f, -0.7071067691f, -0.8314695954f, -0.9238795042f, -0.9807852507f, -1.0f, -0.9807852507f, -0.9238795042f, -0.8314695954f, -0.7071067691f, -0.5555702448f, -0.3826834261f, -0.1950903237f, -0.0f, -0.1950903237f, -0.3826834261f, -0.5555702448f, -0.7071067691f, -0.8314695954f, -0.9238795042f, -0.9807852507f, -1.0f, -0.9807852507f, -0.9238795042f, -0.8314695954f, -0.7071067691f, -0.5555702448f, -0.3826834261f, -0.1950903237f, -0.0f, -0.1950903237f, -0.3826834261f, -0.5555702448f, -0.7071067691f, -0.8314695954f, -0.9238795042f, -0.9807852507f, -1.0f, -0.9807852507f, -0.9238795042f, -0.8314695954f, -0.7071067691f, -0.5555702448f, -0.3826834261f, -0.1950903237f}; +constant float ts31[512] = {-0.0f, -0.09801714122f, -0.1950903237f, -0.2902846634f, -0.3826834261f, -0.4713967443f, -0.5555702448f, -0.6343932748f, -0.7071067691f, -0.7730104327f, -0.8314695954f, -0.8819212914f, -0.9238795042f, -0.9569403529f, -0.9807852507f, -0.9951847196f, -0.0f, -0.09801714122f, -0.1950903237f, -0.2902846634f, -0.3826834261f, -0.4713967443f, -0.5555702448f, -0.6343932748f, -0.7071067691f, -0.7730104327f, -0.8314695954f, -0.8819212914f, -0.9238795042f, -0.9569403529f, -0.9807852507f, -0.9951847196f, -0.0f, -0.09801714122f, -0.1950903237f, -0.2902846634f, -0.3826834261f, -0.4713967443f, -0.5555702448f, -0.6343932748f, -0.7071067691f, -0.7730104327f, -0.8314695954f, -0.8819212914f, -0.9238795042f, -0.9569403529f, -0.9807852507f, -0.9951847196f, -0.0f, -0.09801714122f, -0.1950903237f, -0.2902846634f, -0.3826834261f, -0.4713967443f, -0.5555702448f, -0.6343932748f, -0.7071067691f, -0.7730104327f, -0.8314695954f, -0.8819212914f, -0.9238795042f, -0.9569403529f, -0.9807852507f, -0.9951847196f, -0.0f, -0.09801714122f, -0.1950903237f, -0.2902846634f, -0.3826834261f, -0.4713967443f, -0.5555702448f, -0.6343932748f, -0.7071067691f, -0.7730104327f, -0.8314695954f, -0.8819212914f, -0.9238795042f, -0.9569403529f, -0.9807852507f, -0.9951847196f, -0.0f, -0.09801714122f, -0.1950903237f, -0.2902846634f, -0.3826834261f, -0.4713967443f, -0.5555702448f, -0.6343932748f, -0.7071067691f, -0.7730104327f, -0.8314695954f, -0.8819212914f, -0.9238795042f, -0.9569403529f, -0.9807852507f, -0.9951847196f, -0.0f, -0.09801714122f, -0.1950903237f, -0.2902846634f, -0.3826834261f, -0.4713967443f, -0.5555702448f, -0.6343932748f, -0.7071067691f, -0.7730104327f, -0.8314695954f, -0.8819212914f, -0.9238795042f, -0.9569403529f, -0.9807852507f, -0.9951847196f, -0.0f, -0.09801714122f, -0.1950903237f, -0.2902846634f, -0.3826834261f, -0.4713967443f, -0.5555702448f, -0.6343932748f, -0.7071067691f, -0.7730104327f, -0.8314695954f, -0.8819212914f, -0.9238795042f, -0.9569403529f, -0.9807852507f, -0.9951847196f, -0.0f, -0.09801714122f, -0.1950903237f, -0.2902846634f, -0.3826834261f, -0.4713967443f, -0.5555702448f, -0.6343932748f, -0.7071067691f, -0.7730104327f, -0.8314695954f, -0.8819212914f, -0.9238795042f, -0.9569403529f, -0.9807852507f, -0.9951847196f, -0.0f, -0.09801714122f, -0.1950903237f, -0.2902846634f, -0.3826834261f, -0.4713967443f, -0.5555702448f, -0.6343932748f, -0.7071067691f, -0.7730104327f, -0.8314695954f, -0.8819212914f, -0.9238795042f, -0.9569403529f, -0.9807852507f, -0.9951847196f, -0.0f, -0.09801714122f, -0.1950903237f, -0.2902846634f, -0.3826834261f, -0.4713967443f, -0.5555702448f, -0.6343932748f, -0.7071067691f, -0.7730104327f, -0.8314695954f, -0.8819212914f, -0.9238795042f, -0.9569403529f, -0.9807852507f, -0.9951847196f, -0.0f, -0.09801714122f, -0.1950903237f, -0.2902846634f, -0.3826834261f, -0.4713967443f, -0.5555702448f, -0.6343932748f, -0.7071067691f, -0.7730104327f, -0.8314695954f, -0.8819212914f, -0.9238795042f, -0.9569403529f, -0.9807852507f, -0.9951847196f, -0.0f, -0.09801714122f, -0.1950903237f, -0.2902846634f, -0.3826834261f, -0.4713967443f, -0.5555702448f, -0.6343932748f, -0.7071067691f, -0.7730104327f, -0.8314695954f, -0.8819212914f, -0.9238795042f, -0.9569403529f, -0.9807852507f, -0.9951847196f, -0.0f, -0.09801714122f, -0.1950903237f, -0.2902846634f, -0.3826834261f, -0.4713967443f, -0.5555702448f, -0.6343932748f, -0.7071067691f, -0.7730104327f, -0.8314695954f, -0.8819212914f, -0.9238795042f, -0.9569403529f, -0.9807852507f, -0.9951847196f, -0.0f, -0.09801714122f, -0.1950903237f, -0.2902846634f, -0.3826834261f, -0.4713967443f, -0.5555702448f, -0.6343932748f, -0.7071067691f, -0.7730104327f, -0.8314695954f, -0.8819212914f, -0.9238795042f, -0.9569403529f, -0.9807852507f, -0.9951847196f, -0.0f, -0.09801714122f, -0.1950903237f, -0.2902846634f, -0.3826834261f, -0.4713967443f, -0.5555702448f, -0.6343932748f, -0.7071067691f, -0.7730104327f, -0.8314695954f, -0.8819212914f, -0.9238795042f, -0.9569403529f, -0.9807852507f, -0.9951847196f, -0.0f, -0.09801714122f, -0.1950903237f, -0.2902846634f, -0.3826834261f, -0.4713967443f, -0.5555702448f, -0.6343932748f, -0.7071067691f, -0.7730104327f, -0.8314695954f, -0.8819212914f, -0.9238795042f, -0.9569403529f, -0.9807852507f, -0.9951847196f, -0.0f, -0.09801714122f, -0.1950903237f, -0.2902846634f, -0.3826834261f, -0.4713967443f, -0.5555702448f, -0.6343932748f, -0.7071067691f, -0.7730104327f, -0.8314695954f, -0.8819212914f, -0.9238795042f, -0.9569403529f, -0.9807852507f, -0.9951847196f, -0.0f, -0.09801714122f, -0.1950903237f, -0.2902846634f, -0.3826834261f, -0.4713967443f, -0.5555702448f, -0.6343932748f, -0.7071067691f, -0.7730104327f, -0.8314695954f, -0.8819212914f, -0.9238795042f, -0.9569403529f, -0.9807852507f, -0.9951847196f, -0.0f, -0.09801714122f, -0.1950903237f, -0.2902846634f, -0.3826834261f, -0.4713967443f, -0.5555702448f, -0.6343932748f, -0.7071067691f, -0.7730104327f, -0.8314695954f, -0.8819212914f, -0.9238795042f, -0.9569403529f, -0.9807852507f, -0.9951847196f, -0.0f, -0.09801714122f, -0.1950903237f, -0.2902846634f, -0.3826834261f, -0.4713967443f, -0.5555702448f, -0.6343932748f, -0.7071067691f, -0.7730104327f, -0.8314695954f, -0.8819212914f, -0.9238795042f, -0.9569403529f, -0.9807852507f, -0.9951847196f, -0.0f, -0.09801714122f, -0.1950903237f, -0.2902846634f, -0.3826834261f, -0.4713967443f, -0.5555702448f, -0.6343932748f, -0.7071067691f, -0.7730104327f, -0.8314695954f, -0.8819212914f, -0.9238795042f, -0.9569403529f, -0.9807852507f, -0.9951847196f, -0.0f, -0.09801714122f, -0.1950903237f, -0.2902846634f, -0.3826834261f, -0.4713967443f, -0.5555702448f, -0.6343932748f, -0.7071067691f, -0.7730104327f, -0.8314695954f, -0.8819212914f, -0.9238795042f, -0.9569403529f, -0.9807852507f, -0.9951847196f, -0.0f, -0.09801714122f, -0.1950903237f, -0.2902846634f, -0.3826834261f, -0.4713967443f, -0.5555702448f, -0.6343932748f, -0.7071067691f, -0.7730104327f, -0.8314695954f, -0.8819212914f, -0.9238795042f, -0.9569403529f, -0.9807852507f, -0.9951847196f, -0.0f, -0.09801714122f, -0.1950903237f, -0.2902846634f, -0.3826834261f, -0.4713967443f, -0.5555702448f, -0.6343932748f, -0.7071067691f, -0.7730104327f, -0.8314695954f, -0.8819212914f, -0.9238795042f, -0.9569403529f, -0.9807852507f, -0.9951847196f, -0.0f, -0.09801714122f, -0.1950903237f, -0.2902846634f, -0.3826834261f, -0.4713967443f, -0.5555702448f, -0.6343932748f, -0.7071067691f, -0.7730104327f, -0.8314695954f, -0.8819212914f, -0.9238795042f, -0.9569403529f, -0.9807852507f, -0.9951847196f, -0.0f, -0.09801714122f, -0.1950903237f, -0.2902846634f, -0.3826834261f, -0.4713967443f, -0.5555702448f, -0.6343932748f, -0.7071067691f, -0.7730104327f, -0.8314695954f, -0.8819212914f, -0.9238795042f, -0.9569403529f, -0.9807852507f, -0.9951847196f, -0.0f, -0.09801714122f, -0.1950903237f, -0.2902846634f, -0.3826834261f, -0.4713967443f, -0.5555702448f, -0.6343932748f, -0.7071067691f, -0.7730104327f, -0.8314695954f, -0.8819212914f, -0.9238795042f, -0.9569403529f, -0.9807852507f, -0.9951847196f, -0.0f, -0.09801714122f, -0.1950903237f, -0.2902846634f, -0.3826834261f, -0.4713967443f, -0.5555702448f, -0.6343932748f, -0.7071067691f, -0.7730104327f, -0.8314695954f, -0.8819212914f, -0.9238795042f, -0.9569403529f, -0.9807852507f, -0.9951847196f, -0.0f, -0.09801714122f, -0.1950903237f, -0.2902846634f, -0.3826834261f, -0.4713967443f, -0.5555702448f, -0.6343932748f, -0.7071067691f, -0.7730104327f, -0.8314695954f, -0.8819212914f, -0.9238795042f, -0.9569403529f, -0.9807852507f, -0.9951847196f, -0.0f, -0.09801714122f, -0.1950903237f, -0.2902846634f, -0.3826834261f, -0.4713967443f, -0.5555702448f, -0.6343932748f, -0.7071067691f, -0.7730104327f, -0.8314695954f, -0.8819212914f, -0.9238795042f, -0.9569403529f, -0.9807852507f, -0.9951847196f, -0.0f, -0.09801714122f, -0.1950903237f, -0.2902846634f, -0.3826834261f, -0.4713967443f, -0.5555702448f, -0.6343932748f, -0.7071067691f, -0.7730104327f, -0.8314695954f, -0.8819212914f, -0.9238795042f, -0.9569403529f, -0.9807852507f, -0.9951847196f}; +constant float ts34[512] = {-0.0f, -0.09801714122f, -0.1950903237f, -0.2902846634f, -0.3826834261f, -0.4713967443f, -0.5555702448f, -0.6343932748f, -0.7071067691f, -0.7730104327f, -0.8314695954f, -0.8819212914f, -0.9238795042f, -0.9569403529f, -0.9807852507f, -0.9951847196f, -0.0f, -0.09801714122f, -0.1950903237f, -0.2902846634f, -0.3826834261f, -0.4713967443f, -0.5555702448f, -0.6343932748f, -0.7071067691f, -0.7730104327f, -0.8314695954f, -0.8819212914f, -0.9238795042f, -0.9569403529f, -0.9807852507f, -0.9951847196f, -0.0f, -0.09801714122f, -0.1950903237f, -0.2902846634f, -0.3826834261f, -0.4713967443f, -0.5555702448f, -0.6343932748f, -0.7071067691f, -0.7730104327f, -0.8314695954f, -0.8819212914f, -0.9238795042f, -0.9569403529f, -0.9807852507f, -0.9951847196f, -0.0f, -0.09801714122f, -0.1950903237f, -0.2902846634f, -0.3826834261f, -0.4713967443f, -0.5555702448f, -0.6343932748f, -0.7071067691f, -0.7730104327f, -0.8314695954f, -0.8819212914f, -0.9238795042f, -0.9569403529f, -0.9807852507f, -0.9951847196f, -0.0f, -0.09801714122f, -0.1950903237f, -0.2902846634f, -0.3826834261f, -0.4713967443f, -0.5555702448f, -0.6343932748f, -0.7071067691f, -0.7730104327f, -0.8314695954f, -0.8819212914f, -0.9238795042f, -0.9569403529f, -0.9807852507f, -0.9951847196f, -0.0f, -0.09801714122f, -0.1950903237f, -0.2902846634f, -0.3826834261f, -0.4713967443f, -0.5555702448f, -0.6343932748f, -0.7071067691f, -0.7730104327f, -0.8314695954f, -0.8819212914f, -0.9238795042f, -0.9569403529f, -0.9807852507f, -0.9951847196f, -0.0f, -0.09801714122f, -0.1950903237f, -0.2902846634f, -0.3826834261f, -0.4713967443f, -0.5555702448f, -0.6343932748f, -0.7071067691f, -0.7730104327f, -0.8314695954f, -0.8819212914f, -0.9238795042f, -0.9569403529f, -0.9807852507f, -0.9951847196f, -0.0f, -0.09801714122f, -0.1950903237f, -0.2902846634f, -0.3826834261f, -0.4713967443f, -0.5555702448f, -0.6343932748f, -0.7071067691f, -0.7730104327f, -0.8314695954f, -0.8819212914f, -0.9238795042f, -0.9569403529f, -0.9807852507f, -0.9951847196f, -0.0f, -0.09801714122f, -0.1950903237f, -0.2902846634f, -0.3826834261f, -0.4713967443f, -0.5555702448f, -0.6343932748f, -0.7071067691f, -0.7730104327f, -0.8314695954f, -0.8819212914f, -0.9238795042f, -0.9569403529f, -0.9807852507f, -0.9951847196f, -0.0f, -0.09801714122f, -0.1950903237f, -0.2902846634f, -0.3826834261f, -0.4713967443f, -0.5555702448f, -0.6343932748f, -0.7071067691f, -0.7730104327f, -0.8314695954f, -0.8819212914f, -0.9238795042f, -0.9569403529f, -0.9807852507f, -0.9951847196f, -0.0f, -0.09801714122f, -0.1950903237f, -0.2902846634f, -0.3826834261f, -0.4713967443f, -0.5555702448f, -0.6343932748f, -0.7071067691f, -0.7730104327f, -0.8314695954f, -0.8819212914f, -0.9238795042f, -0.9569403529f, -0.9807852507f, -0.9951847196f, -0.0f, -0.09801714122f, -0.1950903237f, -0.2902846634f, -0.3826834261f, -0.4713967443f, -0.5555702448f, -0.6343932748f, -0.7071067691f, -0.7730104327f, -0.8314695954f, -0.8819212914f, -0.9238795042f, -0.9569403529f, -0.9807852507f, -0.9951847196f, -0.0f, -0.09801714122f, -0.1950903237f, -0.2902846634f, -0.3826834261f, -0.4713967443f, -0.5555702448f, -0.6343932748f, -0.7071067691f, -0.7730104327f, -0.8314695954f, -0.8819212914f, -0.9238795042f, -0.9569403529f, -0.9807852507f, -0.9951847196f, -0.0f, -0.09801714122f, -0.1950903237f, -0.2902846634f, -0.3826834261f, -0.4713967443f, -0.5555702448f, -0.6343932748f, -0.7071067691f, -0.7730104327f, -0.8314695954f, -0.8819212914f, -0.9238795042f, -0.9569403529f, -0.9807852507f, -0.9951847196f, -0.0f, -0.09801714122f, -0.1950903237f, -0.2902846634f, -0.3826834261f, -0.4713967443f, -0.5555702448f, -0.6343932748f, -0.7071067691f, -0.7730104327f, -0.8314695954f, -0.8819212914f, -0.9238795042f, -0.9569403529f, -0.9807852507f, -0.9951847196f, -0.0f, -0.09801714122f, -0.1950903237f, -0.2902846634f, -0.3826834261f, -0.4713967443f, -0.5555702448f, -0.6343932748f, -0.7071067691f, -0.7730104327f, -0.8314695954f, -0.8819212914f, -0.9238795042f, -0.9569403529f, -0.9807852507f, -0.9951847196f, -0.0f, -0.09801714122f, -0.1950903237f, -0.2902846634f, -0.3826834261f, -0.4713967443f, -0.5555702448f, -0.6343932748f, -0.7071067691f, -0.7730104327f, -0.8314695954f, -0.8819212914f, -0.9238795042f, -0.9569403529f, -0.9807852507f, -0.9951847196f, -0.0f, -0.09801714122f, -0.1950903237f, -0.2902846634f, -0.3826834261f, -0.4713967443f, -0.5555702448f, -0.6343932748f, -0.7071067691f, -0.7730104327f, -0.8314695954f, -0.8819212914f, -0.9238795042f, -0.9569403529f, -0.9807852507f, -0.9951847196f, -0.0f, -0.09801714122f, -0.1950903237f, -0.2902846634f, -0.3826834261f, -0.4713967443f, -0.5555702448f, -0.6343932748f, -0.7071067691f, -0.7730104327f, -0.8314695954f, -0.8819212914f, -0.9238795042f, -0.9569403529f, -0.9807852507f, -0.9951847196f, -0.0f, -0.09801714122f, -0.1950903237f, -0.2902846634f, -0.3826834261f, -0.4713967443f, -0.5555702448f, -0.6343932748f, -0.7071067691f, -0.7730104327f, -0.8314695954f, -0.8819212914f, -0.9238795042f, -0.9569403529f, -0.9807852507f, -0.9951847196f, -0.0f, -0.09801714122f, -0.1950903237f, -0.2902846634f, -0.3826834261f, -0.4713967443f, -0.5555702448f, -0.6343932748f, -0.7071067691f, -0.7730104327f, -0.8314695954f, -0.8819212914f, -0.9238795042f, -0.9569403529f, -0.9807852507f, -0.9951847196f, -0.0f, -0.09801714122f, -0.1950903237f, -0.2902846634f, -0.3826834261f, -0.4713967443f, -0.5555702448f, -0.6343932748f, -0.7071067691f, -0.7730104327f, -0.8314695954f, -0.8819212914f, -0.9238795042f, -0.9569403529f, -0.9807852507f, -0.9951847196f, -0.0f, -0.09801714122f, -0.1950903237f, -0.2902846634f, -0.3826834261f, -0.4713967443f, -0.5555702448f, -0.6343932748f, -0.7071067691f, -0.7730104327f, -0.8314695954f, -0.8819212914f, -0.9238795042f, -0.9569403529f, -0.9807852507f, -0.9951847196f, -0.0f, -0.09801714122f, -0.1950903237f, -0.2902846634f, -0.3826834261f, -0.4713967443f, -0.5555702448f, -0.6343932748f, -0.7071067691f, -0.7730104327f, -0.8314695954f, -0.8819212914f, -0.9238795042f, -0.9569403529f, -0.9807852507f, -0.9951847196f, -0.0f, -0.09801714122f, -0.1950903237f, -0.2902846634f, -0.3826834261f, -0.4713967443f, -0.5555702448f, -0.6343932748f, -0.7071067691f, -0.7730104327f, -0.8314695954f, -0.8819212914f, -0.9238795042f, -0.9569403529f, -0.9807852507f, -0.9951847196f, -0.0f, -0.09801714122f, -0.1950903237f, -0.2902846634f, -0.3826834261f, -0.4713967443f, -0.5555702448f, -0.6343932748f, -0.7071067691f, -0.7730104327f, -0.8314695954f, -0.8819212914f, -0.9238795042f, -0.9569403529f, -0.9807852507f, -0.9951847196f, -0.0f, -0.09801714122f, -0.1950903237f, -0.2902846634f, -0.3826834261f, -0.4713967443f, -0.5555702448f, -0.6343932748f, -0.7071067691f, -0.7730104327f, -0.8314695954f, -0.8819212914f, -0.9238795042f, -0.9569403529f, -0.9807852507f, -0.9951847196f, -0.0f, -0.09801714122f, -0.1950903237f, -0.2902846634f, -0.3826834261f, -0.4713967443f, -0.5555702448f, -0.6343932748f, -0.7071067691f, -0.7730104327f, -0.8314695954f, -0.8819212914f, -0.9238795042f, -0.9569403529f, -0.9807852507f, -0.9951847196f, -0.0f, -0.09801714122f, -0.1950903237f, -0.2902846634f, -0.3826834261f, -0.4713967443f, -0.5555702448f, -0.6343932748f, -0.7071067691f, -0.7730104327f, -0.8314695954f, -0.8819212914f, -0.9238795042f, -0.9569403529f, -0.9807852507f, -0.9951847196f, -0.0f, -0.09801714122f, -0.1950903237f, -0.2902846634f, -0.3826834261f, -0.4713967443f, -0.5555702448f, -0.6343932748f, -0.7071067691f, -0.7730104327f, -0.8314695954f, -0.8819212914f, -0.9238795042f, -0.9569403529f, -0.9807852507f, -0.9951847196f, -0.0f, -0.09801714122f, -0.1950903237f, -0.2902846634f, -0.3826834261f, -0.4713967443f, -0.5555702448f, -0.6343932748f, -0.7071067691f, -0.7730104327f, -0.8314695954f, -0.8819212914f, -0.9238795042f, -0.9569403529f, -0.9807852507f, -0.9951847196f, -0.0f, -0.09801714122f, -0.1950903237f, -0.2902846634f, -0.3826834261f, -0.4713967443f, -0.5555702448f, -0.6343932748f, -0.7071067691f, -0.7730104327f, -0.8314695954f, -0.8819212914f, -0.9238795042f, -0.9569403529f, -0.9807852507f, -0.9951847196f}; +constant float ts32[512] = {-0.0f, -0.2902846634f, -0.5555702448f, -0.7730104327f, -0.9238795042f, -0.9951847196f, -0.9807852507f, -0.8819212914f, -0.7071067691f, -0.4713967443f, -0.1950903237f, 0.09801714122f, 0.3826834261f, 0.6343932748f, 0.8314695954f, 0.9569403529f, -0.0f, -0.2902846634f, -0.5555702448f, -0.7730104327f, -0.9238795042f, -0.9951847196f, -0.9807852507f, -0.8819212914f, -0.7071067691f, -0.4713967443f, -0.1950903237f, 0.09801714122f, 0.3826834261f, 0.6343932748f, 0.8314695954f, 0.9569403529f, -0.0f, -0.2902846634f, -0.5555702448f, -0.7730104327f, -0.9238795042f, -0.9951847196f, -0.9807852507f, -0.8819212914f, -0.7071067691f, -0.4713967443f, -0.1950903237f, 0.09801714122f, 0.3826834261f, 0.6343932748f, 0.8314695954f, 0.9569403529f, -0.0f, -0.2902846634f, -0.5555702448f, -0.7730104327f, -0.9238795042f, -0.9951847196f, -0.9807852507f, -0.8819212914f, -0.7071067691f, -0.4713967443f, -0.1950903237f, 0.09801714122f, 0.3826834261f, 0.6343932748f, 0.8314695954f, 0.9569403529f, -0.0f, -0.2902846634f, -0.5555702448f, -0.7730104327f, -0.9238795042f, -0.9951847196f, -0.9807852507f, -0.8819212914f, -0.7071067691f, -0.4713967443f, -0.1950903237f, 0.09801714122f, 0.3826834261f, 0.6343932748f, 0.8314695954f, 0.9569403529f, -0.0f, -0.2902846634f, -0.5555702448f, -0.7730104327f, -0.9238795042f, -0.9951847196f, -0.9807852507f, -0.8819212914f, -0.7071067691f, -0.4713967443f, -0.1950903237f, 0.09801714122f, 0.3826834261f, 0.6343932748f, 0.8314695954f, 0.9569403529f, -0.0f, -0.2902846634f, -0.5555702448f, -0.7730104327f, -0.9238795042f, -0.9951847196f, -0.9807852507f, -0.8819212914f, -0.7071067691f, -0.4713967443f, -0.1950903237f, 0.09801714122f, 0.3826834261f, 0.6343932748f, 0.8314695954f, 0.9569403529f, -0.0f, -0.2902846634f, -0.5555702448f, -0.7730104327f, -0.9238795042f, -0.9951847196f, -0.9807852507f, -0.8819212914f, -0.7071067691f, -0.4713967443f, -0.1950903237f, 0.09801714122f, 0.3826834261f, 0.6343932748f, 0.8314695954f, 0.9569403529f, -0.0f, -0.2902846634f, -0.5555702448f, -0.7730104327f, -0.9238795042f, -0.9951847196f, -0.9807852507f, -0.8819212914f, -0.7071067691f, -0.4713967443f, -0.1950903237f, 0.09801714122f, 0.3826834261f, 0.6343932748f, 0.8314695954f, 0.9569403529f, -0.0f, -0.2902846634f, -0.5555702448f, -0.7730104327f, -0.9238795042f, -0.9951847196f, -0.9807852507f, -0.8819212914f, -0.7071067691f, -0.4713967443f, -0.1950903237f, 0.09801714122f, 0.3826834261f, 0.6343932748f, 0.8314695954f, 0.9569403529f, -0.0f, -0.2902846634f, -0.5555702448f, -0.7730104327f, -0.9238795042f, -0.9951847196f, -0.9807852507f, -0.8819212914f, -0.7071067691f, -0.4713967443f, -0.1950903237f, 0.09801714122f, 0.3826834261f, 0.6343932748f, 0.8314695954f, 0.9569403529f, -0.0f, -0.2902846634f, -0.5555702448f, -0.7730104327f, -0.9238795042f, -0.9951847196f, -0.9807852507f, -0.8819212914f, -0.7071067691f, -0.4713967443f, -0.1950903237f, 0.09801714122f, 0.3826834261f, 0.6343932748f, 0.8314695954f, 0.9569403529f, -0.0f, -0.2902846634f, -0.5555702448f, -0.7730104327f, -0.9238795042f, -0.9951847196f, -0.9807852507f, -0.8819212914f, -0.7071067691f, -0.4713967443f, -0.1950903237f, 0.09801714122f, 0.3826834261f, 0.6343932748f, 0.8314695954f, 0.9569403529f, -0.0f, -0.2902846634f, -0.5555702448f, -0.7730104327f, -0.9238795042f, -0.9951847196f, -0.9807852507f, -0.8819212914f, -0.7071067691f, -0.4713967443f, -0.1950903237f, 0.09801714122f, 0.3826834261f, 0.6343932748f, 0.8314695954f, 0.9569403529f, -0.0f, -0.2902846634f, -0.5555702448f, -0.7730104327f, -0.9238795042f, -0.9951847196f, -0.9807852507f, -0.8819212914f, -0.7071067691f, -0.4713967443f, -0.1950903237f, 0.09801714122f, 0.3826834261f, 0.6343932748f, 0.8314695954f, 0.9569403529f, -0.0f, -0.2902846634f, -0.5555702448f, -0.7730104327f, -0.9238795042f, -0.9951847196f, -0.9807852507f, -0.8819212914f, -0.7071067691f, -0.4713967443f, -0.1950903237f, 0.09801714122f, 0.3826834261f, 0.6343932748f, 0.8314695954f, 0.9569403529f, -0.0f, -0.2902846634f, -0.5555702448f, -0.7730104327f, -0.9238795042f, -0.9951847196f, -0.9807852507f, -0.8819212914f, -0.7071067691f, -0.4713967443f, -0.1950903237f, 0.09801714122f, 0.3826834261f, 0.6343932748f, 0.8314695954f, 0.9569403529f, -0.0f, -0.2902846634f, -0.5555702448f, -0.7730104327f, -0.9238795042f, -0.9951847196f, -0.9807852507f, -0.8819212914f, -0.7071067691f, -0.4713967443f, -0.1950903237f, 0.09801714122f, 0.3826834261f, 0.6343932748f, 0.8314695954f, 0.9569403529f, -0.0f, -0.2902846634f, -0.5555702448f, -0.7730104327f, -0.9238795042f, -0.9951847196f, -0.9807852507f, -0.8819212914f, -0.7071067691f, -0.4713967443f, -0.1950903237f, 0.09801714122f, 0.3826834261f, 0.6343932748f, 0.8314695954f, 0.9569403529f, -0.0f, -0.2902846634f, -0.5555702448f, -0.7730104327f, -0.9238795042f, -0.9951847196f, -0.9807852507f, -0.8819212914f, -0.7071067691f, -0.4713967443f, -0.1950903237f, 0.09801714122f, 0.3826834261f, 0.6343932748f, 0.8314695954f, 0.9569403529f, -0.0f, -0.2902846634f, -0.5555702448f, -0.7730104327f, -0.9238795042f, -0.9951847196f, -0.9807852507f, -0.8819212914f, -0.7071067691f, -0.4713967443f, -0.1950903237f, 0.09801714122f, 0.3826834261f, 0.6343932748f, 0.8314695954f, 0.9569403529f, -0.0f, -0.2902846634f, -0.5555702448f, -0.7730104327f, -0.9238795042f, -0.9951847196f, -0.9807852507f, -0.8819212914f, -0.7071067691f, -0.4713967443f, -0.1950903237f, 0.09801714122f, 0.3826834261f, 0.6343932748f, 0.8314695954f, 0.9569403529f, -0.0f, -0.2902846634f, -0.5555702448f, -0.7730104327f, -0.9238795042f, -0.9951847196f, -0.9807852507f, -0.8819212914f, -0.7071067691f, -0.4713967443f, -0.1950903237f, 0.09801714122f, 0.3826834261f, 0.6343932748f, 0.8314695954f, 0.9569403529f, -0.0f, -0.2902846634f, -0.5555702448f, -0.7730104327f, -0.9238795042f, -0.9951847196f, -0.9807852507f, -0.8819212914f, -0.7071067691f, -0.4713967443f, -0.1950903237f, 0.09801714122f, 0.3826834261f, 0.6343932748f, 0.8314695954f, 0.9569403529f, -0.0f, -0.2902846634f, -0.5555702448f, -0.7730104327f, -0.9238795042f, -0.9951847196f, -0.9807852507f, -0.8819212914f, -0.7071067691f, -0.4713967443f, -0.1950903237f, 0.09801714122f, 0.3826834261f, 0.6343932748f, 0.8314695954f, 0.9569403529f, -0.0f, -0.2902846634f, -0.5555702448f, -0.7730104327f, -0.9238795042f, -0.9951847196f, -0.9807852507f, -0.8819212914f, -0.7071067691f, -0.4713967443f, -0.1950903237f, 0.09801714122f, 0.3826834261f, 0.6343932748f, 0.8314695954f, 0.9569403529f, -0.0f, -0.2902846634f, -0.5555702448f, -0.7730104327f, -0.9238795042f, -0.9951847196f, -0.9807852507f, -0.8819212914f, -0.7071067691f, -0.4713967443f, -0.1950903237f, 0.09801714122f, 0.3826834261f, 0.6343932748f, 0.8314695954f, 0.9569403529f, -0.0f, -0.2902846634f, -0.5555702448f, -0.7730104327f, -0.9238795042f, -0.9951847196f, -0.9807852507f, -0.8819212914f, -0.7071067691f, -0.4713967443f, -0.1950903237f, 0.09801714122f, 0.3826834261f, 0.6343932748f, 0.8314695954f, 0.9569403529f, -0.0f, -0.2902846634f, -0.5555702448f, -0.7730104327f, -0.9238795042f, -0.9951847196f, -0.9807852507f, -0.8819212914f, -0.7071067691f, -0.4713967443f, -0.1950903237f, 0.09801714122f, 0.3826834261f, 0.6343932748f, 0.8314695954f, 0.9569403529f, -0.0f, -0.2902846634f, -0.5555702448f, -0.7730104327f, -0.9238795042f, -0.9951847196f, -0.9807852507f, -0.8819212914f, -0.7071067691f, -0.4713967443f, -0.1950903237f, 0.09801714122f, 0.3826834261f, 0.6343932748f, 0.8314695954f, 0.9569403529f, -0.0f, -0.2902846634f, -0.5555702448f, -0.7730104327f, -0.9238795042f, -0.9951847196f, -0.9807852507f, -0.8819212914f, -0.7071067691f, -0.4713967443f, -0.1950903237f, 0.09801714122f, 0.3826834261f, 0.6343932748f, 0.8314695954f, 0.9569403529f, -0.0f, -0.2902846634f, -0.5555702448f, -0.7730104327f, -0.9238795042f, -0.9951847196f, -0.9807852507f, -0.8819212914f, -0.7071067691f, -0.4713967443f, -0.1950903237f, 0.09801714122f, 0.3826834261f, 0.6343932748f, 0.8314695954f, 0.9569403529f}; +constant float ts35[512] = {-0.0f, -0.2902846634f, -0.5555702448f, -0.7730104327f, -0.9238795042f, -0.9951847196f, -0.9807852507f, -0.8819212914f, -0.7071067691f, -0.4713967443f, -0.1950903237f, 0.09801714122f, 0.3826834261f, 0.6343932748f, 0.8314695954f, 0.9569403529f, -0.0f, -0.2902846634f, -0.5555702448f, -0.7730104327f, -0.9238795042f, -0.9951847196f, -0.9807852507f, -0.8819212914f, -0.7071067691f, -0.4713967443f, -0.1950903237f, 0.09801714122f, 0.3826834261f, 0.6343932748f, 0.8314695954f, 0.9569403529f, -0.0f, -0.2902846634f, -0.5555702448f, -0.7730104327f, -0.9238795042f, -0.9951847196f, -0.9807852507f, -0.8819212914f, -0.7071067691f, -0.4713967443f, -0.1950903237f, 0.09801714122f, 0.3826834261f, 0.6343932748f, 0.8314695954f, 0.9569403529f, -0.0f, -0.2902846634f, -0.5555702448f, -0.7730104327f, -0.9238795042f, -0.9951847196f, -0.9807852507f, -0.8819212914f, -0.7071067691f, -0.4713967443f, -0.1950903237f, 0.09801714122f, 0.3826834261f, 0.6343932748f, 0.8314695954f, 0.9569403529f, -0.0f, -0.2902846634f, -0.5555702448f, -0.7730104327f, -0.9238795042f, -0.9951847196f, -0.9807852507f, -0.8819212914f, -0.7071067691f, -0.4713967443f, -0.1950903237f, 0.09801714122f, 0.3826834261f, 0.6343932748f, 0.8314695954f, 0.9569403529f, -0.0f, -0.2902846634f, -0.5555702448f, -0.7730104327f, -0.9238795042f, -0.9951847196f, -0.9807852507f, -0.8819212914f, -0.7071067691f, -0.4713967443f, -0.1950903237f, 0.09801714122f, 0.3826834261f, 0.6343932748f, 0.8314695954f, 0.9569403529f, -0.0f, -0.2902846634f, -0.5555702448f, -0.7730104327f, -0.9238795042f, -0.9951847196f, -0.9807852507f, -0.8819212914f, -0.7071067691f, -0.4713967443f, -0.1950903237f, 0.09801714122f, 0.3826834261f, 0.6343932748f, 0.8314695954f, 0.9569403529f, -0.0f, -0.2902846634f, -0.5555702448f, -0.7730104327f, -0.9238795042f, -0.9951847196f, -0.9807852507f, -0.8819212914f, -0.7071067691f, -0.4713967443f, -0.1950903237f, 0.09801714122f, 0.3826834261f, 0.6343932748f, 0.8314695954f, 0.9569403529f, -0.0f, -0.2902846634f, -0.5555702448f, -0.7730104327f, -0.9238795042f, -0.9951847196f, -0.9807852507f, -0.8819212914f, -0.7071067691f, -0.4713967443f, -0.1950903237f, 0.09801714122f, 0.3826834261f, 0.6343932748f, 0.8314695954f, 0.9569403529f, -0.0f, -0.2902846634f, -0.5555702448f, -0.7730104327f, -0.9238795042f, -0.9951847196f, -0.9807852507f, -0.8819212914f, -0.7071067691f, -0.4713967443f, -0.1950903237f, 0.09801714122f, 0.3826834261f, 0.6343932748f, 0.8314695954f, 0.9569403529f, -0.0f, -0.2902846634f, -0.5555702448f, -0.7730104327f, -0.9238795042f, -0.9951847196f, -0.9807852507f, -0.8819212914f, -0.7071067691f, -0.4713967443f, -0.1950903237f, 0.09801714122f, 0.3826834261f, 0.6343932748f, 0.8314695954f, 0.9569403529f, -0.0f, -0.2902846634f, -0.5555702448f, -0.7730104327f, -0.9238795042f, -0.9951847196f, -0.9807852507f, -0.8819212914f, -0.7071067691f, -0.4713967443f, -0.1950903237f, 0.09801714122f, 0.3826834261f, 0.6343932748f, 0.8314695954f, 0.9569403529f, -0.0f, -0.2902846634f, -0.5555702448f, -0.7730104327f, -0.9238795042f, -0.9951847196f, -0.9807852507f, -0.8819212914f, -0.7071067691f, -0.4713967443f, -0.1950903237f, 0.09801714122f, 0.3826834261f, 0.6343932748f, 0.8314695954f, 0.9569403529f, -0.0f, -0.2902846634f, -0.5555702448f, -0.7730104327f, -0.9238795042f, -0.9951847196f, -0.9807852507f, -0.8819212914f, -0.7071067691f, -0.4713967443f, -0.1950903237f, 0.09801714122f, 0.3826834261f, 0.6343932748f, 0.8314695954f, 0.9569403529f, -0.0f, -0.2902846634f, -0.5555702448f, -0.7730104327f, -0.9238795042f, -0.9951847196f, -0.9807852507f, -0.8819212914f, -0.7071067691f, -0.4713967443f, -0.1950903237f, 0.09801714122f, 0.3826834261f, 0.6343932748f, 0.8314695954f, 0.9569403529f, -0.0f, -0.2902846634f, -0.5555702448f, -0.7730104327f, -0.9238795042f, -0.9951847196f, -0.9807852507f, -0.8819212914f, -0.7071067691f, -0.4713967443f, -0.1950903237f, 0.09801714122f, 0.3826834261f, 0.6343932748f, 0.8314695954f, 0.9569403529f, -0.0f, -0.2902846634f, -0.5555702448f, -0.7730104327f, -0.9238795042f, -0.9951847196f, -0.9807852507f, -0.8819212914f, -0.7071067691f, -0.4713967443f, -0.1950903237f, 0.09801714122f, 0.3826834261f, 0.6343932748f, 0.8314695954f, 0.9569403529f, -0.0f, -0.2902846634f, -0.5555702448f, -0.7730104327f, -0.9238795042f, -0.9951847196f, -0.9807852507f, -0.8819212914f, -0.7071067691f, -0.4713967443f, -0.1950903237f, 0.09801714122f, 0.3826834261f, 0.6343932748f, 0.8314695954f, 0.9569403529f, -0.0f, -0.2902846634f, -0.5555702448f, -0.7730104327f, -0.9238795042f, -0.9951847196f, -0.9807852507f, -0.8819212914f, -0.7071067691f, -0.4713967443f, -0.1950903237f, 0.09801714122f, 0.3826834261f, 0.6343932748f, 0.8314695954f, 0.9569403529f, -0.0f, -0.2902846634f, -0.5555702448f, -0.7730104327f, -0.9238795042f, -0.9951847196f, -0.9807852507f, -0.8819212914f, -0.7071067691f, -0.4713967443f, -0.1950903237f, 0.09801714122f, 0.3826834261f, 0.6343932748f, 0.8314695954f, 0.9569403529f, -0.0f, -0.2902846634f, -0.5555702448f, -0.7730104327f, -0.9238795042f, -0.9951847196f, -0.9807852507f, -0.8819212914f, -0.7071067691f, -0.4713967443f, -0.1950903237f, 0.09801714122f, 0.3826834261f, 0.6343932748f, 0.8314695954f, 0.9569403529f, -0.0f, -0.2902846634f, -0.5555702448f, -0.7730104327f, -0.9238795042f, -0.9951847196f, -0.9807852507f, -0.8819212914f, -0.7071067691f, -0.4713967443f, -0.1950903237f, 0.09801714122f, 0.3826834261f, 0.6343932748f, 0.8314695954f, 0.9569403529f, -0.0f, -0.2902846634f, -0.5555702448f, -0.7730104327f, -0.9238795042f, -0.9951847196f, -0.9807852507f, -0.8819212914f, -0.7071067691f, -0.4713967443f, -0.1950903237f, 0.09801714122f, 0.3826834261f, 0.6343932748f, 0.8314695954f, 0.9569403529f, -0.0f, -0.2902846634f, -0.5555702448f, -0.7730104327f, -0.9238795042f, -0.9951847196f, -0.9807852507f, -0.8819212914f, -0.7071067691f, -0.4713967443f, -0.1950903237f, 0.09801714122f, 0.3826834261f, 0.6343932748f, 0.8314695954f, 0.9569403529f, -0.0f, -0.2902846634f, -0.5555702448f, -0.7730104327f, -0.9238795042f, -0.9951847196f, -0.9807852507f, -0.8819212914f, -0.7071067691f, -0.4713967443f, -0.1950903237f, 0.09801714122f, 0.3826834261f, 0.6343932748f, 0.8314695954f, 0.9569403529f, -0.0f, -0.2902846634f, -0.5555702448f, -0.7730104327f, -0.9238795042f, -0.9951847196f, -0.9807852507f, -0.8819212914f, -0.7071067691f, -0.4713967443f, -0.1950903237f, 0.09801714122f, 0.3826834261f, 0.6343932748f, 0.8314695954f, 0.9569403529f, -0.0f, -0.2902846634f, -0.5555702448f, -0.7730104327f, -0.9238795042f, -0.9951847196f, -0.9807852507f, -0.8819212914f, -0.7071067691f, -0.4713967443f, -0.1950903237f, 0.09801714122f, 0.3826834261f, 0.6343932748f, 0.8314695954f, 0.9569403529f, -0.0f, -0.2902846634f, -0.5555702448f, -0.7730104327f, -0.9238795042f, -0.9951847196f, -0.9807852507f, -0.8819212914f, -0.7071067691f, -0.4713967443f, -0.1950903237f, 0.09801714122f, 0.3826834261f, 0.6343932748f, 0.8314695954f, 0.9569403529f, -0.0f, -0.2902846634f, -0.5555702448f, -0.7730104327f, -0.9238795042f, -0.9951847196f, -0.9807852507f, -0.8819212914f, -0.7071067691f, -0.4713967443f, -0.1950903237f, 0.09801714122f, 0.3826834261f, 0.6343932748f, 0.8314695954f, 0.9569403529f, -0.0f, -0.2902846634f, -0.5555702448f, -0.7730104327f, -0.9238795042f, -0.9951847196f, -0.9807852507f, -0.8819212914f, -0.7071067691f, -0.4713967443f, -0.1950903237f, 0.09801714122f, 0.3826834261f, 0.6343932748f, 0.8314695954f, 0.9569403529f, -0.0f, -0.2902846634f, -0.5555702448f, -0.7730104327f, -0.9238795042f, -0.9951847196f, -0.9807852507f, -0.8819212914f, -0.7071067691f, -0.4713967443f, -0.1950903237f, 0.09801714122f, 0.3826834261f, 0.6343932748f, 0.8314695954f, 0.9569403529f, -0.0f, -0.2902846634f, -0.5555702448f, -0.7730104327f, -0.9238795042f, -0.9951847196f, -0.9807852507f, -0.8819212914f, -0.7071067691f, -0.4713967443f, -0.1950903237f, 0.09801714122f, 0.3826834261f, 0.6343932748f, 0.8314695954f, 0.9569403529f}; +constant float ts40[512] = {-0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f}; +constant float ts43[512] = {-0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f, -0.0f, -0.7071067691f, -1.0f, -0.7071067691f}; +constant float ts41[512] = {-0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f}; +constant float ts44[512] = {-0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f, -0.0f, -0.3826834261f, -0.7071067691f, -0.9238795042f}; +constant float ts42[512] = {-0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f}; +constant float ts45[512] = {-0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f, -0.0f, -0.9238795042f, -0.7071067691f, 0.3826834261f}; + diff --git a/FFT/src/host/CMakeLists.txt b/FFT/src/host/CMakeLists.txt new file mode 100755 index 00000000..b43cdfdd --- /dev/null +++ b/FFT/src/host/CMakeLists.txt @@ -0,0 +1,12 @@ + +include_directories(../../../extern/cxxopts/include) +include_directories(${IntelFPGAOpenCL_INCLUDE_DIRS}) +include_directories(${CMAKE_BINARY_DIR}/src/common) + +set(HEADER_FILES execution.h setup/fpga_setup.hpp fft_functionality.hpp) +set(HOST_SOURCE execution_default.cpp main.cpp setup/fpga_setup.cpp fft_functionality.cpp) + +add_compile_options(--std=c++11) + +add_executable(fFFT ${HOST_SOURCE} ${HEADER_FILES}) +target_link_libraries(fFFT ${IntelFPGAOpenCL_LIBRARIES}) diff --git a/FFT/src/host/execution.h b/FFT/src/host/execution.h new file mode 100644 index 00000000..27c092ce --- /dev/null +++ b/FFT/src/host/execution.h @@ -0,0 +1,65 @@ +/* +Copyright (c) 2019 Marius Meyer + +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 SRC_HOST_EXECUTION_H_ +#define SRC_HOST_EXECUTION_H_ + +/* C++ standard library headers */ +#include +#include +#include + +/* External library headers */ +#include "CL/cl.hpp" +#include "parameters.h" + + +namespace bm_execution { + + struct ExecutionConfiguration { + cl::Context context; + cl::Device device; + cl::Program program; + uint repetitions; + }; + + struct ExecutionTimings { + unsigned iterations; + bool inverse; + std::vector calculationTimings; + }; + +/** +The actual execution of the benchmark. +This method can be implemented in multiple *.cpp files. This header enables +simple exchange of the different calculation methods. + +@param config struct that contains all necessary information to execute the kernel on the FPGA + + +@return The resulting matrix +*/ + std::shared_ptr + calculate(std::shared_ptr config, std::complex* data, unsigned iterations, bool inverse); + +} // namespace bm_execution + +#endif // SRC_HOST_EXECUTION_H_ diff --git a/FFT/src/host/execution_default.cpp b/FFT/src/host/execution_default.cpp new file mode 100644 index 00000000..eecc2008 --- /dev/null +++ b/FFT/src/host/execution_default.cpp @@ -0,0 +1,92 @@ +/* +Copyright (c) 2019 Marius Meyer + +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. +*/ + +/* Related header files */ +#include "execution.h" + +/* C++ standard library headers */ +#include +#include +#include + +/* External library headers */ +#include "CL/cl.hpp" +#include "CL/cl_ext_intelfpga.h" + +/* Project's headers */ + +namespace bm_execution { + + /* + Implementation for the single kernel. + @copydoc bm_execution::calculate() + */ + std::shared_ptr + calculate(std::shared_ptr config, + std::complex* data, + unsigned iterations, + bool inverse) { + + cl::Buffer inBuffer = cl::Buffer(config->context, CL_MEM_WRITE_ONLY, (1 << LOG_FFT_SIZE) * iterations * 2 * sizeof(HOST_DATA_TYPE)); + cl::Buffer outBuffer = cl::Buffer(config->context, CL_MEM_READ_ONLY, (1 << LOG_FFT_SIZE) * iterations * 2 * sizeof(HOST_DATA_TYPE)); + + cl::Kernel fetchKernel(config->program, FETCH_KERNEL_NAME); + + fetchKernel.setArg(0, inBuffer); + + cl::Kernel fftKernel(config->program, FFT_KERNEL_NAME); + + fftKernel.setArg(0, outBuffer); + fftKernel.setArg(1, iterations); + fftKernel.setArg(2, static_cast(inverse)); + + cl::CommandQueue fetchQueue(config->context); + cl::CommandQueue fftQueue(config->context); + + fetchQueue.enqueueWriteBuffer(inBuffer,CL_TRUE,0, (1 << LOG_FFT_SIZE) * iterations * 2 * sizeof(HOST_DATA_TYPE), data); + + std::vector calculationTimings; + for (uint r =0; r < config->repetitions; r++) { + auto startCalculation = std::chrono::high_resolution_clock::now(); + fetchQueue.enqueueNDRangeKernel(fetchKernel, cl::NullRange, cl::NDRange((1 << LOG_FFT_SIZE)/ FFT_UNROLL * iterations), + cl::NDRange((1 << LOG_FFT_SIZE)/ FFT_UNROLL)); + fftQueue.enqueueTask(fftKernel); + fetchQueue.finish(); + fftQueue.finish(); + auto endCalculation = std::chrono::high_resolution_clock::now(); + std::chrono::duration calculationTime = + std::chrono::duration_cast> + (endCalculation - startCalculation); + calculationTimings.push_back(calculationTime.count()); + } + + fetchQueue.enqueueReadBuffer(outBuffer,CL_TRUE,0, (1 << LOG_FFT_SIZE) * iterations * 2 * sizeof(HOST_DATA_TYPE), data); + + std::shared_ptr result(new ExecutionTimings{ + iterations, + inverse, + calculationTimings + }); + return result; + } + +} // namespace bm_execution diff --git a/FFT/src/host/fft_functionality.cpp b/FFT/src/host/fft_functionality.cpp new file mode 100644 index 00000000..61d4e823 --- /dev/null +++ b/FFT/src/host/fft_functionality.cpp @@ -0,0 +1,290 @@ +// +// Created by Marius Meyer on 04.12.19. +// + +/* +Copyright (c) 2019 Marius Meyer + +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. +*/ + +#include "fft_functionality.hpp" + +/* C++ standard library headers */ +#include +#include + +/* Project's headers */ +#include "execution.h" +#include "cxxopts.hpp" +#include "setup/fpga_setup.hpp" +#include "parameters.h" + +/** +Parses and returns program options using the cxxopts library. +Supports the following parameters: + - file name of the FPGA kernel file (-f,--file) + - number of repetitions (-n) + - number of kernel replications (-r) + - data size (-d) + - use memory interleaving +@see https://github.com/jarro2783/cxxopts + +@return program settings that are created from the given program arguments +*/ +std::shared_ptr +parseProgramParameters(int argc, char *argv[]) { + // Defining and parsing program options + cxxopts::Options options(argv[0], PROGRAM_DESCRIPTION); + options.add_options() + ("f,file", "Kernel file name", cxxopts::value()) + ("n", "Number of repetitions", + cxxopts::value()->default_value(std::to_string(DEFAULT_REPETITIONS))) + ("i", "Multiplier for the used data size that will be i * FFT_SIZE", + cxxopts::value()->default_value(std::to_string(DEFAULT_ITERATIONS))) + ("inverse", "If set, the inverse FFT is calculated instead") + ("device", "Index of the device that has to be used. If not given you "\ + "will be asked which device to use if there are multiple devices "\ + "available.", cxxopts::value()->default_value(std::to_string(DEFAULT_DEVICE))) + ("platform", "Index of the platform that has to be used. If not given "\ + "you will be asked which platform to use if there are multiple "\ + "platforms available.", + cxxopts::value()->default_value(std::to_string(DEFAULT_PLATFORM))) + ("h,help", "Print this help"); + cxxopts::ParseResult result = options.parse(argc, argv); + + // Check parsed options and handle special cases + if (result.count("f") <= 0) { + // Path to the kernel file is mandatory - exit if not given! + std::cerr << "Kernel file must be given! Aborting" << std::endl; + std::cout << options.help() << std::endl; + exit(1); + } + if (result.count("h")) { + // Just print help when argument is given + std::cout << options.help() << std::endl; + exit(0); + } + + // Create program settings from program arguments + std::shared_ptr sharedSettings( + new ProgramSettings{result["n"].as(), + result["i"].as(), + static_cast(result.count("inverse")), + result["platform"].as(), + result["device"].as(), + result["f"].as()}); + return sharedSettings; +} + + +/** +Prints the execution results to stdout + +@param results The execution results +*/ +void +printResults(std::shared_ptr results) { + + double gflop = 5 * (1 << LOG_FFT_SIZE) * LOG_FFT_SIZE * results->iterations * 1.0e-9; + + double minTime = *min_element(results->calculationTimings.begin(), results->calculationTimings.end()); + double avgTime = accumulate(results->calculationTimings.begin(), results->calculationTimings.end(), 0.0) + / results->calculationTimings.size(); + + std::cout << std::setw(ENTRY_SPACE) << " " << std::setw(ENTRY_SPACE) << "avg" + << std::setw(ENTRY_SPACE) << "best" << std::endl; + std::cout << std::setw(ENTRY_SPACE) << "Time in s:" << std::setw(ENTRY_SPACE) << avgTime / results->iterations + << std::setw(ENTRY_SPACE) << minTime / results->iterations << std::endl; + std::cout << std::setw(ENTRY_SPACE) << "GFLOPS:" << std::setw(ENTRY_SPACE) << gflop / avgTime + << std::setw(ENTRY_SPACE) << gflop / minTime << std::endl; + +} + +/** + * Prints the used configuration to std out before starting the actual benchmark. + * + * @param programSettings The program settings retrieved from the command line + * @param device The device used for execution + */ +void printFinalConfiguration(const std::shared_ptr &programSettings, + const cl::Device &device) {// Give setup summary + std::cout << PROGRAM_DESCRIPTION << std::endl << HLINE; + std::cout << "Summary:" << std::endl + << "FFT Size: " << (1 << LOG_FFT_SIZE) + << std::endl + << "Data Size: " << programSettings->iterations << " * FFT Size * sizeof(" + << STR(HOST_DATA_TYPE) + << ") = " << static_cast((1 << LOG_FFT_SIZE) * programSettings->iterations * sizeof(HOST_DATA_TYPE)) << " Byte" + << std::endl + << "Repetitions: " << programSettings->numRepetitions + << std::endl + << "Kernel file: " << programSettings->kernelFileName + << std::endl; + std::cout << "Device: " + << device.getInfo() << std::endl; + std::cout << HLINE + << "Start benchmark using the given configuration." << std::endl + << HLINE; +} + + +void generateInputData(std::complex* data, unsigned iterations) { + std::mt19937 gen(0); + auto dis = std::uniform_real_distribution(-1.0, 1.0); + for (int i=0; i< iterations * (1 << LOG_FFT_SIZE); i++) { + data[i].real(dis(gen)); + data[i].imag(dis(gen)); + } +} + +double checkFFTResult(std::complex* verify_data, std::complex* result_data, unsigned iterations) { + double residual_max = 0; + for (int i = 0; i < iterations; i++) { + // we have to bit reverse the output data of the FPGA kernel, since it will be provided in bit-reversed order. + // Directly applying iFFT on the data would thus not form the identity function we want to have for verification. + // TODO: This might need to be changed for other FPGA implementations that return the data in correct order + bit_reverse(&result_data[i * (1 << LOG_FFT_SIZE)], 1); + fourier_transform_gold(true, LOG_FFT_SIZE, &result_data[i * (1 << LOG_FFT_SIZE)]); + + // Normalize the data after applying iFFT + for (int j = 0; j < (1 << LOG_FFT_SIZE); j++) { + result_data[i * (1 << LOG_FFT_SIZE) + j] /= (1 << LOG_FFT_SIZE); + } + for (int j = 0; j < (1 << LOG_FFT_SIZE); j++) { + double tmp_error = std::abs(verify_data[i * (1 << LOG_FFT_SIZE) + j] - result_data[i * (1 << LOG_FFT_SIZE) + j]); + residual_max = residual_max > tmp_error ? residual_max : tmp_error; + } + } + double error = residual_max / + (std::numeric_limits::epsilon() * LOG_FFT_SIZE); + + std::cout << std::setw(ENTRY_SPACE) << "res. error" << std::setw(ENTRY_SPACE) << "mach. eps" << std::endl; + std::cout << std::setw(ENTRY_SPACE) << error << std::setw(ENTRY_SPACE) + << std::numeric_limits::epsilon() << std::endl << std::endl; + + // Calculate residual according to paper considering also the used iterations + return error; +} + +void bit_reverse(std::complex *data, unsigned iterations) { + auto *tmp = new std::complex[(1 << LOG_FFT_SIZE)]; + for (int k=0; k < iterations; k++) { + for (int i = 0; i < (1 << LOG_FFT_SIZE); i++) { + int fwd = i; + int bit_rev = 0; + for (int j = 0; j < LOG_FFT_SIZE; j++) { + bit_rev <<= 1; + bit_rev |= fwd & 1; + fwd >>= 1; + } + tmp[i] = data[bit_rev]; + } + for (int i = 0; i < (1 << LOG_FFT_SIZE); i++) { + data[k * (1 << LOG_FFT_SIZE) + i] = tmp[i]; + } + } + delete [] tmp; +} + +// The function definitions and implementations below this comment are taken from the +// FFT1D example implementation of the Intel FPGA SDK for OpenCL 19.4 +// They are licensed under the following conditions: +// +// Copyright (C) 2013-2019 Altera Corporation, San Jose, California, USA. All rights reserved. +// 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. +// +// This agreement shall be governed in all respects by the laws of the State of California and +// by the laws of the United States of America. + + +void fourier_transform_gold(bool inverse, const int lognr_points, std::complex *data_sp) { + const int nr_points = 1 << lognr_points; + + auto *data = new std::complex[nr_points]; + + for (int i=0; i < nr_points; i++) { + data[i] = data_sp[i]; + } + + // The inverse requires swapping the real and imaginary component + + if (inverse) { + for (int i = 0; i < nr_points; i++) { + double tmp = data[i].imag(); + data[i].imag(data[i].real()); + data[i].real(tmp); + } + } + // Do a FT recursively + fourier_stage(lognr_points, data); + + // The inverse requires swapping the real and imaginary component + if (inverse) { + for (int i = 0; i < nr_points; i++) { + double tmp = data[i].real(); + data[i].real(data[i].imag()); + data[i].imag(tmp); + } + } + + // Do the bit reversal + for (int i = 0; i < nr_points; i++) { + data_sp[i] = data[i]; + } + + delete [] data; +} + +void fourier_stage(int lognr_points, std::complex *data) { + int nr_points = 1 << lognr_points; + if (nr_points == 1) return; + auto *half1 = new std::complex[nr_points / 2]; + auto *half2 = new std::complex[nr_points / 2]; + for (int i = 0; i < nr_points / 2; i++) { + half1[i] = data[2 * i]; + half2[i] = data[2 * i + 1]; + } + fourier_stage(lognr_points - 1, half1); + fourier_stage(lognr_points - 1, half2); + for (int i = 0; i < nr_points / 2; i++) { + data[i].real(half1[i].real() + cos (2 * M_PI * i / nr_points) * half2[i].real() + sin (2 * M_PI * i / nr_points) * half2[i].imag()); + data[i].imag(half1[i].imag() - sin (2 * M_PI * i / nr_points) * half2[i].real() + cos (2 * M_PI * i / nr_points) * half2[i].imag()); + data[i + nr_points / 2].real(half1[i].real() - cos (2 * M_PI * i / nr_points) * half2[i].real() - sin (2 * M_PI * i / nr_points) * half2[i].imag()); + data[i + nr_points / 2].imag(half1[i].imag() + sin (2 * M_PI * i / nr_points) * half2[i].real() - cos (2 * M_PI * i / nr_points) * half2[i].imag()); + } + + delete [] half1; + delete [] half2; +} \ No newline at end of file diff --git a/FFT/src/host/fft_functionality.hpp b/FFT/src/host/fft_functionality.hpp new file mode 100644 index 00000000..2cd72c3b --- /dev/null +++ b/FFT/src/host/fft_functionality.hpp @@ -0,0 +1,151 @@ +/* +Copyright (c) 2019 Marius Meyer + +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 SRC_HOST_NETWORK_FUNCTIONALITY_H_ +#define SRC_HOST_NETWORK_FUNCTIONALITY_H_ + +/* C++ standard library headers */ +#include +#include + +/* Project's headers */ +#include "execution.h" +#include "cxxopts.hpp" +#include "setup/fpga_setup.hpp" +#include "parameters.h" + +/* +Short description of the program. +Moreover the version and build time is also compiled into the description. +*/ +#define STR_EXPAND(tok) #tok +#define STR(tok) STR_EXPAND(tok) + +#define PROGRAM_DESCRIPTION "Implementation of the FFT benchmark"\ + " proposed in the HPCC benchmark suite for FPGA.\n"\ + "Version: " STR(VERSION) + +#define ENTRY_SPACE 13 + +struct ProgramSettings { + uint numRepetitions; + unsigned iterations; + bool inverse; + int defaultPlatform; + int defaultDevice; + std::string kernelFileName; +}; + + +/** +Parses and returns program options using the cxxopts library. +Supports the following parameters: + - file name of the FPGA kernel file (-f,--file) + - number of repetitions (-n) + - number of kernel replications (-r) + - data size (-d) + - use memory interleaving +@see https://github.com/jarro2783/cxxopts + +@return program settings that are created from the given program arguments +*/ +std::shared_ptr +parseProgramParameters(int argc, char *argv[]); + +/** +Prints the execution results to stdout + +@param results The execution results +*/ +void +printResults(std::shared_ptr results); + +/** + * Prints the used configuration to std out before starting the actual benchmark. + * + * @param programSettings The program settings retrieved from the command line + * @param device The device used for execution + */ +void printFinalConfiguration(const std::shared_ptr &programSettings, + const cl::Device &device); + + +/** + * Fill the data buffer with random number using the mersenne twister engine with + * seed 0. + * + * @param data Data array that has to be filled + * @param size Size of the data array that has to be filled + */ +void generateInputData(std::complex* data, unsigned iterations); + + +/** + * Checks the calculation error of an FFt calculation by calculating the inverse FFT on the result data + * and calculating the residual with abs(x - x')/(epsilon * log(FFT_SIZE)). + * + * @param verify_data The input data of the FFT calculation + * @param result_data Result of the FFT calculation + * @param iterations Number data iterations (total data size should be iterations * FFT_SIZE) + * @return the residual error of the calculation + */ +double checkFFTResult(std::complex* verify_data, std::complex* result_data, unsigned iterations); + +/** + * Bit reverses the order of the given FFT data in place + * + * @param data Array of complex numbers that will be sorted in bit reversed order + * @param iterations Length of the data array will be calculated with iterations * FFT Size + */ +void bit_reverse(std::complex *data, unsigned iterations); + +// The function definitions and implementations below this comment are taken from the +// FFT1D example implementation of the Intel FPGA SDK for OpenCL 19.4 +// They are licensed under the following conditions: +// +// Copyright (C) 2013-2019 Altera Corporation, San Jose, California, USA. All rights reserved. +// 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. +// +// This agreement shall be governed in all respects by the laws of the State of California and +// by the laws of the United States of America. + +void fourier_transform_gold(bool inverse, const int lognr_points, std::complex *data); + +void fourier_stage(int lognr_points, std::complex *data); + + +#endif // SRC_HOST_NETWORK_FUNCTIONALITY_H_ diff --git a/FFT/src/host/main.cpp b/FFT/src/host/main.cpp new file mode 100644 index 00000000..a978a790 --- /dev/null +++ b/FFT/src/host/main.cpp @@ -0,0 +1,48 @@ +// +// Created by Marius Meyer on 04.12.19. +// + +#include "fft_functionality.hpp" + +/** +The program entry point +*/ +int +main(int argc, char *argv[]) { + // Setup benchmark + std::shared_ptr programSettings = + parseProgramParameters(argc, argv); + fpga_setup::setupEnvironmentAndClocks(); + std::vector usedDevice = + fpga_setup::selectFPGADevice(programSettings->defaultPlatform, + programSettings->defaultDevice); + cl::Context context = cl::Context(usedDevice); + cl::Program program = fpga_setup::fpgaSetup(&context, usedDevice, + &programSettings->kernelFileName); + + printFinalConfiguration(programSettings, usedDevice[0]); + + std::shared_ptr config( + new bm_execution::ExecutionConfiguration { + context, usedDevice[0], program, + programSettings->numRepetitions + }); + + //TODO implement actual benchmark execution + std::complex* data; + posix_memalign(reinterpret_cast(&data), 64, programSettings->iterations * (1 << LOG_FFT_SIZE) * sizeof(std::complex)); + generateInputData(data, programSettings->iterations); + + auto timing = bm_execution::calculate(config, data, programSettings->iterations, programSettings->inverse); + + auto* verify_data = new std::complex[programSettings->iterations * (1 << LOG_FFT_SIZE)]; + generateInputData(verify_data, programSettings->iterations); + double error = checkFFTResult(verify_data, data, timing->iterations); + + delete [] verify_data; + + printResults(timing); + + return error < 1 ? 0 : 1; +} + diff --git a/FFT/src/host/setup/fpga_setup.cpp b/FFT/src/host/setup/fpga_setup.cpp new file mode 100644 index 00000000..07845901 --- /dev/null +++ b/FFT/src/host/setup/fpga_setup.cpp @@ -0,0 +1,273 @@ +// +// Created by Marius Meyer on 04.12.19. +// + +#include "fpga_setup.hpp" + +#include +#include +#include +#include +#include +#include + +/* External libraries */ +#include "parameters.h" + +namespace fpga_setup { + + /** +Converts the reveived OpenCL error to a string + +@param err The OpenCL error code + +@return The string representation of the OpenCL error code +*/ + std::string + getCLErrorString(cl_int const err) { + switch (err) { + CL_ERR_TO_STR(CL_DEVICE_NOT_FOUND); + CL_ERR_TO_STR(CL_DEVICE_NOT_AVAILABLE); + CL_ERR_TO_STR(CL_COMPILER_NOT_AVAILABLE); + CL_ERR_TO_STR(CL_MEM_OBJECT_ALLOCATION_FAILURE); + CL_ERR_TO_STR(CL_OUT_OF_RESOURCES); + CL_ERR_TO_STR(CL_OUT_OF_HOST_MEMORY); + CL_ERR_TO_STR(CL_PROFILING_INFO_NOT_AVAILABLE); + CL_ERR_TO_STR(CL_MEM_COPY_OVERLAP); + CL_ERR_TO_STR(CL_IMAGE_FORMAT_MISMATCH); + CL_ERR_TO_STR(CL_IMAGE_FORMAT_NOT_SUPPORTED); + CL_ERR_TO_STR(CL_BUILD_PROGRAM_FAILURE); + CL_ERR_TO_STR(CL_MAP_FAILURE); + CL_ERR_TO_STR(CL_MISALIGNED_SUB_BUFFER_OFFSET); + CL_ERR_TO_STR(CL_EXEC_STATUS_ERROR_FOR_EVENTS_IN_WAIT_LIST); + CL_ERR_TO_STR(CL_KERNEL_ARG_INFO_NOT_AVAILABLE); + CL_ERR_TO_STR(CL_INVALID_VALUE); + CL_ERR_TO_STR(CL_INVALID_DEVICE_TYPE); + CL_ERR_TO_STR(CL_INVALID_PLATFORM); + CL_ERR_TO_STR(CL_INVALID_DEVICE); + CL_ERR_TO_STR(CL_INVALID_CONTEXT); + CL_ERR_TO_STR(CL_INVALID_QUEUE_PROPERTIES); + CL_ERR_TO_STR(CL_INVALID_COMMAND_QUEUE); + CL_ERR_TO_STR(CL_INVALID_HOST_PTR); + CL_ERR_TO_STR(CL_INVALID_MEM_OBJECT); + CL_ERR_TO_STR(CL_INVALID_IMAGE_FORMAT_DESCRIPTOR); + CL_ERR_TO_STR(CL_INVALID_IMAGE_SIZE); + CL_ERR_TO_STR(CL_INVALID_SAMPLER); + CL_ERR_TO_STR(CL_INVALID_BINARY); + CL_ERR_TO_STR(CL_INVALID_BUILD_OPTIONS); + CL_ERR_TO_STR(CL_INVALID_PROGRAM); + CL_ERR_TO_STR(CL_INVALID_PROGRAM_EXECUTABLE); + CL_ERR_TO_STR(CL_INVALID_KERNEL_NAME); + CL_ERR_TO_STR(CL_INVALID_KERNEL_DEFINITION); + CL_ERR_TO_STR(CL_INVALID_KERNEL); + CL_ERR_TO_STR(CL_INVALID_ARG_INDEX); + CL_ERR_TO_STR(CL_INVALID_ARG_VALUE); + CL_ERR_TO_STR(CL_INVALID_ARG_SIZE); + CL_ERR_TO_STR(CL_INVALID_KERNEL_ARGS); + CL_ERR_TO_STR(CL_INVALID_WORK_DIMENSION); + CL_ERR_TO_STR(CL_INVALID_WORK_GROUP_SIZE); + CL_ERR_TO_STR(CL_INVALID_WORK_ITEM_SIZE); + CL_ERR_TO_STR(CL_INVALID_GLOBAL_OFFSET); + CL_ERR_TO_STR(CL_INVALID_EVENT_WAIT_LIST); + CL_ERR_TO_STR(CL_INVALID_EVENT); + CL_ERR_TO_STR(CL_INVALID_OPERATION); + CL_ERR_TO_STR(CL_INVALID_GL_OBJECT); + CL_ERR_TO_STR(CL_INVALID_BUFFER_SIZE); + CL_ERR_TO_STR(CL_INVALID_MIP_LEVEL); + CL_ERR_TO_STR(CL_INVALID_GLOBAL_WORK_SIZE); + CL_ERR_TO_STR(CL_COMPILE_PROGRAM_FAILURE); + CL_ERR_TO_STR(CL_LINKER_NOT_AVAILABLE); + CL_ERR_TO_STR(CL_LINK_PROGRAM_FAILURE); + CL_ERR_TO_STR(CL_DEVICE_PARTITION_FAILED); + CL_ERR_TO_STR(CL_INVALID_PROPERTY); + CL_ERR_TO_STR(CL_INVALID_IMAGE_DESCRIPTOR); + CL_ERR_TO_STR(CL_INVALID_COMPILER_OPTIONS); + CL_ERR_TO_STR(CL_INVALID_LINKER_OPTIONS); + CL_ERR_TO_STR(CL_INVALID_DEVICE_PARTITION_COUNT); + + default: + return "UNKNOWN ERROR CODE"; + } + } + +/** +Check the OpenCL return code for errors. +If an error is detected, it will be printed and the programm execution is +stopped. + +@param err The OpenCL error code +*/ + void + handleClReturnCode(cl_int const err, std::string const file, + int const line) { + if (err != CL_SUCCESS) { + std::string err_string = getCLErrorString(err); + std::cerr << "ERROR in OpenCL library detected! Aborting." + << std::endl << file << ":" << line << ": " << err_string + << std::endl; + exit(err); + } + } + +/** +Sets up the given FPGA with the kernel in the provided file. + +@param context The context used for the program +@param program The devices used for the program +@param usedKernelFile The path to the kernel file +@return The program that is used to create the benchmark kernels +*/ + cl::Program + fpgaSetup(const cl::Context *context, std::vector deviceList, + const std::string *usedKernelFile) { + int err; + + std::cout << HLINE; + std::cout << "FPGA Setup:" << usedKernelFile->c_str() << std::endl; + + // Open file stream if possible + std::ifstream aocxStream(usedKernelFile->c_str(), std::ifstream::binary); + if (!aocxStream.is_open()) { + std::cerr << "Not possible to open from given file!" << std::endl; + } + + // Read in file contents and create program from binaries + std::string prog(std::istreambuf_iterator(aocxStream), + (std::istreambuf_iterator())); + aocxStream.seekg(0, aocxStream.end); + unsigned file_size = aocxStream.tellg(); + aocxStream.seekg(0, aocxStream.beg); + char *buf = new char[file_size]; + aocxStream.read(buf, file_size); + + cl::Program::Binaries mybinaries; + mybinaries.push_back({buf, file_size}); + + // Create the Program from the AOCX file. + cl::Program program(*context, deviceList, mybinaries, NULL, &err); + ASSERT_CL(err); + std::cout << "Prepared FPGA successfully for global Execution!" << + std::endl; + std::cout << HLINE; + return program; + } + +/** +Sets up the C++ environment by configuring std::cout and checking the clock +granularity using bm_helper::checktick() +*/ + void + setupEnvironmentAndClocks() { + std::cout << std::setprecision(5) << std::scientific; + + std::cout << HLINE; + std::cout << "General setup:" << std::endl; + + // Check clock granularity and output result + std::cout << "C++ high resolution clock is used." << std::endl; + std::cout << "The clock precision seems to be " + << static_cast + (std::chrono::high_resolution_clock::period::num) / + std::chrono::high_resolution_clock::period::den * 10e9 + << "ns" << std::endl; + + std::cout << HLINE; + } + + +/** +Searches an selects an FPGA device using the CL library functions. +If multiple platforms or devices are given, the user will be prompted to +choose a device. + +@param defaultPlatform The index of the platform that has to be used. If a + value < 0 is given, the platform can be chosen + interactively +@param defaultDevice The index of the device that has to be used. If a + value < 0 is given, the device can be chosen + interactively + +@return A list containing a single selected device +*/ + std::vector + selectFPGADevice(int defaultPlatform, int defaultDevice) { + // Integer used to store return codes of OpenCL library calls + int err; + + std::vector platformList; + err = cl::Platform::get(&platformList); + ASSERT_CL(err); + + // Choose the target platform + int chosenPlatformId = 0; + if (defaultPlatform >= 0) { + if (defaultPlatform < platformList.size()) { + chosenPlatformId = defaultPlatform; + } else { + std::cerr << "Default platform " << defaultPlatform + << " can not be used. Available platforms: " + << platformList.size() << std::endl; + exit(1); + } + } else if (platformList.size() > 1) { + std::cout << + "Multiple platforms have been found. Select the platform by"\ + " typing a number:" << std::endl; + for (int platformId = 0; + platformId < platformList.size(); platformId++) { + std::cout << platformId << ") " << + platformList[platformId].getInfo() << + std::endl; + } + std::cout << "Enter platform id [0-" << platformList.size() - 1 + << "]:"; + std::cin >> chosenPlatformId; + } + cl::Platform platform = platformList[chosenPlatformId]; + std::cout << "Selected Platform: " + << platform.getInfo() << std::endl; + + std::vector deviceList; + err = platform.getDevices(CL_DEVICE_TYPE_ACCELERATOR, &deviceList); + ASSERT_CL(err); + + // Choose taget device + int chosenDeviceId = 0; + if (defaultDevice >= 0) { + if (defaultDevice < deviceList.size()) { + chosenDeviceId = defaultDevice; + } else { + std::cerr << "Default platform " << defaultDevice + << " can not be used. Available platforms: " + << deviceList.size() << std::endl; + exit(1); + } + } else if (deviceList.size() > 1) { + std::cout << + "Multiple devices have been found. Select the platform by"\ + " typing a number:" << std::endl; + for (int deviceId = 0; + deviceId < deviceList.size(); deviceId++) { + std::cout << deviceId << ") " << + deviceList[deviceId].getInfo() << + std::endl; + } + std::cout << "Enter device id [0-" << deviceList.size() - 1 << "]:"; + std::cin >> chosenDeviceId; + } + std::vector chosenDeviceList; + chosenDeviceList.push_back(deviceList[chosenDeviceId]); + + // Give selection summary + std::cout << HLINE; + std::cout << "Selection summary:" << std::endl; + std::cout << "Platform Name: " << + platform.getInfo() << std::endl; + std::cout << "Device Name: " << + chosenDeviceList[0].getInfo() << std::endl; + std::cout << HLINE; + + return chosenDeviceList; + } + +} // namespace fpga_setup \ No newline at end of file diff --git a/FFT/src/host/setup/fpga_setup.hpp b/FFT/src/host/setup/fpga_setup.hpp new file mode 100644 index 00000000..c1f78b0c --- /dev/null +++ b/FFT/src/host/setup/fpga_setup.hpp @@ -0,0 +1,108 @@ +/* +Copyright (c) 2019 Marius Meyer + +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 SRC_HOST_FPGA_SETUP_H_ +#define SRC_HOST_FPGA_SETUP_H_ + +#include +#include +#include +#include +#include +#include + +/* External libraries */ +#include "CL/cl.hpp" + +/** +Makro to convert the error integer representation to its string representation +Source: https://gist.github.com/allanmac/9328bb2d6a99b86883195f8f78fd1b93 +*/ +#define CL_ERR_TO_STR(err) case err: return #err + +namespace fpga_setup { + + /** +Converts the reveived OpenCL error to a string + +@param err The OpenCL error code + +@return The string representation of the OpenCL error code +*/ + std::string + getCLErrorString(cl_int const err); + +/** +Check the OpenCL return code for errors. +If an error is detected, it will be printed and the programm execution is +stopped. + +@param err The OpenCL error code +*/ + void + handleClReturnCode(cl_int const err, std::string const file, + int const line); + +/** +Makro that enables checks for OpenCL errors with handling of the file and +line number. +*/ +#define ASSERT_CL(err) fpga_setup::handleClReturnCode(err, __FILE__, __LINE__) + +/** +Sets up the given FPGA with the kernel in the provided file. + +@param context The context used for the program +@param program The devices used for the program +@param usedKernelFile The path to the kernel file +@return The program that is used to create the benchmark kernels +*/ + cl::Program + fpgaSetup(const cl::Context *context, std::vector deviceList, + const std::string *usedKernelFile); + +/** +Sets up the C++ environment by configuring std::cout and checking the clock +granularity using bm_helper::checktick() +*/ + void + setupEnvironmentAndClocks(); + + +/** +Searches an selects an FPGA device using the CL library functions. +If multiple platforms or devices are given, the user will be prompted to +choose a device. + +@param defaultPlatform The index of the platform that has to be used. If a + value < 0 is given, the platform can be chosen + interactively +@param defaultDevice The index of the device that has to be used. If a + value < 0 is given, the device can be chosen + interactively + +@return A list containing a single selected device +*/ + std::vector + selectFPGADevice(int defaultPlatform, int defaultDevice); + +} // namespace fpga_setup +#endif // SRC_HOST_FPGA_SETUP_H_ diff --git a/FFT/synth-scripts/synth-fft-noctua.sh b/FFT/synth-scripts/synth-fft-noctua.sh new file mode 100644 index 00000000..3f22e1d0 --- /dev/null +++ b/FFT/synth-scripts/synth-fft-noctua.sh @@ -0,0 +1,20 @@ +#!/bin/bash +#SBATCH -p fpgasyn + +module load devel/CMake +module load intelFPGA_pro/19.4.0 +module load nalla_pcie/19.2.0_hpc + +LOG_FFT_SIZE=12 + + +BUILD_DIR=build-${QUARTUS_VERSION}-${QUARTUS_VERSION_BSP}-${LOG_FFT_SIZE} + +mkdir ../${BUILD_DIR} +cd ../${BUILD_DIR} + +cmake .. -DLOG_FFT_SIZE=${LOG_FFT_SIZE} -DFPGA_BOARD_NAME=${FPGA_BOARD_NAME} + +make fft1d_float_8 + + diff --git a/FFT/tests/CMakeLists.txt b/FFT/tests/CMakeLists.txt new file mode 100755 index 00000000..892adfe7 --- /dev/null +++ b/FFT/tests/CMakeLists.txt @@ -0,0 +1,15 @@ + +add_compile_options(--std=c++11) +# 'lib' is the folder with Google Test sources +add_subdirectory(../../extern/googletest ${CMAKE_CURRENT_BINARY_DIR}/lib) +include_directories(${gtest_SOURCE_DIR}/include ${gtest_SOURCE_DIR}) +include_directories(${CMAKE_BINARY_DIR}/src/common ../../extern/cxxopts/include) +include_directories(SYSTEM ${IntelFPGAOpenCL_INCLUDE_DIRS}) + +set(PROJECT_SOURCES ../src/host/setup/fpga_setup.cpp ../src/host/execution_default.cpp ../src/host/fft_functionality.cpp) +set(TEST_SOURCES test_fpga_setup.cpp "test_fft_functionality.cpp" test_execution_functionality.cpp) + +add_executable(Google_Tests_run ${TEST_SOURCES} ${PROJECT_SOURCES}) +target_link_libraries(Google_Tests_run gtest gmock gtest_main ${IntelFPGAOpenCL_LIBRARIES}) +add_dependencies(Google_Tests_run ${CMAKE_BINARY_DIR}/src/common/parameters.h) +add_dependencies(Google_Tests_run fft1d_float_8_emulate) \ No newline at end of file diff --git a/FFT/tests/test_execution_functionality.cpp b/FFT/tests/test_execution_functionality.cpp new file mode 100644 index 00000000..fe454a6a --- /dev/null +++ b/FFT/tests/test_execution_functionality.cpp @@ -0,0 +1,240 @@ +// +// Created by Marius Meyer on 04.12.19. +// +#include + +#include "gtest/gtest.h" +#include "../src/host/execution.h" +#include "parameters.h" +#include "../src/host/setup/fpga_setup.hpp" +#include "../src/host/fft_functionality.hpp" + + +struct OpenCLKernelTest : testing::Test { + std::string kernelFileName = "fft1d_float_8_emulate.aocx"; + std::shared_ptr config; + unsigned repetitions = 10; + + void setupFPGA() { + std::vector device = fpga_setup::selectFPGADevice(DEFAULT_PLATFORM, DEFAULT_DEVICE); + cl::Context context(device[0]); + cl::Program program = fpga_setup::fpgaSetup(&context, device, &kernelFileName); + config = std::make_shared( + bm_execution::ExecutionConfiguration{ + context, device[0], program, + repetitions + }); + } +}; + +/** + * Parametrized test takes a tuple of 1 parameter: + * - name of the emulation bitstream + */ +struct DifferentOpenCLKernelTest : OpenCLKernelTest, testing::WithParamInterface { + DifferentOpenCLKernelTest() { + auto params = GetParam(); + kernelFileName = params; + setupFPGA(); + } +}; + + +/** + * Tests if calculate returns the correct execution results + */ +TEST_P(DifferentOpenCLKernelTest, CalculateReturnsCorrectExecutionResultFor11False) { + config->repetitions = 1; + std::complex * data; + posix_memalign(reinterpret_cast(&data), 64, sizeof(std::complex) * (1 << LOG_FFT_SIZE)); + auto result = bm_execution::calculate(config, data, 1, false); + EXPECT_EQ(1, result->iterations); + EXPECT_EQ(false, result->inverse); + EXPECT_EQ(1, result->calculationTimings.size()); +} + +/** + * Tests if calculate returns the correct execution results for multiple repetitions + */ +TEST_P(DifferentOpenCLKernelTest, CalculateReturnsCorrectExecutionResultFor24True) { + config->repetitions = 2; + std::complex * data; + posix_memalign(reinterpret_cast(&data), 64, sizeof(std::complex) * (1 << LOG_FFT_SIZE) * 4); + auto result = bm_execution::calculate(config, data, 4, true); + EXPECT_EQ(4, result->iterations); + EXPECT_EQ(true, result->inverse); + EXPECT_EQ(2, result->calculationTimings.size()); +} + +/** + * Check if FFT of zeros returns just zeros + */ +TEST_P (DifferentOpenCLKernelTest, FFTReturnsZero) { + std::complex * data; + posix_memalign(reinterpret_cast(&data), 64, sizeof(std::complex) * (1 << LOG_FFT_SIZE)); + for (int i=0; i<(1 << LOG_FFT_SIZE); i++) { + data[i].real(0.0); + data[i].imag(0.0); + } + auto result = bm_execution::calculate(config, data, 1, false); + for (int i=0; i<(1 << LOG_FFT_SIZE); i++) { + EXPECT_FLOAT_EQ(std::abs(data[i]), 0.0); + } + free(data); +} + + +/** + * Check if FFT calculates the correct result for all number being 1.0,1.0i + */ +TEST_P (DifferentOpenCLKernelTest, FFTCloseToZeroForAll1And1) { + std::complex * data; + posix_memalign(reinterpret_cast(&data), 64, sizeof(std::complex) * (1 << LOG_FFT_SIZE)); + for (int i=0; i<(1 << LOG_FFT_SIZE); i++) { + data[i].real(1.0); + data[i].imag(1.0); + } + auto result = bm_execution::calculate(config, data, 1, false); + EXPECT_NEAR(data[0].real(), (1 << LOG_FFT_SIZE), 0.00001); + EXPECT_NEAR(data[0].imag(), (1 << LOG_FFT_SIZE), 0.00001); + for (int i=1; i < (1 << LOG_FFT_SIZE); i++) { + EXPECT_NEAR(data[i].real(), 0.0, 0.00001); + EXPECT_NEAR(data[i].imag(), 0.0, 0.00001); + } + free(data); +} + + +/** +* Check if iFFT calculates the correct result for all number being 1.0,1.0i +*/ +TEST_P (DifferentOpenCLKernelTest, IFFTCloseToZeroForAll1And1) { + std::complex * data; + posix_memalign(reinterpret_cast(&data), 64, sizeof(std::complex) * (1 << LOG_FFT_SIZE)); + for (int i=0; i<(1 << LOG_FFT_SIZE); i++) { + data[i].real(1.0); + data[i].imag(0.0); + } + auto result = bm_execution::calculate(config, data, 1, true); + EXPECT_NEAR(data[0].real(), static_cast(1 << LOG_FFT_SIZE), 0.00001); + EXPECT_NEAR(data[0].imag(), 0.0, 0.00001); + for (int i=1; i < (1 << LOG_FFT_SIZE); i++) { + EXPECT_NEAR(data[i].real(), 0.0, 0.00001); + EXPECT_NEAR(data[i].imag(), 0.0, 0.00001); + } + free(data); +} + +/** + * Check if calling FFt and iFFT result in data that is close to the original data with small error + */ +TEST_P (DifferentOpenCLKernelTest, FFTandiFFTProduceResultCloseToSource) { + std::complex * data; + posix_memalign(reinterpret_cast(&data), 64, sizeof(std::complex) * (1 << LOG_FFT_SIZE)); + std::complex * verify_data; + posix_memalign(reinterpret_cast(&verify_data), 64, sizeof(std::complex) * (1 << LOG_FFT_SIZE)); + + generateInputData(data, 1); + generateInputData(verify_data, 1); + + auto result = bm_execution::calculate(config, data, 1, false); + + // Normalize iFFT result + for (int i=0; i<(1 << LOG_FFT_SIZE); i++) { + data[i] /= (1 << LOG_FFT_SIZE); + } + + // Need to again bit reverse input for iFFT + bit_reverse(data, 1); + auto result2 = bm_execution::calculate(config, data, 1, true); + // Since data was already sorted by iFFT the bit reversal of the kernel has t be undone + bit_reverse(data, 1); + + for (int i=1; i < (1 << LOG_FFT_SIZE); i++) { + EXPECT_NEAR(std::abs(data[i]), std::abs(verify_data[i]), 0.001); + } + free(data); + free(verify_data); +} + +/** + * Check the included FFT error function on the host code on data produced by FFT + */ +TEST_P (DifferentOpenCLKernelTest, FFTErrorCheck) { + std::complex * data; + posix_memalign(reinterpret_cast(&data), 64, sizeof(std::complex) * (1 << LOG_FFT_SIZE)); + std::complex * verify_data; + posix_memalign(reinterpret_cast(&verify_data), 64, sizeof(std::complex) * (1 << LOG_FFT_SIZE)); + + generateInputData(data, 1); + generateInputData(verify_data, 1); + + auto result = bm_execution::calculate(config, data, 1, false); + + // Need to again bit reverse input for iFFT + double error = checkFFTResult(verify_data, data, 1); + + EXPECT_NEAR(error, 0.0, 1.0); + + free(data); + free(verify_data); +} + +/** + * Check if FPGA FFT and reference FFT give the same results + */ +TEST_P (DifferentOpenCLKernelTest, FPGAFFTAndCPUFFTGiveSameResults) { + std::complex * data; + posix_memalign(reinterpret_cast(&data), 64, sizeof(std::complex) * (1 << LOG_FFT_SIZE)); + std::complex * data2; + posix_memalign(reinterpret_cast(&data2), 64, sizeof(std::complex) * (1 << LOG_FFT_SIZE)); + + generateInputData(data, 1); + generateInputData(data2, 1); + + auto result = bm_execution::calculate(config, data, 1, false); + + fourier_transform_gold(false,LOG_FFT_SIZE,data2); + bit_reverse(data2, 1); + + // Normalize iFFT result + for (int i=0; i<(1 << LOG_FFT_SIZE); i++) { + data[i] -= data2[i]; + } + for (int i=1; i < (1 << LOG_FFT_SIZE); i++) { + EXPECT_NEAR(std::abs(data[i]), 0.0, 0.001); + } + free(data); + free(data2); +} + +/** + * Check if FPGA iFFT and reference iFFT give the same results + */ +TEST_P (DifferentOpenCLKernelTest, FPGAiFFTAndCPUiFFTGiveSameResults) { + std::complex * data; + posix_memalign(reinterpret_cast(&data), 64, sizeof(std::complex) * (1 << LOG_FFT_SIZE)); + std::complex * data2; + posix_memalign(reinterpret_cast(&data2), 64, sizeof(std::complex) * (1 << LOG_FFT_SIZE)); + + generateInputData(data, 1); + generateInputData(data2, 1); + + auto result = bm_execution::calculate(config, data, 1, true); + + fourier_transform_gold(true,LOG_FFT_SIZE,data2); + bit_reverse(data2, 1); + + // Normalize iFFT result + for (int i=0; i<(1 << LOG_FFT_SIZE); i++) { + data[i] -= data2[i]; + } + for (int i=1; i < (1 << LOG_FFT_SIZE); i++) { + EXPECT_NEAR(std::abs(data[i]), 0.0, 0.001); + } + free(data); + free(data2); +} + +INSTANTIATE_TEST_CASE_P(Default, DifferentOpenCLKernelTest, + testing::Values("fft1d_float_8_emulate.aocx")); diff --git a/FFT/tests/test_fft_functionality.cpp b/FFT/tests/test_fft_functionality.cpp new file mode 100644 index 00000000..1d3f2e83 --- /dev/null +++ b/FFT/tests/test_fft_functionality.cpp @@ -0,0 +1,105 @@ +// +// Created by Marius Meyer on 20.01.20. +// + +#include "gtest/gtest.h" +#include "../src/host/fft_functionality.hpp" +#include "parameters.h" + + + +/** + * Check if data generator generates reproducable inputs + */ +TEST (FPGASetup, DataInputReproducible) { + auto *data1 = new std::complex[(1 << LOG_FFT_SIZE)]; + auto *data2 = new std::complex[(1 << LOG_FFT_SIZE)]; + generateInputData(data1, 1); + generateInputData(data2, 1); + for (int i=0; i < (1 << LOG_FFT_SIZE); i++) { + EXPECT_FLOAT_EQ(data1[i].real(), data2[i].real()); + EXPECT_FLOAT_EQ(data1[i].imag(), data2[i].imag()); + } + delete [] data1; + delete [] data2; +} + +/** + * Check if FFT of zeros returns just zeros + */ +TEST (FPGASetup, FFTReturnsZero) { + auto *data = new std::complex[(1 << LOG_FFT_SIZE)]; + for (int i=0; i<(1 << LOG_FFT_SIZE); i++) { + data[i].real(0.0); + data[i].imag(0.0); + } + fourier_transform_gold(false, LOG_FFT_SIZE, data); + for (int i=0; i<(1 << LOG_FFT_SIZE); i++) { + EXPECT_FLOAT_EQ(std::abs(data[i]), 0.0); + } + delete [] data; +} + + +/** + * Check if FFT calculates the correct result for all number being 1.0,1.0i + */ +TEST (FPGASetup, FFTCloseToZeroForAll1And1) { + auto *data = new std::complex[(1 << LOG_FFT_SIZE)]; + for (int i=0; i<(1 << LOG_FFT_SIZE); i++) { + data[i].real(1.0); + data[i].imag(1.0); + } + fourier_transform_gold(false, LOG_FFT_SIZE, data); + EXPECT_NEAR(data[0].real(), (1 << LOG_FFT_SIZE), 0.00001); + EXPECT_NEAR(data[0].imag(), (1 << LOG_FFT_SIZE), 0.00001); + for (int i=1; i < (1 << LOG_FFT_SIZE); i++) { + EXPECT_NEAR(data[i].real(), 0.0, 0.00001); + EXPECT_NEAR(data[i].imag(), 0.0, 0.00001); + } + delete [] data; +} + +/** +* Check if iFFT calculates the correct result for all number being 1.0,1.0i +*/ +TEST (FPGASetup, IFFTCloseToZeroForAll1And1) { + auto *data = new std::complex[(1 << LOG_FFT_SIZE)]; + for (int i=0; i<(1 << LOG_FFT_SIZE); i++) { + data[i].real(1.0); + data[i].imag(1.0); + } + fourier_transform_gold(true, LOG_FFT_SIZE, data); + EXPECT_NEAR(data[0].real(), (1 << LOG_FFT_SIZE), 0.00001); + EXPECT_NEAR(data[0].imag(), (1 << LOG_FFT_SIZE), 0.00001); + for (int i=1; i < (1 << LOG_FFT_SIZE); i++) { + EXPECT_NEAR(data[i].real(), 0.0, 0.00001); + EXPECT_NEAR(data[i].imag(), 0.0, 0.00001); + } + delete [] data; +} + + +/** + * Check if FFT and FFT check give low error when FFT is calculated directly + */ +TEST (FPGASetup, FFTandiFFTProduceResultCloseToSource) { + auto *data = new std::complex[(1 << LOG_FFT_SIZE)]; + auto *verify_data = new std::complex[(1 << LOG_FFT_SIZE)]; + generateInputData(data, 1); + generateInputData(verify_data, 1); + + fourier_transform_gold(false, LOG_FFT_SIZE, data); + fourier_transform_gold(true, LOG_FFT_SIZE, data); + + // Normalize iFFT result + for (int i=1; i < (1 << LOG_FFT_SIZE); i++) { + data[i] /= (1 << LOG_FFT_SIZE); + } + + for (int i=1; i < (1 << LOG_FFT_SIZE); i++) { + EXPECT_NEAR(std::abs(data[i]), std::abs(verify_data[i]), 0.001); + } + delete [] data; + delete [] verify_data; +} \ No newline at end of file diff --git a/FFT/tests/test_fpga_setup.cpp b/FFT/tests/test_fpga_setup.cpp new file mode 100644 index 00000000..fd29ebe9 --- /dev/null +++ b/FFT/tests/test_fpga_setup.cpp @@ -0,0 +1,34 @@ +// +// Created by Marius Meyer on 04.12.19 +// +#include "gtest/gtest.h" +#include "../src/host/setup/fpga_setup.hpp" +#include "parameters.h" + +/** + * Check if it is possible to find the platform and device that are given as default + */ +TEST (FPGASetup, FindValidPlatformAndDevice) { + EXPECT_EQ (1, fpga_setup::selectFPGADevice(DEFAULT_PLATFORM, DEFAULT_DEVICE).size()); +} + +/** + * Checks if non existing platform leads to an error + */ +TEST (FPGASetup, FindNonExistingPlatform) { + // TODO regex does not work so for now its not tested! + EXPECT_EXIT(fpga_setup::selectFPGADevice(DEFAULT_PLATFORM + 100, DEFAULT_DEVICE), + ::testing::ExitedWithCode(1), + ::testing::MatchesRegex(".*")); +} + +/** + * Checks if non existing device leads to an error + */ +TEST (FPGASetup, FindNonExistingDevice) { + // TODO regex does not work so for now its not tested! + EXPECT_EXIT(fpga_setup::selectFPGADevice(DEFAULT_PLATFORM, DEFAULT_DEVICE + 100), + ::testing::ExitedWithCode(1), + ::testing::MatchesRegex(".*")); +} + diff --git a/GEMM/.gitignore b/GEMM/.gitignore new file mode 100644 index 00000000..2e1b8ff9 --- /dev/null +++ b/GEMM/.gitignore @@ -0,0 +1,12 @@ +*/.DS_Store +.DS_Store +bin/* +generated/* +*.genlog +.vscode +libs/* +synth/* +*.zsh +*.sh +/.idea/ +/cmake-build-* diff --git a/GEMM/CMakeLists.txt b/GEMM/CMakeLists.txt new file mode 100755 index 00000000..c7e18cb8 --- /dev/null +++ b/GEMM/CMakeLists.txt @@ -0,0 +1,38 @@ +cmake_minimum_required(VERSION 2.8.4) +project(fGEMM) + +set(VERSION 0.1) +set(KERNEL_NAME gemm CACHE STRING "Name of the OpenCL kernel") +set(DEFAULT_REPETITIONS 10 CACHE STRING "Default number of repetitions") +set(DEFAULT_MATRIX_SIZE 4096 CACHE STRING "Default size of the used matrices") +set(DEFAULT_PLATFORM -1 CACHE STRING "Index of the default platform to use") +set(DEFAULT_DEVICE -1 CACHE STRING "Index of the default device to use") +set(BLOCK_SIZE 512 CACHE STRING "Block size used in the FPGA kernel") +set(GEMM_BLOCK 8 CACHE STRING "Block size used in the FPGA kernel for the cannon algorithm") +set(GLOBAL_MEM_UNROLL 16 CACHE STRING "Unrolling factor used to stream data") +set(DATA_TYPE float CACHE STRING "Data type used for calculation") + +set(FPGA_BOARD_NAME p520_hpc_sg280l CACHE STRING "Name of the target FPGA board") +set(AOC_FLAGS "-fpc -fp-relaxed" CACHE STRING "Used flags for the AOC compiler") +separate_arguments(AOC_FLAGS) + +set(HOST_DATA_TYPE cl_${DATA_TYPE}) +set(DEVICE_DATA_TYPE ${DATA_TYPE}) + +set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_SOURCE_DIR}/../extern/hlslib/cmake) +set(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin) + +configure_file( + "${CMAKE_SOURCE_DIR}/src/common/parameters.h.in" + "${CMAKE_BINARY_DIR}/src/common/parameters.h" +) + +include_directories(${CMAKE_BINARY_DIR}/src/common) + + +find_package(IntelFPGAOpenCL REQUIRED) + +add_subdirectory(src/device) +add_subdirectory(src/host) +add_subdirectory(tests) + diff --git a/GEMM/Readme.md b/GEMM/Readme.md new file mode 100644 index 00000000..ce39b7d5 --- /dev/null +++ b/GEMM/Readme.md @@ -0,0 +1,117 @@ +# GEMM Benchmark for FPGA + +This repository contains the GEMM Benchmark for FPGA and its OpenCL kernels. +Currently only the Intel FPGA SDK for OpenCL utility is supported. + +It is a modified implementation of the +[GEMM Benchmark](http://www.netlib.org/parkbench/html/matrix-kernels.html) +provided in the [HPC Challenge Benchmark](https://icl.utk.edu/hpcc/) suite. +The implementation follows the Python reference implementation given in +_Introduction to the HPCChallenge Benchmark Suite_ available +[here](http://icl.cs.utk.edu/news_pub/submissions/hpcc-challenge-intro.pdf). + +## Dependencies + +The benchmark comes with the following requirements for building and running: + +- CMake 2.8 +- GCC 4.9 +- Intel OpenCL FPGA SDK 19.3 + +It also contains submodules that will be automatically updated when running cmake: + +- cxxopts: A header only library to parse command line parameters +- googletest: A C++ test framework + +## Build + +CMake is used as the build system. +The targets below can be used to build the benchmark and its kernels: + + | Target | Description | + | -------- | ---------------------------------------------- | + | fgemm | Builds the host application | + | Google_Tests_run| Compile the tests and its dependencies | + + More over the are additional targets to generate kernel reports and bitstreams. + They are generated for every kernel code in the `src/device` folder: + + | Target | Description | + | -------- | ---------------------------------------------- | + | KERNEL_FILE_NAME | Synthesizes the kernel (takes several hours!) | + | KERNEL_FILE_NAME_report | Create an HTML report for the kernel | + | KERNEL_FILE_NAME_emulate | Create a n emulation kernel | + +The currently supported values for KERNEL_FILE_NAME are: + +- gemm_cannon + + You can build for example the host application by running + + mkdir build && cd build + cmake .. + make fgemm + +You will find all executables and kernel files in the `bin` +folder of your build directory. +You should always specify a target with make to reduce the build time! +You might want to specify predefined parameters before build: + +Name | Default | Description | +---------------- |-------------|--------------------------------------| + `DATA_TYPE` | float | Data type used for calculation | +`DEFAULT_DEVICE` | -1 | Index of the default device (-1 = ask) | +`DEFAULT_PLATFORM`| -1 | Index of the default platform (-1 = ask) | +`DEFAULT_REPETITIONS`| 10 | Number of times the kernel will be executed | +`KERNEL_NAME`| gemm | Name of the kernel (only needed for own implementations) | +`FPGA_BOARD_NAME`| p520_hpc_sg280l | Name of the target board | +`BLOCK_SIZE` | 512 | Block size used by the kernel to transpose the matrix | +`GEMM_SIZE` | 8 | Block size of the fully unrolled cannon block if cannon kernel is used | +`GLOBAL_MEM_UNROLL`| 16 | Unrolling factor for the global memory access | +`AOC_FLAGS`| `-fpc -fp-relaxed` | Additional AOC compiler flags that are used for kernel compilation | + +Moreover the environment variable `INTELFPGAOCLSDKROOT` has to be set to the root +of the Intel FPGA SDK installation. + +Additionally it is possible to set the used compiler and other build tools +in the `CMakeCache.txt` located in the build directory after running cmake. + + +## Execution + +For execution of the benchmark run: + + ./fgemm -f path_to_kernel.aocx + +For more information on available input parameters run + + ./fgemm -h + +To execute the unit and integration tests run + + ./Google_Tests_run + +in the `bin` folder within the build directory. +It will run an emulation of the kernel and execute some functionality tests. + +## Output Interpretation + +An example output from an emulation is given below: + + norm. resid resid machep + 1.45417e-05 4.76837e-05 1.19209e-07 + best mean GFLOPS + 6.89168e-03 6.89168e-03 1.03868e+02 + +The first two rows give information about the calculation error. + +- `norm. resid`: The normalized residual error based on the used matrix size and used values +- `resid`: The maximum residual error of the calculation +- `machep`: The machine epsilon + +The last two columns contain the time measurements and based on that the achieved FLOPS +of the calculation. + +- `best`: The shortest execution time in all runs +- `mean`: Arithmetic mean of all execution times +- `GFLOPS`: GFLOPS calculated from the shortest execution time diff --git a/GEMM/performance/README.md b/GEMM/performance/README.md new file mode 100755 index 00000000..cfa8e2c1 --- /dev/null +++ b/GEMM/performance/README.md @@ -0,0 +1,159 @@ +# Performance of the Single-Work-Item Kernel + +In the following the performance of the SWI cannon kernel is discussed. + +## Algorithm + +The kernel is structured into three levels. + +1. **register_gemm**: Multiplies two matrices fully unrolled in FPGA registers. Size of these matrices is defined by the build variable `GEMM_BLOCK`. +2. **local_mem_gemm**: Multiplies two matrices of size `BLOCK_SIZE` fully pipelined in local memory and scales the result. Uses internally **register_gemm**. +3. **gemm**: The first level multiplies two matrices that have a size multiple of `BLOCK_SIZE` in global memory and adds the result to a third matrix. Uses **local_gemm** for the multiplication. + +The host code will call **gemm** and the compute performance is depending on the size of `GEMM_BLOCK`, since only there the actual computation is done. + + +## Performance Model + +The calculation of a single block of the result matrix is fully pipelined. +Matrix multiplication and writeback to global memory are executed sequentially to reduce the memory replications and make it possible to store bigger matrices in local memory. + +For the calculation of the runtime we have to look at both. + +#### Theoretical Performance + +To multiply two matrix blocks they first have to be loaded from global memory and and then processed by the local memory GEMM. +The load operations can be calculated with the following formula not considering the latency: + +![t_{mempipeline}=\frac{s_{block}*s_{block}}{s_{bus}}](https://latex.codecogs.com/gif.latex?t_{mempipeline}=\frac{\frac{\(s_{block}\)^2}{s_{bus}}}{f}) + +where ![s_{block}](https://latex.codecogs.com/gif.latex?s_{block}) is the size of a row of the matrix block that has to be stored in local memory in bytes and +![s_{bus}](https://latex.codecogs.com/gif.latex?s_{bus}) the bus width of the global memory controller in bytes. +![f](https://latex.codecogs.com/gif.latex?f) is the kernel frequency. +Moreover latency will be added to this operation for every DRAM page that has to be activated: + +![t_{lat}=\frac{\(s_{block}\)^2}{s_{page}}*\(t_{RCD}+t_{RP}\)](https://latex.codecogs.com/gif.latex?t_{lat}=\frac{\(s_{block}\)^2}{s_{page}}*\(t_{RCD}+t_{RP}\)) + +where ![s_{page}](https://latex.codecogs.com/gif.latex?s_{page}) is the size of a DRAM page in bytes. +![t_{RCD}](https://latex.codecogs.com/gif.latex?t_{RCD}) and ![t_{RP}](https://latex.codecogs.com/gif.latex?t_{RP}) are the +row address to column address delay and the row precharge time of the used DRAM. + +So the total time for the memory accesses for a single block is: + +![t_{mem}=t_{mempipeline}+t_{lat}](https://latex.codecogs.com/gif.latex?t_{mem}=t_{mempipeline}+t_{lat}) + + +For the calculation we have to consider the blocked matrix multiplication from global to local memory as well as the blocked matrix multiplication from local memory to registers. +The calculation within the registers is fully unrolled and will only add a constant latency to the calculation: + +![t_{calc}=\frac{\(\frac{s_{block}}{s_{reg}}\)^3}{f}](https://latex.codecogs.com/gif.latex?t_{calc}=\frac{\(\frac{s_{block}}{s_{reg}}\)^3}{f}) + +where ![s_{reg}](https://latex.codecogs.com/gif.latex?s_{reg}) is the row of a register gemm block in bytes. + +If ![t_{mem} +#include + +/* External library headers */ +#include "CL/cl.hpp" + +#include "parameters.h" + +#ifndef BLOCK_SIZE +#define BLOCK_SIZE 32 +#endif + + +namespace bm_execution { + + struct ExecutionConfiguration { + cl::Context context; + cl::Device device; + cl::Program program; + std::string kernelName; + uint repetitions; + cl_uint matrixSize; + bool useMemInterleaving; + }; + + struct ExecutionTimings { + std::vector transferTimings; + std::vector calculationTimings; + }; + + +/** +The actual execution of the benchmark. +This method can be implemented in multiple *.cpp files. This header enables +simple exchange of the different calculation methods. + +@param context OpenCL context used to create needed Buffers and queues +@param device The OpenCL device that is used to execute the benchmarks +@param program The OpenCL program containing the kernels +@param repetitions Number of times the kernels are executed +@param replications Number of times a kernel is replicated - may be used in + different ways depending on the implementation of this + method +@param dataSize The size of the data array that may be used for benchmark + execution in number of items +@param blockSize Size of a block that is calculated by the kernel + +@return The time measurements and the error rate counted from the executions +*/ +std::shared_ptr +calculate(std::shared_ptr config, HOST_DATA_TYPE* a, HOST_DATA_TYPE* b, HOST_DATA_TYPE* c, + HOST_DATA_TYPE* c_out, HOST_DATA_TYPE alpha, HOST_DATA_TYPE beta); +} // namespace bm_execution + +#endif // SRC_HOST_EXECUTION_H_ diff --git a/GEMM/src/host/execution_cannon.cpp b/GEMM/src/host/execution_cannon.cpp new file mode 100644 index 00000000..54917d95 --- /dev/null +++ b/GEMM/src/host/execution_cannon.cpp @@ -0,0 +1,122 @@ +/* +Copyright (c) 2019 Marius Meyer + +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. +*/ + +/* Related header files */ +#include "execution.h" + +/* C++ standard library headers */ +#include +#include +#include +#include + +/* External library headers */ +#include "CL/cl.hpp" +#include "CL/cl_ext_intelfpga.h" + + +/* Project's headers */ +#include "setup/fpga_setup.hpp" +#include "gemm_functionality.hpp" + +namespace bm_execution { + +/* + Prepare kernels and execute benchmark + + @copydoc bm_execution::calculate() +*/ +std::shared_ptr +calculate(std::shared_ptr config, HOST_DATA_TYPE* a, HOST_DATA_TYPE* b, HOST_DATA_TYPE* c, HOST_DATA_TYPE* c_out, + HOST_DATA_TYPE alpha, HOST_DATA_TYPE beta) { + + int err; + + // Create Command queue + cl::CommandQueue compute_queue(config->context, config->device); + + cl::Buffer Buffer_a(config->context, CL_MEM_READ_WRITE | (config->useMemInterleaving ? 0 :CL_CHANNEL_1_INTELFPGA), + sizeof(HOST_DATA_TYPE)*config->matrixSize*config->matrixSize); + cl::Buffer Buffer_b(config->context, CL_MEM_READ_WRITE | (config->useMemInterleaving ? 0 :CL_CHANNEL_2_INTELFPGA), + sizeof(cl_int)*config->matrixSize*config->matrixSize); + cl::Buffer Buffer_c_in(config->context, CL_MEM_READ_WRITE | (config->useMemInterleaving ? 0 :CL_CHANNEL_3_INTELFPGA), + sizeof(cl_int)*config->matrixSize*config->matrixSize); + cl::Buffer Buffer_c_out(config->context, CL_MEM_READ_WRITE | (config->useMemInterleaving ? 0 :CL_CHANNEL_4_INTELFPGA), + sizeof(cl_int)*config->matrixSize*config->matrixSize); + + + // create the kernels + cl::Kernel gemmkernel(config->program, GEMM_KERNEL, + &err); + ASSERT_CL(err); + + + // prepare kernels + err = gemmkernel.setArg(0, Buffer_a); + ASSERT_CL(err); + err = gemmkernel.setArg(1, Buffer_b); + ASSERT_CL(err); + err = gemmkernel.setArg(2, Buffer_c_in); + ASSERT_CL(err); + err = gemmkernel.setArg(3, Buffer_c_out); + ASSERT_CL(err); + err = gemmkernel.setArg(4, alpha); + ASSERT_CL(err); + err = gemmkernel.setArg(5, beta); + ASSERT_CL(err); + err = gemmkernel.setArg(6, config->matrixSize); + ASSERT_CL(err); + + /* --- Execute actual benchmark kernels --- */ + + double t; + std::vector executionTimes; + for (int i = 0; i < config->repetitions; i++) { + compute_queue.enqueueWriteBuffer(Buffer_a, CL_TRUE, 0, + sizeof(HOST_DATA_TYPE)*config->matrixSize*config->matrixSize, a); + compute_queue.enqueueWriteBuffer(Buffer_b, CL_TRUE, 0, + sizeof(HOST_DATA_TYPE)*config->matrixSize*config->matrixSize, b); + compute_queue.enqueueWriteBuffer(Buffer_c_in, CL_TRUE, 0, + sizeof(HOST_DATA_TYPE)*config->matrixSize*config->matrixSize, c); + compute_queue.finish(); + auto t1 = std::chrono::high_resolution_clock::now(); + compute_queue.enqueueTask(gemmkernel); + compute_queue.finish(); + auto t2 = std::chrono::high_resolution_clock::now(); + std::chrono::duration timespan = + std::chrono::duration_cast> + (t2 - t1); + executionTimes.push_back(timespan.count()); + } + + /* --- Read back results from Device --- */ + + compute_queue.enqueueReadBuffer(Buffer_c_out, CL_TRUE, 0, + sizeof(HOST_DATA_TYPE)*config->matrixSize*config->matrixSize, c_out); + + + std::shared_ptr results( + new ExecutionTimings{executionTimes, executionTimes}); + return results; +} + +} // namespace bm_execution diff --git a/GEMM/src/host/gemm_functionality.cpp b/GEMM/src/host/gemm_functionality.cpp new file mode 100644 index 00000000..d7f805cc --- /dev/null +++ b/GEMM/src/host/gemm_functionality.cpp @@ -0,0 +1,227 @@ +/* +Copyright (c) 2019 Marius Meyer + +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. +*/ + +/* Related header files */ +#include "gemm_functionality.hpp" + +/* C++ standard library headers */ +#include +#include +#include +#include +#include +#include +#include +#include + +/* External library headers */ +#include "CL/cl.hpp" +#if QUARTUS_MAJOR_VERSION > 18 +#include "CL/cl_ext_intelfpga.h" +#endif +#include "cxxopts.hpp" + +/* Project's headers */ +#include "parameters.h" +#include "execution.h" + + +/** +Parses and returns program options using the cxxopts library. +Supports the following parameters: + - file name of the FPGA kernel file (-f,--file) + - number of repetitions (-n) + - number of kernel replications (-r) + - data size (-d) + - use memory interleaving +@see https://github.com/jarro2783/cxxopts + +@return program settings that are created from the given program arguments +*/ +std::shared_ptr +parseProgramParameters(int argc, char * argv[]) { + // Defining and parsing program options + cxxopts::Options options(argv[0], PROGRAM_DESCRIPTION); + options.add_options() + ("f,file", "Kernel file name", cxxopts::value()) + ("n", "Number of repetitions", + cxxopts::value()->default_value(std::to_string(DEFAULT_REPETITIONS))) + ("m", "Matrix size", + cxxopts::value()->default_value(std::to_string(DEFAULT_MATRIX_SIZE))) + ("kernel", "Name of the kernel", + cxxopts::value()->default_value(KERNEL_NAME)) + ("i,nointerleaving", "Disable memory interleaving") + ("device", "Index of the device that has to be used. If not given you "\ + "will be asked which device to use if there are multiple devices "\ + "available.", cxxopts::value()->default_value(std::to_string(DEFAULT_DEVICE))) + ("platform", "Index of the platform that has to be used. If not given "\ + "you will be asked which platform to use if there are multiple "\ + "platforms available.", + cxxopts::value()->default_value(std::to_string(DEFAULT_PLATFORM))) + ("h,help", "Print this help"); + cxxopts::ParseResult result = options.parse(argc, argv); + + // Check parsed options and handle special cases + if (result.count("f") <= 0) { + // Path to the kernel file is mandatory - exit if not given! + std::cerr << "Kernel file must be given! Aborting" << std::endl; + std::cout << options.help() << std::endl; + exit(1); + } + if (result.count("h")) { + // Just print help when argument is given + std::cout << options.help() << std::endl; + exit(0); + } + + // Create program settings from program arguments + std::shared_ptr sharedSettings( + new ProgramSettings{result["n"].as(), + result["m"].as(), + result["platform"].as(), + result["device"].as(), + static_cast(result.count("i") <= 0), + result["f"].as(), + result["kernel"].as()}); + return sharedSettings; +} + +/** +Print the benchmark Results + +@param results The result struct provided by the calculation call +@param dataSize The size of the used data array + +*/ +void printResults(std::shared_ptr results, + size_t dataSize) { + std::cout << std::setw(ENTRY_SPACE) + << "best" << std::setw(ENTRY_SPACE) << "mean" + << std::setw(ENTRY_SPACE) << "GFLOPS" << std::endl; + + // Calculate performance for kernel execution plus data transfer + double tmean = 0; + double tmin = std::numeric_limits::max(); + + double gflops = 2.0 * static_cast(dataSize*dataSize*dataSize)/1.0e9; + for (double currentTime : results->calculationTimings) { + tmean += currentTime; + if (currentTime < tmin) { + tmin = currentTime; + } + } + tmean = tmean / results->calculationTimings.size(); + + std::cout << std::setw(ENTRY_SPACE) + << tmin << std::setw(ENTRY_SPACE) << tmean + << std::setw(ENTRY_SPACE) << gflops / tmin + << std::endl; +} + +void matgen(HOST_DATA_TYPE* a, int seed, cl_int lda, cl_int n, + HOST_DATA_TYPE* norma) { + std::mt19937 gen(seed); + std::uniform_real_distribution<> dis(-1.0, 1.0); + for (int j = 0; j < n; j++) { + for (int i = 0; i < n; i++) { + a[lda*i+j] = dis(gen); + *norma = (a[lda*i+j] > *norma) ? a[lda*i+j] : *norma; + } + for (int i = n; i < lda; i++) { + a[lda*j+i] = 0; + } + } +} + +void gemm_ref(HOST_DATA_TYPE* a, HOST_DATA_TYPE* b, HOST_DATA_TYPE* c_in, HOST_DATA_TYPE* c_out, + int n, HOST_DATA_TYPE alpha, HOST_DATA_TYPE beta) { + for (int i=0; i < n; i++) { + for (int j=0; j < n; j++) { + c_out[i * n + j] = beta * c_in[i*n + j]; + } + } + + for (int i=0; i < n; i++) { + for (int j=0; j < n; j++) { + for (int k=0; k < n; k++) { + c_out[i*n + j] += alpha * a[i*n + k] * b[k*n + j]; + } + } + } +} + +double +checkGEMMresults(HOST_DATA_TYPE* c_res, cl_int lda, cl_int n) { + HOST_DATA_TYPE* a = new HOST_DATA_TYPE[lda*n]; + HOST_DATA_TYPE* b = new HOST_DATA_TYPE[lda*n]; + HOST_DATA_TYPE* c = new HOST_DATA_TYPE[lda*n]; + HOST_DATA_TYPE totalnorm = 0.0; + + /* compute a residual to verify results. */ + + + matgen(a, 1, lda, n, &totalnorm); + matgen(b, 2, lda, n, &totalnorm); + matgen(c, 3, lda, n, &totalnorm); + gemm_ref(a, b, c, c, n, 0.5, 2.0); + HOST_DATA_TYPE resid = 0.0; + HOST_DATA_TYPE normx = 0.0; + + for (int i = 0; i < n * n; i++) { + resid = (resid > fabs(c_res[i] - c[i])) ? resid : fabs(c_res[i] - c[i]); + normx = (normx > fabs(c_res[i])) ? normx : fabs(c_res[i]); + } + + HOST_DATA_TYPE eps = epslon(static_cast(1.0)); + HOST_DATA_TYPE residn = resid / (lda*n*totalnorm*normx*eps); + + std::cout << " norm. resid resid "\ + "machep" << std::endl; + std::cout << std::setw(ENTRY_SPACE) << residn << std::setw(ENTRY_SPACE) + << resid << std::setw(ENTRY_SPACE) << eps + << std::endl; + + delete a; + delete b; + delete c; + return residn; +} + +HOST_DATA_TYPE epslon(HOST_DATA_TYPE x) { + HOST_DATA_TYPE a, b, c, eps; + + a = 4.0e0/3.0e0; + eps = 0.0; + while (eps == 0.0) { + b = a - 1.0; + c = b + b + b; + eps = fabs(static_cast(c-1.0)); + } + return (eps*fabs(static_cast(x))); +} + + +/** +The program entry point. +Prepares the FPGA and executes the kernels on the device. +*/ + diff --git a/GEMM/src/host/gemm_functionality.hpp b/GEMM/src/host/gemm_functionality.hpp new file mode 100644 index 00000000..ff17335b --- /dev/null +++ b/GEMM/src/host/gemm_functionality.hpp @@ -0,0 +1,115 @@ +/* +Copyright (c) 2019 Marius Meyer + +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 COMMON_FUNCTIONALITY_H +#define COMMON_FUNCTIONALITY_H + +/* C++ standard library headers */ +#include + +/* Project's headers */ +#include "execution.h" + +/* +Short description of the program +*/ +#define PROGRAM_DESCRIPTION "Implementation of the GEMM benchmark"\ + " proposed in the HPCC benchmark adapted for FPGA" + +/* +Number of times the execution of the benchmark will be repeated. +*/ +#ifndef NTIMES +#define NTIMES 1 +#endif + +/* +Prefix of the function name of the used kernel. +It will be used to construct the full function name for the case of replications. +The full name will be +*/ +#define GEMM_KERNEL "gemm" + +#define ENTRY_SPACE 13 + +struct ProgramSettings { + uint numRepetitions; + cl_uint matrixSize; + int defaultPlatform; + int defaultDevice; + bool useMemInterleaving; + std::string kernelFileName; + std::string kernelName; +}; + +double +checkGEMMresults(HOST_DATA_TYPE* c_res, cl_int lda, cl_int n); + +/** +Parses and returns program options using the cxxopts library. +Supports the following parameters: + - file name of the FPGA kernel file (-f,--file) + - number of repetitions (-n) + - number of kernel replications (-r) + - data size (-d) + - use memory interleaving +@see https://github.com/jarro2783/cxxopts + +@return program settings that are created from the given program arguments +*/ +std::shared_ptr +parseProgramParameters(int argc, char * argv[]); + +/** +Print the benchmark results to stdout + +@param results the struct containing the results of the benchmark execution +@param matrixSize size of the calculated matrix +*/ +void printResults(std::shared_ptr results, + size_t matrixSize); + +/** +Generate a matrix using pseudo random numbers with fixed seed. +Generates inuts for th + + +@param a pointer to the matrix +@param seed Seed for the pseudo random number generation +@param lda width of a row in the matrix +@param n number of rows in the matrix +@param norma the maximum value in the matrix A that can be used to calculate the residual error +*/ +void matgen(HOST_DATA_TYPE* a, int seed, cl_int lda, cl_int n, HOST_DATA_TYPE* norma); + +/** +Multiply matrix with a vector and add it to another vector. + +// TODO add docs +*/ +void gemm_ref(HOST_DATA_TYPE* a, HOST_DATA_TYPE* b, HOST_DATA_TYPE* c_in, HOST_DATA_TYPE* c_out, + int n, HOST_DATA_TYPE alpha, HOST_DATA_TYPE beta); + +double checkLINPACKresults (HOST_DATA_TYPE* b_res, cl_int lda, cl_int n); + +HOST_DATA_TYPE epslon (HOST_DATA_TYPE x); + +#endif // SRC_HOST_COMMON_FUNCTIONALITY_H_ diff --git a/GEMM/src/host/main.cpp b/GEMM/src/host/main.cpp new file mode 100644 index 00000000..d5573107 --- /dev/null +++ b/GEMM/src/host/main.cpp @@ -0,0 +1,86 @@ +// +// Created by Marius Meyer on 04.12.19. +// + +#include "parameters.h" +#include "gemm_functionality.hpp" +#include "setup/fpga_setup.hpp" + +/** +The program entry point +*/ +int main(int argc, char * argv[]) { + // Setup benchmark + std::shared_ptr programSettings = + parseProgramParameters(argc, argv); + fpga_setup::setupEnvironmentAndClocks(); + std::vector usedDevice = + fpga_setup::selectFPGADevice(programSettings->defaultPlatform, + programSettings->defaultDevice); + cl::Context context = cl::Context(usedDevice); + cl::Program program = fpga_setup::fpgaSetup(&context, usedDevice, + &programSettings->kernelFileName); + + // Give setup summary + std::cout << "Summary:" << std::endl + << "Kernel Repetitions: " << programSettings->numRepetitions + << std::endl + << "Total matrix size: " << programSettings->matrixSize + << std::endl + << "Memory Interleaving: " << programSettings->useMemInterleaving + << std::endl + << "Kernel file: " << programSettings->kernelFileName + << std::endl + << "Device: " + << usedDevice[0].getInfo() << std::endl + << HLINE + << "Start benchmark using the given configuration." << std::endl + << HLINE; + + std::shared_ptr config( + new bm_execution::ExecutionConfiguration{ + context, usedDevice[0], program, + programSettings->kernelName, + programSettings->numRepetitions, + programSettings->matrixSize, + programSettings->useMemInterleaving + }); + + uint lda = config->matrixSize; + HOST_DATA_TYPE* a; + posix_memalign(reinterpret_cast(&a), 64, + sizeof(HOST_DATA_TYPE)*lda*config->matrixSize); + HOST_DATA_TYPE* b; + posix_memalign(reinterpret_cast(&b), 64, + sizeof(HOST_DATA_TYPE)*lda*config->matrixSize); + HOST_DATA_TYPE* c; + posix_memalign(reinterpret_cast(&c), 64, + sizeof(HOST_DATA_TYPE)*lda*config->matrixSize); + HOST_DATA_TYPE* c_out; + posix_memalign(reinterpret_cast(&c_out), 64, + sizeof(HOST_DATA_TYPE)*lda*config->matrixSize); + + HOST_DATA_TYPE alpha = 0.5; + HOST_DATA_TYPE beta = 2.0; + + HOST_DATA_TYPE norma; + matgen(a, 1, config->matrixSize, config->matrixSize, &norma); + matgen(b, 2, config->matrixSize, config->matrixSize, &norma); + matgen(c, 3, config->matrixSize, config->matrixSize, &norma); + + // Start actual benchmark + auto results = bm_execution::calculate(config, a, b, c , c_out, alpha, beta); + + /* --- Check Results --- */ + + double error = checkGEMMresults(c_out, config->matrixSize, config->matrixSize); + + free(reinterpret_cast(a)); + free(reinterpret_cast(b)); + free(reinterpret_cast(c)); + + printResults(results, programSettings->matrixSize); + + return error > 0.1 ? 1 : 0; +} + diff --git a/GEMM/src/host/setup/fpga_setup.cpp b/GEMM/src/host/setup/fpga_setup.cpp new file mode 100644 index 00000000..07845901 --- /dev/null +++ b/GEMM/src/host/setup/fpga_setup.cpp @@ -0,0 +1,273 @@ +// +// Created by Marius Meyer on 04.12.19. +// + +#include "fpga_setup.hpp" + +#include +#include +#include +#include +#include +#include + +/* External libraries */ +#include "parameters.h" + +namespace fpga_setup { + + /** +Converts the reveived OpenCL error to a string + +@param err The OpenCL error code + +@return The string representation of the OpenCL error code +*/ + std::string + getCLErrorString(cl_int const err) { + switch (err) { + CL_ERR_TO_STR(CL_DEVICE_NOT_FOUND); + CL_ERR_TO_STR(CL_DEVICE_NOT_AVAILABLE); + CL_ERR_TO_STR(CL_COMPILER_NOT_AVAILABLE); + CL_ERR_TO_STR(CL_MEM_OBJECT_ALLOCATION_FAILURE); + CL_ERR_TO_STR(CL_OUT_OF_RESOURCES); + CL_ERR_TO_STR(CL_OUT_OF_HOST_MEMORY); + CL_ERR_TO_STR(CL_PROFILING_INFO_NOT_AVAILABLE); + CL_ERR_TO_STR(CL_MEM_COPY_OVERLAP); + CL_ERR_TO_STR(CL_IMAGE_FORMAT_MISMATCH); + CL_ERR_TO_STR(CL_IMAGE_FORMAT_NOT_SUPPORTED); + CL_ERR_TO_STR(CL_BUILD_PROGRAM_FAILURE); + CL_ERR_TO_STR(CL_MAP_FAILURE); + CL_ERR_TO_STR(CL_MISALIGNED_SUB_BUFFER_OFFSET); + CL_ERR_TO_STR(CL_EXEC_STATUS_ERROR_FOR_EVENTS_IN_WAIT_LIST); + CL_ERR_TO_STR(CL_KERNEL_ARG_INFO_NOT_AVAILABLE); + CL_ERR_TO_STR(CL_INVALID_VALUE); + CL_ERR_TO_STR(CL_INVALID_DEVICE_TYPE); + CL_ERR_TO_STR(CL_INVALID_PLATFORM); + CL_ERR_TO_STR(CL_INVALID_DEVICE); + CL_ERR_TO_STR(CL_INVALID_CONTEXT); + CL_ERR_TO_STR(CL_INVALID_QUEUE_PROPERTIES); + CL_ERR_TO_STR(CL_INVALID_COMMAND_QUEUE); + CL_ERR_TO_STR(CL_INVALID_HOST_PTR); + CL_ERR_TO_STR(CL_INVALID_MEM_OBJECT); + CL_ERR_TO_STR(CL_INVALID_IMAGE_FORMAT_DESCRIPTOR); + CL_ERR_TO_STR(CL_INVALID_IMAGE_SIZE); + CL_ERR_TO_STR(CL_INVALID_SAMPLER); + CL_ERR_TO_STR(CL_INVALID_BINARY); + CL_ERR_TO_STR(CL_INVALID_BUILD_OPTIONS); + CL_ERR_TO_STR(CL_INVALID_PROGRAM); + CL_ERR_TO_STR(CL_INVALID_PROGRAM_EXECUTABLE); + CL_ERR_TO_STR(CL_INVALID_KERNEL_NAME); + CL_ERR_TO_STR(CL_INVALID_KERNEL_DEFINITION); + CL_ERR_TO_STR(CL_INVALID_KERNEL); + CL_ERR_TO_STR(CL_INVALID_ARG_INDEX); + CL_ERR_TO_STR(CL_INVALID_ARG_VALUE); + CL_ERR_TO_STR(CL_INVALID_ARG_SIZE); + CL_ERR_TO_STR(CL_INVALID_KERNEL_ARGS); + CL_ERR_TO_STR(CL_INVALID_WORK_DIMENSION); + CL_ERR_TO_STR(CL_INVALID_WORK_GROUP_SIZE); + CL_ERR_TO_STR(CL_INVALID_WORK_ITEM_SIZE); + CL_ERR_TO_STR(CL_INVALID_GLOBAL_OFFSET); + CL_ERR_TO_STR(CL_INVALID_EVENT_WAIT_LIST); + CL_ERR_TO_STR(CL_INVALID_EVENT); + CL_ERR_TO_STR(CL_INVALID_OPERATION); + CL_ERR_TO_STR(CL_INVALID_GL_OBJECT); + CL_ERR_TO_STR(CL_INVALID_BUFFER_SIZE); + CL_ERR_TO_STR(CL_INVALID_MIP_LEVEL); + CL_ERR_TO_STR(CL_INVALID_GLOBAL_WORK_SIZE); + CL_ERR_TO_STR(CL_COMPILE_PROGRAM_FAILURE); + CL_ERR_TO_STR(CL_LINKER_NOT_AVAILABLE); + CL_ERR_TO_STR(CL_LINK_PROGRAM_FAILURE); + CL_ERR_TO_STR(CL_DEVICE_PARTITION_FAILED); + CL_ERR_TO_STR(CL_INVALID_PROPERTY); + CL_ERR_TO_STR(CL_INVALID_IMAGE_DESCRIPTOR); + CL_ERR_TO_STR(CL_INVALID_COMPILER_OPTIONS); + CL_ERR_TO_STR(CL_INVALID_LINKER_OPTIONS); + CL_ERR_TO_STR(CL_INVALID_DEVICE_PARTITION_COUNT); + + default: + return "UNKNOWN ERROR CODE"; + } + } + +/** +Check the OpenCL return code for errors. +If an error is detected, it will be printed and the programm execution is +stopped. + +@param err The OpenCL error code +*/ + void + handleClReturnCode(cl_int const err, std::string const file, + int const line) { + if (err != CL_SUCCESS) { + std::string err_string = getCLErrorString(err); + std::cerr << "ERROR in OpenCL library detected! Aborting." + << std::endl << file << ":" << line << ": " << err_string + << std::endl; + exit(err); + } + } + +/** +Sets up the given FPGA with the kernel in the provided file. + +@param context The context used for the program +@param program The devices used for the program +@param usedKernelFile The path to the kernel file +@return The program that is used to create the benchmark kernels +*/ + cl::Program + fpgaSetup(const cl::Context *context, std::vector deviceList, + const std::string *usedKernelFile) { + int err; + + std::cout << HLINE; + std::cout << "FPGA Setup:" << usedKernelFile->c_str() << std::endl; + + // Open file stream if possible + std::ifstream aocxStream(usedKernelFile->c_str(), std::ifstream::binary); + if (!aocxStream.is_open()) { + std::cerr << "Not possible to open from given file!" << std::endl; + } + + // Read in file contents and create program from binaries + std::string prog(std::istreambuf_iterator(aocxStream), + (std::istreambuf_iterator())); + aocxStream.seekg(0, aocxStream.end); + unsigned file_size = aocxStream.tellg(); + aocxStream.seekg(0, aocxStream.beg); + char *buf = new char[file_size]; + aocxStream.read(buf, file_size); + + cl::Program::Binaries mybinaries; + mybinaries.push_back({buf, file_size}); + + // Create the Program from the AOCX file. + cl::Program program(*context, deviceList, mybinaries, NULL, &err); + ASSERT_CL(err); + std::cout << "Prepared FPGA successfully for global Execution!" << + std::endl; + std::cout << HLINE; + return program; + } + +/** +Sets up the C++ environment by configuring std::cout and checking the clock +granularity using bm_helper::checktick() +*/ + void + setupEnvironmentAndClocks() { + std::cout << std::setprecision(5) << std::scientific; + + std::cout << HLINE; + std::cout << "General setup:" << std::endl; + + // Check clock granularity and output result + std::cout << "C++ high resolution clock is used." << std::endl; + std::cout << "The clock precision seems to be " + << static_cast + (std::chrono::high_resolution_clock::period::num) / + std::chrono::high_resolution_clock::period::den * 10e9 + << "ns" << std::endl; + + std::cout << HLINE; + } + + +/** +Searches an selects an FPGA device using the CL library functions. +If multiple platforms or devices are given, the user will be prompted to +choose a device. + +@param defaultPlatform The index of the platform that has to be used. If a + value < 0 is given, the platform can be chosen + interactively +@param defaultDevice The index of the device that has to be used. If a + value < 0 is given, the device can be chosen + interactively + +@return A list containing a single selected device +*/ + std::vector + selectFPGADevice(int defaultPlatform, int defaultDevice) { + // Integer used to store return codes of OpenCL library calls + int err; + + std::vector platformList; + err = cl::Platform::get(&platformList); + ASSERT_CL(err); + + // Choose the target platform + int chosenPlatformId = 0; + if (defaultPlatform >= 0) { + if (defaultPlatform < platformList.size()) { + chosenPlatformId = defaultPlatform; + } else { + std::cerr << "Default platform " << defaultPlatform + << " can not be used. Available platforms: " + << platformList.size() << std::endl; + exit(1); + } + } else if (platformList.size() > 1) { + std::cout << + "Multiple platforms have been found. Select the platform by"\ + " typing a number:" << std::endl; + for (int platformId = 0; + platformId < platformList.size(); platformId++) { + std::cout << platformId << ") " << + platformList[platformId].getInfo() << + std::endl; + } + std::cout << "Enter platform id [0-" << platformList.size() - 1 + << "]:"; + std::cin >> chosenPlatformId; + } + cl::Platform platform = platformList[chosenPlatformId]; + std::cout << "Selected Platform: " + << platform.getInfo() << std::endl; + + std::vector deviceList; + err = platform.getDevices(CL_DEVICE_TYPE_ACCELERATOR, &deviceList); + ASSERT_CL(err); + + // Choose taget device + int chosenDeviceId = 0; + if (defaultDevice >= 0) { + if (defaultDevice < deviceList.size()) { + chosenDeviceId = defaultDevice; + } else { + std::cerr << "Default platform " << defaultDevice + << " can not be used. Available platforms: " + << deviceList.size() << std::endl; + exit(1); + } + } else if (deviceList.size() > 1) { + std::cout << + "Multiple devices have been found. Select the platform by"\ + " typing a number:" << std::endl; + for (int deviceId = 0; + deviceId < deviceList.size(); deviceId++) { + std::cout << deviceId << ") " << + deviceList[deviceId].getInfo() << + std::endl; + } + std::cout << "Enter device id [0-" << deviceList.size() - 1 << "]:"; + std::cin >> chosenDeviceId; + } + std::vector chosenDeviceList; + chosenDeviceList.push_back(deviceList[chosenDeviceId]); + + // Give selection summary + std::cout << HLINE; + std::cout << "Selection summary:" << std::endl; + std::cout << "Platform Name: " << + platform.getInfo() << std::endl; + std::cout << "Device Name: " << + chosenDeviceList[0].getInfo() << std::endl; + std::cout << HLINE; + + return chosenDeviceList; + } + +} // namespace fpga_setup \ No newline at end of file diff --git a/GEMM/src/host/setup/fpga_setup.hpp b/GEMM/src/host/setup/fpga_setup.hpp new file mode 100644 index 00000000..c1f78b0c --- /dev/null +++ b/GEMM/src/host/setup/fpga_setup.hpp @@ -0,0 +1,108 @@ +/* +Copyright (c) 2019 Marius Meyer + +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 SRC_HOST_FPGA_SETUP_H_ +#define SRC_HOST_FPGA_SETUP_H_ + +#include +#include +#include +#include +#include +#include + +/* External libraries */ +#include "CL/cl.hpp" + +/** +Makro to convert the error integer representation to its string representation +Source: https://gist.github.com/allanmac/9328bb2d6a99b86883195f8f78fd1b93 +*/ +#define CL_ERR_TO_STR(err) case err: return #err + +namespace fpga_setup { + + /** +Converts the reveived OpenCL error to a string + +@param err The OpenCL error code + +@return The string representation of the OpenCL error code +*/ + std::string + getCLErrorString(cl_int const err); + +/** +Check the OpenCL return code for errors. +If an error is detected, it will be printed and the programm execution is +stopped. + +@param err The OpenCL error code +*/ + void + handleClReturnCode(cl_int const err, std::string const file, + int const line); + +/** +Makro that enables checks for OpenCL errors with handling of the file and +line number. +*/ +#define ASSERT_CL(err) fpga_setup::handleClReturnCode(err, __FILE__, __LINE__) + +/** +Sets up the given FPGA with the kernel in the provided file. + +@param context The context used for the program +@param program The devices used for the program +@param usedKernelFile The path to the kernel file +@return The program that is used to create the benchmark kernels +*/ + cl::Program + fpgaSetup(const cl::Context *context, std::vector deviceList, + const std::string *usedKernelFile); + +/** +Sets up the C++ environment by configuring std::cout and checking the clock +granularity using bm_helper::checktick() +*/ + void + setupEnvironmentAndClocks(); + + +/** +Searches an selects an FPGA device using the CL library functions. +If multiple platforms or devices are given, the user will be prompted to +choose a device. + +@param defaultPlatform The index of the platform that has to be used. If a + value < 0 is given, the platform can be chosen + interactively +@param defaultDevice The index of the device that has to be used. If a + value < 0 is given, the device can be chosen + interactively + +@return A list containing a single selected device +*/ + std::vector + selectFPGADevice(int defaultPlatform, int defaultDevice); + +} // namespace fpga_setup +#endif // SRC_HOST_FPGA_SETUP_H_ diff --git a/GEMM/tests/CMakeLists.txt b/GEMM/tests/CMakeLists.txt new file mode 100755 index 00000000..742988a8 --- /dev/null +++ b/GEMM/tests/CMakeLists.txt @@ -0,0 +1,15 @@ + +add_compile_options(--std=c++11) +# 'lib' is the folder with Google Test sources +add_subdirectory(../../extern/googletest ${CMAKE_CURRENT_BINARY_DIR}/lib) +include_directories(${gtest_SOURCE_DIR}/include ${gtest_SOURCE_DIR}) +include_directories(${CMAKE_BINARY_DIR}/src/common ../../extern/cxxopts/include) +include_directories(SYSTEM ${IntelFPGAOpenCL_INCLUDE_DIRS}) + +set(PROJECT_SOURCES ../src/host/setup/fpga_setup.cpp ../src/host/gemm_functionality.cpp ../src/host/execution_cannon.cpp) +set(TEST_SOURCES test_fpga_setup.cpp test_kernel_functionality_and_host_integration.cpp) + +add_executable(Google_Tests_run ${TEST_SOURCES} ${PROJECT_SOURCES}) +target_link_libraries(Google_Tests_run gtest gmock gtest_main ${IntelFPGAOpenCL_LIBRARIES}) +add_dependencies(Google_Tests_run ${CMAKE_BINARY_DIR}/src/common/parameters.h) +add_dependencies(Google_Tests_run gemm_cannon_emulate) \ No newline at end of file diff --git a/GEMM/tests/test_fpga_setup.cpp b/GEMM/tests/test_fpga_setup.cpp new file mode 100644 index 00000000..fd29ebe9 --- /dev/null +++ b/GEMM/tests/test_fpga_setup.cpp @@ -0,0 +1,34 @@ +// +// Created by Marius Meyer on 04.12.19 +// +#include "gtest/gtest.h" +#include "../src/host/setup/fpga_setup.hpp" +#include "parameters.h" + +/** + * Check if it is possible to find the platform and device that are given as default + */ +TEST (FPGASetup, FindValidPlatformAndDevice) { + EXPECT_EQ (1, fpga_setup::selectFPGADevice(DEFAULT_PLATFORM, DEFAULT_DEVICE).size()); +} + +/** + * Checks if non existing platform leads to an error + */ +TEST (FPGASetup, FindNonExistingPlatform) { + // TODO regex does not work so for now its not tested! + EXPECT_EXIT(fpga_setup::selectFPGADevice(DEFAULT_PLATFORM + 100, DEFAULT_DEVICE), + ::testing::ExitedWithCode(1), + ::testing::MatchesRegex(".*")); +} + +/** + * Checks if non existing device leads to an error + */ +TEST (FPGASetup, FindNonExistingDevice) { + // TODO regex does not work so for now its not tested! + EXPECT_EXIT(fpga_setup::selectFPGADevice(DEFAULT_PLATFORM, DEFAULT_DEVICE + 100), + ::testing::ExitedWithCode(1), + ::testing::MatchesRegex(".*")); +} + diff --git a/GEMM/tests/test_kernel_functionality_and_host_integration.cpp b/GEMM/tests/test_kernel_functionality_and_host_integration.cpp new file mode 100644 index 00000000..1bddae28 --- /dev/null +++ b/GEMM/tests/test_kernel_functionality_and_host_integration.cpp @@ -0,0 +1,207 @@ +// +// Created by Marius Meyer on 04.12.19. +// +#include + +#include "gtest/gtest.h" +#include "../src/host/execution.h" +#include "../src/host/gemm_functionality.hpp" +#include "parameters.h" +#include "../src/host/setup/fpga_setup.hpp" + +void +ref_matmul(HOST_DATA_TYPE const* A, HOST_DATA_TYPE const* B, HOST_DATA_TYPE* C, uint size) { + for (int i=0; i< size; i++) { + for (int j=0; j< size; j++) { + C[i * size + j] = 0; + for (int k = 0; k < size; k++) { + C[i * size + j] += A[i * size + k] * B[k * size + j]; + } + } + } +} + + +struct OpenCLKernelTest : testing::Test { + std::string kernelFileName; + HOST_DATA_TYPE *A; + HOST_DATA_TYPE *B; + HOST_DATA_TYPE *C; + HOST_DATA_TYPE *C_out; + std::shared_ptr config; + cl_uint matrix_size; + + OpenCLKernelTest() { + kernelFileName = "gemm_cannon_emulate.aocx"; + matrix_size = 2 * BLOCK_SIZE; + posix_memalign(reinterpret_cast(&A), 64, + sizeof(HOST_DATA_TYPE) * matrix_size * matrix_size); + posix_memalign(reinterpret_cast(&B), 64, + sizeof(HOST_DATA_TYPE) * matrix_size * matrix_size); + posix_memalign(reinterpret_cast(&C), 64, + sizeof(HOST_DATA_TYPE) * matrix_size * matrix_size); + posix_memalign(reinterpret_cast(&C_out), 64, + sizeof(HOST_DATA_TYPE) * matrix_size * matrix_size); + setupFPGA(); + } + + void setupFPGA() { + std::vector device = fpga_setup::selectFPGADevice(DEFAULT_PLATFORM, DEFAULT_DEVICE); + cl::Context context(device[0]); + cl::Program program = fpga_setup::fpgaSetup(&context, device, &kernelFileName); + config = std::make_shared( + bm_execution::ExecutionConfiguration{ + context, device[0], program, + KERNEL_NAME, + 1, + matrix_size, + false + }); + HOST_DATA_TYPE norm; + matgen(A,1,matrix_size, matrix_size, &norm); + matgen(B,2,matrix_size, matrix_size, &norm); + matgen(C,3,matrix_size, matrix_size, &norm); + } + + ~OpenCLKernelTest() override { + free(A); + free(B); + free(C); + free(C_out); + } +}; + +struct DifferentOpenCLKernelTest : OpenCLKernelTest, testing::WithParamInterface> { + DifferentOpenCLKernelTest() { + auto params = GetParam(); + kernelFileName = std::get<0>(params); + matrix_size = std::get<1>(params) * BLOCK_SIZE; + posix_memalign(reinterpret_cast(&A), 64, + sizeof(HOST_DATA_TYPE) * matrix_size * matrix_size); + posix_memalign(reinterpret_cast(&B), 64, + sizeof(HOST_DATA_TYPE) * matrix_size * matrix_size); + posix_memalign(reinterpret_cast(&C), 64, + sizeof(HOST_DATA_TYPE) * matrix_size * matrix_size); + posix_memalign(reinterpret_cast(&C_out), 64, + sizeof(HOST_DATA_TYPE) * matrix_size * matrix_size); + setupFPGA(); + } +}; + + +/** + * Tests if C will be multiplied by beta + */ +TEST_P(DifferentOpenCLKernelTest, FPGACorrectCtimesBeta) { + for (int i = 0; i < matrix_size; i++) { + for (int j = 0; j < matrix_size; j++) { + A[i * matrix_size + j] = 0.0; + B[i * matrix_size + j] = 0.0; + C[i * matrix_size + j] = 1.0; + } + } + auto result = bm_execution::calculate(config, A, B, C, C_out,0.0,2.0); + for (int i = 0; i < matrix_size; i++) { + for (int j = 0; j < matrix_size; j++) { + EXPECT_FLOAT_EQ(C_out[i * matrix_size + j], 2.0 * C[i * matrix_size + j]); + } + } +} + +/** + * Tests if A will be multiplied by alpha + */ +TEST_P(DifferentOpenCLKernelTest, FPGACorrectAtimesAlpha) { + for (int i = 0; i < matrix_size; i++) { + for (int j = 0; j < matrix_size; j++) { + B[i * matrix_size + j] = i == j ? 1.0 : 0.0; + C[i * matrix_size + j] = 0.0; + } + } + auto result = bm_execution::calculate(config, A, B, C, C_out,2.0,0.0); + for (int i = 0; i < matrix_size; i++) { + for (int j = 0; j < matrix_size; j++) { + EXPECT_FLOAT_EQ(C_out[i * matrix_size + j], 2.0 * A[i * matrix_size + j]); + } + } +} + +/** + * Tests if B will be multiplied by alpha + */ +TEST_P(DifferentOpenCLKernelTest, FPGACorrectBtimesAlpha) { + for (int i = 0; i < matrix_size; i++) { + for (int j = 0; j < matrix_size; j++) { + A[i * matrix_size + j] = i == j ? 1.0 : 0.0; + C[i * matrix_size + j] = 0.0; + } + } + auto result = bm_execution::calculate(config, A, B, C, C_out,2.0,0.0); + for (int i = 0; i < matrix_size; i++) { + for (int j = 0; j < matrix_size; j++) { + EXPECT_FLOAT_EQ(C_out[i * matrix_size + j], 2.0 * B[i * matrix_size + j]); + } + } +} + +/** + * Tests if A will be multiplied with B + */ +TEST_P(DifferentOpenCLKernelTest, FPGACorrectAmulB) { + for (int i = 0; i < matrix_size; i++) { + for (int j = 0; j < matrix_size; j++) { + C[i * matrix_size + j] = 0.0; + A[i * matrix_size + j] = j; + B[i * matrix_size + j] = i; + } + } + auto result = bm_execution::calculate(config, A, B, C, C_out,1.0,0.0); + + HOST_DATA_TYPE c_ref_out[matrix_size * matrix_size]; + ref_matmul(A,B,c_ref_out,matrix_size); + for (int i = 0; i < matrix_size; i++) { + for (int j = 0; j < matrix_size; j++) { + EXPECT_NEAR(C_out[i * matrix_size + j], c_ref_out[i * matrix_size + j], 0.001); + } + } +} + +/** + * Tests if C will be added to A + */ +TEST_P(DifferentOpenCLKernelTest, FPGACorrectCplusA) { + + for (int i = 0; i < matrix_size; i++) { + for (int j = 0; j < matrix_size; j++) { + B[i * matrix_size + j] = i == j ? 1.0 : 0.0; + } + } + + auto result = bm_execution::calculate(config, A, B, C, C_out,1.0, 1.0); + for (int i = 0; i < matrix_size; i++) { + for (int j = 0; j < matrix_size; j++) { + EXPECT_FLOAT_EQ(C_out[i * matrix_size + j], A[i * matrix_size + j] + C[i * matrix_size + j]); + } + } +} + + +/** + * Tests full multiply add + */ + +TEST_P(DifferentOpenCLKernelTest, FPGACorrectbetaCplusalphaAB) { + HOST_DATA_TYPE c_ref_out[matrix_size * matrix_size]; + auto result = bm_execution::calculate(config, A, B, C, C_out,0.5, 2.0); + gemm_ref(A,B,C,c_ref_out,matrix_size,0.5,2.0); + for (int i = 0; i < matrix_size; i++) { + for (int j = 0; j < matrix_size; j++) { + EXPECT_NEAR(C_out[i * matrix_size + j], c_ref_out[i * matrix_size + j], 0.001); + } + } +} + + +INSTANTIATE_TEST_CASE_P(Default, DifferentOpenCLKernelTest, + testing::Combine(testing::Values("gemm_cannon_emulate.aocx"), testing::Values(1,2) + )); diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000..fc47bf1c --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020 pc2 + +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/LINPACK/.gitignore b/LINPACK/.gitignore new file mode 100644 index 00000000..dd5e1143 --- /dev/null +++ b/LINPACK/.gitignore @@ -0,0 +1,7 @@ +*/.DS_Store +.DS_Store +cmake-* +.vscode +.idea +*.zsh +*.sh diff --git a/LINPACK/CHANGELOG b/LINPACK/CHANGELOG new file mode 100644 index 00000000..ade164bf --- /dev/null +++ b/LINPACK/CHANGELOG @@ -0,0 +1,9 @@ +# Changelog + +This file contains all changes made to the source code for each release. + +## 2.0 + +#### Added: +- Replace Makefile with CMake as a build system +- Add unit testing \ No newline at end of file diff --git a/LINPACK/CMakeLists.txt b/LINPACK/CMakeLists.txt new file mode 100755 index 00000000..66d1b553 --- /dev/null +++ b/LINPACK/CMakeLists.txt @@ -0,0 +1,59 @@ +cmake_minimum_required(VERSION 3.1) +project(LINPACK VERSION 2.0.0) + +set (CMAKE_CXX_STANDARD 11) + +if(CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME) + include(CTest) +endif() + +set(DEFAULT_REPETITIONS 10 CACHE STRING "Default number of repetitions") +set(DATA_TYPE float) +set(DEFAULT_MATRIX_SIZE 1024 CACHE STRING "Default matrix size") +set(DEFAULT_DEVICE -1 CACHE STRING "Index of the default device to use") +set(DEFAULT_PLATFORM -1 CACHE STRING "Index of the default platform to use") +set(GLOBAL_MEM_UNROLL 16 CACHE STRING "Unrolling factor that is used for all loops in the kernels") +set(FPGA_BOARD_NAME p520_hpc_sg280l CACHE STRING "Name of the target FPGA board") +set(LOCAL_MEM_BLOCK_LOG 5 CACHE STRING "Used to define the width and height of the block stored in local memory") +set(REGISTER_BLOCK_LOG 3 CACHE STRING "Size of the block that will be manipulated in registers") +set(XILINX_GENERATE_LINK_SETTINGS Yes CACHE BOOL "Generate the link settings depending on the number of replications for the kernels") +set(XILINX_LINK_SETTINGS_FILE "${CMAKE_SOURCE_DIR}/settings/settings.link.xilinx.stream_kernels.ddr.ini" CACHE STRING "The link settings file that should be used when generating is disabled") +set(XILINX_COMPILE_FLAGS "-j 40" CACHE STRING "Additional compile flags for the v++ compiler") +set(XILINX_COMPILE_SETTINGS "${CMAKE_SOURCE_DIR}/settings/settings.compile.xilinx.ini" CACHE STRING "Compile settings file for the v++ compiler") +separate_arguments(XILINX_COMPILE_FLAGS) + +set(HOST_DATA_TYPE cl_${DATA_TYPE}) +set(DEVICE_DATA_TYPE ${DATA_TYPE}) + +if (DATA_TYPE STREQUAL "double") + set(_DP Yes) + message(STATUS "Set DP flag since data type seems to be double precision") +else() + set(_DP No) +endif() + +set(AOC_FLAGS "-fpc -fp-relaxed -no-interleaving=default" CACHE STRING "Used flags for the AOC compiler") +separate_arguments(AOC_FLAGS) + +set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_SOURCE_DIR}/../extern/hlslib/cmake) +set(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin) + +configure_file( + "${CMAKE_SOURCE_DIR}/src/common/parameters.h.in" + "${CMAKE_BINARY_DIR}/src/common/parameters.h" +) +include_directories(${CMAKE_BINARY_DIR}/src/common) + + +find_package(OpenMP) +find_package(IntelFPGAOpenCL) +find_package(Vitis) + +add_subdirectory(src/device) +add_subdirectory(src/host) + +if(CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME AND BUILD_TESTING) + add_subdirectory(tests) +endif() + + diff --git a/LINPACK/LICENSE b/LINPACK/LICENSE new file mode 100644 index 00000000..fc47bf1c --- /dev/null +++ b/LINPACK/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020 pc2 + +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/LINPACK/Readme.md b/LINPACK/Readme.md new file mode 100644 index 00000000..590593ce --- /dev/null +++ b/LINPACK/Readme.md @@ -0,0 +1,139 @@ +# LINPACK for FPGA + +This repository contains the LINPACK for FPGA and its OpenCL kernels. +Currently only the Intel FPGA SDK for OpenCL utility is supported. + +The implementation is currently work in progess and is not feature complete. +Read the section **Implementation Details** for more information. + + +## Build + +CMake is used as the build system. +The targets below can be used to build the benchmark and its kernels: + + | Target | Description | + | --------------------- | ---------------------------------------------- | + | LINPACK_intel | Builds the host application linking with the Intel SDK| + | Test_intel | Compile the tests and its dependencies linking with the Intel SDK | + + More over there are additional targets to generate kernel reports and bitstreams. + The provided kernel is optimized for the Bittware 520N board equipped with Stratix 10. + It has a high resource utilization and will most likely not fit on smaller FPGAs. + + The kernel targets are: + + | Target | Description | + | ------------------------------ | ---------------------------------------------- | + | lu_blocked_pvt_intel | Synthesizes the kernel (takes several hours!) | + | lu_blocked_pvt_report_intel | Create an HTML report for the kernel | + | lu_blocked_pvt_emulate_intel | Create a n emulation kernel | + +The report target for xilinx is missing but reports will be generated when the kernel is synthesized. + + You can build for example the host application by running + + mkdir build && cd build + cmake .. + make LINPACK_intel + +A whole emulation build can be done with + + mkdir build && cd build + cmake .. + make LINPACK_intel + +This will compile the host code as well a unit test binary with emulation kernels for functionality testing. + +You will find all executables and kernel files in the `bin` +folder of your build directory. +You might want to specify predefined parameters before build: + +Name | Default | Description | +---------------- |-------------|--------------------------------------| +`DEFAULT_DEVICE` | -1 | Index of the default device (-1 = ask) | +`DEFAULT_PLATFORM`| -1 | Index of the default platform (-1 = ask) | +`DEFAULT_REPETITIONS`| 10 | Number of times the kernel will be executed | +`FPGA_BOARD_NAME`| p520_hpc_sg280l | Name of the target board | +`DEFAULT_MATRIX_SIZE`| 1024 | Width and heigth of the input matrix | +`GLOBAL_MEM_UNROLL`| 16 | Loop unrolling factor for all loops in the device code that load or store to global memory. This will have impact on the the width of the generated LSUs. | +`AOC_FLAGS`| `-fpc -fp-relaxed -no-interleaving=default` | Additional Intel AOC compiler flags that are used for kernel compilation | +`REGISTER_BLOCK_LOG`| 3 | Size of the blocks that will be processed in registers (2^3=8 is the default) | +`LOCAL_MEM_BLOCK_LOG`| 5 | Size of the blocks that will be processed in local memory (2^3=8 is the default) | + +Moreover the environment variable `INTELFPGAOCLSDKROOT` has to be set to the root +of the Intel FPGA SDK installation. + +Additionally it is possible to set the used compiler and other build tools +in the `CMakeCache.txt` located in the build directory after running cmake. + +## Execution + +For execution of the benchmark run: + + ./LINPACK_intel -f path_to_kernel.aocx + +For more information on available input parameters run + + ./LINPACK_intel -h + +To execute the unit and integration tests for Intel devices run + + CL_CONTEXT_EMULATOR_DEVICE=1 ./Test_intel + +in the `bin` folder within the build directory. +It will run an emulation of the kernel and execute some functionality tests. + +## Implementation Details + +The benchmark will measure the elapsed time to execute a kernel for performing +an LU factorization. +It will use the time to calculate the FLOP/s. +Buffer transfer is currently not measured. +The solving of the linear equations is currently done on the CPU. + +The updates are done unaligned and randomly directly on the global memory. +The repository contains two different implementations: +- `blocked`: A blocked, unoptimized kernel that performs the LU factorization + without pivoting. +- `blocked_pvt`: Blocked kernel that performs the LU factorization with pivoting + over the whole block. + +#### Work in Progress + +The implementation is currently work in progress and currently only covers the +GEFA calculation on FPGA. +A rough overview of the WIP with focus on the pivoting kernel: + +- Routines C1 to C3 are not optimized and C4 reduces fMax. +- Only block-wise partial pivoting is used instead of partial pivoting over + the whole matrix. This increases the error in the calculation. +- GESL not implemented on FPGA. + + +## Result Interpretation + +The host code will print the results of the execution to the standard output. +The result summary looks similar to this: + + norm. resid resid machep x[0]-1 x[n-1]-1 + 9.40193e+00 2.87056e-04 1.19209e-07 -3.21865e-05 2.57969e-04 + best mean GFLOPS error + 1.57262e-01 1.57262e-01 7.11221e-02 9.40193e+00 + +The first row contains data from the correctness check that is done once when +executing the benchmark: +- `resid`: The maximum residual error when multiplying the result vector with + the matrix and subtract by the expected result. +- `norm. resid`: The normalized residual error based on `resid`. +- `machep`: machine epsilon that gives an upper bound for rounding errors due + to the used floating point format. +- `x[0] - 1`: The first element of the result vector minus 1. It should be + close to 0. The same holds for `x[n-1] - 1` which is the last element of the + vector. + +The second row contains the measured performance of the benchmark: +- `best`: The best measured time for executing the benchmark in seconds. +- `mean`: The arithmetic mean of all measured execution times in seconds. +- `GFLOPS`: GFLOP/s achieved for the calculation using the best measured time. +- `error`: Same as `norm. resid` to complete the performance overview. diff --git a/LINPACK/src/common/parameters.h.in b/LINPACK/src/common/parameters.h.in new file mode 100644 index 00000000..b50af517 --- /dev/null +++ b/LINPACK/src/common/parameters.h.in @@ -0,0 +1,28 @@ +#ifndef SRC_COMMON_PARAMETERS_H_ +#define SRC_COMMON_PARAMETERS_H_ + +/** + * Host specific parameters + */ +#define VERSION "@PROJECT_VERSION@" +#define DEFAULT_REPETITIONS @DEFAULT_REPETITIONS@ +#define DEFAULT_PLATFORM @DEFAULT_PLATFORM@ +#define DEFAULT_DEVICE @DEFAULT_DEVICE@ +#define HOST_DATA_TYPE @HOST_DATA_TYPE@ +#define DEFAULT_MATRIX_SIZE @DEFAULT_MATRIX_SIZE@ +#cmakedefine _DP @_DP@ + +/** + * Device specific parameters + */ +#define DEVICE_DATA_TYPE @DEVICE_DATA_TYPE@ +#define UNROLL_COUNT @GLOBAL_MEM_UNROLL@ +#define LOCAL_MEM_BLOCK_LOG @LOCAL_MEM_BLOCK_LOG@ +#define REGISTER_BLOCK_LOG @REGISTER_BLOCK_LOG@ +/** +Output separator +*/ +#define HLINE "-------------------------------------------------------------\n" + + +#endif // SRC_COMMON_PARAMETERS_H_ diff --git a/LINPACK/src/device/CMakeLists.txt b/LINPACK/src/device/CMakeLists.txt new file mode 100644 index 00000000..468fd007 --- /dev/null +++ b/LINPACK/src/device/CMakeLists.txt @@ -0,0 +1,101 @@ + +set(COMPILER_INCLUDES "-I${CMAKE_CURRENT_BINARY_DIR}/../common") +set(CLFLAGS --config ${XILINX_COMPILE_SETTINGS}) + +set(Vitis_EMULATION_CONFIG_UTIL $ENV{XILINX_VITIS}/bin/emconfigutil) + +## +# This function will create build targets for the kernels for emulationand synthesis for xilinx. +## +function(generate_kernel_targets_xilinx) + foreach (kernel_file_name ${ARGN}) + set(base_file "${CMAKE_SOURCE_DIR}/src/device/${kernel_file_name}.cl") + set(source_f "${CMAKE_BINARY_DIR}/src/device/replicated_${kernel_file_name}_xilinx.cl") + set(bitstream_compile ${EXECUTABLE_OUTPUT_PATH}/tmp_compile/${kernel_file_name}.xo) + set(bitstream_compile_emulate ${EXECUTABLE_OUTPUT_PATH}/tmp_compile/${kernel_file_name}_emulate.xo) + set(bitstream_emulate_f + ${EXECUTABLE_OUTPUT_PATH}/${kernel_file_name}_emulate.xclbin) + set(bitstream_f ${EXECUTABLE_OUTPUT_PATH}/${kernel_file_name}.xclbin) + set(xilinx_link_settings ${XILINX_LINK_SETTINGS_FILE}) + + # build emulation config for device + add_custom_command(OUTPUT ${EXECUTABLE_OUTPUT_PATH}/emconfig.json + COMMAND ${Vitis_EMULATION_CONFIG_UTIL} -f ${FPGA_BOARD_NAME} --od ${EXECUTABLE_OUTPUT_PATH} + ) + + add_custom_command(OUTPUT ${source_f} + COMMAND ${CMAKE_COMMAND} -Dsource_f=${source_f} -Dbase_file=${base_file} -DNUM_REPLICATIONS=1 -P "${CMAKE_SOURCE_DIR}/src/device/generateKernels.cmake" + MAIN_DEPENDENCY ${base_file} + ) + + add_custom_command(OUTPUT ${bitstream_compile_emulate} + COMMAND ${Vitis_COMPILER} ${CLFLAGS} -t sw_emu ${COMPILER_INCLUDES} -f ${FPGA_BOARD_NAME} -g -c ${XILINX_COMPILE_FLAGS} -o ${bitstream_compile_emulate} ${source_f} + MAIN_DEPENDENCY ${source_f} + ) + add_custom_command(OUTPUT ${bitstream_emulate_f} + COMMAND ${Vitis_COMPILER} -t sw_emu ${COMPILER_INCLUDES} -f ${FPGA_BOARD_NAME} -g -l --config ${xilinx_link_settings} ${XILINX_COMPILE_FLAGS} -o ${bitstream_emulate_f} ${bitstream_compile_emulate} + MAIN_DEPENDENCY ${bitstream_compile_emulate} + DEPENDS ${xilinx_link_settings} + ) + add_custom_command(OUTPUT ${bitstream_compile} + COMMAND ${Vitis_COMPILER} ${CLFLAGS} -t hw ${COMPILER_INCLUDES} --platform ${FPGA_BOARD_NAME} -R2 -c ${XILINX_COMPILE_FLAGS} -o ${bitstream_compile} ${source_f} + MAIN_DEPENDENCY ${source_f} + ) + add_custom_command(OUTPUT ${bitstream_f} + COMMAND ${Vitis_COMPILER} ${CLFLAGS} -t hw ${COMPILER_INCLUDES} --platform ${FPGA_BOARD_NAME} -R2 -l --config ${xilinx_link_settings} ${XILINX_COMPILE_FLAGS} -o ${bitstream_f} ${bitstream_compile} + MAIN_DEPENDENCY ${bitstream_compile} + DEPENDS ${xilinx_link_settings} + ) + add_custom_target(${kernel_file_name}_emulate_xilinx DEPENDS ${bitstream_emulate_f} ${EXECUTABLE_OUTPUT_PATH}/emconfig.json + DEPENDS ${source_f} ${CMAKE_BINARY_DIR}/src/common/parameters.h + SOURCES ${source_f} ${CMAKE_BINARY_DIR}/src/common/parameters.h) + add_custom_target(${kernel_file_name}_xilinx + DEPENDS ${bitstream_f} ${CMAKE_BINARY_DIR}/src/common/parameters.h + ) + add_custom_target(${kernel_file_name}_compile_xilinx + DEPENDS ${bitstream_compile} ${CMAKE_BINARY_DIR}/src/common/parameters.h + ) + endforeach () +endfunction() + + +## +# This function will create build targets for the kernels for emulation, reports and synthesis. +# It will use the generate_kernel_replication function to generate a new code file containing the code for all kernels +## +function(generate_kernel_targets_intel) + foreach (kernel_file_name ${ARGN}) + set(source_f "${CMAKE_SOURCE_DIR}/src/device/${kernel_file_name}.cl") + set(report_f ${EXECUTABLE_OUTPUT_PATH}/${kernel_file_name}_report_intel) + set(bitstream_emulate_f ${EXECUTABLE_OUTPUT_PATH}/${kernel_file_name}_emulate.aocx) + set(bitstream_f ${EXECUTABLE_OUTPUT_PATH}/${kernel_file_name}.aocx) + + add_custom_command(OUTPUT ${bitstream_emulate_f} + COMMAND ${IntelFPGAOpenCL_AOC} ${source_f} ${COMPILER_INCLUDES} ${AOC_FLAGS} -legacy-emulator -march=emulator + -o ${bitstream_emulate_f} + MAIN_DEPENDENCY ${source_f} + ) + add_custom_command(OUTPUT ${bitstream_f} + COMMAND ${IntelFPGAOpenCL_AOC} ${source_f} ${COMPILER_INCLUDES} ${AOC_FLAGS} -board=${FPGA_BOARD_NAME} + -o ${bitstream_f} + MAIN_DEPENDENCY ${source_f} + ) + add_custom_command(OUTPUT ${report_f} + COMMAND ${IntelFPGAOpenCL_AOC} ${source_f} ${COMPILER_INCLUDES} ${AOC_FLAGS} -rtl -report -board=${FPGA_BOARD_NAME} + -o ${report_f} + MAIN_DEPENDENCY ${source_f} + ) + add_custom_target(${kernel_file_name}_report_intel DEPENDS ${report_f} + DEPENDS ${source_f} "${CMAKE_SOURCE_DIR}/src/device/${kernel_file_name}.cl" ${CMAKE_BINARY_DIR}/src/common/parameters.h) + add_custom_target(${kernel_file_name}_intel DEPENDS ${bitstream_f} + DEPENDS ${source_f} "${CMAKE_SOURCE_DIR}/src/device/${kernel_file_name}.cl" ${CMAKE_BINARY_DIR}/src/common/parameters.h) + add_custom_target(${kernel_file_name}_emulate_intel DEPENDS ${bitstream_emulate_f} + DEPENDS ${source_f} "${CMAKE_SOURCE_DIR}/src/device/${kernel_file_name}.cl" ${CMAKE_BINARY_DIR}/src/common/parameters.h) + endforeach () +endfunction() + +if (INTELFPGAOPENCL_FOUND) + generate_kernel_targets_intel(lu_blocked_pvt lu_blocked_pvt_test) + add_test(NAME test_emulation_intel COMMAND STREAM_FPGA_intel -f stream_kernels_emulate.aocx) +endif() + diff --git a/LINPACK/src/device/lu_blocked.cl b/LINPACK/src/device/lu_blocked.cl new file mode 100644 index 00000000..bc28e7ce --- /dev/null +++ b/LINPACK/src/device/lu_blocked.cl @@ -0,0 +1,371 @@ +/* +Copyright (c) 2019 Marius Meyer + +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. +*/ + +#define DATA_TYPE float + +#ifndef BLOCK_SIZE +#define BLOCK_SIZE 8 +#endif + +/** +Load a block from global memory + +@param a_block local memory buffer to store the block in +@param a the global memory buffer of the Matrix +@param x_block x position of the block +@param y_block y position of the block +@param lda_block LDA of the matrix in number of blocks +*/ +void +load_block(local DATA_TYPE a_block[BLOCK_SIZE][BLOCK_SIZE], + global DATA_TYPE* restrict a, + uint x_block, uint y_block, uint lda_block) { + + for (int i = 0; i < BLOCK_SIZE; i++) { + #pragma unroll GLOBAL_MEM_UNROLL + for (int j = 0; j < BLOCK_SIZE; j++) { + a_block[i][j] = a[(y_block * lda_block * BLOCK_SIZE + x_block) * BLOCK_SIZE + j + + i * lda_block * BLOCK_SIZE]; + } + } +} + +/** +Store a block to global memory + +@param a_block local memory buffer to load the block from +@param a the global memory buffer of the Matrix +@param x_block x position of the block +@param y_block y position of the block +@param lda_block LDA of the matrix in number of blocks +*/ +void +store_block(local DATA_TYPE a_block[BLOCK_SIZE][BLOCK_SIZE], + global DATA_TYPE* restrict a, + uint x_block, uint y_block, uint lda_block) { + + for (int i = 0; i < BLOCK_SIZE; i++) { + #pragma unroll GLOBAL_MEM_UNROLL + for (int j = 0; j < BLOCK_SIZE; j++) { + a[(y_block * lda_block * BLOCK_SIZE + x_block) * BLOCK_SIZE + j + + i * lda_block * BLOCK_SIZE] = a_block[i][j]; + } + } +} + +/** +Copy a block to another memory location + +@param a_from local memory buffer to load the block from +@param a_to the local memory buffer to copy the block into +*/ +void +copy_block(local const DATA_TYPE a_from[BLOCK_SIZE][BLOCK_SIZE], + DATA_TYPE a_to[BLOCK_SIZE][BLOCK_SIZE]) { + + for (int i = 0; i < BLOCK_SIZE; i++) { + #pragma unroll + for (int j = 0; j < BLOCK_SIZE; j++) { + a_to[i][j] = a_from[i][j]; + } + } +} + +/** +Copy a block to another memory location column-wise + +@param a_from local memory buffer to load the block from +@param a_to the local memory buffer to copy the block into +*/ +void +copy_block_col(local const DATA_TYPE a_from[BLOCK_SIZE][BLOCK_SIZE], + DATA_TYPE a_to[BLOCK_SIZE][BLOCK_SIZE]) { + + for (int i = 0; i < BLOCK_SIZE; i++) { + #pragma unroll + for (int j = 0; j < BLOCK_SIZE; j++) { + a_to[j][i] = a_from[j][i]; + } + } +} + + +/** +Standard LU factorization on a block with fixed size + +Case 1 of Zhangs description + +@param a_block_in Input block that has to be LU factorized +@param a_block_out Output block to write the result +@param scale_factors scaling factors that where used to scale the columns. They can be reused in C2 +*/ +void +lu_factorization_c1(local const DATA_TYPE a_block_in[BLOCK_SIZE][BLOCK_SIZE], + local DATA_TYPE a_block_out[BLOCK_SIZE][BLOCK_SIZE], + DATA_TYPE scale_factors[BLOCK_SIZE]) { + + DATA_TYPE tmp_block_write[BLOCK_SIZE][BLOCK_SIZE]; + DATA_TYPE tmp_block_read[BLOCK_SIZE][BLOCK_SIZE]; + // copy columnwise + for (int i = 0; i < BLOCK_SIZE; i++) { + #pragma unroll + for (int j = 0; j < BLOCK_SIZE; j++) { + tmp_block_read[i][j] = a_block_in[j][i]; + tmp_block_write[i][j] = a_block_in[j][i]; + } + } + // For each diagnonal element + #pragma max_concurrency 1 + for (int k = 0; k < BLOCK_SIZE; k++) { + DATA_TYPE tmp_scale_col[BLOCK_SIZE]; + scale_factors[k] = 1.0 / tmp_block_read[k][k]; + // For each element below it + #pragma unroll + for (int i = k + 1; i < BLOCK_SIZE; i++) { + tmp_scale_col[i] = tmp_block_read[k][i] * scale_factors[k]; + tmp_block_write[k][i] = tmp_scale_col[i]; + } + // For each column right of current diagonal element + for (int j = k + 1; j < BLOCK_SIZE; j++) { + DATA_TYPE scale_val = tmp_block_write[j][k]; + // For each element below it + #pragma unroll + for (int i = k+1; i < BLOCK_SIZE; i++) { + tmp_block_write[j][i] = tmp_block_read[j][i] - tmp_scale_col[i] * scale_val; + } + } + for (int i = k; i < BLOCK_SIZE; i++) { + #pragma unroll + for (int j = 0; j < BLOCK_SIZE; j++) { + tmp_block_read[i][j] = tmp_block_write[i][j]; + } + } + } + for (int i = 0; i < BLOCK_SIZE; i++) { + #pragma unroll + for (int j = 0; j < BLOCK_SIZE; j++) { + a_block_out[j][i] = tmp_block_write[i][j]; + } + } +} + +/** +Modifying the blocks on the leftmost side + +Case 2 of Zhangs description + +@param top_block LU factorized top block +@param current_block_in Current input block +@param current_block_out Block to write the output to +@param scale_factors Scale factors that were calculated during LU factorization +*/ +void +left_blocks_c2(local const DATA_TYPE top_block[BLOCK_SIZE][BLOCK_SIZE], + local const DATA_TYPE current_block_in[BLOCK_SIZE][BLOCK_SIZE], + local DATA_TYPE current_block_out[BLOCK_SIZE][BLOCK_SIZE], + const DATA_TYPE scale_factors[BLOCK_SIZE]) { + DATA_TYPE tmp_block_write2[BLOCK_SIZE][BLOCK_SIZE]; + DATA_TYPE tmp_block_read2[BLOCK_SIZE][BLOCK_SIZE]; + DATA_TYPE tmp_scale_col[BLOCK_SIZE]; + // copy columnwise + for (int i = 0; i < BLOCK_SIZE; i++) { + #pragma unroll + for (int j = 0; j < BLOCK_SIZE; j++) { + tmp_block_read2[i][j] = current_block_in[j][i]; + } + } + // For each diagonal element in top block + #pragma max_concurrency 1 + for (int k=0; k < BLOCK_SIZE; k++) { + // For each element below it in current block + #pragma unroll + for (int i=0; i < BLOCK_SIZE; i++) { + // printf("C2: %f * %f\n",tmp_block2[i][k], scale_factors[k]); + tmp_scale_col[i] = tmp_block_read2[k][i] * scale_factors[k]; + tmp_block_write2[k][i] = tmp_scale_col[i]; + } + // For each column right of the current diagnonal element + for (int i = k+1; i < BLOCK_SIZE; i++) { + DATA_TYPE tmp_col[BLOCK_SIZE]; + #pragma unroll + for (int j = 0; j < BLOCK_SIZE; j++) { + tmp_block_write2[i][j] = tmp_block_read2[i][j] - tmp_scale_col[j] * top_block[k][i]; + } + } + for (int i = k; i < BLOCK_SIZE; i++) { + #pragma unroll + for (int j = 0; j < BLOCK_SIZE; j++) { + tmp_block_read2[i][j] = tmp_block_write2[i][j]; + } + } + } + for (int i = 0; i < BLOCK_SIZE; i++) { + #pragma unroll + for (int j = 0; j < BLOCK_SIZE; j++) { + current_block_out[j][i] = tmp_block_write2[i][j]; + } + } +} + +/** +Modifying the blocks on the top but not on the left + +Case 3 of Zhangs description + +@param left_block LU factorized left block +@param current_block_in Current input block +@param current_block_out Block to write the output to +*/ +void +top_blocks_c3(local const DATA_TYPE left_block[BLOCK_SIZE][BLOCK_SIZE], + local const DATA_TYPE current_block_in[BLOCK_SIZE][BLOCK_SIZE], + local DATA_TYPE current_block_out[BLOCK_SIZE][BLOCK_SIZE]) { + DATA_TYPE tmp_block3[BLOCK_SIZE][BLOCK_SIZE]; + for (int j = 0; j < BLOCK_SIZE; j++) { + #pragma unroll + for (int i = 0; i < BLOCK_SIZE; i++) { + current_block_out[j][i] = current_block_in[j][i]; + } + } + // For each diagonal element in left block + for (int k=0; k < BLOCK_SIZE; k++) { + // For each column in current block + for (int j = k+1; j < BLOCK_SIZE; j++) { + // For each element below it + #pragma unroll + for (int i = 0; i < BLOCK_SIZE; i++) { + tmp_block3[j][i] = current_block_out[j][i] - left_block[j][k] * current_block_out[k][i]; + } + } + for (int j = k+1; j < BLOCK_SIZE; j++) { + #pragma unroll + for (int i = 0; i < BLOCK_SIZE; i++) { + current_block_out[j][i] = tmp_block3[j][i]; + } + } + } +} + +/** +Modifying the inner blocks + +Case 4 of Zhangs description + +@param left_block Most left block that was modified by C2 before +@param top_block Most upper block that was modified by C3 before +@param current_block_in Current input block +@param current_block_out Block to write the output to +*/ +void +inner_blocks_c4(local const DATA_TYPE left_block[BLOCK_SIZE][BLOCK_SIZE], + local const DATA_TYPE top_block[BLOCK_SIZE][BLOCK_SIZE], + local const DATA_TYPE current_block_in[BLOCK_SIZE][BLOCK_SIZE], + local DATA_TYPE current_block_out[BLOCK_SIZE][BLOCK_SIZE]) { + DATA_TYPE tmp_block4[BLOCK_SIZE][BLOCK_SIZE]; + copy_block(current_block_in, tmp_block4); + // For each diagonal element in left block + + for (int k=0; k < BLOCK_SIZE; k++) { + // For each column in top block + #pragma unroll + for (int j = 0; j < BLOCK_SIZE; j++) { + // For each element below it in current block + #pragma unroll + for (int i = 0; i < BLOCK_SIZE; i++) { + current_block_out[j][i] = tmp_block4[j][i] - left_block[j][k] * top_block[k][i]; + } + } + copy_block(current_block_out, tmp_block4); + } +} + +/** +LU factorization kernel + +@param a The data array representing the whole matrix in global memory +@param a_size the x and y size of the matrix in blocks +*/ +__attribute__((uses_global_work_offset(0))) +__kernel +void gefa(global DATA_TYPE* restrict a, uint a_size) { + + local DATA_TYPE top_block[BLOCK_SIZE][BLOCK_SIZE]; + local DATA_TYPE left_block[BLOCK_SIZE][BLOCK_SIZE]; + local DATA_TYPE diag_block[BLOCK_SIZE][BLOCK_SIZE]; + local DATA_TYPE top_block_out[BLOCK_SIZE][BLOCK_SIZE]; + local DATA_TYPE left_block_out[BLOCK_SIZE][BLOCK_SIZE]; + local DATA_TYPE diag_block_out[BLOCK_SIZE][BLOCK_SIZE]; + local DATA_TYPE current_block[BLOCK_SIZE][BLOCK_SIZE]; + local DATA_TYPE current_block_out[BLOCK_SIZE][BLOCK_SIZE]; + + + // For each diagonal block do the following + #pragma disable_loop_pipelining + for (int diagonal_block=0; diagonal_block < a_size; diagonal_block++) { + + // load next block for factorization + load_block(diag_block, a, diagonal_block, diagonal_block, a_size); + + DATA_TYPE scale_factors[BLOCK_SIZE]; + + // execute factorization of next block + lu_factorization_c1(diag_block, diag_block_out, scale_factors); + + store_block(diag_block_out, a, diagonal_block, diagonal_block, a_size); + + for (int inner_x_block = diagonal_block + 1; inner_x_block < a_size; + inner_x_block++) { + // update top block + load_block(top_block, a, inner_x_block, diagonal_block, a_size); + top_blocks_c3(diag_block_out, top_block, top_block_out); + store_block(top_block_out, a, inner_x_block, + diagonal_block, a_size); + + for (int inner_y_block = diagonal_block + 1; + inner_y_block < a_size; inner_y_block++) { + + // update left block, if it was not already done + if (inner_x_block == diagonal_block + 1) { + load_block(left_block, a, diagonal_block, + inner_y_block, a_size); + left_blocks_c2(diag_block_out, left_block, + left_block_out, scale_factors); + store_block(left_block_out, a, diagonal_block, + inner_y_block, a_size); + } else { + load_block(left_block_out, a, diagonal_block, + inner_y_block, a_size); + } + + // update inner block + load_block(current_block, a, inner_x_block, + inner_y_block, a_size); + + inner_blocks_c4(left_block_out, top_block_out, current_block, + current_block_out); + + store_block(current_block_out, a, inner_x_block, + inner_y_block, a_size); + } + } + } +} diff --git a/LINPACK/src/device/lu_blocked_pvt.cl b/LINPACK/src/device/lu_blocked_pvt.cl new file mode 100644 index 00000000..4d69325a --- /dev/null +++ b/LINPACK/src/device/lu_blocked_pvt.cl @@ -0,0 +1,580 @@ +/* +Copyright (c) 2019 Marius Meyer + +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. +*/ +#include "parameters.h" + +#define BLOCK_SIZE (1 << LOCAL_MEM_BLOCK_LOG) +#define GEMM_BLOCK (1 << REGISTER_BLOCK_LOG) + +/** +Load a block from global memory + +@param a_block local memory buffer to store the block in +@param a the global memory buffer of the Matrix +@param x_block x position of the block +@param y_block y position of the block +@param lda_block LDA of the matrix in number of blocks +*/ +void +load_block(DEVICE_DATA_TYPE a_block[BLOCK_SIZE][BLOCK_SIZE], + global DEVICE_DATA_TYPE* restrict a, + uint x_block, uint y_block, uint lda_block) { + + for (int i = 0; i < BLOCK_SIZE; i++) { + #pragma unroll UNROLL_COUNT + for (int j = 0; j < BLOCK_SIZE; j++) { + a_block[i][j] = a[(y_block * lda_block * BLOCK_SIZE + x_block) + * BLOCK_SIZE + j + i * lda_block * BLOCK_SIZE]; + } + } +} + + +/** +Store a block to global memory + +@param a_block local memory buffer to load the block from +@param a the global memory buffer of the Matrix +@param x_block x position of the block +@param y_block y position of the block +@param lda_block LDA of the matrix in number of blocks +*/ +void +store_block(DEVICE_DATA_TYPE a_block[BLOCK_SIZE][BLOCK_SIZE], + global DEVICE_DATA_TYPE* restrict a, + uint x_block, uint y_block, uint lda_block) { + + for (int i = 0; i < BLOCK_SIZE; i++) { + #pragma unroll UNROLL_COUNT + for (int j = 0; j < BLOCK_SIZE; j++) { + a[(y_block * lda_block * BLOCK_SIZE + x_block) * BLOCK_SIZE + j + + i * lda_block * BLOCK_SIZE] = a_block[i][j]; + } + } +} + + +/** +Calculate for the Level 2 block: + +c = c + a * b + +where a,b,c are matrices of size GEMM_BLOCK. +Calculation itself is fully unrolled. + */ +void register_gemm(const DEVICE_DATA_TYPE a[GEMM_BLOCK][GEMM_BLOCK], + const DEVICE_DATA_TYPE b[GEMM_BLOCK][GEMM_BLOCK], + DEVICE_DATA_TYPE c_out[GEMM_BLOCK][GEMM_BLOCK]) { + + DEVICE_DATA_TYPE a_block[GEMM_BLOCK][GEMM_BLOCK + 1]; + DEVICE_DATA_TYPE b_block[GEMM_BLOCK + 1][GEMM_BLOCK]; + DEVICE_DATA_TYPE c_block[GEMM_BLOCK][GEMM_BLOCK]; + + // Load block of matrix A and B and init C and reorder values +#pragma unroll + for (int y=0; y> 1; + #pragma unroll + for (int i=0; i < remaining_vals; i++) { + if (prepared_col[stage - 1][i] > prepared_col[stage - 1] + [i + remaining_vals]) { + prepared_col[stage][i] = prepared_col[stage - 1][i]; + prepared_col_index[stage][i] = prepared_col_index[stage - 1][i]; + } else { + prepared_col[stage][i] = prepared_col[stage - 1] + [i + remaining_vals]; + prepared_col_index[stage][i] = prepared_col_index[stage - 1] + [i + remaining_vals]; + } + } + } + // The first value in the last row contains the maximum index + return prepared_col_index[LOCAL_MEM_BLOCK_LOG][0]; +} + + +/** +Standard LU factorization on a block with fixed size + +Case 1 of Zhangs description + +TODO: This routine is not optimized yet and just offer basic functionality + +@param a_block_in Input block that has to be LU factorized +@param a_block_out Output block to write the result +@param scale_factors scaling factors that where used to scale the columns. They can be reused in C2 +@param ipvt Pivoting information for C3 and solving of the system +*/ +void +lu_factorization_c1(const DEVICE_DATA_TYPE a_block_in[BLOCK_SIZE][BLOCK_SIZE], + DEVICE_DATA_TYPE a_block_out[BLOCK_SIZE][BLOCK_SIZE], + DEVICE_DATA_TYPE scale_factors[BLOCK_SIZE], + int ipvt[BLOCK_SIZE]) { + + DEVICE_DATA_TYPE tmp_block_write[BLOCK_SIZE][BLOCK_SIZE]; + DEVICE_DATA_TYPE tmp_block_read[BLOCK_SIZE][BLOCK_SIZE]; + + // copy columnwise + for (int i = 0; i < BLOCK_SIZE; i++) { + #pragma unroll + for (int j = 0; j < BLOCK_SIZE; j++) { + tmp_block_read[i][j] = a_block_in[i][j]; + } + } + + #pragma unroll + for (int i = 0; i < BLOCK_SIZE; i++) { + ipvt[i] = i; + } + + // For each diagnonal element + #pragma max_concurrency 1 + for (int k = 0; k < BLOCK_SIZE; k++) { + + DEVICE_DATA_TYPE tmp_scale_col[BLOCK_SIZE]; + int col_order[BLOCK_SIZE]; + #pragma unroll + for (int i=0; i < BLOCK_SIZE; i++) { + col_order[i] = i; + } + + DEVICE_DATA_TYPE current_col[BLOCK_SIZE]; + #pragma unroll + for (int i=0; i k) { + tmp_block_write[j][i] = tmp_block_read[col_order[j]][i] + + tmp_scale_col[j] * tmp_block_read[col_order[k]][i]; + } + } + } + #pragma unroll + for (int i = k; i < BLOCK_SIZE; i++) { + #pragma unroll + for (int j = 0; j < BLOCK_SIZE; j++) { + tmp_block_read[i][j] = tmp_block_write[i][j]; + } + } + } + for (int i = 0; i < BLOCK_SIZE; i++) { + #pragma unroll + for (int j = 0; j < BLOCK_SIZE; j++) { + a_block_out[i][j] = tmp_block_read[i][j]; + } + } + +} + + +/** +Modifying the blocks on the leftmost side + +Case 2 of Zhangs description + +TODO: This routine is not optimized yet and just offer basic functionality + +@param top_block LU factorized top block +@param current_block_in Current input block +@param current_block_out Block to write the output to +@param scale_factors Scale factors that were calculated during LU factorization +*/ +void +left_blocks_c2(const DEVICE_DATA_TYPE top_block[BLOCK_SIZE][BLOCK_SIZE], + const DEVICE_DATA_TYPE current_block_in[BLOCK_SIZE][BLOCK_SIZE], + DEVICE_DATA_TYPE current_block_out[BLOCK_SIZE][BLOCK_SIZE], + const DEVICE_DATA_TYPE scale_factors[BLOCK_SIZE]) { + + DEVICE_DATA_TYPE tmp_block_write2[BLOCK_SIZE][BLOCK_SIZE]; + DEVICE_DATA_TYPE tmp_block_read2[BLOCK_SIZE][BLOCK_SIZE]; + DEVICE_DATA_TYPE tmp_scale_col[BLOCK_SIZE]; + + // copy columnwise + for (int i = 0; i < BLOCK_SIZE; i++) { + #pragma unroll + for (int j = 0; j < BLOCK_SIZE; j++) { + tmp_block_read2[i][j] = current_block_in[j][i]; + } + } + // For each diagonal element in top block + #pragma max_concurrency 1 + for (int k=0; k < BLOCK_SIZE; k++) { + // For each element below it in current block + #pragma unroll + for (int i=0; i < BLOCK_SIZE; i++) { + // printf("C2: %f * %f\n",tmp_block2[i][k], scale_factors[k]); + tmp_scale_col[i] = tmp_block_read2[k][i] * scale_factors[k]; + tmp_block_write2[k][i] = tmp_scale_col[i]; + } + // For each column right of the current diagnonal element + for (int j = k+1; j < BLOCK_SIZE; j++) { + #pragma unroll + for (int i = 0; i < BLOCK_SIZE; i++) { + tmp_block_write2[j][i] = + tmp_block_read2[j][i] + tmp_scale_col[i] + * top_block[k][j]; + } + } + for (int i = k; i < BLOCK_SIZE; i++) { + #pragma unroll + for (int j = 0; j < BLOCK_SIZE; j++) { + tmp_block_read2[i][j] = tmp_block_write2[i][j]; + } + } + } + for (int i = 0; i < BLOCK_SIZE; i++) { + #pragma unroll + for (int j = 0; j < BLOCK_SIZE; j++) { + current_block_out[j][i] = tmp_block_write2[i][j]; + } + } +} + + +/** +Modifying the blocks on the top but not on the left + +Case 3 of Zhangs description + +TODO: This routine is not optimized yet and just offer basic functionality + +@param left_block LU factorized left block +@param current_block_in Current input block +@param current_block_out Block to write the output to +@param ipvt Pivot information created by the LU factorization +*/ +void +top_blocks_c3(const DEVICE_DATA_TYPE left_block[BLOCK_SIZE][BLOCK_SIZE], + const DEVICE_DATA_TYPE current_block_in[BLOCK_SIZE][BLOCK_SIZE], + DEVICE_DATA_TYPE current_block_out[BLOCK_SIZE][BLOCK_SIZE], + int ipvt[BLOCK_SIZE]) { + DEVICE_DATA_TYPE tmp_block_read3[BLOCK_SIZE][BLOCK_SIZE]; + DEVICE_DATA_TYPE tmp_block_write3[BLOCK_SIZE][BLOCK_SIZE]; + + for (int j = 0; j < BLOCK_SIZE; j++) { + #pragma unroll + for (int i = 0; i < BLOCK_SIZE; i++) { + tmp_block_read3[j][i] = current_block_in[j][i]; + } + } + + // For each diagonal element in left block + #pragma max_concurrency 1 + for (int k=0; k < BLOCK_SIZE; k++) { + uint col_order[BLOCK_SIZE]; + #pragma unroll + for (int i=0; i < BLOCK_SIZE; i++) { + col_order[i] = i; + } + col_order[k] = ipvt[k]; + col_order[ipvt[k]] = k; + // For each column in current block + for (int j = k; j < BLOCK_SIZE; j++) { + DEVICE_DATA_TYPE multiply = 0.0; + // For each element below it + if (j > k) { + multiply = left_block[j][k]; + } + #pragma unroll + for (int i = 0; i < BLOCK_SIZE; i++) { + tmp_block_write3[j][i] = tmp_block_read3[col_order[j]][i] + + multiply * tmp_block_read3[col_order[k]][i]; + } + } + for (int j = k; j < BLOCK_SIZE; j++) { + #pragma unroll + for (int i = 0; i < BLOCK_SIZE; i++) { + tmp_block_read3[j][i] = tmp_block_write3[j][i]; + } + } + } + for (int j = 0; j < BLOCK_SIZE; j++) { + #pragma unroll + for (int i = 0; i < BLOCK_SIZE; i++) { + current_block_out[j][i] = tmp_block_write3[j][i]; + } + } +} + +#define SHIFT (1 << REGISTER_BLOCK_LOG) +/** +Modifying the inner blocks + +Case 4 of Zhangs description + +@param left_block Most left block that was modified by C2 before +@param top_block Most upper block that was modified by C3 before +@param current_block_in Current input block +@param current_block_out Block to write the output to +*/ +void +inner_blocks_c4(const DEVICE_DATA_TYPE left_block[BLOCK_SIZE / GEMM_BLOCK][BLOCK_SIZE / GEMM_BLOCK] +[GEMM_BLOCK][GEMM_BLOCK], + const DEVICE_DATA_TYPE top_block[BLOCK_SIZE / GEMM_BLOCK][BLOCK_SIZE / GEMM_BLOCK] + [GEMM_BLOCK][GEMM_BLOCK], + const DEVICE_DATA_TYPE current_block_in[BLOCK_SIZE][BLOCK_SIZE], + DEVICE_DATA_TYPE current_block_out[BLOCK_SIZE][BLOCK_SIZE]) { + +#pragma loop_coalesce 2 + // For each column in top block + for (int i = 0; i < BLOCK_SIZE / GEMM_BLOCK; i++) { + // For each element below it in current block + for (int j = 0; j < BLOCK_SIZE / GEMM_BLOCK; j++) { + DEVICE_DATA_TYPE tmp_small_block_out[GEMM_BLOCK][GEMM_BLOCK]; + +#pragma unroll GEMM_BLOCK + for (int ii = 0; ii < GEMM_BLOCK; ii++) { +#pragma unroll GEMM_BLOCK + for (int jj = 0; jj < GEMM_BLOCK; jj++) { + tmp_small_block_out[ii][jj] = 0; + } + } + // For each diagonal element in left block + for (int k=0; k < BLOCK_SIZE / GEMM_BLOCK; k++) { + register_gemm(left_block[i][k], top_block[k][j], + tmp_small_block_out); + } + + #pragma unroll GEMM_BLOCK + for (int ii = 0; ii < GEMM_BLOCK; ii++) { + #pragma unroll GEMM_BLOCK + for (int jj = 0; jj < GEMM_BLOCK; jj++) { + current_block_out[i * GEMM_BLOCK + ii] + [j * GEMM_BLOCK + jj] = current_block_in[i * GEMM_BLOCK + ii] + [j * GEMM_BLOCK + jj] + + tmp_small_block_out[ii][jj]; + } + } + } + } +} + + +/** +LU factorization kernel + +@param a The data array representing the whole matrix in global memory +@param pvt Pivoting information +@param a_size the x and y size of the matrix in blocks +*/ +__attribute__((uses_global_work_offset(0))) +__kernel +void gefa(global DEVICE_DATA_TYPE* restrict a, global int* restrict pvt, uint a_size) { + + // For each diagonal block do the following + for (int diagonal_block=0; diagonal_block < a_size; diagonal_block++) { +DEVICE_DATA_TYPE diag_block[BLOCK_SIZE][BLOCK_SIZE]; +DEVICE_DATA_TYPE diag_block_out[BLOCK_SIZE][BLOCK_SIZE]; + // load next block for factorization + load_block(diag_block, a, diagonal_block, diagonal_block, a_size); + +DEVICE_DATA_TYPE scale_factors[BLOCK_SIZE]; + int ipvt[BLOCK_SIZE]; + + // LU factorize the diagonal block + lu_factorization_c1(diag_block, diag_block_out, scale_factors, + ipvt); + + // Store pivoting information in global memory + #pragma unroll UNROLL_COUNT + for (int i=0; i -h) +endif() + +if (Vitis_FOUND) + include_directories(${Vitis_INCLUDE_DIRS}) + include_directories(${CMAKE_BINARY_DIR}/src/common) + add_executable(LINPACK_xilinx ${HOST_SOURCE} ${HEADER_FILES}) + target_link_libraries(LINPACK_xilinx ${Vitis_LIBRARIES} "${OpenMP_CXX_FLAGS}") + target_compile_definitions(LINPACK_xilinx PRIVATE -DXILINX_FPGA) + target_compile_options(LINPACK_xilinx PRIVATE "${OpenMP_CXX_FLAGS}") + add_test(NAME test_xilinx_host_executable COMMAND $ -h) +endif() diff --git a/LINPACK/src/host/execution.h b/LINPACK/src/host/execution.h new file mode 100644 index 00000000..d5c669ee --- /dev/null +++ b/LINPACK/src/host/execution.h @@ -0,0 +1,67 @@ +/* +Copyright (c) 2019 Marius Meyer + +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 SRC_HOST_EXECUTION_H_ +#define SRC_HOST_EXECUTION_H_ + +/* C++ standard library headers */ +#include +#include + +/* External library headers */ +#include "CL/cl.hpp" +#include "parameters.h" + + +namespace bm_execution { + + struct ExecutionConfiguration { + cl::Context context; + cl::Device device; + cl::Program program; + uint repetitions; + unsigned matrixSize; + + }; + + struct ExecutionTimings { + std::vector timings; + }; + +/** +The actual execution of the benchmark. +This method can be implemented in multiple *.cpp files. This header enables +simple exchange of the different calculation methods. + +@param config struct that contains all necessary information to execute the kernel on the FPGA + + +@return The resulting matrix +*/ + std::shared_ptr + calculate(std::shared_ptr config, + HOST_DATA_TYPE* A, + HOST_DATA_TYPE* b, + cl_int* ipvt); + +} // namespace bm_execution + +#endif // SRC_HOST_EXECUTION_H_ diff --git a/LINPACK/src/host/execution_blocked.cpp b/LINPACK/src/host/execution_blocked.cpp new file mode 100644 index 00000000..e0ff4a9b --- /dev/null +++ b/LINPACK/src/host/execution_blocked.cpp @@ -0,0 +1,163 @@ +/* +Copyright (c) 2019 Marius Meyer + +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. +*/ + +/* Related header files */ +#include "src/host/execution.h" + +/* C++ standard library headers */ +#include +#include +#include +#include + +#ifdef DEBUG +#include +#endif + +/* External library headers */ +#include "CL/cl.hpp" +#if QUARTUS_MAJOR_VERSION > 18 +#include "CL/cl_ext_intelfpga.h" +#endif + +/* Project's headers */ +#include "src/host/fpga_setup.h" +#include "src/host/linpack_functionality.h" + +namespace bm_execution { + +/* + Prepare kernels and execute benchmark for the blocked approach + + @copydoc bm_execution::calculate() +*/ +std::shared_ptr +calculate(cl::Context context, cl::Device device, cl::Program program, + uint repetitions, ulong matrixSize, uint blockSize) { + uint lda = matrixSize; + DATA_TYPE* a; + posix_memalign(reinterpret_cast(&a), 64, + sizeof(DATA_TYPE)*lda*matrixSize); + DATA_TYPE* b; + posix_memalign(reinterpret_cast(&b), 64, + sizeof(DATA_TYPE)* matrixSize); + cl_int* ipvt; + posix_memalign(reinterpret_cast(&ipvt), 64, + sizeof(cl_int) * matrixSize); + + for (int i = 0; i < matrixSize; i++) { + ipvt[i] = i; + } + + DATA_TYPE norma = 0; + double ops = (2.0e0*(matrixSize*matrixSize*matrixSize))/ + 3.0 + 2.0*(matrixSize*matrixSize); + int err; + + // Create Command queue + cl::CommandQueue compute_queue(context, device); + + // Create Buffers for input and output + cl::Buffer Buffer_a(context, CL_MEM_READ_WRITE, + sizeof(DATA_TYPE)*lda*matrixSize); + + // create the kernels + cl::Kernel gefakernel(program, GEFA_KERNEL, + &err); + ASSERT_CL(err); + + + // prepare kernels + err = gefakernel.setArg(0, Buffer_a); + ASSERT_CL(err); + err = gefakernel.setArg(1, static_cast(matrixSize / blockSize)); + ASSERT_CL(err); + + /* --- Execute actual benchmark kernels --- */ + + double t; + std::vector executionTimes; + for (int i = 0; i < repetitions; i++) { + matgen(a, lda, matrixSize, b, &norma); + compute_queue.enqueueWriteBuffer(Buffer_a, CL_TRUE, 0, + sizeof(DATA_TYPE)*lda*matrixSize, a); + compute_queue.finish(); + auto t1 = std::chrono::high_resolution_clock::now(); + compute_queue.enqueueTask(gefakernel); + compute_queue.finish(); + auto t2 = std::chrono::high_resolution_clock::now(); + std::chrono::duration timespan = + std::chrono::duration_cast> + (t2 - t1); + executionTimes.push_back(timespan.count()); + } + + /* --- Read back results from Device --- */ + + compute_queue.enqueueReadBuffer(Buffer_a, CL_TRUE, 0, + sizeof(DATA_TYPE)*lda*matrixSize, a); + +#ifdef DEBUG + for (int i= 0; i < matrixSize; i++) { + for (int j=0; j < matrixSize; j++) { + std::cout << a[i*lda + j] << ", "; + } + std::cout << std::endl; + } + std::cout << std::endl; +#endif + + gesl_ref(a, b, ipvt, matrixSize, matrixSize); + + /* --- Check Results --- */ + + double error = checkLINPACKresults(b, matrixSize, matrixSize); + + /* Check CPU reference results */ + + matgen(a, lda, matrixSize, b, &norma); + gefa_ref(a, matrixSize, lda, ipvt); + +#ifdef DEBUG + for (int i= 0; i < matrixSize; i++) { + for (int j=0; j < matrixSize; j++) { + std::cout << a[i*lda + j] << ", "; + } + std::cout << std::endl; + } + std::cout << std::endl; +#endif + + gesl_ref(a, b, ipvt, matrixSize, matrixSize); + checkLINPACKresults(b, matrixSize, matrixSize); + + free(reinterpret_cast(a)); + free(reinterpret_cast(b)); + free(reinterpret_cast(ipvt)); + + std::shared_ptr results( + new ExecutionResults{executionTimes, + error}); + return results; +} + +} // namespace bm_execution diff --git a/LINPACK/src/host/execution_blocked_pvt.cpp b/LINPACK/src/host/execution_blocked_pvt.cpp new file mode 100644 index 00000000..f77ff4b5 --- /dev/null +++ b/LINPACK/src/host/execution_blocked_pvt.cpp @@ -0,0 +1,114 @@ +/* +Copyright (c) 2019 Marius Meyer + +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. +*/ + +/* Related header files */ +#include "execution.h" + +/* C++ standard library headers */ +#include +#include +#include +#include + +/* External library headers */ +#include "CL/cl.hpp" +#if QUARTUS_MAJOR_VERSION > 18 +#include "CL/cl_ext_intelfpga.h" +#endif + +/* Project's headers */ +#include "setup/fpga_setup.hpp" +#include "linpack_functionality.hpp" + +namespace bm_execution { + +/* + Prepare kernels and execute benchmark + + @copydoc bm_execution::calculate() +*/ +std::shared_ptr +calculate(std::shared_ptr config, + HOST_DATA_TYPE* A, + HOST_DATA_TYPE* b, + cl_int* ipvt) { + + int err; + + // Create Command queue + cl::CommandQueue compute_queue(config->context, config->device); + + // Create Buffers for input and output + cl::Buffer Buffer_a(config->context, CL_MEM_READ_WRITE, + sizeof(HOST_DATA_TYPE)*config->matrixSize*config->matrixSize); + cl::Buffer Buffer_pivot(config->context, CL_MEM_READ_WRITE, + sizeof(cl_int)*config->matrixSize); + + // create the kernels + cl::Kernel gefakernel(config->program, "gefa", + &err); + ASSERT_CL(err); + + + // prepare kernels + err = gefakernel.setArg(0, Buffer_a); + ASSERT_CL(err); + err = gefakernel.setArg(1, Buffer_pivot); + ASSERT_CL(err); + err = gefakernel.setArg(2, static_cast(config->matrixSize >> LOCAL_MEM_BLOCK_LOG)); + ASSERT_CL(err); + + /* --- Execute actual benchmark kernels --- */ + + double t; + std::vector executionTimes; + for (int i = 0; i < config->repetitions; i++) { + compute_queue.enqueueWriteBuffer(Buffer_a, CL_TRUE, 0, + sizeof(HOST_DATA_TYPE)*config->matrixSize*config->matrixSize, A); + compute_queue.finish(); + auto t1 = std::chrono::high_resolution_clock::now(); + compute_queue.enqueueTask(gefakernel); + compute_queue.finish(); + auto t2 = std::chrono::high_resolution_clock::now(); + std::chrono::duration timespan = + std::chrono::duration_cast> + (t2 - t1); + executionTimes.push_back(timespan.count()); + } + + /* --- Read back results from Device --- */ + + compute_queue.enqueueReadBuffer(Buffer_a, CL_TRUE, 0, + sizeof(HOST_DATA_TYPE)*config->matrixSize*config->matrixSize, A); + compute_queue.enqueueReadBuffer(Buffer_pivot, CL_TRUE, 0, + sizeof(cl_int)*config->matrixSize, ipvt); + + // Solve linear equations on CPU + // TODO: This has to be done on FPGA + gesl_ref(A, b, ipvt, config->matrixSize, config->matrixSize); + + std::shared_ptr results( + new ExecutionTimings{executionTimes}); + return results; +} + +} // namespace bm_execution diff --git a/LINPACK/src/host/linpack_functionality.cpp b/LINPACK/src/host/linpack_functionality.cpp new file mode 100644 index 00000000..35f7d49b --- /dev/null +++ b/LINPACK/src/host/linpack_functionality.cpp @@ -0,0 +1,331 @@ +// +// Created by Marius Meyer on 04.12.19. +// + +/* +Copyright (c) 2019 Marius Meyer + +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. +*/ + +#include "linpack_functionality.hpp" + +/* C++ standard library headers */ +#include +#include + +/* Project's headers */ +#include "execution.h" +#include "cxxopts.hpp" +#include "setup/fpga_setup.hpp" +#include "parameters.h" + +/** +Parses and returns program options using the cxxopts library. +Supports the following parameters: + - file name of the FPGA kernel file (-f,--file) + - number of repetitions (-n) + - number of kernel replications (-r) + - data size (-d) + - use memory interleaving +@see https://github.com/jarro2783/cxxopts + +@return program settings that are created from the given program arguments +*/ +std::shared_ptr +parseProgramParameters(int argc, char *argv[]) { + // Defining and parsing program options + cxxopts::Options options(argv[0], PROGRAM_DESCRIPTION); + options.add_options() + ("f,file", "Kernel file name", cxxopts::value()) + ("n", "Number of repetitions", + cxxopts::value()->default_value(std::to_string(DEFAULT_REPETITIONS))) + ("s", "Size of the data arrays", + cxxopts::value()->default_value(std::to_string(DEFAULT_MATRIX_SIZE))) + ("device", "Index of the device that has to be used. If not given you "\ + "will be asked which device to use if there are multiple devices "\ + "available.", cxxopts::value()->default_value(std::to_string(DEFAULT_DEVICE))) + ("platform", "Index of the platform that has to be used. If not given "\ + "you will be asked which platform to use if there are multiple "\ + "platforms available.", + cxxopts::value()->default_value(std::to_string(DEFAULT_PLATFORM))) + ("h,help", "Print this help"); + cxxopts::ParseResult result = options.parse(argc, argv); + + if (result.count("h")) { + // Just print help when argument is given + std::cout << options.help() << std::endl; + exit(0); + } + // Check parsed options and handle special cases + if (result.count("f") <= 0) { + // Path to the kernel file is mandatory - exit if not given! + std::cerr << "Kernel file must be given! Aborting" << std::endl; + std::cout << options.help() << std::endl; + exit(1); + } + + // Create program settings from program arguments + std::shared_ptr sharedSettings( + new ProgramSettings{result["n"].as(), + result["s"].as(), + result["platform"].as(), + result["device"].as(), + result["f"].as()}); + return sharedSettings; +} + + +/** +Prints the execution results to stdout + +@param results The execution results +*/ +void +printResults(std::shared_ptr results, unsigned matrix_size) { + + std::cout << std::setw(ENTRY_SPACE) + << "best" << std::setw(ENTRY_SPACE) << "mean" + << std::setw(ENTRY_SPACE) << "GFLOPS" << std::endl; + + // Calculate performance for kernel execution plus data transfer + double tmean = 0; + double tmin = std::numeric_limits::max(); + + // GFLOPs for calculation of both GEFA and GESL. + // Currently only GEFA is calculated on the FPGA so GFLOPS have to be + // reduced. + // double gflops = ((2.0e0*(dataSize*dataSize*dataSize))/3.0 + // + 2.0*(dataSize*dataSize)) / 1.0e9; + // TODO: Change this when GESL is also calculated on FPGA + double gflops = (2.0e0*(static_cast(matrix_size)*static_cast(matrix_size)*static_cast(matrix_size)))/3.0/1.0e9; + for (double currentTime : results->timings) { + tmean += currentTime; + if (currentTime < tmin) { + tmin = currentTime; + } + } + tmean = tmean / results->timings.size(); + + std::cout << std::setw(ENTRY_SPACE) + << tmin << std::setw(ENTRY_SPACE) << tmean + << std::setw(ENTRY_SPACE) << (gflops / tmin) + << std::endl; + +} + +/** + * Prints the used configuration to std out before starting the actual benchmark. + * + * @param programSettings The program settings retrieved from the command line + * @param device The device used for execution + */ +void printFinalConfiguration(const std::shared_ptr &programSettings, + const cl::Device &device) {// Give setup summary + std::cout << PROGRAM_DESCRIPTION << std::endl; + std::cout << "Version: " << VERSION << std::endl << HLINE; + std::cout << "Summary:" << std::endl + << "Matrix Size: " << programSettings->matrixSize + << std::endl + << "Block Size: " << (1 << LOCAL_MEM_BLOCK_LOG) + << std::endl + << "Data Type " << STR(HOST_DATA_TYPE) + << std::endl + << "Repetitions: " << programSettings->numRepetitions + << std::endl + << "Kernel file: " << programSettings->kernelFileName + << std::endl; + std::cout << "Device: " + << device.getInfo() << std::endl; + std::cout << HLINE + << "Start benchmark using the given configuration." << std::endl + << HLINE; +} + + +void generateInputData(HOST_DATA_TYPE* A, HOST_DATA_TYPE* b, cl_int* ipvt, unsigned matrix_size, HOST_DATA_TYPE* norma) { + int init = 1325; + *norma = 0.0; + for (int i=0; i< matrix_size; i++) { + for (int j=0; j< matrix_size; j++) { + init = 3125 * init % 65536; + A[matrix_size*j+i] = (init - 32768.0) / 16384.0; + *norma = (fabs(A[matrix_size*j+i]) > *norma) ? fabs(A[matrix_size*j+i]) : *norma; + } + } + for (int i = 0; i < matrix_size; i++) { + b[i] = 0.0; + ipvt[i] = i; + } + for (int j = 0; j < matrix_size; j++) { + for (int i = 0; i < matrix_size; i++) { + b[j] += A[matrix_size*j+i]; + } + } +} + +double +checkLINPACKresults(const HOST_DATA_TYPE* b_res, unsigned n) { + auto a = new HOST_DATA_TYPE[n*n]; + HOST_DATA_TYPE norma; + auto x = new HOST_DATA_TYPE[n]; + auto b = new HOST_DATA_TYPE[n]; + /* compute a residual to verify results. */ + + for (int i = 0; i < n; i++) { + x[i] = b_res[i]; + } + + auto ipvt = new cl_int[n]; + generateInputData(a,b,ipvt,n, &norma); + for (int i = 0; i < n; i++) { + b[i] = -b[i]; + } + dmxpy(n, b, n, n, x, a); + HOST_DATA_TYPE resid = 0.0; + HOST_DATA_TYPE normx = 0.0; + + for (int i = 0; i < n; i++) { + resid = (resid > fabs(b[i])) ? resid : fabs(b[i]); + normx = (normx > fabs(x[i])) ? normx : fabs(x[i]); + } + + HOST_DATA_TYPE eps = std::numeric_limits::epsilon(); + HOST_DATA_TYPE residn = resid / (n*norma*normx*eps); + //std::cout << resid << ", " << norma << ", " << normx << std::endl; + std::cout << " norm. resid resid "\ + "machep x[0]-1 x[n-1]-1" << std::endl; + std::cout << std::setw(ENTRY_SPACE) << residn << std::setw(ENTRY_SPACE) + << resid << std::setw(ENTRY_SPACE) << eps + << std::setw(ENTRY_SPACE) << x[0]-1 << std::setw(ENTRY_SPACE) + << x[n-1]-1 << std::endl; + + delete [] a; + delete [] x; + delete [] b; + delete [] ipvt; + return residn; +} + +/** +Standard LU factorization on a block with fixed size + +Case 1 of Zhangs description +*/ +void +gefa_ref(HOST_DATA_TYPE* a, unsigned n, unsigned lda, cl_int* ipvt) { + for (int i = 0; i < n; i++) { + ipvt[i] = i; + } + // For each diagnonal element + for (int k = 0; k < n - 1; k++) { + HOST_DATA_TYPE max_val = fabs(a[k * lda + k]); + int pvt_index = k; + for (int i = k + 1; i < n; i++) { + if (max_val < fabs(a[i * lda + k])) { + pvt_index = i; + max_val = fabs(a[i * lda + k]); + } + } + + for (int i = k; i < n; i++) { + HOST_DATA_TYPE tmp_val = a[k * lda + i]; + a[k * lda + i] = a[pvt_index * lda + i]; + a[pvt_index * lda + i] = tmp_val; + } + ipvt[k] = pvt_index; + + // For each element below it + for (int i = k + 1; i < n; i++) { + a[i * lda + k] *= -1.0 / a[k * lda + k]; + } + // For each column right of current diagonal element + for (int j = k + 1; j < n; j++) { + // For each element below it + for (int i = k+1; i < n; i++) { + a[i * lda + j] += a[i * lda + k] * a[k * lda + j]; + } + } + +#ifdef DEBUG + std::cout << "A(k=" << k <<"): " << std::endl; + for (int i= 0; i < n; i++) { + for (int j=0; j < n; j++) { + std::cout << a[i*lda + j] << ", "; + } + std::cout << std::endl; + } + std::cout << std::endl; +#endif + + } +} + +void +gesl_ref(HOST_DATA_TYPE* a, HOST_DATA_TYPE* b, cl_int* ipvt, unsigned n, unsigned lda) { + auto b_tmp = new HOST_DATA_TYPE[n]; +#pragma omp parallel default(shared) + { +#pragma omp for + for (int k = 0; k < n; k++) { + b_tmp[k] = b[k]; + } + + // solve l*y = b + // For each row in matrix +#pragma omp single + for (int k = 0; k < n - 1; k++) { + if (ipvt[k] != k) { + HOST_DATA_TYPE tmp = b_tmp[k]; + b_tmp[k] = b_tmp[ipvt[k]]; + b_tmp[ipvt[k]] = tmp; + } + // For each row below add +#pragma omp parallel for + for (int i = k + 1; i < n; i++) { + // add solved upper row to current row + b_tmp[i] += b_tmp[k] * a[lda * i + k]; + } + } + + // now solve u*x = y +#pragma omp single + for (int k = n - 1; k >= 0; k--) { + b_tmp[k] = b_tmp[k] / a[lda * k + k]; +#pragma omp parallel for + for (int i = 0; i < k; i++) { + b_tmp[i] -= b_tmp[k] * a[lda * i + k]; + } + } +#pragma omp for + for (int k = 0; k < n; k++) { + b[k] = b_tmp[k]; + } + } + delete [] b_tmp; +} + +void dmxpy(unsigned n1, HOST_DATA_TYPE* y, unsigned n2, unsigned ldm, HOST_DATA_TYPE* x, HOST_DATA_TYPE* m) { + for (int i=0; i < n1; i++) { + for (int j=0; j < n2; j++) { + y[i] = y[i] + x[j] * m[ldm*i + j]; + } + } +} + diff --git a/LINPACK/src/host/linpack_functionality.hpp b/LINPACK/src/host/linpack_functionality.hpp new file mode 100644 index 00000000..2aa0383e --- /dev/null +++ b/LINPACK/src/host/linpack_functionality.hpp @@ -0,0 +1,140 @@ +/* +Copyright (c) 2019 Marius Meyer + +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 SRC_HOST_NETWORK_FUNCTIONALITY_H_ +#define SRC_HOST_NETWORK_FUNCTIONALITY_H_ + +/* C++ standard library headers */ +#include +#include + +/* Project's headers */ +#include "linpack_functionality.hpp" +#include "execution.h" +#include "parameters.h" + +/* +Short description of the program. +Moreover the version and build time is also compiled into the description. +*/ +#define STR_EXPAND(tok) #tok +#define STR(tok) STR_EXPAND(tok) + +#define PROGRAM_DESCRIPTION "Implementation of the LINPACK benchmark"\ + " proposed in the HPCC benchmark suite for FPGA."\ + +#define ENTRY_SPACE 15 + +struct ProgramSettings { + uint numRepetitions; + uint matrixSize; + int defaultPlatform; + int defaultDevice; + std::string kernelFileName; +}; + + +/** +Parses and returns program options using the cxxopts library. +Supports the following parameters: + - file name of the FPGA kernel file (-f,--file) + - number of repetitions (-n) + - number of kernel replications (-r) + - data size (-d) + - use memory interleaving +@see https://github.com/jarro2783/cxxopts + +@return program settings that are created from the given program arguments +*/ +std::shared_ptr +parseProgramParameters(int argc, char *argv[]); + +/** +Prints the execution results to stdout + +@param results The execution results +*/ +void +printResults(std::shared_ptr results, unsigned matrix_size); + +/** + * Prints the used configuration to std out before starting the actual benchmark. + * + * @param programSettings The program settings retrieved from the command line + * @param device The device used for execution + */ +void printFinalConfiguration(const std::shared_ptr &programSettings, + const cl::Device &device); + + +/** + * Fill the data buffer with random number using the mersenne twister engine with + * seed 0. + * + * @param data Data array that has to be filled + * @param size Size of the data array that has to be filled + */ +void generateInputData(HOST_DATA_TYPE* A, HOST_DATA_TYPE* b, cl_int* ipvt, unsigned matrix_size, HOST_DATA_TYPE* norma); + + + +double checkLINPACKresults(const HOST_DATA_TYPE* b_res, unsigned n); + +/** + * + * + * @param n1 + * @param y + * @param n2 + * @param ldm + * @param x + * @param m + */ +void dmxpy(unsigned n1, HOST_DATA_TYPE* y, unsigned n2, unsigned ldm, HOST_DATA_TYPE* x, HOST_DATA_TYPE* m); + +/** +Gaussian elemination reference implementation without pivoting. +Can be used in exchange with kernel functions for functionality testing + +@param a the matrix with size of n*n +@param n size of matrix A +@param lda row with of the matrix. must be >=n + +*/ +void gefa_ref(HOST_DATA_TYPE* a, unsigned n, unsigned lda, cl_int* ipvt); + +/** +Solve linear equations using its LU decomposition. +Therefore solves A*x = b by solving L*y = b and then U*x = y with A = LU +where A is a matrix of size n*n + +@param a the matrix a in LU representation calculated by gefa call +@param b vector b of the given equation +@param ipvt vector containing pivoting information +@param n size of matrix A +@param lda row with of the matrix. must be >=n + +*/ +void gesl_ref(HOST_DATA_TYPE* a, HOST_DATA_TYPE* b, cl_int* ipvt, unsigned n, unsigned lda); + + +#endif // SRC_HOST_NETWORK_FUNCTIONALITY_H_ diff --git a/LINPACK/src/host/main.cpp b/LINPACK/src/host/main.cpp new file mode 100644 index 00000000..8215b071 --- /dev/null +++ b/LINPACK/src/host/main.cpp @@ -0,0 +1,58 @@ +// +// Created by Marius Meyer on 04.12.19. +// + +#include "linpack_functionality.hpp" +#include "setup/fpga_setup.hpp" +#include "execution.h" + +/** +The program entry point +*/ +int +main(int argc, char *argv[]) { + // Setup benchmark + std::shared_ptr programSettings = + parseProgramParameters(argc, argv); + fpga_setup::setupEnvironmentAndClocks(); + std::vector usedDevice = + fpga_setup::selectFPGADevice(programSettings->defaultPlatform, + programSettings->defaultDevice); + cl::Context context = cl::Context(usedDevice); + cl::Program program = fpga_setup::fpgaSetup(&context, usedDevice, + &programSettings->kernelFileName); + + printFinalConfiguration(programSettings, usedDevice[0]); + + std::shared_ptr config( + new bm_execution::ExecutionConfiguration { + context, usedDevice[0], program, + programSettings->numRepetitions, + programSettings->matrixSize + }); + + HOST_DATA_TYPE *A, *b; + cl_int* ipvt; + + posix_memalign(reinterpret_cast(&A), 4096, programSettings->matrixSize * + programSettings->matrixSize * sizeof(HOST_DATA_TYPE)); + posix_memalign(reinterpret_cast(&b), 4096, programSettings->matrixSize * sizeof(HOST_DATA_TYPE)); + posix_memalign(reinterpret_cast(&ipvt), 4096, programSettings->matrixSize * sizeof(cl_int)); + + + HOST_DATA_TYPE norma; + generateInputData(A, b, ipvt, programSettings->matrixSize, &norma); + + auto timing = bm_execution::calculate(config, A, b, ipvt); + + double error = checkLINPACKresults(b, programSettings->matrixSize); + + free(A); + free(b); + free(ipvt); + + printResults(timing, programSettings->matrixSize); + + return error < 1 ? 0 : 1; +} + diff --git a/LINPACK/src/host/setup/fpga_setup.cpp b/LINPACK/src/host/setup/fpga_setup.cpp new file mode 100644 index 00000000..07845901 --- /dev/null +++ b/LINPACK/src/host/setup/fpga_setup.cpp @@ -0,0 +1,273 @@ +// +// Created by Marius Meyer on 04.12.19. +// + +#include "fpga_setup.hpp" + +#include +#include +#include +#include +#include +#include + +/* External libraries */ +#include "parameters.h" + +namespace fpga_setup { + + /** +Converts the reveived OpenCL error to a string + +@param err The OpenCL error code + +@return The string representation of the OpenCL error code +*/ + std::string + getCLErrorString(cl_int const err) { + switch (err) { + CL_ERR_TO_STR(CL_DEVICE_NOT_FOUND); + CL_ERR_TO_STR(CL_DEVICE_NOT_AVAILABLE); + CL_ERR_TO_STR(CL_COMPILER_NOT_AVAILABLE); + CL_ERR_TO_STR(CL_MEM_OBJECT_ALLOCATION_FAILURE); + CL_ERR_TO_STR(CL_OUT_OF_RESOURCES); + CL_ERR_TO_STR(CL_OUT_OF_HOST_MEMORY); + CL_ERR_TO_STR(CL_PROFILING_INFO_NOT_AVAILABLE); + CL_ERR_TO_STR(CL_MEM_COPY_OVERLAP); + CL_ERR_TO_STR(CL_IMAGE_FORMAT_MISMATCH); + CL_ERR_TO_STR(CL_IMAGE_FORMAT_NOT_SUPPORTED); + CL_ERR_TO_STR(CL_BUILD_PROGRAM_FAILURE); + CL_ERR_TO_STR(CL_MAP_FAILURE); + CL_ERR_TO_STR(CL_MISALIGNED_SUB_BUFFER_OFFSET); + CL_ERR_TO_STR(CL_EXEC_STATUS_ERROR_FOR_EVENTS_IN_WAIT_LIST); + CL_ERR_TO_STR(CL_KERNEL_ARG_INFO_NOT_AVAILABLE); + CL_ERR_TO_STR(CL_INVALID_VALUE); + CL_ERR_TO_STR(CL_INVALID_DEVICE_TYPE); + CL_ERR_TO_STR(CL_INVALID_PLATFORM); + CL_ERR_TO_STR(CL_INVALID_DEVICE); + CL_ERR_TO_STR(CL_INVALID_CONTEXT); + CL_ERR_TO_STR(CL_INVALID_QUEUE_PROPERTIES); + CL_ERR_TO_STR(CL_INVALID_COMMAND_QUEUE); + CL_ERR_TO_STR(CL_INVALID_HOST_PTR); + CL_ERR_TO_STR(CL_INVALID_MEM_OBJECT); + CL_ERR_TO_STR(CL_INVALID_IMAGE_FORMAT_DESCRIPTOR); + CL_ERR_TO_STR(CL_INVALID_IMAGE_SIZE); + CL_ERR_TO_STR(CL_INVALID_SAMPLER); + CL_ERR_TO_STR(CL_INVALID_BINARY); + CL_ERR_TO_STR(CL_INVALID_BUILD_OPTIONS); + CL_ERR_TO_STR(CL_INVALID_PROGRAM); + CL_ERR_TO_STR(CL_INVALID_PROGRAM_EXECUTABLE); + CL_ERR_TO_STR(CL_INVALID_KERNEL_NAME); + CL_ERR_TO_STR(CL_INVALID_KERNEL_DEFINITION); + CL_ERR_TO_STR(CL_INVALID_KERNEL); + CL_ERR_TO_STR(CL_INVALID_ARG_INDEX); + CL_ERR_TO_STR(CL_INVALID_ARG_VALUE); + CL_ERR_TO_STR(CL_INVALID_ARG_SIZE); + CL_ERR_TO_STR(CL_INVALID_KERNEL_ARGS); + CL_ERR_TO_STR(CL_INVALID_WORK_DIMENSION); + CL_ERR_TO_STR(CL_INVALID_WORK_GROUP_SIZE); + CL_ERR_TO_STR(CL_INVALID_WORK_ITEM_SIZE); + CL_ERR_TO_STR(CL_INVALID_GLOBAL_OFFSET); + CL_ERR_TO_STR(CL_INVALID_EVENT_WAIT_LIST); + CL_ERR_TO_STR(CL_INVALID_EVENT); + CL_ERR_TO_STR(CL_INVALID_OPERATION); + CL_ERR_TO_STR(CL_INVALID_GL_OBJECT); + CL_ERR_TO_STR(CL_INVALID_BUFFER_SIZE); + CL_ERR_TO_STR(CL_INVALID_MIP_LEVEL); + CL_ERR_TO_STR(CL_INVALID_GLOBAL_WORK_SIZE); + CL_ERR_TO_STR(CL_COMPILE_PROGRAM_FAILURE); + CL_ERR_TO_STR(CL_LINKER_NOT_AVAILABLE); + CL_ERR_TO_STR(CL_LINK_PROGRAM_FAILURE); + CL_ERR_TO_STR(CL_DEVICE_PARTITION_FAILED); + CL_ERR_TO_STR(CL_INVALID_PROPERTY); + CL_ERR_TO_STR(CL_INVALID_IMAGE_DESCRIPTOR); + CL_ERR_TO_STR(CL_INVALID_COMPILER_OPTIONS); + CL_ERR_TO_STR(CL_INVALID_LINKER_OPTIONS); + CL_ERR_TO_STR(CL_INVALID_DEVICE_PARTITION_COUNT); + + default: + return "UNKNOWN ERROR CODE"; + } + } + +/** +Check the OpenCL return code for errors. +If an error is detected, it will be printed and the programm execution is +stopped. + +@param err The OpenCL error code +*/ + void + handleClReturnCode(cl_int const err, std::string const file, + int const line) { + if (err != CL_SUCCESS) { + std::string err_string = getCLErrorString(err); + std::cerr << "ERROR in OpenCL library detected! Aborting." + << std::endl << file << ":" << line << ": " << err_string + << std::endl; + exit(err); + } + } + +/** +Sets up the given FPGA with the kernel in the provided file. + +@param context The context used for the program +@param program The devices used for the program +@param usedKernelFile The path to the kernel file +@return The program that is used to create the benchmark kernels +*/ + cl::Program + fpgaSetup(const cl::Context *context, std::vector deviceList, + const std::string *usedKernelFile) { + int err; + + std::cout << HLINE; + std::cout << "FPGA Setup:" << usedKernelFile->c_str() << std::endl; + + // Open file stream if possible + std::ifstream aocxStream(usedKernelFile->c_str(), std::ifstream::binary); + if (!aocxStream.is_open()) { + std::cerr << "Not possible to open from given file!" << std::endl; + } + + // Read in file contents and create program from binaries + std::string prog(std::istreambuf_iterator(aocxStream), + (std::istreambuf_iterator())); + aocxStream.seekg(0, aocxStream.end); + unsigned file_size = aocxStream.tellg(); + aocxStream.seekg(0, aocxStream.beg); + char *buf = new char[file_size]; + aocxStream.read(buf, file_size); + + cl::Program::Binaries mybinaries; + mybinaries.push_back({buf, file_size}); + + // Create the Program from the AOCX file. + cl::Program program(*context, deviceList, mybinaries, NULL, &err); + ASSERT_CL(err); + std::cout << "Prepared FPGA successfully for global Execution!" << + std::endl; + std::cout << HLINE; + return program; + } + +/** +Sets up the C++ environment by configuring std::cout and checking the clock +granularity using bm_helper::checktick() +*/ + void + setupEnvironmentAndClocks() { + std::cout << std::setprecision(5) << std::scientific; + + std::cout << HLINE; + std::cout << "General setup:" << std::endl; + + // Check clock granularity and output result + std::cout << "C++ high resolution clock is used." << std::endl; + std::cout << "The clock precision seems to be " + << static_cast + (std::chrono::high_resolution_clock::period::num) / + std::chrono::high_resolution_clock::period::den * 10e9 + << "ns" << std::endl; + + std::cout << HLINE; + } + + +/** +Searches an selects an FPGA device using the CL library functions. +If multiple platforms or devices are given, the user will be prompted to +choose a device. + +@param defaultPlatform The index of the platform that has to be used. If a + value < 0 is given, the platform can be chosen + interactively +@param defaultDevice The index of the device that has to be used. If a + value < 0 is given, the device can be chosen + interactively + +@return A list containing a single selected device +*/ + std::vector + selectFPGADevice(int defaultPlatform, int defaultDevice) { + // Integer used to store return codes of OpenCL library calls + int err; + + std::vector platformList; + err = cl::Platform::get(&platformList); + ASSERT_CL(err); + + // Choose the target platform + int chosenPlatformId = 0; + if (defaultPlatform >= 0) { + if (defaultPlatform < platformList.size()) { + chosenPlatformId = defaultPlatform; + } else { + std::cerr << "Default platform " << defaultPlatform + << " can not be used. Available platforms: " + << platformList.size() << std::endl; + exit(1); + } + } else if (platformList.size() > 1) { + std::cout << + "Multiple platforms have been found. Select the platform by"\ + " typing a number:" << std::endl; + for (int platformId = 0; + platformId < platformList.size(); platformId++) { + std::cout << platformId << ") " << + platformList[platformId].getInfo() << + std::endl; + } + std::cout << "Enter platform id [0-" << platformList.size() - 1 + << "]:"; + std::cin >> chosenPlatformId; + } + cl::Platform platform = platformList[chosenPlatformId]; + std::cout << "Selected Platform: " + << platform.getInfo() << std::endl; + + std::vector deviceList; + err = platform.getDevices(CL_DEVICE_TYPE_ACCELERATOR, &deviceList); + ASSERT_CL(err); + + // Choose taget device + int chosenDeviceId = 0; + if (defaultDevice >= 0) { + if (defaultDevice < deviceList.size()) { + chosenDeviceId = defaultDevice; + } else { + std::cerr << "Default platform " << defaultDevice + << " can not be used. Available platforms: " + << deviceList.size() << std::endl; + exit(1); + } + } else if (deviceList.size() > 1) { + std::cout << + "Multiple devices have been found. Select the platform by"\ + " typing a number:" << std::endl; + for (int deviceId = 0; + deviceId < deviceList.size(); deviceId++) { + std::cout << deviceId << ") " << + deviceList[deviceId].getInfo() << + std::endl; + } + std::cout << "Enter device id [0-" << deviceList.size() - 1 << "]:"; + std::cin >> chosenDeviceId; + } + std::vector chosenDeviceList; + chosenDeviceList.push_back(deviceList[chosenDeviceId]); + + // Give selection summary + std::cout << HLINE; + std::cout << "Selection summary:" << std::endl; + std::cout << "Platform Name: " << + platform.getInfo() << std::endl; + std::cout << "Device Name: " << + chosenDeviceList[0].getInfo() << std::endl; + std::cout << HLINE; + + return chosenDeviceList; + } + +} // namespace fpga_setup \ No newline at end of file diff --git a/LINPACK/src/host/setup/fpga_setup.hpp b/LINPACK/src/host/setup/fpga_setup.hpp new file mode 100644 index 00000000..c1f78b0c --- /dev/null +++ b/LINPACK/src/host/setup/fpga_setup.hpp @@ -0,0 +1,108 @@ +/* +Copyright (c) 2019 Marius Meyer + +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 SRC_HOST_FPGA_SETUP_H_ +#define SRC_HOST_FPGA_SETUP_H_ + +#include +#include +#include +#include +#include +#include + +/* External libraries */ +#include "CL/cl.hpp" + +/** +Makro to convert the error integer representation to its string representation +Source: https://gist.github.com/allanmac/9328bb2d6a99b86883195f8f78fd1b93 +*/ +#define CL_ERR_TO_STR(err) case err: return #err + +namespace fpga_setup { + + /** +Converts the reveived OpenCL error to a string + +@param err The OpenCL error code + +@return The string representation of the OpenCL error code +*/ + std::string + getCLErrorString(cl_int const err); + +/** +Check the OpenCL return code for errors. +If an error is detected, it will be printed and the programm execution is +stopped. + +@param err The OpenCL error code +*/ + void + handleClReturnCode(cl_int const err, std::string const file, + int const line); + +/** +Makro that enables checks for OpenCL errors with handling of the file and +line number. +*/ +#define ASSERT_CL(err) fpga_setup::handleClReturnCode(err, __FILE__, __LINE__) + +/** +Sets up the given FPGA with the kernel in the provided file. + +@param context The context used for the program +@param program The devices used for the program +@param usedKernelFile The path to the kernel file +@return The program that is used to create the benchmark kernels +*/ + cl::Program + fpgaSetup(const cl::Context *context, std::vector deviceList, + const std::string *usedKernelFile); + +/** +Sets up the C++ environment by configuring std::cout and checking the clock +granularity using bm_helper::checktick() +*/ + void + setupEnvironmentAndClocks(); + + +/** +Searches an selects an FPGA device using the CL library functions. +If multiple platforms or devices are given, the user will be prompted to +choose a device. + +@param defaultPlatform The index of the platform that has to be used. If a + value < 0 is given, the platform can be chosen + interactively +@param defaultDevice The index of the device that has to be used. If a + value < 0 is given, the device can be chosen + interactively + +@return A list containing a single selected device +*/ + std::vector + selectFPGADevice(int defaultPlatform, int defaultDevice); + +} // namespace fpga_setup +#endif // SRC_HOST_FPGA_SETUP_H_ diff --git a/LINPACK/tests/CMakeLists.txt b/LINPACK/tests/CMakeLists.txt new file mode 100755 index 00000000..a1b22648 --- /dev/null +++ b/LINPACK/tests/CMakeLists.txt @@ -0,0 +1,43 @@ +# 'lib' is the folder with Google Test sources +add_subdirectory(../../extern/googletest ${CMAKE_CURRENT_BINARY_DIR}/lib) +include_directories(${gtest_SOURCE_DIR}/include ${gtest_SOURCE_DIR}) +include_directories(${CMAKE_BINARY_DIR}/src/common ../../extern/cxxopts/include) + + +set(PROJECT_SOURCES ../src/host/setup/fpga_setup.cpp ../src/host/execution_blocked_pvt.cpp ../src/host/linpack_functionality.cpp) +set(TEST_SOURCES test_fpga_setup.cpp test_kernel_functionality_and_host_integration.cpp test_kernel_functionality_separate_cores.cpp) + +set(BLA_VENDOR Intel10_64lp) +find_package(LAPACK) + +if (NOT LAPACK_FOUND) + message(WARNING "MKL not found! Will not implement all unit tests since MKL is needed to run them.") +endif() + + +if (INTELFPGAOPENCL_FOUND) + include_directories(SYSTEM ${IntelFPGAOpenCL_INCLUDE_DIRS}) + add_executable(Test_intel ${TEST_SOURCES} ${PROJECT_SOURCES}) + target_link_libraries(Test_intel gtest gmock gtest_main ${IntelFPGAOpenCL_LIBRARIES} "${OpenMP_CXX_FLAGS}") + target_compile_definitions(Test_intel PRIVATE -DINTEL_FPGA) + if (LAPACK_FOUND) + target_compile_definitions(Test_intel PRIVATE -D_INTEL_MKL_) + target_link_libraries(Test_intel ${LAPACK_LIBRARIES}) + include_directories(SYSTEM $ENV{MKLROOT}/include) + endif() + add_dependencies(Test_intel lu_blocked_pvt_emulate_intel lu_blocked_pvt_test_emulate_intel) + target_compile_options(Test_intel PRIVATE "${OpenMP_CXX_FLAGS}") + add_test(NAME test_intel_unit COMMAND $) +endif() + +if (Vitis_FOUND) + include_directories(SYSTEM ${Vitis_INCLUDE_DIRS}) + add_executable(Test_xilinx ${TEST_SOURCES} ${PROJECT_SOURCES}) + target_link_libraries(Test_xilinx gtest gmock gtest_main ${Vitis_LIBRARIES} "${OpenMP_CXX_FLAGS}") + target_compile_definitions(Test_xilinx PRIVATE -DXILINX_FPGA) + if (LAPACK_FOUND) + target_compile_definitions(Test_intel PRIVATE -D_INTEL_MKL_) + endif() + target_compile_options(Test_xilinx PRIVATE "${OpenMP_CXX_FLAGS}") + add_test(NAME test_xilinx_unit COMMAND $) +endif() diff --git a/LINPACK/tests/test_fpga_setup.cpp b/LINPACK/tests/test_fpga_setup.cpp new file mode 100644 index 00000000..a3da9cee --- /dev/null +++ b/LINPACK/tests/test_fpga_setup.cpp @@ -0,0 +1,38 @@ +// +// Created by Marius Meyer on 04.12.19 +// +#include "gtest/gtest.h" +#include "../src/host/setup/fpga_setup.hpp" +#include "parameters.h" + +/** + * Check if it is possible to find the platform and device that are given as default + */ +TEST (FPGASetup, FindValidPlatformAndDevice) { + EXPECT_EQ (1, fpga_setup::selectFPGADevice(DEFAULT_PLATFORM, DEFAULT_DEVICE).size()); +} + +/** + * Checks if non existing platform leads to an error + */ +TEST (FPGASetup, FindNonExistingPlatform) { + // Execute thread safe test because emulation leads to problems in google test detecting the exit of the routine + testing::FLAGS_gtest_death_test_style="threadsafe"; + // TODO regex does not work so for now its not tested! + EXPECT_EXIT(fpga_setup::selectFPGADevice(DEFAULT_PLATFORM + 100, DEFAULT_DEVICE), + ::testing::ExitedWithCode(1), + ::testing::MatchesRegex(".*")); +} + +/** + * Checks if non existing device leads to an error + */ +TEST (FPGASetup, FindNonExistingDevice) { + // Execute thread safe test because emulation leads to problems in google test detecting the exit of the routine + testing::FLAGS_gtest_death_test_style="threadsafe"; + // TODO regex does not work so for now its not tested! + EXPECT_EXIT(fpga_setup::selectFPGADevice(DEFAULT_PLATFORM, DEFAULT_DEVICE + 100), + ::testing::ExitedWithCode(1), + ::testing::MatchesRegex(".*")); +} + diff --git a/LINPACK/tests/test_kernel_functionality_and_host_integration.cpp b/LINPACK/tests/test_kernel_functionality_and_host_integration.cpp new file mode 100644 index 00000000..70743149 --- /dev/null +++ b/LINPACK/tests/test_kernel_functionality_and_host_integration.cpp @@ -0,0 +1,194 @@ +// +// Created by Marius Meyer on 14.02.20. +// +#include "gtest/gtest.h" +#include "parameters.h" +#include "../src/host/execution.h" +#include "../src/host/setup/fpga_setup.hpp" +#include "../src/host/linpack_functionality.hpp" +#ifdef _INTEL_MKL_ +#include "mkl.h" +#endif +struct OpenCLKernelTest : testing::Test { + HOST_DATA_TYPE *A; + HOST_DATA_TYPE *b; + cl_int *ipvt; + std::shared_ptr config; + cl_uint array_size; + std::string lastKernelFileName; + + OpenCLKernelTest() { + array_size = (1 << LOCAL_MEM_BLOCK_LOG); + posix_memalign(reinterpret_cast(&A), 64, + sizeof(HOST_DATA_TYPE) * array_size * array_size); + posix_memalign(reinterpret_cast(&b), 64, + sizeof(HOST_DATA_TYPE) * array_size); + posix_memalign(reinterpret_cast(&ipvt), 64, + sizeof(cl_int) * array_size); + } + + void setupFPGA(std::string kernelFileName) { + lastKernelFileName = kernelFileName; + std::vector device = fpga_setup::selectFPGADevice(DEFAULT_PLATFORM, DEFAULT_DEVICE); + cl::Context context(device[0]); + cl::Program program = fpga_setup::fpgaSetup(&context, device, &kernelFileName); + config = std::make_shared( + bm_execution::ExecutionConfiguration{ + context, device[0], program, + 1, + array_size, + }); + HOST_DATA_TYPE norm; + generateInputData(A, b, ipvt, array_size, &norm); + } + + ~OpenCLKernelTest() override { + free(A); + free(b); + free(ipvt); + } +}; + +struct DifferentOpenCLKernelTest : OpenCLKernelTest, testing::WithParamInterface { + DifferentOpenCLKernelTest() { + auto params = GetParam(); + auto kernel_file = params; + setupFPGA(kernel_file); + } +}; + + +/** + * Execution returns correct results for a single repetition + */ +TEST_P(DifferentOpenCLKernelTest, FPGACorrectResultsOneRepetition) { + + auto result = bm_execution::calculate(config, A, b, ipvt); + for (int i = 0; i < array_size; i++) { + EXPECT_NEAR(b[i], 1.0, 1.0e-3); + } +} + +#ifdef __INTEL_MKL__ +/** + * Execution returns correct results for a single repetition + */ +TEST_P(DifferentOpenCLKernelTest, FPGASimilarResultsToLAPACKforSingleBlock) { + + auto result = bm_execution::calculate(config, A, b, ipvt); + int info; + HOST_DATA_TYPE* bcpu = new HOST_DATA_TYPE[array_size]; + HOST_DATA_TYPE* Acpu = new HOST_DATA_TYPE[array_size * array_size]; + HOST_DATA_TYPE norm; + generateInputData(A, bcpu, ipvt, array_size, &norm); + for (int i=0; i(array_size); + int lrhs = 1; +#ifndef _DP + sgesv(&s, &lrhs, Acpu, &s, ipvt, bcpu, &s, &info); +#else + dgesv(&s, &lrhs, Acpu, &s, ipvt, bcpu, &s, &info); +#endif + double error_emu = checkLINPACKresults(b, array_size); + double error_cpu = checkLINPACKresults(bcpu, array_size); + EXPECT_LE(error_emu, error_cpu+ 1.0); + delete [] Acpu; + delete [] bcpu; +} + +/** + * Execution of reference implementation returns correct results for a single repetition + */ +TEST_P(DifferentOpenCLKernelTest, FPGAReferenceImplSimilarToMKL) { + + gefa_ref(A, config->matrixSize, config->matrixSize, ipvt); + gesl_ref(A, b, ipvt, config->matrixSize, config->matrixSize); + int info; + HOST_DATA_TYPE* bcpu = new HOST_DATA_TYPE[array_size]; + HOST_DATA_TYPE* Acpu = new HOST_DATA_TYPE[array_size * array_size]; + HOST_DATA_TYPE norm; + generateInputData(A, bcpu, ipvt, array_size, &norm); + for (int i=0; i(array_size); + int lrhs = 1; +#ifndef _DP + sgesv(&s, &lrhs, Acpu, &s, ipvt, bcpu, &s, &info); +#else + dgesv(&s, &lrhs, Acpu, &s, ipvt, bcpu, &s, &info); +#endif + double error_emu = checkLINPACKresults(b, array_size); + double error_cpu = checkLINPACKresults(bcpu, array_size); + EXPECT_LE(error_emu, error_cpu+ 1.0); + delete [] Acpu; + delete [] bcpu; +} + + +/** + * Execution returns correct results for a single repetition + */ +// TODO this test fails most likely because of inreasing errors in C2. Use partial pivoting or other mechanisms +// to make the calculation stable again! +// Remove DISABLED_ from test name to enable the test again. +TEST_P(DifferentOpenCLKernelTest, DISABLED_FPGASimilarResultsToLAPACKforMultipleBlocks) { + free(A); + free(b); + free(ipvt); + array_size = 4 * (1 << LOCAL_MEM_BLOCK_LOG); + posix_memalign(reinterpret_cast(&A), 64, + sizeof(HOST_DATA_TYPE) * array_size * array_size); + posix_memalign(reinterpret_cast(&b), 64, + sizeof(HOST_DATA_TYPE) * array_size); + posix_memalign(reinterpret_cast(&ipvt), 64, + sizeof(cl_int) * array_size); + setupFPGA(lastKernelFileName); + auto result = bm_execution::calculate(config, A, b, ipvt); + + int info; + HOST_DATA_TYPE* bcpu = new HOST_DATA_TYPE[array_size]; + HOST_DATA_TYPE* Acpu = new HOST_DATA_TYPE[array_size * array_size]; + HOST_DATA_TYPE norm; + generateInputData(A, bcpu, ipvt, array_size, &norm); + for (int i=0; i(array_size); + int lrhs = 1; +#ifndef _DP + sgesv(&s, &lrhs, Acpu, &s, ipvt, bcpu, &s, &info); +#else + dgesv(&s, &lrhs, Acpu, &s, ipvt, bcpu, &s, &info); +#endif + double error_emu = checkLINPACKresults(b, array_size); + double error_cpu = checkLINPACKresults(bcpu, array_size); + + EXPECT_LE(error_emu, error_cpu + 1.0); + + delete [] Acpu; + delete [] bcpu; +} + + +#endif + +#ifdef INTEL_FPGA +INSTANTIATE_TEST_CASE_P(Default, DifferentOpenCLKernelTest, + testing::Values("lu_blocked_pvt_emulate.aocx") +); +#endif + +#ifdef XILINX_FPGA +INSTANTIATE_TEST_CASE_P(Default, DifferentOpenCLKernelTest, + testing::Values("lu_blocked_pvt_emulate.xclbin") +); +#endif diff --git a/LINPACK/tests/test_kernel_functionality_separate_cores.cpp b/LINPACK/tests/test_kernel_functionality_separate_cores.cpp new file mode 100644 index 00000000..92b286d8 --- /dev/null +++ b/LINPACK/tests/test_kernel_functionality_separate_cores.cpp @@ -0,0 +1,290 @@ +// +// Created by Marius Meyer on 14.02.20. +// +#include "gtest/gtest.h" +#include "parameters.h" +#include "../src/host/execution.h" +#include "../src/host/setup/fpga_setup.hpp" +#include "../src/host/linpack_functionality.hpp" +#include +#ifdef _INTEL_MKL_ +#include "mkl.h" +#endif + +struct OpenCLKernelSeparateTest : testing::Test { + HOST_DATA_TYPE *A, *B, *C, *scale; + cl_int *ipvt; + std::shared_ptr config; + cl_uint array_size; + std::string lastKernelFileName; + + OpenCLKernelSeparateTest() { + array_size = (1 << LOCAL_MEM_BLOCK_LOG); + posix_memalign(reinterpret_cast(&A), 4096, + sizeof(HOST_DATA_TYPE) * array_size * array_size); + posix_memalign(reinterpret_cast(&B), 4096, + sizeof(HOST_DATA_TYPE) * array_size * array_size); + posix_memalign(reinterpret_cast(&C), 4096, + sizeof(HOST_DATA_TYPE) * array_size * array_size); + posix_memalign(reinterpret_cast(&scale), 4096, + sizeof(HOST_DATA_TYPE) * array_size ); + posix_memalign(reinterpret_cast(&ipvt), 4096, + sizeof(cl_int) * array_size); + } + + void setupFPGA(std::string kernelFileName) { + lastKernelFileName = kernelFileName; + std::vector device = fpga_setup::selectFPGADevice(DEFAULT_PLATFORM, DEFAULT_DEVICE); + cl::Context context(device[0]); + cl::Program program = fpga_setup::fpgaSetup(&context, device, &kernelFileName); + config = std::make_shared( + bm_execution::ExecutionConfiguration{ + context, device[0], program, + 1, + array_size, + }); + } + + void initializeData() { + int init = 1325; + for (int i=0; icontext, config->device); + + // Create Buffers for input and output + cl::Buffer Buffer_a(config->context, CL_MEM_READ_WRITE, + sizeof(HOST_DATA_TYPE)*config->matrixSize*config->matrixSize); + cl::Buffer Buffer_b(config->context, CL_MEM_READ_WRITE, + sizeof(HOST_DATA_TYPE)*config->matrixSize*config->matrixSize); + cl::Buffer Buffer_c(config->context, CL_MEM_READ_WRITE, + sizeof(HOST_DATA_TYPE)*config->matrixSize*config->matrixSize); + cl::Buffer Buffer_scale(config->context, CL_MEM_READ_WRITE, + sizeof(HOST_DATA_TYPE)*config->matrixSize); + cl::Buffer Buffer_pivot(config->context, CL_MEM_READ_WRITE, + sizeof(cl_int)*config->matrixSize); + + // create the kernels + cl::Kernel test_c4_kernel(config->program, kernel_name.c_str(), + &err); + ASSERT_CL(err); + + + // prepare kernels + err = test_c4_kernel.setArg(0, Buffer_a); + ASSERT_CL(err); + err = test_c4_kernel.setArg(1, Buffer_b); + ASSERT_CL(err); + err = test_c4_kernel.setArg(2, Buffer_c); + ASSERT_CL(err); + err = test_c4_kernel.setArg(3, Buffer_scale); + ASSERT_CL(err); + err = test_c4_kernel.setArg(4, Buffer_pivot); + ASSERT_CL(err); + err = test_c4_kernel.setArg(5, static_cast(config->matrixSize >> LOCAL_MEM_BLOCK_LOG)); + ASSERT_CL(err); + + /* --- Execute actual benchmark kernels --- */ + + double t; + std::vector executionTimes; + for (int i = 0; i < config->repetitions; i++) { + compute_queue.enqueueWriteBuffer(Buffer_a, CL_TRUE, 0, + sizeof(HOST_DATA_TYPE)*config->matrixSize*config->matrixSize, A); + compute_queue.enqueueWriteBuffer(Buffer_b, CL_TRUE, 0, + sizeof(HOST_DATA_TYPE)*config->matrixSize*config->matrixSize, B); + compute_queue.enqueueWriteBuffer(Buffer_c, CL_TRUE, 0, + sizeof(HOST_DATA_TYPE)*config->matrixSize*config->matrixSize, C); + compute_queue.enqueueWriteBuffer(Buffer_scale, CL_TRUE, 0, + sizeof(HOST_DATA_TYPE)*config->matrixSize, scale); + compute_queue.enqueueWriteBuffer(Buffer_pivot, CL_TRUE, 0, + sizeof(cl_int)*config->matrixSize, ipvt); + compute_queue.finish(); + auto t1 = std::chrono::high_resolution_clock::now(); + compute_queue.enqueueTask(test_c4_kernel); + compute_queue.finish(); + auto t2 = std::chrono::high_resolution_clock::now(); + std::chrono::duration timespan = + std::chrono::duration_cast> + (t2 - t1); + executionTimes.push_back(timespan.count()); + } + + /* --- Read back results from Device --- */ + compute_queue.enqueueReadBuffer(Buffer_a, CL_TRUE, 0, + sizeof(HOST_DATA_TYPE)*config->matrixSize*config->matrixSize, A); + compute_queue.enqueueReadBuffer(Buffer_b, CL_TRUE, 0, + sizeof(HOST_DATA_TYPE)*config->matrixSize*config->matrixSize, B); + compute_queue.enqueueReadBuffer(Buffer_c, CL_TRUE, 0, + sizeof(HOST_DATA_TYPE)*config->matrixSize*config->matrixSize, C); + } + + ~OpenCLKernelSeparateTest() override { + free(A); + free(B); + free(C); + free(ipvt); + } +}; + +struct DifferentOpenCLKernelSeparateTest : OpenCLKernelSeparateTest, testing::WithParamInterface { + DifferentOpenCLKernelSeparateTest() { + auto params = GetParam(); + auto kernel_file = params; + setupFPGA(kernel_file); + } +}; + +/** + * Execution returns correct results for a single repetition + */ +TEST_P(DifferentOpenCLKernelSeparateTest, FPGACorrectResultsForC1) { + auto a_result = new HOST_DATA_TYPE[array_size * array_size]; + initializeData(); + executeTest("test_c1"); + for (int i=0; i::epsilon() * array_size * array_size); + EXPECT_LT(normalized_error, 5.0); + delete [] a_result; +} + +/** + * Execution returns correct results for a single repetition + */ +TEST_P(DifferentOpenCLKernelSeparateTest, FPGACorrectResultsForC2) { + auto b_result = new HOST_DATA_TYPE[array_size * array_size]; + initializeData(); + executeTest("test_c2"); + for (int i=0; i::epsilon() * array_size * array_size); + EXPECT_LT(normalized_error, 5.0); + delete [] b_result; +} + +/** + * Execution returns correct results for a single repetition + */ +TEST_P(DifferentOpenCLKernelSeparateTest, FPGACorrectResultsForC3) { + auto b_result = new HOST_DATA_TYPE[array_size * array_size]; + initializeData(); + executeTest("test_c3"); + for (int i=0; i::epsilon() * array_size * array_size); + EXPECT_LT(normalized_error, 5.0); + delete [] b_result; +} + +/** + * Execution returns correct results for a single repetition + */ +TEST_P(DifferentOpenCLKernelSeparateTest, FPGACorrectResultsForC4) { + auto c_result = new HOST_DATA_TYPE[array_size * array_size]; + initializeData(); + executeTest("test_c4"); + for (int i=0; i::epsilon() * array_size * array_size); + EXPECT_LT(normalized_error, 10.0); + EXPECT_LT(error, 1.0e-3); + delete [] c_result; +} + +#ifdef INTEL_FPGA +INSTANTIATE_TEST_CASE_P(Default, DifferentOpenCLKernelSeparateTest, + testing::Values("lu_blocked_pvt_test_emulate.aocx") +); +#endif + +#ifdef XILINX_FPGA +INSTANTIATE_TEST_CASE_P(Default, DifferentOpenCLKernelTest, + testing::Values("lu_blocked_pvt_emulate.xclbin") +); +#endif diff --git a/PTRANS/.gitignore b/PTRANS/.gitignore new file mode 100644 index 00000000..5af72727 --- /dev/null +++ b/PTRANS/.gitignore @@ -0,0 +1,3 @@ +# Exclude cmake build folder and CLion files +cmake-build* +.idea diff --git a/PTRANS/CMakeLists.txt b/PTRANS/CMakeLists.txt new file mode 100755 index 00000000..572a7469 --- /dev/null +++ b/PTRANS/CMakeLists.txt @@ -0,0 +1,36 @@ +cmake_minimum_required(VERSION 2.8.4) +project(fTrans) + +set(VERSION 1.0.0) +set(KERNEL_NAME transpose CACHE STRING "Name of the OpenCL kernel") +set(DEFAULT_REPETITIONS 10 CACHE STRING "Default number of repetitions") +set(DEFAULT_MATRIX_SIZE 4096 CACHE STRING "Default size of the used matrices") +set(DEFAULT_PLATFORM -1 CACHE STRING "Index of the default platform to use") +set(DEFAULT_DEVICE -1 CACHE STRING "Index of the default device to use") +set(BLOCK_SIZE 512 CACHE STRING "Block size used in the FPGA kernel") +set(GLOBAL_MEM_UNROLL 16 CACHE STRING "Unrolling factor used to stream data") +set(DATA_TYPE float CACHE STRING "Data type used for calculation") +set(FPGA_BOARD_NAME p520_hpc_sg280l CACHE STRING "Name of the target FPGA board") + +set(AOC_FLAGS "-fpc -fp-relaxed" CACHE STRING "Used flags for the AOC compiler") +separate_arguments(AOC_FLAGS) + +set(HOST_DATA_TYPE cl_${DATA_TYPE}) +set(DEVICE_DATA_TYPE ${DATA_TYPE}) + +set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_SOURCE_DIR}/../extern/hlslib/cmake) +set(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin) + +configure_file( + "${CMAKE_SOURCE_DIR}/src/common/parameters.h.in" + "${CMAKE_BINARY_DIR}/src/common/parameters.h" +) + +include_directories(${CMAKE_BINARY_DIR}/src/common) + +find_package(IntelFPGAOpenCL REQUIRED) + +add_subdirectory(src/device) +add_subdirectory(src/host) +add_subdirectory(tests) + diff --git a/PTRANS/README.md b/PTRANS/README.md new file mode 100644 index 00000000..77083190 --- /dev/null +++ b/PTRANS/README.md @@ -0,0 +1,116 @@ +# Matrix Transposition Benchmark for FPGA + +This repository contains the Matrix Transposition Benchmark for FPGA and its OpenCL kernels. +Currently only the Intel FPGA SDK for OpenCL utility is supported. + +It is a modified implementation of the +[Matrix Transposition Benchmark](http://www.netlib.org/parkbench/html/matrix-kernels.html) +provided in the [HPC Challenge Benchmark](https://icl.utk.edu/hpcc/) suite. +The implementation follows the Python reference implementation given in +_Introduction to the HPCChallenge Benchmark Suite_ available +[here](http://icl.cs.utk.edu/news_pub/submissions/hpcc-challenge-intro.pdf). + +## Dependencies + +The benchmark comes with the following requirements for building and running: + +- CMake 2.8 +- GCC 4.9 +- Intel OpenCL FPGA SDK 19.3 + +It also contains submodules that will be automatically updated when running cmake: + +- cxxopts: A header only library to parse command line parameters +- googletest: A C++ test framework + +## Build + +CMake is used as the build system. +The targets below can be used to build the benchmark and its kernels: + + | Target | Description | + | -------- | ---------------------------------------------- | + | fTrans | Builds the host application | + | Google_Tests_run| Compile the tests and its dependencies | + + More over the are additional targets to generate kernel reports and bitstreams. + They are generated for every kernel code in the `src/device` folder: + + | Target | Description | + | -------- | ---------------------------------------------- | + | KERNEL_FILE_NAME | Synthesizes the kernel (takes several hours!) | + | KERNEL_FILE_NAME_report | Create an HTML report for the kernel | + | KERNEL_FILE_NAME_emulate | Create a n emulation kernel | + +The currently supported values for KERNEL_FILE_NAME are: + +- transpose_default +- transpose_optimized + + You can build for example the host application by running + + mkdir build && cd build + cmake .. + make fTrans + +You will find all executables and kernel files in the `bin` +folder of your build directory. +You should always specify a target with make to reduce the build time! +You might want to specify predefined parameters before build: + +Name | Default | Description | +---------------- |-------------|--------------------------------------| + `DATA_TYPE` | float | Data type used for calculation | +`DEFAULT_DEVICE` | -1 | Index of the default device (-1 = ask) | +`DEFAULT_PLATFORM`| -1 | Index of the default platform (-1 = ask) | +`DEFAULT_REPETITIONS`| 10 | Number of times the kernel will be executed | +`KERNEL_NAME`| transpose | Name of the kernel (only needed for own implementations) | +`FPGA_BOARD_NAME`| p520_hpc_sg280l | Name of the target board | +`BLOCK_SIZE` | 512 | Block size used by the kernel to transpose the matrix | +`GLOBAL_MEM_UNROLL`| 16 | Unrolling factor for the global memory access | + +Moreover the environment variable `INTELFPGAOCLSDKROOT` has to be set to the root +of the Intel FPGA SDK installation. + +Additionally it is possible to set the used compiler and other build tools +in the `CMakeCache.txt` located in the build directory after running cmake. + + +## Execution + +For execution of the benchmark run: + + ./fTrans -f path_to_kernel.aocx + +For more information on available input parameters run + + ./fTrans -h + +To execute the unit and integration tests run + + ./Google_Tests_run + +in the `bin` folder within the build directory. +It will run an emulation of the kernel and execute some functionality tests. + +## Output Interpretation + +An example output from an emulation is given below: + + trans calc calc FLOPS total FLOPS + avg: 3.92349e-02 3.55022e-01 4.72568e+07 4.25540e+07 + best: 1.55398e-02 2.94127e-01 5.70407e+07 5.41782e+07 + Aggregated Error: 0.00000e00 + +The output gives the average time for calculation and data transfer +over all iterations as well as the best measured performance. +For both rows there are the following columns: + +- `trans`: Transfer time for reading and writing buffers from host to device. +- `calc`: Actual execution time of the kernel. +- `calc FLOPS`: Achieved FLOPS just considering the execution time. +- `total FLOPS`: Total FLOPS combining transfer and calculation time. + +The `Aggregated Error` field sums up the values of the result matrix. All values +should be 0 due to the used input data generation scheme. + diff --git a/PTRANS/src/common/parameters.h.in b/PTRANS/src/common/parameters.h.in new file mode 100644 index 00000000..2c5bcbf6 --- /dev/null +++ b/PTRANS/src/common/parameters.h.in @@ -0,0 +1,25 @@ +#ifndef SRC_COMMON_PARAMETERS_H_ +#define SRC_COMMON_PARAMETERS_H_ + +#define VERSION "@VERSION@" +#define KERNEL_NAME "@KERNEL_NAME@" +#define DEFAULT_REPETITIONS @DEFAULT_REPETITIONS@ +#define DEFAULT_MATRIX_SIZE @DEFAULT_MATRIX_SIZE@ +#define DEFAULT_PLATFORM @DEFAULT_PLATFORM@ +#define DEFAULT_DEVICE @DEFAULT_DEVICE@ + +/** + * Kernel Parameters + */ +#define BLOCK_SIZE @BLOCK_SIZE@ +#define GLOBAL_MEM_UNROLL @GLOBAL_MEM_UNROLL@ + +#define HOST_DATA_TYPE @HOST_DATA_TYPE@ +#define DEVICE_DATA_TYPE @DEVICE_DATA_TYPE@ + +/** +Output separator +*/ +#define HLINE "-------------------------------------------------------------\n" + +#endif // SRC_COMMON_PARAMETERS_H_ \ No newline at end of file diff --git a/PTRANS/src/device/CMakeLists.txt b/PTRANS/src/device/CMakeLists.txt new file mode 100644 index 00000000..eb31e83f --- /dev/null +++ b/PTRANS/src/device/CMakeLists.txt @@ -0,0 +1,35 @@ + +set(AOC_INCLUDES "-I${CMAKE_CURRENT_BINARY_DIR}/../common") + +function(generate_kernel_targets) + foreach (kernel_file_name ${ARGN}) + set(source_f ${CMAKE_CURRENT_SOURCE_DIR}/${kernel_file_name}.cl) + set(report_f ${EXECUTABLE_OUTPUT_PATH}/${kernel_file_name}_report) + set(bitstream_emulate_f ${EXECUTABLE_OUTPUT_PATH}/${kernel_file_name}_emulate.aocx) + set(bitstream_f ${EXECUTABLE_OUTPUT_PATH}/${kernel_file_name}.aocx) + set(out_f "${CMAKE_CURRENT_BINARY_DIR}/${out_f}") + add_custom_command(OUTPUT ${bitstream_emulate_f} + COMMAND ${IntelFPGAOpenCL_AOC} ${source_f} ${AOC_INCLUDES} ${AOC_FLAGS} -legacy-emulator -march=emulator + -o ${bitstream_emulate_f} + ) + add_custom_command(OUTPUT ${bitstream_f} + COMMAND ${IntelFPGAOpenCL_AOC} ${source_f} ${AOC_INCLUDES} ${AOC_FLAGS} -board=${FPGA_BOARD_NAME} + -o ${bitstream_f} + ) + add_custom_command(OUTPUT ${report_f} + COMMAND ${IntelFPGAOpenCL_AOC} ${source_f} ${AOC_INCLUDES} ${AOC_FLAGS} -rtl -report -board=${FPGA_BOARD_NAME} + -o ${report_f} + ) + add_custom_target(${kernel_file_name}_report DEPENDS ${report_f} + DEPENDS ${source_f} ${CMAKE_BINARY_DIR}/src/common/parameters.h + SOURCES ${source_f} ${CMAKE_BINARY_DIR}/src/common/parameters.h) + add_custom_target(${kernel_file_name} DEPENDS ${bitstream_f} + DEPENDS ${source_f} ${CMAKE_BINARY_DIR}/src/common/parameters.h + SOURCES ${source_f} ${CMAKE_BINARY_DIR}/src/common/parameters.h) + add_custom_target(${kernel_file_name}_emulate DEPENDS ${bitstream_emulate_f} + DEPENDS ${source_f} ${CMAKE_BINARY_DIR}/src/common/parameters.h + SOURCES ${source_f} ${CMAKE_BINARY_DIR}/src/common/parameters.h) + endforeach () +endfunction() + +generate_kernel_targets(transpose_default transpose_optimized) diff --git a/PTRANS/src/device/transpose_default.cl b/PTRANS/src/device/transpose_default.cl new file mode 100644 index 00000000..9c31e5f4 --- /dev/null +++ b/PTRANS/src/device/transpose_default.cl @@ -0,0 +1,28 @@ +#include "parameters.h" + +/** + * Simple reference kernel for matrix transposition. + * Not optimized and only using global memory. + * + * Will do the following: + * + * A_out = trans(A) + B + * + * where A_out, A and B are matrices of size matrixSize*matrixSize + * + * @param A Buffer for matrix A + * @param B Buffer for matrix B + * @param A_out Output buffer for result matrix + * @param matrixSize Size of the matrices + */ +__kernel +void transpose(__global DEVICE_DATA_TYPE *restrict A, + __global DEVICE_DATA_TYPE *restrict B, + __global DEVICE_DATA_TYPE *restrict A_out, + uint matrixSize) { + for (int i = 0; i < matrixSize; i++) { + for (int j = 0; j < matrixSize; j++) { + A_out[i * matrixSize + j] = A[j * matrixSize + i] + B[i * matrixSize + j]; + } + } +} \ No newline at end of file diff --git a/PTRANS/src/device/transpose_optimized.cl b/PTRANS/src/device/transpose_optimized.cl new file mode 100644 index 00000000..85479a6b --- /dev/null +++ b/PTRANS/src/device/transpose_optimized.cl @@ -0,0 +1,103 @@ +/****************************************************************************** + * Author: Arjun Ramaswami + * + * Edited by Marius Meyer: + * - Adapt to used kernel signature + * - Change to row-column loop structure + *****************************************************************************/ + +#include "parameters.h" + +/** + * Optimized matrix transposition that simplifies local memory accesses. + * + * Will do the following: + * + * A_out = trans(A) + B + * + * where A_out, A and B are matrices of size matrixSize*matrixSize + * + * @param A Buffer for matrix A + * @param B Buffer for matrix B + * @param A_out Output buffer for result matrix + * @param matrixSize Size of the matrices. Must be multiple of BLOCK_SIZE + */ +__attribute__((max_global_work_dim(0))) +__kernel +void transpose(__global DEVICE_DATA_TYPE volatile *restrict A, + __global DEVICE_DATA_TYPE volatile *restrict B, +__global DEVICE_DATA_TYPE *restrict A_out, + uint matrixSize) { + + const unsigned number_of_blocks = matrixSize / BLOCK_SIZE; + + // transpose the matrix block-wise from global memory +#pragma loop_coalesce 2 + for (int block_row = 0; block_row < number_of_blocks; block_row++) { + for (int block_col = 0; block_col < number_of_blocks; block_col++) { + + // local memory buffer for a matrix block + DEVICE_DATA_TYPE a_block[BLOCK_SIZE * BLOCK_SIZE / GLOBAL_MEM_UNROLL][GLOBAL_MEM_UNROLL]; + + // read in block from global memory and store it in a memory efficient manner +#pragma loop_coalesce 2 + for (int row = 0; row < BLOCK_SIZE; row++) { + for (int col = 0; col < BLOCK_SIZE / GLOBAL_MEM_UNROLL; col++) { + + unsigned local_mem_converted_row = row * (BLOCK_SIZE / GLOBAL_MEM_UNROLL) + col; + + DEVICE_DATA_TYPE rotate_in[GLOBAL_MEM_UNROLL]; + +#pragma unroll + for (unsigned unroll_count = 0; unroll_count < GLOBAL_MEM_UNROLL; unroll_count++) { + rotate_in[unroll_count] = A[block_col * BLOCK_SIZE + col * GLOBAL_MEM_UNROLL + unroll_count + + (block_row * BLOCK_SIZE + row) * matrixSize]; + } + + unsigned rot = row & (GLOBAL_MEM_UNROLL - 1); + + // rotate temporary buffer to store data into local buffer +#pragma unroll + for (unsigned unroll_count = 0; unroll_count < GLOBAL_MEM_UNROLL; unroll_count++) { + // every block of (N / GLOBAL_MEM_UNROLL), rotates the index by 1 + a_block[local_mem_converted_row][unroll_count] = rotate_in[(unroll_count + GLOBAL_MEM_UNROLL - rot) + & (GLOBAL_MEM_UNROLL - 1)]; + } + } + } + + // complete matrix transposition and write the result back to global memory +#pragma loop_coalesce 2 + for (int row = 0; row < BLOCK_SIZE; row++) { + for (int col = 0; col < BLOCK_SIZE / GLOBAL_MEM_UNROLL; col++) { + + DEVICE_DATA_TYPE rotate_out[GLOBAL_MEM_UNROLL]; + + unsigned base = col * BLOCK_SIZE; + unsigned offset = row / GLOBAL_MEM_UNROLL; + + +#pragma unroll + for (unsigned unroll_count = 0; unroll_count < GLOBAL_MEM_UNROLL; unroll_count++) { + unsigned rot = ((GLOBAL_MEM_UNROLL + unroll_count - row) * (BLOCK_SIZE / GLOBAL_MEM_UNROLL)) & + (BLOCK_SIZE - 1); + unsigned row_rotate = base + offset + rot; + rotate_out[unroll_count] = a_block[row_rotate][unroll_count]; + } + + unsigned rot_out = row & (GLOBAL_MEM_UNROLL - 1); + // rotate temporary buffer to store data into local buffer +#pragma unroll + for (unsigned unroll_count = 0; unroll_count < GLOBAL_MEM_UNROLL; unroll_count++) { + + A_out[(block_col * BLOCK_SIZE + row) * matrixSize + + block_row* BLOCK_SIZE + col * GLOBAL_MEM_UNROLL + unroll_count] = + rotate_out[(unroll_count + rot_out) & (GLOBAL_MEM_UNROLL - 1)] + + B[(block_col * BLOCK_SIZE + row) * matrixSize + + block_row * BLOCK_SIZE + col * GLOBAL_MEM_UNROLL + unroll_count]; + } + } + } + } + } +} \ No newline at end of file diff --git a/PTRANS/src/host/CMakeLists.txt b/PTRANS/src/host/CMakeLists.txt new file mode 100755 index 00000000..8c925618 --- /dev/null +++ b/PTRANS/src/host/CMakeLists.txt @@ -0,0 +1,12 @@ + +include_directories(../../../extern/cxxopts/include) +include_directories(${IntelFPGAOpenCL_INCLUDE_DIRS}) +include_directories(${CMAKE_BINARY_DIR}/src/common) + +set(HEADER_FILES execution.h setup/fpga_setup.hpp transpose_functionality.hpp) +set(HOST_SOURCE execution_default.cpp main.cpp setup/fpga_setup.cpp transpose_functionality.cpp) + +add_compile_options(--std=c++11) + +add_executable(fTrans ${HOST_SOURCE} ${HEADER_FILES}) +target_link_libraries(fTrans ${IntelFPGAOpenCL_LIBRARIES}) diff --git a/PTRANS/src/host/execution.h b/PTRANS/src/host/execution.h new file mode 100644 index 00000000..e640ec74 --- /dev/null +++ b/PTRANS/src/host/execution.h @@ -0,0 +1,67 @@ +/* +Copyright (c) 2019 Marius Meyer + +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 SRC_HOST_EXECUTION_H_ +#define SRC_HOST_EXECUTION_H_ + +/* C++ standard library headers */ +#include +#include + +/* External library headers */ +#include "CL/cl.hpp" +#include "parameters.h" + + +namespace bm_execution { + + struct ExecutionConfiguration { + cl::Context context; + cl::Device device; + cl::Program program; + std::string kernelName; + uint repetitons; + cl_uint matrixSize; + bool useMemInterleaving; + }; + + struct ExecutionTimings { + std::vector transferTimings; + std::vector calculationTimings; + }; + +/** +The actual execution of the benchmark. +This method can be implemented in multiple *.cpp files. This header enables +simple exchange of the different calculation methods. + +@param config struct that contains all necessary information to execute the kernel on the FPGA + + +@return The resulting matrix +*/ + std::shared_ptr + calculate(std::shared_ptr config, HOST_DATA_TYPE *const A, + HOST_DATA_TYPE *const B, HOST_DATA_TYPE *A_out); + +} // namespace bm_execution + +#endif // SRC_HOST_EXECUTION_H_ diff --git a/PTRANS/src/host/execution_default.cpp b/PTRANS/src/host/execution_default.cpp new file mode 100644 index 00000000..0d7545f7 --- /dev/null +++ b/PTRANS/src/host/execution_default.cpp @@ -0,0 +1,105 @@ +/* +Copyright (c) 2019 Marius Meyer + +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. +*/ + +/* Related header files */ +#include "execution.h" + +/* C++ standard library headers */ +#include +#include +#include + +/* External library headers */ +#include "CL/cl.hpp" +#include "CL/cl_ext_intelfpga.h" + +/* Project's headers */ + +namespace bm_execution { + + /* + Implementation for the single kernel. + @copydoc bm_execution::calculate() + */ + std::shared_ptr + calculate(std::shared_ptr config, HOST_DATA_TYPE *const A, + HOST_DATA_TYPE *const B, HOST_DATA_TYPE *A_out) { + + cl::Buffer bufferA(config->context, CL_MEM_READ_ONLY, + sizeof(HOST_DATA_TYPE) * config->matrixSize * config->matrixSize); + cl::Buffer bufferB(config->context, CL_MEM_READ_ONLY, + sizeof(HOST_DATA_TYPE) * config->matrixSize * config->matrixSize); + cl::Buffer bufferA_out(config->context, CL_MEM_WRITE_ONLY, + sizeof(HOST_DATA_TYPE) * config->matrixSize * config->matrixSize); + + cl::Kernel transposeKernel(config->program, config->kernelName.c_str()); + + transposeKernel.setArg(0, bufferA); + transposeKernel.setArg(1, bufferB); + transposeKernel.setArg(2, bufferA_out); + transposeKernel.setArg(3, config->matrixSize); + + cl::CommandQueue queue(config->context); + + std::vector transferTimings; + std::vector calculationTimings; + + for (int repetition = 0; repetition < config->repetitons; repetition++) { + + auto startTransfer = std::chrono::high_resolution_clock::now(); + queue.enqueueWriteBuffer(bufferA, CL_FALSE, 0, + sizeof(HOST_DATA_TYPE) * config->matrixSize * config->matrixSize, A); + queue.enqueueWriteBuffer(bufferB, CL_FALSE, 0, + sizeof(HOST_DATA_TYPE) * config->matrixSize * config->matrixSize, B); + queue.finish(); + auto endTransfer = std::chrono::high_resolution_clock::now(); + std::chrono::duration transferTime = + std::chrono::duration_cast> + (endTransfer - startTransfer); + + auto startCalculation = std::chrono::high_resolution_clock::now(); + queue.enqueueTask(transposeKernel); + queue.finish(); + auto endCalculation = std::chrono::high_resolution_clock::now(); + std::chrono::duration calculationTime = + std::chrono::duration_cast> + (endCalculation - startCalculation); + calculationTimings.push_back(calculationTime.count()); + + startTransfer = std::chrono::high_resolution_clock::now(); + queue.enqueueReadBuffer(bufferA_out, CL_TRUE, 0, + sizeof(HOST_DATA_TYPE) * config->matrixSize * config->matrixSize, A_out); + endTransfer = std::chrono::high_resolution_clock::now(); + transferTime += + std::chrono::duration_cast> + (endTransfer - startTransfer); + transferTimings.push_back(transferTime.count()); + } + + std::shared_ptr result(new ExecutionTimings{ + transferTimings, + calculationTimings + }); + return result; + } + +} // namespace bm_execution diff --git a/PTRANS/src/host/main.cpp b/PTRANS/src/host/main.cpp new file mode 100644 index 00000000..c2ed8a96 --- /dev/null +++ b/PTRANS/src/host/main.cpp @@ -0,0 +1,66 @@ +// +// Created by Marius Meyer on 04.12.19. +// + +#include "transpose_functionality.hpp" + +/** +The program entry point +*/ +int +main(int argc, char *argv[]) { + // Setup benchmark + std::shared_ptr programSettings = + parseProgramParameters(argc, argv); + fpga_setup::setupEnvironmentAndClocks(); + + std::vector usedDevice; + cl::Context context; + cl::Program program; + cl::Device device; + + if (programSettings->kernelFileName != "CPU") { + usedDevice = fpga_setup::selectFPGADevice( + programSettings->defaultPlatform, + programSettings->defaultDevice); + context = cl::Context(usedDevice); + program = fpga_setup::fpgaSetup(&context, usedDevice, &programSettings->kernelFileName); + } + + if (usedDevice.size() > 0) { + device = usedDevice[0]; + printFinalConfiguration(programSettings, device); + } + + std::shared_ptr config( + new bm_execution::ExecutionConfiguration{ + context, device, program, + programSettings->kernelName, + programSettings->numRepetitions, + programSettings->matrixSize, + programSettings->useMemInterleaving + }); + + HOST_DATA_TYPE* A; + HOST_DATA_TYPE* B; + HOST_DATA_TYPE* result; + + posix_memalign(reinterpret_cast(&A), 64, + sizeof(HOST_DATA_TYPE) * config->matrixSize * config->matrixSize); + posix_memalign(reinterpret_cast(&B), 64, + sizeof(HOST_DATA_TYPE) * config->matrixSize * config->matrixSize); + posix_memalign(reinterpret_cast(&result), 64, + sizeof(HOST_DATA_TYPE) * config->matrixSize * config->matrixSize); + + generateInputData(config->matrixSize, A, B); + // Start actual benchmark + auto results = bm_execution::calculate(config, A, B, result); + + printResults(results, config->matrixSize); + + double error = printCalculationError(config->matrixSize, result); + + + return (error < 1.0e-5) ? 0 : 1; +} + diff --git a/PTRANS/src/host/setup/fpga_setup.cpp b/PTRANS/src/host/setup/fpga_setup.cpp new file mode 100644 index 00000000..07845901 --- /dev/null +++ b/PTRANS/src/host/setup/fpga_setup.cpp @@ -0,0 +1,273 @@ +// +// Created by Marius Meyer on 04.12.19. +// + +#include "fpga_setup.hpp" + +#include +#include +#include +#include +#include +#include + +/* External libraries */ +#include "parameters.h" + +namespace fpga_setup { + + /** +Converts the reveived OpenCL error to a string + +@param err The OpenCL error code + +@return The string representation of the OpenCL error code +*/ + std::string + getCLErrorString(cl_int const err) { + switch (err) { + CL_ERR_TO_STR(CL_DEVICE_NOT_FOUND); + CL_ERR_TO_STR(CL_DEVICE_NOT_AVAILABLE); + CL_ERR_TO_STR(CL_COMPILER_NOT_AVAILABLE); + CL_ERR_TO_STR(CL_MEM_OBJECT_ALLOCATION_FAILURE); + CL_ERR_TO_STR(CL_OUT_OF_RESOURCES); + CL_ERR_TO_STR(CL_OUT_OF_HOST_MEMORY); + CL_ERR_TO_STR(CL_PROFILING_INFO_NOT_AVAILABLE); + CL_ERR_TO_STR(CL_MEM_COPY_OVERLAP); + CL_ERR_TO_STR(CL_IMAGE_FORMAT_MISMATCH); + CL_ERR_TO_STR(CL_IMAGE_FORMAT_NOT_SUPPORTED); + CL_ERR_TO_STR(CL_BUILD_PROGRAM_FAILURE); + CL_ERR_TO_STR(CL_MAP_FAILURE); + CL_ERR_TO_STR(CL_MISALIGNED_SUB_BUFFER_OFFSET); + CL_ERR_TO_STR(CL_EXEC_STATUS_ERROR_FOR_EVENTS_IN_WAIT_LIST); + CL_ERR_TO_STR(CL_KERNEL_ARG_INFO_NOT_AVAILABLE); + CL_ERR_TO_STR(CL_INVALID_VALUE); + CL_ERR_TO_STR(CL_INVALID_DEVICE_TYPE); + CL_ERR_TO_STR(CL_INVALID_PLATFORM); + CL_ERR_TO_STR(CL_INVALID_DEVICE); + CL_ERR_TO_STR(CL_INVALID_CONTEXT); + CL_ERR_TO_STR(CL_INVALID_QUEUE_PROPERTIES); + CL_ERR_TO_STR(CL_INVALID_COMMAND_QUEUE); + CL_ERR_TO_STR(CL_INVALID_HOST_PTR); + CL_ERR_TO_STR(CL_INVALID_MEM_OBJECT); + CL_ERR_TO_STR(CL_INVALID_IMAGE_FORMAT_DESCRIPTOR); + CL_ERR_TO_STR(CL_INVALID_IMAGE_SIZE); + CL_ERR_TO_STR(CL_INVALID_SAMPLER); + CL_ERR_TO_STR(CL_INVALID_BINARY); + CL_ERR_TO_STR(CL_INVALID_BUILD_OPTIONS); + CL_ERR_TO_STR(CL_INVALID_PROGRAM); + CL_ERR_TO_STR(CL_INVALID_PROGRAM_EXECUTABLE); + CL_ERR_TO_STR(CL_INVALID_KERNEL_NAME); + CL_ERR_TO_STR(CL_INVALID_KERNEL_DEFINITION); + CL_ERR_TO_STR(CL_INVALID_KERNEL); + CL_ERR_TO_STR(CL_INVALID_ARG_INDEX); + CL_ERR_TO_STR(CL_INVALID_ARG_VALUE); + CL_ERR_TO_STR(CL_INVALID_ARG_SIZE); + CL_ERR_TO_STR(CL_INVALID_KERNEL_ARGS); + CL_ERR_TO_STR(CL_INVALID_WORK_DIMENSION); + CL_ERR_TO_STR(CL_INVALID_WORK_GROUP_SIZE); + CL_ERR_TO_STR(CL_INVALID_WORK_ITEM_SIZE); + CL_ERR_TO_STR(CL_INVALID_GLOBAL_OFFSET); + CL_ERR_TO_STR(CL_INVALID_EVENT_WAIT_LIST); + CL_ERR_TO_STR(CL_INVALID_EVENT); + CL_ERR_TO_STR(CL_INVALID_OPERATION); + CL_ERR_TO_STR(CL_INVALID_GL_OBJECT); + CL_ERR_TO_STR(CL_INVALID_BUFFER_SIZE); + CL_ERR_TO_STR(CL_INVALID_MIP_LEVEL); + CL_ERR_TO_STR(CL_INVALID_GLOBAL_WORK_SIZE); + CL_ERR_TO_STR(CL_COMPILE_PROGRAM_FAILURE); + CL_ERR_TO_STR(CL_LINKER_NOT_AVAILABLE); + CL_ERR_TO_STR(CL_LINK_PROGRAM_FAILURE); + CL_ERR_TO_STR(CL_DEVICE_PARTITION_FAILED); + CL_ERR_TO_STR(CL_INVALID_PROPERTY); + CL_ERR_TO_STR(CL_INVALID_IMAGE_DESCRIPTOR); + CL_ERR_TO_STR(CL_INVALID_COMPILER_OPTIONS); + CL_ERR_TO_STR(CL_INVALID_LINKER_OPTIONS); + CL_ERR_TO_STR(CL_INVALID_DEVICE_PARTITION_COUNT); + + default: + return "UNKNOWN ERROR CODE"; + } + } + +/** +Check the OpenCL return code for errors. +If an error is detected, it will be printed and the programm execution is +stopped. + +@param err The OpenCL error code +*/ + void + handleClReturnCode(cl_int const err, std::string const file, + int const line) { + if (err != CL_SUCCESS) { + std::string err_string = getCLErrorString(err); + std::cerr << "ERROR in OpenCL library detected! Aborting." + << std::endl << file << ":" << line << ": " << err_string + << std::endl; + exit(err); + } + } + +/** +Sets up the given FPGA with the kernel in the provided file. + +@param context The context used for the program +@param program The devices used for the program +@param usedKernelFile The path to the kernel file +@return The program that is used to create the benchmark kernels +*/ + cl::Program + fpgaSetup(const cl::Context *context, std::vector deviceList, + const std::string *usedKernelFile) { + int err; + + std::cout << HLINE; + std::cout << "FPGA Setup:" << usedKernelFile->c_str() << std::endl; + + // Open file stream if possible + std::ifstream aocxStream(usedKernelFile->c_str(), std::ifstream::binary); + if (!aocxStream.is_open()) { + std::cerr << "Not possible to open from given file!" << std::endl; + } + + // Read in file contents and create program from binaries + std::string prog(std::istreambuf_iterator(aocxStream), + (std::istreambuf_iterator())); + aocxStream.seekg(0, aocxStream.end); + unsigned file_size = aocxStream.tellg(); + aocxStream.seekg(0, aocxStream.beg); + char *buf = new char[file_size]; + aocxStream.read(buf, file_size); + + cl::Program::Binaries mybinaries; + mybinaries.push_back({buf, file_size}); + + // Create the Program from the AOCX file. + cl::Program program(*context, deviceList, mybinaries, NULL, &err); + ASSERT_CL(err); + std::cout << "Prepared FPGA successfully for global Execution!" << + std::endl; + std::cout << HLINE; + return program; + } + +/** +Sets up the C++ environment by configuring std::cout and checking the clock +granularity using bm_helper::checktick() +*/ + void + setupEnvironmentAndClocks() { + std::cout << std::setprecision(5) << std::scientific; + + std::cout << HLINE; + std::cout << "General setup:" << std::endl; + + // Check clock granularity and output result + std::cout << "C++ high resolution clock is used." << std::endl; + std::cout << "The clock precision seems to be " + << static_cast + (std::chrono::high_resolution_clock::period::num) / + std::chrono::high_resolution_clock::period::den * 10e9 + << "ns" << std::endl; + + std::cout << HLINE; + } + + +/** +Searches an selects an FPGA device using the CL library functions. +If multiple platforms or devices are given, the user will be prompted to +choose a device. + +@param defaultPlatform The index of the platform that has to be used. If a + value < 0 is given, the platform can be chosen + interactively +@param defaultDevice The index of the device that has to be used. If a + value < 0 is given, the device can be chosen + interactively + +@return A list containing a single selected device +*/ + std::vector + selectFPGADevice(int defaultPlatform, int defaultDevice) { + // Integer used to store return codes of OpenCL library calls + int err; + + std::vector platformList; + err = cl::Platform::get(&platformList); + ASSERT_CL(err); + + // Choose the target platform + int chosenPlatformId = 0; + if (defaultPlatform >= 0) { + if (defaultPlatform < platformList.size()) { + chosenPlatformId = defaultPlatform; + } else { + std::cerr << "Default platform " << defaultPlatform + << " can not be used. Available platforms: " + << platformList.size() << std::endl; + exit(1); + } + } else if (platformList.size() > 1) { + std::cout << + "Multiple platforms have been found. Select the platform by"\ + " typing a number:" << std::endl; + for (int platformId = 0; + platformId < platformList.size(); platformId++) { + std::cout << platformId << ") " << + platformList[platformId].getInfo() << + std::endl; + } + std::cout << "Enter platform id [0-" << platformList.size() - 1 + << "]:"; + std::cin >> chosenPlatformId; + } + cl::Platform platform = platformList[chosenPlatformId]; + std::cout << "Selected Platform: " + << platform.getInfo() << std::endl; + + std::vector deviceList; + err = platform.getDevices(CL_DEVICE_TYPE_ACCELERATOR, &deviceList); + ASSERT_CL(err); + + // Choose taget device + int chosenDeviceId = 0; + if (defaultDevice >= 0) { + if (defaultDevice < deviceList.size()) { + chosenDeviceId = defaultDevice; + } else { + std::cerr << "Default platform " << defaultDevice + << " can not be used. Available platforms: " + << deviceList.size() << std::endl; + exit(1); + } + } else if (deviceList.size() > 1) { + std::cout << + "Multiple devices have been found. Select the platform by"\ + " typing a number:" << std::endl; + for (int deviceId = 0; + deviceId < deviceList.size(); deviceId++) { + std::cout << deviceId << ") " << + deviceList[deviceId].getInfo() << + std::endl; + } + std::cout << "Enter device id [0-" << deviceList.size() - 1 << "]:"; + std::cin >> chosenDeviceId; + } + std::vector chosenDeviceList; + chosenDeviceList.push_back(deviceList[chosenDeviceId]); + + // Give selection summary + std::cout << HLINE; + std::cout << "Selection summary:" << std::endl; + std::cout << "Platform Name: " << + platform.getInfo() << std::endl; + std::cout << "Device Name: " << + chosenDeviceList[0].getInfo() << std::endl; + std::cout << HLINE; + + return chosenDeviceList; + } + +} // namespace fpga_setup \ No newline at end of file diff --git a/PTRANS/src/host/setup/fpga_setup.hpp b/PTRANS/src/host/setup/fpga_setup.hpp new file mode 100644 index 00000000..c1f78b0c --- /dev/null +++ b/PTRANS/src/host/setup/fpga_setup.hpp @@ -0,0 +1,108 @@ +/* +Copyright (c) 2019 Marius Meyer + +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 SRC_HOST_FPGA_SETUP_H_ +#define SRC_HOST_FPGA_SETUP_H_ + +#include +#include +#include +#include +#include +#include + +/* External libraries */ +#include "CL/cl.hpp" + +/** +Makro to convert the error integer representation to its string representation +Source: https://gist.github.com/allanmac/9328bb2d6a99b86883195f8f78fd1b93 +*/ +#define CL_ERR_TO_STR(err) case err: return #err + +namespace fpga_setup { + + /** +Converts the reveived OpenCL error to a string + +@param err The OpenCL error code + +@return The string representation of the OpenCL error code +*/ + std::string + getCLErrorString(cl_int const err); + +/** +Check the OpenCL return code for errors. +If an error is detected, it will be printed and the programm execution is +stopped. + +@param err The OpenCL error code +*/ + void + handleClReturnCode(cl_int const err, std::string const file, + int const line); + +/** +Makro that enables checks for OpenCL errors with handling of the file and +line number. +*/ +#define ASSERT_CL(err) fpga_setup::handleClReturnCode(err, __FILE__, __LINE__) + +/** +Sets up the given FPGA with the kernel in the provided file. + +@param context The context used for the program +@param program The devices used for the program +@param usedKernelFile The path to the kernel file +@return The program that is used to create the benchmark kernels +*/ + cl::Program + fpgaSetup(const cl::Context *context, std::vector deviceList, + const std::string *usedKernelFile); + +/** +Sets up the C++ environment by configuring std::cout and checking the clock +granularity using bm_helper::checktick() +*/ + void + setupEnvironmentAndClocks(); + + +/** +Searches an selects an FPGA device using the CL library functions. +If multiple platforms or devices are given, the user will be prompted to +choose a device. + +@param defaultPlatform The index of the platform that has to be used. If a + value < 0 is given, the platform can be chosen + interactively +@param defaultDevice The index of the device that has to be used. If a + value < 0 is given, the device can be chosen + interactively + +@return A list containing a single selected device +*/ + std::vector + selectFPGADevice(int defaultPlatform, int defaultDevice); + +} // namespace fpga_setup +#endif // SRC_HOST_FPGA_SETUP_H_ diff --git a/PTRANS/src/host/transpose_functionality.cpp b/PTRANS/src/host/transpose_functionality.cpp new file mode 100644 index 00000000..8689e92d --- /dev/null +++ b/PTRANS/src/host/transpose_functionality.cpp @@ -0,0 +1,205 @@ +// +// Created by Marius Meyer on 04.12.19. +// + +/* +Copyright (c) 2019 Marius Meyer + +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. +*/ + +#include "transpose_functionality.hpp" + +/* C++ standard library headers */ +#include +#include + +/* Project's headers */ +#include "execution.h" +#include "cxxopts.hpp" +#include "setup/fpga_setup.hpp" +#include "parameters.h" + +/** +Parses and returns program options using the cxxopts library. +Supports the following parameters: + - file name of the FPGA kernel file (-f,--file) + - number of repetitions (-n) + - number of kernel replications (-r) + - data size (-d) + - use memory interleaving +@see https://github.com/jarro2783/cxxopts + +@return program settings that are created from the given program arguments +*/ +std::shared_ptr +parseProgramParameters(int argc, char *argv[]) { + // Defining and parsing program options + cxxopts::Options options(argv[0], PROGRAM_DESCRIPTION); + options.add_options() + ("f,file", "Kernel file name", cxxopts::value()) + ("n", "Number of repetitions", + cxxopts::value()->default_value(std::to_string(DEFAULT_REPETITIONS))) + ("m", "Matrix size", + cxxopts::value()->default_value(std::to_string(DEFAULT_MATRIX_SIZE))) + ("kernel", "Name of the kernel", + cxxopts::value()->default_value(KERNEL_NAME)) + ("i,nointerleaving", "Disable memory interleaving") + ("device", "Index of the device that has to be used. If not given you "\ + "will be asked which device to use if there are multiple devices "\ + "available.", cxxopts::value()->default_value(std::to_string(DEFAULT_DEVICE))) + ("platform", "Index of the platform that has to be used. If not given "\ + "you will be asked which platform to use if there are multiple "\ + "platforms available.", + cxxopts::value()->default_value(std::to_string(DEFAULT_PLATFORM))) + ("h,help", "Print this help"); + cxxopts::ParseResult result = options.parse(argc, argv); + + // Check parsed options and handle special cases + if (result.count("f") <= 0) { + // Path to the kernel file is mandatory - exit if not given! + std::cerr << "Kernel file must be given! Aborting" << std::endl; + std::cout << options.help() << std::endl; + exit(1); + } + if (result.count("h")) { + // Just print help when argument is given + std::cout << options.help() << std::endl; + exit(0); + } + + // Create program settings from program arguments + std::shared_ptr sharedSettings( + new ProgramSettings{result["n"].as(), + result["m"].as(), + result["platform"].as(), + result["device"].as(), + static_cast(result.count("i") <= 0), + result["f"].as(), + result["kernel"].as()}); + return sharedSettings; +} + +/** + * Reference implementation that takes two matrices and calculates + * A_out = trans(A) + B + * where A, B and A_out are matrices of size n*n. + * + * @param A matrix that has to be transposed + * @param B matrix that will be added to the transposed matrix + * @param A_out matrix where the result of the calculation is stored + * @param n dimension of the matrices + */ +void +transposeReference(HOST_DATA_TYPE *const A, HOST_DATA_TYPE *const B, + HOST_DATA_TYPE *A_out, cl_uint n) { + for (int i = 0; i < n; i++) { + for (int j = 0; j < n; j++) { + A_out[i * n + j] = A[j * n + i] + B[i * n + j]; + } + } +} + + +void +generateInputData(cl_uint matrix_size, HOST_DATA_TYPE *A, HOST_DATA_TYPE *B) { + std::mt19937 gen(7); + std::uniform_real_distribution<> dis(-100.0, 100.0); + for (int i = 0; i < matrix_size; i++) { + for (int j = 0; j < matrix_size; j++) { + A[i * matrix_size + j] = dis(gen); + B[j * matrix_size + i] = -A[i * matrix_size + j] + 1.0; + } + } +} + + +/** +Prints the execution results to stdout + +@param results The execution results +*/ +void +printResults(std::shared_ptr results, cl_uint matrixSize) { + double flops = matrixSize * matrixSize; + + double avgTransferTime = accumulate(results->transferTimings.begin(), results->transferTimings.end(), 0.0) + / results->transferTimings.size(); + double minTransferTime = *min_element(results->transferTimings.begin(), results->transferTimings.end()); + + + double avgCalculationTime = accumulate(results->calculationTimings.begin(), results->calculationTimings.end(), 0.0) + / results->calculationTimings.size(); + double minCalculationTime = *min_element(results->calculationTimings.begin(), results->calculationTimings.end()); + + double avgCalcFLOPS = flops / avgCalculationTime; + double avgTotalFLOPS = flops / (avgCalculationTime + avgTransferTime); + double minCalcFLOPS = flops / minCalculationTime; + double minTotalFLOPS = flops / (minCalculationTime + minTransferTime); + + std::cout << " trans calc calc FLOPS total FLOPS" << std::endl; + std::cout << "avg: " << avgTransferTime + << " " << avgCalculationTime + << " " << avgCalcFLOPS + << " " << avgTotalFLOPS + << std::endl; + std::cout << "best: " << minTransferTime + << " " << minCalculationTime + << " " << minCalcFLOPS + << " " << minTotalFLOPS + << std::endl; +} + +/** + * Prints the used configuration to std out before starting the actual benchmark. + * + * @param programSettings The program settings retrieved from the command line + * @param device The device used for execution + */ +void printFinalConfiguration(const std::shared_ptr &programSettings, + const cl::Device &device) {// Give setup summary + std::cout << PROGRAM_DESCRIPTION << std::endl << HLINE; + std::cout << "Summary:" << std::endl + << "Repetitions: " << programSettings->numRepetitions + << std::endl + << "Matrix Size: " << programSettings->matrixSize + << "x" << programSettings->matrixSize + << std::endl + << "Memory Interleaving: " << (programSettings->useMemInterleaving ? "Yes" : "No") + << std::endl + << "Kernel file: " << programSettings->kernelFileName + << std::endl; + std::cout << "Device: " + << device.getInfo() << std::endl; + std::cout << HLINE + << "Start benchmark using the given configuration." << std::endl + << HLINE; +} + +double +printCalculationError(cl_uint matrixSize, const HOST_DATA_TYPE *result) { + double max_error = 0.0; + for (int i = 0; i < matrixSize * matrixSize; i++) { + max_error = std::max(fabs(result[i] - 1.0), max_error); + } + + std::cout << "Maximum error: " << max_error << std::endl; + + return max_error; +} \ No newline at end of file diff --git a/PTRANS/src/host/transpose_functionality.hpp b/PTRANS/src/host/transpose_functionality.hpp new file mode 100644 index 00000000..7a2a2e05 --- /dev/null +++ b/PTRANS/src/host/transpose_functionality.hpp @@ -0,0 +1,121 @@ +/* +Copyright (c) 2019 Marius Meyer + +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 SRC_HOST_TRANSPOSE_FUNCTIONALITY_H_ +#define SRC_HOST_TRANSPOSE_FUNCTIONALITY_H_ + +/* C++ standard library headers */ +#include + +/* Project's headers */ +#include "execution.h" +#include "cxxopts.hpp" +#include "setup/fpga_setup.hpp" +#include "parameters.h" + +/* +Short description of the program. +Moreover the version and build time is also compiled into the description. +*/ +#define STR_EXPAND(tok) #tok +#define STR(tok) STR_EXPAND(tok) + +#define PROGRAM_DESCRIPTION "Implementation of the matrix transposition benchmark"\ + " proposed in the HPCC benchmark suite for FPGA.\n"\ + "Version: " VERSION + +struct ProgramSettings { + uint numRepetitions; + cl_uint matrixSize; + int defaultPlatform; + int defaultDevice; + bool useMemInterleaving; + std::string kernelFileName; + std::string kernelName; +}; + + +/** +Parses and returns program options using the cxxopts library. +Supports the following parameters: + - file name of the FPGA kernel file (-f,--file) + - number of repetitions (-n) + - number of kernel replications (-r) + - data size (-d) + - use memory interleaving +@see https://github.com/jarro2783/cxxopts + +@return program settings that are created from the given program arguments +*/ +std::shared_ptr +parseProgramParameters(int argc, char *argv[]); + +/** + * Reference implementation that takes two matrices and calculates + * A_out = trans(A) + B + * where A, B and A_out are matrices of size n*n. + * + * @param A matrix that has to be transposed + * @param B matrix that will be added to the transposed matrix + * @param A_out matrix where the result of the calculation is stored + * @param n dimension of the matrices + */ +void +transposeReference(HOST_DATA_TYPE *const A, HOST_DATA_TYPE *const B, + HOST_DATA_TYPE *A_out, cl_uint n); + + +void +generateInputData(cl_uint matrix_size, HOST_DATA_TYPE *A, HOST_DATA_TYPE *B); + + +/** +Prints the execution results to stdout + +@param results The execution results +*/ +void +printResults(std::shared_ptr results, cl_uint matrixSize); + +/** + * Prints the used configuration to std out before starting the actual benchmark. + * + * @param programSettings The program settings retrieved from the command line + * @param device The device used for execution + */ +void printFinalConfiguration(const std::shared_ptr &programSettings, + const cl::Device &device); + + +/** + * Prints the aggregated error for the result matrix to stdout. + * All values in the result matrix should be 0 because of the used matrix generation scheme. + * All values of the matrix are aggregated to get to total aggregated error. + * + * @param matrixSize Size of the result matrix in one dimension (i. e. N for matrix size N*N) + * @param result The buffer containing the result matrix + * @returns the aggregated normalized error + */ +double +printCalculationError(cl_uint matrixSize, const HOST_DATA_TYPE *result); + +#endif // SRC_HOST_TRANSPOSE_FUNCTIONALITY_H_ diff --git a/PTRANS/tests/CMakeLists.txt b/PTRANS/tests/CMakeLists.txt new file mode 100755 index 00000000..a80534fa --- /dev/null +++ b/PTRANS/tests/CMakeLists.txt @@ -0,0 +1,15 @@ + +add_compile_options(--std=c++11) +# 'lib' is the folder with Google Test sources +add_subdirectory(../../extern/googletest ${CMAKE_CURRENT_BINARY_DIR}/lib) +include_directories(${gtest_SOURCE_DIR}/include ${gtest_SOURCE_DIR}) +include_directories(${CMAKE_BINARY_DIR}/src/common ../../extern/cxxopts/include) +include_directories(SYSTEM ${IntelFPGAOpenCL_INCLUDE_DIRS}) + +set(PROJECT_SOURCES ../src/host/setup/fpga_setup.cpp ../src/host/execution_default.cpp ../src/host/transpose_functionality.cpp) +set(TEST_SOURCES test_host_functionality.cpp test_kernel_functionality_and_host_integration.cpp test_fpga_setup.cpp) + +add_executable(Google_Tests_run ${TEST_SOURCES} ${PROJECT_SOURCES}) +target_link_libraries(Google_Tests_run gtest gmock gtest_main ${IntelFPGAOpenCL_LIBRARIES}) +add_dependencies(Google_Tests_run ${CMAKE_BINARY_DIR}/src/common/parameters.h) +add_dependencies(Google_Tests_run transpose_default_emulate transpose_optimized_emulate) \ No newline at end of file diff --git a/PTRANS/tests/test_fpga_setup.cpp b/PTRANS/tests/test_fpga_setup.cpp new file mode 100644 index 00000000..fd29ebe9 --- /dev/null +++ b/PTRANS/tests/test_fpga_setup.cpp @@ -0,0 +1,34 @@ +// +// Created by Marius Meyer on 04.12.19 +// +#include "gtest/gtest.h" +#include "../src/host/setup/fpga_setup.hpp" +#include "parameters.h" + +/** + * Check if it is possible to find the platform and device that are given as default + */ +TEST (FPGASetup, FindValidPlatformAndDevice) { + EXPECT_EQ (1, fpga_setup::selectFPGADevice(DEFAULT_PLATFORM, DEFAULT_DEVICE).size()); +} + +/** + * Checks if non existing platform leads to an error + */ +TEST (FPGASetup, FindNonExistingPlatform) { + // TODO regex does not work so for now its not tested! + EXPECT_EXIT(fpga_setup::selectFPGADevice(DEFAULT_PLATFORM + 100, DEFAULT_DEVICE), + ::testing::ExitedWithCode(1), + ::testing::MatchesRegex(".*")); +} + +/** + * Checks if non existing device leads to an error + */ +TEST (FPGASetup, FindNonExistingDevice) { + // TODO regex does not work so for now its not tested! + EXPECT_EXIT(fpga_setup::selectFPGADevice(DEFAULT_PLATFORM, DEFAULT_DEVICE + 100), + ::testing::ExitedWithCode(1), + ::testing::MatchesRegex(".*")); +} + diff --git a/PTRANS/tests/test_host_functionality.cpp b/PTRANS/tests/test_host_functionality.cpp new file mode 100644 index 00000000..8f830179 --- /dev/null +++ b/PTRANS/tests/test_host_functionality.cpp @@ -0,0 +1,192 @@ +// +// Created by Marius Meyer on 04.12.19. +// +#include "gtest/gtest.h" +#include "parameters.h" +#include "CL/cl.hpp" +#include "../src/host/transpose_functionality.hpp" +#include "../extern/googletest/googlemock/include/gmock/gmock-matchers.h" + +/** + * Check correctness of reference matrix transposition implementation for 2x2 + */ +TEST(TransposeFunctionality, make2x2MatrixTranspose) { + + size_t used_size = 2; + auto A_test = new HOST_DATA_TYPE[used_size * used_size]; + auto B_test = new HOST_DATA_TYPE[used_size * used_size]; + auto result = new HOST_DATA_TYPE[used_size * used_size]; + for (int i = 0; i < used_size; i++) { + for (int j = 0; j < used_size; j++) { + A_test[i * used_size + j] = (HOST_DATA_TYPE) (i * used_size + j); + B_test[i * used_size + j] = 0.0; + } + } + transposeReference(A_test, B_test, result, used_size); + for (int i = 0; i < used_size; i++) { + for (int j = 0; j < used_size; j++) { + EXPECT_FLOAT_EQ(A_test[i * used_size + j], result[j * used_size + i]); + } + } +} + +/** + * Check correctness of reference matrix transposition implementation for 9x9 + */ +TEST(TransposeFunctionality, make9x9MatrixTranspose) { + + size_t used_size = 9; + auto A_test = new HOST_DATA_TYPE[used_size * used_size]; + auto B_test = new HOST_DATA_TYPE[used_size * used_size]; + auto result = new HOST_DATA_TYPE[used_size * used_size]; + for (int i = 0; i < used_size; i++) { + for (int j = 0; j < used_size; j++) { + A_test[i * used_size + j] = (HOST_DATA_TYPE) (i * used_size + j); + B_test[i * used_size + j] = 0.0; + } + } + transposeReference(A_test, B_test, result, used_size); + for (int i = 0; i < used_size; i++) { + for (int j = 0; j < used_size; j++) { + EXPECT_FLOAT_EQ(A_test[i * used_size + j], result[j * used_size + i]); + } + } +} + +/** + * Check that B is not transposed + */ +TEST(TransposeFunctionality, BStaysTheSame) { + + size_t used_size = 10; + auto A_test = new HOST_DATA_TYPE[used_size * used_size]; + auto B_test = new HOST_DATA_TYPE[used_size * used_size]; + auto result = new HOST_DATA_TYPE[used_size * used_size]; + for (int i = 0; i < used_size; i++) { + for (int j = 0; j < used_size; j++) { + B_test[i * used_size + j] = (HOST_DATA_TYPE) (i * used_size + j); + A_test[i * used_size + j] = 0.0; + } + } + transposeReference(A_test, B_test, result, used_size); + for (int i = 0; i < used_size; i++) { + for (int j = 0; j < used_size; j++) { + EXPECT_FLOAT_EQ(B_test[i * used_size + j], result[i * used_size + j]); + } + } +} + +/** + * Check if addition is done for A and B + */ +TEST(TransposeFunctionality, BAndAAreAddedUp) { + + size_t used_size = 10; + auto A_test = new HOST_DATA_TYPE[used_size * used_size]; + auto B_test = new HOST_DATA_TYPE[used_size * used_size]; + auto result = new HOST_DATA_TYPE[used_size * used_size]; + for (int i = 0; i < used_size; i++) { + for (int j = 0; j < used_size; j++) { + B_test[i * used_size + j] = (HOST_DATA_TYPE) (i * used_size + j); + A_test[i * used_size + j] = 1.0; + } + } + transposeReference(A_test, B_test, result, used_size); + for (int i = 0; i < used_size; i++) { + for (int j = 0; j < used_size; j++) { + EXPECT_FLOAT_EQ(B_test[i * used_size + j] + 1.0, result[i * used_size + j]); + } + } +} + +/** + * Check if the output has the correct structure + */ +TEST(ResultOutput, OutputsCorrectFormatHeader) { + std::vector transferTimings; + std::vector calculateTimings; + transferTimings.push_back(1.0); + calculateTimings.push_back(1.0); + std::shared_ptr results( + new bm_execution::ExecutionTimings{transferTimings, calculateTimings}); + + fpga_setup::setupEnvironmentAndClocks(); + + // Redirect stout buffer to local buffer to make checks possible + std::stringstream newStdOutBuffer; + std::streambuf *oldStdOutBuffer = std::cout.rdbuf(); + std::cout.rdbuf(newStdOutBuffer.rdbuf()); + + printResults(results, 10); + + // Redirect stdout to old buffer + std::cout.rdbuf(oldStdOutBuffer); + + EXPECT_THAT(newStdOutBuffer.str(), + ::testing::MatchesRegex("(\\s+)trans(\\s+)calc(\\s+)calc\\sFLOPS(\\s+)total\\sFLOPS\n.*")); +} + +/** + * Check if the output values have correct formatting + */ +TEST(ResultOutput, OutputsCorrectFormatValues) { + std::vector transferTimings; + std::vector calculateTimings; + transferTimings.push_back(1.0); + calculateTimings.push_back(1.0); + std::shared_ptr results( + new bm_execution::ExecutionTimings{transferTimings, calculateTimings}); + + fpga_setup::setupEnvironmentAndClocks(); + + // Redirect stout buffer to local buffer to make checks possible + std::stringstream newStdOutBuffer; + std::streambuf *oldStdOutBuffer = std::cout.rdbuf(); + std::cout.rdbuf(newStdOutBuffer.rdbuf()); + + printResults(results, 10); + + // Redirect stdout to old buffer + std::cout.rdbuf(oldStdOutBuffer); + + EXPECT_THAT(newStdOutBuffer.str(), + ::testing::MatchesRegex(".*\navg:\\s+1\\.00000e\\+00\\s+1\\.00000e\\+00.*\n.*\n")); +} + +/** + * Checks if the error is printed to stdout and the error is aggregated over the whole matrix. + */ +TEST(ErrorOutput, AggregatedErrorIsPrinted) { + HOST_DATA_TYPE *results = new HOST_DATA_TYPE[4 * 4]; + for (int i = 0; i < 4 * 4; i++) { + results[i] = 0.0; + } + + // Redirect stout buffer to local buffer to make checks possible + std::stringstream newStdOutBuffer; + std::streambuf *oldStdOutBuffer = std::cout.rdbuf(); + std::cout.rdbuf(newStdOutBuffer.rdbuf()); + + printCalculationError(4, results); + + // Redirect stdout to old buffer + std::cout.rdbuf(oldStdOutBuffer); + + EXPECT_THAT(newStdOutBuffer.str(), + ::testing::MatchesRegex("Maximum error:\\s+1\\.00000e\\+00\n")); +} + +/** + * Checks if the error is returned as an integer by the error calculation function. + */ +TEST(ErrorOutput, AggregatedErrorIsReturned) { + HOST_DATA_TYPE *results = new HOST_DATA_TYPE[4 * 4]; + for (int i = 0; i < 4 * 4; i++) { + results[i] = 0.0; + } + + int error = printCalculationError(4, results); + + EXPECT_EQ(error, 1); +} + diff --git a/PTRANS/tests/test_kernel_functionality_and_host_integration.cpp b/PTRANS/tests/test_kernel_functionality_and_host_integration.cpp new file mode 100644 index 00000000..2e57d7b2 --- /dev/null +++ b/PTRANS/tests/test_kernel_functionality_and_host_integration.cpp @@ -0,0 +1,211 @@ +// +// Created by Marius Meyer on 04.12.19. +// +#include + +#include "gtest/gtest.h" +#include "../src/host/execution.h" +#include "../src/host/transpose_functionality.hpp" +#include "parameters.h" + + +struct OpenCLKernelTest : testing::Test { + std::string kernelFileName; + HOST_DATA_TYPE *A; + HOST_DATA_TYPE *B; + HOST_DATA_TYPE *A_out; + std::shared_ptr config; + cl_uint matrix_size; + + OpenCLKernelTest() { + kernelFileName = "transpose_default_emulate.aocx"; + matrix_size = BLOCK_SIZE; + posix_memalign(reinterpret_cast(&A), 64, + sizeof(HOST_DATA_TYPE) * matrix_size * matrix_size); + posix_memalign(reinterpret_cast(&B), 64, + sizeof(HOST_DATA_TYPE) * matrix_size * matrix_size); + posix_memalign(reinterpret_cast(&A_out), 64, + sizeof(HOST_DATA_TYPE) * matrix_size * matrix_size); + for (int i = 0; i < matrix_size; i++) { + for (int j = 0; j < matrix_size; j++) { + A[i * matrix_size + j] = 0.0; + B[i * matrix_size + j] = 0.0; + A_out[i * matrix_size + j] = 0.0; + } + } + } + + void setupFPGA() { + std::vector device = fpga_setup::selectFPGADevice(DEFAULT_PLATFORM, DEFAULT_DEVICE); + cl::Context context(device[0]); + cl::Program program = fpga_setup::fpgaSetup(&context, device, &kernelFileName); + config = std::make_shared( + bm_execution::ExecutionConfiguration{ + context, device[0], program, + KERNEL_NAME, + 1, + matrix_size, + false + }); + generateInputData(matrix_size, A, B); + } + + ~OpenCLKernelTest() override { + free(A); + free(B); + free(A_out); + } +}; + +struct DifferentOpenCLKernelTest : OpenCLKernelTest, testing::WithParamInterface { + DifferentOpenCLKernelTest() { + kernelFileName = GetParam(); + setupFPGA(); + } +}; + + +/** + * Tests if B will not be transposed + */ +TEST_P(DifferentOpenCLKernelTest, FPGACorrectBStaysTheSame) { + for (int i = 0; i < matrix_size; i++) { + for (int j = 0; j < matrix_size; j++) { + A[i * matrix_size + j] = 0.0; + B[i * matrix_size + j] = i * matrix_size + j; + } + } + auto result = bm_execution::calculate(config, A, B, A_out); + for (int i = 0; i < matrix_size; i++) { + for (int j = 0; j < matrix_size; j++) { + EXPECT_FLOAT_EQ(A_out[i * matrix_size + j], B[i * matrix_size + j]); + } + } +} + +/** + * Tests if a block of A will be correctly transposed + */ +TEST_P(DifferentOpenCLKernelTest, FPGAABlockIsTransposed) { + for (int i = 0; i < matrix_size; i++) { + for (int j = 0; j < matrix_size; j++) { + A[i * matrix_size + j] = i * matrix_size + j; + B[i * matrix_size + j] = 0.0; + } + } + auto result = bm_execution::calculate(config, A, B, A_out); + for (int i = 0; i < matrix_size; i++) { + for (int j = 0; j < matrix_size; j++) { + EXPECT_FLOAT_EQ(A_out[i * matrix_size + j], A[j * matrix_size + i]); + } + } +} + +/** + * Tests if A will be transposed when it is bigger than one block + */ +TEST_P(DifferentOpenCLKernelTest, FPGAAIsTransposed) { + + // delete memory allocated in constructor + free(A); + free(B); + free(A_out); + + // allocate more memory for test with multiple blocks + matrix_size = 2 * BLOCK_SIZE; + posix_memalign(reinterpret_cast(&A), 64, + sizeof(HOST_DATA_TYPE) * matrix_size * matrix_size); + posix_memalign(reinterpret_cast(&B), 64, + sizeof(HOST_DATA_TYPE) * matrix_size * matrix_size); + posix_memalign(reinterpret_cast(&A_out), 64, + sizeof(HOST_DATA_TYPE) * matrix_size * matrix_size); + + setupFPGA(); + + // Do actual test + + for (int i = 0; i < matrix_size; i++) { + for (int j = 0; j < matrix_size; j++) { + A[i * matrix_size + j] = i * matrix_size + j; + B[i * matrix_size + j] = 0.0; + } + } + auto result = bm_execution::calculate(config, A, B, A_out); + for (int i = 0; i < matrix_size; i++) { + for (int j = 0; j < matrix_size; j++) { + EXPECT_FLOAT_EQ(A_out[i * matrix_size + j], A[j * matrix_size + i]); + } + } +} + +/** + * Tests if matrix A and B will be summed up in the result + */ +TEST_P(DifferentOpenCLKernelTest, FPGAAAndBAreSummedUp) { + for (int i = 0; i < matrix_size; i++) { + for (int j = 0; j < matrix_size; j++) { + A[i * matrix_size + j] = 1.0; + B[i * matrix_size + j] = i * matrix_size + j; + } + } + auto result = bm_execution::calculate(config, A, B, A_out); + for (int i = 0; i < matrix_size; i++) { + for (int j = 0; j < matrix_size; j++) { + EXPECT_FLOAT_EQ(A_out[i * matrix_size + j], B[i * matrix_size + j] + 1.0); + } + } +} + + +/** + * Checks the size and values of the timing measurements that are retured by calculate. + */ +TEST_P(DifferentOpenCLKernelTest, FPGATimingsMeasuredForEveryIteration) { + config->repetitons = 10; + auto result = bm_execution::calculate(config, A, B, A_out); + EXPECT_EQ(result->calculationTimings.size(), 10); + EXPECT_EQ(result->transferTimings.size(), 10); + for (int t = 0; t < 10; t++) { + EXPECT_GE(result->transferTimings[t], 0.0); + EXPECT_GE(result->calculationTimings[t], 0.0); + } +} + +INSTANTIATE_TEST_CASE_P(Default, DifferentOpenCLKernelTest, + testing::Values( + "transpose_optimized_emulate.aocx", + "transpose_default_emulate.aocx" + )); + +/** + * Check if the generated input data is in the specified range + */ +TEST(ExecutionDefault, GenerateInputDataRange) { + HOST_DATA_TYPE *A = new HOST_DATA_TYPE[25]; + HOST_DATA_TYPE *B = new HOST_DATA_TYPE[25]; + generateInputData(5, A, B); + for (int i = 0; i < 5; i++) { + for (int j = 0; j < 5; j++) { + EXPECT_LT(A[i * 5 + j], 100); + EXPECT_GT(A[i * 5 + j], -100); + EXPECT_LT(B[i * 5 + j], 101); + EXPECT_GT(B[i * 5 + j], -99); + } + } +} + +/** + * Check if the input data is generated correctly + */ +TEST(ExecutionDefault, GenerateInputDataCorrectness) { + HOST_DATA_TYPE *A = new HOST_DATA_TYPE[25]; + HOST_DATA_TYPE *B = new HOST_DATA_TYPE[25]; + HOST_DATA_TYPE *result = new HOST_DATA_TYPE[25]; + generateInputData(5, A, B); + transposeReference(A, B, result, 5); + for (int i = 0; i < 5; i++) { + for (int j = 0; j < 5; j++) { + EXPECT_NEAR(result[i * 5 + j], 1.0, std::numeric_limits::epsilon()); + } + } +} diff --git a/README.md b/README.md new file mode 100755 index 00000000..50a39c8e --- /dev/null +++ b/README.md @@ -0,0 +1,11 @@ +# HPCC FPGA + +HPCC FPGA is an OpenCL-based FPGA benchmark suite with focus on high performance computing. +It is based on the benchmarks of the well-established CPU benchmark suite HPCC. +This repository contains the OpenCL kernels and host code for all benchmarks together with the build scripts and instructions. + +The repository is structured as follows: + +- The code and build instructions for every benchmark is located in the subfolders +- Every benchmark comes with a separate host code and build scripts +- The repository contains multiple submodules used by the benchmarks contained in the `extern` folder. Make sure to initialize and update the submodules before building a benchmark with `git submodule update --init --recursive`! diff --git a/RandomAccess/.gitignore b/RandomAccess/.gitignore new file mode 100644 index 00000000..fe4d0a26 --- /dev/null +++ b/RandomAccess/.gitignore @@ -0,0 +1,6 @@ +*/.DS_Store +.DS_Store +cmake-* +*.genlog +.vscode +.idea diff --git a/RandomAccess/CHANGELOG b/RandomAccess/CHANGELOG new file mode 100644 index 00000000..9ee25fef --- /dev/null +++ b/RandomAccess/CHANGELOG @@ -0,0 +1,24 @@ +# Changelog + +This file contains all changes made to the source code for each release. + +## 2.0.2.1 + +##### Added: +- Host side support for SVM + +## 2.0.2 + +#### Changed: +- OpenMP pragmas from single to master with explicit barriers + +## 2.0.1 + +#### Added: +- Unit tests for result validation and kernel execution +- Add new boolean parameter `COMBINE_LOOPS` that allows to combine the address calculation and loading of the data into a single loop instead of two separate loops. This can improve the performance if the loops are executed sequentially because the outer loop can not be pipelined. + +## 2.0 + +#### Added: +- Replace Makefile with CMake as a build system \ No newline at end of file diff --git a/RandomAccess/CMakeLists.txt b/RandomAccess/CMakeLists.txt new file mode 100755 index 00000000..2c77c8b9 --- /dev/null +++ b/RandomAccess/CMakeLists.txt @@ -0,0 +1,54 @@ +cmake_minimum_required(VERSION 3.1) +project(RandomAccess VERSION 2.0.2.1 + DESCRIPTION "OpenCL RandomAccess Benchmark for FPGA") + +set (CMAKE_CXX_STANDARD 11) +set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_SOURCE_DIR}/../extern/hlslib/cmake) +set(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin) + +find_package(OpenMP) +find_package(IntelFPGAOpenCL) +find_package(Vitis) + +set(DEFAULT_REPETITIONS 10 CACHE STRING "Default number of repetitions") +set(DEFAULT_ARRAY_LENGTH 536870912 CACHE STRING "Default size of the data arrays") +set(DEFAULT_DEVICE -1 CACHE STRING "Index of the default device to use") +set(DEFAULT_PLATFORM -1 CACHE STRING "Index of the default platform to use") +set(PARALLEL_MEM_ACCESSES 1 CACHE STRING "Unrolling factor that is used for all loops in the kernels that access the global memory.") +set(FPGA_BOARD_NAME p520_hpc_sg280l CACHE STRING "Name of the target FPGA board") +set(NUM_REPLICATIONS 4 CACHE STRING "Number of times the kernels will be replicated") +set(DEVICE_BUFFER_SIZE 1 CACHE STRING "Buffer size in number of values that is used within the single kernel implementation.") +set(COMBINE_LOOPS Yes CACHE BOOL "If enabled this will combine the address calculation loop and the load darta loop to a single loop. This can improve the performance when all loops are running sequentially") +set(USE_SVM No CACHE BOOL "Use coarse grained SVM instead of loading the buffer on the FPGA before execution. Device needs to support this feature.") + +if (VITIS_FOUND) + set(XILINX_GENERATE_LINK_SETTINGS Yes CACHE BOOL "Generate the link settings depending on the number of replications for the kernels") + set(XILINX_LINK_SETTINGS_FILE "${CMAKE_SOURCE_DIR}/settings/settings.link.xilinx.random_access_single.generator.ini" CACHE STRING "The link settings file that should be used when generating is disabled") + set(XILINX_COMPILE_FLAGS "-j 40" CACHE STRING "Additional compile flags for the v++ compiler") + set(XILINX_COMPILE_SETTINGS "${CMAKE_SOURCE_DIR}/settings/settings.compile.xilinx.ini" CACHE STRING "Compile settings file for the v++ compiler") + separate_arguments(XILINX_COMPILE_FLAGS) +endif() + +if (INTELFPGAOPENCL_FOUND) + set(AOC_FLAGS "-fpc -fp-relaxed -no-interleaving=default" CACHE STRING "Used flags for the AOC compiler") + separate_arguments(AOC_FLAGS) +endif() + +set(HOST_DATA_TYPE cl_ulong) +set(HOST_DATA_TYPE_SIGNED cl_long) +set(DEVICE_DATA_TYPE long) +set(DEVICE_DATA_TYPE_UNSIGNED ulong) + +if (NUM_REPLICATIONS EQUAL 1) + set(SINGLE_KERNEL Yes) +endif() + +configure_file( + "${CMAKE_SOURCE_DIR}/src/common/parameters.h.in" + "${CMAKE_BINARY_DIR}/src/common/parameters.h" +) +include_directories(${CMAKE_BINARY_DIR}/src/common) + +add_subdirectory(src/device) +add_subdirectory(src/host) +add_subdirectory(tests) diff --git a/RandomAccess/LICENSE b/RandomAccess/LICENSE new file mode 100644 index 00000000..fbb6b7d3 --- /dev/null +++ b/RandomAccess/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2019 pc2 + +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/RandomAccess/README.md b/RandomAccess/README.md new file mode 100644 index 00000000..f3bf0094 --- /dev/null +++ b/RandomAccess/README.md @@ -0,0 +1,111 @@ +# Random Access Benchmark for FPGA + +This repository contains the Random Access Benchmark for FPGA and its OpenCL kernels. +Currently only the Intel FPGA SDK for OpenCL utility is supported. + +It is a modified implementation of the +[Random Access Benchmark](https://icl.utk.edu/projectsfiles/hpcc/RandomAccess/) +provided in the [HPC Challenge Benchmark](https://icl.utk.edu/hpcc/) suite. +The implementation follows the Python reference implementation given in +_Introduction to the HPCChallenge Benchmark Suite_ available +[here](http://icl.cs.utk.edu/news_pub/submissions/hpcc-challenge-intro.pdf). + +## Build + +The Makefile will generate the device code using the code generator given in a submodule. +So to make use of the code generation make sure to check out the repository recursively. + +The code has the following dependencies: + +- C++ compiler with C++11 support +- Intel FPGA SDK for OpenCL or Xilinx Vitis +- CMake 3.1 for building + +CMake is used as the build system. +The targets below can be used to build the benchmark and its kernels: + + | Target | Description | + | --------------------- | ---------------------------------------------- | + | RandomAccess_`VENDOR` | Builds the host application linking with the Intel SDK| + | Test_`VENDOR` | Compile the tests and its dependencies linking with the Intel SDK | + + More over there are additional targets to generate kernel reports and bitstreams. + The kernel targets are: + + | Target | Description | + | ------------------------------ | ---------------------------------------------- | + | random_access_kernels_single_`VENDOR` | Synthesizes the kernel (takes several hours!) | + | random_access_kernels_single_report_intel | Create an HTML report for the kernel | + | random_access_kernels_single_compile_xilinx | Just compile kernel and create logs and reports | + | random_access_kernels_single_emulate_`VENDOR` | Create a n emulation kernel | + +For the host code as well as the kernels `VENDOR` can be `intel` or `xilinx`. +The report target for Xilinx is missing but reports will be generated when the kernel is synthesized. + + You can build for example the host application by running + + mkdir build && cd build + cmake .. + make RandomAccess_intel + +You will find all executables and kernel files in the `bin` +folder of your build directory. +You should always specify a target with make to reduce the build time! +You might want to specify predefined parameters before build: + +Name | Default | Description | +---------------- |-------------|--------------------------------------| +`DEFAULT_DEVICE` | -1 | Index of the default device (-1 = ask) | +`DEFAULT_PLATFORM`| -1 | Index of the default platform (-1 = ask) | +`DEFAULT_REPETITIONS`| 10 | Number of times the kernel will be executed | +`FPGA_BOARD_NAME`| p520_hpc_sg280l | Name of the target board | +`DEFAULT_ARRAY_LENGTH`| 536870912 | Length of each input array (4GB) | +`GLOBAL_MEM_UNROLL`| 1 | Loop unrolling factor for all loops in the device code | +`AOC_FLAGS`| `-fpc -fp-relaxed -no-interleaving=default` | Additional Intel AOC compiler flags that are used for kernel compilation | +`NUM_REPLICATIONS`| 4 | Replicates the kernels the given number of times | +`DEVICE_BUFFER_SIZE`| 1 | Number of values that are stored in the local memory in the single kernel approach | + +Moreover the environment variable `INTELFPGAOCLSDKROOT` has to be set to the root +of the Intel FPGA SDK installation. + +Additionally it is possible to set the used compiler and other build tools +in the `CMakeCache.txt` located in the build directory after running cmake. + +## Execution + +For execution of the benchmark run: + + ./RandomAccess_intel -f path_to_kernel.aocx + +For more information on available input parameters run + + ./RandomAccess_intel -h + +To execute the unit and integration tests for Intel devices run + + CL_CONTEXT_EMULATOR_DEVICE=1 ./Test_intel + +in the `bin` folder within the build directory. +It will run an emulation of the kernel and execute some functionality tests. + +## Result Interpretation + +The host code will print the results of the execution to the standard output. +The result summary looks similar to this: + + best mean GUPS error + 1.73506e+01 1.73507e+01 2.47540e-01 9.87137e-03 + +- `best` and `mean` are the fastest and the mean kernel execution time. + The pure kernel execution time is measured without transferring the buffer + back and forth to the host. +- `GUPS` contains the calculated metric _Giga Updates per Second_. It takes the + fastest kernel execution time. The formula is + ![GOPs = 4 * GLOBAL_MEM_SIZE / (best_time * 10^9)](https://latex.codecogs.com/gif.latex?\inline&space;GUPS&space;=&space;\frac{4&space;*&space;GLOBAL\\_MEM\\_SIZE}{&space;best\\_time&space;*&space;10^9}). +- `error` contains the percentage of memory positions with wrong values + after the updates where made. The maximal allowed error rate of the + random access benchmark is 1% according to the rules given in the HPCChallenge + specification. + +Benchmark results can be found in the `results` folder in this +repository. diff --git a/RandomAccess/results/README.md b/RandomAccess/results/README.md new file mode 100644 index 00000000..e8965b8d --- /dev/null +++ b/RandomAccess/results/README.md @@ -0,0 +1,50 @@ +# Benchmark Results + +This folder contains the results for the execution of the benchmark using the +`single` kernel on +[Noctua](https://pc2.uni-paderborn.de/hpc-services/available-systems/noctua/) +and the research clusters of the PC2. + +### Execution Environment + +The used clusters are: +- [Noctua](https://pc2.uni-paderborn.de/hpc-services/available-systems/noctua/): [BittWare 520N board](https://www.bittware.com/fpga/520n/) equipped with +Intel Stratix 10 GX 2800 +- [CC](https://pc2.uni-paderborn.de/hpc-services/available-systems/fpga-research-clusters/): [Bittware 385A board](https://www.bittware.com/fpga/385a/) equipped with +Intel Arria 10 GX1150 +- [HARP2](https://pc2.uni-paderborn.de/hpc-services/available-systems/fpga-research-clusters/): Intel BDW+FPGA hybrid CPU/FPGA Arria 10 GX1150 + +Details of the synthesized kernels for all used clusters. +The values for the most important build flags is given. +Also the used compiler versions for host and device and the benchmark version is given for all setups. +The default `AOC_FLAGS` where: `-fpc -fp-relaxed -no-interleaving=default -seed=2809`. + +| Parameter | Noctua | CC | HARP2 | +|------------------ | -------------------------------- | --------------- | -------------------------------- | +| `BOARD` | p520_hpc_sg280l/ p520_max_sg280l | p385a_sch_ax115 | bdw_fpga_v1.0 | +| `AOC_FLAGS` | default | default | `--fpc --fp-relaxed --seed 2809` | +| `REPLICATIONS` | 4 | 2 | 4 | +| `UPDATE_SPLIT` | 1024 | 1024 | 1024 | +| `GLOBAL_MEM_UNROLL`| 8 | 8 | 8 | +| `CXX_FLAGS` | | | `-DNO_CXXOPTS` | +| SDK Versions | 18.0.1, 18.1.1, 19.1.0, 19.2.0 | 17.1.2 | 16.0.2 | +| GCC Version | g++ (GCC) 7.3.0 | g++ (GCC) 5.4.0 | g++ (Ubuntu 4.8.4-2ubuntu1~14.04.4) 4.8.4 | +| BM Version | `v1.0.0` | `v1.0.0` | `v1.0.1-SVM` | + +For the HARP2 system, the implementation was ported to use SVM in the host code. +The kernel code remained unchanged. + +### Result Interpretation + +All results are summarized in the given +[CSV](./frandom_single_results.csv) file together with the fMax of the +synthesized kernel. + +The benchmark results are also plotted in the figure below. +Note that the SDK version 17.1.2 is not directly comparable with the other +results since another FPGA and board was used. +One difference is the number of memory banks on the board. +For a better comparison of the results the metric _GOP/s per memory bank_ is +used in the plot instead that was calculated as follows: ![GOP/s / num_memory_banks](https://latex.codecogs.com/gif.latex?\inline&space;\frac{GOP/s}{num\\_memory\\_banks}) + +![Graph of the collected results](frandom_single_results.jpg) diff --git a/RandomAccess/results/frandom_single_results.csv b/RandomAccess/results/frandom_single_results.csv new file mode 100644 index 00000000..be3c0c58 --- /dev/null +++ b/RandomAccess/results/frandom_single_results.csv @@ -0,0 +1,7 @@ +,board,best,mean,guops,error,guops_per_bank,fmax +19.2.0_hpc_single,p520_hpc_sg280l,17.3506,17.3507,0.24754,0.00987137,0.061885,264.13 +19.1.0_single,p520_max_sg280l,17.3717,17.3721,0.247239,0.00946922,0.06180975,254.38 +18.1.1_hpc_single,p520_hpc_sg280l,17.3558,17.3559,0.247466,0.00977451,0.0618665,259.6 +18.0.1_single,p520_max_sg280l,17.8358,17.8364,0.240805,0.0105986,0.06020125,271.58 +17.1.2_single,p385a_sch_ax115,23.2705,23.2705,0.0922836,0.0157055,0.0461418,217.67 +16.0.2_single,bdw_fpga_v1.0,12.211,12.2115,0.0879323,0.0215061,0.04396615,190.87 diff --git a/RandomAccess/results/frandom_single_results.jpg b/RandomAccess/results/frandom_single_results.jpg new file mode 100644 index 0000000000000000000000000000000000000000..efd0babbc08be6cbe216bbe868b049e7a3761a29 GIT binary patch literal 60182 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
    !i{mWr>tF*i*mSZBl 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 3) & (m - 1); + #ifndef SINGLE_KERNEL + local_address[ld] = address - address_start; + #else + local_address[ld] = address; + #endif +#ifndef COMBINE_LOOPS + } + + // load the data of the calculated addresses from global memory + __attribute__((opencl_unroll_hint(UNROLL_COUNT))) + for (int ld=0; ld< BUFFER_SIZE; ld++) { +#endif + #ifdef SINGLE_KERNEL + loaded_data[ld] = data[local_address[ld]]; + #else + if (local_address[ld] < data_chunk) { + loaded_data[ld] = data[local_address[ld]]; + } + #endif + } + + // store back the calculated addresses from global memory + __attribute__((opencl_unroll_hint(UNROLL_COUNT))) + for (int ld=0; ld< BUFFER_SIZE; ld++) { + #ifdef SINGLE_KERNEL + data[local_address[ld]] = loaded_data[ld] ^update_val[ld]; + #else + if (local_address[ld] < data_chunk) { + data[local_address[ld]] = loaded_data[ld] ^ update_val[ld]; + } + #endif + } + } +} diff --git a/RandomAccess/src/device/random_access_kernels_single_rnd.cl b/RandomAccess/src/device/random_access_kernels_single_rnd.cl new file mode 100644 index 00000000..46eb5e84 --- /dev/null +++ b/RandomAccess/src/device/random_access_kernels_single_rnd.cl @@ -0,0 +1,147 @@ +/* +Copyright (c) 2019 Marius Meyer + +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. +*/ + +/* +The data type used for the data array. +*/ +#ifndef DATA_TYPE +#define DATA_TYPE long +#endif + +/* +Unsigned version of the data type used for the data array. +*/ +#ifndef DATA_TYPE_UNSIGNED +#define DATA_TYPE_UNSIGNED ulong +#endif + +/* +Unrolling factor for the read and write pipelines of each kernel. +*/ +#ifndef GLOBAL_MEM_UNROLL +#define GLOBAL_MEM_UNROLL 4 +#endif + +/* +Constant used to update the pseudo random number +*/ +#define POLY 7 + +// PY_CODE_GEN block_start +#define SINGLE_KERNEL +// PY_CODE_GEN block_end if_cond(replications == 1, CODE, None) + +/* +The size of the local memory buffer. +If it is chosen too big, the experienced error might increase because Multiple +updates to the same memory address within this range will be overridden. +*/ +#ifndef UPDATE_SPLIT +#define UPDATE_SPLIT 1024 +#endif + +/* +Kernel, that will update the given data array accoring to a predefined pseudo- +random access scheme. The overall data array might be equally split between +multiple kernels. In that case, the index of the current split can be given +to the kernel. + +@param data The data array that will be updated +@param random precalculated random numbers that are used to simultaneously update + multiple addresses +@param m The size of the data array +@param data_chunk The chunk index that has to be updated by the kernel +*/ +// PY_CODE_GEN block_start +__kernel +void accessMemory$repl$(__global volatile DATA_TYPE_UNSIGNED* restrict data, + __global DATA_TYPE_UNSIGNED* restrict random, + DATA_TYPE_UNSIGNED m, + DATA_TYPE_UNSIGNED data_chunk) { + + DATA_TYPE_UNSIGNED local_random[UPDATE_SPLIT]; + #pragma unroll GLOBAL_MEM_UNROLL + for (int i=0; i< UPDATE_SPLIT; i++) { + local_random[i] = random[i]; + } + + // calculate the start of the address range this kernel is responsible for + #ifndef SINGLE_KERNEL + DATA_TYPE_UNSIGNED const address_start = $repl$ * data_chunk; + #endif + + DATA_TYPE_UNSIGNED const mupdate = 4 * m; + + // do random accesses + #pragma ivdep + for (DATA_TYPE_UNSIGNED i=0; i< mupdate / UPDATE_SPLIT; i++) { + + DATA_TYPE_UNSIGNED local_address[UPDATE_SPLIT]; + DATA_TYPE_UNSIGNED loaded_data[UPDATE_SPLIT]; + DATA_TYPE_UNSIGNED writeback_data[UPDATE_SPLIT]; + DATA_TYPE_UNSIGNED update_val[UPDATE_SPLIT]; + + // calculate next addresses + #pragma unroll GLOBAL_MEM_UNROLL + for (int ld=0; ld< UPDATE_SPLIT; ld++) { + DATA_TYPE v = 0; + if (((DATA_TYPE) local_random[ld]) < 0) { + v = POLY; + } + local_random[ld] = (local_random[ld] << 1) ^ v; + update_val[ld] = local_random[ld]; + DATA_TYPE_UNSIGNED address = local_random[ld] & (m - 1); + #ifndef SINGLE_KERNEL + local_address[ld] = address - address_start; + #else + local_address[ld] = address; + #endif + } + + // load the data of the calculated addresses from global memory + #pragma unroll GLOBAL_MEM_UNROLL + #pragma ivdep + for (int ld=0; ld< UPDATE_SPLIT; ld++) { + #ifdef SINGLE_KERNEL + loaded_data[ld] = data[local_address[ld]]; + #else + if (local_address[ld] < data_chunk) { + loaded_data[ld] = data[local_address[ld]]; + } + #endif + } + + // store back the calculated addresses from global memory + #pragma unroll GLOBAL_MEM_UNROLL + #pragma ivdep + for (int ld=0; ld< UPDATE_SPLIT; ld++) { + #ifdef SINGLE_KERNEL + data[local_address[ld]] = loaded_data[ld] ^update_val[ld]; + #else + if (local_address[ld] < data_chunk) { + data[local_address[ld]] = loaded_data[ld] ^ update_val[ld]; + } + #endif + } + } +} +// PY_CODE_GEN block_end [replace(replace_dict=locals()) for repl in range(replications)] diff --git a/RandomAccess/src/host/CMakeLists.txt b/RandomAccess/src/host/CMakeLists.txt new file mode 100755 index 00000000..d2f2114f --- /dev/null +++ b/RandomAccess/src/host/CMakeLists.txt @@ -0,0 +1,25 @@ +include_directories(../../../extern/cxxopts/include) + +set(HEADER_FILES execution.h setup/fpga_setup.hpp random_access_functionality.hpp) +set(HOST_SOURCE execution_single.cpp main.cpp setup/fpga_setup.cpp random_access_functionality.cpp) + +if (INTELFPGAOPENCL_FOUND) + include_directories(${IntelFPGAOpenCL_INCLUDE_DIRS}) + include_directories(${CMAKE_BINARY_DIR}/src/common) + add_executable(RandomAccess_intel ${HOST_SOURCE} ${HEADER_FILES}) + target_link_libraries(RandomAccess_intel ${IntelFPGAOpenCL_LIBRARIES}) + if (USE_SVM) + target_compile_definitions(RandomAccess_intel PRIVATE -DCL_VERSION_2_0) + endif() + target_compile_definitions(RandomAccess_intel PRIVATE -DINTEL_FPGA -DCL_VERSION_2_0) + add_test(NAME test_intel_host_executable COMMAND $ -h) +endif() + +if (Vitis_FOUND) + include_directories(${Vitis_INCLUDE_DIRS}) + include_directories(${CMAKE_BINARY_DIR}/src/common) + add_executable(RandomAccess_xilinx ${HOST_SOURCE} ${HEADER_FILES}) + target_link_libraries(RandomAccess_xilinx ${Vitis_LIBRARIES}) + target_compile_definitions(RandomAccess_xilinx PRIVATE -DXILINX_FPGA) + add_test(NAME test_xilinx_host_executable COMMAND $ -h) +endif() diff --git a/RandomAccess/src/host/execution.h b/RandomAccess/src/host/execution.h new file mode 100644 index 00000000..2a691fc0 --- /dev/null +++ b/RandomAccess/src/host/execution.h @@ -0,0 +1,79 @@ +/* +Copyright (c) 2019 Marius Meyer + +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 SRC_HOST_EXECUTION_H_ +#define SRC_HOST_EXECUTION_H_ + +/* C++ standard library headers */ +#include +#include + +/* External library headers */ +#include "CL/cl.hpp" + +#include "parameters.h" + + +namespace bm_execution { + +struct ExecutionConfiguration { + cl::Context context; + cl::Device device; + cl::Program program; + uint repetitions; + uint replications; + size_t arraySize; +}; + +/** +This struct is returned by the calculate call and contains the measured +runtimes and the error rate in the data set after the updates. + +@see bm_execution::calculate() +*/ +struct ExecutionResults { + std::vector times; +}; + +/** +The actual execution of the benchmark. +This method can be implemented in multiple *.cpp files. This header enables +simple exchange of the different calculation methods. + +@param context OpenCL context used to create needed Buffers and queues +@param device The OpenCL device that is used to execute the benchmarks +@param program The OpenCL program containing the kernels +@param repetitions Number of times the kernels are executed +@param replications Number of times a kernel is replicated - may be used in + different ways depending on the implementation of this + method +@param dataSize The size of the data array that may be used for benchmark + execution in number of items +@param useMemInterleaving Prepare buffers using memory interleaving + +@return The time measurements and the error rate counted from the executions +*/ +std::shared_ptr +calculate(std::shared_ptr config, HOST_DATA_TYPE * data); + +} // namespace bm_execution + +#endif // SRC_HOST_EXECUTION_H_ diff --git a/RandomAccess/src/host/execution_ndrange.cpp b/RandomAccess/src/host/execution_ndrange.cpp new file mode 100644 index 00000000..3b8b0d67 --- /dev/null +++ b/RandomAccess/src/host/execution_ndrange.cpp @@ -0,0 +1,191 @@ +/* +Copyright (c) 2019 Marius Meyer + +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. +*/ + +/* Related header files */ +#include "src/host/execution.h" + +/* C++ standard library headers */ +#include +#include +#include +#include + +/* External library headers */ +#include "CL/cl.hpp" +#if QUARTUS_MAJOR_VERSION > 18 +#include "CL/cl_ext_intelfpga.h" +#endif + +/* Project's headers */ +#include "src/host/fpga_setup.h" +#include "src/host/random_access_functionality.h" + +namespace bm_execution { + + /* + Implementation for the ndrange kernel. + @copydoc bm_execution::calculate() + */ + std::shared_ptr + calculate(cl::Context context, cl::Device device, cl::Program program, + uint repetitions, uint replications, size_t dataSize, + bool useMemInterleaving) { + // int used to check for OpenCL errors + int err; + DATA_TYPE_UNSIGNED* random; + posix_memalign(reinterpret_cast(&random), 64, + sizeof(DATA_TYPE_UNSIGNED)*UPDATE_SPLIT); + + for (DATA_TYPE i=0; i < UPDATE_SPLIT; i++) { + random[i] = starts((4 * DATA_LENGTH) / UPDATE_SPLIT * i); + } + + std::vector compute_queue; + std::vector Buffer_data; + std::vector Buffer_random; + std::vector accesskernel; + std::vector data_sets; + + /* --- Prepare kernels --- */ + + for (int r=0; r < replications; r++) { + DATA_TYPE_UNSIGNED* data; + posix_memalign(reinterpret_cast(&data), 64, + sizeof(DATA_TYPE)*(dataSize / replications)); + data_sets.push_back(data); + + compute_queue.push_back(cl::CommandQueue(context, device)); + + // Select memory bank to place data replication + int channel = 0; + if (!useMemInterleaving) { + switch ((r % replications) + 1) { + case 1: channel = CL_CHANNEL_1_INTELFPGA; break; + case 2: channel = CL_CHANNEL_2_INTELFPGA; break; + case 3: channel = CL_CHANNEL_3_INTELFPGA; break; + case 4: channel = CL_CHANNEL_4_INTELFPGA; break; + case 5: channel = CL_CHANNEL_5_INTELFPGA; break; + case 6: channel = CL_CHANNEL_6_INTELFPGA; break; + case 7: channel = CL_CHANNEL_7_INTELFPGA; break; + } + } + + Buffer_data.push_back(cl::Buffer(context, channel | + CL_MEM_READ_WRITE, + sizeof(DATA_TYPE_UNSIGNED)*(dataSize / replications))); + Buffer_random.push_back(cl::Buffer(context, channel | + CL_MEM_WRITE_ONLY, + sizeof(DATA_TYPE_UNSIGNED) * UPDATE_SPLIT)); + accesskernel.push_back(cl::Kernel(program, + RANDOM_ACCESS_KERNEL, &err)); + ASSERT_CL(err); + + // prepare kernels + err = accesskernel[r].setArg(0, Buffer_data[r]); + ASSERT_CL(err); + err = accesskernel[r].setArg(1, Buffer_random[r]); + ASSERT_CL(err); + err = accesskernel[r].setArg(2, DATA_TYPE_UNSIGNED(dataSize)); + ASSERT_CL(err); + } + + /* --- Execute actual benchmark kernels --- */ + + double t; + std::vector executionTimes; + for (int i = 0; i < repetitions; i++) { + // prepare data and send them to device + for (DATA_TYPE_UNSIGNED r =0; r < replications; r++) { + for (DATA_TYPE_UNSIGNED j=0; + j < (dataSize / replications); j++) { + data_sets[r][j] = r*(dataSize / replications) + j; + } + } + for (int r=0; r < replications; r++) { + compute_queue[r].enqueueWriteBuffer(Buffer_data[r], CL_TRUE, 0, + sizeof(DATA_TYPE)*(dataSize / replications), data_sets[r]); + compute_queue[r].enqueueWriteBuffer(Buffer_random[r], CL_TRUE, + 0, sizeof(DATA_TYPE_UNSIGNED) * UPDATE_SPLIT, random); + } + + // Execute benchmark kernels + auto t1 = std::chrono::high_resolution_clock::now(); + for (int r=0; r < replications; r++) { + compute_queue[r].enqueueNDRangeKernel(accesskernel[r], + cl::NullRange, + cl::NDRange(UPDATE_SPLIT), + cl::NDRange(1), NULL, NULL); + } + for (int r=0; r < replications; r++) { + compute_queue[r].finish(); + } + auto t2 = std::chrono::high_resolution_clock::now(); + std::chrono::duration timespan = + std::chrono::duration_cast> + (t2 - t1); + executionTimes.push_back(timespan.count()); + } + + /* --- Read back results from Device --- */ + + for (int r=0; r < replications; r++) { + compute_queue[r].enqueueReadBuffer(Buffer_data[r], CL_TRUE, 0, + sizeof(DATA_TYPE)*(dataSize / replications), data_sets[r]); + } + DATA_TYPE_UNSIGNED* data; + posix_memalign(reinterpret_cast(&data), 64, + (sizeof(DATA_TYPE)*dataSize)); + for (size_t r =0; r < replications; r++) { + for (size_t j=0; j < (dataSize / replications); j++) { + data[r*(dataSize / replications) + j] = data_sets[r][j]; + } + free(reinterpret_cast(data_sets[r])); + } + + /* --- Check Results --- */ + + DATA_TYPE_UNSIGNED temp = 1; + for (DATA_TYPE_UNSIGNED i=0; i < 4L*dataSize; i++) { + DATA_TYPE v = 0; + if (((DATA_TYPE)temp) < 0) { + v = POLY; + } + temp = (temp << 1) ^ v; + data[temp & (dataSize - 1)] ^= temp; + } + + double errors = 0; + for (DATA_TYPE_UNSIGNED i=0; i< dataSize; i++) { + if (data[i] != i) { + errors++; + } + } + free(reinterpret_cast(data)); + free(reinterpret_cast(random)); + + std::shared_ptr results( + new ExecutionResults{executionTimes, + errors / dataSize}); + return results; + } + +} // namespace bm_execution diff --git a/RandomAccess/src/host/execution_single.cpp b/RandomAccess/src/host/execution_single.cpp new file mode 100644 index 00000000..44a22265 --- /dev/null +++ b/RandomAccess/src/host/execution_single.cpp @@ -0,0 +1,163 @@ +/* +Copyright (c) 2019 Marius Meyer + +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. +*/ + +/* Related header files */ +#include "execution.h" + +/* C++ standard library headers */ +#include +#include +#include +#include + +/* External library headers */ +#include "CL/cl.hpp" + +/* Project's headers */ +#include "setup/fpga_setup.hpp" +#include "random_access_functionality.hpp" + +namespace bm_execution { + + /* + Implementation for the single kernel. + @copydoc bm_execution::calculate() + */ + std::shared_ptr + calculate(std::shared_ptr config, HOST_DATA_TYPE * data) { + // int used to check for OpenCL errors + int err; + + std::vector compute_queue; + std::vector Buffer_data; + std::vector Buffer_random; + std::vector accesskernel; + + /* --- Prepare kernels --- */ + + for (int r=0; r < config->replications; r++) { + compute_queue.push_back(cl::CommandQueue(config->context, config->device)); + + int memory_bank_info = 0; +#ifdef INTEL_FPGA + memory_bank_info = ((r + 1) << 16); +#endif + Buffer_data.push_back(cl::Buffer(config->context, + CL_MEM_READ_WRITE | memory_bank_info, + sizeof(HOST_DATA_TYPE)*(config->arraySize / config->replications))); +#ifdef INTEL_FPGA + accesskernel.push_back(cl::Kernel(config->program, + (RANDOM_ACCESS_KERNEL + std::to_string(r)).c_str() , + &err)); +#endif +#ifdef XILINX_FPGA + accesskernel.push_back(cl::Kernel(config->program, + (std::string(RANDOM_ACCESS_KERNEL) + "0:{" + RANDOM_ACCESS_KERNEL + "0_" + std::to_string(r + 1) + "}").c_str() , + &err)); +#endif + ASSERT_CL(err); + + // prepare kernels +#ifdef USE_SVM + err = clSetKernelArgSVMPointer(accesskernel[r](), 0, + reinterpret_cast(&data[r * (config->arraySize / config->replications)])); +#else + err = accesskernel[r].setArg(0, Buffer_data[r]); +#endif + + ASSERT_CL(err); + err = accesskernel[r].setArg(1, HOST_DATA_TYPE(config->arraySize)); + ASSERT_CL(err); + err = accesskernel[r].setArg(2, + HOST_DATA_TYPE((config->arraySize / config->replications))); + ASSERT_CL(err); + err = accesskernel[r].setArg(3, + cl_uint(r)); + ASSERT_CL(err); + } + + /* --- Execute actual benchmark kernels --- */ + + std::vector executionTimes; + for (int i = 0; i < config->repetitions; i++) { + std::chrono::time_point t1; +#pragma omp parallel default(shared) + { +#pragma omp for + for (int r = 0; r < config->replications; r++) { +#ifdef USE_SVM + clEnqueueSVMMap(compute_queue[r](), CL_TRUE, + CL_MAP_READ | CL_MAP_WRITE, + reinterpret_cast(&data[r * (config->arraySize / config->replications)]), + sizeof(HOST_DATA_TYPE) * + (config->arraySize / config->replications), 0, + NULL, NULL); +#else + compute_queue[r].enqueueWriteBuffer(Buffer_data[r], CL_TRUE, 0, + sizeof(HOST_DATA_TYPE) * + (config->arraySize / config->replications), + &data[r * (config->arraySize / config->replications)]); +#endif + } +#pragma omp master + { + // Execute benchmark kernels + t1 = std::chrono::high_resolution_clock::now(); + } +#pragma omp barrier +#pragma omp for nowait + for (int r = 0; r < config->replications; r++) { + compute_queue[r].enqueueTask(accesskernel[r]); + } +#pragma omp for + for (int r = 0; r < config->replications; r++) { + compute_queue[r].finish(); + } +#pragma omp master + { + auto t2 = std::chrono::high_resolution_clock::now(); + std::chrono::duration timespan = + std::chrono::duration_cast> + (t2 - t1); + executionTimes.push_back(timespan.count()); + } + } + } + + /* --- Read back results from Device --- */ + for (int r=0; r < config->replications; r++) { +#ifdef USE_SVM + clEnqueueSVMUnmap(compute_queue[r](), + reinterpret_cast(&data[r * (config->arraySize / config->replications)]), 0, + NULL, NULL); +#else + compute_queue[r].enqueueReadBuffer(Buffer_data[r], CL_TRUE, 0, + sizeof(HOST_DATA_TYPE)*(config->arraySize / config->replications), &data[r * (config->arraySize / config->replications)]); +#endif + } + + std::shared_ptr results( + new ExecutionResults{executionTimes}); + return results; + } + +} // namespace bm_execution diff --git a/RandomAccess/src/host/execution_single_rnd.cpp b/RandomAccess/src/host/execution_single_rnd.cpp new file mode 100644 index 00000000..7802bfa8 --- /dev/null +++ b/RandomAccess/src/host/execution_single_rnd.cpp @@ -0,0 +1,195 @@ +/* +Copyright (c) 2019 Marius Meyer + +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 UPDATE_SPLIT +#define UPDATE_SPLIT 8 +#endif + +/* Related header files */ +#include "src/host/execution.h" + +/* C++ standard library headers */ +#include +#include +#include +#include + +/* External library headers */ +#include "CL/cl.hpp" +#if QUARTUS_MAJOR_VERSION > 18 +#include "CL/cl_ext_intelfpga.h" +#endif + +/* Project's headers */ +#include "src/host/fpga_setup.h" +#include "src/host/random_access_functionality.h" + +namespace bm_execution { + + /* + Implementation for the single_rnd kernel. + @copydoc bm_execution::calculate() + */ + std::shared_ptr + calculate(cl::Context context, cl::Device device, cl::Program program, + uint repetitions, uint replications, size_t dataSize, + bool useMemInterleaving) { + // int used to check for OpenCL errors + int err; + DATA_TYPE_UNSIGNED* random; + posix_memalign(reinterpret_cast(&random), 64, + sizeof(DATA_TYPE_UNSIGNED)*UPDATE_SPLIT); + + for (DATA_TYPE i=0; i < UPDATE_SPLIT; i++) { + random[i] = starts((4 * DATA_LENGTH) / UPDATE_SPLIT * i); + } + + std::vector compute_queue; + std::vector Buffer_data; + std::vector Buffer_random; + std::vector accesskernel; + std::vector data_sets; + + /* --- Prepare kernels --- */ + + for (int r=0; r < replications; r++) { + DATA_TYPE_UNSIGNED* data; + posix_memalign(reinterpret_cast(&data), 64, + sizeof(DATA_TYPE)*(dataSize / replications)); + data_sets.push_back(data); + + compute_queue.push_back(cl::CommandQueue(context, device)); + + // Select memory bank to place data replication + int channel = 0; + if (!useMemInterleaving) { + switch ((r % replications) + 1) { + case 1: channel = CL_CHANNEL_1_INTELFPGA; break; + case 2: channel = CL_CHANNEL_2_INTELFPGA; break; + case 3: channel = CL_CHANNEL_3_INTELFPGA; break; + case 4: channel = CL_CHANNEL_4_INTELFPGA; break; + case 5: channel = CL_CHANNEL_5_INTELFPGA; break; + case 6: channel = CL_CHANNEL_6_INTELFPGA; break; + case 7: channel = CL_CHANNEL_7_INTELFPGA; break; + } + } + + Buffer_data.push_back(cl::Buffer(context, channel | + CL_MEM_READ_WRITE, + sizeof(DATA_TYPE_UNSIGNED)*(dataSize / replications))); + Buffer_random.push_back(cl::Buffer(context, channel | + CL_MEM_WRITE_ONLY, + sizeof(DATA_TYPE_UNSIGNED) * UPDATE_SPLIT)); + accesskernel.push_back(cl::Kernel(program, + (RANDOM_ACCESS_KERNEL + std::to_string(r)).c_str() , + &err)); + ASSERT_CL(err); + + // prepare kernels + err = accesskernel[r].setArg(0, Buffer_data[r]); + ASSERT_CL(err); + err = accesskernel[r].setArg(1, Buffer_random[r]); + ASSERT_CL(err); + err = accesskernel[r].setArg(2, DATA_TYPE_UNSIGNED(dataSize)); + ASSERT_CL(err); + err = accesskernel[r].setArg(3, + DATA_TYPE_UNSIGNED(dataSize / replications)); + ASSERT_CL(err); + } + + /* --- Execute actual benchmark kernels --- */ + + double t; + std::vector executionTimes; + for (int i = 0; i < repetitions; i++) { + // prepare data and send them to device + for (DATA_TYPE_UNSIGNED r =0; r < replications; r++) { + for (DATA_TYPE_UNSIGNED j=0; + j < (dataSize / replications); j++) { + data_sets[r][j] = r*(dataSize / replications) + j; + } + } + for (int r=0; r < replications; r++) { + compute_queue[r].enqueueWriteBuffer(Buffer_data[r], CL_TRUE, 0, + sizeof(DATA_TYPE)*(dataSize / replications), data_sets[r]); + compute_queue[r].enqueueWriteBuffer(Buffer_random[r], CL_TRUE, + 0, sizeof(DATA_TYPE_UNSIGNED) * UPDATE_SPLIT, random); + } + + // Execute benchmark kernels + auto t1 = std::chrono::high_resolution_clock::now(); + for (int r=0; r < replications; r++) { + compute_queue[r].enqueueTask(accesskernel[r]); + } + for (int r=0; r < replications; r++) { + compute_queue[r].finish(); + } + auto t2 = std::chrono::high_resolution_clock::now(); + std::chrono::duration timespan = + std::chrono::duration_cast> + (t2 - t1); + executionTimes.push_back(timespan.count()); + } + + /* --- Read back results from Device --- */ + + for (int r=0; r < replications; r++) { + compute_queue[r].enqueueReadBuffer(Buffer_data[r], CL_TRUE, 0, + sizeof(DATA_TYPE)*(dataSize / replications), data_sets[r]); + } + DATA_TYPE_UNSIGNED* data; + posix_memalign(reinterpret_cast(&data), 64, + (sizeof(DATA_TYPE)*dataSize)); + for (size_t r =0; r < replications; r++) { + for (size_t j=0; j < (dataSize / replications); j++) { + data[r*(dataSize / replications) + j] = data_sets[r][j]; + } + free(reinterpret_cast(data_sets[r])); + } + + /* --- Check Results --- */ + + DATA_TYPE_UNSIGNED temp = 1; + for (DATA_TYPE_UNSIGNED i=0; i < 4L*dataSize; i++) { + DATA_TYPE v = 0; + if (((DATA_TYPE)temp) < 0) { + v = POLY; + } + temp = (temp << 1) ^ v; + data[temp & (dataSize - 1)] ^= temp; + } + + double errors = 0; + for (DATA_TYPE_UNSIGNED i=0; i< dataSize; i++) { + if (data[i] != i) { + errors++; + } + } + free(reinterpret_cast(data)); + free(reinterpret_cast(random)); + + std::shared_ptr results( + new ExecutionResults{executionTimes, + errors / dataSize}); + return results; + } + +} // namespace bm_execution diff --git a/RandomAccess/src/host/main.cpp b/RandomAccess/src/host/main.cpp new file mode 100644 index 00000000..184ed82f --- /dev/null +++ b/RandomAccess/src/host/main.cpp @@ -0,0 +1,59 @@ +// +// Created by Marius Meyer on 04.12.19. +// + +#include "random_access_functionality.hpp" +#include "execution.h" + +/** +The program entry point +*/ +int +main(int argc, char *argv[]) { + // Setup benchmark + std::shared_ptr programSettings = + parseProgramParameters(argc, argv); + fpga_setup::setupEnvironmentAndClocks(); + std::vector usedDevice = + fpga_setup::selectFPGADevice(programSettings->defaultPlatform, + programSettings->defaultDevice); + cl::Context context = cl::Context(usedDevice); + cl::Program program = fpga_setup::fpgaSetup(&context, usedDevice, + &programSettings->kernelFileName); + + printFinalConfiguration(programSettings, usedDevice[0]); + + std::shared_ptr config( + new bm_execution::ExecutionConfiguration { + context, usedDevice[0], program, + programSettings->numRepetitions, + programSettings->numReplications, + programSettings->dataSize + }); + + HOST_DATA_TYPE *data; +#ifdef USE_SVM + data = reinterpret_cast( + clSVMAlloc(context(), 0 , + programSettings->dataSize * sizeof(HOST_DATA_TYPE), 1024)); +#else + posix_memalign(reinterpret_cast(&data), 4096, programSettings->dataSize * sizeof(HOST_DATA_TYPE)); +#endif + + generateInputData(data, programSettings->dataSize); + + auto timing = bm_execution::calculate(config, data); + + double error = checkRandomAccessResults(data, programSettings->dataSize); + +#ifdef USE_SVM + clSVMFree(context(), reinterpret_cast(data)); +#else + free(data); +#endif + + printResults(timing, programSettings->dataSize, error); + + return error < 0.01 ? 0 : 1; +} + diff --git a/RandomAccess/src/host/random_access_functionality.cpp b/RandomAccess/src/host/random_access_functionality.cpp new file mode 100644 index 00000000..cef29139 --- /dev/null +++ b/RandomAccess/src/host/random_access_functionality.cpp @@ -0,0 +1,247 @@ +/* +Copyright (c) 2019 Marius Meyer + +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. +*/ + +/* Related header files */ +#include "random_access_functionality.hpp" + +/* C++ standard library headers */ +#include +#include +#include +#include +#include +#include + +/* External library headers */ +#include "CL/cl.hpp" +#include "cxxopts.hpp" + +/* Project's headers */ +#include "setup/fpga_setup.hpp" +#include "execution.h" + + +/** +Parses and returns program options using the cxxopts library. +Supports the following parameters: + - file name of the FPGA kernel file (-f,--file) + - number of repetitions (-n) + - number of kernel replications (-r) + - data size (-d) + - use memory interleaving +@see https://github.com/jarro2783/cxxopts + +@return program settings that are created from the given program arguments +*/ +std::shared_ptr +parseProgramParameters(int argc, char * argv[]) { + // Defining and parsing program options + cxxopts::Options options(argv[0], PROGRAM_DESCRIPTION); + options.add_options() + ("f,file", "Kernel file name", cxxopts::value()) + ("n", "Number of repetitions", + cxxopts::value()->default_value(std::to_string(DEFAULT_REPETITIONS))) + ("r", "Number of used kernel replications", + cxxopts::value()->default_value(std::to_string(NUM_KERNEL_REPLICATIONS))) + ("d,data", "Size of the used data array (Should be half of the "\ + "available global memory)", + cxxopts::value() + ->default_value(std::to_string(DEFAULT_ARRAY_LENGTH))) + ("device", "Index of the device that has to be used. If not given you "\ + "will be asked which device to use if there are multiple devices "\ + "available.", cxxopts::value()->default_value(std::to_string(-1))) + ("platform", "Index of the platform that has to be used. If not given "\ + "you will be asked which platform to use if there are multiple "\ + "platforms available.", + cxxopts::value()->default_value(std::to_string(-1))) + ("h,help", "Print this help"); + cxxopts::ParseResult result = options.parse(argc, argv); + + // Check parsed options and handle special cases + if (result.count("f") <= 0) { + // Path to the kernel file is mandatory - exit if not given! + std::cerr << "Kernel file must be given! Aborting" << std::endl; + std::cout << options.help() << std::endl; + exit(1); + } + if (result.count("h")) { + // Just print help when argument is given + std::cout << options.help() << std::endl; + exit(0); + } + + // Create program settings from program arguments + std::shared_ptr sharedSettings( + new ProgramSettings {result["n"].as(), result["r"].as(), + result["platform"].as(), + result["device"].as(), + result["d"].as(), + result["f"].as()}); + return sharedSettings; +} + +/** +Print the benchmark Results + +@param results The result struct provided by the calculation call +@param dataSize The size of the used data array + +*/ +void +printResults(std::shared_ptr results, + size_t dataSize, double error) { + std::cout << std::setw(ENTRY_SPACE) + << "best" << std::setw(ENTRY_SPACE) << "mean" + << std::setw(ENTRY_SPACE) << "GUOPS" + << std::setw(ENTRY_SPACE) << "error" << std::endl; + + // Calculate performance for kernel execution plus data transfer + double tmean = 0; + double tmin = std::numeric_limits::max(); + double gups = static_cast(4 * dataSize) / 1000000000; + for (double currentTime : results->times) { + tmean += currentTime; + if (currentTime < tmin) { + tmin = currentTime; + } + } + tmean = tmean / results->times.size(); + + std::cout << std::setw(ENTRY_SPACE) + << tmin << std::setw(ENTRY_SPACE) << tmean + << std::setw(ENTRY_SPACE) << gups / tmin + << std::setw(ENTRY_SPACE) << (100.0 * error) + << std::endl; +} + +/** + Generates the value of the random number after a desired number of updates + + @param n number of random number updates + + @return The random number after n number of updates + */ +HOST_DATA_TYPE +starts(HOST_DATA_TYPE_SIGNED n) { + HOST_DATA_TYPE m2[BIT_SIZE]; + + while (n < 0) { + n += PERIOD; + } + while (n > 0) { + n -= PERIOD; + } + + if (n == 0) { + return 1; + } + + HOST_DATA_TYPE temp = 1; + for (int i=0; i < BIT_SIZE; i++) { + m2[i] = temp; + for (int j=0; j < 2; j++) { + HOST_DATA_TYPE_SIGNED v = 0; + if (((HOST_DATA_TYPE_SIGNED) temp) < 0) { + v = POLY; + } + temp = (temp << 1) ^ v; + } + } + // DATA_TYPE i = BIT_SIZE - 2; + // while (i >= 0 && !((n >> i) & 1)) { + // i--; + // } + int i = 0; + for (i=BIT_SIZE - 2; i >= 0; i--) { + if ((n >> i) & 1) { + break; + } + } + + HOST_DATA_TYPE ran = 2; + while (i > 0) { + temp = 0; + for (int j=0; j < BIT_SIZE; j++) { + if ((ran >> j) & 1) { + temp ^= m2[j]; + } + } + ran = temp; + i--; + if ((n >> i) & 1) { + HOST_DATA_TYPE_SIGNED v = 0; + if (((HOST_DATA_TYPE_SIGNED) ran) < 0) { + v = POLY; + } + ran = (ran << 1) ^v; + } + } + return ran; +} + +double checkRandomAccessResults(HOST_DATA_TYPE* result_array, size_t array_size){ + HOST_DATA_TYPE temp = 1; + for (HOST_DATA_TYPE i=0; i < 4L*array_size; i++) { + HOST_DATA_TYPE_SIGNED v = 0; + if (((HOST_DATA_TYPE_SIGNED)temp) < 0) { + v = POLY; + } + temp = (temp << 1) ^ v; + result_array[(temp >> 3) & (array_size - 1)] ^= temp; + } + + double errors = 0; +#pragma omp parallel for reduction(+:errors) + for (HOST_DATA_TYPE i=0; i< array_size; i++) { + if (result_array[i] != i) { + errors++; + } + } + return errors / array_size; +} + +void printFinalConfiguration(const std::shared_ptr &programSettings, + const cl::Device &device) { + // Give setup summary + std::cout << PROGRAM_DESCRIPTION << std::endl << HLINE; + std::cout << "Summary:" << std::endl + << "Kernel Replications: " << programSettings->numReplications + << std::endl + << "Repetitions: " << programSettings->numRepetitions + << std::endl + << "Total data size: " << (programSettings->dataSize + * sizeof(HOST_DATA_TYPE)) * 1.0 + << " Byte" << std::endl + << "Kernel file: " << programSettings->kernelFileName + << std::endl; + std::cout << "Device: " + << device.getInfo() << std::endl; + std::cout << HLINE + << "Start benchmark using the given configuration." << std::endl + << HLINE; +} + +void generateInputData(HOST_DATA_TYPE* data, size_t dataSize) { + for (HOST_DATA_TYPE j=0; j < dataSize ; j++) { + data[j] = j; + } +} diff --git a/RandomAccess/src/host/random_access_functionality.hpp b/RandomAccess/src/host/random_access_functionality.hpp new file mode 100644 index 00000000..2328b450 --- /dev/null +++ b/RandomAccess/src/host/random_access_functionality.hpp @@ -0,0 +1,133 @@ +/* +Copyright (c) 2019 Marius Meyer + +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 SRC_HOST_RANDOM_ACCESS_FUNCTIONALITY_H_ +#define SRC_HOST_RANDOM_ACCESS_FUNCTIONALITY_H_ + +/* C++ standard library headers */ +#include + +/* Project's headers */ +#include "execution.h" +#include "setup/fpga_setup.hpp" +#include "parameters.h" + +/* +Short description of the program. +Moreover the version and build time is also compiled into the description. +*/ +#define STR_EXPAND(tok) #tok +#define STR(tok) STR_EXPAND(tok) + +#define PROGRAM_DESCRIPTION "Implementation of the random access benchmark"\ + " proposed in the HPCC benchmark suite for FPGA.\n"\ + "Version: " VERSION "\n" + +/** +Prefix of the function name of the used kernel. +It will be used to construct the full function name for the case of replications. +The full name will be +*/ +#define RANDOM_ACCESS_KERNEL "accessMemory_" + +/** +Constants used to verify benchmark results +*/ +#define POLY 7 +#define PERIOD 1317624576693539401L + +#define BIT_SIZE (sizeof(HOST_DATA_TYPE) * 8) + +#define ENTRY_SPACE 13 + +struct ProgramSettings { + uint numRepetitions; + uint numReplications; + int defaultPlatform; + int defaultDevice; + size_t dataSize; + std::string kernelFileName; +}; + + +/** +Parses and returns program options using the cxxopts library. +Supports the following parameters: + - file name of the FPGA kernel file (-f,--file) + - number of repetitions (-n) + - number of kernel replications (-r) + - data size (-d) + - use memory interleaving +@see https://github.com/jarro2783/cxxopts + +@return program settings that are created from the given program arguments +*/ +std::shared_ptr +parseProgramParameters(int argc, char * argv[]); + + +/** + Generates the value of the random number after a desired number of updates + + @param n number of random number updates + + @return The random number after n number of updates + */ +HOST_DATA_TYPE +starts(HOST_DATA_TYPE_SIGNED n); + +/** +Prints the execution results to stdout + +@param results The execution results +@param dataSize Size of the used data array. Needed to calculate GUOP/s from + timings +*/ +void printResults(std::shared_ptr results, + size_t dataSize, double error); + + +/** + * Checks the correctness of the updates by recalculating all updated addresses and apply the same update again. + * Since the update is a xor, the original values of the array can be recalculated with this method. + * If a value differs from the original value it means there was an error during the benchmark execution. + * 1% errors in the array are allowed. + * + * @param result_array The data array containing the data from the benchmark execution. It will be modified for the evaluation! + * @param array_size The size of the data array + */ +double checkRandomAccessResults(HOST_DATA_TYPE* result_array, size_t array_size); + +/** + * Prints the used configuration to std out before starting the actual benchmark. + * + * @param programSettings The program settings retrieved from the command line + * @param device The device used for execution + */ +void printFinalConfiguration(const std::shared_ptr &programSettings, + const cl::Device &device); + + + +void generateInputData(HOST_DATA_TYPE* data, size_t dataSize); + +#endif // SRC_HOST_RANDOM_ACCESS_FUNCTIONALITY_H_ diff --git a/RandomAccess/src/host/setup/fpga_setup.cpp b/RandomAccess/src/host/setup/fpga_setup.cpp new file mode 100644 index 00000000..07845901 --- /dev/null +++ b/RandomAccess/src/host/setup/fpga_setup.cpp @@ -0,0 +1,273 @@ +// +// Created by Marius Meyer on 04.12.19. +// + +#include "fpga_setup.hpp" + +#include +#include +#include +#include +#include +#include + +/* External libraries */ +#include "parameters.h" + +namespace fpga_setup { + + /** +Converts the reveived OpenCL error to a string + +@param err The OpenCL error code + +@return The string representation of the OpenCL error code +*/ + std::string + getCLErrorString(cl_int const err) { + switch (err) { + CL_ERR_TO_STR(CL_DEVICE_NOT_FOUND); + CL_ERR_TO_STR(CL_DEVICE_NOT_AVAILABLE); + CL_ERR_TO_STR(CL_COMPILER_NOT_AVAILABLE); + CL_ERR_TO_STR(CL_MEM_OBJECT_ALLOCATION_FAILURE); + CL_ERR_TO_STR(CL_OUT_OF_RESOURCES); + CL_ERR_TO_STR(CL_OUT_OF_HOST_MEMORY); + CL_ERR_TO_STR(CL_PROFILING_INFO_NOT_AVAILABLE); + CL_ERR_TO_STR(CL_MEM_COPY_OVERLAP); + CL_ERR_TO_STR(CL_IMAGE_FORMAT_MISMATCH); + CL_ERR_TO_STR(CL_IMAGE_FORMAT_NOT_SUPPORTED); + CL_ERR_TO_STR(CL_BUILD_PROGRAM_FAILURE); + CL_ERR_TO_STR(CL_MAP_FAILURE); + CL_ERR_TO_STR(CL_MISALIGNED_SUB_BUFFER_OFFSET); + CL_ERR_TO_STR(CL_EXEC_STATUS_ERROR_FOR_EVENTS_IN_WAIT_LIST); + CL_ERR_TO_STR(CL_KERNEL_ARG_INFO_NOT_AVAILABLE); + CL_ERR_TO_STR(CL_INVALID_VALUE); + CL_ERR_TO_STR(CL_INVALID_DEVICE_TYPE); + CL_ERR_TO_STR(CL_INVALID_PLATFORM); + CL_ERR_TO_STR(CL_INVALID_DEVICE); + CL_ERR_TO_STR(CL_INVALID_CONTEXT); + CL_ERR_TO_STR(CL_INVALID_QUEUE_PROPERTIES); + CL_ERR_TO_STR(CL_INVALID_COMMAND_QUEUE); + CL_ERR_TO_STR(CL_INVALID_HOST_PTR); + CL_ERR_TO_STR(CL_INVALID_MEM_OBJECT); + CL_ERR_TO_STR(CL_INVALID_IMAGE_FORMAT_DESCRIPTOR); + CL_ERR_TO_STR(CL_INVALID_IMAGE_SIZE); + CL_ERR_TO_STR(CL_INVALID_SAMPLER); + CL_ERR_TO_STR(CL_INVALID_BINARY); + CL_ERR_TO_STR(CL_INVALID_BUILD_OPTIONS); + CL_ERR_TO_STR(CL_INVALID_PROGRAM); + CL_ERR_TO_STR(CL_INVALID_PROGRAM_EXECUTABLE); + CL_ERR_TO_STR(CL_INVALID_KERNEL_NAME); + CL_ERR_TO_STR(CL_INVALID_KERNEL_DEFINITION); + CL_ERR_TO_STR(CL_INVALID_KERNEL); + CL_ERR_TO_STR(CL_INVALID_ARG_INDEX); + CL_ERR_TO_STR(CL_INVALID_ARG_VALUE); + CL_ERR_TO_STR(CL_INVALID_ARG_SIZE); + CL_ERR_TO_STR(CL_INVALID_KERNEL_ARGS); + CL_ERR_TO_STR(CL_INVALID_WORK_DIMENSION); + CL_ERR_TO_STR(CL_INVALID_WORK_GROUP_SIZE); + CL_ERR_TO_STR(CL_INVALID_WORK_ITEM_SIZE); + CL_ERR_TO_STR(CL_INVALID_GLOBAL_OFFSET); + CL_ERR_TO_STR(CL_INVALID_EVENT_WAIT_LIST); + CL_ERR_TO_STR(CL_INVALID_EVENT); + CL_ERR_TO_STR(CL_INVALID_OPERATION); + CL_ERR_TO_STR(CL_INVALID_GL_OBJECT); + CL_ERR_TO_STR(CL_INVALID_BUFFER_SIZE); + CL_ERR_TO_STR(CL_INVALID_MIP_LEVEL); + CL_ERR_TO_STR(CL_INVALID_GLOBAL_WORK_SIZE); + CL_ERR_TO_STR(CL_COMPILE_PROGRAM_FAILURE); + CL_ERR_TO_STR(CL_LINKER_NOT_AVAILABLE); + CL_ERR_TO_STR(CL_LINK_PROGRAM_FAILURE); + CL_ERR_TO_STR(CL_DEVICE_PARTITION_FAILED); + CL_ERR_TO_STR(CL_INVALID_PROPERTY); + CL_ERR_TO_STR(CL_INVALID_IMAGE_DESCRIPTOR); + CL_ERR_TO_STR(CL_INVALID_COMPILER_OPTIONS); + CL_ERR_TO_STR(CL_INVALID_LINKER_OPTIONS); + CL_ERR_TO_STR(CL_INVALID_DEVICE_PARTITION_COUNT); + + default: + return "UNKNOWN ERROR CODE"; + } + } + +/** +Check the OpenCL return code for errors. +If an error is detected, it will be printed and the programm execution is +stopped. + +@param err The OpenCL error code +*/ + void + handleClReturnCode(cl_int const err, std::string const file, + int const line) { + if (err != CL_SUCCESS) { + std::string err_string = getCLErrorString(err); + std::cerr << "ERROR in OpenCL library detected! Aborting." + << std::endl << file << ":" << line << ": " << err_string + << std::endl; + exit(err); + } + } + +/** +Sets up the given FPGA with the kernel in the provided file. + +@param context The context used for the program +@param program The devices used for the program +@param usedKernelFile The path to the kernel file +@return The program that is used to create the benchmark kernels +*/ + cl::Program + fpgaSetup(const cl::Context *context, std::vector deviceList, + const std::string *usedKernelFile) { + int err; + + std::cout << HLINE; + std::cout << "FPGA Setup:" << usedKernelFile->c_str() << std::endl; + + // Open file stream if possible + std::ifstream aocxStream(usedKernelFile->c_str(), std::ifstream::binary); + if (!aocxStream.is_open()) { + std::cerr << "Not possible to open from given file!" << std::endl; + } + + // Read in file contents and create program from binaries + std::string prog(std::istreambuf_iterator(aocxStream), + (std::istreambuf_iterator())); + aocxStream.seekg(0, aocxStream.end); + unsigned file_size = aocxStream.tellg(); + aocxStream.seekg(0, aocxStream.beg); + char *buf = new char[file_size]; + aocxStream.read(buf, file_size); + + cl::Program::Binaries mybinaries; + mybinaries.push_back({buf, file_size}); + + // Create the Program from the AOCX file. + cl::Program program(*context, deviceList, mybinaries, NULL, &err); + ASSERT_CL(err); + std::cout << "Prepared FPGA successfully for global Execution!" << + std::endl; + std::cout << HLINE; + return program; + } + +/** +Sets up the C++ environment by configuring std::cout and checking the clock +granularity using bm_helper::checktick() +*/ + void + setupEnvironmentAndClocks() { + std::cout << std::setprecision(5) << std::scientific; + + std::cout << HLINE; + std::cout << "General setup:" << std::endl; + + // Check clock granularity and output result + std::cout << "C++ high resolution clock is used." << std::endl; + std::cout << "The clock precision seems to be " + << static_cast + (std::chrono::high_resolution_clock::period::num) / + std::chrono::high_resolution_clock::period::den * 10e9 + << "ns" << std::endl; + + std::cout << HLINE; + } + + +/** +Searches an selects an FPGA device using the CL library functions. +If multiple platforms or devices are given, the user will be prompted to +choose a device. + +@param defaultPlatform The index of the platform that has to be used. If a + value < 0 is given, the platform can be chosen + interactively +@param defaultDevice The index of the device that has to be used. If a + value < 0 is given, the device can be chosen + interactively + +@return A list containing a single selected device +*/ + std::vector + selectFPGADevice(int defaultPlatform, int defaultDevice) { + // Integer used to store return codes of OpenCL library calls + int err; + + std::vector platformList; + err = cl::Platform::get(&platformList); + ASSERT_CL(err); + + // Choose the target platform + int chosenPlatformId = 0; + if (defaultPlatform >= 0) { + if (defaultPlatform < platformList.size()) { + chosenPlatformId = defaultPlatform; + } else { + std::cerr << "Default platform " << defaultPlatform + << " can not be used. Available platforms: " + << platformList.size() << std::endl; + exit(1); + } + } else if (platformList.size() > 1) { + std::cout << + "Multiple platforms have been found. Select the platform by"\ + " typing a number:" << std::endl; + for (int platformId = 0; + platformId < platformList.size(); platformId++) { + std::cout << platformId << ") " << + platformList[platformId].getInfo() << + std::endl; + } + std::cout << "Enter platform id [0-" << platformList.size() - 1 + << "]:"; + std::cin >> chosenPlatformId; + } + cl::Platform platform = platformList[chosenPlatformId]; + std::cout << "Selected Platform: " + << platform.getInfo() << std::endl; + + std::vector deviceList; + err = platform.getDevices(CL_DEVICE_TYPE_ACCELERATOR, &deviceList); + ASSERT_CL(err); + + // Choose taget device + int chosenDeviceId = 0; + if (defaultDevice >= 0) { + if (defaultDevice < deviceList.size()) { + chosenDeviceId = defaultDevice; + } else { + std::cerr << "Default platform " << defaultDevice + << " can not be used. Available platforms: " + << deviceList.size() << std::endl; + exit(1); + } + } else if (deviceList.size() > 1) { + std::cout << + "Multiple devices have been found. Select the platform by"\ + " typing a number:" << std::endl; + for (int deviceId = 0; + deviceId < deviceList.size(); deviceId++) { + std::cout << deviceId << ") " << + deviceList[deviceId].getInfo() << + std::endl; + } + std::cout << "Enter device id [0-" << deviceList.size() - 1 << "]:"; + std::cin >> chosenDeviceId; + } + std::vector chosenDeviceList; + chosenDeviceList.push_back(deviceList[chosenDeviceId]); + + // Give selection summary + std::cout << HLINE; + std::cout << "Selection summary:" << std::endl; + std::cout << "Platform Name: " << + platform.getInfo() << std::endl; + std::cout << "Device Name: " << + chosenDeviceList[0].getInfo() << std::endl; + std::cout << HLINE; + + return chosenDeviceList; + } + +} // namespace fpga_setup \ No newline at end of file diff --git a/RandomAccess/src/host/setup/fpga_setup.hpp b/RandomAccess/src/host/setup/fpga_setup.hpp new file mode 100644 index 00000000..c1f78b0c --- /dev/null +++ b/RandomAccess/src/host/setup/fpga_setup.hpp @@ -0,0 +1,108 @@ +/* +Copyright (c) 2019 Marius Meyer + +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 SRC_HOST_FPGA_SETUP_H_ +#define SRC_HOST_FPGA_SETUP_H_ + +#include +#include +#include +#include +#include +#include + +/* External libraries */ +#include "CL/cl.hpp" + +/** +Makro to convert the error integer representation to its string representation +Source: https://gist.github.com/allanmac/9328bb2d6a99b86883195f8f78fd1b93 +*/ +#define CL_ERR_TO_STR(err) case err: return #err + +namespace fpga_setup { + + /** +Converts the reveived OpenCL error to a string + +@param err The OpenCL error code + +@return The string representation of the OpenCL error code +*/ + std::string + getCLErrorString(cl_int const err); + +/** +Check the OpenCL return code for errors. +If an error is detected, it will be printed and the programm execution is +stopped. + +@param err The OpenCL error code +*/ + void + handleClReturnCode(cl_int const err, std::string const file, + int const line); + +/** +Makro that enables checks for OpenCL errors with handling of the file and +line number. +*/ +#define ASSERT_CL(err) fpga_setup::handleClReturnCode(err, __FILE__, __LINE__) + +/** +Sets up the given FPGA with the kernel in the provided file. + +@param context The context used for the program +@param program The devices used for the program +@param usedKernelFile The path to the kernel file +@return The program that is used to create the benchmark kernels +*/ + cl::Program + fpgaSetup(const cl::Context *context, std::vector deviceList, + const std::string *usedKernelFile); + +/** +Sets up the C++ environment by configuring std::cout and checking the clock +granularity using bm_helper::checktick() +*/ + void + setupEnvironmentAndClocks(); + + +/** +Searches an selects an FPGA device using the CL library functions. +If multiple platforms or devices are given, the user will be prompted to +choose a device. + +@param defaultPlatform The index of the platform that has to be used. If a + value < 0 is given, the platform can be chosen + interactively +@param defaultDevice The index of the device that has to be used. If a + value < 0 is given, the device can be chosen + interactively + +@return A list containing a single selected device +*/ + std::vector + selectFPGADevice(int defaultPlatform, int defaultDevice); + +} // namespace fpga_setup +#endif // SRC_HOST_FPGA_SETUP_H_ diff --git a/RandomAccess/tests/CMakeLists.txt b/RandomAccess/tests/CMakeLists.txt new file mode 100755 index 00000000..5b7e1b34 --- /dev/null +++ b/RandomAccess/tests/CMakeLists.txt @@ -0,0 +1,29 @@ + +# 'lib' is the folder with Google Test sources +add_subdirectory(../../extern/googletest ${CMAKE_CURRENT_BINARY_DIR}/lib) +include_directories(${gtest_SOURCE_DIR}/include ${gtest_SOURCE_DIR}) +include_directories(${CMAKE_BINARY_DIR}/src/common ../../extern/cxxopts/include) + + +set(PROJECT_SOURCES ../src/host/setup/fpga_setup.cpp ../src/host/execution_single.cpp ../src/host/random_access_functionality.cpp) +set(TEST_SOURCES test_fpga_setup.cpp test_host_code.cpp test_kernel_functionality_and_host_integration.cpp) + +if (INTELFPGAOPENCL_FOUND) + include_directories(SYSTEM ${IntelFPGAOpenCL_INCLUDE_DIRS}) + add_executable(Test_intel ${TEST_SOURCES} ${PROJECT_SOURCES}) + target_link_libraries(Test_intel gtest gmock gtest_main ${IntelFPGAOpenCL_LIBRARIES} "${OpenMP_CXX_FLAGS}") + add_dependencies(Test_intel random_access_kernels_single_emulate_intel) + target_compile_definitions(Test_intel PRIVATE -DINTEL_FPGA) + target_compile_options(Test_intel PRIVATE "${OpenMP_CXX_FLAGS}") + add_test(NAME test_intel_unit COMMAND $) +endif() + +if (Vitis_FOUND) + include_directories(SYSTEM ${Vitis_INCLUDE_DIRS}) + add_executable(Test_xilinx ${TEST_SOURCES} ${PROJECT_SOURCES}) + target_link_libraries(Test_xilinx gtest gmock gtest_main ${Vitis_LIBRARIES} "${OpenMP_CXX_FLAGS}") + add_dependencies(Test_xilinx random_access_kernels_single_emulate_xilinx) + target_compile_definitions(Test_xilinx PRIVATE -DXILINX_FPGA) + target_compile_options(Test_xilinx PRIVATE "${OpenMP_CXX_FLAGS}") + add_test(NAME test_xilinx_unit COMMAND $) +endif() \ No newline at end of file diff --git a/RandomAccess/tests/test_fpga_setup.cpp b/RandomAccess/tests/test_fpga_setup.cpp new file mode 100644 index 00000000..a3da9cee --- /dev/null +++ b/RandomAccess/tests/test_fpga_setup.cpp @@ -0,0 +1,38 @@ +// +// Created by Marius Meyer on 04.12.19 +// +#include "gtest/gtest.h" +#include "../src/host/setup/fpga_setup.hpp" +#include "parameters.h" + +/** + * Check if it is possible to find the platform and device that are given as default + */ +TEST (FPGASetup, FindValidPlatformAndDevice) { + EXPECT_EQ (1, fpga_setup::selectFPGADevice(DEFAULT_PLATFORM, DEFAULT_DEVICE).size()); +} + +/** + * Checks if non existing platform leads to an error + */ +TEST (FPGASetup, FindNonExistingPlatform) { + // Execute thread safe test because emulation leads to problems in google test detecting the exit of the routine + testing::FLAGS_gtest_death_test_style="threadsafe"; + // TODO regex does not work so for now its not tested! + EXPECT_EXIT(fpga_setup::selectFPGADevice(DEFAULT_PLATFORM + 100, DEFAULT_DEVICE), + ::testing::ExitedWithCode(1), + ::testing::MatchesRegex(".*")); +} + +/** + * Checks if non existing device leads to an error + */ +TEST (FPGASetup, FindNonExistingDevice) { + // Execute thread safe test because emulation leads to problems in google test detecting the exit of the routine + testing::FLAGS_gtest_death_test_style="threadsafe"; + // TODO regex does not work so for now its not tested! + EXPECT_EXIT(fpga_setup::selectFPGADevice(DEFAULT_PLATFORM, DEFAULT_DEVICE + 100), + ::testing::ExitedWithCode(1), + ::testing::MatchesRegex(".*")); +} + diff --git a/RandomAccess/tests/test_host_code.cpp b/RandomAccess/tests/test_host_code.cpp new file mode 100644 index 00000000..e7437bb9 --- /dev/null +++ b/RandomAccess/tests/test_host_code.cpp @@ -0,0 +1,31 @@ +// +// Created by Marius Meyer on 04.12.19 +// +#include "gtest/gtest.h" +#include "../src/host/random_access_functionality.hpp" +#include "parameters.h" + +/** + * Check if the correctness test gives correct results for correct array + */ +TEST (FPGASetup, ResultValidationWorksForCorrectUpdates) { + HOST_DATA_TYPE data[1024]; + generateInputData(data, 1024); + // do random accesses + checkRandomAccessResults(data, 1024); + // check correctness of random accesses + double error = checkRandomAccessResults(data, 1024); + ASSERT_FLOAT_EQ(error, 0.0); +} + + +/** + * Check if the correctness test gives correct results for not updated array + */ +TEST (FPGASetup, ResultValidationWorksForWrongUpdates) { + HOST_DATA_TYPE data[1024]; + generateInputData(data, 1024); + // check correctness of random accesses + double error = checkRandomAccessResults(data, 1024); + ASSERT_GT(error, 0.3); +} diff --git a/RandomAccess/tests/test_kernel_functionality_and_host_integration.cpp b/RandomAccess/tests/test_kernel_functionality_and_host_integration.cpp new file mode 100644 index 00000000..bc8932ce --- /dev/null +++ b/RandomAccess/tests/test_kernel_functionality_and_host_integration.cpp @@ -0,0 +1,88 @@ +// +// Created by Marius Meyer on 14.02.20. +// +#include "gtest/gtest.h" +#include "parameters.h" +#include "../src/host/execution.h" +#include "../src/host/setup/fpga_setup.hpp" +#include "../src/host/random_access_functionality.hpp" + + +struct OpenCLKernelTest : testing::Test { + HOST_DATA_TYPE *data; + std::shared_ptr config; + cl_uint array_size; + + OpenCLKernelTest() { + array_size = 128 * NUM_KERNEL_REPLICATIONS * BUFFER_SIZE; + posix_memalign(reinterpret_cast(&data), 4096, + sizeof(HOST_DATA_TYPE) * array_size); + } + + void setupFPGA(std::string kernelFileName) { + std::vector device = fpga_setup::selectFPGADevice(DEFAULT_PLATFORM, DEFAULT_DEVICE); + cl::Context context(device[0]); + cl::Program program = fpga_setup::fpgaSetup(&context, device, &kernelFileName); + config = std::make_shared( + bm_execution::ExecutionConfiguration{ + context, device[0], program, + 1, + NUM_KERNEL_REPLICATIONS, + array_size + }); + generateInputData(data, array_size); + } + + ~OpenCLKernelTest() override { + free(data); + } +}; + +struct DifferentOpenCLKernelTest : OpenCLKernelTest, testing::WithParamInterface { + DifferentOpenCLKernelTest() { + auto params = GetParam(); + auto kernel_file = params; + setupFPGA(kernel_file); + } +}; + +/** + * Check if the number of measurements from the calculation matches the number of repetitions + */ +TEST_P(DifferentOpenCLKernelTest, FPGACorrectNumberOfMeasurements1Rep) { + + auto result = bm_execution::calculate(config, data); + EXPECT_EQ(result->times.size(), 1); +} + +/** + * Check if the number of measurements from the calculation matches the number of repetitions + */ +TEST_P(DifferentOpenCLKernelTest, FPGACorrectNumberOfMeasurements3Rep) { + config->repetitions = 3; + auto result = bm_execution::calculate(config, data); + EXPECT_EQ(result->times.size(), 3); +} + +/** + * Execution returns correct results for a single repetition + */ +TEST_P(DifferentOpenCLKernelTest, FPGAErrorBelow1Percent) { + + auto result = bm_execution::calculate(config, data); + double errors = checkRandomAccessResults(data, array_size); + EXPECT_LT(errors, 0.01); +} + + +#ifdef INTEL_FPGA +INSTANTIATE_TEST_CASE_P(Default, DifferentOpenCLKernelTest, + testing::Values("random_access_kernels_single_emulate.aocx") +); +#endif + +#ifdef XILINX_FPGA +INSTANTIATE_TEST_CASE_P(Default, DifferentOpenCLKernelTest, + testing::Values("random_access_kernels_single_emulate.xclbin") +); +#endif \ No newline at end of file diff --git a/STREAM/.gitignore b/STREAM/.gitignore new file mode 100644 index 00000000..b79be468 --- /dev/null +++ b/STREAM/.gitignore @@ -0,0 +1,5 @@ +cmake-* +.DS_Store +build-* +.idea +*.pdf diff --git a/STREAM/CHANGELOG b/STREAM/CHANGELOG new file mode 100644 index 00000000..34008765 --- /dev/null +++ b/STREAM/CHANGELOG @@ -0,0 +1,45 @@ +# Changelog + +This file contains all changes made to the source code for each release. + +## 2.1.4.1 + +#### Added: +- Support for SVM in host code + +## 2.1.4 + +#### Modified: +- Reduce BRAM usage of kernels by combining two local memory buffers to one. This allows larger sizes of the local memory buffer with the same resurce usage. + + +## 2.1.3 + +#### Modified: +- Optimize kernels for Xilinx toolchain +- Remove volatile keyword of kernel parameters to enable bursts on Xilinx boards +- Change buffer placement on memory banks for Intel FPGAs + +## 2.1.2 + +#### Added: +- Optimize build system for Xilinx boards + +## 2.1.1 + +#### Added: +- Add support for kernel replication for Intel devices and host code +- Add single kernel with operation switch + +## 2.1 + +#### Added: +- Add basic support for Xilinx boards + +#### Modified: +- Change from CMake 2.8 to CMake 3.1 as build system + +## 2.0 + +#### Added: +- Replace Makefile with CMake as a build system diff --git a/STREAM/CMakeLists.txt b/STREAM/CMakeLists.txt new file mode 100755 index 00000000..d093b738 --- /dev/null +++ b/STREAM/CMakeLists.txt @@ -0,0 +1,59 @@ +cmake_minimum_required(VERSION 3.1) +project(STREAM VERSION 2.1.4.1 + DESCRIPTION "OpenCL STREAM Benchmark for FPGA") + +set (CMAKE_CXX_STANDARD 11) + +if(CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME) + include(CTest) +endif() + +set(DEFAULT_REPETITIONS 10 CACHE STRING "Default number of repetitions") +set(DEFAULT_ARRAY_LENGTH 134217728 CACHE STRING "Default size of the data arrays") +set(DATA_TYPE float CACHE STRING "Data type used for host and device") +set(VECTOR_COUNT 16 CACHE STRING "If greater than 1 will use OpenCL vector types in the kernels. e.g. float2") +set(DEFAULT_DEVICE -1 CACHE STRING "Index of the default device to use") +set(DEFAULT_PLATFORM -1 CACHE STRING "Index of the default platform to use") +set(GLOBAL_MEM_UNROLL 1 CACHE STRING "Unrolling factor that is used for all loops in the kernels") +set(FPGA_BOARD_NAME p520_hpc_sg280l CACHE STRING "Name of the target FPGA board") +set(NUM_REPLICATIONS 4 CACHE STRING "Number of times the kernels will be replicated") +set(DEVICE_BUFFER_SIZE 512 CACHE STRING "Buffer size in number of values that is used within the single kernel implementation.") +set(INNER_LOOP_BUFFERS ON CACHE BOOL "Put the local memory buffers inside the outer loop in the kernel code") +set(USE_SVM No CACHE BOOL "Use SVM pointers instead of creating buffers on the board and transferring the data there before execution.") +set(XILINX_GENERATE_LINK_SETTINGS Yes CACHE BOOL "Generate the link settings depending on the number of replications for the kernels") +set(XILINX_LINK_SETTINGS_FILE "${CMAKE_SOURCE_DIR}/settings/settings.link.xilinx.stream_kernels.ddr.ini" CACHE STRING "The link settings file that should be used when generating is disabled") +set(XILINX_COMPILE_FLAGS "-j 40" CACHE STRING "Additional compile flags for the v++ compiler") +set(XILINX_COMPILE_SETTINGS "${CMAKE_SOURCE_DIR}/settings/settings.compile.xilinx.ini" CACHE STRING "Compile settings file for the v++ compiler") +separate_arguments(XILINX_COMPILE_FLAGS) + +set(HOST_DATA_TYPE cl_${DATA_TYPE}) +if (VECTOR_COUNT GREATER 1) + set(DEVICE_DATA_TYPE ${DATA_TYPE}${VECTOR_COUNT}) +else() + set(DEVICE_DATA_TYPE ${DATA_TYPE}) +endif() + +set(AOC_FLAGS "-fpc -fp-relaxed -no-interleaving=default" CACHE STRING "Used flags for the AOC compiler") +separate_arguments(AOC_FLAGS) + +set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_SOURCE_DIR}/../extern/hlslib/cmake) +set(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin) + +configure_file( + "${CMAKE_SOURCE_DIR}/src/common/parameters.h.in" + "${CMAKE_BINARY_DIR}/src/common/parameters.h" +) +include_directories(${CMAKE_BINARY_DIR}/src/common) + +find_package(OpenMP) +find_package(IntelFPGAOpenCL) +find_package(Vitis) + +add_subdirectory(src/device) +add_subdirectory(src/host) + +if(CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME AND BUILD_TESTING) + add_subdirectory(tests) +endif() + + diff --git a/STREAM/LICENSE b/STREAM/LICENSE new file mode 100644 index 00000000..fc47bf1c --- /dev/null +++ b/STREAM/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020 pc2 + +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/STREAM/README.md b/STREAM/README.md new file mode 100644 index 00000000..3f3a2906 --- /dev/null +++ b/STREAM/README.md @@ -0,0 +1,134 @@ +# STREAM for FPGA + +This repository contains the STREAM benchmark for FPGA and its OpenCL kernels. +This version adds basic support for the Xilinx Vitis toolcchain. + +The implementation is based on the STREAM benchmark 5.10 by John D. McCaplin, Ph.D. +available at [https://www.cs.virginia.edu/stream/](https://www.cs.virginia.edu/stream/). + +## Build + +CMake is used as the build system. +The targets below can be used to build the benchmark and its kernels: + + | Target | Description | + | --------------------- | ---------------------------------------------- | + | STREAM_FPGA_`VENDOR` | Builds the host application linking with the Intel SDK| + | Test_`VENDOR` | Compile the tests and its dependencies linking with the Intel SDK | + + More over there are additional targets to generate kernel reports and bitstreams. + The provided kernel is optimized for the Bittware 520N board with four external + channels. + The code might need to be modified for other boards since the external channel descriptor + string might be different. + Also the number of channels is hardcoded and has to be modified for other boards. + The kernel targets are: + + | Target | Description | + | ------------------------------ | ---------------------------------------------- | + | stream_kernels_`VENDOR` | Synthesizes the kernel (takes several hours!) | + | stream_kernels_report_intel | Create an HTML report for the kernel | + | stream_kernels_emulate_`VENDOR` | Create a n emulation kernel | + | stream_kernels_single_`VENDOR` | Synthesizes the kernel (takes several hours!) | + | stream_kernels_single_report_intel | Create an HTML report for the kernel | + | stream_kernels_single_emulate_`VENDOR` | Create a n emulation kernel | + +For the host code as well as the kernels `VENDOR` can be `intel` or `xilinx`. +The report target for xilinx is missing but reports will be generated when the kernel is synthesized. +The `stream_kernels_single` targets build a single kernel for all four vector operations. +In that case the operation will be defined by an additional operation type input parameter. + + You can build for example the host application by running + + mkdir build && cd build + cmake .. + make STREAM_FPGA_intel + +You will find all executables and kernel files in the `bin` +folder of your build directory. +You should always specify a target with make to reduce the build time! +You might want to specify predefined parameters before build: + +Name | Default | Description | +---------------- |-------------|--------------------------------------| +`DEFAULT_DEVICE` | -1 | Index of the default device (-1 = ask) | +`DEFAULT_PLATFORM`| -1 | Index of the default platform (-1 = ask) | +`DEFAULT_REPETITIONS`| 10 | Number of times the kernel will be executed | +`FPGA_BOARD_NAME`| p520_hpc_sg280l | Name of the target board | +`DATA_TYPE` | float | Data type used for host and device code | +`VECTOR_COUNT` | 16 | If >1 OpenCL vector types of the given size are used in the device code | +`DEFAULT_ARRAY_LENGTH`| 134217728 | Length of each input array | +`GLOBAL_MEM_UNROLL`| 1 | Loop unrolling factor for all loops in the device code | +`AOC_FLAGS`| `-fpc -fp-relaxed -no-interleaving=default` | Additional Intel AOC compiler flags that are used for kernel compilation | +`NUM_REPLICATIONS`| 1 | Replicates the kernels the given number of times | +`DEVICE_BUFFER_SIZE`| 16384 | Number of values that are stored in the local memory in the single kernel approach | + +Moreover the environment variable `INTELFPGAOCLSDKROOT` has to be set to the root +of the Intel FPGA SDK installation. + +Additionally it is possible to set the used compiler and other build tools +in the `CMakeCache.txt` located in the build directory after running cmake. + +## Execution + +For execution of the benchmark run: + + ./STREAM_FPGA_intel -f path_to_kernel.aocx + +For more information on available input parameters run + + ./STREAM_FPGA_intel -h + +To execute the unit and integration tests for Intel devices run + + CL_CONTEXT_EMULATOR_DEVICE=1 ./Test_intel + +in the `bin` folder within the build directory. +It will run an emulation of the kernel and execute some functionality tests. + +## Result interpretation + +The output of the host application is similar to the original STREAM benchmark: + + Function Best Rate MB/s Avg time Min time Max time + Copy: 30875.9 0.025914 0.025910 0.025919 + Scale: 30885.6 0.025905 0.025902 0.025911 + Add: 46289.2 0.025928 0.025924 0.025935 + Triad: 45613.4 0.026310 0.026308 0.026312 + PCI Write: 6324.0 0.189800 0.189753 0.189862 + PCI Read: 5587.3 0.214869 0.214773 0.214943 + +In addition it also measures the bandwidth of the connection between host and +device. It is distinguished between writing to and reading from the devices +memory. +The buffers are written to the device before every iteration and read back +after each iteration. + +## Exemplary Results + +The benchmark was executed on Bittware 520N cards for different Intel® Quartus® Prime versions. +The detailed results of the runs are given in [results.txt](results.txt) and as +CSV files in the subfolder `csv_result_export`. + +#### Single Precision + +![Single precision results](csv_result_export/sp_plot.jpeg) + +#### Double Precision + +![Double precision results](csv_result_export/dp_plot.jpeg) + +#### Usage of the Global Ring + +It is possible to force a ring interconnect for the global memory with the compiler command +`-global-ring`. To test the impact of this type of interconnect, the benchmark was also +synthesized with the additional parameters `-global-ring -duplicate-ring` for all SDK versions +supporting this option. + +The raw data of these runs can be found in the folder `csv_result_export`. + +##### Single Precision +![Single precision results](csv_result_export/sp_global_ring_plot.jpeg) + +##### Double Precision +![Double precision results](csv_result_export/dp_global_ring_plot.jpeg) \ No newline at end of file diff --git a/STREAM/csv_result_export/README.md b/STREAM/csv_result_export/README.md new file mode 100644 index 00000000..998d3f99 --- /dev/null +++ b/STREAM/csv_result_export/README.md @@ -0,0 +1,28 @@ +# Plot and Data Scripts + +The Python scripts in this folder can be used to parse the output of the STREAM benchmark +to the CSV format and plot the data. + +### Dependencies + +To run the scripts, Python 3 has to be installed. +Also the following Python packages need to be installed: + +- Numpy +- Matplotlib +- Pandas + +They can be installed using the `requirements.txt` file in this folder with: + + pip install -r requirements.txt + +Both scripts can retrieve the data from files, folder or over the standard input. +For further information run the scripts with the help option. + +Example use: + + ./parse_data.py -i input.txt | create_plots.py + +This will load the output of the benchmark execution from a file `input.txt` and parse it +to CSV. The CSV format will be piped to the next script that will use it to generate the plots. + diff --git a/STREAM/csv_result_export/create_plots.py b/STREAM/csv_result_export/create_plots.py new file mode 100755 index 00000000..c3c5236e --- /dev/null +++ b/STREAM/csv_result_export/create_plots.py @@ -0,0 +1,62 @@ +#!/usr/bin/env python3 +from io import StringIO +import pandas as pd +import argparse +import sys +import numpy as np +import matplotlib.pyplot as plt + +beautify_map = { + "Copy": "Copy", + "Scale": "Scale", + "Add": "Add", + "Triad": "Triad", + "PCIRead": "PCIe read", + "PCIWrite": "PCIe write", + "fmax": "max. Frequency" +} + + +def plot_polar(data_content): + df = pd.read_csv(StringIO(data_content),index_col=0).T + cols = [r for r in df.columns] + used_bw_rows = [r for r in df.index if "PCI" not in r and "rate" in r and "total_rate" not in r] + rotation = (np.pi / 4) + + # create polar plots + fig1 = plt.figure(figsize=(6,3)) + ax = fig1.add_subplot(121,projection='polar') + ax.set_xticklabels([beautify_map[r.split('_')[0]] for r in used_bw_rows]) + ax.set_xticks(np.array(range(len(used_bw_rows))) * (2 * np.pi) / len(used_bw_rows) + rotation) + for col in cols: + xv = np.array(list(range(len(used_bw_rows))) + [0]) * (2 * np.pi) / len(used_bw_rows) + rotation + yv = list(df[col].T[used_bw_rows].values / 1000) + [df[col].T[used_bw_rows[0]] / 1000] + col_label = col + ax.plot(xv, yv, label=col_label, zorder=10) + ax.grid(linestyle="dotted") + ax.set_ylabel("GB/s") + box = ax.get_position() + ax.set_position([box.x0*0.4, box.y0*0.2, box.width * 1.3, box.height*1.3]) + ax.legend(loc='upper right', bbox_to_anchor=(2, 1), + ncol=1, fancybox=True) + fig1.savefig("kernel_bw_polar.pdf") + + +if __name__ == "__main__": + parser = argparse.ArgumentParser(description="Create Plots from CSV files") + parser.add_argument('-i', dest="input_path", + help="Path to CSV file that should be used to create plots", + default="-") + args = parser.parse_args() + # Select, if stdin and stdout or files should be used + if args.input_path == "-": + file_content = "" + isopen = True + while isopen: + t = sys.stdin.read() + if t == "": + isopen = False + file_content += t + plot_polar(file_content) + else: + plot_polar(args.input_path) diff --git a/STREAM/csv_result_export/dp_global_ring_plot.jpeg b/STREAM/csv_result_export/dp_global_ring_plot.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..4479ef4c74ff365d9c49dddbb2b0f0c28eb5fc30 GIT binary patch literal 210389 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⋘mCZsa^0goZIP;|Q7g=| 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 E(p@I>Opz~RX9T2=ej=+(e=19F0 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 z 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�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

    +2pjdTH>>RH|-?q{+U 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 literal 0 HcmV?d00001 diff --git a/STREAM/csv_result_export/parse_data.py b/STREAM/csv_result_export/parse_data.py new file mode 100755 index 00000000..5739a1f6 --- /dev/null +++ b/STREAM/csv_result_export/parse_data.py @@ -0,0 +1,162 @@ +#!/usr/bin/env python +import pandas as pd +import os +from os import path +import re +import logging +import argparse +import sys + +""" +This script will parse output of the STREAM benchmark for FPGA from stdout or from a file or multiple files +and return the data as a CSV + +""" + + + +""" +Some constants used for file parsing + +""" + +STREAM_REGEX = ("{name}:\\s+(?P<{field}_rate>\\d+\\.\\d+(e[\\+|-]d+)?)\\s+(?P<{field}_avg_time>\\d+\\.\\d+(e[\\+|-]d+)?)" + "\\s+(?P<{field}_min_time>\\d+\\.\\d+(e[\\+|-]d+)?)\\s+(?P<{field}_max_time>\\d+\\.\\d+(e[\\+|-]d+)?)") +FREQUENCY_REGEX = "fMax=(?P\\d+\\.\\d+)" + +STREAM_METRICS = ["Copy","Scale","Add","Triad","PCI Write","PCI Read"] + +regex_list = [STREAM_REGEX.format(name=m, field=''.join(m.split(' '))) for m in STREAM_METRICS] + [FREQUENCY_REGEX] + +""" +Start of the function definition + +""" + + +def parse(path_to_source, recursive=False): + """ + Main function that should be called when data has to be parsed. + Will parse the file content of one or more files and return it as a DataFrame + + :param path_to_source: Path to the file or folder that should be parsed + :param recursive: If True, will parse files in a folder recursively + :return: A Dataframe containing the data parsed from all files in the folder (and subfolders) + """ + df = pd.DataFrame() + if path.isfile(path_to_source): + s_result = parse_file(path_to_source) + df = df.append(s_result) + elif path.isdir(path_to_source): + df = parse_dir(path_to_source, recursive) + else: + logging.error("Not a file or folder. Aborting!") + sys.exit(1) + return df + + +""" +Start of the definition of helper functions + +""" + + +def parse_dir(folder_name, recursive=False): + """ + Parse all files in the folder and subfolders, if wanted. + + :param folder_name: Path to the folder that should be used to find output files + :param recursive: Will search recursively if true + :return: A Dataframe containing the data parsed from all files in the folder (and subfolders) + """ + df = pd.DataFrame() + folder_content = [path.join(folder_name,f) for f in os.listdir(folder_name)] + for fname in [f for f in folder_content if path.isfile(f) and f.endswith(".txt")]: + s_result = parse_file(fname) + df = df.append(s_result) + if recursive: + for fname in [f for f in folder_content if path.isdir(f)]: + df_result = parse_dir(fname, recursive) + df = df.append(df_result) + return df + + +def parse_file(fname): + """ + Open a file and pass the content to parse_content function + + :param fname: Path to the file + :return: A Dataframe containing the data parsed from the file content + """ + with open(fname) as f: + return parse_content(f.read(), fname) + + +def parse_content(file_content, file_path): + """ + Parse a string in to a DataFrame. Will use the given file path for logging + + :param file_content: Content of the file + :param file_path: Path to the file + :return: A Dataframe containing the data parsed from the file content + """ + res = [list(re.finditer(r, file_content)) for r in regex_list] + result_dicts = [] + for i,r in enumerate(res): + if len(r) == 0: + logging.warning("Some results could not be parsed with regex '%s' from %s" % (regex_list[i],file_path)) + res.remove(r) + if any([len(res[0]) != len(r) for r in res]): + logging.error("Different number of matches! Aborting! %s" % file_path) + sys.exit(1) + data_name = path.basename(file_path).split('.')[0] + sdk_version = ".".join(data_name.split('_')[0].split("-")) + no_interleaving = (data_name.split('_')[-1] != "ni") + bsp_version = ".".join(data_name.split('_')[1].split("-")) + device_name = " ".join(data_name.split('_')[2].split("-")) + for i, x in enumerate(res[0]): + result_dict = {"SDK": sdk_version, "no_interleaving": no_interleaving, + "Device" : device_name, "BSP": bsp_version} + for y in res: + d = y[i].groupdict() + for k in d.keys(): + result_dict[k] = float(d[k]) + result_dicts.append(result_dict) + + if len(result_dicts) > 1: + index_names = ["%s_%d" % (data_name, i) for i in range(len(result_dicts))] + else: + index_names = [data_name] + return pd.DataFrame(result_dicts, index=index_names) + + +""" +Main code that will be executed when this script is directly called + +""" + + +if __name__ == "__main__": + parser = argparse.ArgumentParser(description="Parse plain text STREAM benchmark outputs to CSV") + parser.add_argument('-i', dest="input_path", + help="Path to a text file containing the output of the STREAM benchmark or to a folder containing multiple output files", + default="-") + parser.add_argument('-o', dest='output_file', help="Name of the output file", default="-") + parser.add_argument('-r', dest='recursive', action='store_true', help="Recursively parse files in folders", default=False) + args = parser.parse_args() + # Select, if stdin and stdout or files should be used + if args.input_path == "-": + file_content = "" + isopen = True + while isopen: + t = sys.stdin.read() + if t == "": + isopen = False + file_content += t + df = parse_content(file_content, "stdout") + else: + df = parse(args.input_path, args.recursive) + if args.output_file == "-": + df.to_csv(sys.stdout, header=True) + else: + df.to_csv(args.output_file, header=True) diff --git a/STREAM/csv_result_export/raw_results/16-0-2_16_0_2_Intel-BDW+FPGA-hybrid-CPU+FPGA-Arria-10-GX1150.txt b/STREAM/csv_result_export/raw_results/16-0-2_16_0_2_Intel-BDW+FPGA-hybrid-CPU+FPGA-Arria-10-GX1150.txt new file mode 100644 index 00000000..4707dea1 --- /dev/null +++ b/STREAM/csv_result_export/raw_results/16-0-2_16_0_2_Intel-BDW+FPGA-hybrid-CPU+FPGA-Arria-10-GX1150.txt @@ -0,0 +1,51 @@ +------------------------------------------------------------- +STREAM FPGA based in STREAM version $Revision: 5.10 $ +------------------------------------------------------------- +This system uses 4 bytes per array element. +------------------------------------------------------------- +Array size = 100000000 (elements), Offset = 0 (elements) +Memory per array = 381.5 MiB (= 0.4 GiB). +Total memory required = 1144.4 MiB (= 1.1 GiB). +Each kernel will be executed 10 times. + The *best* time for each kernel (excluding the first iteration) + will be used to compute the reported bandwidth. +------------------------------------------------------------- +Platform Name: Altera SDK for OpenCL +Device Name: bdw_fpga_v1.0 : BDW FPGA OpenCL BSP +Using kernel given as argument +Kernel: bin/stream_kernels_sp.aocx +Reprogramming device with handle 1 +------------------------------------------------------------- +Prepared FPGA successfully! +------------------------------------------------------------- +Your clock granularity/precision appears to be 1 microseconds. +Each test below will take on the order of 51820 microseconds. + (= 51820 clock ticks) +Increase the size of the arrays if this shows that +you are not getting at least 20 clock ticks per test. +------------------------------------------------------------- +WARNING -- The above is only a rough guideline. +For best results, please be sure you know the +precision of your system timer. +------------------------------------------------------------- +Execute iteration 1 of 10 +Execute iteration 2 of 10 +Execute iteration 3 of 10 +Execute iteration 4 of 10 +Execute iteration 5 of 10 +Execute iteration 6 of 10 +Execute iteration 7 of 10 +Execute iteration 8 of 10 +Execute iteration 9 of 10 +Execute iteration 10 of 10 +fMax=252.9 +Function Best Rate MB/s Avg time Min time Max time +Copy: 15416.1 0.051917 0.051894 0.051965 +Scale: 15674.0 0.051056 0.051040 0.051066 +Add: 20816.4 0.057661 0.057647 0.057676 +Triad: 20543.0 0.058422 0.058414 0.058438 +PCI Write: 74017129.4 0.000031 0.000016 0.000045 +PCI Read: 75121862.7 0.000028 0.000016 0.000041 +------------------------------------------------------------- +Solution Validates: avg error less than 1.000000e-06 on all three arrays +------------------------------------------------------------- \ No newline at end of file diff --git a/STREAM/csv_result_export/raw_results/17-1-2_17-1-2_Bittware-385A-(Intel-Arria-10-GX1150)-Buffer-Reorder_ni.txt b/STREAM/csv_result_export/raw_results/17-1-2_17-1-2_Bittware-385A-(Intel-Arria-10-GX1150)-Buffer-Reorder_ni.txt new file mode 100644 index 00000000..ebd4b621 --- /dev/null +++ b/STREAM/csv_result_export/raw_results/17-1-2_17-1-2_Bittware-385A-(Intel-Arria-10-GX1150)-Buffer-Reorder_ni.txt @@ -0,0 +1,52 @@ +------------------------------------------------------------- +STREAM FPGA based in STREAM version $Revision: 5.10 $ +------------------------------------------------------------- +This system uses 4 bytes per array element. +------------------------------------------------------------- +Array size = 100000000 (elements), Offset = 0 (elements) +Memory per array = 381.5 MiB (= 0.4 GiB). +Total memory required = 1144.4 MiB (= 1.1 GiB). +Each kernel will be executed 10 times. + The *best* time for each kernel (excluding the first iteration) + will be used to compute the reported bandwidth. +------------------------------------------------------------- +Platform Name: Intel(R) FPGA SDK for OpenCL(TM) +Device Name: p385a_sch_ax115 : nalla_pcie (aclnalla_pcie0) +Using kernel given as argument +Kernel: bin_old/stream_kernels_no_interleaving_no_interleaving.aocx +------------------------------------------------------------- +Reprogramming device [0] with handle 1 +0 +Prepared FPGA successfully! +------------------------------------------------------------- +Your clock granularity/precision appears to be 1 microseconds. +Each test below will take on the order of 51517 microseconds. + (= 51517 clock ticks) +Increase the size of the arrays if this shows that +you are not getting at least 20 clock ticks per test. +------------------------------------------------------------- +WARNING -- The above is only a rough guideline. +For best results, please be sure you know the +precision of your system timer. +------------------------------------------------------------- +Execute iteration 1 of 10 +Execute iteration 2 of 10 +Execute iteration 3 of 10 +Execute iteration 4 of 10 +Execute iteration 5 of 10 +Execute iteration 6 of 10 +Execute iteration 7 of 10 +Execute iteration 8 of 10 +Execute iteration 9 of 10 +Execute iteration 10 of 10 +fMax=342.93 +Function Best Rate MB/s Avg time Min time Max time +Copy: 32582.8 0.024561 0.024553 0.024564 +Scale: 32502.0 0.024647 0.024614 0.024665 +Add: 24522.2 0.049019 0.048935 0.049042 +Triad: 23350.0 0.051433 0.051392 0.051493 +PCI Write: 6424.2 0.187405 0.186795 0.187862 +PCI Read: 6396.6 0.191436 0.187601 0.193465 +------------------------------------------------------------- +Solution Validates: avg error less than 1.000000e-06 on all three arrays +------------------------------------------------------------- \ No newline at end of file diff --git a/STREAM/csv_result_export/raw_results/17-1-2_17-1-2_Bittware-385A-(Intel-Arria-10-GX1150).txt b/STREAM/csv_result_export/raw_results/17-1-2_17-1-2_Bittware-385A-(Intel-Arria-10-GX1150).txt new file mode 100644 index 00000000..3de66a37 --- /dev/null +++ b/STREAM/csv_result_export/raw_results/17-1-2_17-1-2_Bittware-385A-(Intel-Arria-10-GX1150).txt @@ -0,0 +1,51 @@ +------------------------------------------------------------- +STREAM FPGA based in STREAM version $Revision: 5.10 $ +------------------------------------------------------------- +This system uses 4 bytes per array element. +------------------------------------------------------------- +Array size = 100000000 (elements), Offset = 0 (elements) +Memory per array = 381.5 MiB (= 0.4 GiB). +Total memory required = 1144.4 MiB (= 1.1 GiB). +Each kernel will be executed 10 times. + The *best* time for each kernel (excluding the first iteration) + will be used to compute the reported bandwidth. +------------------------------------------------------------- +ERROR: XILINX_XRT must be set +Platform Name: Intel(R) FPGA SDK for OpenCL(TM) +Device Name: p385a_sch_ax115 : nalla_pcie (aclnalla_pcie0) +Kernel: stream_kernels_p385a_sch_ax115_17.1.aocx +------------------------------------------------------------- +Reprogramming device [0] with handle 1 +Prepared FPGA successfully! +------------------------------------------------------------- +Your clock granularity/precision appears to be 1 microseconds. +Each test below will take on the order of 29942 microseconds. + (= 29942 clock ticks) +Increase the size of the arrays if this shows that +you are not getting at least 20 clock ticks per test. +------------------------------------------------------------- +WARNING -- The above is only a rough guideline. +For best results, please be sure you know the +precision of your system timer. +------------------------------------------------------------- +Execute iteration 1 of 10 +Execute iteration 2 of 10 +Execute iteration 3 of 10 +Execute iteration 4 of 10 +Execute iteration 5 of 10 +Execute iteration 6 of 10 +Execute iteration 7 of 10 +Execute iteration 8 of 10 +Execute iteration 9 of 10 +Execute iteration 10 of 10 +fMax=341.06 +Function Best Rate MB/s Avg time Min time Max time +Copy: 26767.5 0.029892 0.029887 0.029901 +Scale: 27112.3 0.029562 0.029507 0.029594 +Add: 28839.9 0.041659 0.041609 0.041704 +Triad: 28848.8 0.041656 0.041596 0.041725 +PCI Write: 6419.2 0.187463 0.186940 0.187959 +PCI Read: 6356.5 0.192998 0.188783 0.196114 +------------------------------------------------------------- +Solution Validates: avg error less than 1.000000e-06 on all three arrays +------------------------------------------------------------- \ No newline at end of file diff --git a/STREAM/csv_result_export/raw_results/17-1-2_17-1-2_Bittware-385A-(Intel-Arria-10-GX1150)_ni.txt b/STREAM/csv_result_export/raw_results/17-1-2_17-1-2_Bittware-385A-(Intel-Arria-10-GX1150)_ni.txt new file mode 100644 index 00000000..c02f9b57 --- /dev/null +++ b/STREAM/csv_result_export/raw_results/17-1-2_17-1-2_Bittware-385A-(Intel-Arria-10-GX1150)_ni.txt @@ -0,0 +1,52 @@ +------------------------------------------------------------- +STREAM FPGA based in STREAM version $Revision: 5.10 $ +------------------------------------------------------------- +This system uses 4 bytes per array element. +------------------------------------------------------------- +Array size = 100000000 (elements), Offset = 0 (elements) +Memory per array = 381.5 MiB (= 0.4 GiB). +Total memory required = 1144.4 MiB (= 1.1 GiB). +Each kernel will be executed 10 times. + The *best* time for each kernel (excluding the first iteration) + will be used to compute the reported bandwidth. +------------------------------------------------------------- +ERROR: XILINX_XRT must be set +Platform Name: Intel(R) FPGA SDK for OpenCL(TM) +Device Name: p385a_sch_ax115 : nalla_pcie (aclnalla_pcie0) +Using kernel given as argument +Kernel: stream_kernels_no_interleaving_no_interleaving.aocx +------------------------------------------------------------- +Reprogramming device [0] with handle 1 +Prepared FPGA successfully! +------------------------------------------------------------- +Your clock granularity/precision appears to be 1 microseconds. +Each test below will take on the order of 51541 microseconds. + (= 51541 clock ticks) +Increase the size of the arrays if this shows that +you are not getting at least 20 clock ticks per test. +------------------------------------------------------------- +WARNING -- The above is only a rough guideline. +For best results, please be sure you know the +precision of your system timer. +------------------------------------------------------------- +Execute iteration 1 of 10 +Execute iteration 2 of 10 +Execute iteration 3 of 10 +Execute iteration 4 of 10 +Execute iteration 5 of 10 +Execute iteration 6 of 10 +Execute iteration 7 of 10 +Execute iteration 8 of 10 +Execute iteration 9 of 10 +Execute iteration 10 of 10 +fMax=342.93 +Function Best Rate MB/s Avg time Min time Max time +Copy: 15553.6 0.051469 0.051435 0.051553 +Scale: 32581.2 0.024617 0.024554 0.024661 +Add: 23444.7 0.051247 0.051184 0.051280 +Triad: 23370.0 0.051414 0.051348 0.051455 +PCI Write: 6415.0 0.187305 0.187060 0.187613 +PCI Read: 6478.7 0.189044 0.185221 0.193868 +------------------------------------------------------------- +Solution Validates: avg error less than 1.000000e-06 on all three arrays +------------------------------------------------------------- \ No newline at end of file diff --git a/STREAM/csv_result_export/raw_results/18-0-1_18-0-1_Bittware-520N-(Intel-Stratix-10-GX2800).txt b/STREAM/csv_result_export/raw_results/18-0-1_18-0-1_Bittware-520N-(Intel-Stratix-10-GX2800).txt new file mode 100644 index 00000000..3a9300d5 --- /dev/null +++ b/STREAM/csv_result_export/raw_results/18-0-1_18-0-1_Bittware-520N-(Intel-Stratix-10-GX2800).txt @@ -0,0 +1,52 @@ +------------------------------------------------------------- +STREAM FPGA based in STREAM version $Revision: 5.10 $ +------------------------------------------------------------- +This system uses 4 bytes per array element. +------------------------------------------------------------- +Array size = 100000000 (elements), Offset = 0 (elements) +Memory per array = 381.5 MiB (= 0.4 GiB). +Total memory required = 1144.4 MiB (= 1.1 GiB). +Each kernel will be executed 10 times. + The *best* time for each kernel (excluding the first iteration) + will be used to compute the reported bandwidth. +------------------------------------------------------------- +Platform Name: Intel(R) FPGA SDK for OpenCL(TM) +Device Name: p520_max_sg280l : nalla_pcie (aclnalla_pcie0) +Using kernel given as argument +Kernel: bin/stream_kernels_18.0.1.aocx +------------------------------------------------------------- +MMD INFO : Disabling SmartVID (fix) polling +MMD INFO : Enabling SmartVID (fix) polling +Prepared FPGA successfully! +------------------------------------------------------------- +Your clock granularity/precision appears to be 1 microseconds. +Each test below will take on the order of 22789 microseconds. + (= 22789 clock ticks) +Increase the size of the arrays if this shows that +you are not getting at least 20 clock ticks per test. +------------------------------------------------------------- +WARNING -- The above is only a rough guideline. +For best results, please be sure you know the +precision of your system timer. +------------------------------------------------------------- +Execute iteration 1 of 10 +Execute iteration 2 of 10 +Execute iteration 3 of 10 +Execute iteration 4 of 10 +Execute iteration 5 of 10 +Execute iteration 6 of 10 +Execute iteration 7 of 10 +Execute iteration 8 of 10 +Execute iteration 9 of 10 +Execute iteration 10 of 10 +fMax=276.77 +Function Best Rate MB/s Avg time Min time Max time +Copy: 35130.7 0.022776 0.022772 0.022782 +Scale: 35138.5 0.022771 0.022767 0.022776 +Add: 52705.5 0.022772 0.022768 0.022777 +Triad: 48802.2 0.024593 0.024589 0.024598 +PCI Write: 6303.5 0.190460 0.190369 0.190521 +PCI Read: 3673.8 0.326716 0.326636 0.326799 +------------------------------------------------------------- +Solution Validates: avg error less than 1.000000e-06 on all three arrays +------------------------------------------------------------- \ No newline at end of file diff --git a/STREAM/csv_result_export/raw_results/18-0-1_18-0-1_Bittware-520N-(Intel-Stratix-10-GX2800)_ni.txt b/STREAM/csv_result_export/raw_results/18-0-1_18-0-1_Bittware-520N-(Intel-Stratix-10-GX2800)_ni.txt new file mode 100644 index 00000000..218ec61e --- /dev/null +++ b/STREAM/csv_result_export/raw_results/18-0-1_18-0-1_Bittware-520N-(Intel-Stratix-10-GX2800)_ni.txt @@ -0,0 +1,55 @@ +------------------------------------------------------------- +STREAM FPGA version $Revision: 0.10 $ +------------------------------------------------------------- +This system uses 4 bytes per array element. +------------------------------------------------------------- +Array size = 100000000 (elements), Offset = 0 (elements) +Memory per array = 381.5 MiB (= 0.4 GiB). +Total memory required = 1144.4 MiB (= 1.1 GiB). +Each kernel will be executed 10 times. + The *best* time for each kernel (excluding the first iteration) + will be used to compute the reported bandwidth. +------------------------------------------------------------- +Your clock granularity/precision appears to be 1 microseconds. +Each test below will take on the order of 241681 microseconds. + (= 241681 clock ticks) +Increase the size of the arrays if this shows that +you are not getting at least 20 clock ticks per test. +------------------------------------------------------------- +WARNING -- The above is only a rough guideline. +For best results, please be sure you know the +precision of your system timer. +------------------------------------------------------------- +Platform Name: Intel(R) FPGA SDK for OpenCL(TM) +Device Name: p520_max_sg280l : nalla_pcie (aclnalla_pcie0) +Using kernel given as argument +Kernel: stream_kernels_18.0.1_no_interleaving_no_interleaving.aocx +Reprogramming device [0] with handle 1 +------------------------------------------------------------- +MMD INFO : Disabling SmartVID (fix) polling +MMD INFO : Enabling SmartVID (fix) polling +0 +0 +0 +0 +Execute iteration 1 of 10 +Execute iteration 2 of 10 +Execute iteration 3 of 10 +Execute iteration 4 of 10 +Execute iteration 5 of 10 +Execute iteration 6 of 10 +Execute iteration 7 of 10 +Execute iteration 8 of 10 +Execute iteration 9 of 10 +Execute iteration 10 of 10 +fMax=372.16 +Function Best Rate MB/s Avg time Min time Max time +Copy: 31992.3 0.025015 0.025006 0.025025 +Scale: 32020.6 0.024990 0.024984 0.024997 +Add: 48009.4 0.025003 0.024995 0.025012 +Triad: 48005.8 0.025003 0.024997 0.025007 +PCI Write: 6303.9 0.190423 0.190359 0.190566 +PCI Read: 3771.6 0.318213 0.318168 0.318284 +------------------------------------------------------------- +Solution Validates: avg error less than 1.000000e-06 on all three arrays +------------------------------------------------------------- \ No newline at end of file diff --git a/STREAM/csv_result_export/raw_results/18-1-1_18-1-1_Bittware-520N-(Intel-Stratix-10-GX2800).txt b/STREAM/csv_result_export/raw_results/18-1-1_18-1-1_Bittware-520N-(Intel-Stratix-10-GX2800).txt new file mode 100644 index 00000000..a11323e7 --- /dev/null +++ b/STREAM/csv_result_export/raw_results/18-1-1_18-1-1_Bittware-520N-(Intel-Stratix-10-GX2800).txt @@ -0,0 +1,52 @@ +------------------------------------------------------------- +STREAM FPGA based in STREAM version $Revision: 5.10 $ +------------------------------------------------------------- +This system uses 4 bytes per array element. +------------------------------------------------------------- +Array size = 100000000 (elements), Offset = 0 (elements) +Memory per array = 381.5 MiB (= 0.4 GiB). +Total memory required = 1144.4 MiB (= 1.1 GiB). +Each kernel will be executed 10 times. + The *best* time for each kernel (excluding the first iteration) + will be used to compute the reported bandwidth. +------------------------------------------------------------- +Platform Name: Intel(R) FPGA SDK for OpenCL(TM) +Device Name: p520_hpc_sg280l : nalla_pcie (aclnalla_pcie0) +Using kernel given as argument +Kernel: bin/stream_kernels_18.1.1_hpc.aocx +------------------------------------------------------------- +MMD INFO : Disabling SmartVID (fix) polling +MMD INFO : Enabling SmartVID (fix) polling +Prepared FPGA successfully! +------------------------------------------------------------- +Your clock granularity/precision appears to be 1 microseconds. +Each test below will take on the order of 25948 microseconds. + (= 25948 clock ticks) +Increase the size of the arrays if this shows that +you are not getting at least 20 clock ticks per test. +------------------------------------------------------------- +WARNING -- The above is only a rough guideline. +For best results, please be sure you know the +precision of your system timer. +------------------------------------------------------------- +Execute iteration 1 of 10 +Execute iteration 2 of 10 +Execute iteration 3 of 10 +Execute iteration 4 of 10 +Execute iteration 5 of 10 +Execute iteration 6 of 10 +Execute iteration 7 of 10 +Execute iteration 8 of 10 +Execute iteration 9 of 10 +Execute iteration 10 of 10 +fMax=242.18 +Function Best Rate MB/s Avg time Min time Max time +Copy: 30875.9 0.025914 0.025910 0.025919 +Scale: 30885.6 0.025905 0.025902 0.025911 +Add: 46289.2 0.025928 0.025924 0.025935 +Triad: 45613.4 0.026310 0.026308 0.026312 +PCI Write: 6324.0 0.189800 0.189753 0.189862 +PCI Read: 5587.3 0.214869 0.214773 0.214943 +------------------------------------------------------------- +Solution Validates: avg error less than 1.000000e-06 on all three arrays +------------------------------------------------------------- \ No newline at end of file diff --git a/STREAM/csv_result_export/raw_results/18-1-1_18-1-1_Bittware-520N-(Intel-Stratix-10-GX2800)_ni.txt b/STREAM/csv_result_export/raw_results/18-1-1_18-1-1_Bittware-520N-(Intel-Stratix-10-GX2800)_ni.txt new file mode 100644 index 00000000..d8109640 --- /dev/null +++ b/STREAM/csv_result_export/raw_results/18-1-1_18-1-1_Bittware-520N-(Intel-Stratix-10-GX2800)_ni.txt @@ -0,0 +1,54 @@ +------------------------------------------------------------- +STREAM FPGA version $Revision: 0.10 $ +------------------------------------------------------------- +This system uses 4 bytes per array element. +------------------------------------------------------------- +Array size = 100000000 (elements), Offset = 0 (elements) +Memory per array = 381.5 MiB (= 0.4 GiB). +Total memory required = 1144.4 MiB (= 1.1 GiB). +Each kernel will be executed 10 times. + The *best* time for each kernel (excluding the first iteration) + will be used to compute the reported bandwidth. +------------------------------------------------------------- +Your clock granularity/precision appears to be 1 microseconds. +Each test below will take on the order of 242321 microseconds. + (= 242321 clock ticks) +Increase the size of the arrays if this shows that +you are not getting at least 20 clock ticks per test. +------------------------------------------------------------- +WARNING -- The above is only a rough guideline. +For best results, please be sure you know the +precision of your system timer. +------------------------------------------------------------- +Platform Name: Intel(R) FPGA SDK for OpenCL(TM) +Device Name: p520_hpc_sg280l : nalla_pcie (aclnalla_pcie0) +Using kernel given as argument +Kernel: stream_kernels_18.1.1_hpc_no_interleaving.aocx +------------------------------------------------------------- +MMD INFO : Disabling SmartVID (fix) polling +MMD INFO : Enabling SmartVID (fix) polling +0 +0 +0 +0 +Execute iteration 1 of 10 +Execute iteration 2 of 10 +Execute iteration 3 of 10 +Execute iteration 4 of 10 +Execute iteration 5 of 10 +Execute iteration 6 of 10 +Execute iteration 7 of 10 +Execute iteration 8 of 10 +Execute iteration 9 of 10 +Execute iteration 10 of 10 +fMax=328.125 +Function Best Rate MB/s Avg time Min time Max time +Copy: 31767.5 0.025188 0.025183 0.025196 +Scale: 31777.7 0.025177 0.025175 0.025181 +Add: 47672.0 0.025174 0.025172 0.025177 +Triad: 47559.0 0.025236 0.025232 0.025246 +PCI Write: 6316.0 0.190029 0.189994 0.190060 +PCI Read: 5728.0 0.209528 0.209497 0.209626 +------------------------------------------------------------- +Solution Validates: avg error less than 1.000000e-06 on all three arrays +------------------------------------------------------------- \ No newline at end of file diff --git a/STREAM/csv_result_export/raw_results/19-1_19-1_Bittware-520N-(Intel-Stratix-10-GX2800).txt b/STREAM/csv_result_export/raw_results/19-1_19-1_Bittware-520N-(Intel-Stratix-10-GX2800).txt new file mode 100644 index 00000000..b954a818 --- /dev/null +++ b/STREAM/csv_result_export/raw_results/19-1_19-1_Bittware-520N-(Intel-Stratix-10-GX2800).txt @@ -0,0 +1,52 @@ +------------------------------------------------------------- +STREAM FPGA based in STREAM version $Revision: 5.10 $ +------------------------------------------------------------- +This system uses 4 bytes per array element. +------------------------------------------------------------- +Array size = 100000000 (elements), Offset = 0 (elements) +Memory per array = 381.5 MiB (= 0.4 GiB). +Total memory required = 1144.4 MiB (= 1.1 GiB). +Each kernel will be executed 10 times. + The *best* time for each kernel (excluding the first iteration) + will be used to compute the reported bandwidth. +------------------------------------------------------------- +Platform Name: Intel(R) FPGA SDK for OpenCL(TM) +Device Name: p520_max_sg280l : BittWare Stratix 10 OpenCL platform (aclbitt_s10_pcie0) +Using kernel given as argument +Kernel: bin/stream_kernels_19.1.aocx +------------------------------------------------------------- +MMD INFO : Disabling SmartVID (fix) polling +MMD INFO : Enabling SmartVID (fix) polling +Prepared FPGA successfully! +------------------------------------------------------------- +Your clock granularity/precision appears to be 1 microseconds. +Each test below will take on the order of 24574 microseconds. + (= 24574 clock ticks) +Increase the size of the arrays if this shows that +you are not getting at least 20 clock ticks per test. +------------------------------------------------------------- +WARNING -- The above is only a rough guideline. +For best results, please be sure you know the +precision of your system timer. +------------------------------------------------------------- +Execute iteration 1 of 10 +Execute iteration 2 of 10 +Execute iteration 3 of 10 +Execute iteration 4 of 10 +Execute iteration 5 of 10 +Execute iteration 6 of 10 +Execute iteration 7 of 10 +Execute iteration 8 of 10 +Execute iteration 9 of 10 +Execute iteration 10 of 10 +fMax=255.68 +Function Best Rate MB/s Avg time Min time Max time +Copy: 32597.4 0.024545 0.024542 0.024548 +Scale: 32602.8 0.024539 0.024538 0.024541 +Add: 48572.8 0.024708 0.024705 0.024711 +Triad: 47423.6 0.025308 0.025304 0.025313 +PCI Write: 6330.3 0.189646 0.189563 0.189759 +PCI Read: 6315.9 0.190041 0.189998 0.190126 +------------------------------------------------------------- +Solution Validates: avg error less than 1.000000e-06 on all three arrays +------------------------------------------------------------- \ No newline at end of file diff --git a/STREAM/csv_result_export/raw_results/19-1_19-1_Bittware-520N-(Intel-Stratix-10-GX2800)_ni.txt b/STREAM/csv_result_export/raw_results/19-1_19-1_Bittware-520N-(Intel-Stratix-10-GX2800)_ni.txt new file mode 100644 index 00000000..5583a9d7 --- /dev/null +++ b/STREAM/csv_result_export/raw_results/19-1_19-1_Bittware-520N-(Intel-Stratix-10-GX2800)_ni.txt @@ -0,0 +1,52 @@ +------------------------------------------------------------- +STREAM FPGA based in STREAM version $Revision: 5.10 $ +------------------------------------------------------------- +This system uses 4 bytes per array element. +------------------------------------------------------------- +Array size = 100000000 (elements), Offset = 0 (elements) +Memory per array = 381.5 MiB (= 0.4 GiB). +Total memory required = 1144.4 MiB (= 1.1 GiB). +Each kernel will be executed 10 times. + The *best* time for each kernel (excluding the first iteration) + will be used to compute the reported bandwidth. +------------------------------------------------------------- +Platform Name: Intel(R) FPGA SDK for OpenCL(TM) +Device Name: p520_max_sg280l : BittWare Stratix 10 OpenCL platform (aclbitt_s10_pcie0) +Using kernel given as argument +Kernel: bin/stream_kernels_19.1_no_interleaving.aocx +------------------------------------------------------------- +MMD INFO : Disabling SmartVID (fix) polling +MMD INFO : Enabling SmartVID (fix) polling +Prepared FPGA successfully! +------------------------------------------------------------- +Your clock granularity/precision appears to be 1 microseconds. +Each test below will take on the order of 46746 microseconds. + (= 46746 clock ticks) +Increase the size of the arrays if this shows that +you are not getting at least 20 clock ticks per test. +------------------------------------------------------------- +WARNING -- The above is only a rough guideline. +For best results, please be sure you know the +precision of your system timer. +------------------------------------------------------------- +Execute iteration 1 of 10 +Execute iteration 2 of 10 +Execute iteration 3 of 10 +Execute iteration 4 of 10 +Execute iteration 5 of 10 +Execute iteration 6 of 10 +Execute iteration 7 of 10 +Execute iteration 8 of 10 +Execute iteration 9 of 10 +Execute iteration 10 of 10 +fMax=342.93 +Function Best Rate MB/s Avg time Min time Max time +Copy: 32642.4 0.024513 0.024508 0.024525 +Scale: 32659.9 0.024499 0.024495 0.024501 +Add: 48979.8 0.024502 0.024500 0.024504 +Triad: 48973.6 0.024504 0.024503 0.024506 +PCI Write: 6318.7 0.189960 0.189912 0.190026 +PCI Read: 6413.0 0.187184 0.187119 0.187271 +------------------------------------------------------------- +Solution Validates: avg error less than 1.000000e-06 on all three arrays +------------------------------------------------------------- \ No newline at end of file diff --git a/STREAM/csv_result_export/raw_results/19-2_19-2_Bittware-520N-(Intel-Stratix-10-GX2800).txt b/STREAM/csv_result_export/raw_results/19-2_19-2_Bittware-520N-(Intel-Stratix-10-GX2800).txt new file mode 100644 index 00000000..54750e41 --- /dev/null +++ b/STREAM/csv_result_export/raw_results/19-2_19-2_Bittware-520N-(Intel-Stratix-10-GX2800).txt @@ -0,0 +1,53 @@ +------------------------------------------------------------- +STREAM FPGA based in STREAM version $Revision: 5.10 $ +------------------------------------------------------------- +This system uses 4 bytes per array element. +------------------------------------------------------------- +Array size = 100000000 (elements), Offset = 0 (elements) +Memory per array = 381.5 MiB (= 0.4 GiB). +Total memory required = 1144.4 MiB (= 1.1 GiB). +Total transmitted Bytes: 800000000.0 +Each kernel will be executed 10 times. + The *best* time for each kernel (excluding the first iteration) + will be used to compute the reported bandwidth. +------------------------------------------------------------- +Platform Name: Intel(R) FPGA SDK for OpenCL(TM) +Device Name: p520_hpc_sg280l : BittWare Stratix 10 OpenCL platform (aclbitt_s10_pcie0) +Using kernel given as argument +Kernel: ../bin/stream_kernels_19.2_sp.aocx +------------------------------------------------------------- +MMD INFO : Disabling SmartVID (fix) polling +MMD INFO : Enabling SmartVID (fix) polling +Prepared FPGA successfully! +------------------------------------------------------------- +Your clock granularity/precision appears to be 1 microseconds. +Each test below will take on the order of 23329 microseconds. + (= 23329 clock ticks) +Increase the size of the arrays if this shows that +you are not getting at least 20 clock ticks per test. +------------------------------------------------------------- +WARNING -- The above is only a rough guideline. +For best results, please be sure you know the +precision of your system timer. +------------------------------------------------------------- +Execute iteration 1 of 10 +Execute iteration 2 of 10 +Execute iteration 3 of 10 +Execute iteration 4 of 10 +Execute iteration 5 of 10 +Execute iteration 6 of 10 +Execute iteration 7 of 10 +Execute iteration 8 of 10 +Execute iteration 9 of 10 +Execute iteration 10 of 10 +fMax=269.68 +Function Best Rate MB/s Avg time Min time Max time +Copy: 34349.6 0.023293 0.023290 0.023298 +Scale: 34358.1 0.023286 0.023284 0.023288 +Add: 50335.7 0.023842 0.023840 0.023843 +Triad: 48650.3 0.024668 0.024666 0.024671 +PCI Write: 6310.6 0.190267 0.190155 0.190397 +PCI Read: 6306.2 0.190343 0.190289 0.190388 +------------------------------------------------------------- +Solution Validates: avg error less than 1.000000e-06 on all three arrays +------------------------------------------------------------- \ No newline at end of file diff --git a/STREAM/csv_result_export/raw_results/19-2_19-2_Bittware-520N-(Intel-Stratix-10-GX2800)_ni.txt b/STREAM/csv_result_export/raw_results/19-2_19-2_Bittware-520N-(Intel-Stratix-10-GX2800)_ni.txt new file mode 100644 index 00000000..89655e08 --- /dev/null +++ b/STREAM/csv_result_export/raw_results/19-2_19-2_Bittware-520N-(Intel-Stratix-10-GX2800)_ni.txt @@ -0,0 +1,53 @@ +------------------------------------------------------------- +STREAM FPGA based in STREAM version $Revision: 5.10 $ +------------------------------------------------------------- +This system uses 4 bytes per array element. +------------------------------------------------------------- +Array size = 100000000 (elements), Offset = 0 (elements) +Memory per array = 381.5 MiB (= 0.4 GiB). +Total memory required = 1144.4 MiB (= 1.1 GiB). +Total transmitted Bytes: 800000000.0 +Each kernel will be executed 10 times. + The *best* time for each kernel (excluding the first iteration) + will be used to compute the reported bandwidth. +------------------------------------------------------------- +Platform Name: Intel(R) FPGA SDK for OpenCL(TM) +Device Name: p520_hpc_sg280l : BittWare Stratix 10 OpenCL platform (aclbitt_s10_pcie0) +Using kernel given as argument +Kernel: bin/stream_kernels_19.2_sp_no_interleaving.aocx +------------------------------------------------------------- +MMD INFO : Disabling SmartVID (fix) polling +MMD INFO : Enabling SmartVID (fix) polling +Prepared FPGA successfully! +------------------------------------------------------------- +Your clock granularity/precision appears to be 1 microseconds. +Each test below will take on the order of 45063 microseconds. + (= 45063 clock ticks) +Increase the size of the arrays if this shows that +you are not getting at least 20 clock ticks per test. +------------------------------------------------------------- +WARNING -- The above is only a rough guideline. +For best results, please be sure you know the +precision of your system timer. +------------------------------------------------------------- +Execute iteration 1 of 10 +Execute iteration 2 of 10 +Execute iteration 3 of 10 +Execute iteration 4 of 10 +Execute iteration 5 of 10 +Execute iteration 6 of 10 +Execute iteration 7 of 10 +Execute iteration 8 of 10 +Execute iteration 9 of 10 +Execute iteration 10 of 10 +fMax=378.64 +Function Best Rate MB/s Avg time Min time Max time +Copy: 35187.9 0.022747 0.022735 0.022761 +Scale: 35172.7 0.022754 0.022745 0.022769 +Add: 52800.6 0.022739 0.022727 0.022749 +Triad: 52763.5 0.022753 0.022743 0.022765 +PCI Write: 6321.3 0.189897 0.189834 0.189961 +PCI Read: 6405.2 0.187370 0.187347 0.187391 +------------------------------------------------------------- +Solution Validates: avg error less than 1.000000e-06 on all three arrays +------------------------------------------------------------- \ No newline at end of file diff --git a/STREAM/csv_result_export/raw_results/19-3_18-1-2_Intel-FPGA-PAC-D5005-(Intel-Stratix-10-SX).txt b/STREAM/csv_result_export/raw_results/19-3_18-1-2_Intel-FPGA-PAC-D5005-(Intel-Stratix-10-SX).txt new file mode 100644 index 00000000..a3f65e04 --- /dev/null +++ b/STREAM/csv_result_export/raw_results/19-3_18-1-2_Intel-FPGA-PAC-D5005-(Intel-Stratix-10-SX).txt @@ -0,0 +1,50 @@ +------------------------------------------------------------- +STREAM FPGA based in STREAM version $Revision: 5.10 $ +------------------------------------------------------------- +This system uses 4 bytes per array element. +------------------------------------------------------------- +Array size = 100000000 (elements), Offset = 0 (elements) +Memory per array = 381.5 MiB (= 0.4 GiB). +Total memory required = 1144.4 MiB (= 1.1 GiB). +Each kernel will be executed 10 times. + The *best* time for each kernel (excluding the first iteration) + will be used to compute the reported bandwidth. +------------------------------------------------------------- +Platform Name: Intel(R) FPGA SDK for OpenCL(TM) +Device Name: pac_s10_dc : Intel PAC Platform (pac_e600000) +Using kernel given as argument +Kernel: bin/stream_kernels_sp_pac.aocx +------------------------------------------------------------- +Prepared FPGA successfully! +------------------------------------------------------------- +Your clock granularity/precision appears to be 1 microseconds. +Each test below will take on the order of 25766 microseconds. + (= 25766 clock ticks) +Increase the size of the arrays if this shows that +you are not getting at least 20 clock ticks per test. +------------------------------------------------------------- +WARNING -- The above is only a rough guideline. +For best results, please be sure you know the +precision of your system timer. +------------------------------------------------------------- +Execute iteration 1 of 10 +Execute iteration 2 of 10 +Execute iteration 3 of 10 +Execute iteration 4 of 10 +Execute iteration 5 of 10 +Execute iteration 6 of 10 +Execute iteration 7 of 10 +Execute iteration 8 of 10 +Execute iteration 9 of 10 +Execute iteration 10 of 10 +fMax=244.37 +Function Best Rate MB/s Avg time Min time Max time +Copy: 31109.0 0.025743 0.025716 0.025781 +Scale: 31144.2 0.025715 0.025687 0.025756 +Add: 46705.4 0.025717 0.025693 0.025775 +Triad: 46696.3 0.025720 0.025698 0.025758 +PCI Write: 8863.4 0.135605 0.135388 0.135980 +PCI Read: 7491.7 0.160535 0.160178 0.160861 +------------------------------------------------------------- +Solution Validates: avg error less than 1.000000e-06 on all three arrays +------------------------------------------------------------- \ No newline at end of file diff --git a/STREAM/csv_result_export/raw_results/19-3_18-1-2_Intel-FPGA-PAC-D5005-(Intel-Stratix-10-SX)_ni.txt b/STREAM/csv_result_export/raw_results/19-3_18-1-2_Intel-FPGA-PAC-D5005-(Intel-Stratix-10-SX)_ni.txt new file mode 100644 index 00000000..c5c97d1a --- /dev/null +++ b/STREAM/csv_result_export/raw_results/19-3_18-1-2_Intel-FPGA-PAC-D5005-(Intel-Stratix-10-SX)_ni.txt @@ -0,0 +1,50 @@ +------------------------------------------------------------- +STREAM FPGA based in STREAM version $Revision: 5.10 $ +------------------------------------------------------------- +This system uses 4 bytes per array element. +------------------------------------------------------------- +Array size = 100000000 (elements), Offset = 0 (elements) +Memory per array = 381.5 MiB (= 0.4 GiB). +Total memory required = 1144.4 MiB (= 1.1 GiB). +Each kernel will be executed 10 times. + The *best* time for each kernel (excluding the first iteration) + will be used to compute the reported bandwidth. +------------------------------------------------------------- +Platform Name: Intel(R) FPGA SDK for OpenCL(TM) +Device Name: pac_s10_dc : Intel PAC Platform (pac_e600000) +Using kernel given as argument +Kernel: bin/stream_kernels_sp_pac_no_interleaving.aocx +------------------------------------------------------------- +Prepared FPGA successfully! +------------------------------------------------------------- +Your clock granularity/precision appears to be 1 microseconds. +Each test below will take on the order of 49671 microseconds. + (= 49671 clock ticks) +Increase the size of the arrays if this shows that +you are not getting at least 20 clock ticks per test. +------------------------------------------------------------- +WARNING -- The above is only a rough guideline. +For best results, please be sure you know the +precision of your system timer. +------------------------------------------------------------- +Execute iteration 1 of 10 +Execute iteration 2 of 10 +Execute iteration 3 of 10 +Execute iteration 4 of 10 +Execute iteration 5 of 10 +Execute iteration 6 of 10 +Execute iteration 7 of 10 +Execute iteration 8 of 10 +Execute iteration 9 of 10 +Execute iteration 10 of 10 +fMax=313.28 +Function Best Rate MB/s Avg time Min time Max time +Copy: 29244.1 0.027398 0.027356 0.027434 +Scale: 28044.5 0.028559 0.028526 0.028595 +Add: 42087.5 0.028547 0.028512 0.028603 +Triad: 41905.0 0.028712 0.028636 0.028804 +PCI Write: 8678.2 0.155757 0.138277 0.183577 +PCI Read: 7365.4 0.179162 0.162925 0.210698 +------------------------------------------------------------- +Solution Validates: avg error less than 1.000000e-06 on all three arrays +------------------------------------------------------------- \ No newline at end of file diff --git a/STREAM/csv_result_export/raw_results/19-3_19-2_Bittware-520N-(Intel-Stratix-10-GX2800).txt b/STREAM/csv_result_export/raw_results/19-3_19-2_Bittware-520N-(Intel-Stratix-10-GX2800).txt new file mode 100644 index 00000000..737d9041 --- /dev/null +++ b/STREAM/csv_result_export/raw_results/19-3_19-2_Bittware-520N-(Intel-Stratix-10-GX2800).txt @@ -0,0 +1,52 @@ +------------------------------------------------------------- +STREAM FPGA based in STREAM version $Revision: 5.10 $ +------------------------------------------------------------- +This system uses 4 bytes per array element. +------------------------------------------------------------- +Array size = 100000000 (elements), Offset = 0 (elements) +Memory per array = 381.5 MiB (= 0.4 GiB). +Total memory required = 1144.4 MiB (= 1.1 GiB). +Each kernel will be executed 10 times. + The *best* time for each kernel (excluding the first iteration) + will be used to compute the reported bandwidth. +------------------------------------------------------------- +Platform Name: Intel(R) FPGA SDK for OpenCL(TM) +Device Name: p520_hpc_sg280l : BittWare Stratix 10 OpenCL platform (aclbitt_s10_pcie0) +Using kernel given as argument +Kernel: bin/stream_kernels_sp.aocx +------------------------------------------------------------- +MMD INFO : Disabling SmartVID (fix) polling +MMD INFO : Enabling SmartVID (fix) polling +Prepared FPGA successfully! +------------------------------------------------------------- +Your clock granularity/precision appears to be 1 microseconds. +Each test below will take on the order of 23596 microseconds. + (= 23596 clock ticks) +Increase the size of the arrays if this shows that +you are not getting at least 20 clock ticks per test. +------------------------------------------------------------- +WARNING -- The above is only a rough guideline. +For best results, please be sure you know the +precision of your system timer. +------------------------------------------------------------- +Execute iteration 1 of 10 +Execute iteration 2 of 10 +Execute iteration 3 of 10 +Execute iteration 4 of 10 +Execute iteration 5 of 10 +Execute iteration 6 of 10 +Execute iteration 7 of 10 +Execute iteration 8 of 10 +Execute iteration 9 of 10 +Execute iteration 10 of 10 +fMax=265.81 +Function Best Rate MB/s Avg time Min time Max time +Copy: 33953.0 0.023564 0.023562 0.023567 +Scale: 33958.5 0.023560 0.023558 0.023562 +Add: 49403.4 0.024293 0.024290 0.024296 +Triad: 48575.2 0.024707 0.024704 0.024709 +PCI Write: 6319.5 0.189961 0.189889 0.190036 +PCI Read: 6293.0 0.190761 0.190688 0.190918 +------------------------------------------------------------- +Solution Validates: avg error less than 1.000000e-06 on all three arrays +------------------------------------------------------------- \ No newline at end of file diff --git a/STREAM/csv_result_export/raw_results/19-3_19-2_Bittware-520N-(Intel-Stratix-10-GX2800)_ni.txt b/STREAM/csv_result_export/raw_results/19-3_19-2_Bittware-520N-(Intel-Stratix-10-GX2800)_ni.txt new file mode 100644 index 00000000..fa96e61c --- /dev/null +++ b/STREAM/csv_result_export/raw_results/19-3_19-2_Bittware-520N-(Intel-Stratix-10-GX2800)_ni.txt @@ -0,0 +1,52 @@ +------------------------------------------------------------- +STREAM FPGA based in STREAM version $Revision: 5.10 $ +------------------------------------------------------------- +This system uses 4 bytes per array element. +------------------------------------------------------------- +Array size = 100000000 (elements), Offset = 0 (elements) +Memory per array = 381.5 MiB (= 0.4 GiB). +Total memory required = 1144.4 MiB (= 1.1 GiB). +Each kernel will be executed 10 times. + The *best* time for each kernel (excluding the first iteration) + will be used to compute the reported bandwidth. +------------------------------------------------------------- +Platform Name: Intel(R) FPGA SDK for OpenCL(TM) +Device Name: p520_hpc_sg280l : BittWare Stratix 10 OpenCL platform (aclbitt_s10_pcie0) +Using kernel given as argument +Kernel: stream_kernels_sp_no_interleaving.aocx +------------------------------------------------------------- +MMD INFO : Disabling SmartVID (fix) polling +MMD INFO : Enabling SmartVID (fix) polling +Prepared FPGA successfully! +------------------------------------------------------------- +Your clock granularity/precision appears to be 1 microseconds. +Each test below will take on the order of 46267 microseconds. + (= 46267 clock ticks) +Increase the size of the arrays if this shows that +you are not getting at least 20 clock ticks per test. +------------------------------------------------------------- +WARNING -- The above is only a rough guideline. +For best results, please be sure you know the +precision of your system timer. +------------------------------------------------------------- +Execute iteration 1 of 10 +Execute iteration 2 of 10 +Execute iteration 3 of 10 +Execute iteration 4 of 10 +Execute iteration 5 of 10 +Execute iteration 6 of 10 +Execute iteration 7 of 10 +Execute iteration 8 of 10 +Execute iteration 9 of 10 +Execute iteration 10 of 10 +fMax=307.78 +Function Best Rate MB/s Avg time Min time Max time +Copy: 33555.4 0.023847 0.023841 0.023855 +Scale: 33581.3 0.023836 0.023823 0.023844 +Add: 50362.9 0.023838 0.023827 0.023845 +Triad: 50333.7 0.023842 0.023841 0.023844 +PCI Write: 6307.8 0.190291 0.190242 0.190335 +PCI Read: 6405.3 0.187365 0.187346 0.187387 +------------------------------------------------------------- +Solution Validates: avg error less than 1.000000e-06 on all three arrays +------------------------------------------------------------- \ No newline at end of file diff --git a/STREAM/csv_result_export/raw_results/19-4_18-1-2_Intel-FPGA-PAC-D5005-(Intel-Stratix-10-SX)-SVM.txt b/STREAM/csv_result_export/raw_results/19-4_18-1-2_Intel-FPGA-PAC-D5005-(Intel-Stratix-10-SX)-SVM.txt new file mode 100644 index 00000000..662529aa --- /dev/null +++ b/STREAM/csv_result_export/raw_results/19-4_18-1-2_Intel-FPGA-PAC-D5005-(Intel-Stratix-10-SX)-SVM.txt @@ -0,0 +1,61 @@ +------------------------------------------------------------- +STREAM FPGA based in STREAM version $Revision: 5.10 $ +------------------------------------------------------------- +This system uses 4 bytes per array element. +------------------------------------------------------------- +Array size = 100000000 (elements), Offset = 0 (elements) +Memory per array = 381.5 MiB (= 0.4 GiB). +Total memory required = 1144.4 MiB (= 1.1 GiB). +Each kernel will be executed 10 times. + The *best* time for each kernel (excluding the first iteration) + will be used to compute the reported bandwidth. +------------------------------------------------------------- +XRT build version: 2.3.1301 +Build hash: 192e706aea53163a04c574f9b3fe9ed76b6ca471 +Build date: 2019-10-25 03:04:42 +Git branch: 2019.2 +PID: 253324 +UID: 18577 +[Wed Jan 22 16:20:24 2020] +HOST: fpga-0010 +EXE: /upb/scratch/departments/pc2/groups/pc2-mitarbeiter/mariusme/noctua/stream_measure_svm/bin/stream_fpga_sp +[XRT] ERROR: XILINX_XRT must be set +Platform Name: Intel(R) FPGA SDK for OpenCL(TM) +Initialized BSP +Device Name: pac_s10_dc : Intel PAC Platform (pac_e600000) +Using kernel given as argument +Kernel: stream_kernels_19.1.0_sp.aocx +------------------------------------------------------------- +Prepared FPGA successfully! +------------------------------------------------------------- +Your clock granularity/precision appears to be 1 microseconds. +Each test below will take on the order of 34543 microseconds. + (= 34543 clock ticks) +Increase the size of the arrays if this shows that +you are not getting at least 20 clock ticks per test. +------------------------------------------------------------- +WARNING -- The above is only a rough guideline. +For best results, please be sure you know the +precision of your system timer. +------------------------------------------------------------- +Execute iteration 1 of 10 +Execute iteration 2 of 10 +Execute iteration 3 of 10 +Execute iteration 4 of 10 +Execute iteration 5 of 10 +Execute iteration 6 of 10 +Execute iteration 7 of 10 +Execute iteration 8 of 10 +Execute iteration 9 of 10 +Execute iteration 10 of 10 +fMax=296.12 +Function Best Rate MB/s Avg time Min time Max time +Copy: 19255.3 0.041615 0.041547 0.041697 +Scale: 20487.6 0.039103 0.039048 0.039184 +Add: 14709.2 0.081682 0.081582 0.081774 +Triad: 14676.7 0.081817 0.081762 0.081857 +PCI Write: 419430400.0 0.000003 0.000003 0.000004 +PCI Read: 419430400.0 0.000003 0.000003 0.000006 +------------------------------------------------------------- +Solution Validates: avg error less than 1.000000e-06 on all three arrays +------------------------------------------------------------- \ No newline at end of file diff --git a/STREAM/csv_result_export/raw_results/19-4_18-1-2_Intel-FPGA-PAC-D5005-(Intel-Stratix-10-SX)-SVM_ni.txt b/STREAM/csv_result_export/raw_results/19-4_18-1-2_Intel-FPGA-PAC-D5005-(Intel-Stratix-10-SX)-SVM_ni.txt new file mode 100644 index 00000000..a8440fd2 --- /dev/null +++ b/STREAM/csv_result_export/raw_results/19-4_18-1-2_Intel-FPGA-PAC-D5005-(Intel-Stratix-10-SX)-SVM_ni.txt @@ -0,0 +1,61 @@ +------------------------------------------------------------- +STREAM FPGA based in STREAM version $Revision: 5.10 $ +------------------------------------------------------------- +This system uses 4 bytes per array element. +------------------------------------------------------------- +Array size = 100000000 (elements), Offset = 0 (elements) +Memory per array = 381.5 MiB (= 0.4 GiB). +Total memory required = 1144.4 MiB (= 1.1 GiB). +Each kernel will be executed 10 times. + The *best* time for each kernel (excluding the first iteration) + will be used to compute the reported bandwidth. +------------------------------------------------------------- +XRT build version: 2.3.1301 +Build hash: 192e706aea53163a04c574f9b3fe9ed76b6ca471 +Build date: 2019-10-25 03:04:42 +Git branch: 2019.2 +PID: 254432 +UID: 18577 +[Wed Jan 22 16:22:06 2020] +HOST: fpga-0010 +EXE: /upb/scratch/departments/pc2/groups/pc2-mitarbeiter/mariusme/noctua/stream_measure_svm/bin/stream_fpga_sp_no_interleaving +[XRT] ERROR: XILINX_XRT must be set +Platform Name: Intel(R) FPGA SDK for OpenCL(TM) +Initialized BSP +Device Name: pac_s10_dc : Intel PAC Platform (pac_e600000) +Using kernel given as argument +Kernel: stream_kernels_19.1.0_sp_no_interleaving.aocx +------------------------------------------------------------- +Prepared FPGA successfully! +------------------------------------------------------------- +Your clock granularity/precision appears to be 1 microseconds. +Each test below will take on the order of 34569 microseconds. + (= 34569 clock ticks) +Increase the size of the arrays if this shows that +you are not getting at least 20 clock ticks per test. +------------------------------------------------------------- +WARNING -- The above is only a rough guideline. +For best results, please be sure you know the +precision of your system timer. +------------------------------------------------------------- +Execute iteration 1 of 10 +Execute iteration 2 of 10 +Execute iteration 3 of 10 +Execute iteration 4 of 10 +Execute iteration 5 of 10 +Execute iteration 6 of 10 +Execute iteration 7 of 10 +Execute iteration 8 of 10 +Execute iteration 9 of 10 +Execute iteration 10 of 10 +fMax=296.12 +Function Best Rate MB/s Avg time Min time Max time +Copy: 19234.5 0.041710 0.041592 0.041775 +Scale: 20179.1 0.039771 0.039645 0.039899 +Add: 14677.2 0.081870 0.081759 0.081907 +Triad: 14670.8 0.081833 0.081795 0.081899 +PCI Write: 387166523.1 0.000004 0.000003 0.000004 +PCI Read: 419430400.0 0.000003 0.000003 0.000004 +------------------------------------------------------------- +Solution Validates: avg error less than 1.000000e-06 on all three arrays +------------------------------------------------------------- \ No newline at end of file diff --git a/STREAM/csv_result_export/raw_results/README.md b/STREAM/csv_result_export/raw_results/README.md new file mode 100644 index 00000000..fe1a756e --- /dev/null +++ b/STREAM/csv_result_export/raw_results/README.md @@ -0,0 +1,21 @@ +# Raw Result Folder + +This folder contains the single precision results for different FPGA boards and SDK versions. +The files contain the raw output from the benchmark execution and can be parsed with the +script `parse_data.py` given in the parent directory. + +The files are neamed after the following scheme: + + SDK_BSP_BOARD-INFO[_ni].txt + +with the following meaning: + +- **SDK**: The used version of the SDK with major and minor releases are separated by `-`. e.g. 18.1.1 would resemble `18-1-1` +- **BSP**: The version of the used board support packages with same version scheme than the SDK. +- **BOARD_INFO**: Information about the used FPGA board following the scheme `BOARD-NAME-(FPGA-NAME)[-SVM]` + giving the board name and FPGA name with `-` instead of spaces and an optional `-SVM` suffix, if the + OpenCL shared virtual memory functionality was used. e.g. `Bittware-385A-(Intel-Arria-10-GX1150)` +- optional **ni**: Indicates if automatic memory interleaving by the compiler was used or not. + If `_ni` is appended then no interleaving by the compiler was used. + +All runs were executed with arrays over 100 million values. \ No newline at end of file diff --git a/STREAM/csv_result_export/requirements.txt b/STREAM/csv_result_export/requirements.txt new file mode 100644 index 00000000..c0b35399 --- /dev/null +++ b/STREAM/csv_result_export/requirements.txt @@ -0,0 +1,3 @@ +matplotlib==3.0.3 +numpy==1.16.2 +pandas==0.24.2 \ No newline at end of file diff --git a/STREAM/csv_result_export/sp_global_ring_plot.jpeg b/STREAM/csv_result_export/sp_global_ring_plot.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..6d747aab3fb470b8414e601067a6bdbf26b69f95 GIT binary patch literal 209487 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�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ⅇ9POxW@H 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

    Qf5URP34A7s)hDX{^Xe8)qBMrXj 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� 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 GasCdQQ!FC@ literal 0 HcmV?d00001 diff --git a/STREAM/results.txt b/STREAM/results.txt new file mode 100644 index 00000000..0c9b32ae --- /dev/null +++ b/STREAM/results.txt @@ -0,0 +1,506 @@ +This file contains execution results for multiple systems administrated by the Paderborn Center for Parallel Computing (PC2) (https://pc2.upb.de). +Frequencies are given in MHz. fMax are retrieved from the AOC report. + +Results for Single Precision +============================= +Executed using arrays with 100000000 elements. +The following environment variables where set for the kernel file: +UNROLL_COUNT=16 + +1. System: Noctua + Url: https://pc2.uni-paderborn.de/hpc-services/available-systems/noctua/ + FPGA card: Bittware 520N (Intel Stratix 10 GX2800) + Compiler: Intel(R) FPGA SDK for OpenCL(TM), 64-Bit Offline Compiler, Version: 18.0.1 + + Interleaved Memory: + fMax=276.77 + Function Best Rate MB/s Avg time Min time Max time + Copy: 35130.7 0.022776 0.022772 0.022782 + Scale: 35138.5 0.022771 0.022767 0.022776 + Add: 52705.5 0.022772 0.022768 0.022777 + Triad: 48802.2 0.024593 0.024589 0.024598 + PCI Write: 6303.5 0.190460 0.190369 0.190521 + PCI Read: 3673.8 0.326716 0.326636 0.326799 + + Non-Interleaved: + fMax=372.16 + Function Best Rate MB/s Avg time Min time Max time + Copy: 31992.3 0.025015 0.025006 0.025025 + Scale: 32020.6 0.024990 0.024984 0.024997 + Add: 48009.4 0.025003 0.024995 0.025012 + Triad: 48005.8 0.025003 0.024997 0.025007 + PCI Write: 6303.9 0.190423 0.190359 0.190566 + PCI Read: 3771.6 0.318213 0.318168 0.318284 + + + +2. System: Noctua + Url: https://pc2.uni-paderborn.de/hpc-services/available-systems/noctua/ + FPGA card: Bittware 520N (Intel Stratix 10 GX2800) + Compiler: Intel(R) FPGA SDK for OpenCL(TM), 64-Bit Offline Compiler, Version: 18.1.1 + + Interleaved Memory: + fMax=242.18 + Function Best Rate MB/s Avg time Min time Max time + Copy: 30875.9 0.025914 0.025910 0.025919 + Scale: 30885.6 0.025905 0.025902 0.025911 + Add: 46289.2 0.025928 0.025924 0.025935 + Triad: 45613.4 0.026310 0.026308 0.026312 + PCI Write: 6324.0 0.189800 0.189753 0.189862 + PCI Read: 5587.3 0.214869 0.214773 0.214943 + + Non-Interleaved: + fMax=328.125 + Function Best Rate MB/s Avg time Min time Max time + Copy: 31767.5 0.025188 0.025183 0.025196 + Scale: 31777.7 0.025177 0.025175 0.025181 + Add: 47672.0 0.025174 0.025172 0.025177 + Triad: 47559.0 0.025236 0.025232 0.025246 + PCI Write: 6316.0 0.190029 0.189994 0.190060 + PCI Read: 5728.0 0.209528 0.209497 0.209626 + + + +3. System: Noctua + Url: https://pc2.uni-paderborn.de/hpc-services/available-systems/noctua/ + FPGA card: Bittware 520N (Intel Stratix 10 GX2800) + Compiler: Intel(R) FPGA SDK for OpenCL(TM), 64-Bit Offline Compiler, Version: 19.1 + + Interleaved Memory: + fMax=255.68 + Function Best Rate MB/s Avg time Min time Max time + Copy: 32597.4 0.024545 0.024542 0.024548 + Scale: 32602.8 0.024539 0.024538 0.024541 + Add: 48572.8 0.024708 0.024705 0.024711 + Triad: 47423.6 0.025308 0.025304 0.025313 + PCI Write: 6330.3 0.189646 0.189563 0.189759 + PCI Read: 6315.9 0.190041 0.189998 0.190126 + + Non-Interleaved: + fMax=342.93 + Function Best Rate MB/s Avg time Min time Max time + Copy: 32642.4 0.024513 0.024508 0.024525 + Scale: 32659.9 0.024499 0.024495 0.024501 + Add: 48979.8 0.024502 0.024500 0.024504 + Triad: 48973.6 0.024504 0.024503 0.024506 + PCI Write: 6318.7 0.189960 0.189912 0.190026 + PCI Read: 6413.0 0.187184 0.187119 0.187271 + + + +4. System: Noctua + Url: https://pc2.uni-paderborn.de/hpc-services/available-systems/noctua/ + FPGA card: Bittware 520N (Intel Stratix 10 GX2800) + Compiler: Intel(R) FPGA SDK for OpenCL(TM), 64-Bit Offline Compiler, Version: 19.2.0_hpc + + Interleaved Memory: + fMax=269.68 + Function Best Rate MB/s Avg time Min time Max time + Copy: 34349.6 0.023293 0.023290 0.023298 + Scale: 34358.1 0.023286 0.023284 0.023288 + Add: 50335.7 0.023842 0.023840 0.023843 + Triad: 48650.3 0.024668 0.024666 0.024671 + PCI Write: 6310.6 0.190267 0.190155 0.190397 + PCI Read: 6306.2 0.190343 0.190289 0.190388 + + Non-Interleaved: + fMax=378.64 + Function Best Rate MB/s Avg time Min time Max time + Copy: 35187.9 0.022747 0.022735 0.022761 + Scale: 35172.7 0.022754 0.022745 0.022769 + Add: 52800.6 0.022739 0.022727 0.022749 + Triad: 52763.5 0.022753 0.022743 0.022765 + PCI Write: 6321.3 0.189897 0.189834 0.189961 + PCI Read: 6405.2 0.187370 0.187347 0.187391 + + + +5. System: Noctua + Url: https://pc2.uni-paderborn.de/hpc-services/available-systems/noctua/ + FPGA card: Bittware 520N (Intel Stratix 10 GX2800) + Compiler: Intel(R) FPGA SDK for OpenCL(TM), 64-Bit Offline Compiler, Version: 19.3.0 + BSP Version:19.2.0_hpc + + Interleaved Memory: + fMax=265.81 + Function Best Rate MB/s Avg time Min time Max time + Copy: 33953.0 0.023564 0.023562 0.023567 + Scale: 33958.5 0.023560 0.023558 0.023562 + Add: 49403.4 0.024293 0.024290 0.024296 + Triad: 48575.2 0.024707 0.024704 0.024709 + PCI Write: 6319.5 0.189961 0.189889 0.190036 + PCI Read: 6293.0 0.190761 0.190688 0.190918 + + Non-Interleaved: + fMax=307.78 + Function Best Rate MB/s Avg time Min time Max time + Copy: 33555.4 0.023847 0.023841 0.023855 + Scale: 33581.3 0.023836 0.023823 0.023844 + Add: 50362.9 0.023838 0.023827 0.023845 + Triad: 50333.7 0.023842 0.023841 0.023844 + PCI Write: 6307.8 0.190291 0.190242 0.190335 + PCI Read: 6405.3 0.187365 0.187346 0.187387 + + + +6. System: FPGA Research Clusters + Url: https://pc2.uni-paderborn.de/hpc-services/available-systems/fpga-research-clusters/ + FPGA: Bittware 385A (Intel Arria 10 GX1150) + Compiler: Intel(R) FPGA SDK for OpenCL(TM), 64-Bit Offline Compiler, Version: 17.1.2 + + Interleaved Memory: + fMax=341.06 + Function Best Rate MB/s Avg time Min time Max time + Copy: 26767.5 0.029892 0.029887 0.029901 + Scale: 27112.3 0.029562 0.029507 0.029594 + Add: 28839.9 0.041659 0.041609 0.041704 + Triad: 28848.8 0.041656 0.041596 0.041725 + PCI Write: 6419.2 0.187463 0.186940 0.187959 + PCI Read: 6356.5 0.192998 0.188783 0.196114 + + Non-Interleaved: + fMax=342.93 + Function Best Rate MB/s Avg time Min time Max time + Copy: 15553.6 0.051469 0.051435 0.051553 + Scale: 32581.2 0.024617 0.024554 0.024661 + Add: 23444.7 0.051247 0.051184 0.051280 + Triad: 23370.0 0.051414 0.051348 0.051455 + PCI Write: 6415.0 0.187305 0.187060 0.187613 + PCI Read: 6478.7 0.189044 0.185221 0.193868 + + + +7. System: HARP2 + Url: https://pc2.uni-paderborn.de/hpc-services/available-systems/fpga-research-clusters/ + FPGA: Intel BDW+FPGA hybrid CPU/FPGA Arria 10 GX1150 + Compiler: Altera SDK for OpenCL, 64-Bit Offline Compiler, Version 16.0.2 Build 222 + + Interleaved Memory: + fMax=252.9 + Function Best Rate MB/s Avg time Min time Max time + Copy: 15416.1 0.051917 0.051894 0.051965 + Scale: 15674.0 0.051056 0.051040 0.051066 + Add: 20816.4 0.057661 0.057647 0.057676 + Triad: 20543.0 0.058422 0.058414 0.058438 + PCI Write: 74017129.4 0.000031 0.000016 0.000045 + PCI Read: 75121862.7 0.000028 0.000016 0.000041 + + !!!! + !! NOTE: The data buffers are not copied and mapping/unmapping them introduces a + !! constant overhead. The PCI rows in this result measure this overhead and + !! no PCI communication! + !!!! + + + +8. System: Noctua + Url: https://pc2.uni-paderborn.de/hpc-services/available-systems/noctua/ + FPGA: Intel FPGA PAC D5005 + Compiler: Intel(R) FPGA SDK for OpenCL(TM), 64-Bit Offline Compiler, Version: 19.3.0 + BSP Version:18.1.2 + + Interleaved Memory: + fMax=244.37 + Function Best Rate MB/s Avg time Min time Max time + Copy: 31109.0 0.025743 0.025716 0.025781 + Scale: 31144.2 0.025715 0.025687 0.025756 + Add: 46705.4 0.025717 0.025693 0.025775 + Triad: 46696.3 0.025720 0.025698 0.025758 + PCI Write: 8863.4 0.135605 0.135388 0.135980 + PCI Read: 7491.7 0.160535 0.160178 0.160861 + + Non-Interleaved: + fMax=313.28 + Function Best Rate MB/s Avg time Min time Max time + Copy: 29244.1 0.027398 0.027356 0.027434 + Scale: 28044.5 0.028559 0.028526 0.028595 + Add: 42087.5 0.028547 0.028512 0.028603 + Triad: 41905.0 0.028712 0.028636 0.028804 + PCI Write: 8678.2 0.155757 0.138277 0.183577 + PCI Read: 7365.4 0.179162 0.162925 0.210698 + + + +9. System: Noctua + Url: https://pc2.uni-paderborn.de/hpc-services/available-systems/noctua/ + FPGA: Intel FPGA PAC D5005 using SVM + Compiler: Intel(R) FPGA SDK for OpenCL(TM), 64-Bit Offline Compiler, Version: 19.4.0 + BSP Version:18.1.2_svm + + Interleaved Memory: + fMax=296.12 + Function Best Rate MB/s Avg time Min time Max time + Copy: 19255.3 0.041615 0.041547 0.041697 + Scale: 20487.6 0.039103 0.039048 0.039184 + Add: 14709.2 0.081682 0.081582 0.081774 + Triad: 14676.7 0.081817 0.081762 0.081857 + PCI Write: 419430400.0 0.000003 0.000003 0.000004 + PCI Read: 419430400.0 0.000003 0.000003 0.000006 + + Non-Interleaved: + fMax=296.12 + Function Best Rate MB/s Avg time Min time Max time + Copy: 19234.5 0.041710 0.041592 0.041775 + Scale: 20179.1 0.039771 0.039645 0.039899 + Add: 14677.2 0.081870 0.081759 0.081907 + Triad: 14670.8 0.081833 0.081795 0.081899 + PCI Write: 387166523.1 0.000004 0.000003 0.000004 + PCI Read: 419430400.0 0.000003 0.000003 0.000004 + + !!!! + !! NOTE: The data buffers are not copied and mapping/unmapping them introduces a + !! constant overhead. The PCI rows in this result measure this overhead and + !! no PCI communication! + !!!! + + + +Results for Double Precision +============================= +Executed using arrays with 50000000 elements. +UNROLL_COUNT=8 for all kernels + +1. System: Noctua + Url: https://pc2.uni-paderborn.de/hpc-services/available-systems/noctua/ + FPGA card: Bittware 520N (Intel Stratix 10 GX2800) + Compiler: Intel(R) FPGA SDK for OpenCL(TM), 64-Bit Offline Compiler, Version: 18.0.1 + + Interleaved Memory: + fMax=251.06 + Function Best Rate MB/s Avg time Min time Max time + Copy: 31938.7 0.025057 0.025048 0.025069 + Scale: 31946.3 0.025047 0.025042 0.025058 + Add: 47921.2 0.025044 0.025041 0.025052 + Triad: 47923.0 0.025044 0.025040 0.025049 + PCI Write: 6324.1 0.189882 0.189750 0.190120 + PCI Read: 3531.8 0.339810 0.339775 0.339877 + + Non-Interleaved: + fMax=348.79 + Function Best Rate MB/s Avg time Min time Max time + Copy: 31119.9 0.025712 0.025707 0.025716 + Scale: 31213.4 0.025640 0.025630 0.025652 + Add: 46707.2 0.025705 0.025692 0.025718 + Triad: 46831.0 0.025634 0.025624 0.025645 + PCI Write: 6322.6 0.189834 0.189796 0.189907 + PCI Read: 3771.0 0.318305 0.318222 0.318372 + + + +2. System: Noctua + Url: https://pc2.uni-paderborn.de/hpc-services/available-systems/noctua/ + FPGA card: Bittware 520N (Intel Stratix 10 GX2800) + Compiler: Intel(R) FPGA SDK for OpenCL(TM), 64-Bit Offline Compiler, Version: 18.1.1 + + Interleaved Memory: + fMax=260.82 + Function Best Rate MB/s Avg time Min time Max time + Copy: 33218.3 0.024086 0.024083 0.024092 + Scale: 33226.8 0.024080 0.024077 0.024085 + Add: 49249.2 0.024374 0.024366 0.024380 + Triad: 48108.1 0.024952 0.024944 0.024959 + PCI Write: 6326.8 0.189740 0.189670 0.189814 + PCI Read: 5397.1 0.222387 0.222343 0.222454 + + Non-Interleaved: + fMax=338.63 + Function Best Rate MB/s Avg time Min time Max time + Copy: 32108.0 0.024926 0.024916 0.024937 + Scale: 32122.1 0.024912 0.024905 0.024925 + Add: 48175.3 0.024913 0.024909 0.024920 + Triad: 48220.1 0.024897 0.024886 0.024906 + PCI Write: 6326.2 0.189774 0.189687 0.189906 + PCI Read: 5730.6 0.209460 0.209402 0.209591 + + + +3. System: Noctua + Url: https://pc2.uni-paderborn.de/hpc-services/available-systems/noctua/ + FPGA card: Bittware 520N (Intel Stratix 10 GX2800) + Compiler: Intel(R) FPGA SDK for OpenCL(TM), 64-Bit Offline Compiler, Version: 19.1 + + Interleaved Memory: + fMax=266.8 + Function Best Rate MB/s Avg time Min time Max time + Copy: 34084.4 0.023472 0.023471 0.023474 + Scale: 34092.1 0.023467 0.023466 0.023468 + Add: 50331.1 0.023843 0.023842 0.023847 + Triad: 48379.1 0.024808 0.024804 0.024810 + PCI Write: 6320.5 0.189925 0.189860 0.190030 + PCI Read: 6308.9 0.190234 0.190206 0.190309 + + Non-Interleaved: + fMax=372.02 + Function Best Rate MB/s Avg time Min time Max time + Copy: 32682.5 0.024480 0.024478 0.024483 + Scale: 32694.6 0.024470 0.024469 0.024470 + Add: 49043.3 0.024470 0.024468 0.024470 + Triad: 49028.0 0.024477 0.024476 0.024479 + PCI Write: 6320.8 0.189931 0.189848 0.189990 + PCI Read: 6412.5 0.187189 0.187134 0.187254 + + + +4. System: Noctua + Url: https://pc2.uni-paderborn.de/hpc-services/available-systems/noctua/ + FPGA card: Bittware 520N (Intel Stratix 10 GX2800) + Compiler: Intel(R) FPGA SDK for OpenCL(TM), 64-Bit Offline Compiler, Version: 19.2.0_hpc + + Interleaved Memory: + fMax=243.96 + Function Best Rate MB/s Avg time Min time Max time + Copy: 31162.4 0.025674 0.025672 0.025677 + Scale: 31166.2 0.025670 0.025669 0.025672 + Add: 46740.1 0.025675 0.025674 0.025679 + Triad: 46708.9 0.025693 0.025691 0.025695 + PCI Write: 6319.6 0.189941 0.189884 0.189996 + PCI Read: 6302.0 0.190504 0.190416 0.190622 + + Non-Interleaved: + fMax=362.58 + Function Best Rate MB/s Avg time Min time Max time + Copy: 33162.1 0.024133 0.024124 0.024139 + Scale: 33111.4 0.024171 0.024161 0.024176 + Add: 49757.4 0.024126 0.024117 0.024132 + Triad: 49472.3 0.024263 0.024256 0.024268 + PCI Write: 6319.0 0.189989 0.189902 0.190059 + PCI Read: 6405.0 0.187421 0.187355 0.187522 + + + +5. System: Noctua + Url: https://pc2.uni-paderborn.de/hpc-services/available-systems/noctua/ + FPGA card: Bittware 520N (Intel Stratix 10 GX2800) + Compiler: Intel(R) FPGA SDK for OpenCL(TM), 64-Bit Offline Compiler, Version: 19.3.0 + BSP Version:19.2.0_hpc + + Interleaved Memory: + fMax=254.0 + Function Best Rate MB/s Avg time Min time Max time + Copy: 32468.7 0.024642 0.024639 0.024646 + Scale: 32472.8 0.024639 0.024636 0.024642 + Add: 48589.2 0.024700 0.024697 0.024704 + Triad: 48217.8 0.024893 0.024887 0.024896 + PCI Write: 6319.2 0.190012 0.189897 0.190127 + PCI Read: 6385.8 0.187974 0.187918 0.188051 + + Non-Interleaved: + fMax=320.92 + Function Best Rate MB/s Avg time Min time Max time + Copy: 32614.5 0.024545 0.024529 0.024562 + Scale: 32626.5 0.024532 0.024520 0.024548 + Add: 48941.2 0.024530 0.024519 0.024545 + Triad: 48861.9 0.024574 0.024559 0.024586 + PCI Write: 6317.5 0.190006 0.189948 0.190101 + PCI Read: 6397.4 0.187635 0.187575 0.187734 + + + +6. System: FPGA Research Clusters + Url: https://pc2.uni-paderborn.de/hpc-services/available-systems/fpga-research-clusters/ + FPGA: Bittware 385A (Intel Arria 10 GX1150) + Compiler: Intel(R) FPGA SDK for OpenCL(TM), 64-Bit Offline Compiler, Version: 17.1.2 + + Interleaved Memory: + fMax=315.85 + Function Best Rate MB/s Avg time Min time Max time + Copy: 26711.3 0.029969 0.029950 0.030000 + Scale: 26987.8 0.029729 0.029643 0.029777 + Add: 28662.2 0.041928 0.041867 0.041976 + Triad: 28633.5 0.041938 0.041909 0.041968 + PCI Write: 6405.3 0.190147 0.187346 0.192743 + PCI Read: 6391.9 0.192733 0.187739 0.196223 + + Non-Interleaved: + fMax=318.16 + Function Best Rate MB/s Avg time Min time Max time + Copy: 15612.5 0.051260 0.051241 0.051291 + Scale: 32520.3 0.024654 0.024600 0.024689 + Add: 23581.6 0.051006 0.050887 0.051087 + Triad: 23516.1 0.051060 0.051029 0.051139 + PCI Write: 6408.7 0.188771 0.187246 0.191645 + PCI Read: 6373.6 0.191591 0.188277 0.195962 + + + +7. System: HARP2 + Url: https://pc2.uni-paderborn.de/hpc-services/available-systems/fpga-research-clusters/ + FPGA: Intel BDW+FPGA hybrid CPU/FPGA Arria 10 GX1150 + Compiler: Altera SDK for OpenCL, 64-Bit Offline Compiler, Version 16.0.2 Build 222 + + Interleaved Memory: + fMax=195.23 + Copy: 14735.5 0.054322 0.054291 0.054338 + Scale: 15410.1 0.051922 0.051914 0.051927 + Add: 18619.1 0.064461 0.064450 0.064471 + Triad: 18651.5 0.064355 0.064338 0.064367 + PCI Write: 79891504.8 0.000031 0.000015 0.000046 + PCI Read: 75121862.7 0.000027 0.000016 0.000041 + + !!!! + !! NOTE: The data buffers are not copied and mapping/unmapping them introduces a + !! constant overhead. The PCI rows in this result measure this overhead and + !! no PCI communication! + !!!! + + + +8. System: Noctua + Url: https://pc2.uni-paderborn.de/hpc-services/available-systems/noctua/ + FPGA: Intel FPGA PAC D5005 + Compiler: Intel(R) FPGA SDK for OpenCL(TM), 64-Bit Offline Compiler, Version: 19.3.0 + BSP Version:18.1.2 + + Interleaved Memory: + fMax=254.0 + Function Best Rate MB/s Avg time Min time Max time + Copy: 31692.2 0.025302 0.025243 0.025351 + Scale: 31694.3 0.025305 0.025241 0.025341 + Add: 47543.7 0.025298 0.025240 0.025338 + Triad: 47532.5 0.025305 0.025246 0.025347 + PCI Write: 8931.8 0.134656 0.134352 0.134971 + PCI Read: 7535.7 0.159409 0.159241 0.159720 + + Non-Interleaved: + fMax=320.92 + Function Best Rate MB/s Avg time Min time Max time + Copy: 29106.6 0.027511 0.027485 0.027536 + Scale: 28046.6 0.028585 0.028524 0.028629 + Add: 42072.8 0.028547 0.028522 0.028612 + Triad: 42047.8 0.028587 0.028539 0.028631 + PCI Write: 8638.0 0.167112 0.138921 0.208923 + PCI Read: 7285.5 0.193966 0.164710 0.241611 + + + +9. System: Noctua + Url: https://pc2.uni-paderborn.de/hpc-services/available-systems/noctua/ + FPGA: Intel FPGA PAC D5005 using SVM + Compiler: Intel(R) FPGA SDK for OpenCL(TM), 64-Bit Offline Compiler, Version: 19.4.0 + BSP Version:18.1.2_svm + + Interleaved Memory: + fMax=326.58 + Function Best Rate MB/s Avg time Min time Max time + Copy: 19237.7 0.041656 0.041585 0.041723 + Scale: 20886.7 0.038456 0.038302 0.038566 + Add: 14575.5 0.082394 0.082330 0.082478 + Triad: 14578.3 0.082346 0.082314 0.082383 + PCI Write: 419430400.0 0.000004 0.000003 0.000004 + PCI Read: 419430400.0 0.000004 0.000003 0.000006 + + Non-Interleaved: + fMax=326.58 + Function Best Rate MB/s Avg time Min time Max time + Copy: 19186.0 0.041747 0.041697 0.041840 + Scale: 20561.3 0.038969 0.038908 0.039044 + Add: 14574.8 0.082398 0.082334 0.082447 + Triad: 14581.7 0.082371 0.082295 0.082418 + PCI Write: 419430400.0 0.000003 0.000003 0.000004 + PCI Read: 419430400.0 0.000003 0.000003 0.000004 + + !!!! + !! NOTE: The data buffers are not copied and mapping/unmapping them introduces a + !! constant overhead. The PCI rows in this result measure this overhead and + !! no PCI communication! + !!!! \ No newline at end of file diff --git a/STREAM/settings/settings.compile.xilinx.ddr.ini b/STREAM/settings/settings.compile.xilinx.ddr.ini new file mode 100644 index 00000000..4a2d3be6 --- /dev/null +++ b/STREAM/settings/settings.compile.xilinx.ddr.ini @@ -0,0 +1,5 @@ +kernel_frequency=300 + +[hls] +max_memory_ports=all +#memory_port_data_width=512 diff --git a/STREAM/settings/settings.compile.xilinx.ini b/STREAM/settings/settings.compile.xilinx.ini new file mode 100644 index 00000000..8297020d --- /dev/null +++ b/STREAM/settings/settings.compile.xilinx.ini @@ -0,0 +1,4 @@ +kernel_frequency=450 + +#[hls] +#max_memory_ports=all diff --git a/STREAM/settings/settings.link.xilinx.stream_kernels.ddr.ini b/STREAM/settings/settings.link.xilinx.stream_kernels.ddr.ini new file mode 100644 index 00000000..76cff927 --- /dev/null +++ b/STREAM/settings/settings.link.xilinx.stream_kernels.ddr.ini @@ -0,0 +1,24 @@ +[connectivity] +nk=copy_0:1 +nk=scale_0:1 +nk=add_0:1 +nk=triad_0:1 + +# slrs +slr=copy_0_1:SLR0 +slr=scale_0_1:SLR1 +slr=add_0_1:SLR0 +slr=triad_0_1:SLR1 + +# matrix ports +sp=copy_0_1.m_axi_gmem0:DDR[0] +sp=copy_0_1.m_axi_gmem1:DDR[1] +sp=scale_0_1.m_axi_gmem0:DDR[0:1] +sp=scale_0_1.m_axi_gmem1:DDR[0] +sp=add_0_1.m_axi_gmem0:DDR[0] +sp=add_0_1.m_axi_gmem1:DDR[0] +sp=add_0_1.m_axi_gmem2:DDR[1] +sp=triad_0_1.m_axi_gmem0:DDR[0] +sp=triad_0_1.m_axi_gmem1:DDR[1] +sp=triad_0_1.m_axi_gmem2:DDR[0] + diff --git a/STREAM/settings/settings.link.xilinx.stream_kernels.generator.ini b/STREAM/settings/settings.link.xilinx.stream_kernels.generator.ini new file mode 100644 index 00000000..30617e7a --- /dev/null +++ b/STREAM/settings/settings.link.xilinx.stream_kernels.generator.ini @@ -0,0 +1,17 @@ +[connectivity] +nk=copy_0:{TOTAL_KERNEL_NUMBER} +nk=scale_0:{TOTAL_KERNEL_NUMBER} +nk=add_0:{TOTAL_KERNEL_NUMBER} +nk=triad_0:{TOTAL_KERNEL_NUMBER} + +# slrs +slr=copy_0_{KERNEL_NUMBER}:SLR{KERNEL_NUMBER_DEC} +slr=scale_0_{KERNEL_NUMBER}:SLR{KERNEL_NUMBER_DEC} +slr=add_0_{KERNEL_NUMBER}:SLR{KERNEL_NUMBER_DEC} +slr=triad_0_{KERNEL_NUMBER}:SLR{KERNEL_NUMBER_DEC} + +# matrix ports +sp=copy_0_{KERNEL_NUMBER}.m_axi_gmem:HBM[0:2] +sp=scale_0_{KERNEL_NUMBER}.m_axi_gmem:HBM[0:2] +sp=add_0_{KERNEL_NUMBER}.m_axi_gmem:HBM[0:2] +sp=triad_0_{KERNEL_NUMBER}.m_axi_gmem:HBM[0:2] diff --git a/STREAM/settings/settings.link.xilinx.stream_kernels_single.ddr.ini b/STREAM/settings/settings.link.xilinx.stream_kernels_single.ddr.ini new file mode 100644 index 00000000..fd2fda81 --- /dev/null +++ b/STREAM/settings/settings.link.xilinx.stream_kernels_single.ddr.ini @@ -0,0 +1,15 @@ +[connectivity] +nk=calc_0:2 + +# slrs +slr=calc_0_1:SLR0 +slr=calc_0_2:SLR1 + +# matrix ports +sp=calc_0_1.m_axi_gmem0:DDR[0] +sp=calc_0_1.m_axi_gmem1:DDR[0] +sp=calc_0_1.m_axi_gmem2:DDR[0] +sp=calc_0_2.m_axi_gmem0:DDR[1] +sp=calc_0_2.m_axi_gmem1:DDR[1] +sp=calc_0_2.m_axi_gmem2:DDR[1] + diff --git a/STREAM/settings/settings.link.xilinx.stream_kernels_single.generator.ini b/STREAM/settings/settings.link.xilinx.stream_kernels_single.generator.ini new file mode 100644 index 00000000..f64070f3 --- /dev/null +++ b/STREAM/settings/settings.link.xilinx.stream_kernels_single.generator.ini @@ -0,0 +1,8 @@ +[connectivity] +nk=calc_0:{TOTAL_KERNEL_NUMBER} + +# slrs +slr=calc_0_{KERNEL_NUMBER}:SLR{KERNEL_NUMBER_DEC} + +# matrix ports +sp=calc_0_{KERNEL_NUMBER}.m_axi_gmem:HBM[{KERNEL_NUMBER_DEC}] diff --git a/STREAM/src/common/parameters.h.in b/STREAM/src/common/parameters.h.in new file mode 100644 index 00000000..ba5ef0e5 --- /dev/null +++ b/STREAM/src/common/parameters.h.in @@ -0,0 +1,37 @@ +#ifndef SRC_COMMON_PARAMETERS_H_ +#define SRC_COMMON_PARAMETERS_H_ + +/** + * Host specific parameters + */ +#define VERSION "@PROJECT_VERSION@" +#define DEFAULT_REPETITIONS @DEFAULT_REPETITIONS@ +#define DEFAULT_ARRAY_LENGTH @DEFAULT_ARRAY_LENGTH@ +#define DEFAULT_PLATFORM @DEFAULT_PLATFORM@ +#define DEFAULT_DEVICE @DEFAULT_DEVICE@ +#define HOST_DATA_TYPE @HOST_DATA_TYPE@ +#define NUM_KERNEL_REPLICATIONS @NUM_REPLICATIONS@ + +/** + * Device specific parameters + */ +#define DEVICE_ARRAY_DATA_TYPE @DEVICE_DATA_TYPE@ +#define DEVICE_SCALAR_DATA_TYPE @DATA_TYPE@ +#define VECTOR_COUNT @VECTOR_COUNT@ +#define UNROLL_COUNT @GLOBAL_MEM_UNROLL@ +#define BUFFER_SIZE @DEVICE_BUFFER_SIZE@ +#cmakedefine INNER_LOOP_BUFFERS +#cmakedefine USE_SVM + +/** +Output separator +*/ +#define HLINE "-------------------------------------------------------------\n" + +#define COPY_KERNEL_TYPE 0 +#define SCALE_KERNEL_TYPE 1 +#define ADD_KERNEL_TYPE 2 +#define TRIAD_KERNEL_TYPE 3 + + +#endif // SRC_COMMON_PARAMETERS_H_ \ No newline at end of file diff --git a/STREAM/src/device/CMakeLists.txt b/STREAM/src/device/CMakeLists.txt new file mode 100644 index 00000000..7ae6be8e --- /dev/null +++ b/STREAM/src/device/CMakeLists.txt @@ -0,0 +1,124 @@ + +set(COMPILER_INCLUDES "-I${CMAKE_CURRENT_BINARY_DIR}/../common") +set(CLFLAGS --config ${XILINX_COMPILE_SETTINGS}) + +set(Vitis_EMULATION_CONFIG_UTIL $ENV{XILINX_VITIS}/bin/emconfigutil) + +## +# This function will create build targets for the kernels for emulationand synthesis for xilinx. +## +function(generate_kernel_targets_xilinx) + foreach (kernel_file_name ${ARGN}) + set(base_file "${CMAKE_SOURCE_DIR}/src/device/${kernel_file_name}.cl") + set(source_f "${CMAKE_BINARY_DIR}/src/device/replicated_${kernel_file_name}_xilinx.cl") + set(bitstream_compile ${EXECUTABLE_OUTPUT_PATH}/tmp_compile/${kernel_file_name}.xo) + set(bitstream_compile_emulate ${EXECUTABLE_OUTPUT_PATH}/tmp_compile/${kernel_file_name}_emulate.xo) + set(bitstream_emulate_f + ${EXECUTABLE_OUTPUT_PATH}/${kernel_file_name}_emulate.xclbin) + set(bitstream_f ${EXECUTABLE_OUTPUT_PATH}/${kernel_file_name}.xclbin) + if (XILINX_GENERATE_LINK_SETTINGS) + set(gen_xilinx_link_settings ${CMAKE_SOURCE_DIR}/settings/settings.link.xilinx.${kernel_file_name}.generator.ini) + set(xilinx_link_settings ${CMAKE_BINARY_DIR}/settings/settings.link.xilinx.${kernel_file_name}.ini) + else() + set(gen_xilinx_link_settings ${XILINX_LINK_SETTINGS_FILE}) + set(xilinx_link_settings ${XILINX_LINK_SETTINGS_FILE}) + endif() + + # build emulation config for device + add_custom_command(OUTPUT ${EXECUTABLE_OUTPUT_PATH}/emconfig.json + COMMAND ${Vitis_EMULATION_CONFIG_UTIL} -f ${FPGA_BOARD_NAME} --od ${EXECUTABLE_OUTPUT_PATH} + ) + if (XILINX_GENERATE_LINK_SETTINGS) + add_custom_command(OUTPUT ${xilinx_link_settings} + COMMAND ${CMAKE_COMMAND} -Dsettings_f=${xilinx_link_settings} -Dbase_file=${gen_xilinx_link_settings} -DNUM_REPLICATIONS=${NUM_REPLICATIONS} -P "${CMAKE_SOURCE_DIR}/src/device/generateXilinxSettings.cmake" + MAIN_DEPENDENCY ${gen_xilinx_link_settings} + ) + endif() + + add_custom_command(OUTPUT ${source_f} + COMMAND ${CMAKE_COMMAND} -Dsource_f=${source_f} -Dbase_file=${base_file} -DNUM_REPLICATIONS=1 -P "${CMAKE_SOURCE_DIR}/src/device/generateKernels.cmake" + MAIN_DEPENDENCY ${base_file} + ) + + add_custom_command(OUTPUT ${bitstream_compile_emulate} + COMMAND ${Vitis_COMPILER} ${CLFLAGS} -t sw_emu ${COMPILER_INCLUDES} -f ${FPGA_BOARD_NAME} -g -c ${XILINX_COMPILE_FLAGS} -o ${bitstream_compile_emulate} ${source_f} + MAIN_DEPENDENCY ${source_f} + ) + add_custom_command(OUTPUT ${bitstream_emulate_f} + COMMAND ${Vitis_COMPILER} -t sw_emu ${COMPILER_INCLUDES} -f ${FPGA_BOARD_NAME} -g -l --config ${xilinx_link_settings} ${XILINX_COMPILE_FLAGS} -o ${bitstream_emulate_f} ${bitstream_compile_emulate} + MAIN_DEPENDENCY ${bitstream_compile_emulate} + DEPENDS ${xilinx_link_settings} + ) + add_custom_command(OUTPUT ${bitstream_compile} + COMMAND ${Vitis_COMPILER} ${CLFLAGS} -t hw ${COMPILER_INCLUDES} --platform ${FPGA_BOARD_NAME} -R2 -c ${XILINX_COMPILE_FLAGS} -o ${bitstream_compile} ${source_f} + MAIN_DEPENDENCY ${source_f} + ) + add_custom_command(OUTPUT ${bitstream_f} + COMMAND ${Vitis_COMPILER} ${CLFLAGS} -t hw ${COMPILER_INCLUDES} --platform ${FPGA_BOARD_NAME} -R2 -l --config ${xilinx_link_settings} ${XILINX_COMPILE_FLAGS} -o ${bitstream_f} ${bitstream_compile} + MAIN_DEPENDENCY ${bitstream_compile} + DEPENDS ${xilinx_link_settings} + ) + add_custom_target(${kernel_file_name}_emulate_xilinx DEPENDS ${bitstream_emulate_f} ${EXECUTABLE_OUTPUT_PATH}/emconfig.json + DEPENDS ${source_f} ${CMAKE_BINARY_DIR}/src/common/parameters.h + SOURCES ${source_f} ${CMAKE_BINARY_DIR}/src/common/parameters.h) + add_custom_target(${kernel_file_name}_xilinx + DEPENDS ${bitstream_f} ${CMAKE_BINARY_DIR}/src/common/parameters.h + ) + add_custom_target(${kernel_file_name}_compile_xilinx + DEPENDS ${bitstream_compile} ${CMAKE_BINARY_DIR}/src/common/parameters.h + ) + endforeach () +endfunction() + + +## +# This function will create build targets for the kernels for emulation, reports and synthesis. +# It will use the generate_kernel_replication function to generate a new code file containing the code for all kernels +## +function(generate_kernel_targets_intel) + foreach (kernel_file_name ${ARGN}) + set(base_file "${CMAKE_SOURCE_DIR}/src/device/${kernel_file_name}.cl") + set(source_f "${CMAKE_BINARY_DIR}/src/device/replicated_${kernel_file_name}.cl") + set(report_f ${EXECUTABLE_OUTPUT_PATH}/${kernel_file_name}_report_intel) + set(bitstream_emulate_f ${EXECUTABLE_OUTPUT_PATH}/${kernel_file_name}_emulate.aocx) + set(bitstream_f ${EXECUTABLE_OUTPUT_PATH}/${kernel_file_name}.aocx) + add_custom_command(OUTPUT ${source_f} + COMMAND ${CMAKE_COMMAND} -Dsource_f=${source_f} -Dbase_file=${base_file} -DNUM_REPLICATIONS=${NUM_REPLICATIONS} -P "${CMAKE_SOURCE_DIR}/src/device/generateKernels.cmake" + MAIN_DEPENDENCY ${base_file} + ) + add_custom_command(OUTPUT ${bitstream_emulate_f} + COMMAND ${IntelFPGAOpenCL_AOC} ${source_f} ${COMPILER_INCLUDES} ${AOC_FLAGS} -legacy-emulator -march=emulator + -o ${bitstream_emulate_f} + MAIN_DEPENDENCY ${source_f} + ) + add_custom_command(OUTPUT ${bitstream_f} + COMMAND ${IntelFPGAOpenCL_AOC} ${source_f} ${COMPILER_INCLUDES} ${AOC_FLAGS} -board=${FPGA_BOARD_NAME} + -o ${bitstream_f} + MAIN_DEPENDENCY ${source_f} + ) + add_custom_command(OUTPUT ${report_f} + COMMAND ${IntelFPGAOpenCL_AOC} ${source_f} ${COMPILER_INCLUDES} ${AOC_FLAGS} -rtl -report -board=${FPGA_BOARD_NAME} + -o ${report_f} + MAIN_DEPENDENCY ${source_f} + ) + add_custom_target(${kernel_file_name}_report_intel DEPENDS ${report_f} + DEPENDS ${source_f} "${CMAKE_SOURCE_DIR}/src/device/${kernel_file_name}.cl" ${CMAKE_BINARY_DIR}/src/common/parameters.h + SOURCES ${source_f} "${CMAKE_SOURCE_DIR}/src/device/${kernel_file_name}.cl" ${CMAKE_BINARY_DIR}/src/common/parameters.h) + add_custom_target(${kernel_file_name}_intel DEPENDS ${bitstream_f} + DEPENDS ${source_f} "${CMAKE_SOURCE_DIR}/src/device/${kernel_file_name}.cl" ${CMAKE_BINARY_DIR}/src/common/parameters.h + SOURCES ${source_f} ${CMAKE_BINARY_DIR}/src/common/parameters.h) + add_custom_target(${kernel_file_name}_emulate_intel DEPENDS ${bitstream_emulate_f} + DEPENDS ${source_f} "${CMAKE_SOURCE_DIR}/src/device/${kernel_file_name}.cl" ${CMAKE_BINARY_DIR}/src/common/parameters.h + SOURCES ${source_f} "${CMAKE_SOURCE_DIR}/src/device/${kernel_file_name}.cl" ${CMAKE_BINARY_DIR}/src/common/parameters.h) + endforeach () +endfunction() + +if (INTELFPGAOPENCL_FOUND) + generate_kernel_targets_intel(stream_kernels stream_kernels_single) + add_test(NAME test_emulation_intel COMMAND STREAM_FPGA_intel -f stream_kernels_emulate.aocx) +endif() + +if (VITIS_FOUND) + generate_kernel_targets_xilinx(stream_kernels stream_kernels_single) + add_test(NAME test_emulation_xilinx COMMAND STREAM_FPGA_xilinx -f stream_kernels_emulate.xclbin) +endif() diff --git a/STREAM/src/device/generateKernels.cmake b/STREAM/src/device/generateKernels.cmake new file mode 100644 index 00000000..dd0e2035 --- /dev/null +++ b/STREAM/src/device/generateKernels.cmake @@ -0,0 +1,17 @@ + +## +# This function will replicate the stream kernels to the desired amount given in NUM_REPLICATIONS +# +## +function(generate_kernel_replications) + file(READ "${base_file}" file_content) + file(WRITE "${source_f}" "") + math(EXPR iterator "${ARGV0} - 1") + foreach(number RANGE ${iterator}) + string(REGEX REPLACE "KERNEL_NUMBER" ${number} mod_file_content "${file_content}") + file(APPEND "${source_f}" "${mod_file_content}") + file(APPEND "${source_f}" "\n") + endforeach() +endfunction() + +generate_kernel_replications(${NUM_REPLICATIONS}) diff --git a/STREAM/src/device/generateXilinxSettings.cmake b/STREAM/src/device/generateXilinxSettings.cmake new file mode 100644 index 00000000..3216b61a --- /dev/null +++ b/STREAM/src/device/generateXilinxSettings.cmake @@ -0,0 +1,36 @@ +## +# This function will generate a settings file for the Xilinx Vitis compiler with the desired memory mapping +# +## +function(generate_compiler_settings_xilinx) + file(READ "${base_file}" file_content) + file(WRITE "${settings_f}" "[connectivity]\n") + string(REGEX MATCHALL "nk=[^\n]+\n" number_kernels_string "${file_content}") + string(REGEX REPLACE ";" "" number_kernels_string "${number_kernels_string}") + string(REGEX REPLACE "{TOTAL_KERNEL_NUMBER}" ${NUM_REPLICATIONS} mod_file_content "${number_kernels_string}") + file(APPEND "${settings_f}" "${mod_file_content}") + math(EXPR iterator "${NUM_REPLICATIONS} - 1") + message(STATUS ${iterator}) + string(REGEX MATCHALL "slr=[^\n]+\n" slr_placement_string "${file_content}") + string(REGEX REPLACE ";" "" slr_placement_string "${slr_placement_string}") + foreach(number RANGE ${iterator}) + math(EXPR inc_number "${number} + 1") + math(EXPR number "${number} % 3") + string(REGEX REPLACE "{KERNEL_NUMBER}" ${inc_number} mod_file_content "${slr_placement_string}") + string(REGEX REPLACE "{KERNEL_NUMBER_DEC}" ${number} mod_file_content "${mod_file_content}") + file(APPEND "${settings_f}" "${mod_file_content}") + endforeach() + file(APPEND "${settings_f}" "\n") + string(REGEX MATCHALL "sp=[^\n]+\n" hbm_placement_string "${file_content}") + string(REGEX REPLACE ";" "" hbm_placement_string "${hbm_placement_string}") + foreach(number RANGE ${iterator}) + math(EXPR inc_number "${number} + 1") + string(REGEX REPLACE "{KERNEL_NUMBER}" ${inc_number} mod_file_content "${hbm_placement_string}") + string(REGEX REPLACE "{KERNEL_NUMBER_DEC}" ${number} mod_file_content "${mod_file_content}") + file(APPEND "${settings_f}" "${mod_file_content}") + endforeach() + file(APPEND "${settings_f}" "\n") + +endfunction() + +generate_compiler_settings_xilinx() diff --git a/STREAM/src/device/stream_kernels.cl b/STREAM/src/device/stream_kernels.cl new file mode 100644 index 00000000..b266127f --- /dev/null +++ b/STREAM/src/device/stream_kernels.cl @@ -0,0 +1,59 @@ +/* +STREAM kernels as separate kernels for each operation. + +KERNEL_NUMBER will be replaced by the build script with the ID of the current replication. + That means the kernels will be named copy_0, copy_1, ... up to the number of given replications. +*/ +#include "parameters.h" + +__kernel +__attribute__((uses_global_work_offset(0))) +void copy_KERNEL_NUMBER(__global const DEVICE_ARRAY_DATA_TYPE * restrict in, + __global DEVICE_ARRAY_DATA_TYPE * restrict out, + const uint array_size) { + uint number_elements = array_size / VECTOR_COUNT; + __attribute__((opencl_unroll_hint(UNROLL_COUNT))) + for(uint i = 0; i < number_elements; i++){ + out[i] = in[i]; + } +} + +__kernel +__attribute__((uses_global_work_offset(0))) +void add_KERNEL_NUMBER(__global const DEVICE_ARRAY_DATA_TYPE * restrict in1, + __global const DEVICE_ARRAY_DATA_TYPE * restrict in2, + __global DEVICE_ARRAY_DATA_TYPE * restrict out, + const uint array_size) { + uint number_elements = array_size / VECTOR_COUNT; + __attribute__((opencl_unroll_hint(UNROLL_COUNT))) + for (uint i=0; i -h) +endif() + +if (Vitis_FOUND) + include_directories(${Vitis_INCLUDE_DIRS}) + include_directories(${CMAKE_BINARY_DIR}/src/common) + add_executable(STREAM_FPGA_xilinx ${HOST_SOURCE} ${HEADER_FILES}) + target_link_libraries(STREAM_FPGA_xilinx ${Vitis_LIBRARIES} "${OpenMP_CXX_FLAGS}") + target_compile_definitions(STREAM_FPGA_xilinx PRIVATE -DXILINX_FPGA) + target_compile_options(STREAM_FPGA_xilinx PRIVATE "${OpenMP_CXX_FLAGS}") + add_test(NAME test_xilinx_host_executable COMMAND $ -h) +endif() diff --git a/STREAM/src/host/execution.h b/STREAM/src/host/execution.h new file mode 100644 index 00000000..52785382 --- /dev/null +++ b/STREAM/src/host/execution.h @@ -0,0 +1,88 @@ +/* +Copyright (c) 2019 Marius Meyer + +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 SRC_HOST_EXECUTION_H_ +#define SRC_HOST_EXECUTION_H_ + +/* C++ standard library headers */ +#include +#include +#include +#include + +/* External library headers */ +#include "CL/cl.hpp" +#include "parameters.h" + +// Map keys for execution timings +#define PCIE_WRITE_KEY "PCI write" +#define PCIE_READ_KEY "PCI read" +#define COPY_KEY "Copy" +#define SCALE_KEY "Scale" +#define ADD_KEY "Add" +#define TRIAD_KEY "Triad" + +namespace bm_execution { + + struct ExecutionConfiguration { + cl::Context context; + cl::Device device; + cl::Program program; + uint repetitions; + uint replications; + unsigned arraySize; + bool useMemoryInterleaving; + bool useSingleKernel; + }; + + struct ExecutionTimings { + std::map> timings; + uint arraySize; + }; + + static std::map multiplicatorMap = { + {PCIE_WRITE_KEY, 3.0}, + {PCIE_READ_KEY, 3.0}, + {COPY_KEY, 2.0}, + {SCALE_KEY, 2.0}, + {ADD_KEY, 3.0}, + {TRIAD_KEY, 3.0} + }; + +/** +The actual execution of the benchmark. +This method can be implemented in multiple *.cpp files. This header enables +simple exchange of the different calculation methods. + +@param config struct that contains all necessary information to execute the kernel on the FPGA + + +@return The resulting matrix +*/ + std::shared_ptr + calculate(std::shared_ptr config, + HOST_DATA_TYPE* A, + HOST_DATA_TYPE* B, + HOST_DATA_TYPE* C); + +} // namespace bm_execution + +#endif // SRC_HOST_EXECUTION_H_ diff --git a/STREAM/src/host/execution_default.cpp b/STREAM/src/host/execution_default.cpp new file mode 100644 index 00000000..25ce019e --- /dev/null +++ b/STREAM/src/host/execution_default.cpp @@ -0,0 +1,632 @@ +/* +Copyright (c) 2019 Marius Meyer + +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. +*/ + +/* Related header files */ +#include "execution.h" + +/* C++ standard library headers */ +#include +#include +#include + +/* External library headers */ +#include "CL/cl.hpp" +#include "CL/opencl.h" + +#ifdef INTEL_FPGA +#include "CL/cl_ext_intelfpga.h" +#endif +/* Project's headers */ +#include "setup/fpga_setup.hpp" + +namespace bm_execution { + + void initialize_buffers(const std::shared_ptr &config, unsigned int data_per_kernel, + std::vector &Buffers_A, std::vector &Buffers_B, + std::vector &Buffers_C); + + void initialize_queues_and_kernels(const std::shared_ptr &config, + unsigned int data_per_kernel, const std::vector &Buffers_A, + const std::vector &Buffers_B, + const std::vector &Buffers_C, + std::vector &test_kernels, std::vector ©_kernels, + std::vector &scale_kernels, std::vector &add_kernels, + std::vector &triad_kernels, + std::vector &command_queues); + + void initialize_queues_and_kernels_single(const std::shared_ptr &config, + unsigned int data_per_kernel, const std::vector &Buffers_A, + const std::vector &Buffers_B, + const std::vector &Buffers_C, + std::vector &test_kernels, std::vector ©_kernels, + std::vector &scale_kernels, std::vector &add_kernels, + std::vector &triad_kernels, + HOST_DATA_TYPE* A, + HOST_DATA_TYPE* B, + HOST_DATA_TYPE* C, + std::vector &command_queues); + +/* + Implementation for the single kernel. + @copydoc bm_execution::calculate() + */ + std::shared_ptr + calculate(std::shared_ptr config, + HOST_DATA_TYPE* A, + HOST_DATA_TYPE* B, + HOST_DATA_TYPE* C) { + + unsigned data_per_kernel = config->arraySize/config->replications; + + std::vector Buffers_A; + std::vector Buffers_B; + std::vector Buffers_C; + std::vector test_kernels; + std::vector copy_kernels; + std::vector scale_kernels; + std::vector add_kernels; + std::vector triad_kernels; + std::vector command_queues; + + // + // Setup buffers + // + initialize_buffers(config, data_per_kernel, Buffers_A, Buffers_B, Buffers_C); + + // + // Setup kernels + // + if (config->useSingleKernel) { + initialize_queues_and_kernels_single(config, data_per_kernel, Buffers_A, Buffers_B, Buffers_C, test_kernels, + copy_kernels, scale_kernels, + add_kernels, triad_kernels, A, B, C, command_queues); + } + else { + initialize_queues_and_kernels(config, data_per_kernel, Buffers_A, Buffers_B, Buffers_C, test_kernels, + copy_kernels, scale_kernels, + add_kernels, triad_kernels, command_queues); + } + + // + // Setup counters for runtime measurement + // + std::map> timingMap; + timingMap.insert({PCIE_READ_KEY, std::vector()}); + timingMap.insert({PCIE_WRITE_KEY, std::vector()}); + timingMap.insert({COPY_KEY, std::vector()}); + timingMap.insert({SCALE_KEY, std::vector()}); + timingMap.insert({ADD_KEY, std::vector()}); + timingMap.insert({TRIAD_KEY, std::vector()}); + + // + // Do first test execution + // + std::chrono::time_point startExecution, endExecution; + std::chrono::duration duration; + // Time checking with test kernel + for (int i=0; ireplications; i++) { +#ifdef USE_SVM + ASSERT_CL(clEnqueueSVMMap(command_queues[i](), CL_FALSE, + CL_MAP_READ | CL_MAP_WRITE, + reinterpret_cast(A), + sizeof(HOST_DATA_TYPE) * data_per_kernel, 0, + NULL, NULL)); + +#else + ASSERT_CL(command_queues[i].enqueueWriteBuffer(Buffers_A[i], CL_FALSE, 0, sizeof(HOST_DATA_TYPE)*data_per_kernel, &A[data_per_kernel*i])); +#endif + } + for (int i=0; ireplications; i++) { + ASSERT_CL(command_queues[i].finish()); + } + startExecution = std::chrono::high_resolution_clock::now(); + for (int i=0; ireplications; i++) { + ASSERT_CL(command_queues[i].enqueueTask(test_kernels[i])); + } + for (int i=0; ireplications; i++) { + ASSERT_CL(command_queues[i].finish()); + } + endExecution = std::chrono::high_resolution_clock::now(); + duration = std::chrono::duration_cast> + (endExecution - startExecution); + std::cout << "Each test below will take on the order of " << duration.count() * 1.0e6 << " microseconds." << std::endl; + + std::cout << HLINE; + + std::cout << "WARNING -- The above is only a rough guideline." << std::endl; + std::cout << "For best results, please be sure you know the" << std::endl; + std::cout << "precision of your system timer." << std::endl; + std::cout << HLINE; + + for (int i=0; ireplications; i++) { +#ifdef USE_SVM + ASSERT_CL(clEnqueueSVMUnmap(command_queues[i](), + reinterpret_cast(A), 0, + NULL, NULL)); + +#else + ASSERT_CL(command_queues[i].enqueueReadBuffer(Buffers_A[i], CL_FALSE, 0, sizeof(HOST_DATA_TYPE)*data_per_kernel, &A[data_per_kernel*i])); +#endif + } + for (int i=0; ireplications; i++) { + ASSERT_CL(command_queues[i].finish()); + } + + + // + // Do actual benchmark measurements + // + for (uint r = 0; r < config->repetitions; r++) { +#pragma omp parallel + { +#pragma omp single + startExecution = std::chrono::high_resolution_clock::now(); +#pragma omp for nowait + for (int i = 0; i < config->replications; i++) { +#ifdef USE_SVM + clEnqueueSVMMap(command_queues[i](), CL_FALSE, + CL_MAP_READ | CL_MAP_WRITE, + reinterpret_cast(&A[data_per_kernel * i]), + sizeof(HOST_DATA_TYPE) * data_per_kernel, 0, + NULL, NULL); + clEnqueueSVMMap(command_queues[i](), CL_FALSE, + CL_MAP_READ | CL_MAP_WRITE, + reinterpret_cast(&B[data_per_kernel * i]), + sizeof(HOST_DATA_TYPE) * data_per_kernel, 0, + NULL, NULL); + clEnqueueSVMMap(command_queues[i](), CL_FALSE, + CL_MAP_READ | CL_MAP_WRITE, + reinterpret_cast(&C[data_per_kernel * i]), + sizeof(HOST_DATA_TYPE) * data_per_kernel, 0, + NULL, NULL); +#else + command_queues[i].enqueueWriteBuffer(Buffers_A[i], CL_FALSE, 0, + sizeof(HOST_DATA_TYPE) * data_per_kernel, + &A[data_per_kernel * i]); + command_queues[i].enqueueWriteBuffer(Buffers_B[i], CL_FALSE, 0, + sizeof(HOST_DATA_TYPE) * data_per_kernel, + &B[data_per_kernel * i]); + command_queues[i].enqueueWriteBuffer(Buffers_C[i], CL_FALSE, 0, + sizeof(HOST_DATA_TYPE) * data_per_kernel, + &C[data_per_kernel * i]); +#endif + } +#pragma omp for + for (int i = 0; i < config->replications; i++) { + command_queues[i].finish(); + } +#pragma omp single + { + endExecution = std::chrono::high_resolution_clock::now(); + duration = std::chrono::duration_cast> + (endExecution - startExecution); + timingMap[PCIE_WRITE_KEY].push_back(duration.count()); + + startExecution = std::chrono::high_resolution_clock::now(); + } +#pragma omp for nowait + for (int i = 0; i < config->replications; i++) { + command_queues[i].enqueueTask(copy_kernels[i]); + } +#pragma omp for + for (int i = 0; i < config->replications; i++) { + command_queues[i].finish(); + } +#pragma omp single + { + endExecution = std::chrono::high_resolution_clock::now(); + duration = std::chrono::duration_cast> + (endExecution - startExecution); + timingMap[COPY_KEY].push_back(duration.count()); + + startExecution = std::chrono::high_resolution_clock::now(); + } +#pragma omp for nowait + for (int i = 0; i < config->replications; i++) { + command_queues[i].enqueueTask(scale_kernels[i]); + } +#pragma omp for + for (int i = 0; i < config->replications; i++) { + command_queues[i].finish(); + } +#pragma omp single + { + endExecution = std::chrono::high_resolution_clock::now(); + duration = std::chrono::duration_cast> + (endExecution - startExecution); + timingMap[SCALE_KEY].push_back(duration.count()); + + startExecution = std::chrono::high_resolution_clock::now(); + } +#pragma omp for nowait + for (int i = 0; i < config->replications; i++) { + command_queues[i].enqueueTask(add_kernels[i]); + } +#pragma omp for + for (int i = 0; i < config->replications; i++) { + command_queues[i].finish(); + } +#pragma omp single + { + endExecution = std::chrono::high_resolution_clock::now(); + duration = std::chrono::duration_cast> + (endExecution - startExecution); + timingMap[ADD_KEY].push_back(duration.count()); + + startExecution = std::chrono::high_resolution_clock::now(); + } +#pragma omp for nowait + for (int i = 0; i < config->replications; i++) { + command_queues[i].enqueueTask(triad_kernels[i]); + } +#pragma omp for + for (int i = 0; i < config->replications; i++) { + command_queues[i].finish(); + } +#pragma omp single + { + endExecution = std::chrono::high_resolution_clock::now(); + duration = std::chrono::duration_cast> + (endExecution - startExecution); + timingMap[TRIAD_KEY].push_back(duration.count()); + + startExecution = std::chrono::high_resolution_clock::now(); + } +#pragma omp for nowait + for (int i = 0; i < config->replications; i++) { +#ifdef USE_SVM + clEnqueueSVMUnmap(command_queues[i](), + reinterpret_cast(&A[data_per_kernel * i]), 0, + NULL, NULL); + clEnqueueSVMUnmap(command_queues[i](), + reinterpret_cast(&B[data_per_kernel * i]), 0, + NULL, NULL); + clEnqueueSVMUnmap(command_queues[i](), + reinterpret_cast(&C[data_per_kernel * i]), 0, + NULL, NULL); +#else + command_queues[i].enqueueReadBuffer(Buffers_A[i], CL_FALSE, 0, + sizeof(HOST_DATA_TYPE) * data_per_kernel, + &A[data_per_kernel * i]); + command_queues[i].enqueueReadBuffer(Buffers_B[i], CL_FALSE, 0, + sizeof(HOST_DATA_TYPE) * data_per_kernel, + &B[data_per_kernel * i]); + command_queues[i].enqueueReadBuffer(Buffers_C[i], CL_FALSE, 0, + sizeof(HOST_DATA_TYPE) * data_per_kernel, + &C[data_per_kernel * i]); +#endif + } +#pragma omp for + for (int i = 0; i < config->replications; i++) { + command_queues[i].finish(); + } +#pragma omp single + { + endExecution = std::chrono::high_resolution_clock::now(); + duration = std::chrono::duration_cast> + (endExecution - startExecution); + timingMap[PCIE_READ_KEY].push_back(duration.count()); + } + } + } + + std::shared_ptr result(new ExecutionTimings{ + timingMap, + config->arraySize + }); + return result; + } + + void initialize_queues_and_kernels(const std::shared_ptr &config, + unsigned int data_per_kernel, const std::vector &Buffers_A, + const std::vector &Buffers_B, + const std::vector &Buffers_C, + std::vector &test_kernels, std::vector ©_kernels, + std::vector &scale_kernels, std::vector &add_kernels, + std::vector &triad_kernels, + std::vector &command_queues) { + int err; + for (int i=0; i < config->replications; i++) { + // create the kernels + cl::Kernel testkernel(config->program, ("scale_" + std::to_string(i)).c_str(), &err); + ASSERT_CL(err); + cl::Kernel copykernel(config->program, ("copy_" + std::to_string(i)).c_str(), &err); + ASSERT_CL(err); + cl::Kernel scalekernel(config->program, ("scale_" + std::to_string(i)).c_str(), &err); + ASSERT_CL(err); + cl::Kernel addkernel(config->program, ("add_" + std::to_string(i)).c_str(), &err); + ASSERT_CL(err); + cl::Kernel triadkernel(config->program, ("triad_" + std::to_string(i)).c_str(), &err); + ASSERT_CL(err); + + HOST_DATA_TYPE scalar = 3.0; + HOST_DATA_TYPE test_scalar = 2.0E0; + //prepare kernels + err = testkernel.setArg(0, Buffers_A[i]); + ASSERT_CL(err); + err = testkernel.setArg(1, Buffers_A[i]); + ASSERT_CL(err); + err = testkernel.setArg(2, test_scalar); + ASSERT_CL(err); + err = testkernel.setArg(3, data_per_kernel); + ASSERT_CL(err); + //set arguments of copy kernel + err = copykernel.setArg(0, Buffers_A[i]); + ASSERT_CL(err); + err = copykernel.setArg(1, Buffers_C[i]); + ASSERT_CL(err); + err = copykernel.setArg(2, data_per_kernel); + ASSERT_CL(err); + //set arguments of scale kernel + err = scalekernel.setArg(0, Buffers_C[i]); + ASSERT_CL(err); + err = scalekernel.setArg(1, Buffers_B[i]); + ASSERT_CL(err); + err = scalekernel.setArg(2, scalar); + ASSERT_CL(err); + err = scalekernel.setArg(3, data_per_kernel); + ASSERT_CL(err); + //set arguments of add kernel + err = addkernel.setArg(0, Buffers_A[i]); + ASSERT_CL(err); + err = addkernel.setArg(1, Buffers_B[i]); + ASSERT_CL(err); + err = addkernel.setArg(2, Buffers_C[i]); + ASSERT_CL(err); + err = addkernel.setArg(3, data_per_kernel); + ASSERT_CL(err); + //set arguments of triad kernel + err = triadkernel.setArg(0, Buffers_B[i]); + ASSERT_CL(err); + err = triadkernel.setArg(1, Buffers_C[i]); + ASSERT_CL(err); + err = triadkernel.setArg(2, Buffers_A[i]); + ASSERT_CL(err); + err = triadkernel.setArg(3, scalar); + ASSERT_CL(err); + err = triadkernel.setArg(4, data_per_kernel); + ASSERT_CL(err); + + command_queues.push_back(cl::CommandQueue(config->context)); + test_kernels.push_back(testkernel); + copy_kernels.push_back(copykernel); + scale_kernels.push_back(scalekernel); + add_kernels.push_back(addkernel); + triad_kernels.push_back(triadkernel); + } + } + + void initialize_queues_and_kernels_single(const std::shared_ptr &config, + unsigned int data_per_kernel, const std::vector &Buffers_A, + const std::vector &Buffers_B, + const std::vector &Buffers_C, + std::vector &test_kernels, std::vector ©_kernels, + std::vector &scale_kernels, std::vector &add_kernels, + std::vector &triad_kernels, + HOST_DATA_TYPE* A, + HOST_DATA_TYPE* B, + HOST_DATA_TYPE* C, + std::vector &command_queues) { + int err; + for (int i=0; i < config->replications; i++) { +#ifdef INTEL_FPGA + // create the kernels + cl::Kernel testkernel(config->program, ("calc_" + std::to_string(i)).c_str(), &err); + ASSERT_CL(err); + cl::Kernel copykernel(config->program, ("calc_" + std::to_string(i)).c_str(), &err); + ASSERT_CL(err); + cl::Kernel scalekernel(config->program, ("calc_" + std::to_string(i)).c_str(), &err); + ASSERT_CL(err); + cl::Kernel addkernel(config->program, ("calc_" + std::to_string(i)).c_str(), &err); + ASSERT_CL(err); + cl::Kernel triadkernel(config->program, ("calc_" + std::to_string(i)).c_str(), &err); + ASSERT_CL(err); +#endif +#ifdef XILINX_FPGA + // create the kernels + cl::Kernel testkernel(config->program, ("calc_0:{calc_0_" + std::to_string(i+1) + "}").c_str(), &err); + ASSERT_CL(err); + cl::Kernel copykernel(config->program, ("calc_0:{calc_0_" + std::to_string(i+1) + "}").c_str(), &err); + ASSERT_CL(err); + cl::Kernel scalekernel(config->program, ("calc_0:{calc_0_" + std::to_string(i+1) + "}").c_str(), &err); + ASSERT_CL(err); + cl::Kernel addkernel(config->program, ("calc_0:{calc_0_" + std::to_string(i+1) + "}").c_str(), &err); + ASSERT_CL(err); + cl::Kernel triadkernel(config->program, ("calc_0:{calc_0_" + std::to_string(i+1) + "}").c_str(), &err); + ASSERT_CL(err); +#endif + HOST_DATA_TYPE scalar = 3.0; + HOST_DATA_TYPE test_scalar = 2.0E0; + //prepare kernels +#ifdef USE_SVM + err = clSetKernelArgSVMPointer(testkernel(), 0, + reinterpret_cast(A)); + ASSERT_CL(err); + err = clSetKernelArgSVMPointer(testkernel(), 1, + reinterpret_cast(A)); + ASSERT_CL(err); + err = clSetKernelArgSVMPointer(testkernel(), 2, + reinterpret_cast(A)); + ASSERT_CL(err); +#else + err = testkernel.setArg(0, Buffers_A[i]); + ASSERT_CL(err); + err = testkernel.setArg(1, Buffers_A[i]); + ASSERT_CL(err); + err = testkernel.setArg(2, Buffers_A[i]); + ASSERT_CL(err); +#endif + err = testkernel.setArg(3, test_scalar); + ASSERT_CL(err); + err = testkernel.setArg(4, data_per_kernel); + ASSERT_CL(err); + err = testkernel.setArg(5, SCALE_KERNEL_TYPE); + ASSERT_CL(err); + + //set arguments of copy kernel +#ifdef USE_SVM + err = clSetKernelArgSVMPointer(copykernel(), 0, + reinterpret_cast(A)); + ASSERT_CL(err); + err = clSetKernelArgSVMPointer(copykernel(), 1, + reinterpret_cast(A)); + ASSERT_CL(err); + err = clSetKernelArgSVMPointer(copykernel(), 2, + reinterpret_cast(C)); + ASSERT_CL(err); +#else + err = copykernel.setArg(0, Buffers_A[i]); + ASSERT_CL(err); + err = copykernel.setArg(1, Buffers_A[i]); + ASSERT_CL(err); + err = copykernel.setArg(2, Buffers_C[i]); + ASSERT_CL(err); +#endif + err = copykernel.setArg(3, 1.0f); + ASSERT_CL(err); + err = copykernel.setArg(4, data_per_kernel); + ASSERT_CL(err); + err = copykernel.setArg(5, COPY_KERNEL_TYPE); + ASSERT_CL(err); + //set arguments of scale kernel +#ifdef USE_SVM + err = clSetKernelArgSVMPointer(scalekernel(), 0, + reinterpret_cast(C)); + ASSERT_CL(err); + err = clSetKernelArgSVMPointer(scalekernel(), 1, + reinterpret_cast(C)); + ASSERT_CL(err); + err = clSetKernelArgSVMPointer(scalekernel(), 2, + reinterpret_cast(B)); + ASSERT_CL(err); +#else + err = scalekernel.setArg(0, Buffers_C[i]); + ASSERT_CL(err); + err = scalekernel.setArg(1, Buffers_C[i]); + ASSERT_CL(err); + err = scalekernel.setArg(2, Buffers_B[i]); + ASSERT_CL(err); +#endif + err = scalekernel.setArg(3, scalar); + ASSERT_CL(err); + err = scalekernel.setArg(4, data_per_kernel); + ASSERT_CL(err); + err = scalekernel.setArg(5, SCALE_KERNEL_TYPE); + ASSERT_CL(err); + //set arguments of add kernel +#ifdef USE_SVM + err = clSetKernelArgSVMPointer(addkernel(), 0, + reinterpret_cast(A)); + ASSERT_CL(err); + err = clSetKernelArgSVMPointer(addkernel(), 1, + reinterpret_cast(B)); + ASSERT_CL(err); + err = clSetKernelArgSVMPointer(addkernel(), 2, + reinterpret_cast(C)); + ASSERT_CL(err); +#else + err = addkernel.setArg(0, Buffers_A[i]); + ASSERT_CL(err); + err = addkernel.setArg(1, Buffers_B[i]); + ASSERT_CL(err); + err = addkernel.setArg(2, Buffers_C[i]); + ASSERT_CL(err); +#endif + err = addkernel.setArg(3, 1.0f); + ASSERT_CL(err); + err = addkernel.setArg(4, data_per_kernel); + ASSERT_CL(err); + err = addkernel.setArg(5, ADD_KERNEL_TYPE); + ASSERT_CL(err); + //set arguments of triad kernel +#ifdef USE_SVM + err = clSetKernelArgSVMPointer(triadkernel(), 0, + reinterpret_cast(C)); + ASSERT_CL(err); + err = clSetKernelArgSVMPointer(triadkernel(), 1, + reinterpret_cast(B)); + ASSERT_CL(err); + err = clSetKernelArgSVMPointer(triadkernel(), 2, + reinterpret_cast(A)); + ASSERT_CL(err); +#else + err = triadkernel.setArg(0, Buffers_C[i]); + ASSERT_CL(err); + err = triadkernel.setArg(1, Buffers_B[i]); + ASSERT_CL(err); + err = triadkernel.setArg(2, Buffers_A[i]); + ASSERT_CL(err); +#endif + err = triadkernel.setArg(3, scalar); + ASSERT_CL(err); + err = triadkernel.setArg(4, data_per_kernel); + ASSERT_CL(err); + err = triadkernel.setArg(5, TRIAD_KERNEL_TYPE); + ASSERT_CL(err); + + command_queues.push_back(cl::CommandQueue(config->context)); + test_kernels.push_back(testkernel); + copy_kernels.push_back(copykernel); + scale_kernels.push_back(scalekernel); + add_kernels.push_back(addkernel); + triad_kernels.push_back(triadkernel); + } + } + + void initialize_buffers(const std::shared_ptr &config, unsigned int data_per_kernel, + std::vector &Buffers_A, std::vector &Buffers_B, + std::vector &Buffers_C) { + if (!config->useMemoryInterleaving) { + //Create Buffers for input and output + for (int i=0; i < config->replications; i++) { +#ifdef INTEL_FPGA + if (config->useSingleKernel) { + //Create Buffers for input and output + Buffers_A.push_back(cl::Buffer(config->context, CL_MEM_READ_WRITE | ((i + 1) << 16), sizeof(HOST_DATA_TYPE)*data_per_kernel)); + Buffers_B.push_back(cl::Buffer(config->context, CL_MEM_READ_WRITE | ((i + 1) << 16), sizeof(HOST_DATA_TYPE)*data_per_kernel)); + Buffers_C.push_back(cl::Buffer(config->context, CL_MEM_READ_WRITE | ((i + 1) << 16), sizeof(HOST_DATA_TYPE)*data_per_kernel)); + } + else { + //Create Buffers for input and output + Buffers_A.push_back(cl::Buffer(config->context, CL_MEM_READ_WRITE | CL_CHANNEL_1_INTELFPGA, sizeof(HOST_DATA_TYPE)*data_per_kernel)); + Buffers_B.push_back(cl::Buffer(config->context, CL_MEM_READ_WRITE | CL_CHANNEL_3_INTELFPGA, sizeof(HOST_DATA_TYPE)*data_per_kernel)); + Buffers_C.push_back(cl::Buffer(config->context, CL_MEM_READ_WRITE | CL_CHANNEL_2_INTELFPGA, sizeof(HOST_DATA_TYPE)*data_per_kernel)); + } +#endif +#ifdef XILINX_FPGA + Buffers_A.push_back(cl::Buffer(config->context, CL_MEM_READ_WRITE, sizeof(HOST_DATA_TYPE)*data_per_kernel)); + Buffers_B.push_back(cl::Buffer(config->context, CL_MEM_READ_WRITE, sizeof(HOST_DATA_TYPE)*data_per_kernel)); + Buffers_C.push_back(cl::Buffer(config->context, CL_MEM_READ_WRITE, sizeof(HOST_DATA_TYPE)*data_per_kernel)); +#endif + } + + } else { + for (int i=0; i < config->replications; i++) { + //Create Buffers for input and output + Buffers_A.push_back(cl::Buffer(config->context, CL_MEM_READ_WRITE, sizeof(HOST_DATA_TYPE)*data_per_kernel)); + Buffers_B.push_back(cl::Buffer(config->context, CL_MEM_READ_WRITE, sizeof(HOST_DATA_TYPE)*data_per_kernel)); + Buffers_C.push_back(cl::Buffer(config->context, CL_MEM_READ_WRITE, sizeof(HOST_DATA_TYPE)*data_per_kernel)); + } + } + } + +} // namespace bm_execution diff --git a/STREAM/src/host/main.cpp b/STREAM/src/host/main.cpp new file mode 100644 index 00000000..2d9395a2 --- /dev/null +++ b/STREAM/src/host/main.cpp @@ -0,0 +1,79 @@ +// +// Created by Marius Meyer on 04.12.19. +// + +#include "stream_functionality.hpp" +#include "CL/opencl.h" + +/** +The program entry point +*/ +int +main(int argc, char *argv[]) { + // Setup benchmark + std::shared_ptr programSettings = + parseProgramParameters(argc, argv); + fpga_setup::setupEnvironmentAndClocks(); + std::vector usedDevice = + fpga_setup::selectFPGADevice(programSettings->defaultPlatform, + programSettings->defaultDevice); + cl::Context context = cl::Context(usedDevice); + cl::Program program = fpga_setup::fpgaSetup(&context, usedDevice, + &programSettings->kernelFileName); + + printFinalConfiguration(programSettings, usedDevice[0]); + + std::shared_ptr config( + new bm_execution::ExecutionConfiguration { + context, usedDevice[0], program, + programSettings->numRepetitions, + programSettings->kernelReplications, + programSettings->streamArraySize, + programSettings->useMemoryInterleaving, + programSettings->useSingleKernel + }); + + HOST_DATA_TYPE *A, *B, *C; +#ifdef INTEL_FPGA +#ifdef USE_SVM + A = reinterpret_cast( + clSVMAlloc(context(), 0 , + programSettings->streamArraySize * sizeof(HOST_DATA_TYPE), 1024)); + B = reinterpret_cast( + clSVMAlloc(context(), 0 , + programSettings->streamArraySize * sizeof(HOST_DATA_TYPE), 1024)); + C = reinterpret_cast( + clSVMAlloc(context(), 0 , + programSettings->streamArraySize * sizeof(HOST_DATA_TYPE), 1024)); +#else + posix_memalign(reinterpret_cast(&A), 64, programSettings->streamArraySize * sizeof(HOST_DATA_TYPE)); + posix_memalign(reinterpret_cast(&B), 64, programSettings->streamArraySize * sizeof(HOST_DATA_TYPE)); + posix_memalign(reinterpret_cast(&C), 64, programSettings->streamArraySize * sizeof(HOST_DATA_TYPE)); +#endif +#endif +#ifdef XILINX_FPGA + posix_memalign(reinterpret_cast(&A), 4096, programSettings->streamArraySize * sizeof(HOST_DATA_TYPE)); + posix_memalign(reinterpret_cast(&B), 4096, programSettings->streamArraySize * sizeof(HOST_DATA_TYPE)); + posix_memalign(reinterpret_cast(&C), 4096, programSettings->streamArraySize * sizeof(HOST_DATA_TYPE)); +#endif + generateInputData(A, B, C, programSettings->streamArraySize); + + auto timing = bm_execution::calculate(config, A, B, C); + + double error = checkSTREAMResult(A, B, C, programSettings->numRepetitions, programSettings->streamArraySize); + +#ifdef USE_SVM + clSVMFree(context(), reinterpret_cast(A)); + clSVMFree(context(), reinterpret_cast(B)); + clSVMFree(context(), reinterpret_cast(C)); +#else + free(A); + free(B); + free(C); +#endif + + printResults(timing); + + return error < 1 ? 0 : 1; +} + diff --git a/STREAM/src/host/setup/fpga_setup.cpp b/STREAM/src/host/setup/fpga_setup.cpp new file mode 100644 index 00000000..07845901 --- /dev/null +++ b/STREAM/src/host/setup/fpga_setup.cpp @@ -0,0 +1,273 @@ +// +// Created by Marius Meyer on 04.12.19. +// + +#include "fpga_setup.hpp" + +#include +#include +#include +#include +#include +#include + +/* External libraries */ +#include "parameters.h" + +namespace fpga_setup { + + /** +Converts the reveived OpenCL error to a string + +@param err The OpenCL error code + +@return The string representation of the OpenCL error code +*/ + std::string + getCLErrorString(cl_int const err) { + switch (err) { + CL_ERR_TO_STR(CL_DEVICE_NOT_FOUND); + CL_ERR_TO_STR(CL_DEVICE_NOT_AVAILABLE); + CL_ERR_TO_STR(CL_COMPILER_NOT_AVAILABLE); + CL_ERR_TO_STR(CL_MEM_OBJECT_ALLOCATION_FAILURE); + CL_ERR_TO_STR(CL_OUT_OF_RESOURCES); + CL_ERR_TO_STR(CL_OUT_OF_HOST_MEMORY); + CL_ERR_TO_STR(CL_PROFILING_INFO_NOT_AVAILABLE); + CL_ERR_TO_STR(CL_MEM_COPY_OVERLAP); + CL_ERR_TO_STR(CL_IMAGE_FORMAT_MISMATCH); + CL_ERR_TO_STR(CL_IMAGE_FORMAT_NOT_SUPPORTED); + CL_ERR_TO_STR(CL_BUILD_PROGRAM_FAILURE); + CL_ERR_TO_STR(CL_MAP_FAILURE); + CL_ERR_TO_STR(CL_MISALIGNED_SUB_BUFFER_OFFSET); + CL_ERR_TO_STR(CL_EXEC_STATUS_ERROR_FOR_EVENTS_IN_WAIT_LIST); + CL_ERR_TO_STR(CL_KERNEL_ARG_INFO_NOT_AVAILABLE); + CL_ERR_TO_STR(CL_INVALID_VALUE); + CL_ERR_TO_STR(CL_INVALID_DEVICE_TYPE); + CL_ERR_TO_STR(CL_INVALID_PLATFORM); + CL_ERR_TO_STR(CL_INVALID_DEVICE); + CL_ERR_TO_STR(CL_INVALID_CONTEXT); + CL_ERR_TO_STR(CL_INVALID_QUEUE_PROPERTIES); + CL_ERR_TO_STR(CL_INVALID_COMMAND_QUEUE); + CL_ERR_TO_STR(CL_INVALID_HOST_PTR); + CL_ERR_TO_STR(CL_INVALID_MEM_OBJECT); + CL_ERR_TO_STR(CL_INVALID_IMAGE_FORMAT_DESCRIPTOR); + CL_ERR_TO_STR(CL_INVALID_IMAGE_SIZE); + CL_ERR_TO_STR(CL_INVALID_SAMPLER); + CL_ERR_TO_STR(CL_INVALID_BINARY); + CL_ERR_TO_STR(CL_INVALID_BUILD_OPTIONS); + CL_ERR_TO_STR(CL_INVALID_PROGRAM); + CL_ERR_TO_STR(CL_INVALID_PROGRAM_EXECUTABLE); + CL_ERR_TO_STR(CL_INVALID_KERNEL_NAME); + CL_ERR_TO_STR(CL_INVALID_KERNEL_DEFINITION); + CL_ERR_TO_STR(CL_INVALID_KERNEL); + CL_ERR_TO_STR(CL_INVALID_ARG_INDEX); + CL_ERR_TO_STR(CL_INVALID_ARG_VALUE); + CL_ERR_TO_STR(CL_INVALID_ARG_SIZE); + CL_ERR_TO_STR(CL_INVALID_KERNEL_ARGS); + CL_ERR_TO_STR(CL_INVALID_WORK_DIMENSION); + CL_ERR_TO_STR(CL_INVALID_WORK_GROUP_SIZE); + CL_ERR_TO_STR(CL_INVALID_WORK_ITEM_SIZE); + CL_ERR_TO_STR(CL_INVALID_GLOBAL_OFFSET); + CL_ERR_TO_STR(CL_INVALID_EVENT_WAIT_LIST); + CL_ERR_TO_STR(CL_INVALID_EVENT); + CL_ERR_TO_STR(CL_INVALID_OPERATION); + CL_ERR_TO_STR(CL_INVALID_GL_OBJECT); + CL_ERR_TO_STR(CL_INVALID_BUFFER_SIZE); + CL_ERR_TO_STR(CL_INVALID_MIP_LEVEL); + CL_ERR_TO_STR(CL_INVALID_GLOBAL_WORK_SIZE); + CL_ERR_TO_STR(CL_COMPILE_PROGRAM_FAILURE); + CL_ERR_TO_STR(CL_LINKER_NOT_AVAILABLE); + CL_ERR_TO_STR(CL_LINK_PROGRAM_FAILURE); + CL_ERR_TO_STR(CL_DEVICE_PARTITION_FAILED); + CL_ERR_TO_STR(CL_INVALID_PROPERTY); + CL_ERR_TO_STR(CL_INVALID_IMAGE_DESCRIPTOR); + CL_ERR_TO_STR(CL_INVALID_COMPILER_OPTIONS); + CL_ERR_TO_STR(CL_INVALID_LINKER_OPTIONS); + CL_ERR_TO_STR(CL_INVALID_DEVICE_PARTITION_COUNT); + + default: + return "UNKNOWN ERROR CODE"; + } + } + +/** +Check the OpenCL return code for errors. +If an error is detected, it will be printed and the programm execution is +stopped. + +@param err The OpenCL error code +*/ + void + handleClReturnCode(cl_int const err, std::string const file, + int const line) { + if (err != CL_SUCCESS) { + std::string err_string = getCLErrorString(err); + std::cerr << "ERROR in OpenCL library detected! Aborting." + << std::endl << file << ":" << line << ": " << err_string + << std::endl; + exit(err); + } + } + +/** +Sets up the given FPGA with the kernel in the provided file. + +@param context The context used for the program +@param program The devices used for the program +@param usedKernelFile The path to the kernel file +@return The program that is used to create the benchmark kernels +*/ + cl::Program + fpgaSetup(const cl::Context *context, std::vector deviceList, + const std::string *usedKernelFile) { + int err; + + std::cout << HLINE; + std::cout << "FPGA Setup:" << usedKernelFile->c_str() << std::endl; + + // Open file stream if possible + std::ifstream aocxStream(usedKernelFile->c_str(), std::ifstream::binary); + if (!aocxStream.is_open()) { + std::cerr << "Not possible to open from given file!" << std::endl; + } + + // Read in file contents and create program from binaries + std::string prog(std::istreambuf_iterator(aocxStream), + (std::istreambuf_iterator())); + aocxStream.seekg(0, aocxStream.end); + unsigned file_size = aocxStream.tellg(); + aocxStream.seekg(0, aocxStream.beg); + char *buf = new char[file_size]; + aocxStream.read(buf, file_size); + + cl::Program::Binaries mybinaries; + mybinaries.push_back({buf, file_size}); + + // Create the Program from the AOCX file. + cl::Program program(*context, deviceList, mybinaries, NULL, &err); + ASSERT_CL(err); + std::cout << "Prepared FPGA successfully for global Execution!" << + std::endl; + std::cout << HLINE; + return program; + } + +/** +Sets up the C++ environment by configuring std::cout and checking the clock +granularity using bm_helper::checktick() +*/ + void + setupEnvironmentAndClocks() { + std::cout << std::setprecision(5) << std::scientific; + + std::cout << HLINE; + std::cout << "General setup:" << std::endl; + + // Check clock granularity and output result + std::cout << "C++ high resolution clock is used." << std::endl; + std::cout << "The clock precision seems to be " + << static_cast + (std::chrono::high_resolution_clock::period::num) / + std::chrono::high_resolution_clock::period::den * 10e9 + << "ns" << std::endl; + + std::cout << HLINE; + } + + +/** +Searches an selects an FPGA device using the CL library functions. +If multiple platforms or devices are given, the user will be prompted to +choose a device. + +@param defaultPlatform The index of the platform that has to be used. If a + value < 0 is given, the platform can be chosen + interactively +@param defaultDevice The index of the device that has to be used. If a + value < 0 is given, the device can be chosen + interactively + +@return A list containing a single selected device +*/ + std::vector + selectFPGADevice(int defaultPlatform, int defaultDevice) { + // Integer used to store return codes of OpenCL library calls + int err; + + std::vector platformList; + err = cl::Platform::get(&platformList); + ASSERT_CL(err); + + // Choose the target platform + int chosenPlatformId = 0; + if (defaultPlatform >= 0) { + if (defaultPlatform < platformList.size()) { + chosenPlatformId = defaultPlatform; + } else { + std::cerr << "Default platform " << defaultPlatform + << " can not be used. Available platforms: " + << platformList.size() << std::endl; + exit(1); + } + } else if (platformList.size() > 1) { + std::cout << + "Multiple platforms have been found. Select the platform by"\ + " typing a number:" << std::endl; + for (int platformId = 0; + platformId < platformList.size(); platformId++) { + std::cout << platformId << ") " << + platformList[platformId].getInfo() << + std::endl; + } + std::cout << "Enter platform id [0-" << platformList.size() - 1 + << "]:"; + std::cin >> chosenPlatformId; + } + cl::Platform platform = platformList[chosenPlatformId]; + std::cout << "Selected Platform: " + << platform.getInfo() << std::endl; + + std::vector deviceList; + err = platform.getDevices(CL_DEVICE_TYPE_ACCELERATOR, &deviceList); + ASSERT_CL(err); + + // Choose taget device + int chosenDeviceId = 0; + if (defaultDevice >= 0) { + if (defaultDevice < deviceList.size()) { + chosenDeviceId = defaultDevice; + } else { + std::cerr << "Default platform " << defaultDevice + << " can not be used. Available platforms: " + << deviceList.size() << std::endl; + exit(1); + } + } else if (deviceList.size() > 1) { + std::cout << + "Multiple devices have been found. Select the platform by"\ + " typing a number:" << std::endl; + for (int deviceId = 0; + deviceId < deviceList.size(); deviceId++) { + std::cout << deviceId << ") " << + deviceList[deviceId].getInfo() << + std::endl; + } + std::cout << "Enter device id [0-" << deviceList.size() - 1 << "]:"; + std::cin >> chosenDeviceId; + } + std::vector chosenDeviceList; + chosenDeviceList.push_back(deviceList[chosenDeviceId]); + + // Give selection summary + std::cout << HLINE; + std::cout << "Selection summary:" << std::endl; + std::cout << "Platform Name: " << + platform.getInfo() << std::endl; + std::cout << "Device Name: " << + chosenDeviceList[0].getInfo() << std::endl; + std::cout << HLINE; + + return chosenDeviceList; + } + +} // namespace fpga_setup \ No newline at end of file diff --git a/STREAM/src/host/setup/fpga_setup.hpp b/STREAM/src/host/setup/fpga_setup.hpp new file mode 100644 index 00000000..c1f78b0c --- /dev/null +++ b/STREAM/src/host/setup/fpga_setup.hpp @@ -0,0 +1,108 @@ +/* +Copyright (c) 2019 Marius Meyer + +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 SRC_HOST_FPGA_SETUP_H_ +#define SRC_HOST_FPGA_SETUP_H_ + +#include +#include +#include +#include +#include +#include + +/* External libraries */ +#include "CL/cl.hpp" + +/** +Makro to convert the error integer representation to its string representation +Source: https://gist.github.com/allanmac/9328bb2d6a99b86883195f8f78fd1b93 +*/ +#define CL_ERR_TO_STR(err) case err: return #err + +namespace fpga_setup { + + /** +Converts the reveived OpenCL error to a string + +@param err The OpenCL error code + +@return The string representation of the OpenCL error code +*/ + std::string + getCLErrorString(cl_int const err); + +/** +Check the OpenCL return code for errors. +If an error is detected, it will be printed and the programm execution is +stopped. + +@param err The OpenCL error code +*/ + void + handleClReturnCode(cl_int const err, std::string const file, + int const line); + +/** +Makro that enables checks for OpenCL errors with handling of the file and +line number. +*/ +#define ASSERT_CL(err) fpga_setup::handleClReturnCode(err, __FILE__, __LINE__) + +/** +Sets up the given FPGA with the kernel in the provided file. + +@param context The context used for the program +@param program The devices used for the program +@param usedKernelFile The path to the kernel file +@return The program that is used to create the benchmark kernels +*/ + cl::Program + fpgaSetup(const cl::Context *context, std::vector deviceList, + const std::string *usedKernelFile); + +/** +Sets up the C++ environment by configuring std::cout and checking the clock +granularity using bm_helper::checktick() +*/ + void + setupEnvironmentAndClocks(); + + +/** +Searches an selects an FPGA device using the CL library functions. +If multiple platforms or devices are given, the user will be prompted to +choose a device. + +@param defaultPlatform The index of the platform that has to be used. If a + value < 0 is given, the platform can be chosen + interactively +@param defaultDevice The index of the device that has to be used. If a + value < 0 is given, the device can be chosen + interactively + +@return A list containing a single selected device +*/ + std::vector + selectFPGADevice(int defaultPlatform, int defaultDevice); + +} // namespace fpga_setup +#endif // SRC_HOST_FPGA_SETUP_H_ diff --git a/STREAM/src/host/stream_functionality.cpp b/STREAM/src/host/stream_functionality.cpp new file mode 100644 index 00000000..5b548bfb --- /dev/null +++ b/STREAM/src/host/stream_functionality.cpp @@ -0,0 +1,271 @@ +// +// Created by Marius Meyer on 04.12.19. +// + +/* +Copyright (c) 2019 Marius Meyer + +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. +*/ + +#include "stream_functionality.hpp" + +/* C++ standard library headers */ +#include +#include + +/* Project's headers */ +#include "execution.h" +#include "cxxopts.hpp" +#include "setup/fpga_setup.hpp" +#include "parameters.h" + +/** +Parses and returns program options using the cxxopts library. +Supports the following parameters: + - file name of the FPGA kernel file (-f,--file) + - number of repetitions (-n) + - number of kernel replications (-r) + - data size (-d) + - use memory interleaving +@see https://github.com/jarro2783/cxxopts + +@return program settings that are created from the given program arguments +*/ +std::shared_ptr +parseProgramParameters(int argc, char *argv[]) { + // Defining and parsing program options + cxxopts::Options options(argv[0], PROGRAM_DESCRIPTION); + options.add_options() + ("f,file", "Kernel file name", cxxopts::value()) + ("n", "Number of repetitions", + cxxopts::value()->default_value(std::to_string(DEFAULT_REPETITIONS))) + ("s", "Size of the data arrays", + cxxopts::value()->default_value(std::to_string(DEFAULT_ARRAY_LENGTH))) + ("r", "Number of kernel replications used", + cxxopts::value()->default_value(std::to_string(NUM_KERNEL_REPLICATIONS))) +#ifdef INTEL_FPGA + ("i", "Use memory Interleaving") +#endif + ("single-kernel", "Use the single kernel implementation") + ("device", "Index of the device that has to be used. If not given you "\ + "will be asked which device to use if there are multiple devices "\ + "available.", cxxopts::value()->default_value(std::to_string(DEFAULT_DEVICE))) + ("platform", "Index of the platform that has to be used. If not given "\ + "you will be asked which platform to use if there are multiple "\ + "platforms available.", + cxxopts::value()->default_value(std::to_string(DEFAULT_PLATFORM))) + ("h,help", "Print this help"); + cxxopts::ParseResult result = options.parse(argc, argv); + + if (result.count("h")) { + // Just print help when argument is given + std::cout << options.help() << std::endl; + exit(0); + } + // Check parsed options and handle special cases + if (result.count("f") <= 0) { + // Path to the kernel file is mandatory - exit if not given! + std::cerr << "Kernel file must be given! Aborting" << std::endl; + std::cout << options.help() << std::endl; + exit(1); + } + + // Create program settings from program arguments + std::shared_ptr sharedSettings( + new ProgramSettings{result["n"].as(), + result["s"].as(), + result["r"].as(), +#ifdef INTEL_FPGA + static_cast(result.count("i")), +#else + false, +#endif + result["platform"].as(), + result["device"].as(), + result["f"].as(), + static_cast(result.count("single-kernel"))}); + return sharedSettings; +} + + +/** +Prints the execution results to stdout + +@param results The execution results +*/ +void +printResults(std::shared_ptr results) { + + std::cout << std::setw(ENTRY_SPACE) << "Function"; + std::cout << std::setw(ENTRY_SPACE) << "Best Rate MB/s"; + std::cout << std::setw(ENTRY_SPACE) << "Avg time s"; + std::cout << std::setw(ENTRY_SPACE) << "Min time" ; + std::cout << std::setw(ENTRY_SPACE) << "Max time" << std::endl; + + for (auto v : results->timings) { + double minTime = *min_element(v.second.begin(), v.second.end()); + double avgTime = accumulate(v.second.begin(), v.second.end(), 0.0) + / v.second.size(); + double maxTime = *max_element(v.second.begin(), v.second.end()); + + std::cout << std::setw(ENTRY_SPACE) << v.first; + std::cout << std::setw(ENTRY_SPACE) + << (static_cast(sizeof(HOST_DATA_TYPE)) * results->arraySize * bm_execution::multiplicatorMap[v.first] / minTime) * 1.0e-6 + << std::setw(ENTRY_SPACE) << avgTime + << std::setw(ENTRY_SPACE) << minTime + << std::setw(ENTRY_SPACE) << maxTime << std::endl; + } + +} + +/** + * Prints the used configuration to std out before starting the actual benchmark. + * + * @param programSettings The program settings retrieved from the command line + * @param device The device used for execution + */ +void printFinalConfiguration(const std::shared_ptr &programSettings, + const cl::Device &device) {// Give setup summary + std::cout << PROGRAM_DESCRIPTION << std::endl; + std::cout << "Version: " << VERSION << std::endl << HLINE; + std::cout << "Summary:" << std::endl + << "Array Size: " + << static_cast(programSettings->streamArraySize * sizeof(HOST_DATA_TYPE)) << " Byte" + << std::endl + << "Data Type " << STR(HOST_DATA_TYPE) + << std::endl + << "Repetitions: " << programSettings->numRepetitions + << std::endl + << "Kernel replications: " << programSettings->kernelReplications + << std::endl + << "Kernel type: " << (programSettings->useSingleKernel ? "Single" : "Separate") + << std::endl + << "Kernel file: " << programSettings->kernelFileName + << std::endl; + std::cout << "Device: " + << device.getInfo() << std::endl; + std::cout << HLINE + << "Start benchmark using the given configuration." << std::endl + << HLINE; +} + + +void generateInputData(HOST_DATA_TYPE* A, HOST_DATA_TYPE* B, HOST_DATA_TYPE* C, unsigned array_size) { + for (int i=0; i< array_size; i++) { + A[i] = 1.0; + B[i] = 2.0; + C[i] = 0.0; + } +} + +double checkSTREAMResult(const HOST_DATA_TYPE* A, const HOST_DATA_TYPE* B, const HOST_DATA_TYPE* C, unsigned repetitions, + unsigned array_size) { + HOST_DATA_TYPE aj,bj,cj,scalar; + HOST_DATA_TYPE aSumErr,bSumErr,cSumErr; + HOST_DATA_TYPE aAvgErr,bAvgErr,cAvgErr; + double epsilon; + ssize_t j; + int k,ierr,err; + + /* reproduce initialization */ + aj = 1.0; + bj = 2.0; + cj = 0.0; + /* a[] is modified during timing check */ + aj = 2.0E0 * aj; + /* now execute timing loop */ + scalar = 3.0; + for (k=0; k epsilon) { + err++; + printf ("Failed Validation on array a[], AvgRelAbsErr > epsilon (%e)\n",epsilon); + printf (" Expected Value: %e, AvgAbsErr: %e, AvgRelAbsErr: %e\n",aj,aAvgErr,abs(aAvgErr)/aj); + ierr = 0; + for (j=0; j epsilon) { + ierr++; + } + } + printf(" For array a[], %d errors were found.\n",ierr); + } + if (abs(bAvgErr/bj) > epsilon) { + err++; + printf ("Failed Validation on array b[], AvgRelAbsErr > epsilon (%e)\n",epsilon); + printf (" Expected Value: %e, AvgAbsErr: %e, AvgRelAbsErr: %e\n",bj,bAvgErr,abs(bAvgErr)/bj); + printf (" AvgRelAbsErr > Epsilon (%e)\n",epsilon); + ierr = 0; + for (j=0; j epsilon) { + ierr++; + } + } + printf(" For array b[], %d errors were found.\n",ierr); + } + if (abs(cAvgErr/cj) > epsilon) { + err++; + printf ("Failed Validation on array c[], AvgRelAbsErr > epsilon (%e)\n",epsilon); + printf (" Expected Value: %e, AvgAbsErr: %e, AvgRelAbsErr: %e\n",cj,cAvgErr,abs(cAvgErr)/cj); + printf (" AvgRelAbsErr > Epsilon (%e)\n",epsilon); + ierr = 0; + for (j=0; j epsilon) { + ierr++; + } + } + printf(" For array c[], %d errors were found.\n",ierr); + } + if (err == 0) { + printf ("Solution Validates: avg error less than %e on all three arrays\n",epsilon); + } + return err; +} \ No newline at end of file diff --git a/STREAM/src/host/stream_functionality.hpp b/STREAM/src/host/stream_functionality.hpp new file mode 100644 index 00000000..5f35cb4b --- /dev/null +++ b/STREAM/src/host/stream_functionality.hpp @@ -0,0 +1,116 @@ +/* +Copyright (c) 2019 Marius Meyer + +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 SRC_HOST_NETWORK_FUNCTIONALITY_H_ +#define SRC_HOST_NETWORK_FUNCTIONALITY_H_ + +/* C++ standard library headers */ +#include +#include + +/* Project's headers */ +#include "execution.h" +#include "cxxopts.hpp" +#include "setup/fpga_setup.hpp" +#include "parameters.h" + +/* +Short description of the program. +Moreover the version and build time is also compiled into the description. +*/ +#define STR_EXPAND(tok) #tok +#define STR(tok) STR_EXPAND(tok) + +#define PROGRAM_DESCRIPTION "Implementation of the STREAM benchmark"\ + " proposed in the HPCC benchmark suite for FPGA."\ + +#define ENTRY_SPACE 15 + +struct ProgramSettings { + uint numRepetitions; + uint streamArraySize; + uint kernelReplications; + bool useMemoryInterleaving; + int defaultPlatform; + int defaultDevice; + std::string kernelFileName; + bool useSingleKernel; +}; + + +/** +Parses and returns program options using the cxxopts library. +Supports the following parameters: + - file name of the FPGA kernel file (-f,--file) + - number of repetitions (-n) + - number of kernel replications (-r) + - data size (-d) + - use memory interleaving +@see https://github.com/jarro2783/cxxopts + +@return program settings that are created from the given program arguments +*/ +std::shared_ptr +parseProgramParameters(int argc, char *argv[]); + +/** +Prints the execution results to stdout + +@param results The execution results +*/ +void +printResults(std::shared_ptr results); + +/** + * Prints the used configuration to std out before starting the actual benchmark. + * + * @param programSettings The program settings retrieved from the command line + * @param device The device used for execution + */ +void printFinalConfiguration(const std::shared_ptr &programSettings, + const cl::Device &device); + + +/** + * Fill the data buffer with random number using the mersenne twister engine with + * seed 0. + * + * @param data Data array that has to be filled + * @param size Size of the data array that has to be filled + */ +void generateInputData(HOST_DATA_TYPE* A, HOST_DATA_TYPE* B, HOST_DATA_TYPE* C, unsigned array_size); + + +/** + * Checks the calculation error of an FFt calculation by calculating the inverse FFT on the result data + * and calculating the residual with abs(x - x')/(epsilon * log(FFT_SIZE)). + * + * @param verify_data The input data of the FFT calculation + * @param result_data Result of the FFT calculation + * @param iterations Number data iterations (total data size should be iterations * FFT_SIZE) + * @return the residual error of the calculation + */ +double checkSTREAMResult(const HOST_DATA_TYPE* A, const HOST_DATA_TYPE* B, const HOST_DATA_TYPE* C, unsigned repetitions, + unsigned array_size); + + +#endif // SRC_HOST_NETWORK_FUNCTIONALITY_H_ diff --git a/STREAM/tests/CMakeLists.txt b/STREAM/tests/CMakeLists.txt new file mode 100755 index 00000000..f709c3f4 --- /dev/null +++ b/STREAM/tests/CMakeLists.txt @@ -0,0 +1,29 @@ + +# 'lib' is the folder with Google Test sources +add_subdirectory(../../extern/googletest ${CMAKE_CURRENT_BINARY_DIR}/lib) +include_directories(${gtest_SOURCE_DIR}/include ${gtest_SOURCE_DIR}) +include_directories(${CMAKE_BINARY_DIR}/src/common ../../extern/cxxopts/include) + + +set(PROJECT_SOURCES ../src/host/setup/fpga_setup.cpp ../src/host/execution_default.cpp ../src/host/stream_functionality.cpp) +set(TEST_SOURCES test_fpga_setup.cpp test_kernel_functionality_and_host_integration.cpp) + +if (INTELFPGAOPENCL_FOUND) + include_directories(SYSTEM ${IntelFPGAOpenCL_INCLUDE_DIRS}) + add_executable(Test_intel ${TEST_SOURCES} ${PROJECT_SOURCES}) + target_link_libraries(Test_intel gtest gmock gtest_main ${IntelFPGAOpenCL_LIBRARIES} "${OpenMP_CXX_FLAGS}") + add_dependencies(Test_intel stream_kernels_emulate_intel stream_kernels_single_emulate_intel) + target_compile_definitions(Test_intel PRIVATE -DINTEL_FPGA) + target_compile_options(Test_intel PRIVATE "${OpenMP_CXX_FLAGS}") + add_test(NAME test_intel_unit COMMAND $) +endif() + +if (Vitis_FOUND) + include_directories(SYSTEM ${Vitis_INCLUDE_DIRS}) + add_executable(Test_xilinx ${TEST_SOURCES} ${PROJECT_SOURCES}) + target_link_libraries(Test_xilinx gtest gmock gtest_main ${Vitis_LIBRARIES} "${OpenMP_CXX_FLAGS}") + add_dependencies(Test_xilinx stream_kernels_single_emulate_xilinx) + target_compile_definitions(Test_xilinx PRIVATE -DXILINX_FPGA) + target_compile_options(Test_xilinx PRIVATE "${OpenMP_CXX_FLAGS}") + add_test(NAME test_xilinx_unit COMMAND $) +endif() \ No newline at end of file diff --git a/STREAM/tests/test_fpga_setup.cpp b/STREAM/tests/test_fpga_setup.cpp new file mode 100644 index 00000000..a3da9cee --- /dev/null +++ b/STREAM/tests/test_fpga_setup.cpp @@ -0,0 +1,38 @@ +// +// Created by Marius Meyer on 04.12.19 +// +#include "gtest/gtest.h" +#include "../src/host/setup/fpga_setup.hpp" +#include "parameters.h" + +/** + * Check if it is possible to find the platform and device that are given as default + */ +TEST (FPGASetup, FindValidPlatformAndDevice) { + EXPECT_EQ (1, fpga_setup::selectFPGADevice(DEFAULT_PLATFORM, DEFAULT_DEVICE).size()); +} + +/** + * Checks if non existing platform leads to an error + */ +TEST (FPGASetup, FindNonExistingPlatform) { + // Execute thread safe test because emulation leads to problems in google test detecting the exit of the routine + testing::FLAGS_gtest_death_test_style="threadsafe"; + // TODO regex does not work so for now its not tested! + EXPECT_EXIT(fpga_setup::selectFPGADevice(DEFAULT_PLATFORM + 100, DEFAULT_DEVICE), + ::testing::ExitedWithCode(1), + ::testing::MatchesRegex(".*")); +} + +/** + * Checks if non existing device leads to an error + */ +TEST (FPGASetup, FindNonExistingDevice) { + // Execute thread safe test because emulation leads to problems in google test detecting the exit of the routine + testing::FLAGS_gtest_death_test_style="threadsafe"; + // TODO regex does not work so for now its not tested! + EXPECT_EXIT(fpga_setup::selectFPGADevice(DEFAULT_PLATFORM, DEFAULT_DEVICE + 100), + ::testing::ExitedWithCode(1), + ::testing::MatchesRegex(".*")); +} + diff --git a/STREAM/tests/test_kernel_functionality_and_host_integration.cpp b/STREAM/tests/test_kernel_functionality_and_host_integration.cpp new file mode 100644 index 00000000..c182b307 --- /dev/null +++ b/STREAM/tests/test_kernel_functionality_and_host_integration.cpp @@ -0,0 +1,100 @@ +// +// Created by Marius Meyer on 14.02.20. +// +#include "gtest/gtest.h" +#include "parameters.h" +#include "../src/host/execution.h" +#include "../src/host/setup/fpga_setup.hpp" +#include "../src/host/stream_functionality.hpp" + + +struct OpenCLKernelTest : testing::Test { + HOST_DATA_TYPE *A; + HOST_DATA_TYPE *B; + HOST_DATA_TYPE *C; + std::shared_ptr config; + cl_uint array_size; + + OpenCLKernelTest() { + array_size = VECTOR_COUNT * UNROLL_COUNT * NUM_KERNEL_REPLICATIONS * BUFFER_SIZE; + posix_memalign(reinterpret_cast(&A), 64, + sizeof(HOST_DATA_TYPE) * array_size); + posix_memalign(reinterpret_cast(&B), 64, + sizeof(HOST_DATA_TYPE) * array_size); + posix_memalign(reinterpret_cast(&C), 64, + sizeof(HOST_DATA_TYPE) * array_size); + } + + void setupFPGA(std::string kernelFileName, bool is_single_kernel) { + std::vector device = fpga_setup::selectFPGADevice(DEFAULT_PLATFORM, DEFAULT_DEVICE); + cl::Context context(device[0]); + cl::Program program = fpga_setup::fpgaSetup(&context, device, &kernelFileName); + config = std::make_shared( + bm_execution::ExecutionConfiguration{ + context, device[0], program, + 1, + NUM_KERNEL_REPLICATIONS, + array_size, + false, + is_single_kernel + }); + HOST_DATA_TYPE norm; + generateInputData(A, B, C, array_size); + } + + ~OpenCLKernelTest() override { + free(A); + free(B); + free(C); + } +}; + +struct DifferentOpenCLKernelTest : OpenCLKernelTest, testing::WithParamInterface> { + DifferentOpenCLKernelTest() { + auto params = GetParam(); + auto kernel_file = std::get<0>(params); + bool is_single_kernel = std::get<1>(params); + setupFPGA(kernel_file, is_single_kernel); + } +}; + + +/** + * Execution returns correct results for a single repetition + */ +TEST_P(DifferentOpenCLKernelTest, FPGACorrectResultsOneRepetition) { + + auto result = bm_execution::calculate(config, A, B, C); + for (int i = 0; i < array_size; i++) { + EXPECT_FLOAT_EQ(A[i], 30.0); + EXPECT_FLOAT_EQ(B[i], 6.0); + EXPECT_FLOAT_EQ(C[i], 8.0); + } +} + +/** + * Execution returns correct results for three repetitions + */ +TEST_P(DifferentOpenCLKernelTest, FPGACorrectResultsThreeRepetition) { + config->repetitions = 3; + auto result = bm_execution::calculate(config, A, B, C); + for (int i = 0; i < array_size; i++) { + EXPECT_FLOAT_EQ(A[i], 6750.0); + EXPECT_FLOAT_EQ(B[i], 1350.0); + EXPECT_FLOAT_EQ(C[i], 1800.0); + } +} + + +#ifdef INTEL_FPGA +INSTANTIATE_TEST_CASE_P(Default, DifferentOpenCLKernelTest, + testing::Values(std::make_tuple("stream_kernels_emulate.aocx", false), + std::make_tuple("stream_kernels_single_emulate.aocx", true)) +); +#endif + +#ifdef XILINX_FPGA +INSTANTIATE_TEST_CASE_P(Default, DifferentOpenCLKernelTest, + testing::Values(std::make_tuple("stream_kernels_single_emulate.xclbin", true)) +); +#endif \ No newline at end of file diff --git a/b_eff/.gitignore b/b_eff/.gitignore new file mode 100644 index 00000000..5af72727 --- /dev/null +++ b/b_eff/.gitignore @@ -0,0 +1,3 @@ +# Exclude cmake build folder and CLion files +cmake-build* +.idea diff --git a/b_eff/CHANGELOG b/b_eff/CHANGELOG new file mode 100755 index 00000000..48a3cac4 --- /dev/null +++ b/b_eff/CHANGELOG @@ -0,0 +1,24 @@ +# Changelog + +This file contains all changes made to the source code for each release. + +## 1.1 + +#### Fixes: +- Simplify communication kernels to always send over all channels independent of the message size +- Reorder channel reads and writes in communication kernels +- Fix calculation of the aggregated bandwidth in host code + +#### Added: +- A new git submodule for hlslib to utilize the CMake module files included +- Measurement results for the current version in the performance section + +#### Removed: +- "cmake" folder in root directory since it is not needed anymore + +## 1.0 + +#### Added: +- Host code and OpenCL kernel +- First performance model for the OpenCL kernel targeting the Bittware 520N board equipped with Stratix 10 +- Basic MPI support in the host code \ No newline at end of file diff --git a/b_eff/CMakeLists.txt b/b_eff/CMakeLists.txt new file mode 100755 index 00000000..f72dc34b --- /dev/null +++ b/b_eff/CMakeLists.txt @@ -0,0 +1,55 @@ +cmake_minimum_required(VERSION 2.8.12) +project(fNet) + +# Include CMake module files provided by hlslib +set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_SOURCE_DIR}/../extern/hlslib/cmake) + +set(VERSION 1.1) +set(SEND_KERNEL_NAME send) +set(RECV_KERNEL_NAME recv) +set(DEFAULT_PLATFORM -1 CACHE STRING "Default platform index that is used by all ranks. (-1: manual selection)") +set(DEFAULT_DEVICE -1 CACHE STRING "Default device index that is used by all ranks. (-1: manual selection)") +set(DEFAULT_REPETITIONS 10 CACHE STRING "Default number of repetitions") + +set(AOC_FLAGS "-fpc -fp-relaxed" CACHE STRING "Used flags for the AOC compiler") +separate_arguments(AOC_FLAGS) + +set(CHANNEL_WIDTH 32 CACHE STRING "Width of a single external channel in Byte") +set(FPGA_BOARD_NAME p520_max_sg280l CACHE STRING "Name of the target FPGA board") + +set(HOST_DATA_TYPE cl_uchar) +set(DEVICE_DATA_TYPE uchar) + +set(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin) + +configure_file( + "${CMAKE_SOURCE_DIR}/src/common/parameters.h.in" + "${CMAKE_BINARY_DIR}/src/common/parameters.h" +) + +include_directories(${CMAKE_BINARY_DIR}/src/common) + +# Update git submodules + +find_package(Git QUIET) +if (GIT_FOUND AND EXISTS "${PROJECT_SOURCE_DIR}/.git") + # Update submodules as needed + option(GIT_SUBMODULE "Check submodules during build" ON) + if (GIT_SUBMODULE) + message(STATUS "Submodule update") + execute_process(COMMAND ${GIT_EXECUTABLE} submodule update --init --recursive + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + RESULT_VARIABLE GIT_SUBMOD_RESULT) + if (NOT GIT_SUBMOD_RESULT EQUAL "0") + message(FATAL_ERROR "git submodule update --init failed with ${GIT_SUBMOD_RESULT}, please checkout submodules") + endif () + endif () +endif () + +find_package(IntelFPGAOpenCL REQUIRED) +find_package(MPI REQUIRED) + +add_subdirectory(src/device) +add_subdirectory(src/host) +add_subdirectory(tests) + diff --git a/b_eff/LICENSE b/b_eff/LICENSE new file mode 100644 index 00000000..fc47bf1c --- /dev/null +++ b/b_eff/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020 pc2 + +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/b_eff/README.md b/b_eff/README.md new file mode 100644 index 00000000..d9685321 --- /dev/null +++ b/b_eff/README.md @@ -0,0 +1,164 @@ +# Effective Bandwidth Benchmark for FPGA + +This repository contains the effective bandwidth Benchmark for FPGA and its OpenCL kernels. +Currently only the Intel FPGA SDK for OpenCL utility is supported. + +It is a modified implementation of the +[Effective Bandwidth Benchmark](https://fs.hlrs.de/projects/par/mpi/b_eff/b_eff_3.1/#REF) +contained in the [HPC Challenge Benchmark](https://icl.utk.edu/hpcc/) suite. + +A performance model for the benchmark is given in the subfolder [performance](performance). + +Have a look into the [Changelog](CHANGELOG) to see recent changes in the recent version. + +## Dependencies + +The benchmark comes with the following requirements for building and running: + +- CMake 2.8 +- GCC 4.9 +- MPI (tested with OpenMPI 3.1.4) +- Intel OpenCL FPGA SDK 19.3 + +It also contains submodules that will be automatically updated when running cmake: + +- cxxopts: A header only library to parse command line parameters +- googletest: A C++ test framework +- hlslib: CMake support and helper functions for the work with HLS tools for FPGA + +## Build + +CMake is used as the build system. +The targets below can be used to build the benchmark and its kernels: + + | Target | Description | + | -------- | ---------------------------------------------- | + | fnet | Builds the host application | + | Google_Tests_run| Compile the tests and its dependencies | + + More over the are additional targets to generate kernel reports and bitstreams. + The provided kernel is optimized for the Bittware 520N board with four external + channels. + The code might need to be modified for other boards since the external channel descriptor + string might be different. + Also the number of channels is hardcoded and has to be modified for other boards. + The kernel targets are: + + | Target | Description | + | ------------------------------ | ---------------------------------------------- | + | communication_bw520n | Synthesizes the kernel (takes several hours!) | + | communication_bw520n_report | Create an HTML report for the kernel | + | communication_bw520n_emulate | Create a n emulation kernel | + + + You can build for example the host application by running + + mkdir build && cd build + cmake .. + make fnet + +You will find all executables and kernel files in the `bin` +folder of your build directory. +You should always specify a target with make to reduce the build time! +You might want to specify predefined parameters before build: + +Name | Default | Description | +---------------- |-------------|--------------------------------------| +`DEFAULT_DEVICE` | -1 | Index of the default device (-1 = ask) | +`DEFAULT_PLATFORM`| -1 | Index of the default platform (-1 = ask) | +`DEFAULT_REPETITIONS`| 10 | Number of times the kernel will be executed | +`FPGA_BOARD_NAME`| p520_max_sg280l | Name of the target board | +`CHANNEL_WIDTH` | 32 | The width of a single channel in bytes. | +`AOC_FLAGS`| `-fpc -fp-relaxed` | Additional AOC compiler flags that are used for kernel compilation | + +Moreover the environment variable `INTELFPGAOCLSDKROOT` has to be set to the root +of the Intel FPGA SDK installation. + +Additionally it is possible to set the used compiler and other build tools +in the `CMakeCache.txt` located in the build directory after running cmake. + +## Execution + +For execution of the benchmark run: + + ./fnet -f path_to_kernel.aocx + +For more information on available input parameters run + + ./fnet -h + +To execute the unit and integration tests run + + ./Google_Tests_run + +in the `bin` folder within the build directory. +It will run an emulation of the kernel and execute some functionality tests. +The external channels are emulated through files in the `bin` folder. +They will be automatically created by the test binary. +Sending kernels will write to a file in the file system and reading kernel will +read from those files. + +#### Execution with MPI + +The host code also supports MPI. To run the application with two MPI processes run + + mpirun -n 2 ./fnet -f path_to_kernel.aocx + +Platform and device selection is restricted if the application is executed with more than one rank. +In this case, the application will not ask for a platform and device if it was not explicitly specified, but just pick one under the following criteria: +- It always picks the first available platform +- The device is picked by calculating `rank mod #devices`. This allows to run multiple processes on the same node with multiple FPGAs without conflicts in device accesses. + +For example we execute the command above on a single node with two FPGAs. +Both ranks would pick the first available platform, if no platform is specified explicitly. +The used device index will be calculated with rank mod 2, so it would be 0 for the first device and 1 for the second. + +Every rank will measure the kernel execution time. +The result of the measurements will be collected by rank 0 and the send bandwidth will be calculated individually for every rank. + +**Note:** The MPI processes synchronize before every kernel execution using a MPI Barrier. +This might still lead to inaccuracies in the time measurements depending on the latency of the MPI network. + +## Output Interpretation + +The benchmark will output a result table to the standard output after execution. +This is an example output using a single rank in emulation: + + MSize looplength time GB/s + 1 16384 5.46779e-02 5.99292e+05 + 2 8192 5.19651e-02 6.30578e+05 + 4 4096 2.58565e-02 1.26730e+06 + 8 2048 7.51376e-03 4.36107e+06 + 16 1024 3.01288e-03 1.08760e+07 + 32 512 1.66958e-03 1.96265e+07 + 64 256 4.60622e-03 7.11386e+06 + 128 128 1.86568e-03 1.75636e+07 + 256 64 3.75094e-03 8.73594e+06 + 512 32 3.81549e-03 8.58814e+06 + 1024 16 3.44074e-03 9.52354e+06 + 2048 8 3.83420e-03 8.54624e+06 + 4096 4 3.34786e-03 9.78775e+06 + 16384 2 7.84717e-03 8.35154e+06 + 32768 1 7.42386e-03 8.82775e+06 + 65536 1 1.40822e-02 9.30761e+06 + 131072 1 1.28135e-02 2.04585e+07 + 262144 1 5.52680e-02 9.48628e+06 + 524288 1 9.99676e-02 1.04892e+07 + 1048576 1 1.21861e-01 1.72094e+07 + 2097152 1 4.20120e-01 9.98360e+06 + + b_eff = 9.58731e+06 GB/s + +The table contains the measurements for all tested message sizes. +It is split into the following four columns: + +- **MSize**: The size of the message in bytes +- **looplength**: The number of message exchanges that where used to measure the execution time +- **time**: Fastest execution time of the kernel with the configuration given in the first two columns over all ranks +- **GB/s**: The accumulated maximum achieved bandwidth with the current configuration using all ranks + +It is possible to set the number of repetitions of the experiment. +In this case, the best measured time will be used to calculate the bandwidth. + +Under the table the calculated effective bandwidth is printed. +It is the mean of the achieved bandwidths for all used message sizes. \ No newline at end of file diff --git a/b_eff/performance/README.md b/b_eff/performance/README.md new file mode 100644 index 00000000..05d4ebbb --- /dev/null +++ b/b_eff/performance/README.md @@ -0,0 +1,231 @@ +# Benchmark Performance + +This subfolder contains a performance model together with the expected performance for +different configurations on a Stratix 10 FPGA. + +## The Algorithm + +The kernel of the benchmark will send and receive data over the external channels of the FPGA using +21 different message sizes _L_. +They are defined like the following: + +![L=1B,2B,4B,\dots,2kB,4kB,4kB*(a^1),4kB*(a^2),\dots,4kB*(a^8)](http://latex.codecogs.com/gif.latex?L=1B,2B,4B,\dots,2kB,4kB,4kB*(a^1),4kB*(a^2),\dots,4kB*(a^8)) +where `a` is a integer constant that is set to 2 in this implementation. + +The FPGAs are using a Circuit-Switched-Network for communication. +So the random topology does not affect the performance since the the network topology can always be +adjusted to physically recreate the rings. +Also different ring sizes do not matter because of the same reason. +So the six different topologies of the original benchmark can be reduced to just a single topology. + +Since we only use one topology the equation for the effective bandwidth shrinks to: + + b_eff = sum_L(max_rep(b(L, rep))) / 21 + +where b(L, rep) is the measured bandwidth for a data size `L` in repetition `rep`. + +The `looplength` described in the original benchmark will be used to minimize measurement +errors caused by too short runtimes. +It specifies the total number of message exchanges that will be done with a certain message size. +It may vary between message sizes to achieve similar runtimes independent of the message size. + +The bandwidth will be calculated by measuring the execution time `t_exe` of the kernel and calculate +the total number of bytes transmitted. + + b(L) = (L * looplength) / t_exe + +The aggregated bandwidth is then calculated with: + + b_eff = sum_L(b(L)) / 21 + +#### Kernel Implementation + +A bitstream will contain two identical kernels that have to be connected to different +external channels to form the desired topologies. +The kernel executes the following: + +``` +def kernel(size, looplength): + const iterations = ceil(size / (#channels * channel_width)) + for 1..looplength: + for 1..iterations: + send and receive on all assigned channels in parallel +``` + +`channel_width` and `#channels` depend on the hardware specification of the external channels. +In the original benchmark `looplength` is calculated depending on the time off a single message +exchange. In practice it must be sufficiently large to reduce te measurement error for small data +sizes. +Both kernels take the used data size and the loop length as parameters. + +The bitstream will contain both kernels and they will be executed in parallel on multiple FPGAs. +An example for the external channel setup between two FPGA is given below: + +``` + FPGA 1 FPGA 2 + ..... CH1 ..... + |send | --------- |recv | + | | CH3 | | + |send | --------- |recv | + | | CH2 | | + |recv | --------- |send | + | | CH4 | | + |recv | --------- |send | + ..... ..... +``` +where `send` is the send kernel and `recv` the receive kernel. + +## Performance Model + +The following performance model is based on the pseudo code of the previous chapter. +So we have a send kernel and a receive kernel on each device and thus can send and receive in parallel. +They will be used to connect to the left-hand and right-hand neighbor in the ring topology. + +For the performance model we first need to model the runtime of the kernel for a given data size and +`looplength`. + +We will model the performance for the BittWare 520N cards equipped with Intel Stratix 10 FPGAs. +From the BittWare OpenCL S10 BSP Reference Guide we get the following relevant metrics for +external channels: + + Number of external: channels c = 4 + channel latency: c_latency = 520ns + channel clock frequency: c_frequency = 156.25MHz + channel width: c_width = 256bit + +We divide the available channels between the two kernels so every kernel will just have `c = 2` channels +for transmission. +The kernel execution time can then be calculated using the constants above and the used message size +`L` and the number of message exchanges `i`: + +![t_{L, i} = \frac{ceil(\frac{L}{c * c_{width}}) * i}{c_{frequency}} + i * c_{latency}](https://latex.codecogs.com/gif.latex?t_{L,&space;i}&space;=&space;\frac{ceil(\frac{L}{c&space;*&space;c_{width}})&space;*&space;i}{c_{frequency}}&space;+&space;i&space;*&space;c_{latency}) + +With that we can calculate the bandwidth with: + +![b_{L, i} = \frac{2 * L * i}{t_{L, i}}](https://latex.codecogs.com/gif.latex?b_{L,&space;i}&space;=&space;\frac{2&space;*&space;L&space;*&space;i}{t_{L,&space;i}}) + +The number of iterations can be removed from the equation for the model since we do not need to care about measurement +inaccuracy. So the equation reduces to: + +![b_{L} = \frac{L}{\frac{ceil(\frac{L}{c * c_{width}})}{c_{frequency}} + c_{latency}}](https://latex.codecogs.com/gif.latex?b_%7BL%7D%20%3D%20%5Cfrac%7BL%7D%7B%5Cfrac%7Bceil%28%5Cfrac%7BL%7D%7Bc%20*%20c_%7Bwidth%7D%7D%29%7D%7Bc_%7Bfrequency%7D%7D%20+%20c_%7Blatency%7D%7D) + +when inserting all constant values for the Bittware 520N board we get: + +![b_{L} = \frac{L}{\frac{ceil(\frac{L}{64B})}{156.25MHz} + 520ns}](https://latex.codecogs.com/gif.latex?b_{L}=\frac{L}{\frac{ceil(\frac{L}{64B})}{156.25MHz}+520ns}) + +where everything except `L` is constant. +This results in the following bandwidth depending on the used message sizes for a kernel that uses two channels for transmission: + +![Plot of model bandwidth](bandwidth_model.jpg) + +The effective bandwidth for a single kernel pair is then calculated with: + +![b_{agg} = \frac{\sum_{L} b_{L}}{21}](https://latex.codecogs.com/gif.latex?b_{agg}&space;=&space;\frac{\sum_{L}&space;b_{L}}{21}) + +over all `L`. + +Considering the model the predicted effective bandwidth for the BittWare 520N card will be ~8.17 GB/s per kernel pair. +Since all additional kernel pairs will have their own dedicated channels for communication the effective bandwidth is expected +to scale linearly with the number of kernel pairs `n` which is the same as the number + of used FPGA. + So we can calculate the effective bandwidth for the whole network with: + + ![b_{eff,n} = n * \frac{\sum_{L} b_{L}}{21} = n * 8.17GBs^{-1}](https://latex.codecogs.com/gif.latex?b_{agg,n}=n*\frac{\sum_{L}b_{L}}{21}=n*8.17GBs^{-1}) + +where `n` is the number of FPGA used in the network. + +## Measurement Results + +The benchmark was synthesized for the Bittware 520N board with the following configuration: + +Name | Default | Description | +---------------- |-------------|--------------------------------------| +`DEFAULT_DEVICE` | -1 | Index of the default device (-1 = ask) | +`DEFAULT_PLATFORM`| -1 | Index of the default platform (-1 = ask) | +`DEFAULT_REPETITIONS`| 10 | Number of times the kernel will be executed | +`FPGA_BOARD_NAME`| p520_max_sg280l | Name of the target board | +`CHANNEL_WIDTH` | 32 | The width of a single channel in bytes. | +`AOC_FLAGS`| `-fpc -fp-relaxed -no-interleaving=default` | Additional AOC compiler flags that are used for kernel compilation | + +The used tool versions where: + +Tool | Version | +---------------- |---------| +Intel OpenCL SDK | 19.4.0 | +BSP | 19.2.0 | +OpenMPI | 3.1.4 | +GCC | 8.3.0 | + + +The output of a test run on a single FPGA: + + MSize looplength transfer B/s + 1 16384 2.02084e-02 1.62150e+06 + 2 16384 2.02076e-02 3.24313e+06 + 4 16384 2.02078e-02 6.48621e+06 + 8 16384 2.02082e-02 1.29722e+07 + 16 16384 2.02075e-02 2.59453e+07 + 32 16384 2.02074e-02 5.18907e+07 + 64 16384 2.02074e-02 1.03781e+08 + 128 16384 2.03811e-02 2.05794e+08 + 256 16384 2.08290e-02 4.02737e+08 + 512 16384 2.17212e-02 7.72389e+08 + 1024 16384 2.34488e-02 1.43097e+09 + 2048 16384 2.67959e-02 2.50444e+09 + 4096 16384 3.35864e-02 3.99620e+09 + 16384 16384 7.41481e-02 7.24052e+09 + 32768 16384 1.28218e-01 8.37438e+09 + 65536 16384 2.36447e-01 9.08229e+09 + 131072 16384 4.52898e-01 9.48329e+09 + 262144 16384 8.85840e-01 9.69693e+09 + 524288 16384 1.75159e+00 9.80814e+09 + 1048576 16384 3.48309e+00 9.86474e+09 + 2097152 16384 6.94615e+00 9.89317e+09 + + b_eff = 3.95057e+09 B/s + +It can be observed, that the bandwidth is half of the bandwidth predicted by the model. +The reason for that is, that the send and receive loop in the kernel are executed sequentially. +This means the kernel is only sending half of the measured time although there is no direct dependency between the loop in a single iteration. +The model and the measurement together with their effective bandwidth is given in the following figure: + +![Compare model with measurement](bandwidth_compare.jpg) + +Changing the model to match the behavior of the synthesized bitstream leads to the following runtime function: + +![t_{L, i} = 2 *(\frac{ceil(\frac{L}{c * c_{width}}) * i}{c_{frequency}} + i * c_{latency})](https://latex.codecogs.com/gif.latex?t_{L,&space;i}&space;=&space;2*\(\frac{ceil(\frac{L}{c&space;*&space;c_{width}})&space;*&space;i}{c_{frequency}}&space;+&space;i&space;*&space;c_{latency}\)) + +Now the time is doubled since a message exchange includes sending and then receiving a message sequentially. +The effective bandwidth of the model is now 4.07 GB/s, which means an residual error of 3% the performance model to the actual measurement. + +#### Scaling + +Scaling the benchmark to 8 FPGAs on four different nodes gives the following result: + + MSize looplength transfer B/s + 1 16384 2.16071e-02 1.21323e+07 + 2 16384 2.16078e-02 2.42639e+07 + 4 16384 2.16072e-02 4.85291e+07 + 8 16384 2.16072e-02 9.70579e+07 + 16 16384 2.16080e-02 1.94109e+08 + 32 16384 2.16077e-02 3.88224e+08 + 64 16384 2.16064e-02 7.76494e+08 + 128 16384 2.17348e-02 1.54381e+09 + 256 16384 2.21099e-02 3.03525e+09 + 512 16384 2.29710e-02 5.84292e+09 + 1024 16384 2.46347e-02 1.08967e+10 + 2048 16384 2.80785e-02 1.91204e+10 + 4096 16384 3.47917e-02 3.08620e+10 + 16384 16384 7.54236e-02 5.69446e+10 + 32768 16384 1.29557e-01 6.63023e+10 + 65536 16384 2.37797e-01 7.22459e+10 + 131072 16384 4.54291e-01 7.56337e+10 + 262144 16384 8.87293e-01 7.74485e+10 + 524288 16384 1.75325e+00 7.83910e+10 + 1048576 16384 3.48511e+00 7.88720e+10 + 2097152 16384 6.94855e+00 7.91180e+10 + + b_eff = 3.13237e+10 B/s + +The effective bandwidth is 31.13 GB/s, which is close to the model with 8 * 4.07 GB/s = 32.56 GB/s. +So the implementation seems to scale well over multiple FPGA. \ No newline at end of file diff --git a/b_eff/performance/bandwidth_compare.jpg b/b_eff/performance/bandwidth_compare.jpg new file mode 100644 index 0000000000000000000000000000000000000000..fb006a24c12a68d96337de2e1f420393fc68680c GIT binary patch literal 30840 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 z%0A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 ft4taH{HO+k`@%6|)*8Y*}vc$^w`AI0$_X4l5 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 Xh26#zPYc~t-$i(7DR84N|Nol+%L`T= literal 0 HcmV?d00001 diff --git a/b_eff/src/common/parameters.h.in b/b_eff/src/common/parameters.h.in new file mode 100644 index 00000000..3c42f885 --- /dev/null +++ b/b_eff/src/common/parameters.h.in @@ -0,0 +1,24 @@ +#ifndef SRC_COMMON_PARAMETERS_H_ +#define SRC_COMMON_PARAMETERS_H_ + +#define VERSION "@VERSION@" +#define SEND_KERNEL_NAME "@SEND_KERNEL_NAME@" +#define RECV_KERNEL_NAME "@RECV_KERNEL_NAME@" +#define DEFAULT_REPETITIONS @DEFAULT_REPETITIONS@ +#define DEFAULT_PLATFORM @DEFAULT_PLATFORM@ +#define DEFAULT_DEVICE @DEFAULT_DEVICE@ + +/** + * Kernel Parameters + */ +#define CHANNEL_WIDTH @CHANNEL_WIDTH@ + +#define HOST_DATA_TYPE @HOST_DATA_TYPE@ +#define DEVICE_DATA_TYPE @DEVICE_DATA_TYPE@ + +/** +Output separator +*/ +#define HLINE "-------------------------------------------------------------\n" + +#endif // SRC_COMMON_PARAMETERS_H_ \ No newline at end of file diff --git a/b_eff/src/device/CMakeLists.txt b/b_eff/src/device/CMakeLists.txt new file mode 100644 index 00000000..e113db8b --- /dev/null +++ b/b_eff/src/device/CMakeLists.txt @@ -0,0 +1,35 @@ + +set(AOC_INCLUDES "-I${CMAKE_CURRENT_BINARY_DIR}/../common") + +function(generate_kernel_targets) + foreach (kernel_file_name ${ARGN}) + set(source_f ${CMAKE_CURRENT_SOURCE_DIR}/${kernel_file_name}.cl) + set(report_f ${EXECUTABLE_OUTPUT_PATH}/${kernel_file_name}_report) + set(bitstream_emulate_f ${EXECUTABLE_OUTPUT_PATH}/${kernel_file_name}_emulate.aocx) + set(bitstream_f ${EXECUTABLE_OUTPUT_PATH}/${kernel_file_name}.aocx) + set(out_f "${CMAKE_CURRENT_BINARY_DIR}/${out_f}") + add_custom_command(OUTPUT ${bitstream_emulate_f} + COMMAND ${IntelFPGAOpenCL_AOC} ${source_f} ${AOC_INCLUDES} ${AOC_FLAGS} -legacy-emulator -march=emulator + -o ${bitstream_emulate_f} + ) + add_custom_command(OUTPUT ${bitstream_f} + COMMAND ${IntelFPGAOpenCL_AOC} ${source_f} ${AOC_INCLUDES} ${AOC_FLAGS} -board=${FPGA_BOARD_NAME} + -o ${bitstream_f} + ) + add_custom_command(OUTPUT ${report_f} + COMMAND ${IntelFPGAOpenCL_AOC} ${source_f} ${AOC_INCLUDES} ${AOC_FLAGS} -rtl -report -board=${FPGA_BOARD_NAME} + -o ${report_f} + ) + add_custom_target(${kernel_file_name}_report DEPENDS ${report_f} + DEPENDS ${source_f} ${CMAKE_BINARY_DIR}/src/common/parameters.h + SOURCES ${source_f} ${CMAKE_BINARY_DIR}/src/common/parameters.h) + add_custom_target(${kernel_file_name} DEPENDS ${bitstream_f} + DEPENDS ${source_f} ${CMAKE_BINARY_DIR}/src/common/parameters.h + SOURCES ${source_f} ${CMAKE_BINARY_DIR}/src/common/parameters.h) + add_custom_target(${kernel_file_name}_emulate DEPENDS ${bitstream_emulate_f} + DEPENDS ${source_f} ${CMAKE_BINARY_DIR}/src/common/parameters.h + SOURCES ${source_f} ${CMAKE_BINARY_DIR}/src/common/parameters.h) + endforeach () +endfunction() + +generate_kernel_targets(communication_bw520n communication_bw520n_disable_pipelining communication_bw520n_combined_loops) diff --git a/b_eff/src/device/communication_bw520n.cl b/b_eff/src/device/communication_bw520n.cl new file mode 100644 index 00000000..ac8f68bd --- /dev/null +++ b/b_eff/src/device/communication_bw520n.cl @@ -0,0 +1,138 @@ +/* +Copyright (c) 2019 Marius Meyer + +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. +*/ + +/** + * This file contains two kernels "send" and "recv". + * They are identical in their function except two differences: + * - they use different channels for sending and receiving data + * - "send" sends first a message and then receives, "recv" does it the other way around + * + * The kernels are hardcoded to work with the Bittware 520N board that offers + * 4 external channels. + * The file might need to be adapted for the use with other boards! + */ + + +#include "parameters.h" + +#pragma OPENCL EXTENSION cl_intel_channels : enable + +#define ITEMS_PER_CHANNEL (CHANNEL_WIDTH / sizeof(DEVICE_DATA_TYPE)) + +/** + * Data type used for the channels + * Consider using vector types instead + */ +typedef struct { + DEVICE_DATA_TYPE values[ITEMS_PER_CHANNEL]; +} message_part; + +/** + * Definition of the external channels + */ +channel message_part ch_out_1 __attribute((io("kernel_output_ch0"))); +channel message_part ch_out_2 __attribute((io("kernel_output_ch2"))); +channel message_part ch_in_1 __attribute((io("kernel_input_ch0"))); +channel message_part ch_in_2 __attribute((io("kernel_input_ch2"))); + +channel message_part ch_out_3 __attribute((io("kernel_output_ch1"))); +channel message_part ch_out_4 __attribute((io("kernel_output_ch3"))); +channel message_part ch_in_3 __attribute((io("kernel_input_ch1"))); +channel message_part ch_in_4 __attribute((io("kernel_input_ch3"))); + + +/** + * Send kernel that will send messages through channel 1 and 2 and receive messages from + * channel 1 and 2 in parallel. + * + * @param data_size Size of the used message + * @param repetitions Number of times the message will be sent and received + */ +__kernel +__attribute__ ((max_global_work_dim(0))) +void send(const unsigned data_size, + const unsigned repetitions) { + const unsigned send_iterations = (data_size + 2 * ITEMS_PER_CHANNEL - 1) / (2 * ITEMS_PER_CHANNEL); + message_part send_part1; + message_part send_part2; + __attribute__((opencl_unroll_hint(ITEMS_PER_CHANNEL))) + for (DEVICE_DATA_TYPE d = 0; d < ITEMS_PER_CHANNEL; d++) { + send_part1.values[d] = data_size & 255; + send_part2.values[d] = data_size & 255; + } + for (unsigned i=0; i < repetitions; i++) { + for (unsigned k=0; k < send_iterations; k++) { + write_channel_intel(ch_out_1, send_part1); + write_channel_intel(ch_out_2, send_part2); + } + message_part recv_part1; + message_part recv_part2; + for (unsigned k=0; k < send_iterations; k++) { + recv_part1 = read_channel_intel(ch_in_1); + recv_part2 = read_channel_intel(ch_in_2); + } + __attribute__((opencl_unroll_hint(ITEMS_PER_CHANNEL))) + for (DEVICE_DATA_TYPE d = 0; d < ITEMS_PER_CHANNEL; d++) { + send_part1.values[d] = recv_part1.values[d]; + send_part2.values[d] = recv_part2.values[d]; + } + } +} + + +/** + * Receive kernel that will send messages through channel 3 and 4 and receive messages from + * channel 3 and 4 in parallel. + * + * @param data_size Size of the used message + * @param repetitions Number of times the message will be sent and received + */ +__kernel +__attribute__ ((max_global_work_dim(0))) +void recv(const unsigned data_size, + const unsigned repetitions) { + const unsigned send_iterations = (data_size + 2 * ITEMS_PER_CHANNEL - 1) / (2 * ITEMS_PER_CHANNEL); + message_part send_part1; + message_part send_part2; + message_part recv_part1; + message_part recv_part2; + __attribute__((opencl_unroll_hint(ITEMS_PER_CHANNEL))) + for (DEVICE_DATA_TYPE d = 0; d < ITEMS_PER_CHANNEL; d++) { + send_part1.values[d] = data_size & 255; + send_part2.values[d] = data_size & 255; + } + for (unsigned i=0; i < repetitions; i++) { + for (unsigned k=0; k < send_iterations; k++) { + recv_part1 = read_channel_intel(ch_in_3); + recv_part2 = read_channel_intel(ch_in_4); + } + for (unsigned k=0; k < send_iterations; k++) { + write_channel_intel(ch_out_3, send_part1); + write_channel_intel(ch_out_4, send_part2); + } + __attribute__((opencl_unroll_hint(ITEMS_PER_CHANNEL))) + for (DEVICE_DATA_TYPE d = 0; d < ITEMS_PER_CHANNEL; d++) { + send_part1.values[d] = recv_part1.values[d]; + send_part2.values[d] = recv_part2.values[d]; + } + } +} diff --git a/b_eff/src/device/communication_bw520n_combined_loops.cl b/b_eff/src/device/communication_bw520n_combined_loops.cl new file mode 100644 index 00000000..1c35458c --- /dev/null +++ b/b_eff/src/device/communication_bw520n_combined_loops.cl @@ -0,0 +1,118 @@ +/* +Copyright (c) 2019 Marius Meyer + +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. +*/ + +/** + * This file contains two kernels "send" and "recv". + * They are identical in their function except two differences: + * - they use different channels for sending and receiving data + * - "send" sends first a message and then receives, "recv" does it the other way around + * + * The kernels are hardcoded to work with the Bittware 520N board that offers + * 4 external channels. + * The file might need to be adapted for the use with other boards! + */ + + +#include "parameters.h" + +#pragma OPENCL EXTENSION cl_intel_channels : enable + +#define ITEMS_PER_CHANNEL (CHANNEL_WIDTH / sizeof(DEVICE_DATA_TYPE)) + +/** + * Data type used for the channels + * Consider using vector types instead + */ +typedef struct { + DEVICE_DATA_TYPE values[ITEMS_PER_CHANNEL]; +} message_part; + +/** + * Definition of the external channels + */ +channel message_part ch_out_1 __attribute((io("kernel_output_ch0"))); +channel message_part ch_out_2 __attribute((io("kernel_output_ch2"))); +channel message_part ch_in_1 __attribute((io("kernel_input_ch0"))); +channel message_part ch_in_2 __attribute((io("kernel_input_ch2"))); + +channel message_part ch_out_3 __attribute((io("kernel_output_ch1"))); +channel message_part ch_out_4 __attribute((io("kernel_output_ch3"))); +channel message_part ch_in_3 __attribute((io("kernel_input_ch1"))); +channel message_part ch_in_4 __attribute((io("kernel_input_ch3"))); + + +/** + * Send kernel that will send messages through channel 1 and 2 and receive messages from + * channel 1 and 2 in parallel. + * + * @param data_size Size of the used message + * @param repetitions Number of times the message will be sent and received + */ +__kernel +__attribute__ ((max_global_work_dim(0))) +void send(const unsigned data_size, + const unsigned repetitions) { + const unsigned send_iterations = (data_size + 2 * ITEMS_PER_CHANNEL - 1) / (2 * ITEMS_PER_CHANNEL); + message_part m_part; +#pragma unroll + for (DEVICE_DATA_TYPE d = 0; d < ITEMS_PER_CHANNEL; d++) { + m_part.values[d] = data_size & 255; + } +#pragma max_concurrency 1 + for (unsigned i=0; i < repetitions; i++) { + for (unsigned k=0; k < send_iterations; k++) { + write_channel_intel(ch_out_1, m_part); + write_channel_intel(ch_out_2, m_part); + message_part part1 = read_channel_intel(ch_in_1); + message_part part2 = read_channel_intel(ch_in_2); + } + } +} + + +/** + * Receive kernel that will send messages through channel 3 and 4 and receive messages from + * channel 3 and 4 in parallel. + * + * @param data_size Size of the used message + * @param repetitions Number of times the message will be sent and received + */ +__kernel +__attribute__ ((max_global_work_dim(0))) +void recv(const unsigned data_size, + const unsigned repetitions) { + const unsigned send_iterations = (data_size + 2 * ITEMS_PER_CHANNEL - 1) / (2 * ITEMS_PER_CHANNEL); + message_part m_part; +#pragma unroll + for (DEVICE_DATA_TYPE d = 0; d < ITEMS_PER_CHANNEL; d++) { + m_part.values[d] = data_size & 255; + } +#pragma max_concurrency 1 + for (unsigned i=0; i < repetitions; i++) { + for (unsigned k=0; k < send_iterations; k++) { + message_part part1 = read_channel_intel(ch_in_3); + message_part part2 = read_channel_intel(ch_in_4); + write_channel_intel(ch_out_3, m_part); + write_channel_intel(ch_out_4, m_part); + } + } +} \ No newline at end of file diff --git a/b_eff/src/device/communication_bw520n_disable_pipelining.cl b/b_eff/src/device/communication_bw520n_disable_pipelining.cl new file mode 100644 index 00000000..3853be83 --- /dev/null +++ b/b_eff/src/device/communication_bw520n_disable_pipelining.cl @@ -0,0 +1,122 @@ +/* +Copyright (c) 2019 Marius Meyer + +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. +*/ + +/** + * This file contains two kernels "send" and "recv". + * They are identical in their function except two differences: + * - they use different channels for sending and receiving data + * - "send" sends first a message and then receives, "recv" does it the other way around + * + * The kernels are hardcoded to work with the Bittware 520N board that offers + * 4 external channels. + * The file might need to be adapted for the use with other boards! + */ + + +#include "parameters.h" + +#pragma OPENCL EXTENSION cl_intel_channels : enable + +#define ITEMS_PER_CHANNEL (CHANNEL_WIDTH / sizeof(DEVICE_DATA_TYPE)) + +/** + * Data type used for the channels + * Consider using vector types instead + */ +typedef struct { + DEVICE_DATA_TYPE values[ITEMS_PER_CHANNEL]; +} message_part; + +/** + * Definition of the external channels + */ +channel message_part ch_out_1 __attribute((io("kernel_output_ch0"))); +channel message_part ch_out_2 __attribute((io("kernel_output_ch2"))); +channel message_part ch_in_1 __attribute((io("kernel_input_ch0"))); +channel message_part ch_in_2 __attribute((io("kernel_input_ch2"))); + +channel message_part ch_out_3 __attribute((io("kernel_output_ch1"))); +channel message_part ch_out_4 __attribute((io("kernel_output_ch3"))); +channel message_part ch_in_3 __attribute((io("kernel_input_ch1"))); +channel message_part ch_in_4 __attribute((io("kernel_input_ch3"))); + + +/** + * Send kernel that will send messages through channel 1 and 2 and receive messages from + * channel 1 and 2 in parallel. + * + * @param data_size Size of the used message + * @param repetitions Number of times the message will be sent and received + */ +__kernel +__attribute__ ((max_global_work_dim(0))) +void send(const unsigned data_size, + const unsigned repetitions) { + const unsigned send_iterations = (data_size + 2 * ITEMS_PER_CHANNEL - 1) / (2 * ITEMS_PER_CHANNEL); + message_part m_part; +#pragma unroll + for (DEVICE_DATA_TYPE d = 0; d < ITEMS_PER_CHANNEL; d++) { + m_part.values[d] = data_size & 255; + } +#pragma disable_loop_pipelining + for (unsigned i=0; i < repetitions; i++) { + for (unsigned k=0; k < send_iterations; k++) { + write_channel_intel(ch_out_1, m_part); + write_channel_intel(ch_out_2, m_part); + } + for (unsigned k=0; k < send_iterations; k++) { + message_part part1 = read_channel_intel(ch_in_1); + message_part part2 = read_channel_intel(ch_in_2); + } + } +} + + +/** + * Receive kernel that will send messages through channel 3 and 4 and receive messages from + * channel 3 and 4 in parallel. + * + * @param data_size Size of the used message + * @param repetitions Number of times the message will be sent and received + */ +__kernel +__attribute__ ((max_global_work_dim(0))) +void recv(const unsigned data_size, + const unsigned repetitions) { + const unsigned send_iterations = (data_size + 2 * ITEMS_PER_CHANNEL - 1) / (2 * ITEMS_PER_CHANNEL); + message_part m_part; +#pragma unroll + for (DEVICE_DATA_TYPE d = 0; d < ITEMS_PER_CHANNEL; d++) { + m_part.values[d] = data_size & 255; + } +#pragma disable_loop_pipelining + for (unsigned i=0; i < repetitions; i++) { + for (unsigned k=0; k < send_iterations; k++) { + message_part part1 = read_channel_intel(ch_in_3); + message_part part2 = read_channel_intel(ch_in_4); + } + for (unsigned k=0; k < send_iterations; k++) { + write_channel_intel(ch_out_3, m_part); + write_channel_intel(ch_out_4, m_part); + } + } +} \ No newline at end of file diff --git a/b_eff/src/host/CMakeLists.txt b/b_eff/src/host/CMakeLists.txt new file mode 100755 index 00000000..40a36760 --- /dev/null +++ b/b_eff/src/host/CMakeLists.txt @@ -0,0 +1,13 @@ + +include_directories(../../../extern/cxxopts/include) +include_directories(${IntelFPGAOpenCL_INCLUDE_DIRS}) +include_directories(${MPI_CXX_INCLUDE_PATH}) +include_directories(${CMAKE_BINARY_DIR}/src/common) + +set(HEADER_FILES execution.h setup/fpga_setup.hpp network_functionality.hpp) +set(HOST_SOURCE execution_default.cpp main.cpp setup/fpga_setup.cpp network_functionality.cpp) + +add_compile_options(--std=c++11) + +add_executable(fnet ${HOST_SOURCE} ${HEADER_FILES}) +target_link_libraries(fnet ${IntelFPGAOpenCL_LIBRARIES} ${MPI_LIBRARIES}) diff --git a/b_eff/src/host/execution.h b/b_eff/src/host/execution.h new file mode 100644 index 00000000..34db801a --- /dev/null +++ b/b_eff/src/host/execution.h @@ -0,0 +1,67 @@ +/* +Copyright (c) 2019 Marius Meyer + +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 SRC_HOST_EXECUTION_H_ +#define SRC_HOST_EXECUTION_H_ + +/* C++ standard library headers */ +#include +#include +#include + +/* External library headers */ +#include "CL/cl.hpp" +#include "parameters.h" + + +namespace bm_execution { + + struct ExecutionConfiguration { + cl::Context context; + cl::Device device; + cl::Program program; + uint repetitions; + }; + + struct ExecutionTimings { + cl_uint looplength; + cl_uint messageSize; + std::vector calculationTimings; + }; + + typedef std::map>>> CollectedResultMap; + +/** +The actual execution of the benchmark. +This method can be implemented in multiple *.cpp files. This header enables +simple exchange of the different calculation methods. + +@param config struct that contains all necessary information to execute the kernel on the FPGA + + +@return The resulting matrix +*/ + std::shared_ptr + calculate(std::shared_ptr config, cl_uint messageSize, cl_uint looplength); + +} // namespace bm_execution + +#endif // SRC_HOST_EXECUTION_H_ diff --git a/b_eff/src/host/execution_default.cpp b/b_eff/src/host/execution_default.cpp new file mode 100644 index 00000000..1ce4640d --- /dev/null +++ b/b_eff/src/host/execution_default.cpp @@ -0,0 +1,84 @@ +/* +Copyright (c) 2019 Marius Meyer + +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. +*/ + +/* Related header files */ +#include "execution.h" + +/* C++ standard library headers */ +#include +#include +#include + +/* External library headers */ +#include "CL/cl.hpp" +#include "CL/cl_ext_intelfpga.h" +#include "mpi.h" + +/* Project's headers */ + +namespace bm_execution { + + /* + Implementation for the single kernel. + @copydoc bm_execution::calculate() + */ + std::shared_ptr + calculate(std::shared_ptr config, cl_uint messageSize, cl_uint looplength) { + + + cl::Kernel sendKernel(config->program, SEND_KERNEL_NAME); + + sendKernel.setArg(0, messageSize); + sendKernel.setArg(1, looplength); + + cl::Kernel recvKernel(config->program, RECV_KERNEL_NAME); + + recvKernel.setArg(0, messageSize); + recvKernel.setArg(1, looplength); + + cl::CommandQueue sendQueue(config->context); + cl::CommandQueue recvQueue(config->context); + + std::vector calculationTimings; + for (uint r =0; r < config->repetitions; r++) { + MPI_Barrier(MPI_COMM_WORLD); + auto startCalculation = std::chrono::high_resolution_clock::now(); + sendQueue.enqueueTask(sendKernel); + recvQueue.enqueueTask(recvKernel); + recvQueue.finish(); + sendQueue.finish(); + auto endCalculation = std::chrono::high_resolution_clock::now(); + std::chrono::duration calculationTime = + std::chrono::duration_cast> + (endCalculation - startCalculation); + calculationTimings.push_back(calculationTime.count()); + } + + std::shared_ptr result(new ExecutionTimings{ + looplength, + messageSize, + calculationTimings + }); + return result; + } + +} // namespace bm_execution diff --git a/b_eff/src/host/main.cpp b/b_eff/src/host/main.cpp new file mode 100644 index 00000000..b2d8ce20 --- /dev/null +++ b/b_eff/src/host/main.cpp @@ -0,0 +1,115 @@ +// +// Created by Marius Meyer on 04.12.19. +// + +#include + +#include "network_functionality.hpp" +#include "mpi.h" + +/** +The program entry point +*/ +int +main(int argc, char *argv[]) { + + // Initialize the MPI environment + MPI_Init(&argc, &argv); + + // Get the number of processes + int world_size; + MPI_Comm_size(MPI_COMM_WORLD, &world_size); + + // Get the rank of the process + int world_rank; + MPI_Comm_rank(MPI_COMM_WORLD, &world_rank); + + // Setup benchmark + std::shared_ptr programSettings = + parseProgramParameters(argc, argv); + if (world_rank == 0) { + fpga_setup::setupEnvironmentAndClocks(); + } + std::vector usedDevice = + fpga_setup::selectFPGADevice(programSettings->defaultPlatform, + programSettings->defaultDevice); + cl::Context context = cl::Context(usedDevice); + cl::Program program = fpga_setup::fpgaSetup(&context, usedDevice, + &programSettings->kernelFileName); + + if (world_rank == 0) { + printFinalConfiguration(programSettings, usedDevice[0]); + } + + std::shared_ptr config( + new bm_execution::ExecutionConfiguration { + context, usedDevice[0], program, + programSettings->numRepetitions + }); + + auto msgSizes = getMessageSizes(); + std::vector> timing_results; + + for (cl_uint size : msgSizes) { + if (world_rank == 0) { + std::cout << "Measure for " << size << " Byte" << std::endl; + } + cl_uint looplength = std::max((programSettings->looplength) / ((size + (CHANNEL_WIDTH)) / (CHANNEL_WIDTH)), 1u); + timing_results.push_back(bm_execution::calculate(config, size,looplength)); + } + + // Collect the measurement results from all other nodes + bm_execution::CollectedResultMap collected_results; + if (world_rank > 0) { + for (const auto& t : timing_results) { + MPI_Send(&(t->messageSize), + 1, + MPI_UNSIGNED, 0, 0, MPI_COMM_WORLD); + MPI_Send(&(t->looplength), + 1, + MPI_UNSIGNED, 0, 1, MPI_COMM_WORLD); + MPI_Send(&(t->calculationTimings.front()), + programSettings->numRepetitions, + MPI_DOUBLE, 0, 2, MPI_COMM_WORLD); + } + } else { + std::cout << "Collect results over MPI."; + int k = 0; + for (int size : msgSizes) { + std::vector> tmp_timings; + std::cout << "."; + for (int i=1; i < world_size; i++) { + auto execution_result = std::make_shared( bm_execution::ExecutionTimings { + 0,0,std::vector(programSettings->numRepetitions) + }); + MPI_Status status; + MPI_Recv(&(execution_result->messageSize), + 1, + MPI_UNSIGNED, i, 0, MPI_COMM_WORLD, &status); + MPI_Recv(&(execution_result->looplength), + 1, + MPI_UNSIGNED, i, 1, MPI_COMM_WORLD, &status); + MPI_Recv(&(execution_result->calculationTimings.front()), + programSettings->numRepetitions, + MPI_DOUBLE, i, 2, MPI_COMM_WORLD, &status); + tmp_timings.push_back(execution_result); + if (execution_result->messageSize != size) { + std::cerr << "Wrong message size: " << execution_result->messageSize << " != " << size << " from rank " << i << std::endl; + exit(2); + } + } + tmp_timings.push_back(timing_results[k]); + k++; + collected_results.emplace(size, std::make_shared>>(tmp_timings)); + } + std::cout << " done!" << std::endl; + } + + if (world_rank == 0) { + printResults(collected_results); + } + + MPI_Finalize(); + return 0; +} + diff --git a/b_eff/src/host/network_functionality.cpp b/b_eff/src/host/network_functionality.cpp new file mode 100644 index 00000000..dd30699f --- /dev/null +++ b/b_eff/src/host/network_functionality.cpp @@ -0,0 +1,180 @@ +// +// Created by Marius Meyer on 04.12.19. +// + +/* +Copyright (c) 2019 Marius Meyer + +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. +*/ + +#include "network_functionality.hpp" + +/* C++ standard library headers */ +#include +#include + +/* Project's headers */ +#include "execution.h" +#include "cxxopts.hpp" +#include "setup/fpga_setup.hpp" +#include "parameters.h" + +/** +Parses and returns program options using the cxxopts library. +Supports the following parameters: + - file name of the FPGA kernel file (-f,--file) + - number of repetitions (-n) + - number of kernel replications (-r) + - data size (-d) + - use memory interleaving +@see https://github.com/jarro2783/cxxopts + +@return program settings that are created from the given program arguments +*/ +std::shared_ptr +parseProgramParameters(int argc, char *argv[]) { + // Defining and parsing program options + cxxopts::Options options(argv[0], PROGRAM_DESCRIPTION); + options.add_options() + ("f,file", "Kernel file name", cxxopts::value()) + ("n", "Number of repetitions", + cxxopts::value()->default_value(std::to_string(DEFAULT_REPETITIONS))) + ("l", "Inital looplength of Kernel", + cxxopts::value()->default_value(std::to_string(1u << 15u))) + ("device", "Index of the device that has to be used. If not given you "\ + "will be asked which device to use if there are multiple devices "\ + "available.", cxxopts::value()->default_value(std::to_string(DEFAULT_DEVICE))) + ("platform", "Index of the platform that has to be used. If not given "\ + "you will be asked which platform to use if there are multiple "\ + "platforms available.", + cxxopts::value()->default_value(std::to_string(DEFAULT_PLATFORM))) + ("h,help", "Print this help"); + cxxopts::ParseResult result = options.parse(argc, argv); + + // Check parsed options and handle special cases + if (result.count("f") <= 0) { + // Path to the kernel file is mandatory - exit if not given! + std::cerr << "Kernel file must be given! Aborting" << std::endl; + std::cout << options.help() << std::endl; + exit(1); + } + if (result.count("h")) { + // Just print help when argument is given + std::cout << options.help() << std::endl; + exit(0); + } + + // Create program settings from program arguments + std::shared_ptr sharedSettings( + new ProgramSettings{result["n"].as(), + result["l"].as(), + result["platform"].as(), + result["device"].as(), + result["f"].as()}); + return sharedSettings; +} + + +/** +Prints the execution results to stdout + +@param results The execution results +*/ +void +printResults(bm_execution::CollectedResultMap results) { + + std::vector maxBandwidths; + + std::cout << std::setw(ENTRY_SPACE) << "MSize" << " " + << std::setw(ENTRY_SPACE) << "looplength" << " " + << std::setw(ENTRY_SPACE) << "transfer" << " " + << std::setw(ENTRY_SPACE) << "B/s" << std::endl; + + std::vector totalMaxMinCalculationTime; + for (int i =0; i < results.size(); i++) { + totalMaxMinCalculationTime.push_back(0.0); + } + int i = 0; + for (const auto& msgSizeResults : results) { + for (const auto& r : *msgSizeResults.second) { + double localMinCalculationTime = *min_element(r->calculationTimings.begin(), r->calculationTimings.end()); + totalMaxMinCalculationTime[i] = std::max(totalMaxMinCalculationTime[i], localMinCalculationTime); + } + i++; + } + i = 0; + for (const auto& msgSizeResults : results) { + int looplength = msgSizeResults.second->at(0)->looplength; + // The total sent data in bytes will be: + // #Nodes * message_size * looplength * 2 + // the * 2 is because we have two kernels per bitstream that will send and receive simultaneously. + // This will be divided by half of the maximum of the minimum measured runtime over all ranks. + double maxCalcBW = static_cast(msgSizeResults.second->size() * 2 * msgSizeResults.first * looplength) + / (totalMaxMinCalculationTime[i]); + + maxBandwidths.push_back(maxCalcBW); + + std::cout << std::setw(ENTRY_SPACE) << msgSizeResults.first << " " + << std::setw(ENTRY_SPACE) << looplength << " " + << std::setw(ENTRY_SPACE) << totalMaxMinCalculationTime[i] << " " + << std::setw(ENTRY_SPACE) << maxCalcBW + << std::endl; + i++; + } + + + double b_eff = accumulate(maxBandwidths.begin(), maxBandwidths.end(), 0.0) / maxBandwidths.size(); + + std::cout << std::endl << "b_eff = " << b_eff << " B/s" << std::endl; + +} + +/** + * Prints the used configuration to std out before starting the actual benchmark. + * + * @param programSettings The program settings retrieved from the command line + * @param device The device used for execution + */ +void printFinalConfiguration(const std::shared_ptr &programSettings, + const cl::Device &device) {// Give setup summary + std::cout << PROGRAM_DESCRIPTION << std::endl << HLINE; + std::cout << "Summary:" << std::endl + << "Repetitions: " << programSettings->numRepetitions + << std::endl + << "Kernel file: " << programSettings->kernelFileName + << std::endl; + std::cout << "Device: " + << device.getInfo() << std::endl; + std::cout << HLINE + << "Start benchmark using the given configuration." << std::endl + << HLINE; +} + +std::vector getMessageSizes() { + std::vector sizes; + for (uint i = 0; i < 13; i++) { + sizes.push_back(1u << i); + } + cl_uint fourKB = 1u << 13u; + for (uint i=1; i <= 8; i++) { + sizes.push_back(fourKB * (1u << i)); + } + return sizes; +} \ No newline at end of file diff --git a/b_eff/src/host/network_functionality.hpp b/b_eff/src/host/network_functionality.hpp new file mode 100644 index 00000000..d1fc797c --- /dev/null +++ b/b_eff/src/host/network_functionality.hpp @@ -0,0 +1,94 @@ +/* +Copyright (c) 2019 Marius Meyer + +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 SRC_HOST_NETWORK_FUNCTIONALITY_H_ +#define SRC_HOST_NETWORK_FUNCTIONALITY_H_ + +/* C++ standard library headers */ +#include + +/* Project's headers */ +#include "execution.h" +#include "cxxopts.hpp" +#include "setup/fpga_setup.hpp" +#include "parameters.h" + +/* +Short description of the program. +Moreover the version and build time is also compiled into the description. +*/ +#define STR_EXPAND(tok) #tok +#define STR(tok) STR_EXPAND(tok) + +#define PROGRAM_DESCRIPTION "Implementation of the effective bandwidth benchmark"\ + " proposed in the HPCC benchmark suite for FPGA.\n"\ + "Version: " STR(VERSION) + +#define ENTRY_SPACE 13 + +struct ProgramSettings { + uint numRepetitions; + uint looplength; + int defaultPlatform; + int defaultDevice; + std::string kernelFileName; +}; + + +/** +Parses and returns program options using the cxxopts library. +Supports the following parameters: + - file name of the FPGA kernel file (-f,--file) + - number of repetitions (-n) + - number of kernel replications (-r) + - data size (-d) + - use memory interleaving +@see https://github.com/jarro2783/cxxopts + +@return program settings that are created from the given program arguments +*/ +std::shared_ptr +parseProgramParameters(int argc, char *argv[]); + +/** +Prints the execution results to stdout + +@param results The execution results +*/ +void +printResults(bm_execution::CollectedResultMap results); + +/** + * Prints the used configuration to std out before starting the actual benchmark. + * + * @param programSettings The program settings retrieved from the command line + * @param device The device used for execution + */ +void printFinalConfiguration(const std::shared_ptr &programSettings, + const cl::Device &device); + + +std::vector getMessageSizes(); + + + +#endif // SRC_HOST_NETWORK_FUNCTIONALITY_H_ diff --git a/b_eff/src/host/setup/fpga_setup.cpp b/b_eff/src/host/setup/fpga_setup.cpp new file mode 100644 index 00000000..a003ab27 --- /dev/null +++ b/b_eff/src/host/setup/fpga_setup.cpp @@ -0,0 +1,295 @@ +// +// Created by Marius Meyer on 04.12.19. +// + +#include "fpga_setup.hpp" + +#include +#include +#include +#include +#include +#include + +/* External libraries */ +#include "parameters.h" +#include "mpi.h" + +namespace fpga_setup { + + /** +Converts the reveived OpenCL error to a string + +@param err The OpenCL error code + +@return The string representation of the OpenCL error code +*/ + std::string + getCLErrorString(cl_int const err) { + switch (err) { + CL_ERR_TO_STR(CL_DEVICE_NOT_FOUND); + CL_ERR_TO_STR(CL_DEVICE_NOT_AVAILABLE); + CL_ERR_TO_STR(CL_COMPILER_NOT_AVAILABLE); + CL_ERR_TO_STR(CL_MEM_OBJECT_ALLOCATION_FAILURE); + CL_ERR_TO_STR(CL_OUT_OF_RESOURCES); + CL_ERR_TO_STR(CL_OUT_OF_HOST_MEMORY); + CL_ERR_TO_STR(CL_PROFILING_INFO_NOT_AVAILABLE); + CL_ERR_TO_STR(CL_MEM_COPY_OVERLAP); + CL_ERR_TO_STR(CL_IMAGE_FORMAT_MISMATCH); + CL_ERR_TO_STR(CL_IMAGE_FORMAT_NOT_SUPPORTED); + CL_ERR_TO_STR(CL_BUILD_PROGRAM_FAILURE); + CL_ERR_TO_STR(CL_MAP_FAILURE); + CL_ERR_TO_STR(CL_MISALIGNED_SUB_BUFFER_OFFSET); + CL_ERR_TO_STR(CL_EXEC_STATUS_ERROR_FOR_EVENTS_IN_WAIT_LIST); + CL_ERR_TO_STR(CL_KERNEL_ARG_INFO_NOT_AVAILABLE); + CL_ERR_TO_STR(CL_INVALID_VALUE); + CL_ERR_TO_STR(CL_INVALID_DEVICE_TYPE); + CL_ERR_TO_STR(CL_INVALID_PLATFORM); + CL_ERR_TO_STR(CL_INVALID_DEVICE); + CL_ERR_TO_STR(CL_INVALID_CONTEXT); + CL_ERR_TO_STR(CL_INVALID_QUEUE_PROPERTIES); + CL_ERR_TO_STR(CL_INVALID_COMMAND_QUEUE); + CL_ERR_TO_STR(CL_INVALID_HOST_PTR); + CL_ERR_TO_STR(CL_INVALID_MEM_OBJECT); + CL_ERR_TO_STR(CL_INVALID_IMAGE_FORMAT_DESCRIPTOR); + CL_ERR_TO_STR(CL_INVALID_IMAGE_SIZE); + CL_ERR_TO_STR(CL_INVALID_SAMPLER); + CL_ERR_TO_STR(CL_INVALID_BINARY); + CL_ERR_TO_STR(CL_INVALID_BUILD_OPTIONS); + CL_ERR_TO_STR(CL_INVALID_PROGRAM); + CL_ERR_TO_STR(CL_INVALID_PROGRAM_EXECUTABLE); + CL_ERR_TO_STR(CL_INVALID_KERNEL_NAME); + CL_ERR_TO_STR(CL_INVALID_KERNEL_DEFINITION); + CL_ERR_TO_STR(CL_INVALID_KERNEL); + CL_ERR_TO_STR(CL_INVALID_ARG_INDEX); + CL_ERR_TO_STR(CL_INVALID_ARG_VALUE); + CL_ERR_TO_STR(CL_INVALID_ARG_SIZE); + CL_ERR_TO_STR(CL_INVALID_KERNEL_ARGS); + CL_ERR_TO_STR(CL_INVALID_WORK_DIMENSION); + CL_ERR_TO_STR(CL_INVALID_WORK_GROUP_SIZE); + CL_ERR_TO_STR(CL_INVALID_WORK_ITEM_SIZE); + CL_ERR_TO_STR(CL_INVALID_GLOBAL_OFFSET); + CL_ERR_TO_STR(CL_INVALID_EVENT_WAIT_LIST); + CL_ERR_TO_STR(CL_INVALID_EVENT); + CL_ERR_TO_STR(CL_INVALID_OPERATION); + CL_ERR_TO_STR(CL_INVALID_GL_OBJECT); + CL_ERR_TO_STR(CL_INVALID_BUFFER_SIZE); + CL_ERR_TO_STR(CL_INVALID_MIP_LEVEL); + CL_ERR_TO_STR(CL_INVALID_GLOBAL_WORK_SIZE); + CL_ERR_TO_STR(CL_COMPILE_PROGRAM_FAILURE); + CL_ERR_TO_STR(CL_LINKER_NOT_AVAILABLE); + CL_ERR_TO_STR(CL_LINK_PROGRAM_FAILURE); + CL_ERR_TO_STR(CL_DEVICE_PARTITION_FAILED); + CL_ERR_TO_STR(CL_INVALID_PROPERTY); + CL_ERR_TO_STR(CL_INVALID_IMAGE_DESCRIPTOR); + CL_ERR_TO_STR(CL_INVALID_COMPILER_OPTIONS); + CL_ERR_TO_STR(CL_INVALID_LINKER_OPTIONS); + CL_ERR_TO_STR(CL_INVALID_DEVICE_PARTITION_COUNT); + + default: + return "UNKNOWN ERROR CODE"; + } + } + +/** +Check the OpenCL return code for errors. +If an error is detected, it will be printed and the programm execution is +stopped. + +@param err The OpenCL error code +*/ + void + handleClReturnCode(cl_int const err, std::string const file, + int const line) { + if (err != CL_SUCCESS) { + std::string err_string = getCLErrorString(err); + std::cerr << "ERROR in OpenCL library detected! Aborting." + << std::endl << file << ":" << line << ": " << err_string + << std::endl; + exit(err); + } + } + +/** +Sets up the given FPGA with the kernel in the provided file. + +@param context The context used for the program +@param program The devices used for the program +@param usedKernelFile The path to the kernel file +@return The program that is used to create the benchmark kernels +*/ + cl::Program + fpgaSetup(const cl::Context *context, std::vector deviceList, + const std::string *usedKernelFile) { + int err; + int world_rank; + int world_size; + MPI_Comm_rank(MPI_COMM_WORLD, &world_rank); + MPI_Comm_size(MPI_COMM_WORLD, &world_size); + + if (world_rank == 0) { + std::cout << HLINE; + std::cout << "FPGA Setup:" << usedKernelFile->c_str() << std::endl; + } + + // Open file stream if possible + std::ifstream aocxStream(usedKernelFile->c_str(), std::ifstream::binary); + if (!aocxStream.is_open()) { + std::cerr << "Not possible to open from given file!" << std::endl; + } + + // Read in file contents and create program from binaries + std::string prog(std::istreambuf_iterator(aocxStream), + (std::istreambuf_iterator())); + aocxStream.seekg(0, aocxStream.end); + unsigned file_size = aocxStream.tellg(); + aocxStream.seekg(0, aocxStream.beg); + char *buf = new char[file_size]; + aocxStream.read(buf, file_size); + + cl::Program::Binaries mybinaries; + mybinaries.push_back({buf, file_size}); + + // Create the Program from the AOCX file. + cl::Program program(*context, deviceList, mybinaries, NULL, &err); + ASSERT_CL(err); + if (world_rank == 0) { + std::cout << "Prepared FPGA successfully for global Execution!" << + std::endl; + std::cout << HLINE; + } + return program; + } + +/** +Sets up the C++ environment by configuring std::cout and checking the clock +granularity using bm_helper::checktick() +*/ + void + setupEnvironmentAndClocks() { + std::cout << std::setprecision(5) << std::scientific; + + std::cout << HLINE; + std::cout << "General setup:" << std::endl; + + // Check clock granularity and output result + std::cout << "C++ high resolution clock is used." << std::endl; + std::cout << "The clock precision seems to be " + << static_cast + (std::chrono::high_resolution_clock::period::num) / + std::chrono::high_resolution_clock::period::den * 10e9 + << "ns" << std::endl; + + std::cout << HLINE; + } + + +/** +Searches an selects an FPGA device using the CL library functions. +If multiple platforms or devices are given, the user will be prompted to +choose a device. + +@param defaultPlatform The index of the platform that has to be used. If a + value < 0 is given, the platform can be chosen + interactively +@param defaultDevice The index of the device that has to be used. If a + value < 0 is given, the device can be chosen + interactively + +@return A list containing a single selected device +*/ + std::vector + selectFPGADevice(int defaultPlatform, int defaultDevice) { + // Integer used to store return codes of OpenCL library calls + int err; + + int world_rank; + int world_size; + MPI_Comm_rank(MPI_COMM_WORLD, &world_rank); + MPI_Comm_size(MPI_COMM_WORLD, &world_size); + + std::vector platformList; + err = cl::Platform::get(&platformList); + ASSERT_CL(err); + + // Choose the target platform + int chosenPlatformId = 0; + if (defaultPlatform >= 0) { + if (defaultPlatform < platformList.size()) { + chosenPlatformId = defaultPlatform; + } else { + std::cerr << "Default platform " << defaultPlatform + << " can not be used. Available platforms: " + << platformList.size() << std::endl; + exit(1); + } + } else if (platformList.size() > 1 && world_size == 1) { + std::cout << + "Multiple platforms have been found. Select the platform by"\ + " typing a number:" << std::endl; + for (int platformId = 0; + platformId < platformList.size(); platformId++) { + std::cout << platformId << ") " << + platformList[platformId].getInfo() << + std::endl; + } + std::cout << "Enter platform id [0-" << platformList.size() - 1 + << "]:"; + std::cin >> chosenPlatformId; + } + cl::Platform platform = platformList[chosenPlatformId]; + if (world_rank == 0) { + std::cout << "Selected Platform: " + << platform.getInfo() << std::endl; + } + std::vector deviceList; + err = platform.getDevices(CL_DEVICE_TYPE_ACCELERATOR, &deviceList); + ASSERT_CL(err); + + // Choose taget device + int chosenDeviceId = 0; + if (defaultDevice >= 0) { + if (defaultDevice < deviceList.size()) { + chosenDeviceId = defaultDevice; + } else { + std::cerr << "Default platform " << defaultDevice + << " can not be used. Available platforms: " + << deviceList.size() << std::endl; + exit(1); + } + } else if (deviceList.size() > 1) { + if (world_size == 1) { + std::cout << + "Multiple devices have been found. Select the platform by"\ + " typing a number:" << std::endl; + + for (int deviceId = 0; + deviceId < deviceList.size(); deviceId++) { + std::cout << deviceId << ") " << + deviceList[deviceId].getInfo() << + std::endl; + } + std::cout << "Enter device id [0-" << deviceList.size() - 1 << "]:"; + std::cin >> chosenDeviceId; + } else { + chosenDeviceId = static_cast(world_rank % deviceList.size()); + } + } + std::vector chosenDeviceList; + chosenDeviceList.push_back(deviceList[chosenDeviceId]); + + if (world_rank == 0) { + // Give selection summary + std::cout << HLINE; + std::cout << "Selection summary:" << std::endl; + std::cout << "Platform Name: " << + platform.getInfo() << std::endl; + std::cout << "Device Name: " << + chosenDeviceList[0].getInfo() << std::endl; + std::cout << HLINE; + } + + return chosenDeviceList; + } + +} // namespace fpga_setup \ No newline at end of file diff --git a/b_eff/src/host/setup/fpga_setup.hpp b/b_eff/src/host/setup/fpga_setup.hpp new file mode 100644 index 00000000..c1f78b0c --- /dev/null +++ b/b_eff/src/host/setup/fpga_setup.hpp @@ -0,0 +1,108 @@ +/* +Copyright (c) 2019 Marius Meyer + +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 SRC_HOST_FPGA_SETUP_H_ +#define SRC_HOST_FPGA_SETUP_H_ + +#include +#include +#include +#include +#include +#include + +/* External libraries */ +#include "CL/cl.hpp" + +/** +Makro to convert the error integer representation to its string representation +Source: https://gist.github.com/allanmac/9328bb2d6a99b86883195f8f78fd1b93 +*/ +#define CL_ERR_TO_STR(err) case err: return #err + +namespace fpga_setup { + + /** +Converts the reveived OpenCL error to a string + +@param err The OpenCL error code + +@return The string representation of the OpenCL error code +*/ + std::string + getCLErrorString(cl_int const err); + +/** +Check the OpenCL return code for errors. +If an error is detected, it will be printed and the programm execution is +stopped. + +@param err The OpenCL error code +*/ + void + handleClReturnCode(cl_int const err, std::string const file, + int const line); + +/** +Makro that enables checks for OpenCL errors with handling of the file and +line number. +*/ +#define ASSERT_CL(err) fpga_setup::handleClReturnCode(err, __FILE__, __LINE__) + +/** +Sets up the given FPGA with the kernel in the provided file. + +@param context The context used for the program +@param program The devices used for the program +@param usedKernelFile The path to the kernel file +@return The program that is used to create the benchmark kernels +*/ + cl::Program + fpgaSetup(const cl::Context *context, std::vector deviceList, + const std::string *usedKernelFile); + +/** +Sets up the C++ environment by configuring std::cout and checking the clock +granularity using bm_helper::checktick() +*/ + void + setupEnvironmentAndClocks(); + + +/** +Searches an selects an FPGA device using the CL library functions. +If multiple platforms or devices are given, the user will be prompted to +choose a device. + +@param defaultPlatform The index of the platform that has to be used. If a + value < 0 is given, the platform can be chosen + interactively +@param defaultDevice The index of the device that has to be used. If a + value < 0 is given, the device can be chosen + interactively + +@return A list containing a single selected device +*/ + std::vector + selectFPGADevice(int defaultPlatform, int defaultDevice); + +} // namespace fpga_setup +#endif // SRC_HOST_FPGA_SETUP_H_ diff --git a/b_eff/tests/CMakeLists.txt b/b_eff/tests/CMakeLists.txt new file mode 100755 index 00000000..d84f785f --- /dev/null +++ b/b_eff/tests/CMakeLists.txt @@ -0,0 +1,16 @@ + +add_compile_options(--std=c++11) +# 'lib' is the folder with Google Test sources +add_subdirectory(../../extern/googletest ${CMAKE_CURRENT_BINARY_DIR}/lib) +include_directories(${gtest_SOURCE_DIR}/include ${gtest_SOURCE_DIR}) +include_directories(${CMAKE_BINARY_DIR}/src/common ../../extern/cxxopts/include) +include_directories(SYSTEM ${IntelFPGAOpenCL_INCLUDE_DIRS}) +include_directories(${MPI_CXX_INCLUDE_PATH}) + +set(PROJECT_SOURCES ../src/host/setup/fpga_setup.cpp ../src/host/execution_default.cpp) +set(TEST_SOURCES test_fpga_setup.cpp test_kernel_functionality_and_host_integration.cpp) + +add_executable(Google_Tests_run ${TEST_SOURCES} ${PROJECT_SOURCES}) +target_link_libraries(Google_Tests_run gtest gmock gtest_main ${IntelFPGAOpenCL_LIBRARIES} ${MPI_LIBRARIES}) +add_dependencies(Google_Tests_run ${CMAKE_BINARY_DIR}/src/common/parameters.h) +add_dependencies(Google_Tests_run communication_bw520n_emulate communication_bw520n_combined_loops_emulate communication_bw520n_disable_pipelining_emulate) \ No newline at end of file diff --git a/b_eff/tests/test_fpga_setup.cpp b/b_eff/tests/test_fpga_setup.cpp new file mode 100644 index 00000000..aee711b4 --- /dev/null +++ b/b_eff/tests/test_fpga_setup.cpp @@ -0,0 +1,38 @@ +// +// Created by Marius Meyer on 04.12.19 +// +#include "gtest/gtest.h" +#include "../src/host/setup/fpga_setup.hpp" +#include "parameters.h" +#include "mpi.h" + +/** + * Check if it is possible to find the platform and device that are given as default + */ +TEST (FPGASetup, FindValidPlatformAndDevice) { + MPI_Init(NULL, NULL); + EXPECT_EQ (1, fpga_setup::selectFPGADevice(DEFAULT_PLATFORM, DEFAULT_DEVICE).size()); +} + +/** + * Checks if non existing platform leads to an error + */ +TEST (FPGASetup, FindNonExistingPlatform) { + MPI_Init(NULL, NULL); + // TODO regex does not work so for now its not tested! + EXPECT_EXIT(fpga_setup::selectFPGADevice(DEFAULT_PLATFORM + 100, DEFAULT_DEVICE), + ::testing::ExitedWithCode(1), + ::testing::MatchesRegex(".*")); +} + +/** + * Checks if non existing device leads to an error + */ +TEST (FPGASetup, FindNonExistingDevice) { + MPI_Init(NULL, NULL); + // TODO regex does not work so for now its not tested! + EXPECT_EXIT(fpga_setup::selectFPGADevice(DEFAULT_PLATFORM, DEFAULT_DEVICE + 100), + ::testing::ExitedWithCode(1), + ::testing::MatchesRegex(".*")); +} + diff --git a/b_eff/tests/test_kernel_functionality_and_host_integration.cpp b/b_eff/tests/test_kernel_functionality_and_host_integration.cpp new file mode 100644 index 00000000..68c20186 --- /dev/null +++ b/b_eff/tests/test_kernel_functionality_and_host_integration.cpp @@ -0,0 +1,183 @@ +// +// Created by Marius Meyer on 04.12.19. +// +#include + +#include "gtest/gtest.h" +#include "../src/host/execution.h" +#include "parameters.h" +#include "../src/host/setup/fpga_setup.hpp" +#include "unistd.h" +#include "mpi.h" +#include + + +struct OpenCLKernelTest : testing::Test { + std::string kernelFileName = "communication_bw520n_emulate.aocx"; + std::shared_ptr config; + unsigned repetitions = 10; + unsigned numberOfChannels = 4; + std::string channelOutName = "kernel_output_ch"; + std::string channelInName = "kernel_input_ch"; + + void createChannelFilesAndSymbolicLinks() { + for (int i=0; i < numberOfChannels; i++) { + std::string fname = channelOutName + std::to_string(i); + std::fstream fs; + fs.open(fname, std::ios::out); + fs.close(); + std::remove((channelInName + std::to_string(i%2 ? i-1 : i+1)).c_str()); + symlink(fname.c_str(), (channelInName + std::to_string(i%2 ? i-1 : i+1)).c_str()); + } + } + + void setupFPGA() { + createChannelFilesAndSymbolicLinks(); + std::vector device = fpga_setup::selectFPGADevice(DEFAULT_PLATFORM, DEFAULT_DEVICE); + cl::Context context(device[0]); + cl::Program program = fpga_setup::fpgaSetup(&context, device, &kernelFileName); + config = std::make_shared( + bm_execution::ExecutionConfiguration{ + context, device[0], program, + repetitions + }); + } +}; + +/** + * Parametrized test takes a tuple of 4 parameters: + * - name of the emulation bitstream + * - number of channels + * - name of the external output channel descriptors + * - name of the external input channel descriptors + */ +struct DifferentOpenCLKernelTest : OpenCLKernelTest, testing::WithParamInterface> { + DifferentOpenCLKernelTest() { + MPI_Init(NULL, NULL); + auto params = GetParam(); + kernelFileName = std::get<0>(params); + numberOfChannels = std::get<1>(params); + channelOutName = std::get<2>(params); + channelInName = std::get<3>(params); + setupFPGA(); + } + + ~DifferentOpenCLKernelTest() { + MPI_Finalize(); + } +}; + + +/** + * Tests if calculate returns the correct execution results + */ +TEST_P(DifferentOpenCLKernelTest, CalculateReturnsCorrectExecutionResultFor111) { + config->repetitions = 1; + auto result = bm_execution::calculate(config, 1,1); + EXPECT_EQ(1, result->messageSize); + EXPECT_EQ(1, result->looplength); + EXPECT_EQ(1, result->calculationTimings.size()); +} + +/** + * Tests if calculate returns the correct execution results for multiple repetitions + */ +TEST_P(DifferentOpenCLKernelTest, CalculateReturnsCorrectExecutionResultFor842) { + config->repetitions = 2; + auto result = bm_execution::calculate(config, 8,4); + EXPECT_EQ(8, result->messageSize); + EXPECT_EQ(4, result->looplength); + EXPECT_EQ(2, result->calculationTimings.size()); +} + +/** + * Tests if data is written to the channels for small message sizes + */ +TEST_P(DifferentOpenCLKernelTest, DataIsWrittenToChannelForMessageSizeFillingOneChannel) { + config->repetitions = 1; + const unsigned messageSize = CHANNEL_WIDTH / sizeof(HOST_DATA_TYPE); + const unsigned looplength = 4; + auto result = bm_execution::calculate(config, messageSize,looplength); + HOST_DATA_TYPE* buffer = new HOST_DATA_TYPE[messageSize * looplength * 2]; + for (int i=0; i < numberOfChannels; i++) { + std::string ifname = channelOutName + std::to_string(i); + std::fstream fs; + fs.open(ifname, std::ios::in | std::ios::binary); + int num_bytes = fs.readsome(reinterpret_cast(buffer), messageSize * looplength * 2); + fs.close(); + // Altough only one channel would be necessary to send the data, also a dummy is send over the second channel + // to simplify the kernel logic + EXPECT_EQ( messageSize * looplength ,num_bytes); + + } + delete [] buffer; +} + +/** + * Tests if data is written to the channels for small message sizes filling two channels + */ +TEST_P(DifferentOpenCLKernelTest, DataIsWrittenToChannelForMessageSizeFillingTwoChannels) { + config->repetitions = 1; + const unsigned messageSize = 2 * CHANNEL_WIDTH / sizeof(HOST_DATA_TYPE); + const unsigned looplength = 4; + auto result = bm_execution::calculate(config, messageSize,looplength); + HOST_DATA_TYPE* buffer = new HOST_DATA_TYPE[messageSize * looplength * 2]; + for (int i=0; i < numberOfChannels; i++) { + std::string ifname = channelOutName + std::to_string(i); + std::fstream fs; + fs.open(ifname, std::ios::in | std::ios::binary); + int num_bytes = fs.readsome(reinterpret_cast(buffer), messageSize * looplength * 2); + fs.close(); + EXPECT_FLOAT_EQ( messageSize * looplength / 2 ,num_bytes); + } + delete [] buffer; +} + +/** + * Tests if data is written to the channels for message sizes filling more than two channels + */ +TEST_P(DifferentOpenCLKernelTest, DataIsWrittenToChannelForMessageSizeFillingMoreThanTwoChannels) { + config->repetitions = 1; + const unsigned messageSize = 4 * CHANNEL_WIDTH / sizeof(HOST_DATA_TYPE); + const unsigned looplength = 1; + auto result = bm_execution::calculate(config, messageSize,looplength); + HOST_DATA_TYPE* buffer = new HOST_DATA_TYPE[messageSize * looplength * 2]; + for (int i=0; i < numberOfChannels; i++) { + std::string ifname = channelOutName + std::to_string(i); + std::fstream fs; + fs.open(ifname, std::ios::in | std::ios::binary); + int num_bytes = fs.readsome(reinterpret_cast(buffer), messageSize * looplength * 2); + fs.close(); + EXPECT_FLOAT_EQ( messageSize * looplength / 2,num_bytes); + } + delete [] buffer; +} + +/** + * Tests if correct data is written to the channels + */ +TEST_P(DifferentOpenCLKernelTest, CorrectDataIsWrittenToChannel) { + config->repetitions = 1; + const unsigned messageSize = 2 * CHANNEL_WIDTH / sizeof(HOST_DATA_TYPE); + const unsigned looplength = 4; + auto result = bm_execution::calculate(config, messageSize,looplength); + HOST_DATA_TYPE* buffer = new HOST_DATA_TYPE[messageSize * looplength * 2]; + for (int i=0; i < numberOfChannels; i++) { + std::string ifname = channelOutName + std::to_string(i); + std::fstream fs; + fs.open(ifname, std::ios::in | std::ios::binary); + int num_bytes = fs.readsome(reinterpret_cast(buffer), messageSize * looplength * 2); + fs.close(); + for (int k=0; k < messageSize * looplength / 2; k++) { + EXPECT_EQ(static_cast(messageSize & 255), buffer[k]); + } + } + delete [] buffer; +} + + + +INSTANTIATE_TEST_CASE_P(Default, DifferentOpenCLKernelTest, + testing::Values(std::make_tuple("communication_bw520n_emulate.aocx", 4, "kernel_output_ch", "kernel_input_ch"), + std::make_tuple("communication_bw520n_combined_loops_emulate.aocx", 4, "kernel_output_ch", "kernel_input_ch"), + std::make_tuple("communication_bw520n_disable_pipelining_emulate.aocx", 4, "kernel_output_ch", "kernel_input_ch"))); diff --git a/extern/cxxopts b/extern/cxxopts new file mode 160000 index 00000000..b0f67a06 --- /dev/null +++ b/extern/cxxopts @@ -0,0 +1 @@ +Subproject commit b0f67a06de3446aa97a4943ad0ad6086460b2b61 diff --git a/extern/googletest b/extern/googletest new file mode 160000 index 00000000..dcc92d0a --- /dev/null +++ b/extern/googletest @@ -0,0 +1 @@ +Subproject commit dcc92d0ab6c4ce022162a23566d44f673251eee4 diff --git a/extern/hlslib b/extern/hlslib new file mode 160000 index 00000000..6135e3b2 --- /dev/null +++ b/extern/hlslib @@ -0,0 +1 @@ +Subproject commit 6135e3b26ddb3ba22502a1edacf0025980a19cf8