diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 33c173d40b..b16ce6fa66 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,4 +1,4 @@ -exclude: (^hls4ml\/templates\/(vivado|quartus)\/(ap_types|ac_types)\/|^test/pytest/test_report/) +exclude: (^hls4ml\/templates\/vivado\/ap_types\/|^test/pytest/test_report/) repos: - repo: https://github.com/astral-sh/ruff-pre-commit diff --git a/docs/advanced/extension.rst b/docs/advanced/extension.rst index 903794bd65..7d47d3c0e2 100644 --- a/docs/advanced/extension.rst +++ b/docs/advanced/extension.rst @@ -159,8 +159,9 @@ These two templates determine how to populate the config template based on the l return self.template.format(**params) Now, we need to tell hls4ml about the existence of this new layer by registering it. -We also need to register the parser (a.k.a. the layer handler), the template passes, and the HLS implementation source code with the particular backend. -In this case, the HLS code is valid for both the Vivado and Quartus backends. +We also need to register the parser (a.k.a. the layer handler), the +template passes, and the HLS implementation source code with the particular backend. +In this case, the HLS code is valid for the Vivado backend. .. code-block:: Python @@ -171,16 +172,14 @@ In this case, the HLS code is valid for both the Vivado and Quartus backends. # Register the hls4ml's IR layer hls4ml.model.layers.register_layer('KReverse', HReverse) - for backend_id in ['Vivado', 'Quartus']: - # Register the optimization passes (if any) - backend = hls4ml.backends.get_backend(backend_id) + backend = hls4ml.backends.get_backend('Vivado') - # Register template passes for the given backend - backend.register_template(HReverseConfigTemplate) - backend.register_template(HReverseFunctionTemplate) + # Register template passes for the backend + backend.register_template(HReverseConfigTemplate) + backend.register_template(HReverseFunctionTemplate) - # Register HLS implementation - backend.register_source('/path/to/your/nnet_reverse.h') + # Register HLS implementation + backend.register_source('/path/to/your/nnet_reverse.h') Finally, we can actually test the ``hls4ml`` custom layer compared to the Keras one. @@ -198,17 +197,15 @@ Finally, we can actually test the ``hls4ml`` custom layer compared to the Keras x = np.random.randint(-5, 5, (8,), dtype='int32') kres = kmodel(x) - for backend_id in ['Vivado', 'Quartus']: - - hmodel = hls4ml.converters.convert_from_keras_model( - kmodel, - output_dir=str(f'hls4mlprj_extensions_{backend_id}'), - backend=backend_id, - io_type='io_parallel', - hls_config={'Model': {'Precision': 'ap_int<6>', 'ReuseFactor': 1}}, - ) + hmodel = hls4ml.converters.convert_from_keras_model( + kmodel, + output_dir='hls4mlprj_extensions_Vivado', + backend='Vivado', + io_type='io_parallel', + hls_config={'Model': {'Precision': 'ap_int<6>', 'ReuseFactor': 1}}, + ) - hmodel.compile() - hres = hmodel.predict(x.astype('float32')) + hmodel.compile() + hres = hmodel.predict(x.astype('float32')) - np.testing.assert_array_equal(kres, hres) + np.testing.assert_array_equal(kres, hres) diff --git a/docs/backend/quartus.rst b/docs/backend/quartus.rst deleted file mode 100644 index 56ea1eb557..0000000000 --- a/docs/backend/quartus.rst +++ /dev/null @@ -1,12 +0,0 @@ -======= -Quartus -======= - -.. warning:: - The **Quartus** backend is deprecated and will be removed in a future version. Users should migrate to the **Altera** backend. - -The **Quartus** backend of hls4ml is designed for deploying NNs on Intel/Altera FPGAs. It uses the discontinued Intel HLS compiler. The **Altera** backend should be preferred for new projects. -The **Altera** backend contains the migrated the HLS code from this backend, with significantly better io_stream support, though the **Altera** backend does not yet support profiling, tracing, -or the BramFactor option supported by the **Quartus** backend. Nevertheless, little or no further development is expected for this backend. - -The **Quartus** backend only implements the ``Resource`` strategy for the layers. There is no ``Latency`` implementation of any of the layers. diff --git a/docs/index.rst b/docs/index.rst index dbb80dae68..5254fa2f4c 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -39,7 +39,6 @@ backend/altera backend/vitis_unified backend/catapult - backend/quartus backend/sr backend/xls diff --git a/docs/intro/setup.rst b/docs/intro/setup.rst index 26ba797e3f..842831cd39 100644 --- a/docs/intro/setup.rst +++ b/docs/intro/setup.rst @@ -70,8 +70,6 @@ To run FPGA synthesis, installation of the following tools is required: * Vitis HLS 2023.1 or newer is required for synthesis for Xilinx FPGAs using the ``Vitis`` backend. -* Intel Quartus 20.1 to 21.4 for synthesis for Intel/Altera FPGAs using the ``Quartus`` backend. - * Altera HLS IP Gen (version 2026.1.0 tested) and recent Intel/Altera Quartus for Intel/Altera FPGAs using the ``Altera`` backend. Catapult HLS 2024.1_1 or 2024.2 can be used to synthesize both for ASICs and FPGAs. @@ -204,9 +202,6 @@ Optional Dependencies # For QKeras-v3 frontend pip install hls4ml[qkeras-v3] - # For Quartus report parsing - pip install hls4ml[quartus-report] - # For snnTorch frontend pip install hls4ml[snn] diff --git a/docs/ir/attributes.rst b/docs/ir/attributes.rst index b0dec5fcbc..14e2ca5127 100644 --- a/docs/ir/attributes.rst +++ b/docs/ir/attributes.rst @@ -87,19 +87,19 @@ Backend-specific attributes * The number of times each multiplier is used by controlling the amount of pipelining/unrolling. A lower number results in more parallelism and lower latency at the expense of the resources used. Reuse factor = 1 corresponds to all multiplications executed in parallel, and hence, the lowest possible latency. - * Available in: Vivado, VivadoAccelerator, Vitis, VitisUnified, Quartus, Catapult, SymbolicExpression, Altera, Coyote, Libero, XLS + * Available in: Vivado, VivadoAccelerator, Vitis, VitisUnified, Catapult, SymbolicExpression, Altera, Coyote, Libero, XLS * table_size: int (Default: 1024) * The size of the lookup table used to approximate the function. - * Available in: Vivado, VivadoAccelerator, Vitis, VitisUnified, Quartus, Catapult, SymbolicExpression, Altera, Coyote, Libero, XLS + * Available in: Vivado, VivadoAccelerator, Vitis, VitisUnified, Catapult, SymbolicExpression, Altera, Coyote, Libero, XLS * table_t: NamedType (Default: fixed<18,8,TRN,WRAP,0>) * The datatype (precision) used for the values of the lookup table. - * Available in: Vivado, VivadoAccelerator, Vitis, VitisUnified, Quartus, Catapult, SymbolicExpression, Altera, Coyote, Libero, XLS + * Available in: Vivado, VivadoAccelerator, Vitis, VitisUnified, Catapult, SymbolicExpression, Altera, Coyote, Libero, XLS ParametrizedActivation ====================== @@ -143,19 +143,19 @@ Backend-specific attributes * The number of times each multiplier is used by controlling the amount of pipelining/unrolling. A lower number results in more parallelism and lower latency at the expense of the resources used. Reuse factor = 1 corresponds to all multiplications executed in parallel, and hence, the lowest possible latency. - * Available in: Vivado, VivadoAccelerator, Vitis, VitisUnified, Quartus, Catapult, SymbolicExpression, Altera, Coyote, Libero, XLS + * Available in: Vivado, VivadoAccelerator, Vitis, VitisUnified, Catapult, SymbolicExpression, Altera, Coyote, Libero, XLS * table_size: int (Default: 1024) * The size of the lookup table used to approximate the function. - * Available in: Vivado, VivadoAccelerator, Vitis, VitisUnified, Quartus, Catapult, SymbolicExpression, Altera, Coyote, Libero, XLS + * Available in: Vivado, VivadoAccelerator, Vitis, VitisUnified, Catapult, SymbolicExpression, Altera, Coyote, Libero, XLS * table_t: NamedType (Default: fixed<18,8,TRN,WRAP,0>) * The datatype (precision) used for the values of the lookup table. - * Available in: Vivado, VivadoAccelerator, Vitis, VitisUnified, Quartus, Catapult, SymbolicExpression, Altera, Coyote, Libero, XLS + * Available in: Vivado, VivadoAccelerator, Vitis, VitisUnified, Catapult, SymbolicExpression, Altera, Coyote, Libero, XLS PReLU ===== @@ -203,19 +203,19 @@ Backend-specific attributes * The number of times each multiplier is used by controlling the amount of pipelining/unrolling. A lower number results in more parallelism and lower latency at the expense of the resources used. Reuse factor = 1 corresponds to all multiplications executed in parallel, and hence, the lowest possible latency. - * Available in: Vivado, VivadoAccelerator, Vitis, VitisUnified, Quartus, Catapult, SymbolicExpression, Altera, Coyote, Libero, XLS + * Available in: Vivado, VivadoAccelerator, Vitis, VitisUnified, Catapult, SymbolicExpression, Altera, Coyote, Libero, XLS * table_size: int (Default: 1024) * The size of the lookup table used to approximate the function. - * Available in: Vivado, VivadoAccelerator, Vitis, VitisUnified, Quartus, Catapult, SymbolicExpression, Altera, Coyote, Libero, XLS + * Available in: Vivado, VivadoAccelerator, Vitis, VitisUnified, Catapult, SymbolicExpression, Altera, Coyote, Libero, XLS * table_t: NamedType (Default: fixed<18,8,TRN,WRAP,0>) * The datatype (precision) used for the values of the lookup table. - * Available in: Vivado, VivadoAccelerator, Vitis, VitisUnified, Quartus, Catapult, SymbolicExpression, Altera, Coyote, Libero, XLS + * Available in: Vivado, VivadoAccelerator, Vitis, VitisUnified, Catapult, SymbolicExpression, Altera, Coyote, Libero, XLS Softmax ======= @@ -251,59 +251,59 @@ Backend-specific attributes * The number of times each multiplier is used by controlling the amount of pipelining/unrolling. A lower number results in more parallelism and lower latency at the expense of the resources used. Reuse factor = 1 corresponds to all multiplications executed in parallel, and hence, the lowest possible latency. - * Available in: Vivado, VivadoAccelerator, Vitis, VitisUnified, Quartus, Catapult, SymbolicExpression, Altera, Coyote, Libero, XLS + * Available in: Vivado, VivadoAccelerator, Vitis, VitisUnified, Catapult, SymbolicExpression, Altera, Coyote, Libero, XLS * table_size: int (Default: 1024) * The size of the lookup table used to approximate the function. - * Available in: Vivado, VivadoAccelerator, Vitis, VitisUnified, Quartus, Catapult, SymbolicExpression, Altera, Coyote, Libero, XLS + * Available in: Vivado, VivadoAccelerator, Vitis, VitisUnified, Catapult, SymbolicExpression, Altera, Coyote, Libero, XLS * table_t: NamedType (Default: fixed<18,8,TRN,WRAP,0>) * The datatype (precision) used for the values of the lookup table. - * Available in: Vivado, VivadoAccelerator, Vitis, VitisUnified, Quartus, Catapult, SymbolicExpression, Altera, Coyote, Libero, XLS + * Available in: Vivado, VivadoAccelerator, Vitis, VitisUnified, Catapult, SymbolicExpression, Altera, Coyote, Libero, XLS * n_outer: int (Default: 1) - * Available in: Vivado, VivadoAccelerator, Vitis, VitisUnified, Quartus, Catapult, SymbolicExpression, Altera, Coyote, Libero, XLS + * Available in: Vivado, VivadoAccelerator, Vitis, VitisUnified, Catapult, SymbolicExpression, Altera, Coyote, Libero, XLS * n_inner: int (Default: 1) - * Available in: Vivado, VivadoAccelerator, Vitis, VitisUnified, Quartus, Catapult, SymbolicExpression, Altera, Coyote, Libero, XLS + * Available in: Vivado, VivadoAccelerator, Vitis, VitisUnified, Catapult, SymbolicExpression, Altera, Coyote, Libero, XLS * implementation: list [latency,stable,argmax,legacy] (Default: stable) * Choice of implementation of softmax function. "latency" provides good latency at the expense of extra resources. It performs well with a small number of classes. "stable" may require extra clock cycles but has better accuracy. "legacy" is the older implementation which has bad accuracy, but is fast and has low resource use. It is superseded by the "latency" implementation for most applications. "argmax" is a special implementation that can be used if only the output with the highest probability is important. Using this implementation will save resources and clock cycles. - * Available in: Vivado, VivadoAccelerator, Vitis, VitisUnified, Quartus, Catapult, SymbolicExpression, Altera, Coyote, Libero, XLS + * Available in: Vivado, VivadoAccelerator, Vitis, VitisUnified, Catapult, SymbolicExpression, Altera, Coyote, Libero, XLS * skip: bool (Default: False) * If enabled, skips the softmax node and returns the raw outputs. - * Available in: Vivado, VivadoAccelerator, Vitis, VitisUnified, Quartus, Catapult, SymbolicExpression, Altera, Coyote, Libero, XLS + * Available in: Vivado, VivadoAccelerator, Vitis, VitisUnified, Catapult, SymbolicExpression, Altera, Coyote, Libero, XLS * exp_table_t: NamedType (Default: fixed<18,8,RND,SAT,0>) * The datatype (precision) used for the values of the lookup table. - * Available in: Vivado, VivadoAccelerator, Vitis, VitisUnified, Quartus, Catapult, SymbolicExpression, Altera, Coyote, Libero, XLS + * Available in: Vivado, VivadoAccelerator, Vitis, VitisUnified, Catapult, SymbolicExpression, Altera, Coyote, Libero, XLS * inv_table_t: NamedType (Default: fixed<18,8,RND,SAT,0>) * The datatype (precision) used for the values of the lookup table. - * Available in: Vivado, VivadoAccelerator, Vitis, VitisUnified, Quartus, Catapult, SymbolicExpression, Altera, Coyote, Libero, XLS + * Available in: Vivado, VivadoAccelerator, Vitis, VitisUnified, Catapult, SymbolicExpression, Altera, Coyote, Libero, XLS * inv_inp_t: NamedType (Default: fixed<18,8,RND,SAT,0>) - * Available in: Vivado, VivadoAccelerator, Vitis, VitisUnified, Quartus, Catapult, SymbolicExpression, Altera, Coyote, Libero, XLS + * Available in: Vivado, VivadoAccelerator, Vitis, VitisUnified, Catapult, SymbolicExpression, Altera, Coyote, Libero, XLS * accum_t: NamedType (Default: fixed<18,8,RND,SAT,0>) - * Available in: Vivado, VivadoAccelerator, Vitis, VitisUnified, Quartus, Catapult, SymbolicExpression, Altera, Coyote, Libero, XLS + * Available in: Vivado, VivadoAccelerator, Vitis, VitisUnified, Catapult, SymbolicExpression, Altera, Coyote, Libero, XLS IFNeuron ======== @@ -477,19 +477,19 @@ Backend-specific attributes * The number of times each multiplier is used by controlling the amount of pipelining/unrolling. A lower number results in more parallelism and lower latency at the expense of the resources used. Reuse factor = 1 corresponds to all multiplications executed in parallel, and hence, the lowest possible latency. - * Available in: Vivado, VivadoAccelerator, Vitis, VitisUnified, Quartus, Catapult, SymbolicExpression, Altera, Coyote, Libero, XLS + * Available in: Vivado, VivadoAccelerator, Vitis, VitisUnified, Catapult, SymbolicExpression, Altera, Coyote, Libero, XLS * table_size: int (Default: 1024) * The size of the lookup table used to approximate the function. - * Available in: Vivado, VivadoAccelerator, Vitis, VitisUnified, Quartus, Catapult, SymbolicExpression, Altera, Coyote, Libero, XLS + * Available in: Vivado, VivadoAccelerator, Vitis, VitisUnified, Catapult, SymbolicExpression, Altera, Coyote, Libero, XLS * table_t: NamedType (Default: fixed<18,8,TRN,WRAP,0>) * The datatype (precision) used for the values of the lookup table. - * Available in: Vivado, VivadoAccelerator, Vitis, VitisUnified, Quartus, Catapult, SymbolicExpression, Altera, Coyote, Libero, XLS + * Available in: Vivado, VivadoAccelerator, Vitis, VitisUnified, Catapult, SymbolicExpression, Altera, Coyote, Libero, XLS HardActivation ============== @@ -537,19 +537,19 @@ Backend-specific attributes * The number of times each multiplier is used by controlling the amount of pipelining/unrolling. A lower number results in more parallelism and lower latency at the expense of the resources used. Reuse factor = 1 corresponds to all multiplications executed in parallel, and hence, the lowest possible latency. - * Available in: Vivado, VivadoAccelerator, Vitis, VitisUnified, Quartus, Catapult, SymbolicExpression, Altera, Coyote, Libero, XLS + * Available in: Vivado, VivadoAccelerator, Vitis, VitisUnified, Catapult, SymbolicExpression, Altera, Coyote, Libero, XLS * table_size: int (Default: 1024) * The size of the lookup table used to approximate the function. - * Available in: Vivado, VivadoAccelerator, Vitis, VitisUnified, Quartus, Catapult, SymbolicExpression, Altera, Coyote, Libero, XLS + * Available in: Vivado, VivadoAccelerator, Vitis, VitisUnified, Catapult, SymbolicExpression, Altera, Coyote, Libero, XLS * table_t: NamedType (Default: fixed<18,8,TRN,WRAP,0>) * The datatype (precision) used for the values of the lookup table. - * Available in: Vivado, VivadoAccelerator, Vitis, VitisUnified, Quartus, Catapult, SymbolicExpression, Altera, Coyote, Libero, XLS + * Available in: Vivado, VivadoAccelerator, Vitis, VitisUnified, Catapult, SymbolicExpression, Altera, Coyote, Libero, XLS Reshape ======= @@ -625,13 +625,13 @@ Backend-specific attributes * The datatype (precision) used to store intermediate results of the computation within the layer. - * Available in: Vivado, VivadoAccelerator, Vitis, VitisUnified, Quartus, Catapult, SymbolicExpression, Altera, Coyote, Libero, XLS + * Available in: Vivado, VivadoAccelerator, Vitis, VitisUnified, Catapult, SymbolicExpression, Altera, Coyote, Libero, XLS * reuse_factor: int (Default: 1) * The number of times each multiplier is used by controlling the amount of pipelining/unrolling. A lower number results in more parallelism and lower latency at the expense of the resources used. Reuse factor = 1 corresponds to all multiplications executed in parallel, and hence, the lowest possible latency. - * Available in: Vivado, VivadoAccelerator, Vitis, VitisUnified, Quartus, Catapult, SymbolicExpression, Altera, Coyote, Libero, XLS + * Available in: Vivado, VivadoAccelerator, Vitis, VitisUnified, Catapult, SymbolicExpression, Altera, Coyote, Libero, XLS * strategy: list [latency,resource] (Default: latency) @@ -667,13 +667,13 @@ Backend-specific attributes * The datatype (precision) used to store intermediate results of the computation within the layer. - * Available in: Vivado, VivadoAccelerator, Vitis, VitisUnified, Quartus, Catapult, SymbolicExpression, Altera, Coyote, Libero, XLS + * Available in: Vivado, VivadoAccelerator, Vitis, VitisUnified, Catapult, SymbolicExpression, Altera, Coyote, Libero, XLS * reuse_factor: int (Default: 1) * The number of times each multiplier is used by controlling the amount of pipelining/unrolling. A lower number results in more parallelism and lower latency at the expense of the resources used. Reuse factor = 1 corresponds to all multiplications executed in parallel, and hence, the lowest possible latency. - * Available in: Vivado, VivadoAccelerator, Vitis, VitisUnified, Quartus, Catapult, SymbolicExpression, Altera, Coyote, Libero, XLS + * Available in: Vivado, VivadoAccelerator, Vitis, VitisUnified, Catapult, SymbolicExpression, Altera, Coyote, Libero, XLS Conv1D ====== @@ -735,13 +735,13 @@ Backend-specific attributes * The datatype (precision) used to store intermediate results of the computation within the layer. - * Available in: Vivado, VivadoAccelerator, Vitis, VitisUnified, Quartus, Catapult, SymbolicExpression, Altera, Coyote, Libero, XLS + * Available in: Vivado, VivadoAccelerator, Vitis, VitisUnified, Catapult, SymbolicExpression, Altera, Coyote, Libero, XLS * reuse_factor: int (Default: 1) * The number of times each multiplier is used by controlling the amount of pipelining/unrolling. A lower number results in more parallelism and lower latency at the expense of the resources used. Reuse factor = 1 corresponds to all multiplications executed in parallel, and hence, the lowest possible latency. - * Available in: Vivado, VivadoAccelerator, Vitis, VitisUnified, Quartus, Catapult, SymbolicExpression, Altera, Coyote, Libero, XLS + * Available in: Vivado, VivadoAccelerator, Vitis, VitisUnified, Catapult, SymbolicExpression, Altera, Coyote, Libero, XLS * parallelization_factor: int (Default: 1) @@ -827,13 +827,13 @@ Backend-specific attributes * The datatype (precision) used to store intermediate results of the computation within the layer. - * Available in: Vivado, VivadoAccelerator, Vitis, VitisUnified, Quartus, Catapult, SymbolicExpression, Altera, Coyote, Libero, XLS + * Available in: Vivado, VivadoAccelerator, Vitis, VitisUnified, Catapult, SymbolicExpression, Altera, Coyote, Libero, XLS * reuse_factor: int (Default: 1) * The number of times each multiplier is used by controlling the amount of pipelining/unrolling. A lower number results in more parallelism and lower latency at the expense of the resources used. Reuse factor = 1 corresponds to all multiplications executed in parallel, and hence, the lowest possible latency. - * Available in: Vivado, VivadoAccelerator, Vitis, VitisUnified, Quartus, Catapult, SymbolicExpression, Altera, Coyote, Libero, XLS + * Available in: Vivado, VivadoAccelerator, Vitis, VitisUnified, Catapult, SymbolicExpression, Altera, Coyote, Libero, XLS * parallelization_factor: int (Default: 1) @@ -919,13 +919,13 @@ Backend-specific attributes * The datatype (precision) used to store intermediate results of the computation within the layer. - * Available in: Vivado, VivadoAccelerator, Vitis, VitisUnified, Quartus, Catapult, SymbolicExpression, Altera, Coyote, Libero, XLS + * Available in: Vivado, VivadoAccelerator, Vitis, VitisUnified, Catapult, SymbolicExpression, Altera, Coyote, Libero, XLS * reuse_factor: int (Default: 1) * The number of times each multiplier is used by controlling the amount of pipelining/unrolling. A lower number results in more parallelism and lower latency at the expense of the resources used. Reuse factor = 1 corresponds to all multiplications executed in parallel, and hence, the lowest possible latency. - * Available in: Vivado, VivadoAccelerator, Vitis, VitisUnified, Quartus, Catapult, SymbolicExpression, Altera, Coyote, Libero, XLS + * Available in: Vivado, VivadoAccelerator, Vitis, VitisUnified, Catapult, SymbolicExpression, Altera, Coyote, Libero, XLS * parallelization_factor: int (Default: 1) @@ -1005,23 +1005,23 @@ Backend-specific attributes --------------------------- * depthwise_accum_t: NamedType - * Available in: Vivado, VivadoAccelerator, Vitis, VitisUnified, Quartus, Catapult, SymbolicExpression, Altera, Coyote, Libero, XLS + * Available in: Vivado, VivadoAccelerator, Vitis, VitisUnified, Catapult, SymbolicExpression, Altera, Coyote, Libero, XLS * pointwise_accum_t: NamedType - * Available in: Vivado, VivadoAccelerator, Vitis, VitisUnified, Quartus, Catapult, SymbolicExpression, Altera, Coyote, Libero, XLS + * Available in: Vivado, VivadoAccelerator, Vitis, VitisUnified, Catapult, SymbolicExpression, Altera, Coyote, Libero, XLS * depthwise_result_t: NamedType - * Available in: Vivado, VivadoAccelerator, Vitis, VitisUnified, Quartus, Catapult, SymbolicExpression, Altera, Coyote, Libero, XLS + * Available in: Vivado, VivadoAccelerator, Vitis, VitisUnified, Catapult, SymbolicExpression, Altera, Coyote, Libero, XLS * depthwise_reuse_factor: int (Default: 1) - * Available in: Vivado, VivadoAccelerator, Vitis, VitisUnified, Quartus, Catapult, SymbolicExpression, Altera, Coyote, Libero, XLS + * Available in: Vivado, VivadoAccelerator, Vitis, VitisUnified, Catapult, SymbolicExpression, Altera, Coyote, Libero, XLS * pointwise_reuse_factor: int (Default: 1) - * Available in: Vivado, VivadoAccelerator, Vitis, VitisUnified, Quartus, Catapult, SymbolicExpression, Altera, Coyote, Libero, XLS + * Available in: Vivado, VivadoAccelerator, Vitis, VitisUnified, Catapult, SymbolicExpression, Altera, Coyote, Libero, XLS * conv_implementation: list [LineBuffer,Encoded] (Default: LineBuffer) @@ -1123,13 +1123,13 @@ Backend-specific attributes * The datatype (precision) used to store intermediate results of the computation within the layer. - * Available in: Vivado, VivadoAccelerator, Vitis, VitisUnified, Quartus, Catapult, SymbolicExpression, Altera, Coyote, Libero, XLS + * Available in: Vivado, VivadoAccelerator, Vitis, VitisUnified, Catapult, SymbolicExpression, Altera, Coyote, Libero, XLS * reuse_factor: int (Default: 1) * The number of times each multiplier is used by controlling the amount of pipelining/unrolling. A lower number results in more parallelism and lower latency at the expense of the resources used. Reuse factor = 1 corresponds to all multiplications executed in parallel, and hence, the lowest possible latency. - * Available in: Vivado, VivadoAccelerator, Vitis, VitisUnified, Quartus, Catapult, SymbolicExpression, Altera, Coyote, Libero, XLS + * Available in: Vivado, VivadoAccelerator, Vitis, VitisUnified, Catapult, SymbolicExpression, Altera, Coyote, Libero, XLS * parallelization_factor: int (Default: 1) @@ -1221,23 +1221,23 @@ Backend-specific attributes --------------------------- * depthwise_accum_t: NamedType - * Available in: Vivado, VivadoAccelerator, Vitis, VitisUnified, Quartus, Catapult, SymbolicExpression, Altera, Coyote, Libero, XLS + * Available in: Vivado, VivadoAccelerator, Vitis, VitisUnified, Catapult, SymbolicExpression, Altera, Coyote, Libero, XLS * pointwise_accum_t: NamedType - * Available in: Vivado, VivadoAccelerator, Vitis, VitisUnified, Quartus, Catapult, SymbolicExpression, Altera, Coyote, Libero, XLS + * Available in: Vivado, VivadoAccelerator, Vitis, VitisUnified, Catapult, SymbolicExpression, Altera, Coyote, Libero, XLS * depthwise_result_t: NamedType - * Available in: Vivado, VivadoAccelerator, Vitis, VitisUnified, Quartus, Catapult, SymbolicExpression, Altera, Coyote, Libero, XLS + * Available in: Vivado, VivadoAccelerator, Vitis, VitisUnified, Catapult, SymbolicExpression, Altera, Coyote, Libero, XLS * depthwise_reuse_factor: int (Default: 1) - * Available in: Vivado, VivadoAccelerator, Vitis, VitisUnified, Quartus, Catapult, SymbolicExpression, Altera, Coyote, Libero, XLS + * Available in: Vivado, VivadoAccelerator, Vitis, VitisUnified, Catapult, SymbolicExpression, Altera, Coyote, Libero, XLS * pointwise_reuse_factor: int (Default: 1) - * Available in: Vivado, VivadoAccelerator, Vitis, VitisUnified, Quartus, Catapult, SymbolicExpression, Altera, Coyote, Libero, XLS + * Available in: Vivado, VivadoAccelerator, Vitis, VitisUnified, Catapult, SymbolicExpression, Altera, Coyote, Libero, XLS * conv_implementation: list [LineBuffer,Encoded] (Default: LineBuffer) @@ -1363,13 +1363,13 @@ Backend-specific attributes * The datatype (precision) used to store intermediate results of the computation within the layer. - * Available in: Vivado, VivadoAccelerator, Vitis, VitisUnified, Quartus, Catapult, SymbolicExpression, Altera, Coyote, Libero, XLS + * Available in: Vivado, VivadoAccelerator, Vitis, VitisUnified, Catapult, SymbolicExpression, Altera, Coyote, Libero, XLS * reuse_factor: int (Default: 1) * The number of times each multiplier is used by controlling the amount of pipelining/unrolling. A lower number results in more parallelism and lower latency at the expense of the resources used. Reuse factor = 1 corresponds to all multiplications executed in parallel, and hence, the lowest possible latency. - * Available in: Vivado, VivadoAccelerator, Vitis, VitisUnified, Quartus, Catapult, SymbolicExpression, Altera, Coyote, Libero, XLS + * Available in: Vivado, VivadoAccelerator, Vitis, VitisUnified, Catapult, SymbolicExpression, Altera, Coyote, Libero, XLS * parallelization_factor: int (Default: 1) @@ -1435,7 +1435,7 @@ Backend-specific attributes * The number of times each multiplier is used by controlling the amount of pipelining/unrolling. A lower number results in more parallelism and lower latency at the expense of the resources used. Reuse factor = 1 corresponds to all multiplications executed in parallel, and hence, the lowest possible latency. - * Available in: Vivado, VivadoAccelerator, Vitis, VitisUnified, Quartus, Catapult, SymbolicExpression, Altera, Coyote, Libero, XLS + * Available in: Vivado, VivadoAccelerator, Vitis, VitisUnified, Catapult, SymbolicExpression, Altera, Coyote, Libero, XLS Pooling1D ========= @@ -1485,13 +1485,13 @@ Backend-specific attributes * The datatype (precision) used to store intermediate results of the computation within the layer. - * Available in: Vivado, VivadoAccelerator, Vitis, VitisUnified, Quartus, Catapult, SymbolicExpression, Altera, Coyote, Libero, XLS + * Available in: Vivado, VivadoAccelerator, Vitis, VitisUnified, Catapult, SymbolicExpression, Altera, Coyote, Libero, XLS * reuse_factor: int (Default: 1) * The number of times each multiplier is used by controlling the amount of pipelining/unrolling. A lower number results in more parallelism and lower latency at the expense of the resources used. Reuse factor = 1 corresponds to all multiplications executed in parallel, and hence, the lowest possible latency. - * Available in: Vivado, VivadoAccelerator, Vitis, VitisUnified, Quartus, Catapult, SymbolicExpression, Altera, Coyote, Libero, XLS + * Available in: Vivado, VivadoAccelerator, Vitis, VitisUnified, Catapult, SymbolicExpression, Altera, Coyote, Libero, XLS * conv_implementation: list [LineBuffer,Encoded] (Default: LineBuffer) @@ -1559,13 +1559,13 @@ Backend-specific attributes * The datatype (precision) used to store intermediate results of the computation within the layer. - * Available in: Vivado, VivadoAccelerator, Vitis, VitisUnified, Quartus, Catapult, SymbolicExpression, Altera, Coyote, Libero, XLS + * Available in: Vivado, VivadoAccelerator, Vitis, VitisUnified, Catapult, SymbolicExpression, Altera, Coyote, Libero, XLS * reuse_factor: int (Default: 1) * The number of times each multiplier is used by controlling the amount of pipelining/unrolling. A lower number results in more parallelism and lower latency at the expense of the resources used. Reuse factor = 1 corresponds to all multiplications executed in parallel, and hence, the lowest possible latency. - * Available in: Vivado, VivadoAccelerator, Vitis, VitisUnified, Quartus, Catapult, SymbolicExpression, Altera, Coyote, Libero, XLS + * Available in: Vivado, VivadoAccelerator, Vitis, VitisUnified, Catapult, SymbolicExpression, Altera, Coyote, Libero, XLS * conv_implementation: list [LineBuffer,Encoded] (Default: LineBuffer) @@ -1611,13 +1611,13 @@ Backend-specific attributes * The datatype (precision) used to store intermediate results of the computation within the layer. - * Available in: Vivado, VivadoAccelerator, Vitis, VitisUnified, Quartus, Catapult, SymbolicExpression, Altera, Coyote, Libero, XLS + * Available in: Vivado, VivadoAccelerator, Vitis, VitisUnified, Catapult, SymbolicExpression, Altera, Coyote, Libero, XLS * reuse_factor: int (Default: 1) * The number of times each multiplier is used by controlling the amount of pipelining/unrolling. A lower number results in more parallelism and lower latency at the expense of the resources used. Reuse factor = 1 corresponds to all multiplications executed in parallel, and hence, the lowest possible latency. - * Available in: Vivado, VivadoAccelerator, Vitis, VitisUnified, Quartus, Catapult, SymbolicExpression, Altera, Coyote, Libero, XLS + * Available in: Vivado, VivadoAccelerator, Vitis, VitisUnified, Catapult, SymbolicExpression, Altera, Coyote, Libero, XLS GlobalPooling2D =============== @@ -1659,13 +1659,13 @@ Backend-specific attributes * The datatype (precision) used to store intermediate results of the computation within the layer. - * Available in: Vivado, VivadoAccelerator, Vitis, VitisUnified, Quartus, Catapult, SymbolicExpression, Altera, Coyote, Libero, XLS + * Available in: Vivado, VivadoAccelerator, Vitis, VitisUnified, Catapult, SymbolicExpression, Altera, Coyote, Libero, XLS * reuse_factor: int (Default: 1) * The number of times each multiplier is used by controlling the amount of pipelining/unrolling. A lower number results in more parallelism and lower latency at the expense of the resources used. Reuse factor = 1 corresponds to all multiplications executed in parallel, and hence, the lowest possible latency. - * Available in: Vivado, VivadoAccelerator, Vitis, VitisUnified, Quartus, Catapult, SymbolicExpression, Altera, Coyote, Libero, XLS + * Available in: Vivado, VivadoAccelerator, Vitis, VitisUnified, Catapult, SymbolicExpression, Altera, Coyote, Libero, XLS ZeroPadding1D ============= @@ -1849,7 +1849,7 @@ Backend-specific attributes * The number of times each multiplier is used by controlling the amount of pipelining/unrolling. A lower number results in more parallelism and lower latency at the expense of the resources used. Reuse factor = 1 corresponds to all multiplications executed in parallel, and hence, the lowest possible latency. - * Available in: Vivado, VivadoAccelerator, Vitis, VitisUnified, Quartus, Catapult, SymbolicExpression, Altera, Coyote, Libero, XLS + * Available in: Vivado, VivadoAccelerator, Vitis, VitisUnified, Catapult, SymbolicExpression, Altera, Coyote, Libero, XLS MatMul ====== @@ -1881,13 +1881,13 @@ Backend-specific attributes * The datatype (precision) used to store intermediate results of the computation within the layer. - * Available in: Vivado, VivadoAccelerator, Vitis, VitisUnified, Quartus, Catapult, SymbolicExpression, Altera, Coyote, Libero, XLS + * Available in: Vivado, VivadoAccelerator, Vitis, VitisUnified, Catapult, SymbolicExpression, Altera, Coyote, Libero, XLS * reuse_factor: int (Default: 1) * The number of times each multiplier is used by controlling the amount of pipelining/unrolling. A lower number results in more parallelism and lower latency at the expense of the resources used. Reuse factor = 1 corresponds to all multiplications executed in parallel, and hence, the lowest possible latency. - * Available in: Vivado, VivadoAccelerator, Vitis, VitisUnified, Quartus, Catapult, SymbolicExpression, Altera, Coyote, Libero, XLS + * Available in: Vivado, VivadoAccelerator, Vitis, VitisUnified, Catapult, SymbolicExpression, Altera, Coyote, Libero, XLS Dot === @@ -1935,13 +1935,13 @@ Backend-specific attributes * The datatype (precision) used to store intermediate results of the computation within the layer. - * Available in: Vivado, VivadoAccelerator, Vitis, VitisUnified, Quartus, Catapult, SymbolicExpression, Altera, Coyote, Libero, XLS + * Available in: Vivado, VivadoAccelerator, Vitis, VitisUnified, Catapult, SymbolicExpression, Altera, Coyote, Libero, XLS * reuse_factor: int (Default: 1) * The number of times each multiplier is used by controlling the amount of pipelining/unrolling. A lower number results in more parallelism and lower latency at the expense of the resources used. Reuse factor = 1 corresponds to all multiplications executed in parallel, and hence, the lowest possible latency. - * Available in: Vivado, Vivado, VivadoAccelerator, VivadoAccelerator, Vitis, Vitis, VitisUnified, VitisUnified, Quartus, Quartus, Catapult, Catapult, SymbolicExpression, SymbolicExpression, Altera, Altera, Coyote, Coyote, Libero, Libero, XLS, XLS + * Available in: Vivado, Vivado, VivadoAccelerator, VivadoAccelerator, Vitis, Vitis, VitisUnified, VitisUnified, Catapult, Catapult, SymbolicExpression, SymbolicExpression, Altera, Altera, Coyote, Coyote, Libero, Libero, XLS, XLS Concatenate =========== @@ -1989,7 +1989,7 @@ Backend-specific attributes * The number of times each multiplier is used by controlling the amount of pipelining/unrolling. A lower number results in more parallelism and lower latency at the expense of the resources used. Reuse factor = 1 corresponds to all multiplications executed in parallel, and hence, the lowest possible latency. - * Available in: Vivado, VivadoAccelerator, Vitis, VitisUnified, Quartus, Catapult, SymbolicExpression, Altera, Coyote, Libero, XLS + * Available in: Vivado, VivadoAccelerator, Vitis, VitisUnified, Catapult, SymbolicExpression, Altera, Coyote, Libero, XLS Resize ====== @@ -2097,7 +2097,7 @@ Backend-specific attributes * The number of times each multiplier is used by controlling the amount of pipelining/unrolling. A lower number results in more parallelism and lower latency at the expense of the resources used. Reuse factor = 1 corresponds to all multiplications executed in parallel, and hence, the lowest possible latency. - * Available in: Vivado, VivadoAccelerator, Vitis, VitisUnified, Quartus, Catapult, SymbolicExpression, Altera, Coyote, Libero, XLS + * Available in: Vivado, VivadoAccelerator, Vitis, VitisUnified, Catapult, SymbolicExpression, Altera, Coyote, Libero, XLS SimpleRNN ========= @@ -2161,13 +2161,13 @@ Backend-specific attributes * The datatype (precision) used to store intermediate results of the computation within the layer. - * Available in: Vivado, VivadoAccelerator, Vitis, VitisUnified, Quartus, Catapult, SymbolicExpression, Altera, Coyote, Libero, XLS + * Available in: Vivado, VivadoAccelerator, Vitis, VitisUnified, Catapult, SymbolicExpression, Altera, Coyote, Libero, XLS * reuse_factor: int (Default: 1) * The number of times each multiplier is used by controlling the amount of pipelining/unrolling. A lower number results in more parallelism and lower latency at the expense of the resources used. Reuse factor = 1 corresponds to all multiplications executed in parallel, and hence, the lowest possible latency. - * Available in: Vivado, VivadoAccelerator, Vitis, VitisUnified, Quartus, Catapult, SymbolicExpression, Altera, Coyote, Libero, XLS + * Available in: Vivado, VivadoAccelerator, Vitis, VitisUnified, Catapult, SymbolicExpression, Altera, Coyote, Libero, XLS * static: bool (Default: True) @@ -2179,19 +2179,19 @@ Backend-specific attributes * The size of the lookup table used to approximate the function. - * Available in: Vivado, VivadoAccelerator, Vitis, VitisUnified, Quartus, Catapult, Altera, Coyote + * Available in: Vivado, VivadoAccelerator, Vitis, VitisUnified, Catapult, Altera, Coyote * table_t: NamedType (Default: fixed<18,8,TRN,WRAP,0>) * The datatype (precision) used for the values of the lookup table. - * Available in: Vivado, VivadoAccelerator, Vitis, VitisUnified, Quartus, Catapult, Altera, Coyote + * Available in: Vivado, VivadoAccelerator, Vitis, VitisUnified, Catapult, Altera, Coyote * recurrent_reuse_factor: int (Default: 1) * The number of times each multiplier is used by controlling the amount of pipelining/unrolling. A lower number results in more parallelism and lower latency at the expense of the resources used. Reuse factor = 1 corresponds to all multiplications executed in parallel, and hence, the lowest possible latency. - * Available in: Quartus, Catapult + * Available in: Catapult LSTM ==== @@ -2265,13 +2265,13 @@ Backend-specific attributes * The datatype (precision) used to store intermediate results of the computation within the layer. - * Available in: Vivado, VivadoAccelerator, Vitis, VitisUnified, Quartus, Catapult, SymbolicExpression, Altera, Coyote, Libero, XLS + * Available in: Vivado, VivadoAccelerator, Vitis, VitisUnified, Catapult, SymbolicExpression, Altera, Coyote, Libero, XLS * reuse_factor: int (Default: 1) * The number of times each multiplier is used by controlling the amount of pipelining/unrolling. A lower number results in more parallelism and lower latency at the expense of the resources used. Reuse factor = 1 corresponds to all multiplications executed in parallel, and hence, the lowest possible latency. - * Available in: Vivado, VivadoAccelerator, Vitis, VitisUnified, Quartus, Catapult, SymbolicExpression, Altera, Coyote, Libero, XLS + * Available in: Vivado, VivadoAccelerator, Vitis, VitisUnified, Catapult, SymbolicExpression, Altera, Coyote, Libero, XLS * static: bool (Default: True) @@ -2283,19 +2283,19 @@ Backend-specific attributes * The size of the lookup table used to approximate the function. - * Available in: Vivado, VivadoAccelerator, Vitis, VitisUnified, Quartus, Catapult, Altera, Coyote + * Available in: Vivado, VivadoAccelerator, Vitis, VitisUnified, Catapult, Altera, Coyote * table_t: NamedType (Default: fixed<18,8,TRN,WRAP,0>) * The datatype (precision) used for the values of the lookup table. - * Available in: Vivado, VivadoAccelerator, Vitis, VitisUnified, Quartus, Catapult, Altera, Coyote + * Available in: Vivado, VivadoAccelerator, Vitis, VitisUnified, Catapult, Altera, Coyote * recurrent_reuse_factor: int (Default: 1) * The number of times each multiplier is used by controlling the amount of pipelining/unrolling. A lower number results in more parallelism and lower latency at the expense of the resources used. Reuse factor = 1 corresponds to all multiplications executed in parallel, and hence, the lowest possible latency. - * Available in: Quartus, Catapult + * Available in: Catapult GRU === @@ -2371,13 +2371,13 @@ Backend-specific attributes * The datatype (precision) used to store intermediate results of the computation within the layer. - * Available in: Vivado, VivadoAccelerator, Vitis, VitisUnified, Quartus, Catapult, SymbolicExpression, Altera, Coyote, Libero, XLS + * Available in: Vivado, VivadoAccelerator, Vitis, VitisUnified, Catapult, SymbolicExpression, Altera, Coyote, Libero, XLS * reuse_factor: int (Default: 1) * The number of times each multiplier is used by controlling the amount of pipelining/unrolling. A lower number results in more parallelism and lower latency at the expense of the resources used. Reuse factor = 1 corresponds to all multiplications executed in parallel, and hence, the lowest possible latency. - * Available in: Vivado, VivadoAccelerator, Vitis, VitisUnified, Quartus, Catapult, SymbolicExpression, Altera, Coyote, Libero, XLS + * Available in: Vivado, VivadoAccelerator, Vitis, VitisUnified, Catapult, SymbolicExpression, Altera, Coyote, Libero, XLS * static: bool (Default: True) @@ -2389,19 +2389,19 @@ Backend-specific attributes * The size of the lookup table used to approximate the function. - * Available in: Vivado, VivadoAccelerator, Vitis, VitisUnified, Quartus, Catapult, Altera, Coyote + * Available in: Vivado, VivadoAccelerator, Vitis, VitisUnified, Catapult, Altera, Coyote * table_t: NamedType (Default: fixed<18,8,TRN,WRAP,0>) * The datatype (precision) used for the values of the lookup table. - * Available in: Vivado, VivadoAccelerator, Vitis, VitisUnified, Quartus, Catapult, Altera, Coyote + * Available in: Vivado, VivadoAccelerator, Vitis, VitisUnified, Catapult, Altera, Coyote * recurrent_reuse_factor: int (Default: 1) * The number of times each multiplier is used by controlling the amount of pipelining/unrolling. A lower number results in more parallelism and lower latency at the expense of the resources used. Reuse factor = 1 corresponds to all multiplications executed in parallel, and hence, the lowest possible latency. - * Available in: Quartus, Catapult + * Available in: Catapult Bidirectional ============= @@ -2501,13 +2501,13 @@ Backend-specific attributes * The datatype (precision) used to store intermediate results of the computation within the layer. - * Available in: Vivado, VivadoAccelerator, Vitis, VitisUnified, Quartus, Catapult, SymbolicExpression, Altera, Coyote, Libero, XLS + * Available in: Vivado, VivadoAccelerator, Vitis, VitisUnified, Catapult, SymbolicExpression, Altera, Coyote, Libero, XLS * reuse_factor: int (Default: 1) * The number of times each multiplier is used by controlling the amount of pipelining/unrolling. A lower number results in more parallelism and lower latency at the expense of the resources used. Reuse factor = 1 corresponds to all multiplications executed in parallel, and hence, the lowest possible latency. - * Available in: Vivado, VivadoAccelerator, Vitis, VitisUnified, Quartus, Catapult, SymbolicExpression, Altera, Coyote, Libero, XLS + * Available in: Vivado, VivadoAccelerator, Vitis, VitisUnified, Catapult, SymbolicExpression, Altera, Coyote, Libero, XLS * static: bool (Default: True) @@ -2595,7 +2595,7 @@ Backend-specific attributes * The number of times each multiplier is used by controlling the amount of pipelining/unrolling. A lower number results in more parallelism and lower latency at the expense of the resources used. Reuse factor = 1 corresponds to all multiplications executed in parallel, and hence, the lowest possible latency. - * Available in: Vivado, Vivado, VivadoAccelerator, VivadoAccelerator, Vitis, Vitis, VitisUnified, VitisUnified, Quartus, Quartus, Catapult, Catapult, SymbolicExpression, SymbolicExpression, Altera, Altera, Coyote, Coyote, Libero, Libero, XLS, XLS + * Available in: Vivado, Vivado, VivadoAccelerator, VivadoAccelerator, Vitis, Vitis, VitisUnified, VitisUnified, Catapult, Catapult, SymbolicExpression, SymbolicExpression, Altera, Altera, Coyote, Coyote, Libero, Libero, XLS, XLS GarNetStack =========== @@ -2643,7 +2643,7 @@ Backend-specific attributes * The number of times each multiplier is used by controlling the amount of pipelining/unrolling. A lower number results in more parallelism and lower latency at the expense of the resources used. Reuse factor = 1 corresponds to all multiplications executed in parallel, and hence, the lowest possible latency. - * Available in: Vivado, Vivado, VivadoAccelerator, VivadoAccelerator, Vitis, Vitis, VitisUnified, VitisUnified, Quartus, Quartus, Catapult, Catapult, SymbolicExpression, SymbolicExpression, Altera, Altera, Coyote, Coyote, Libero, Libero, XLS, XLS + * Available in: Vivado, Vivado, VivadoAccelerator, VivadoAccelerator, Vitis, Vitis, VitisUnified, VitisUnified, Catapult, Catapult, SymbolicExpression, SymbolicExpression, Altera, Altera, Coyote, Coyote, Libero, Libero, XLS, XLS Quant ===== @@ -2681,7 +2681,7 @@ Backend-specific attributes * The number of times each multiplier is used by controlling the amount of pipelining/unrolling. A lower number results in more parallelism and lower latency at the expense of the resources used. Reuse factor = 1 corresponds to all multiplications executed in parallel, and hence, the lowest possible latency. - * Available in: Vivado, VivadoAccelerator, Vitis, VitisUnified, Quartus, Catapult, SymbolicExpression, Altera, Coyote, Libero, XLS + * Available in: Vivado, VivadoAccelerator, Vitis, VitisUnified, Catapult, SymbolicExpression, Altera, Coyote, Libero, XLS BipolarQuant ============ @@ -2759,7 +2759,7 @@ Backend-specific attributes * The number of times each multiplier is used by controlling the amount of pipelining/unrolling. A lower number results in more parallelism and lower latency at the expense of the resources used. Reuse factor = 1 corresponds to all multiplications executed in parallel, and hence, the lowest possible latency. - * Available in: Vivado, VivadoAccelerator, Vitis, VitisUnified, Quartus, Catapult, SymbolicExpression, Altera, Coyote, Libero, XLS + * Available in: Vivado, VivadoAccelerator, Vitis, VitisUnified, Catapult, SymbolicExpression, Altera, Coyote, Libero, XLS BatchNormOnnx ============= @@ -2791,7 +2791,7 @@ Backend-specific attributes * The number of times each multiplier is used by controlling the amount of pipelining/unrolling. A lower number results in more parallelism and lower latency at the expense of the resources used. Reuse factor = 1 corresponds to all multiplications executed in parallel, and hence, the lowest possible latency. - * Available in: Vivado, VivadoAccelerator, Vitis, VitisUnified, Quartus, Catapult, SymbolicExpression, Altera, Coyote, Libero, XLS + * Available in: Vivado, VivadoAccelerator, Vitis, VitisUnified, Catapult, SymbolicExpression, Altera, Coyote, Libero, XLS LayerGroup ========== @@ -2909,13 +2909,13 @@ Backend-specific attributes * The datatype (precision) used to store intermediate results of the computation within the layer. - * Available in: Vivado, VivadoAccelerator, Vitis, VitisUnified, Quartus, Catapult, SymbolicExpression, Altera, Coyote, Libero, XLS + * Available in: Vivado, VivadoAccelerator, Vitis, VitisUnified, Catapult, SymbolicExpression, Altera, Coyote, Libero, XLS * reuse_factor: int (Default: 1) * The number of times each multiplier is used by controlling the amount of pipelining/unrolling. A lower number results in more parallelism and lower latency at the expense of the resources used. Reuse factor = 1 corresponds to all multiplications executed in parallel, and hence, the lowest possible latency. - * Available in: Vivado, VivadoAccelerator, Vitis, VitisUnified, Quartus, Catapult, SymbolicExpression, Altera, Coyote, Libero, XLS + * Available in: Vivado, VivadoAccelerator, Vitis, VitisUnified, Catapult, SymbolicExpression, Altera, Coyote, Libero, XLS * table_range_power2: int (Default: 0) @@ -3071,7 +3071,7 @@ Backend-specific attributes * The number of times each multiplier is used by controlling the amount of pipelining/unrolling. A lower number results in more parallelism and lower latency at the expense of the resources used. Reuse factor = 1 corresponds to all multiplications executed in parallel, and hence, the lowest possible latency. - * Available in: Vivado, VivadoAccelerator, Vitis, VitisUnified, Quartus, Catapult, SymbolicExpression, Altera, Coyote, Libero, XLS + * Available in: Vivado, VivadoAccelerator, Vitis, VitisUnified, Catapult, SymbolicExpression, Altera, Coyote, Libero, XLS DACombinational =============== @@ -3345,30 +3345,6 @@ Configurable attributes * table_t: NamedType (Default: fixed<18,8,TRN,WRAP,0>) -Clone -===== -Base attributes ---------------- -* result_t: NamedType - - * The datatype (precision) of the output tensor. - -Type attributes ---------------- -* index: int - - * Internal node counter used for bookkeeping and variable/tensor naming. - -Configurable attributes ------------------------ -* trace: int (Default: False) - - * Enables saving of layer output (tracing) when using hls_model.predict(...) or hls_model.trace(...) - -* result_t: NamedType - - * The datatype (precision) of the output tensor. - Repack ====== Base attributes @@ -3393,26 +3369,20 @@ Configurable attributes * The datatype (precision) of the output tensor. -BatchNormalizationQuantizedTanh -=============================== +Clone +===== Base attributes --------------- * result_t: NamedType * The datatype (precision) of the output tensor. -* accum_t: NamedType - Type attributes --------------- * index: int * Internal node counter used for bookkeeping and variable/tensor naming. -* n_in: int - -* n_filt: int (Default: 0) - Configurable attributes ----------------------- * trace: int (Default: False) @@ -3423,10 +3393,6 @@ Configurable attributes * The datatype (precision) of the output tensor. -* accum_t: NamedType - -* reuse_factor: int (Default: 1) - Broadcast ========= Base attributes @@ -3511,13 +3477,13 @@ Backend-specific attributes * The datatype (precision) used to store intermediate results of the computation within the layer. - * Available in: Vivado, VivadoAccelerator, Vitis, VitisUnified, Quartus, Catapult, SymbolicExpression, Altera, Coyote, Libero, XLS + * Available in: Vivado, VivadoAccelerator, Vitis, VitisUnified, Catapult, SymbolicExpression, Altera, Coyote, Libero, XLS * reuse_factor: int (Default: 1) * The number of times each multiplier is used by controlling the amount of pipelining/unrolling. A lower number results in more parallelism and lower latency at the expense of the resources used. Reuse factor = 1 corresponds to all multiplications executed in parallel, and hence, the lowest possible latency. - * Available in: Vivado, VivadoAccelerator, Vitis, VitisUnified, Quartus, Catapult, SymbolicExpression, Altera, Coyote, Libero, XLS + * Available in: Vivado, VivadoAccelerator, Vitis, VitisUnified, Catapult, SymbolicExpression, Altera, Coyote, Libero, XLS * parallelization_factor: int (Default: 1) @@ -3603,13 +3569,13 @@ Backend-specific attributes * The datatype (precision) used to store intermediate results of the computation within the layer. - * Available in: Vivado, VivadoAccelerator, Vitis, VitisUnified, Quartus, Catapult, SymbolicExpression, Altera, Coyote, Libero, XLS + * Available in: Vivado, VivadoAccelerator, Vitis, VitisUnified, Catapult, SymbolicExpression, Altera, Coyote, Libero, XLS * reuse_factor: int (Default: 1) * The number of times each multiplier is used by controlling the amount of pipelining/unrolling. A lower number results in more parallelism and lower latency at the expense of the resources used. Reuse factor = 1 corresponds to all multiplications executed in parallel, and hence, the lowest possible latency. - * Available in: Vivado, VivadoAccelerator, Vitis, VitisUnified, Quartus, Catapult, SymbolicExpression, Altera, Coyote, Libero, XLS + * Available in: Vivado, VivadoAccelerator, Vitis, VitisUnified, Catapult, SymbolicExpression, Altera, Coyote, Libero, XLS * parallelization_factor: int (Default: 1) @@ -3622,3 +3588,37 @@ Backend-specific attributes * "LineBuffer" implementation is preferred over "Encoded" for most use cases. This attribute only applies to io_stream. * Available in: Vivado, VivadoAccelerator, Vitis, VitisUnified, Catapult, Coyote + +BatchNormalizationQuantizedTanh +=============================== +Base attributes +--------------- +* result_t: NamedType + + * The datatype (precision) of the output tensor. + +* accum_t: NamedType + +Type attributes +--------------- +* index: int + + * Internal node counter used for bookkeeping and variable/tensor naming. + +* n_in: int + +* n_filt: int (Default: 0) + +Configurable attributes +----------------------- +* trace: int (Default: False) + + * Enables saving of layer output (tracing) when using hls_model.predict(...) or hls_model.trace(...) + +* result_t: NamedType + + * The datatype (precision) of the output tensor. + +* accum_t: NamedType + +* reuse_factor: int (Default: 1) diff --git a/docs/ir/flows.rst b/docs/ir/flows.rst index 511b5b7753..012bdbd490 100644 --- a/docs/ir/flows.rst +++ b/docs/ir/flows.rst @@ -18,8 +18,8 @@ perform the transformation from the ``transform`` function. The boolean return v model graph that may require running the optimizers again. In that case, optimizers in a flow are run again. Optimizers can be general, independent of the backend, in which case they are located in :py:mod:`hls4ml.model.optimizer.passes`, or they may be backend-specific, -in which case they are located in a folder dependent on the backend, e.g., :py:mod:`hls4ml.backends.vivado.passes` or -:py:mod:`hls4ml.backends.quartus.passes`. A common set of optimizers that are used by FPGA backends are located in :py:mod:`hls4ml.backends.fpga.passes`. +in which case they are located in a folder dependent on the backend, e.g., :py:mod:`hls4ml.backends.vivado.passes`. +A common set of optimizers that are used by FPGA backends are located in :py:mod:`hls4ml.backends.fpga.passes`. Certain optimizers are used frequently enough that it makes sense to define special classes, which inherit from :py:class:`~hls4ml.model.optimizer.optimizer.OptimizerPass`: diff --git a/docs/ir/ir.rst b/docs/ir/ir.rst index 92d2810d3b..1991fce6e0 100644 --- a/docs/ir/ir.rst +++ b/docs/ir/ir.rst @@ -53,7 +53,7 @@ io_parallel Parallel IO is applicable to small models that require a low-latency implementation. Larger models face synthesizability limits very quickly. -In Vivado/Vitis backends, parallel convolution relies on the *im2col* transformation of the input, which turns convolution into a matrix-multiplication task. This task is then implemented as a sequence of matrix-vector multiplications using the routine mentioned above. The ``Latency`` and ``Resource`` strategies refer to the function used for the matrix-vector multiplication routine, with ``Resource`` allowing slightly larger models to be synthesized. Parallelism can be further controlled via the ``ParallelizationFactor``. The Catapult backend in turn uses a direct implementation of convolution via nested loops. The ``Quartus``, ``Altera``, and ``Catapult`` backends also implement a ``Winograd`` algorithm choosable by setting the ``implementation`` to ``Winograd`` or ``combination``. Winograd implementation is available for only a handful of filter size configurations, and it is less concerned about bit accuracy and overflow. In certain conditions it can be faster. +In Vivado/Vitis backends, parallel convolution relies on the *im2col* transformation of the input, which turns convolution into a matrix-multiplication task. This task is then implemented as a sequence of matrix-vector multiplications using the routine mentioned above. The ``Latency`` and ``Resource`` strategies refer to the function used for the matrix-vector multiplication routine, with ``Resource`` allowing slightly larger models to be synthesized. Parallelism can be further controlled via the ``ParallelizationFactor``. The Catapult backend in turn uses a direct implementation of convolution via nested loops. The ``Altera`` and ``Catapult`` backends also implement a ``Winograd`` algorithm choosable by setting the ``implementation`` to ``Winograd`` or ``combination``. Winograd implementation is available for only a handful of filter size configurations, and it is less concerned about bit accuracy and overflow. In certain conditions it can be faster. io_stream ^^^^^^^^^ diff --git a/hls4ml/backends/__init__.py b/hls4ml/backends/__init__.py index b094c5dca3..b46117cac1 100644 --- a/hls4ml/backends/__init__.py +++ b/hls4ml/backends/__init__.py @@ -4,7 +4,6 @@ from hls4ml.backends.altera.altera_backend import AlteraBackend # isort: skip from hls4ml.backends.plugin_loader import load_backend_plugins -from hls4ml.backends.quartus.quartus_backend import QuartusBackend from hls4ml.backends.symbolic.symbolic_backend import SymbolicExpressionBackend from hls4ml.backends.vivado.vivado_backend import VivadoBackend from hls4ml.backends.vivado_accelerator.vivado_accelerator_backend import VivadoAcceleratorBackend @@ -24,7 +23,6 @@ def _register_builtin_backends(): register_backend('VivadoAccelerator', VivadoAcceleratorBackend) register_backend('Vitis', VitisBackend) register_backend('VitisUnified', VitisUnifiedBackend) - register_backend('Quartus', QuartusBackend) register_backend('Catapult', CatapultBackend) register_backend('SymbolicExpression', SymbolicExpressionBackend) register_backend('Altera', AlteraBackend) diff --git a/hls4ml/backends/fpga/passes/fix_softmax_table_size.py b/hls4ml/backends/fpga/passes/fix_softmax_table_size.py index 28ae404d41..7deba3a752 100644 --- a/hls4ml/backends/fpga/passes/fix_softmax_table_size.py +++ b/hls4ml/backends/fpga/passes/fix_softmax_table_size.py @@ -21,17 +21,11 @@ def transform(self, model, node: Layer): table_bw: int = node.get_attr('inv_table_t').precision.width # type: ignore table_size = int(node.get_attr('table_size')) # type: ignore - backend = model.config.config['Backend'] - - # Somehow, Intel want one extra bits for the table. - # I don't know why but if not simulation will crash with segmentation fault. - backend_limitation = -1 if backend == 'Quartus' else 0 - - if 2 ** (min(input_bw, table_bw) + backend_limitation) < table_size: + if 2 ** min(input_bw, table_bw) < table_size: # If table size is too large w.r.t. input bitwidth and table bitwidth, # reduce table size to avoid undefined behavior when cutting indices from, # fixed point number. - node.set_attr('table_size', str(2 ** (min(input_bw, table_bw) + backend_limitation))) + node.set_attr('table_size', str(2 ** min(input_bw, table_bw))) if 2**input_bw < table_size: # The warning message does not have to be looking like this, but you are asking # 125 characters long line. @@ -54,14 +48,6 @@ def transform(self, model, node: Layer): ), stacklevel=1, ) - if backend == 'Quartus': - warnings.warn( - ( - "Quartus backend's table size is half of 2^min(input_bw-1,table_bw-1)" - ' instead of 2^min(input_bw,table_bw).' - ), - stacklevel=1, - ) return False diff --git a/hls4ml/backends/fpga/passes/hgq_proxy_model.py b/hls4ml/backends/fpga/passes/hgq_proxy_model.py index f3d5b765de..d512de70fe 100644 --- a/hls4ml/backends/fpga/passes/hgq_proxy_model.py +++ b/hls4ml/backends/fpga/passes/hgq_proxy_model.py @@ -27,12 +27,12 @@ def generate_mask_fn( ) -> str: """Generate heterogeneous quantization mask function, ONLY works for IOType=io_parallel""" assert k.shape[0] == b.shape[0] == i.shape[0] == 1 - assert backend.lower() in ('altera', 'quartus', 'vivado', 'vitis'), f'Backend {backend} not tested' + assert backend.lower() in ('altera', 'vivado', 'vitis'), f'Backend {backend} not tested' Ks, Bs, Is = k[0], b[0], i[0] Ks, Bs, Is = np.broadcast_to(Ks, shape), np.broadcast_to(Bs, shape), np.broadcast_to(Is, shape) Ks, Bs, Is = Ks.ravel(), Bs.ravel(), Is.ravel() masks = [] - to_fixed = to_acfixed if backend.lower() in ['altera', 'quartus'] else to_apfixed + to_fixed = to_acfixed if backend.lower() == 'altera' else to_apfixed for idx, (k, b, i) in enumerate(zip(Ks, Bs, Is)): if b == 0: fn = f'out[{idx}] = 0;' @@ -40,13 +40,11 @@ def generate_mask_fn( fn = f'out[{idx}] = {to_fixed(k, b, i, RND, SAT)}(inp[{idx}]);' masks.append(f' {fn}') body = '\n'.join(masks) - arguments = ( - 'input_t *inp, output_t *out' if backend.lower() not in ['altera', 'quartus'] else 'input_t &inp, output_t &out' - ) + arguments = 'input_t *inp, output_t *out' if backend.lower() != 'altera' else 'input_t &inp, output_t &out' mask_fn = f""" template void {name}({arguments}) {{ - {'#pragma HLS INLINE' if backend.lower() not in ['altera', 'quartus'] else ''} + {'#pragma HLS INLINE' if backend.lower() != 'altera' else ''} {body} }} diff --git a/hls4ml/backends/quartus/__init__.py b/hls4ml/backends/quartus/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/hls4ml/backends/quartus/passes/__init__.py b/hls4ml/backends/quartus/passes/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/hls4ml/backends/quartus/passes/bn_quant.py b/hls4ml/backends/quartus/passes/bn_quant.py deleted file mode 100644 index d74434bcdf..0000000000 --- a/hls4ml/backends/quartus/passes/bn_quant.py +++ /dev/null @@ -1,169 +0,0 @@ -import numpy as np - -from hls4ml.backends.fpga.fpga_layers import BatchNormalizationQuantizedTanh -from hls4ml.backends.template import FunctionCallTemplate, LayerConfigTemplate -from hls4ml.model.layers import BatchNormalization, register_layer -from hls4ml.model.optimizer import OptimizerPass -from hls4ml.model.types import IntegerPrecisionType, NamedType, XnorPrecisionType - -batchnorm_quantized_tanh_config_template = """struct config{index} : nnet::batchnorm_quantized_tanh_config {{ - static const unsigned n_in = {n_in}; - static const unsigned n_filt = {n_filt}; - static const unsigned n_scale_bias = (n_filt == -1) ? n_in : n_filt; - static const unsigned io_type = nnet::{iotype}; - static const unsigned reuse_factor = {reuse}; -}};\n""" - -batchnorm_quantized_tanh_function_template = ( - 'nnet::normalize_{quantize}_tanh<{input_t}, {config}>({input}, {output}, {threshold});' -) - -bn_include_list = ['nnet_utils/nnet_batchnorm.h', 'nnet_utils/nnet_batchnorm_stream.h'] - - -class BatchNormalizationQuantizedTanhConfigTemplate(LayerConfigTemplate): - def __init__(self): - super().__init__(BatchNormalizationQuantizedTanh) - self.template = batchnorm_quantized_tanh_config_template - - def format(self, node): - params = self._default_config_params(node) - params['n_in'] = node.get_input_variable().size_cpp() - - return self.template.format(**params) - - -class BatchNormalizationQuantizedTanhFunctionTemplate(FunctionCallTemplate): - def __init__(self): - super().__init__(BatchNormalizationQuantizedTanh, include_header=bn_include_list) - self.template = batchnorm_quantized_tanh_function_template - - def format(self, node): - params = self._default_function_params(node) - if node.get_attr('quantize') == 2: - params['quantize'] = 'binary' - params['threshold'] = node.get_weights('threshold').name - elif node.get_attr('quantize') == 3: - params['quantize'] = 'ternary' - params['threshold'] = node.get_weights('threshold_hi').name + ', ' + node.get_weights('threshold_lo').name - - return self.template.format(**params) - - -def register_bn_quant(backend): - # Register the layer types to the layer map - register_layer('BatchNormalizationQuantizedTanh', BatchNormalizationQuantizedTanh) - - # Register the optimization passes - backend.register_pass('merge_batch_norm_quantized_tanh', MergeBatchNormAndQuantizedTanh) - backend.register_pass('quantize_dense_output', QuantizeDenseOutput) - - # Register template passes - backend.register_template(BatchNormalizationQuantizedTanhConfigTemplate) - backend.register_template(BatchNormalizationQuantizedTanhFunctionTemplate) - - -class MergeBatchNormAndQuantizedTanh(OptimizerPass): - def match(self, node): - is_match = ( - node.class_name == 'Activation' - and node.get_attr('activation') in ['binary', 'binary_tanh', 'ternary', 'ternary_tanh'] - or node.class_name == 'TernaryTanh' - ) - is_match = is_match and isinstance(node.get_input_node(), BatchNormalization) - return is_match - - def transform(self, model, node): - bn_layer = node.get_input_node() - # Make a new layer with the new attributes - quantize = 0 - if 'binary' in node.get_attr('activation'): - quantize = 2 - if 'ternary' in node.get_attr('activation'): - quantize = 3 - attrs = { - 'name': bn_layer.get_attr('name'), - 'original_name': bn_layer.get_attr('name'), - 'class_name': 'BatchNormalizationQuantizedTanh', - 'n_in': bn_layer.get_attr('n_in'), - 'n_out': bn_layer.get_attr('n_in'), - 'n_filt': bn_layer.get_attr('n_filt'), - 'quantize': quantize, - 'trace': bn_layer.get_attr('trace'), - } - bnbt_layer = model.make_node(BatchNormalizationQuantizedTanh, 'bnbt_' + bn_layer.name, attrs, bn_layer.inputs) - bnbt_layer.set_thresholds( - bn_layer.get_weights('scale').data, bn_layer.get_weights('bias').data, node.get_attr('threshold', 0.5) - ) - # Remove the BatchNormalization layer - model.remove_node(bn_layer) - # Replace the old Activation layer with this one - model.replace_node(node, bnbt_layer) - - return True - - -class QuantizeDenseOutput(OptimizerPass): - def match(self, node): - is_dense = node.class_name == 'Dense' - input_node = node.get_input_node() - is_input_bnqt = input_node is not None and input_node.class_name == 'BatchNormalizationQuantizedTanh' - quantizer = node.get_attr('weight_quantizer') - is_binary_ternary = quantizer is not None and ( - quantizer.__class__.__name__ == 'BinaryQuantizer' or quantizer.__class__.__name__ == 'TernaryQuantizer' - ) - return is_dense and is_input_bnqt and is_binary_ternary - - def transform(self, model, node): - # Compute the required precision and update the variables - # Number of bits for output is log2 of number of input nodes - # Since this is the number of uint<1>'s which are summed - nbits = int(np.ceil(np.log2(node.attributes['n_in'])) + 2) - out_type = IntegerPrecisionType(width=nbits) - accum_t = NamedType(f'layer{node.index}_accum_t', out_type) - node.set_attr('accum_t', accum_t) - out_var = node.get_output_variable() - out_var.type.precision = out_type - - quantized_data = None - quantized_precision = None - quantizer = node.get_attr('weight_quantizer') - if quantizer.__class__.__name__ == 'BinaryQuantizer': - quantized_precision = XnorPrecisionType() - elif quantizer.__class__.__name__ == 'TernaryQuantizer': - quantized_precision = IntegerPrecisionType(width=2) - else: - print(f'WARNING: Unknown quantizer - {quantizer.__class__.__name__}. Bailing out') - return False - quantizer.bits = quantized_precision.width - quantizer.hls_type = quantized_precision - quantized_data = quantizer(node.weights['weight'].data) - - weights = node.weights['weight'] - weights.data = quantized_data - weights.type.name = f'weight{node.index}_t' - weights.update_precision(quantized_precision) - - bias = node.weights['bias'] - bias.data = np.zeros(shape=(node.get_attr('n_out'))) - bias.type.name = f'bias{node.index}_t' - bias.nzeros = 0 - bias.update_precision(quantized_precision) - - # If followed by the BatchNormalizationBinaryTanh, update its input - # Also requantise the weights - bd_out_nodes = node.get_output_nodes() - for out_node in bd_out_nodes: - if isinstance(out_node, BatchNormalizationQuantizedTanh): - var_names = [] - if quantizer.__class__.__name__ == 'BinaryQuantizer': - var_names.append('threshold') - elif quantizer.__class__.__name__ == 'TernaryQuantizer': - var_names.append('threshold_hi') - var_names.append('threshold_lo') - for var_name in var_names: - threshold_var = out_node.weights[var_name] - threshold_var.update_precision(out_type) - threshold_var.data = np.floor(threshold_var.data) - - return False diff --git a/hls4ml/backends/quartus/passes/convolution_templates.py b/hls4ml/backends/quartus/passes/convolution_templates.py deleted file mode 100644 index acff8d7043..0000000000 --- a/hls4ml/backends/quartus/passes/convolution_templates.py +++ /dev/null @@ -1,183 +0,0 @@ -from hls4ml.backends.backend import get_backend -from hls4ml.backends.template import FunctionCallTemplate, LayerConfigTemplate -from hls4ml.model.layers import Conv1D, Conv2D, Conv2DBatchnorm - -# TODO - Dilation rate ? - -""" Shared multiplication config """ -conv_mult_config_template = """struct config{index}_mult : nnet::dense_config {{ - static const unsigned n_in = {n_in}; - static const unsigned n_out = {n_out}; - - static const unsigned rf_pad = {rfpad}; - static const unsigned bf_pad = {bfpad}; - - static const unsigned reuse_factor = {reuse}; - static const unsigned reuse_factor_rounded = reuse_factor + rf_pad; - static const unsigned block_factor = DIV_ROUNDUP(n_in*n_out, reuse_factor); - static const unsigned block_factor_rounded = block_factor + bf_pad; - static const unsigned multiplier_factor = MIN(n_in, reuse_factor); - static const unsigned multiplier_limit = DIV_ROUNDUP(n_in*n_out, multiplier_factor); - static const unsigned multiplier_scale = multiplier_limit/n_out; - - typedef {accum_t.name} accum_t; - typedef {bias_t.name} bias_t; - typedef {weight_t.name} weight_t; - - template - using product = nnet::product::{product_type}; -}};\n""" - -""" 1D Conv """ -conv1d_config_template = """struct config{index} : nnet::conv1d_config {{ - static const unsigned in_width = {in_width}; - static const unsigned n_chan = {n_chan}; - - static const unsigned filt_width = {filt_width}; - static const unsigned impl_filt_width = {impl_filt_width}; - static const unsigned kernel_size = filt_width; - - static const unsigned n_filt = {n_filt}; - static const unsigned out_width = {out_width}; - - static const unsigned pad_left = {pad_left}; - static const unsigned pad_right = {pad_right}; - static const unsigned stride_width = {stride_width}; - static const unsigned dilation = {dilation}; - - static const unsigned reuse_factor = {reuse}; - static const unsigned parallelization_factor = {parallelization}; - static const bool store_weights_in_bram = false; - - static const nnet::conv1d_implementation implementation = nnet::conv1d_implementation::{implementation}; - - typedef {accum_t.name} accum_t; - typedef {bias_t.name} bias_t; - typedef {weight_t.name} weight_t; - typedef {config_t} mult_config; -}}; -""" - -conv1d_function_template = 'nnet::conv_1d_{data_format}<{input_t}, {output_t}, {config}>({input}, {output}, {w}, {b});' -conv1d_include_list = ['nnet_utils/nnet_conv1d.h', 'nnet_utils/nnet_conv1d_stream.h'] - - -class Conv1DConfigTemplate(LayerConfigTemplate): - def __init__(self): - super().__init__(Conv1D) - self.template = conv1d_config_template - self.mult_template = conv_mult_config_template - - def format(self, node): - conv_params = self._default_config_params(node) - conv_params['dilation'] = node.get_attr('dilation', 1) - if conv_params['dilation'] != 1: - raise Exception('dilation != 1 not supported yet') - conv_params['config_t'] = f'config{node.index}_mult' - conv_config = self.template.format(**conv_params) - - mult_params = self._default_config_params(node) - mult_params['n_in'] = node.get_attr('n_chan') * node.get_attr('filt_width') - mult_params['n_out'] = node.get_attr('n_filt') - mult_params['product_type'] = get_backend('quartus').product_type( - node.get_input_variable().type.precision, node.get_weights('weight').type.precision - ) - mult_config = self.mult_template.format(**mult_params) - - return mult_config + '\n' + conv_config - - -class Conv1DFunctionTemplate(FunctionCallTemplate): - def __init__(self): - super().__init__(Conv1D, include_header=conv1d_include_list) - self.template = conv1d_function_template - - def format(self, node): - params = self._default_function_params(node) - if node.get_attr('data_format') == 'channels_first': - raise Exception('channels_first not supported on Quartus') - params['data_format'] = 'cl' - params['w'] = node.get_weights('weight').name - params['b'] = node.get_weights('bias').name - - return self.template.format(**params) - - -""" 2D Conv """ -conv2d_config_template = """struct config{index} : nnet::conv2d_config {{ - static const unsigned in_height = {in_height}; - static const unsigned in_width = {in_width}; - static const unsigned n_chan = {n_chan}; - - static const unsigned out_height = {out_height}; - static const unsigned out_width = {out_width}; - - static const unsigned n_filt = {n_filt}; - static const unsigned filt_height = {filt_height}; - static const unsigned filt_width = {filt_width}; - static const unsigned impl_filt_height = {impl_filt_height}; - static const unsigned impl_filt_width = {impl_filt_width}; - static const unsigned kernel_size = filt_height * filt_width; - - static const unsigned pad_top = {pad_top}; - static const unsigned pad_bottom = {pad_bottom}; - static const unsigned pad_left = {pad_left}; - static const unsigned pad_right = {pad_right}; - static const unsigned stride_height = {stride_height}; - static const unsigned stride_width = {stride_width}; - - static const unsigned reuse_factor = {reuse}; - static const unsigned parallelization_factor = {parallelization}; - static const bool store_weights_in_bram = false; - - static const nnet::conv2d_implementation implementation = nnet::conv2d_implementation::{implementation}; - - typedef {accum_t.name} accum_t; - typedef {bias_t.name} bias_t; - typedef {weight_t.name} weight_t; - typedef {config_t} mult_config; -}};\n""" - -conv2d_function_template = 'nnet::conv_2d_{data_format}<{input_t}, {output_t}, {config}>({input}, {output}, {w}, {b});' -conv2d_include_list = ['nnet_utils/nnet_conv2d.h', 'nnet_utils/nnet_conv2d_stream.h'] - - -class Conv2DConfigTemplate(LayerConfigTemplate): - def __init__(self): - super().__init__((Conv2D, Conv2DBatchnorm)) - self.template = conv2d_config_template - self.mult_template = conv_mult_config_template - - def format(self, node): - conv_params = self._default_config_params(node) - conv_params['dilation'] = node.get_attr('dilation', 1) - if conv_params['dilation'] != 1: - raise Exception('dilation != 1 not supported yet') - conv_params['config_t'] = f'config{node.index}_mult' - conv_config = self.template.format(**conv_params) - - mult_params = self._default_config_params(node) - mult_params['n_in'] = node.get_attr('n_chan') * node.get_attr('filt_height') * node.get_attr('filt_width') - mult_params['n_out'] = node.get_attr('n_filt') - mult_params['product_type'] = get_backend('quartus').product_type( - node.get_input_variable().type.precision, node.get_weights('weight').type.precision - ) - mult_config = self.mult_template.format(**mult_params) - - return mult_config + '\n' + conv_config - - -class Conv2DFunctionTemplate(FunctionCallTemplate): - def __init__(self): - super().__init__((Conv2D, Conv2DBatchnorm), include_header=conv2d_include_list) - self.template = conv2d_function_template - - def format(self, node): - params = self._default_function_params(node) - if node.get_attr('data_format') == 'channels_first': - raise Exception('channels_first not supported for Quartus') - params['data_format'] = 'cl' - params['w'] = node.get_weights('weight').name - params['b'] = node.get_weights('bias').name - - return self.template.format(**params) diff --git a/hls4ml/backends/quartus/passes/convolution_winograd.py b/hls4ml/backends/quartus/passes/convolution_winograd.py deleted file mode 100644 index 52a5e1af86..0000000000 --- a/hls4ml/backends/quartus/passes/convolution_winograd.py +++ /dev/null @@ -1,175 +0,0 @@ -import math - -import numpy as np - -from hls4ml.model.layers import Conv1D, Conv2D -from hls4ml.model.optimizer import OptimizerPass - - -class ApplyWinogradKernelTransformation(OptimizerPass): - """ - Transforms the weights of a Conv2D kernel to a format suitable for Wingorad convolution - For further information, refer to Lavin & Gray, 2015 - Fast Algorithms for Convolutional Neural Networks - """ - - def match(self, node): - node_matches = isinstance(node, (Conv1D, Conv2D)) - - # This optimizer works only after the Resource Strategy Optimizer, since order of transposition matters - weights_transformed = node.get_attr('_weights_transposed', False) is True - - # User opted for Winograd - implementation_is_winograd = ( - node.get_attr('implementation', 'combination') == 'combination' - or node.get_attr('implementation', 'combination') == 'winograd' - ) - - parallel_io_type = node.model.config.get_config_value('IOType') == 'io_parallel' - - # Winograd algorithm-specific conditions - if isinstance(node, Conv1D): - # Winograd only applies to specific kernel sizes - # Current implementation only supports fs = 3; easily extendable to other filter sizes - filter_size_matches = node.get_attr('filt_width', 3) == 3 - - # Winograd's minimal filtering algorithm doesn't work with stride != 1 - stride_is_one = node.get_attr('stride_width', 1) == 1 - - # HLS Compiler fails to pipeline the entire component if Winograd loop only executes once - loop_itr_gt_one = node.get_attr('out_width') > 2 - - winograd_conditions = filter_size_matches and stride_is_one and loop_itr_gt_one and parallel_io_type - - elif isinstance(node, (Conv2D)): - # Winograd only applies to specific kernel sizes - # Current implementation only supports fs = 3; easily extendable to other filter sizes - filter_size_matches = node.get_attr('filt_height', 3) == 3 and node.get_attr('filt_width', 3) == 3 - - # Winograd's minimal filtering algorithm doesn't work with striede != 1 - stride_is_one = node.get_attr('stride_height', 1) == 1 and node.get_attr('stride_width', 1) == 1 - - # HLS Compiler fails to pipeline the entire component if Winograd loop only executes once - loop_itr_gt_one = node.get_attr('out_height') > 2 and node.get_attr('out_width') > 2 - - padding_is_equal = node.get_attr('pad_top', 0) == node.get_attr('pad_bottom', 0) and node.get_attr( - 'pad_left', 0 - ) == node.get_attr('pad_right', 0) - - winograd_conditions = ( - filter_size_matches and stride_is_one and padding_is_equal and loop_itr_gt_one and parallel_io_type - ) - - else: - winograd_conditions = False - - # Check any previous transformations - already_transformed = node.get_attr('_winograd_transformation_applied', False) is True - - if not winograd_conditions and node.get_attr('implementation', 'combination') == 'winograd': - raise RuntimeError( - 'Not possible to use Winograd algorithm with current architecture. ' - 'Please set implementation to im2col or combination' - ) - - return ( - node_matches - and weights_transformed - and winograd_conditions - and not already_transformed - and implementation_is_winograd - ) - - def transform(self, model, node): - if isinstance(node, Conv1D): - if node.get_attr('filt_width', 3) == 3: - # First, transpose to a format suitable for the Winograd algorithm (F, C, W) - # Note, this assumes a format post-resource strategy optimizer, that is (F, W, C) - # Therefore, (F, W, C) => (F, C, W) - node.weights['weight'].data = np.transpose(node.weights['weight'].data, axes=[0, 2, 1]) - - # Temporary copy of data - weights = node.weights['weight'].data - - # Expand weight dimensionality (3) => (4) - node.weights['weight'].data = np.zeros((weights.shape[0], weights.shape[1], 4)) - - # Transformation matrices for 3x1 kernels - G = np.array([[1, 0, 0], [0.5, 0.5, 0.5], [0.5, -0.5, 0.5], [0, 0, 1]]) - - # Transformation GfG' - for filter in range(0, weights.data.shape[0]): - for channel in range(0, weights.data.shape[1]): - node.weights['weight'].data[filter][channel] = np.matmul(G, weights[filter][channel]) - node.weights['weight'].data_length = node.weights['weight'].data.size - - # Winograd's minimal filtering algorithm transforms the weight matrix - # This transformation consists of addition and division (by 2&4) of the weight matrix - # Therefore, increase precision (if needed), to accommodate for new weights - # This error is only noticeable for low precisions, such as those used with QKeras - - # Integer precision is only updated if it exceeds the one defined in hls4ml config - maximum_value_rounded = int(math.ceil(np.abs(node.weights['weight'].data).max())) - if maximum_value_rounded.bit_length() + 1 > node.weights['weight'].type.precision.integer: - node.weights['weight'].type.precision.integer = maximum_value_rounded.bit_length() + 1 - node.weights['weight'].type.precision.width += ( - maximum_value_rounded.bit_length() + 1 - node.weights['weight'].type.precision.integer - ) - - # Fractional precision is increased by 2 bits (division by 4), - # for low-precision (less than 8) fractional weights - if node.weights['weight'].type.precision.fractional < 8: - node.weights['weight'].type.precision.width += 2 - - # Modified kernel size - node.set_attr('impl_filt_width', 4) - - elif isinstance(node, Conv2D): - if node.get_attr('filt_height', 3) == 3 and node.get_attr('filt_width', 3) == 3: - # First, transpose to a format suitable for the Winograd algorithm (F, C, H, W) - # Note, this assumes a format post-resource strategy optimizer, that is (F, H, W, C) - # Therefore, (F, H, W, C) => (F, C, H, W) - node.weights['weight'].data = np.transpose(node.weights['weight'].data, axes=[0, 3, 1, 2]) - - # Temporary copy of data - weights = node.weights['weight'].data - - # Expand weight dimensionality (3x3) => (4x4) - node.weights['weight'].data = np.zeros((weights.shape[0], weights.shape[1], 4, 4)) - - # Transformation matrices for 3x3 kernels - G = np.array([[1, 0, 0], [0.5, 0.5, 0.5], [0.5, -0.5, 0.5], [0, 0, 1]]) - GT = np.array([[1, 0.5, 0.5, 0], [0, 0.5, -0.5, 0], [0, 0.5, 0.5, 1]]) - - # Transformation GfG' - for filter in range(0, weights.data.shape[0]): - for channel in range(0, weights.data.shape[1]): - node.weights['weight'].data[filter][channel] = np.matmul(np.matmul(G, weights[filter][channel]), GT) - node.weights['weight'].data_length = node.weights['weight'].data.size - - # Winograd's minimal filtering algorithm transforms the weight matrix - # This transformation consists of addition and division (by 2&4) of the weight matrix - # Therefore, increase precision (if needed), to accommodate for new weights - # This error is only noticeable for low precisions, such as those used with QKeras - - # Integer precision is only updated if it exceeds the one defined in hls4ml config - maximum_value_rounded = int(math.ceil(np.abs(node.weights['weight'].data).max())) - if maximum_value_rounded.bit_length() + 1 > node.weights['weight'].type.precision.integer: - node.weights['weight'].type.precision.integer = maximum_value_rounded.bit_length() + 1 - node.weights['weight'].type.precision.width += ( - maximum_value_rounded.bit_length() + 1 - node.weights['weight'].type.precision.integer - ) - - # Fractional precision is increased by 2 bits (division by 4), - # for low-precision (less than 8) fractional weights - if node.weights['weight'].type.precision.fractional < 8: - node.weights['weight'].type.precision.width += 2 - - # Modified kernel size - node.set_attr('impl_filt_height', 4) - node.set_attr('impl_filt_width', 4) - else: - raise Exception(f'Unexpected layer {node.class_name} with Winograd kernel optimizer') - - node.set_attr('_winograd_transformation_applied', True) - - return False diff --git a/hls4ml/backends/quartus/passes/core_templates.py b/hls4ml/backends/quartus/passes/core_templates.py deleted file mode 100644 index b474e14df5..0000000000 --- a/hls4ml/backends/quartus/passes/core_templates.py +++ /dev/null @@ -1,245 +0,0 @@ -from hls4ml.backends.backend import get_backend -from hls4ml.backends.template import FunctionCallTemplate, LayerConfigTemplate -from hls4ml.model.layers import Activation, BatchNormalization, Dense, HardActivation, ParametrizedActivation, PReLU, Softmax -from hls4ml.model.optimizer.passes.hgq_proxy_model import UnaryLUT - -# Dense templates - -dense_config_template = """struct config{index} : nnet::dense_config {{ - static const unsigned n_in = {n_in}; - static const unsigned n_out = {n_out}; - static const unsigned io_type = nnet::{iotype}; - static const unsigned n_zeros = {nzeros}; - static const unsigned n_nonzeros = {nonzeros}; - static const bool store_weights_in_bram = false; - - static const unsigned rf_pad = {rfpad}; - static const unsigned bf_pad = {bfpad}; - - static const unsigned reuse_factor = {reuse}; - static const unsigned compressed_block_factor = DIV_ROUNDUP(n_nonzeros, reuse_factor); - static const unsigned reuse_factor_rounded = reuse_factor + rf_pad; - static const unsigned block_factor = DIV_ROUNDUP(n_in*n_out, reuse_factor); - static const unsigned block_factor_rounded = block_factor + bf_pad; - static const unsigned multiplier_factor = MIN(n_in, reuse_factor); - static const unsigned multiplier_limit = DIV_ROUNDUP(n_in*n_out, multiplier_factor); - static const unsigned multiplier_scale = multiplier_limit/n_out; - - typedef {accum_t.name} accum_t; - typedef {bias_t.name} bias_t; - typedef {weight_t.name} weight_t; - typedef {index_t.name} index_t; - - template - using product = nnet::product::{product_type}; -}};\n""" - -dense_function_template = 'nnet::dense_{strategy}<{input_t}, {output_t}, {config}>({input}, {output}, {w}, {b});' - -dense_include_list = ['nnet_utils/nnet_dense.h', 'nnet_utils/nnet_dense_compressed.h', 'nnet_utils/nnet_dense_stream.h'] - - -class DenseConfigTemplate(LayerConfigTemplate): - def __init__(self): - super().__init__(Dense) - self.template = dense_config_template - - def format(self, node): - params = self._default_config_params(node) - params['nzeros'] = node.get_weights('weight').nzeros - params['nonzeros'] = node.get_weights('weight').nonzeros - params['product_type'] = get_backend('quartus').product_type( - node.get_input_variable().type.precision, node.get_weights('weight').type.precision - ) - - return self.template.format(**params) - - -class DenseFunctionTemplate(FunctionCallTemplate): - def __init__(self): - super().__init__(Dense, include_header=dense_include_list) - self.template = dense_function_template - - def format(self, node): - params = self._default_function_params(node) - params['w'] = node.get_weights('weight').name - params['b'] = node.get_weights('bias').name - - return self.template.format(**params) - - -# BatchNormalization templates - -batchnorm_config_template = """struct config{index} : nnet::batchnorm_config {{ - static const unsigned n_in = {n_in}; - static const unsigned n_filt = {n_filt}; - static const unsigned io_type = nnet::{iotype}; - static const unsigned reuse_factor = {reuse}; - static const bool store_weights_in_bram = false; - typedef {bias_t.name} bias_t; - typedef {scale_t.name} scale_t; - template - using product = nnet::product::{product_type}; -}};\n""" - -batchnorm_function_template = 'nnet::normalize<{input_t}, {output_t}, {config}>({input}, {output}, {scale}, {bias});' - -batchnorm_include_list = ['nnet_utils/nnet_batchnorm.h', 'nnet_utils/nnet_batchnorm_stream.h'] - - -class BatchNormalizationConfigTemplate(LayerConfigTemplate): - def __init__(self): - super().__init__(BatchNormalization) - self.template = batchnorm_config_template - - def format(self, node): - params = self._default_config_params(node) - params['n_in'] = node.get_input_variable().size_cpp() - params['product_type'] = get_backend('quartus').product_type( - node.get_input_variable().type.precision, node.get_weights('scale').type.precision - ) - - return self.template.format(**params) - - -class BatchNormalizationFunctionTemplate(FunctionCallTemplate): - def __init__(self): - super().__init__(BatchNormalization, include_header=batchnorm_include_list) - self.template = batchnorm_function_template - - def format(self, node): - params = self._default_function_params(node) - params['scale'] = node.get_weights('scale').name - params['bias'] = node.get_weights('bias').name - - return self.template.format(**params) - - -# Activation templates - -activ_config_template = """struct {type}_config{index} : nnet::activ_config {{ - static const unsigned n_in = {n_in}; - static const unsigned table_size = {table_size}; - static const unsigned io_type = nnet::{iotype}; - static const unsigned reuse_factor = {reuse}; - typedef {table_t.name} table_t; -}};\n""" - -param_activ_config_template = """struct {type}_config{index} : nnet::activ_config {{ - static const unsigned n_in = {n_in}; - static const unsigned table_size = {table_size}; - static const unsigned io_type = nnet::{iotype}; - static const unsigned reuse_factor = {reuse}; - typedef {table_t.name} table_t; - typedef {param_t.name} param_t; -}};\n""" - -hard_activ_config_template = """struct {type}_config{index} {{ - static const unsigned n_in = {n_in}; - static const {slope_t.name} slope; - static const {shift_t.name} shift; - static const unsigned io_type = nnet::{iotype}; - static const unsigned reuse_factor = {reuse}; -}}; -const {slope_t.name} {type}_config{index}::slope = {slope}; -const {shift_t.name} {type}_config{index}::shift = {shift};\n""" - -softmax_config_template = """struct {type}_config{index} : nnet::activ_config {{ - static const unsigned n_in = {n_in}; - static const unsigned table_size = {table_size}; - static const unsigned io_type = nnet::{iotype}; - static const unsigned reuse_factor = {reuse}; - static const nnet::softmax_implementation implementation = nnet::softmax_implementation::{implementation}; - typedef {exp_table_t.name} exp_table_t; - typedef {inv_table_t.name} inv_table_t; -}};\n""" - -activ_function_template = 'nnet::{activation}<{input_t}, {output_t}, {config}>({input}, {output});' -param_activ_function_template = ( - 'nnet::{activation}<{input_t}, {param_t.name}, {output_t}, {config}>({input}, {param}, {output});' -) - -activ_include_list = ['nnet_utils/nnet_activation.h', 'nnet_utils/nnet_activation_stream.h'] - - -class ActivationConfigTemplate(LayerConfigTemplate): - def __init__(self): - super().__init__((Activation, UnaryLUT)) - self.template = activ_config_template - - def format(self, node): - params = self._default_config_params(node) - params['type'] = node.get_attr('activation') - - return self.template.format(**params) - - -class ParamActivationConfigTemplate(LayerConfigTemplate): - def __init__(self): - super().__init__((ParametrizedActivation, PReLU)) - self.template = param_activ_config_template - - def format(self, node): - params = self._default_config_params(node) - params['type'] = node.get_attr('activation') - - return self.template.format(**params) - - -class HardActivationConfigTemplate(LayerConfigTemplate): - def __init__(self): - super().__init__(HardActivation) - self.template = hard_activ_config_template - - def format(self, node): - params = self._default_config_params(node) - params['type'] = node.get_attr('activation') - - return self.template.format(**params) - - -class SoftmaxConfigTemplate(ActivationConfigTemplate): - def __init__(self): - super(ActivationConfigTemplate, self).__init__(Softmax) # Skip ActivationConfigTemplate's __init__ - self.template = softmax_config_template - - -class ActivationFunctionTemplate(FunctionCallTemplate): - def __init__(self): - super().__init__((Activation, HardActivation, Softmax), include_header=activ_include_list) - self.template = activ_function_template - - def format(self, node): - params = self._default_function_params(node) - params['activation'] = node.get_attr('activation').lower() - params['config'] = '{}_config{}'.format(node.get_attr('activation'), node.index) - - return self.template.format(**params) - - -class ParametrizedActivationFunctionTemplate(FunctionCallTemplate): - def __init__(self): - super().__init__(ParametrizedActivation, include_header=activ_include_list) - self.template = param_activ_function_template - - def format(self, node): - params = self._default_function_params(node) - params['activation'] = node._get_act_function_name() - params['param'] = node.get_attr('activ_param', 1.0) - params['config'] = '{}_config{}'.format(node.get_attr('activation'), node.index) - - return self.template.format(**params) - - -class PReLUFunctionTemplate(FunctionCallTemplate): - def __init__(self): - super().__init__(PReLU, include_header=activ_include_list) - self.template = param_activ_function_template - - def format(self, node): - params = self._default_function_params(node) - params['activation'] = node.get_attr('activation').lower() - params['param'] = node.get_weights('param').name - params['config'] = '{}_config{}'.format(node.get_attr('activation'), node.index) - - return self.template.format(**params) diff --git a/hls4ml/backends/quartus/passes/merge_templates.py b/hls4ml/backends/quartus/passes/merge_templates.py deleted file mode 100644 index 6eab51bda7..0000000000 --- a/hls4ml/backends/quartus/passes/merge_templates.py +++ /dev/null @@ -1,109 +0,0 @@ -from hls4ml.backends.backend import get_backend -from hls4ml.backends.template import FunctionCallTemplate, LayerConfigTemplate -from hls4ml.model.layers import Concatenate, Dot, Merge - -# TODO - Very similar to vivado/merge_templates.py - only difference is on line 67: -# TODO - get_backend('vivado').product_type(inp1.type.precision, inp2.type.precision) -# TODO - Look into ways of having passes similar across many backends in a shared folder through inheritance and overriding. - -# Merge templates -merge_config_template = """struct config{index} : nnet::merge_config {{ - static const unsigned n_elem = {n_elem}; - static const unsigned reuse_factor = {reuse}; -}};\n""" - -merge_function_template = 'nnet::{merge}<{input1_t}, {input2_t}, {output_t}, {config}>({input1}, {input2}, {output});' -merge_include_list = ['nnet_utils/nnet_merge.h', 'nnet_utils/nnet_merge_stream.h'] - - -class MergeConfigTemplate(LayerConfigTemplate): - def __init__(self): - super().__init__(Merge) - self.template = merge_config_template - - def format(self, node): - params = self._default_config_params(node) - params['n_elem'] = node.get_input_variable(node.inputs[0]).size_cpp() - - return self.template.format(**params) - - -class MergeFunctionTemplate(FunctionCallTemplate): - def __init__(self): - super().__init__((Merge, Concatenate, Dot), include_header=merge_include_list) - self.template = merge_function_template - - def format(self, node): - params = {} - params['merge'] = node.get_attr('op').lower() - params['config'] = f'config{node.index}' - params['input1_t'] = node.get_input_variable(node.inputs[0]).type.name - params['input2_t'] = node.get_input_variable(node.inputs[1]).type.name - params['output_t'] = node.get_output_variable().type.name - params['input1'] = node.get_input_variable(node.inputs[0]).name - params['input2'] = node.get_input_variable(node.inputs[1]).name - params['output'] = node.get_output_variable().name - - return self.template.format(**params) - - -# Dot templates -dot_config_template = """struct config{index} : nnet::dot_config {{ - static const unsigned n_in = {n_in}; - static const unsigned n_out = {n_out}; - - static const unsigned reuse_factor = {reuse}; - - typedef {accum_t.name} accum_t; - - template - using product = nnet::product::{product_type}; -}};\n""" - - -class DotConfigTemplate(LayerConfigTemplate): - def __init__(self): - super().__init__(Dot) - self.template = dot_config_template - - def format(self, node): - inp1 = node.get_input_variable(node.inputs[0]) - inp2 = node.get_input_variable(node.inputs[1]) - params = self._default_config_params(node) - params['n_out'] = 1 - params['n_in'] = inp1.shape[0] - params['product_type'] = get_backend('quartus').product_type(inp1.type.precision, inp2.type.precision) - - return self.template.format(**params) - - -# Concatenate templates -concat_config_template = """struct config{index} : nnet::concat_config {{ - static const unsigned n_elem1_0 = {n_elem1_0}; - static const unsigned n_elem1_1 = {n_elem1_1}; - static const unsigned n_elem1_2 = {n_elem1_2}; - static const unsigned n_elem2_0 = {n_elem2_0}; - static const unsigned n_elem2_1 = {n_elem2_1}; - static const unsigned n_elem2_2 = {n_elem2_2}; - - static const int axis = {axis}; -}};\n""" - - -class ConcatenateConfigTemplate(LayerConfigTemplate): - def __init__(self): - super().__init__(Concatenate) - self.template = concat_config_template - - def format(self, node): - params = self._default_config_params(node) - for i in range(3): - params.setdefault(f'n_elem1_{i}', 0) - params.setdefault(f'n_elem2_{i}', 0) - inp1 = node.get_input_variable(node.inputs[0]) - inp2 = node.get_input_variable(node.inputs[1]) - for i, (s1, s2) in enumerate(zip(inp1.shape, inp2.shape)): - params[f'n_elem1_{i}'] = s1 - params[f'n_elem2_{i}'] = s2 - - return self.template.format(**params) diff --git a/hls4ml/backends/quartus/passes/pointwise.py b/hls4ml/backends/quartus/passes/pointwise.py deleted file mode 100644 index 0a5ecf380a..0000000000 --- a/hls4ml/backends/quartus/passes/pointwise.py +++ /dev/null @@ -1,88 +0,0 @@ -from hls4ml.backends.fpga.fpga_layers import PointwiseConv1D, PointwiseConv2D -from hls4ml.backends.quartus.passes.convolution_templates import ( - Conv1DConfigTemplate, - Conv1DFunctionTemplate, - Conv2DConfigTemplate, - Conv2DFunctionTemplate, - conv1d_config_template, - conv2d_config_template, - conv_mult_config_template, -) -from hls4ml.model.layers import register_layer -from hls4ml.model.optimizer import OptimizerPass - -""" -Custom hls4ml layer implementation for 1x1 Conv filters using im2col -Allows lower latency andresource usage, due to less loop invocations -""" - -pointwise_conv1d_function_template = ( - 'nnet::pointwise_conv_1d_{data_format}<{input_t}, {output_t}, {config}>({input}, {output}, {w}, {b});' -) -pointwise_conv2d_function_template = ( - 'nnet::pointwise_conv_2d_{data_format}<{input_t}, {output_t}, {config}>({input}, {output}, {w}, {b});' -) - -sepconv1d_include_list = ['nnet_utils/nnet_conv1d.h'] -sepconv2d_include_list = ['nnet_utils/nnet_conv2d.h'] - - -class PointwiseConv1DConfigTemplate(Conv1DConfigTemplate): - def __init__(self): - super(Conv1DConfigTemplate, self).__init__(PointwiseConv1D) - self.template = conv1d_config_template - self.mult_template = conv_mult_config_template - - -class PointwiseConv1DFunctionTemplate(Conv1DFunctionTemplate): - def __init__(self): - super(Conv1DFunctionTemplate, self).__init__(PointwiseConv1D, include_header=sepconv1d_include_list) - self.template = pointwise_conv1d_function_template - - -class PointwiseConv2DConfigTemplate(Conv2DConfigTemplate): - def __init__(self): - super(Conv2DConfigTemplate, self).__init__(PointwiseConv2D) - self.template = conv2d_config_template - self.mult_template = conv_mult_config_template - - -class PointwiseConv2DFunctionTemplate(Conv2DFunctionTemplate): - def __init__(self): - super(Conv2DFunctionTemplate, self).__init__(PointwiseConv2D, include_header=sepconv2d_include_list) - self.template = pointwise_conv2d_function_template - - -def register_pointwise(backend): - # Register the layer types to the layer map - register_layer('PointwiseConv1D', PointwiseConv1D) - register_layer('PointwiseConv2D', PointwiseConv2D) - - # Register the optimization passes - backend.register_pass('optimize_pointwise_conv', OptimizePointwiseConv) - - # Register template passes - backend.register_template(PointwiseConv1DConfigTemplate) - backend.register_template(PointwiseConv1DFunctionTemplate) - backend.register_template(PointwiseConv2DConfigTemplate) - backend.register_template(PointwiseConv2DFunctionTemplate) - - -class OptimizePointwiseConv(OptimizerPass): - def match(self, node): - return ( - node.class_name in ('Conv1D', 'Conv2D') - and node.get_attr('filt_height', 1) == 1 - and node.get_attr('filt_width') == 1 - and node.model.config.get_config_value('IOType') == 'io_parallel' - ) - - def transform(self, model, node): - dim = node.__class__.__name__[-2:] # '1D' or '2D' - new_attrs = node.attributes.attributes.copy() - pw_node = model.make_node( - 'PointwiseConv' + dim, node.name, new_attrs, node.inputs.copy(), outputs=node.outputs.copy() - ) - model.replace_node(node, pw_node) - - return True diff --git a/hls4ml/backends/quartus/passes/pooling_templates.py b/hls4ml/backends/quartus/passes/pooling_templates.py deleted file mode 100644 index 9a3ee41923..0000000000 --- a/hls4ml/backends/quartus/passes/pooling_templates.py +++ /dev/null @@ -1,111 +0,0 @@ -from hls4ml.backends.template import FunctionCallTemplate, LayerConfigTemplate -from hls4ml.model.layers import GlobalPooling1D, GlobalPooling2D, Pooling1D, Pooling2D - -# TODO - Move to ../fpga/passes, once streaming is supported on Quartus (should be identical to Vivado) - -pooling1d_config_template = """struct config{index} : nnet::pooling1d_config {{ - static const unsigned stride_width = {stride_width}; - static const unsigned pool_width = {pool_width}; - - static const unsigned n_in = {n_in}; - static const unsigned n_out = {n_out}; - static const unsigned filt_width = {pool_width}; - - static const unsigned n_filt = {n_filt}; - static const unsigned n_chan = {n_filt}; - - static const unsigned in_width = {n_in}; - - static const unsigned pad_left = {pad_left}; - static const unsigned pad_right = {pad_right}; - static const bool count_pad = {count_pad}; - - static const nnet::Pool_Op pool_op = nnet::{pool_op}; - typedef {accum_t.name} accum_t; -}};\n""" - -pooling2d_config_template = """struct config{index} : nnet::pooling2d_config {{ - static const unsigned stride_height = {stride_height}; - static const unsigned stride_width = {stride_width}; - - static const unsigned pool_height = {pool_height}; - static const unsigned pool_width = {pool_width}; - static const unsigned filt_height = {pool_height}; - static const unsigned filt_width = {pool_width}; - - static const unsigned in_height = {in_height}; - static const unsigned in_width = {in_width}; - static const unsigned out_height = {out_height}; - static const unsigned out_width = {out_width}; - - static const unsigned n_filt = {n_filt}; - static const unsigned n_chan = {n_filt}; - - static const unsigned pad_top = {pad_top}; - static const unsigned pad_bottom = {pad_bottom}; - static const unsigned pad_left = {pad_left}; - static const unsigned pad_right = {pad_right}; - static const bool count_pad = {count_pad}; - - static const nnet::Pool_Op pool_op = nnet::{pool_op}; - typedef {accum_t.name} accum_t; -}};\n""" - -global_pooling1d_config_template = """struct config{index} : nnet::pooling1d_config {{ - static const unsigned n_in = {n_in}; - static const unsigned n_filt = {n_filt}; - static const nnet::Pool_Op pool_op = nnet::{pool_op}; - typedef {accum_t.name} accum_t; -}};\n""" - -global_pooling2d_config_template = """struct config{index} : nnet::pooling2d_config {{ - static const unsigned in_height = {in_height}; - static const unsigned in_width = {in_width}; - static const unsigned n_filt = {n_filt}; - static const nnet::Pool_Op pool_op = nnet::{pool_op}; - typedef {accum_t.name} accum_t; -}};\n""" - -pooling1d_function_template = 'nnet::pooling1d_{data_format}<{input_t}, {output_t}, {config}>({input}, {output});' -pooling2d_function_template = 'nnet::pooling2d_{data_format}<{input_t}, {output_t}, {config}>({input}, {output});' -global_pooling1d_function_template = ( - 'nnet::global_pooling1d_{data_format}<{input_t}, {output_t}, {config}>({input}, {output});' -) -global_pooling2d_function_template = ( - 'nnet::global_pooling2d_{data_format}<{input_t}, {output_t}, {config}>({input}, {output});' -) - -pooling_include_list = ['nnet_utils/nnet_pooling.h', 'nnet_utils/nnet_pooling_stream.h'] - - -class PoolingConfigTemplate(LayerConfigTemplate): - def __init__(self): - super().__init__((Pooling1D, Pooling2D, GlobalPooling1D, GlobalPooling2D)) - self.templates = { - 'Pooling1D': pooling1d_config_template, - 'Pooling2D': pooling2d_config_template, - 'GlobalPooling1D': global_pooling1d_config_template, - 'GlobalPooling2D': global_pooling2d_config_template, - } - - def format(self, node): - params = self._default_config_params(node) - return self.templates[node.class_name].format(**params) - - -class PoolingFunctionTemplate(FunctionCallTemplate): - def __init__(self): - super().__init__((Pooling1D, Pooling2D, GlobalPooling1D, GlobalPooling2D), include_header=pooling_include_list) - self.templates = { - 'Pooling1D': pooling1d_function_template, - 'Pooling2D': pooling2d_function_template, - 'GlobalPooling1D': global_pooling1d_function_template, - 'GlobalPooling2D': global_pooling2d_function_template, - } - - def format(self, node): - params = self._default_function_params(node) - if node.get_attr('data_format') == 'channels_first': - raise Exception('channels_first not supported for Quartus') - params['data_format'] = 'cl' - return self.templates[node.class_name].format(**params) diff --git a/hls4ml/backends/quartus/passes/quantization_templates.py b/hls4ml/backends/quartus/passes/quantization_templates.py deleted file mode 100644 index d6cf2d2dae..0000000000 --- a/hls4ml/backends/quartus/passes/quantization_templates.py +++ /dev/null @@ -1,36 +0,0 @@ -from hls4ml.backends.backend import get_backend -from hls4ml.backends.quartus.passes.core_templates import ( - batchnorm_config_template, - batchnorm_function_template, - batchnorm_include_list, -) -from hls4ml.backends.template import FunctionCallTemplate, LayerConfigTemplate -from hls4ml.model.optimizer.passes.qkeras import ApplyAlpha - - -class ApplyAlphaConfigTemplate(LayerConfigTemplate): - def __init__(self): - super().__init__(ApplyAlpha) - self.template = batchnorm_config_template - - def format(self, node): - params = self._default_config_params(node) - params['n_in'] = node.get_input_variable().size_cpp() - params['product_type'] = get_backend('quartus').product_type( - node.get_input_variable().type.precision, node.get_weights('scale').type.precision - ) - - return self.template.format(**params) - - -class ApplyAlphaFunctionTemplate(FunctionCallTemplate): - def __init__(self): - super().__init__(ApplyAlpha, include_header=batchnorm_include_list) - self.template = batchnorm_function_template - - def format(self, node): - params = self._default_function_params(node) - params['scale'] = node.get_weights('scale').name - params['bias'] = node.get_weights('bias').name - - return self.template.format(**params) diff --git a/hls4ml/backends/quartus/passes/recurrent_templates.py b/hls4ml/backends/quartus/passes/recurrent_templates.py deleted file mode 100644 index fb2976cb13..0000000000 --- a/hls4ml/backends/quartus/passes/recurrent_templates.py +++ /dev/null @@ -1,348 +0,0 @@ -from hls4ml.backends.backend import get_backend -from hls4ml.backends.template import FunctionCallTemplate, LayerConfigTemplate -from hls4ml.model.layers import GRU, LSTM, SimpleRNN - -recurrent_include_list = ['nnet_utils/nnet_recurrent.h', 'nnet_utils/nnet_recurrent_stream.h'] - -################################################ -# Shared Matrix Multiplication Template (Dense) -################################################ -recr_mult_config_template = """struct config{index}_mult : nnet::dense_config {{ - static const unsigned n_in = {n_in}; - static const unsigned n_out = {n_out}; - - static const unsigned rf_pad = {rfpad}; - static const unsigned bf_pad = {bfpad}; - static const unsigned reuse_factor = {reuse}; - static const unsigned reuse_factor_rounded = reuse_factor + rf_pad; - static const unsigned block_factor = DIV_ROUNDUP(n_in*n_out, reuse_factor); - static const unsigned block_factor_rounded = block_factor + bf_pad; - static const unsigned multiplier_factor = MIN(n_in, reuse_factor); - static const unsigned multiplier_limit = DIV_ROUNDUP(n_in*n_out, multiplier_factor); - static const unsigned multiplier_scale = multiplier_limit/n_out; - typedef {accum_t.name} accum_t; - typedef {bias_t.name} bias_t; - typedef {weight_t.name} weight_t; - - template - using product = nnet::product::{product_type}; -}};\n""" - -################################################ -# Shared Activation Template -################################################ -activ_config_template = """struct {type}_config{index} : nnet::activ_config {{ - static const unsigned n_in = {n_in}; - static const unsigned table_size = {table_size}; - static const unsigned io_type = nnet::{iotype}; - static const unsigned reuse_factor = {reuse}; - typedef {table_t.name} table_t; -}};\n""" - -################################################ -# GRU Template -################################################ -gru_config_template = """struct config{index} : nnet::gru_config {{ - static const unsigned n_in = {n_in}; - static const unsigned n_out = {n_out}; - static const unsigned n_units = {n_units}; - static const unsigned n_timesteps = {n_timesteps}; - static const unsigned n_outputs = {n_outputs}; - static const bool return_sequences = {return_sequences}; - - typedef {accum_t.name} accum_t; - typedef {weight_t.name} weight_t; - typedef {bias_t.name} bias_t; - - typedef {config_mult_x} mult_config_x; - typedef {config_mult_h} mult_config_h; - - typedef {act_t} ACT_CONFIG_T; - template - using activation = nnet::activation::{activation}; - - typedef {act_recurrent_t} ACT_CONFIG_RECURRENT_T; - template - using activation_recr = nnet::activation::{recurrent_activation}; - - static const unsigned reuse_factor = {reuse}; - static const unsigned pytorch_order = {pytorch}; - static const bool store_weights_in_bram = false; -}};\n""" - -gru_function_template = 'nnet::gru<{input_t}, {output_t}, {config}>({input}, {output}, {w}, {wr}, {b}, {br});' -gru_function_initial_state_template = ( - 'nnet::gru<{input_t}, {input2_t}, {output_t}, {config}>({input}, {input2}, {output}, {w}, {wr}, {b}, {br});' -) - - -class GRUConfigTemplate(LayerConfigTemplate): - def __init__(self): - super().__init__(GRU) - self.gru_template = gru_config_template - self.act_template = activ_config_template - self.recr_act_template = activ_config_template - self.mult_x_template = recr_mult_config_template - self.mult_h_template = recr_mult_config_template - - def format(self, node): - # Input has shape (n_timesteps, inp_dimensionality) - # Output / hidden units has shape (1 if !return_sequences else n_timesteps , n_units) - params = self._default_config_params(node) - params['n_units'] = node.get_attr('n_out') - params['n_outputs'] = node.get_attr('n_timesteps') if node.get_attr('return_sequences', False) else '1' - params['return_sequences'] = 'true' if node.get_attr('return_sequences', False) else 'false' - params['config_mult_x'] = f'config{node.index}_x_mult' - params['config_mult_h'] = f'config{node.index}_h_mult' - params['act_t'] = '{}_config{}'.format(node.get_attr('activation'), str(node.index) + '_act') - params['act_recurrent_t'] = '{}_config{}'.format(node.get_attr('recurrent_activation'), str(node.index) + '_rec_act') - params['pytorch'] = 'true' if node.get_attr('pytorch', False) else 'false' - gru_config = self.gru_template.format(**params) - - # Activation is on candidate hidden state, dimensionality (1, n_units) - act_params = self._default_config_params(node) - act_params['type'] = node.get_attr('activation') - act_params['n_in'] = node.get_attr('n_out') - act_params['index'] = str(node.index) + '_act' - act_config = self.act_template.format(**act_params) - - # Recurrent activation is on reset and update gates (therefore x2), dimensionality (1, n_units) - recr_act_params = self._default_config_params(node) - recr_act_params['type'] = node.get_attr('recurrent_activation') - recr_act_params['n_in'] = str(node.get_attr('n_out')) + ' * 2' - recr_act_params['index'] = str(node.index) + '_rec_act' - recr_act_config = self.recr_act_template.format(**recr_act_params) - - # Multiplication config for matrix multiplications of type Wx (reset, update and candidate states) - mult_params_x = self._default_config_params(node) - mult_params_x['n_in'] = node.get_attr('n_in') - mult_params_x['n_out'] = str(node.get_attr('n_out')) + ' * 3' - mult_params_x['product_type'] = get_backend('quartus').product_type( - node.get_input_variable().type.precision, node.get_weights('weight').type.precision - ) - mult_params_x['index'] = str(node.index) + '_x' - mult_config_x = self.mult_x_template.format(**mult_params_x) - - # Multiplication config for matrix multiplications of type Wh (reset, update and candidate states) - mult_params_h = self._default_config_params(node) - mult_params_h['n_in'] = node.get_attr('n_out') - mult_params_h['n_out'] = str(node.get_attr('n_out')) + ' * 3' - mult_params_h['reuse_factor'] = params['recurrent_reuse_factor'] - mult_params_h['product_type'] = get_backend('quartus').product_type( - node.get_input_variable().type.precision, node.get_weights('recurrent_weight').type.precision - ) - mult_params_h['index'] = str(node.index) + '_h' - mult_config_h = self.mult_h_template.format(**mult_params_h) - - return mult_config_x + '\n' + mult_config_h + '\n' + recr_act_config + '\n' + act_config + '\n' + gru_config - - -class GRUFunctionTemplate(FunctionCallTemplate): - def __init__(self): - super().__init__(GRU, include_header=recurrent_include_list) - - def format(self, node): - params = self._default_function_params(node) - if params['pass_initial_states'] == 'true': - params['input2_t'] = node.get_input_variable(node.inputs[1]).type.name - params['input2'] = node.get_input_variable(node.inputs[1]).name - params['w'] = node.get_weights('weight').name - params['b'] = node.get_weights('bias').name - params['wr'] = node.get_weights('recurrent_weight').name - params['br'] = node.get_weights('recurrent_bias').name - - if params['pass_initial_states'] == 'true': - template = gru_function_initial_state_template - else: - template = gru_function_template - - return template.format(**params) - - -################################################ -# LSTM Template -################################################ -lstm_config_template = """struct config{index} : nnet::lstm_config {{ - static const unsigned n_in = {n_in}; - static const unsigned n_out = {n_out}; - static const unsigned n_timesteps = {n_timesteps}; - static const unsigned return_sequences = {return_sequences}; - - typedef {accum_t.name} accum_t; - typedef {weight_t.name} weight_t; - typedef {bias_t.name} bias_t; - - typedef {act_t} ACT_CONFIG_T; - template - using activation = nnet::activation::{activation}; - - typedef {act_recurrent_t} ACT_CONFIG_RECURRENT_T; - template - using activation_recr = nnet::activation::{recurrent_activation}; - - static const unsigned reuse_factor = {reuse}; - static const bool store_weights_in_bram = false; -}};\n""" - -lstm_function_template = 'nnet::lstm<{input_t}, {output_t}, {config}>({input}, {output}, {weights});' -lstm_function_initial_state_template = ( - 'nnet::lstm<{input_t}, {input2_t}, {input3_t}, {output_t}, {config}>({input}, {input2}, {input3}, {output}, {weights});' -) - - -class LSTMConfigTemplate(LayerConfigTemplate): - def __init__(self): - super().__init__(LSTM) - self.template = lstm_config_template - self.act_template = activ_config_template - self.recr_act_template = activ_config_template - - def format(self, node): - lstm_params = self._default_config_params(node) - lstm_params['n_in'] = node.get_attr('n_in') - lstm_params['n_out'] = node.get_attr('n_out') - lstm_params['n_outputs'] = node.get_attr('n_timesteps') if node.get_attr('return_sequences', False) else '1' - - lstm_params['return_sequences'] = str(node.get_attr('return_sequences')).lower() - lstm_params['act_t'] = '{}_config{}'.format(node.get_attr('activation'), str(node.index) + '_act') - lstm_params['act_recurrent_t'] = '{}_config{}'.format( - node.get_attr('recurrent_activation'), str(node.index) + '_rec_act' - ) - lstm_config = self.template.format(**lstm_params) - - act_params = self._default_config_params(node) - act_params['type'] = node.get_attr('activation') - act_params['n_in'] = node.get_attr('n_out') - act_params['index'] = str(node.index) + '_act' - act_config = self.act_template.format(**act_params) - - recr_act_params = self._default_config_params(node) - recr_act_params['type'] = node.get_attr('recurrent_activation') - recr_act_params['n_in'] = node.get_attr('n_out') - recr_act_params['index'] = str(node.index) + '_rec_act' - recr_act_config = self.recr_act_template.format(**recr_act_params) - - return act_config + '\n' + recr_act_config + '\n' + lstm_config - - -class LSTMFunctionTemplate(FunctionCallTemplate): - def __init__(self): - super().__init__(LSTM, include_header=recurrent_include_list) - - def format(self, node): - params = self._default_function_params(node) - - if params['pass_initial_states'] == 'true': - params['input2_t'] = node.get_input_variable(node.inputs[1]).type.name - params['input2'] = node.get_input_variable(node.inputs[1]).name - params['input3'] = node.get_input_variable(node.inputs[2]).name - params['input3_t'] = node.get_input_variable(node.inputs[2]).type.name - - types = ['i', 'f', 'c', 'o'] - params['weights'] = '' - for t in types: - params['weights'] += f'kernel_{t}_{str(node.index)},' - for t in types: - params['weights'] += f'recurrent_kernel_{t}_{str(node.index)},' - for t in types: - params['weights'] += 'bias_{}_{}{}'.format(t, str(node.index), ',' if t != 'o' else '') - - if params['pass_initial_states'] == 'true': - template = lstm_function_initial_state_template - else: - template = lstm_function_template - - return template.format(**params) - - -################################################ -# SimpleRNN Template -################################################ -simple_rnn_config_template = """struct config{index} : nnet::simple_rnn_config {{ - static const unsigned n_in = {n_in}; - static const unsigned n_out = {n_out}; - static const unsigned n_outputs = {n_outputs}; - static const unsigned n_timesteps = {n_timesteps}; - static const unsigned return_sequences = {return_sequences}; - - typedef {accum_t.name} accum_t; - typedef {weight_t.name} weight_t; - typedef {bias_t.name} bias_t; - - typedef {act_t} ACT_CONFIG_T; - template - using activation = nnet::activation::{activation}; - - typedef {act_recurrent_t} ACT_CONFIG_RECURRENT_T; - template - using activation_recr = nnet::activation::{recurrent_activation}; - - static const unsigned reuse_factor = {reuse}; - static const bool store_weights_in_bram = false; -}};\n""" - -simple_rnn_function_template = 'nnet::simple_rnn<{input_t}, {output_t}, {config}>({input}, {output}, {weights});' -simple_rnn_pytorch_function_template = ( - 'nnet::simple_rnn_pytorch<{input_t}, {output_t}, {config}>({input}, {output}, {weights});' -) -simple_rnn_pytorch_function_initial_state_template = ( - 'nnet::simple_rnn_pytorch<{input_t}, {input2_t}, {output_t}, {config}>({input}, {input2}, {output}, {weights});' -) - - -class SimpleRNNConfigTemplate(LayerConfigTemplate): - def __init__(self): - super().__init__(SimpleRNN) - self.template = simple_rnn_config_template - self.act_template = activ_config_template - self.recr_act_template = activ_config_template - - def format(self, node): - simple_rnn_params = self._default_config_params(node) - simple_rnn_params['n_in'] = node.get_attr('n_in') - simple_rnn_params['n_out'] = node.get_attr('n_out') - simple_rnn_params['n_outputs'] = node.get_attr('n_timesteps') if node.get_attr('return_sequences', False) else '1' - simple_rnn_params['return_sequences'] = str(node.get_attr('return_sequences')).lower() - simple_rnn_params['act_t'] = '{}_config{}'.format(node.get_attr('activation'), str(node.index) + '_act') - simple_rnn_params['act_recurrent_t'] = '{}_config{}'.format( - node.get_attr('recurrent_activation'), str(node.index) + '_rec_act' - ) - simple_rnn_params['recurrent_activation'] = 'relu' - - simple_rnn_config = self.template.format(**simple_rnn_params) - - act_params = self._default_config_params(node) - act_params['type'] = node.get_attr('activation') - act_params['n_in'] = node.get_attr('n_out') - act_params['index'] = str(node.index) + '_act' - act_config = self.act_template.format(**act_params) - - recr_act_params = self._default_config_params(node) - recr_act_params['type'] = node.get_attr('recurrent_activation') - recr_act_params['n_in'] = node.get_attr('n_out') - recr_act_params['index'] = str(node.index) + '_rec_act' - recr_act_config = self.recr_act_template.format(**recr_act_params) - - return act_config + '\n' + recr_act_config + '\n' + simple_rnn_config - - -class SimpleRNNFunctionTemplate(FunctionCallTemplate): - def __init__(self): - super().__init__(SimpleRNN, include_header=recurrent_include_list) - - def format(self, node): - params = self._default_function_params(node) - if params['pass_initial_states'] == 'true': - params['input2_t'] = node.get_input_variable(node.inputs[1]).type.name - params['input2'] = node.get_input_variable(node.inputs[1]).name - - if node.get_attr('pytorch', False): - if params['pass_initial_states'] == 'true': - template = simple_rnn_pytorch_function_initial_state_template - else: - template = simple_rnn_pytorch_function_template - params['weights'] = 'w{0}, wr{0}, b{0}, br{0}'.format(str(node.index)) - else: - template = simple_rnn_function_template - params['weights'] = 'w{0}, wr{0}, b{0}'.format(str(node.index)) - return template.format(**params) diff --git a/hls4ml/backends/quartus/passes/reshaping_templates.py b/hls4ml/backends/quartus/passes/reshaping_templates.py deleted file mode 100644 index 0db01e6543..0000000000 --- a/hls4ml/backends/quartus/passes/reshaping_templates.py +++ /dev/null @@ -1,138 +0,0 @@ -from hls4ml.backends.template import FunctionCallTemplate, LayerConfigTemplate -from hls4ml.model.layers import Resize, Transpose, ZeroPadding1D, ZeroPadding2D - -# ZeroPadding templates - -zeropad1d_config_template = """struct config{index} : nnet::padding1d_config {{ - static const unsigned in_width = {in_width}; - static const unsigned out_width = {out_width}; - static const unsigned n_chan = {n_chan}; - - static const unsigned pad_left = {pad_left}; - static const unsigned pad_right = {pad_right}; -}};\n""" - -zeropad2d_config_template = """struct config{index} : nnet::padding2d_config {{ - static const unsigned in_height = {in_height}; - static const unsigned in_width = {in_width}; - static const unsigned out_height = {out_height}; - static const unsigned out_width = {out_width}; - static const unsigned n_chan = {n_chan}; - - static const unsigned pad_top = {pad_top}; - static const unsigned pad_bottom = {pad_bottom}; - static const unsigned pad_left = {pad_left}; - static const unsigned pad_right = {pad_right}; -}};\n""" - -zeropad1d_function_template = 'nnet::zeropad1d_{data_format}<{input_t}, {output_t}, {config}>({input}, {output});' -zeropad2d_function_template = 'nnet::zeropad2d_{data_format}<{input_t}, {output_t}, {config}>({input}, {output});' - -padding_include_list = ['nnet_utils/nnet_padding.h', 'nnet_utils/nnet_padding_stream.h'] - - -class ZeroPaddingConfigTemplate(LayerConfigTemplate): - def __init__(self): - super().__init__((ZeroPadding1D, ZeroPadding2D)) - self.templates = { - 'ZeroPadding1D': zeropad1d_config_template, - 'ZeroPadding2D': zeropad2d_config_template, - } - - def format(self, node): - params = self._default_config_params(node) - return self.templates[node.class_name].format(**params) - - -class ZeroPaddingFunctionTemplate(FunctionCallTemplate): - def __init__(self): - super().__init__((ZeroPadding1D, ZeroPadding2D), include_header=padding_include_list) - self.templates = { - 'ZeroPadding1D': zeropad1d_function_template, - 'ZeroPadding2D': zeropad2d_function_template, - } - - def format(self, node): - params = self._default_function_params(node) - if node.get_attr('data_format') == 'channels_first': - raise Exception('Quartus only supports channels_last data format') - params['data_format'] = 'cl' - - return self.templates[node.class_name].format(**params) - - -# Resize templates - -resize_config_template = """struct config{index} : nnet::resize_config {{ - static const unsigned height = {in_height}; - static const unsigned width = {in_width}; - - static const unsigned new_height = {out_height}; - static const unsigned new_width = {out_width}; - - static const unsigned n_chan = {n_chan}; -}};\n""" - -resize_function_template = 'nnet::resize_{algorithm}<{input_t}, {config}>({input}, {output});' -resize_include_list = ['nnet_utils/nnet_resize.h', 'nnet_utils/nnet_resize_stream.h'] - - -class ResizeConfigTemplate(LayerConfigTemplate): - def __init__(self): - super().__init__(Resize) - self.template = resize_config_template - - def format(self, node): - params = self._default_config_params(node) - - return self.template.format(**params) - - -class ResizeFunctionTemplate(FunctionCallTemplate): - def __init__(self): - super().__init__(Resize, include_header=resize_include_list) - self.template = resize_function_template - - def format(self, node): - params = self._default_function_params(node) - if node.get_attr('algorithm') != 'nearest': - raise Exception('Currently only supporting resize_nearest') - params['algorithm'] = node.get_attr('algorithm') - - return self.template.format(**params) - - -# Transpose templates - -transpose_config_template = """struct config{index} : nnet::transpose_config {{ - static const unsigned depth = {depth}; - static const unsigned height = {height}; - static const unsigned width = {width}; - static constexpr unsigned perm[3] = {{{perm_str}}}; -}};\n""" - -transpose_function_template = 'nnet::transpose_{dim}<{input_t}, {output_t}, {config}>({input}, {output});' -transpose_include_list = ['nnet_utils/nnet_transpose.h', 'nnet_utils/nnet_transpose_stream.h'] - - -class TransposeConfigTemplate(LayerConfigTemplate): - def __init__(self): - super().__init__(Transpose) - self.template = transpose_config_template - - def format(self, node): - params = self._default_config_params(node) - - return self.template.format(**params) - - -class TransposeFunctionTemplate(FunctionCallTemplate): - def __init__(self): - super().__init__(Transpose, include_header=transpose_include_list) - self.template = transpose_function_template - - def format(self, node): - params = self._default_function_params(node) - params['dim'] = node.get_attr('dim') - - return self.template.format(**params) diff --git a/hls4ml/backends/quartus/passes/resource_strategy.py b/hls4ml/backends/quartus/passes/resource_strategy.py deleted file mode 100644 index d44a8b36a3..0000000000 --- a/hls4ml/backends/quartus/passes/resource_strategy.py +++ /dev/null @@ -1,77 +0,0 @@ -import numpy as np - -from hls4ml.model.layers import GRU, LSTM, Conv1D, Conv2D, Dense, SimpleRNN -from hls4ml.model.optimizer import OptimizerPass - - -class ApplyResourceStrategy(OptimizerPass): - """Transposes the weights to use the dense_resource matrix multiply routine""" - - def match(self, node): - node_matches = isinstance(node, (Dense, Conv1D, Conv2D, GRU, LSTM, SimpleRNN)) - is_resource_strategy = ( - True # node.get_attr('strategy', '').lower() == 'resource' -> Quartus only supportr Resource strategy - ) - already_transformed = node.get_attr('_weights_transposed', False) is True - return node_matches and is_resource_strategy and not already_transformed - - def transform(self, model, node): - if isinstance(node, Dense) and not node.model.config.get_compression(node): - rf = node.get_attr('reuse_factor') - bf = int((node.attributes['n_in'] * node.attributes['n_out']) / rf) - bf_rounded = int(pow(2, np.ceil(np.log2(bf)))) - rf_rounded = int(pow(2, np.ceil(np.log2(rf)))) - - node.weights['weight'].data = np.transpose(node.weights['weight'].data).flatten() - - if node.attributes['n_in'] * node.attributes['n_out'] > 2048 and rf_rounded != rf: - node.set_attr('rfpad', rf_rounded - rf) - node.set_attr('bfpad', bf_rounded - bf) - - temp = np.empty([bf_rounded, rf_rounded]) - for i in range(rf_rounded): - for j in range(bf_rounded): - if i < rf and j < bf: - w_index = i + rf * j - temp[j][i] = node.weights['weight'].data[w_index] - else: - temp[j][i] = 0 - node.weights['weight'].data = temp.flatten() - node.weights['weight'].data_length = node.weights['weight'].data.size - - elif isinstance(node, Conv1D): - # (W,C,F) => (F,W,C) - # IMPORTANT - This format only works with im2col convolution - # - Future commits add new optimizers that further transpose THIS format to a format - # useful for Winograd's minimal filtering algorithm - node.weights['weight'].data = np.transpose(node.weights['weight'].data, axes=[2, 0, 1]) - - elif isinstance(node, Conv2D): - # (H,W,C,F) => (F,H,W,C) - # IMPORTANT - This format only works with im2col convolution - # - Future commits add new optimizers that further transpose THIS format to a format - # useful for Winograd's minimal filtering algorithm - node.weights['weight'].data = np.transpose(node.weights['weight'].data, axes=[3, 0, 1, 2]) - - elif isinstance(node, GRU): - node.weights['weight'].data = np.transpose(node.weights['weight'].data) - node.weights['recurrent_weight'].data = np.transpose(node.weights['recurrent_weight'].data) - - elif isinstance(node, SimpleRNN): - node.weights['weight'].data = np.transpose(node.weights['weight'].data) - node.weights['recurrent_weight'].data = np.transpose(node.weights['recurrent_weight'].data) - - elif isinstance(node, LSTM): - node.weights['weight'].data = np.transpose(node.weights['weight'].data) - node.weights['recurrent_weight'].data = np.transpose(node.weights['recurrent_weight'].data) - - for weight_type in ['i', 'f', 'c', 'o']: - node.weights[f'weight_{weight_type}'].data = np.transpose(node.weights[f'weight_{weight_type}'].data) - node.weights[f'recurrent_weight_{weight_type}'].data = np.transpose( - node.weights[f'recurrent_weight_{weight_type}'].data - ) - - else: - raise Exception(f'Unexpected layer {node.class_name} with resource strategy') - node.set_attr('_weights_transposed', True) - return False diff --git a/hls4ml/backends/quartus/passes/transform_types.py b/hls4ml/backends/quartus/passes/transform_types.py deleted file mode 100644 index 041aad8136..0000000000 --- a/hls4ml/backends/quartus/passes/transform_types.py +++ /dev/null @@ -1,52 +0,0 @@ -from hls4ml.backends.fpga.fpga_types import ACTypeConverter, HLSTypeConverter, StaticWeightVariableConverter -from hls4ml.backends.quartus.quartus_types import ( - QuartusArrayVariableConverter, - QuartusInplaceArrayVariableConverter, - QuartusInplaceStreamVariableConverter, - QuartusStreamVariableConverter, - QuartusStructMemberVariableConverter, -) -from hls4ml.model.optimizer import GlobalOptimizerPass -from hls4ml.model.types import InplaceTensorVariable - - -class TransformTypes(GlobalOptimizerPass): - def __init__(self): - self.type_converter = HLSTypeConverter(precision_converter=ACTypeConverter()) - self.array_var_converter = QuartusArrayVariableConverter(type_converter=self.type_converter) - self.inplace_array_var_converter = QuartusInplaceArrayVariableConverter(type_converter=self.type_converter) - self.struct_var_converter = QuartusStructMemberVariableConverter(type_converter=self.type_converter) - self.stream_var_converter = QuartusStreamVariableConverter(type_converter=self.type_converter) - self.inplace_stream_var_converter = QuartusInplaceStreamVariableConverter(type_converter=self.type_converter) - self.weight_var_converter = StaticWeightVariableConverter(type_converter=self.type_converter) - - def transform(self, model, node): - io_type = node.model.config.get_config_value('IOType') - - for out_name, var in node.variables.items(): - if io_type == 'io_stream': - if isinstance(var, InplaceTensorVariable): - new_var = self.inplace_stream_var_converter.convert(var) - else: - new_var = self.stream_var_converter.convert(var) - elif io_type == 'io_parallel': - if out_name in node.model.inputs: - new_var = self.struct_var_converter.convert(var, pragma='hls_register', struct_name='inputs') - elif out_name in node.model.outputs: - new_var = self.struct_var_converter.convert(var, pragma='hls_register', struct_name='outputs') - elif isinstance(var, InplaceTensorVariable): - new_var = self.inplace_array_var_converter.convert(var, pragma='') - else: - new_var = self.array_var_converter.convert(var, pragma='hls_register') - else: - raise Exception(f'Unknown IOType {io_type} in {node.name} ({node.class_name})') - - node.set_attr(out_name, new_var) - - for w_name, weight in node.weights.items(): - new_weight = self.weight_var_converter.convert(weight) - node.set_attr(w_name, new_weight) - - for t_name, type in node.types.items(): - new_type = self.type_converter.convert(type) - node.set_attr(t_name, new_type) diff --git a/hls4ml/backends/quartus/quartus_backend.py b/hls4ml/backends/quartus/quartus_backend.py deleted file mode 100644 index 8f0731d496..0000000000 --- a/hls4ml/backends/quartus/quartus_backend.py +++ /dev/null @@ -1,392 +0,0 @@ -import os -from contextlib import contextmanager -from warnings import warn - -import numpy as np - -from hls4ml.backends import FPGABackend -from hls4ml.model.attributes import ConfigurableAttribute, TypeAttribute -from hls4ml.model.flow import register_flow -from hls4ml.model.layers import GRU, LSTM, Activation, Conv1D, Conv2D, Dense, Embedding, Layer, SimpleRNN, Softmax -from hls4ml.model.optimizer import get_backend_passes, layer_optimizer -from hls4ml.model.types import FixedPrecisionType, IntegerPrecisionType, NamedType -from hls4ml.report import parse_quartus_report -from hls4ml.utils import attribute_descriptions as descriptions - - -@contextmanager -def chdir(newdir): - prevdir = os.getcwd() - os.chdir(os.path.expanduser(newdir)) - try: - yield - finally: - os.chdir(prevdir) - - -class QuartusBackend(FPGABackend): - def __init__(self): - super().__init__('Quartus') - self._register_layer_attributes() - self._register_flows() - - def _register_layer_attributes(self): - # Add RNN-specific recurrent_reuse_factor attribute - rnn_layers = [ - SimpleRNN, - LSTM, - GRU, - ] - - for layer in rnn_layers: - attrs = self.attribute_map.get(layer, []) - attrs.append(ConfigurableAttribute('recurrent_reuse_factor', default=1, description=descriptions.reuse_factor)) - attrs.append(ConfigurableAttribute('table_size', default=1024, description=descriptions.table_size)) - attrs.append(TypeAttribute('table', default=FixedPrecisionType(18, 8), description=descriptions.table_type)) - self.attribute_map[layer] = attrs - - def _register_flows(self): - initializers = self._get_layer_initializers() - init_flow = register_flow('init_layers', initializers, requires=['optimize'], backend=self.name) - - streaming_passes = [ - 'quartus:inplace_stream_flatten', # Inform downstream changed packsize in case of skipping flatten - 'quartus:reshape_stream', - 'quartus:clone_output', - ] - - streaming_flow = register_flow('streaming', streaming_passes, requires=[init_flow], backend=self.name) - - quartus_types = [ - 'quartus:transform_types', - 'quartus:register_bram_weights', - 'quartus:apply_resource_strategy', - 'quartus:apply_winograd_kernel_transformation', - ] - quartus_types_flow = register_flow('specific_types', quartus_types, requires=[init_flow], backend=self.name) - - quantization_passes = [ - 'quartus:merge_batch_norm_quantized_tanh', - 'quartus:quantize_dense_output', - 'fuse_consecutive_batch_normalization', - 'quartus:xnor_pooling', - ] - quantization_flow = register_flow('quantization', quantization_passes, requires=[init_flow], backend=self.name) - - optimization_passes = [ - 'quartus:remove_final_reshape', - 'quartus:optimize_pointwise_conv', - 'quartus:inplace_parallel_reshape', - 'quartus:inplace_stream_flatten', - 'quartus:skip_softmax', - 'quartus:fix_softmax_table_size', - 'quartus:process_fixed_point_quantizer_layer', - 'infer_precision_types', - ] - optimization_flow = register_flow('optimize', optimization_passes, requires=[init_flow], backend=self.name) - - templates = self._get_layer_templates() - template_flow = register_flow('apply_templates', self._get_layer_templates, requires=[init_flow], backend=self.name) - - writer_passes = ['make_stamp', 'quartus:write_hls'] - - self._writer_flow = register_flow('write', writer_passes, requires=['quartus:ip'], backend=self.name) - - all_passes = get_backend_passes(self.name) - - extras = [ - # Ideally this should be empty - opt_pass - for opt_pass in all_passes - if opt_pass - not in initializers - + streaming_passes - + quartus_types - + quantization_passes - + templates - + optimization_passes - + writer_passes - ] - - if len(extras) > 0: - for opt in extras: - warn(f'WARNING: Optimizer "{opt}" is not part of any flow and will not be executed.') - - ip_flow_requirements = [ - 'optimize', - init_flow, - streaming_flow, - quantization_flow, - optimization_flow, - quartus_types_flow, - template_flow, - ] - - self._default_flow = register_flow('ip', None, requires=ip_flow_requirements, backend=self.name) - - def get_default_flow(self): - return self._default_flow - - def get_writer_flow(self): - return self._writer_flow - - def create_initial_config(self, part='Arria10', clock_period=5, io_type='io_parallel', write_tar=False, **_): - config = {} - - config['Part'] = part if part is not None else 'Arria10' - config['ClockPeriod'] = clock_period if clock_period is not None else 5 - config['IOType'] = io_type if io_type is not None else 'io_parallel' - config['HLSConfig'] = {} - config['WriterConfig'] = { - 'WriteTar': write_tar, - } - - return config - - def build(self, model, synth=True, fpgasynth=False, log_level=1, cont_if_large_area=False): - """ - Builds the project using Intel HLS compiler. - - Args: - model (ModelGraph): The model to build - synth, optional: Whether to run HLS synthesis - fpgasynth, optional: Whether to run FPGA synthesis (Quartus Compile) - log_level, optional: Logging level to be displayed during HLS synthesis (0, 1, 2) - cont_if_large_area: Instruct the HLS compiler to continue synthesis if the estimated resource usage exceeds - device resources - Errors raise exceptions - """ - - # Check software needed is present - found = os.system('command -v i++ > /dev/null') - if found != 0: - raise Exception('Intel HLS installation not found. Make sure "i++" is on PATH.') - - if fpgasynth: - if fpgasynth and not synth: - raise Exception('HLS Synthesis needs to be run before FPGA synthesis') - found = os.system('command -v quartus_sh > /dev/null') - if found != 0: - raise Exception('Quartus installation not found. Make sure "quartus_sh" is on PATH.') - - with chdir(model.config.get_output_dir()): - if synth: - quartus_compile = 'QUARTUS_COMPILE=--quartus-compile' if fpgasynth else '' - cont_synth = 'CONT_IF_LARGE_AREA=--dont-error-if-large-area-est' if cont_if_large_area else '' - log_1 = 'LOGGING_1=-v ' if log_level >= 1 else '' - log_2 = 'LOGGING_2=-v ' if log_level >= 2 else '' - os.system(f'make {model.config.get_project_name()}-fpga {log_1} {log_2} {cont_synth} {quartus_compile}') - - # If running i++ through a container, such a singularity, this command will throw an exception, because the - # host OS doesn't have access to HLS simulation tools. To avoid the exception, shell into the container - # (e.g. singularity shell ....) and then execute the following command manually - # This command simply tests the IP using a simulation tool and obtains the latency and initiation interval - os.system(f'./{model.config.get_project_name()}-fpga') - - return parse_quartus_report(model.config.get_output_dir()) - - @layer_optimizer(Layer) - def init_base_layer(self, layer): - reuse_factor = layer.model.config.get_reuse_factor(layer) - layer.set_attr('reuse_factor', reuse_factor) - - target_cycles = layer.model.config.get_target_cycles(layer) - layer.set_attr('target_cycles', target_cycles) - - @layer_optimizer(Dense) - def init_dense(self, layer): - index_t = IntegerPrecisionType(width=1, signed=False) - - layer.set_attr('rfpad', 0) - layer.set_attr('bfpad', 0) - - if layer.model.config.get_compression(layer): - layer.set_attr('strategy', 'compressed') - else: - n_in, n_out = self.get_layer_mult_size(layer) - self.set_closest_reuse_factor(layer, n_in, n_out) - layer.set_attr('strategy', 'resource') - - if layer.model.config.is_resource_strategy(layer): - if layer.model.config.get_compression(layer): - index_t = layer.get_weights('weight').type.index_precision - - layer.set_attr('index_t', NamedType(f'layer{layer.index}_index', index_t)) - - @layer_optimizer(Activation) - def init_activation(self, layer): - if layer.get_attr('activation') == 'tanh': - layer.set_attr('activation', 'dense_tanh') - if layer.get_attr('recurrent_activation') == 'tanh': - layer.set_attr('recurrent_activation', 'dense_tanh') - - @layer_optimizer(Softmax) - def init_softmax(self, layer): - if layer.model.config.get_config_value('IOType') == 'io_parallel': - assert len(layer.get_input_variable().shape) == 1, ( - 'Softmax with io_parallel strategy cannot be used on multidimensional tensors.' - ) - - @layer_optimizer(Embedding) - def init_embed(self, layer): - if layer.attributes['n_in'] is None: - raise Exception('Input length of Embedding layer must be specified.') - - @layer_optimizer(GRU) - def init_gru(self, layer): - reuse_factor = layer.model.config.get_reuse_factor(layer) - layer.set_attr('recurrent_reuse_factor', reuse_factor) - - # Dense multiplication properties - layer.set_attr('rfpad', 0) - layer.set_attr('bfpad', 0) - - index_t = IntegerPrecisionType(width=1, signed=False) - layer.set_attr('index_t', index_t) - - if 'table_t' not in layer.attributes: - layer.set_attr( - 'table_t', NamedType(name=layer.name + '_table_t', precision=FixedPrecisionType(width=18, integer=8)) - ) - if 'table_size' not in layer.attributes: - layer.set_attr('table_size', 1024) - if True: # layer.model.config.is_resource_strategy(layer): ... Quartus only supports Dense resource multiplication - n_in, n_out, n_in_recr, n_out_recr = self.get_layer_mult_size(layer) - self.set_closest_reuse_factor(layer, n_in, n_out) - self.set_closest_reuse_factor(layer, n_in_recr, n_out_recr, attribute='recurrent_reuse_factor') - layer.set_attr('strategy', 'resource') - - layer.set_attr('index_t', index_t) - - @layer_optimizer(Conv1D) - def init_conv1d(self, layer): - # This can happen if we assign weights of Dense layer to 1x1 Conv1D - if len(layer.weights['weight'].data.shape) == 2: - layer.weights['weight'].data = np.expand_dims(layer.weights['weight'].data, axis=(0, 1)) - - # Dense matrix multiply properties - layer.set_attr('rfpad', 0) - layer.set_attr('bfpad', 0) - - # Reuse and parallelization factors - layer.set_attr('strategy', 'resource') - n_in, n_out = self.get_layer_mult_size(layer) - self.set_target_reuse_factor(layer) - self.set_closest_reuse_factor(layer, n_in, n_out) - - # Not overriding user parallelization factor, if already set and user has not specified a value - user_pf = layer.model.config.get_layer_config_value(layer, 'ParallelizationFactor', None) - layer_pf = layer.get_attr('parallelization_factor', None) - chosen_pf = user_pf or layer_pf or 1 - if user_pf is not None and layer_pf is not None: - if user_pf != layer_pf: - warn( - f'For layer {layer.name}, parallelization factor of {layer_pf} is defined in the proxy-model, but is overridden by the user to {user_pf}.' # noqa: E501 - ) - layer.set_attr('parallelization', chosen_pf) - - # impl_filt_width determines the filter size post-Winograd transformation - layer.set_attr('impl_filt_width', layer.get_attr('filt_width')) - - # Implementation: - # - combination - at compile-time, the decision between Winograd and im2col is made - # - im2col - specifically use im2col - # - Winograd - use Winograd, if possible - layer.set_attr('implementation', layer.model.config.get_layer_config_value(layer, 'Implementation', 'im2col')) - - layer.set_attr( - 'n_partitions', 1 - ) # TODO Not used yet as there is no codegen implementation of CNNs for Quartus backend - - @layer_optimizer(Conv2D) - def init_conv2d(self, layer): - # This can happen if we assign weights of Dense layer to 1x1 Conv2D - if len(layer.weights['weight'].data.shape) == 2: - layer.weights['weight'].data = np.expand_dims(layer.weights['weight'].data, axis=(0, 1)) - - # Dense matrix multiply properties - layer.set_attr('rfpad', 0) - layer.set_attr('bfpad', 0) - - # Reuse and parallelization factors - layer.set_attr('strategy', 'resource') - n_in, n_out = self.get_layer_mult_size(layer) - self.set_target_reuse_factor(layer) - self.set_closest_reuse_factor(layer, n_in, n_out) - - # Not overriding user parallelization factor, if already set and user has not specified a value - user_pf = layer.model.config.get_layer_config_value(layer, 'ParallelizationFactor', None) - layer_pf = layer.get_attr('parallelization_factor', None) - chosen_pf = user_pf or layer_pf or 1 - if user_pf is not None and layer_pf is not None: - if user_pf != layer_pf: - warn( - f'For layer {layer.name}, parallelization factor of {layer_pf} is defined in the proxy-model, but is overridden by the user to {user_pf}.' # noqa: E501 - ) - layer.set_attr('parallelization', chosen_pf) - - # impl_filt_width & impl_filt_height determine the filter size post-Winograd transformation - layer.set_attr('impl_filt_height', layer.get_attr('filt_height')) - layer.set_attr('impl_filt_width', layer.get_attr('filt_width')) - - # Implementation: - # - combination - at compile-time, the decision between Winograd and im2col is made - # - im2col - specifically use im2col - # - Winograd - use Winograd, if possible - layer.set_attr('implementation', layer.model.config.get_layer_config_value(layer, 'Implementation', 'im2col')) - - layer.set_attr( - 'n_partitions', 1 - ) # TODO Not used yet as there is no codegen implementation of CNNs for Quartus backend - - @layer_optimizer(LSTM) - def init_lstm(self, layer): - reuse_factor = layer.model.config.get_reuse_factor(layer) - layer.set_attr('recurrent_reuse_factor', reuse_factor) - - # We don't use RF yet - if True: # layer.model.config.is_resource_strategy(layer): ... Quartus only supports Dense resource multiplication - n_in, n_out, n_in_recr, n_out_recr = self.get_layer_mult_size(layer) - self.set_closest_reuse_factor(layer, n_in, n_out) - self.set_closest_reuse_factor(layer, n_in_recr, n_out_recr, attribute='recurrent_reuse_factor') - layer.set_attr('strategy', 'resource') - - # Split weights for easier storage in on-chip memory and implementation in HLS - weights_data = layer.weights['weight'].data - rec_weights_data = layer.weights['recurrent_weight'].data - bias_data = layer.weights['bias'].data - - weight_types = ['i', 'f', 'c', 'o'] - for i in range(0, 4): - layer.add_weights_variable( - name=f'weight_{weight_types[i]}', - var_name=f'kernel_{weight_types[i]}_{{index}}', - data=weights_data[ - 0 : layer.get_attr('n_in'), i * layer.get_attr('n_out') : (i + 1) * layer.get_attr('n_out') - ], - quantizer=layer.get_attr('weight_quantizer'), - compression=None, - ) - layer.add_weights_variable( - name=f'recurrent_weight_{weight_types[i]}', - var_name=f'recurrent_kernel_{weight_types[i]}_{{index}}', - data=rec_weights_data[ - 0 : layer.get_attr('n_out'), i * layer.get_attr('n_out') : (i + 1) * layer.get_attr('n_out') - ], - quantizer=layer.get_attr('weight_quantizer'), - compression=None, - ) - layer.add_weights_variable( - name=f'bias_{weight_types[i]}', - var_name=f'bias_{weight_types[i]}_{{index}}', - data=bias_data[i * layer.get_attr('n_out') : (i + 1) * (layer.get_attr('n_out'))], - quantizer=layer.get_attr('weight_quantizer'), - compression=None, - ) - - @layer_optimizer(SimpleRNN) - def init_simple_rnn(self, layer): - reuse_factor = layer.model.config.get_reuse_factor(layer) - layer.set_attr('recurrent_reuse_factor', reuse_factor) - - # TODO - Consider setting and using RF diff --git a/hls4ml/backends/quartus/quartus_types.py b/hls4ml/backends/quartus/quartus_types.py deleted file mode 100644 index e641c9aba7..0000000000 --- a/hls4ml/backends/quartus/quartus_types.py +++ /dev/null @@ -1,90 +0,0 @@ -from hls4ml.backends.fpga.fpga_types import ( - ArrayVariableConverter, - InplaceStreamVariableConverter, - StreamVariableConverter, - StructMemberVariableConverter, - VariableDefinition, -) - -# region ArrayVariable - - -class QuartusArrayVariableDefinition(VariableDefinition): - def definition_cpp(self, name_suffix='', as_reference=False): - return '{type} {name}{suffix}[{shape}] {pragma}'.format( - type=self.type.name, name=self.name, suffix=name_suffix, shape=self.size_cpp(), pragma=self.pragma - ) - - -class QuartusInplaceArrayVariableDefinition(VariableDefinition): - def definition_cpp(self): - return f'auto& {self.name} = {self.input_var.name}' - - -class QuartusArrayVariableConverter(ArrayVariableConverter): - def __init__(self, type_converter): - super().__init__(type_converter=type_converter, prefix='Quartus', definition_cls=QuartusArrayVariableDefinition) - - -class QuartusInplaceArrayVariableConverter(ArrayVariableConverter): - def __init__(self, type_converter): - super().__init__( - type_converter=type_converter, prefix='Quartus', definition_cls=QuartusInplaceArrayVariableDefinition - ) - - -# endregion - -# region StructMemberVariable - - -class QuartusStructMemberVariableDefinition(VariableDefinition): - def definition_cpp(self, name_suffix='', as_reference=False): - return '{type} {name}{suffix}[{shape}]'.format( - type=self.type.name, name=self.member_name, suffix=name_suffix, shape=self.size_cpp() - ) - - -class QuartusStructMemberVariableConverter(StructMemberVariableConverter): - def __init__(self, type_converter): - super().__init__( - type_converter=type_converter, prefix='Quartus', definition_cls=QuartusStructMemberVariableDefinition - ) - - -# endregion - -# region StreamVariable - - -class QuartusStreamVariableDefinition(VariableDefinition): - def definition_cpp(self, name_suffix='', as_reference=False): - if as_reference: # Function parameter - return f'stream<{self.type.name}> &{self.name}{name_suffix}' - else: # Declaration - return f'stream<{self.type.name}> {self.name}{name_suffix}' - - -class QuartusInplaceStreamVariableDefinition(VariableDefinition): - def definition_cpp(self): - return f'auto& {self.name} = {self.input_var.name}' - - -class QuartusStreamVariableConverter(StreamVariableConverter): - def __init__(self, type_converter): - super().__init__(type_converter=type_converter, prefix='Quartus', definition_cls=QuartusStreamVariableDefinition) - - -# endregion - -# region InplaceStreamVariable - - -class QuartusInplaceStreamVariableConverter(InplaceStreamVariableConverter): - def __init__(self, type_converter): - super().__init__( - type_converter=type_converter, prefix='Quartus', definition_cls=QuartusInplaceStreamVariableDefinition - ) - - -# endregion diff --git a/hls4ml/cli/__init__.py b/hls4ml/cli/__init__.py index ddaf4ed4db..4b8eaaba10 100755 --- a/hls4ml/cli/__init__.py +++ b/hls4ml/cli/__init__.py @@ -42,9 +42,7 @@ def main(): config_parser.add_argument('-d', '--dir', help='Project output directory', default='my-hls-test') config_parser.add_argument('-f', '--fpga', help='FPGA part', default='xcvu13p-flga2577-2-e') config_parser.add_argument('-bo', '--board', help='Board used.', default='pynq-z2') - config_parser.add_argument( - '-ba', '--backend', help='Backend to use (Vivado, VivadoAccelerator, Quartus)', default='Vivado' - ) + config_parser.add_argument('-ba', '--backend', help='Backend to use (Vivado, VivadoAccelerator)', default='Vivado') config_parser.add_argument('-c', '--clock', help='Clock frequency (ns)', type=int, default=5) config_parser.add_argument( '-g', '--granularity', help='Granularity of configuration. One of "model", "type" or "name"', default='model' @@ -166,7 +164,6 @@ def _build(args, extra_args): backend_map = {} backend_map['vivado'] = _build_vivado - backend_map['quartus'] = _build_quartus backend = yamlConfig.get('Backend') @@ -244,50 +241,6 @@ def _build_vivado(args, extra_args): ) -def _build_quartus(args, extra_args): - quartus_parser = argparse.ArgumentParser(prog=f'hls4ml build -p {args.project}', add_help=False) - quartus_parser.add_argument( - '-s', '--synthesis', help='Compile project and run C/RTL synthesis', action='store_true', default=False - ) - quartus_parser.add_argument( - '-q', '--quartus-synthesis', help='Run Quartus synthesis (implies -s)', action='store_true', default=False - ) - quartus_parser.add_argument( - '-a', '--all', help='Run C simulation, C/RTL synthesis, Quartus synthesis', action='store_true' - ) - - if args.list_options: - quartus_parser.print_help() - sys.exit(0) - - quartus_args = quartus_parser.parse_args(extra_args) - - synth = int(quartus_args.synthesis) - qsynth = int(quartus_args.quartus_synthesis) - if quartus_args.all: - synth = qsynth = 1 - - yamlConfig = hls4ml.converters.parse_yaml_config(args.project + '/' + config_filename) - project_name = yamlConfig['ProjectName'] - - curr_dir = os.getcwd() - - os.chdir(yamlConfig['OutputDir']) - if synth: - os.system(f'make {project_name}-fpga') - os.system(f'./{project_name}-fpga') - - if qsynth: - found = os.system('command -v quartus_sh > /dev/null') - if found != 0: - print('Quartus installation not found. Make sure "quartus_sh" is on PATH.') - sys.exit(1) - os.chdir(project_name + '-fpga.prj/quartus') - os.system('quartus_sh --flow compile quartus_compile') - - os.chdir(curr_dir) - - def _report(args, extra_args): if args.project is None: print('Project directory (-p or --project) must be provided.') @@ -301,7 +254,6 @@ def _report(args, extra_args): backend_map = {} backend_map['vivado'] = _report_vivado - backend_map['quartus'] = _report_quartus backend = yamlConfig.get('Backend') @@ -322,18 +274,5 @@ def _report_vivado(args, extra_args): hls4ml.report.read_vivado_report(args.project, vivado_args.full) -def _report_quartus(args, extra_args): - quartus_parser = argparse.ArgumentParser(prog=f'hls4ml report -p {args.project}', add_help=False) - quartus_parser.add_argument( - '-b', '--open-browser', help='Open a web browser with the report', action='store_true', default=False - ) - - if args.list_options: - quartus_parser.print_help() - else: - quartus_args = quartus_parser.parse_args(extra_args) - hls4ml.report.read_quartus_report(args.project, quartus_args.open_browser) - - if __name__ == '__main__': main() diff --git a/hls4ml/converters/__init__.py b/hls4ml/converters/__init__.py index 27d560cbe5..b94012ecd6 100644 --- a/hls4ml/converters/__init__.py +++ b/hls4ml/converters/__init__.py @@ -191,8 +191,7 @@ def convert_from_keras_model( used during csim and cosim. output_data_tb (str, optional): String representing the path of output data in .npy or .dat format that will be used during csim and cosim. - backend (str, optional): Name of the backend to use, e.g., 'Vivado' - or 'Quartus' or 'Catapult'. + backend (str, optional): Name of the backend to use, e.g., 'Vivado' or 'Catapult'. board (str, optional): One of target boards specified in `supported_board.json` file. If set to `None` a default device of a backend will be used. See documentation of the backend used. part (str, optional): The FPGA part. If set to `None` a default part of a backend will be used. @@ -268,7 +267,7 @@ def convert_from_pytorch_model( used during csim and cosim. Defaults to None. output_data_tb (str, optional): String representing the path of output data in .npy or .dat format that will be used during csim and cosim. Defaults to None. - backend (str, optional): Name of the backend to use, e.g., 'Vivado' or 'Quartus' or 'Catapult'. Defaults to 'Vivado'. + backend (str, optional): Name of the backend to use, e.g., 'Vivado' or 'Catapult'. Defaults to 'Vivado'. board (str, optional): One of target boards specified in `supported_board.json` file. If set to `None` a default device of a backend will be used. See documentation of the backend used. part (str, optional): The FPGA part. If set to `None` a default part of a backend will be used. @@ -343,8 +342,7 @@ def convert_from_onnx_model( used during csim and cosim. output_data_tb (str, optional): String representing the path of output data in .npy or .dat format that will be used during csim and cosim. - backend (str, optional): Name of the backend to use, e.g., 'Vivado' - or 'Quartus' or 'Catapult'. + backend (str, optional): Name of the backend to use, e.g., 'Vivado' or 'Catapult'. board (str, optional): One of target boards specified in `supported_board.json` file. If set to `None` a default device of a backend will be used. See documentation of the backend used. part (str, optional): The FPGA part. If set to `None` a default part of a backend will be used. diff --git a/hls4ml/converters/keras/qkeras.py b/hls4ml/converters/keras/qkeras.py index 42abb2f48e..90e9082d80 100644 --- a/hls4ml/converters/keras/qkeras.py +++ b/hls4ml/converters/keras/qkeras.py @@ -165,7 +165,6 @@ def get_activation_quantizer(keras_layer, input_names, activation_name='activati layer['class_name'] = 'HardActivation' layer['slope'] = 0.5 # the default values in QKeras layer['shift'] = 0.5 - # Quartus seems to have trouble if the width is 1. layer['slope_prec'] = FixedPrecisionType(width=2, integer=0, signed=False) layer['shift_prec'] = FixedPrecisionType(width=2, integer=0, signed=False) layer[activation_name] = activation_config['class_name'].replace('quantized_', 'hard_') diff --git a/hls4ml/report/__init__.py b/hls4ml/report/__init__.py index fee79b3020..e20d38c91e 100644 --- a/hls4ml/report/__init__.py +++ b/hls4ml/report/__init__.py @@ -8,10 +8,6 @@ read_catapult_report, # noqa: F401 ) from hls4ml.report.libero_report import parse_libero_report # noqa: F401 -from hls4ml.report.quartus_report import ( - parse_quartus_report, # noqa: F401 - read_quartus_report, # noqa: F401 -) from hls4ml.report.vitis_unified_report import parse_vitis_unified_report # noqa: F401 from hls4ml.report.vivado_report import ( aggregate_graph_reports, # noqa: F401 diff --git a/hls4ml/report/quartus_report.py b/hls4ml/report/quartus_report.py deleted file mode 100644 index afe6a1cc24..0000000000 --- a/hls4ml/report/quartus_report.py +++ /dev/null @@ -1,262 +0,0 @@ -import os -import webbrowser -from ast import literal_eval - -from hls4ml.utils.dependency import requires - - -def parse_quartus_report(hls_dir, write_to_file=True): - """ - Parse a report from a given Quartus project as a dictionary. - - Args: - hls_dir (string): The directory where the project is found - write_to_file (bool): A flag indicating whether to write the results to a separate file - - Returns: - results (dict): The report dictionary, containing latency, resource usage etc. - - """ - if not os.path.exists(hls_dir): - print(f'Path {hls_dir} does not exist. Exiting.') - return - - prj_dir = _find_project_dir(hls_dir) - - rpt_dir = hls_dir + '/' + prj_dir + '/reports' - if not os.path.exists(rpt_dir): - print(f'Project {prj_dir} does not exist. Rerun "hls4ml build -p {hls_dir}".') - return - - results = _find_reports(rpt_dir) - print(results) - if write_to_file: - print('Here') - f = open(hls_dir + '/synthesis-report.txt', 'w') - f.write('HLS Synthesis Latency & Resource Usage Report') - for key in results: - f.write(str(key) + ':' + str(results[key]) + '\n') - print('There') - print(f'Saved latency & resource usage summary to {hls_dir}/synthesis-report.txt') - return results - - -@requires('quartus-report') -def read_quartus_report(hls_dir, open_browser=False): - """ - Parse and print the Quartus report to print the report. Optionally open a browser. - - Args: - hls_dir (string): The directory where the project is found - open_browser, optional: whether to open a browser - - Returns: - None - """ - from tabulate import tabulate - - report = parse_quartus_report(hls_dir) - - print('HLS Resource Summary\n') - print(tabulate(list(report.items())[0:10], tablefmt='orgtbl', headers=['Resource', 'Utilization'])) - print('\n\nHLS Validation Summary\n') - print(tabulate(list(report.items())[11:13], tablefmt='orgtbl', headers=['', '[Min, Max, Avg]'])) - if 'Clock' in report.keys(): - print('\n\nQuartus Synthesis Summary\n') - print(tabulate(list(report.items())[13:], tablefmt='orgtbl', headers=['Resource', 'Utilization'])) - else: - print('Quartus compile data not found! To generate data run FPGA synthesis') - - if open_browser: - url = 'file:' + os.getcwd() + '/' + _find_project_dir(hls_dir) + '/report.html' - webbrowser.open(url) - - -def _find_project_dir(hls_dir): - """ - Finds the synthesis folder from the HLS project directory - - Args: - hls_dir (string): HLS project location - - Returns: - project_dir (string): Synthesis folder within HLS project directory - """ - top_func_name = None - - with open(hls_dir + '/build_lib.sh') as f: - for line in f.readlines(): - if 'PROJECT=' in line: - top_func_name = line.split(sep='=')[-1].rstrip() - - return top_func_name + '-fpga.prj' - - -@requires('quartus-report') -def read_js_object(js_script): - """ - Reads the JavaScript file and return a dictionary of variables defined in the script. - - Args: - js_script (string) - path to JavaScript File - - Returns: - Dictionary of variables defines in script - """ - from calmjs.parse import asttypes, es5 - - def visit(node): - if isinstance(node, asttypes.Program): - d = {} - for child in node: - if not isinstance(child, asttypes.VarStatement): - raise ValueError('All statements should be var statements') - key, val = visit(child) - d[key] = val - return d - elif isinstance(node, asttypes.VarStatement): - return visit(node.children()[0]) - elif isinstance(node, asttypes.VarDecl): - return (visit(node.identifier), visit(node.initializer)) - elif isinstance(node, asttypes.Object): - d = {} - for property in node: - key = visit(property.left) - value = visit(property.right) - d[key] = value - return d - elif isinstance(node, asttypes.BinOp): - # simple constant folding - if node.op == '+': - if isinstance(node.left, asttypes.String) and isinstance(node.right, asttypes.String): - return visit(node.left) + visit(node.right) - elif isinstance(node.left, asttypes.Number) and isinstance(node.right, asttypes.Number): - return visit(node.left) + visit(node.right) - else: - raise ValueError('Cannot + on anything other than two literals') - else: - raise ValueError("Cannot do operator '%s'" % node.op) - - elif isinstance(node, asttypes.String) or isinstance(node, asttypes.Number): - return literal_eval(node.value) - elif isinstance(node, asttypes.Array): - return [visit(x) for x in node] - elif isinstance(node, asttypes.Null): - return None - elif isinstance(node, asttypes.Boolean): - if str(node) == 'false': - return False - else: - return True - elif isinstance(node, asttypes.Identifier): - return node.value - else: - raise Exception('Unhandled node: %r' % node) - - return visit(es5(js_script)) - - -def _read_quartus_file(filename): - """ - Reads results (clock frequency, resource usage) obtained through FPGA synthesis (full Quartus compilation) - - Args: - filename (string): Location of Quartus report - - Returns: - results (dict): Resource usage obtained through Quartus Compile - """ - - with open(filename) as dataFile: - quartus_data = dataFile.read() - quartus_data = read_js_object(quartus_data) - - results = {} - if quartus_data['quartusJSON']['quartusFitClockSummary']['nodes'][0]['clock'] != 'TBD': - results['Clock'] = quartus_data['quartusJSON']['quartusFitClockSummary']['nodes'][0]['clock'] - results['Quartus ALM'] = quartus_data['quartusJSON']['quartusFitResourceUsageSummary']['nodes'][-1]['alm'] - results['Quartus REG'] = quartus_data['quartusJSON']['quartusFitResourceUsageSummary']['nodes'][-1]['reg'] - results['Quartus DSP'] = quartus_data['quartusJSON']['quartusFitResourceUsageSummary']['nodes'][-1]['dsp'] - results['Quartus RAM'] = quartus_data['quartusJSON']['quartusFitResourceUsageSummary']['nodes'][-1]['ram'] - results['Quartus MLAB'] = quartus_data['quartusJSON']['quartusFitResourceUsageSummary']['nodes'][-1]['mlab'] - else: - print( - 'Quartus report not found. ' - 'Run Quartus Compilation using Quartus Shell or Full Compilation from Intel Quartus Prime' - ) - return results - - -def _read_hls_file(filename): - """ - Reads HLS resource estimate obtained through HLS synthesis - - Args: - filename (string): Location of HLS report - - Returns: - results (dict): Resource usage obtained through HLS Estimation - """ - with open(filename) as dataFile: - report_data = dataFile.read() - report_data = report_data[: report_data.rfind('var fileJSON')] - report_data = read_js_object(report_data) - results = {} - ( - results['HLS Estimate ALUT'], - results['HLS Estimate FF'], - results['HLS Estimate RAM'], - results['HLS Estimate DSP'], - results['HLS Estimate MLAB'], - ) = report_data['areaJSON']['total'] - ( - results['HLS Estimate ALUT (%)'], - results['HLS Estimate FF(%)'], - results['HLS Estimate RAM (%)'], - results['HLS Estimate DSP (%)'], - results['HLS Estimate MLAB (%)'], - ) = report_data['areaJSON']['total_percent'] - return results - - -def _read_verification_file(filename): - """ - Reads verification data (latency, initiation interval) obtained through simulation - - Args: - filename (string): Location of verification file - - Returns: - results (dict): Verification data obtained from simulation - """ - results = {} - if os.path.isfile(filename): - with open(filename) as dataFile: - verification_data = dataFile.read() - verification_data = read_js_object(verification_data) - - try: - results['Number of Invoations'] = verification_data['verifJSON']['functions'][0]['data'][0] - - latency = verification_data['verifJSON']['functions'][0]['data'][1].split(',') - results['Latency (MIN)'] = latency[0] - results['Latency (MAX)'] = latency[1] - results['Latency (AVG)'] = latency[2] - - ii = verification_data['verifJSON']['functions'][0]['data'][2].split(',') - results['ii (MIN)'] = ii[0] - results['ii (MAX)'] = ii[1] - results['ii (AVG)'] = ii[2] - except Exception: - print('Verification data not found. Run ./[projectname]-fpga to generate.') - else: - print('Verification file not found. Run ./[projectname]-fpga to generate.') - return results - - -def _find_reports(rpt_dir): - results = {} - results.update(_read_hls_file(rpt_dir + '/lib/report_data.js')) - results.update(_read_verification_file(rpt_dir + '/lib/verification_data.js')) - results.update(_read_quartus_file(rpt_dir + '/lib/quartus_data.js')) - return results diff --git a/hls4ml/templates/quartus/Makefile b/hls4ml/templates/quartus/Makefile deleted file mode 100644 index 754ea35176..0000000000 --- a/hls4ml/templates/quartus/Makefile +++ /dev/null @@ -1,30 +0,0 @@ -DEVICE := Arria10 -TARGETS := myproject-fpga - -CXX := i++ -CXXFLAGS := -march=$(DEVICE) -RM := rm -rf -DEBUG_FLAGS := --time quartus-hlssynt.log -SOURCE_FILES := myproject_test.cpp firmware/myproject.cpp -HEADER_FILES := firmware/myproject.h -LOGGING_1 := -LOGGING_2 := -QUARTUS_COMPILE := -CONT_IF_LARGE_AREA := - -.PHONY: test -test: $(TARGETS) - @$(foreach t,$(TARGETS),echo ./$(t); ./$(t) | tee $(t)_run.log; echo "";) - -.PHONY: all -all: $(TARGETS) - -.PHONY: clean -clean: - -$(RM) $(foreach t,$(TARGETS),$(t).prj $(t) $(t)_time.log) - -.PHONY: myproject-fpga -myproject-fpga: CXXFLAGS := $(CXXFLAGS) - -$(TARGETS) : $(SOURCE_FILES) $(HEADER_FILES) - $(CXX) $(LOGGING_1) $(LOGGING_2) $(CXXFLAGS) $(DEBUG_FLAGS) $(SOURCE_FILES) $(CONT_IF_LARGE_AREA) $(QUARTUS_COMPILE) -o $@ diff --git a/hls4ml/templates/quartus/ac_types/ac_channel.h b/hls4ml/templates/quartus/ac_types/ac_channel.h deleted file mode 100644 index 62e0542736..0000000000 --- a/hls4ml/templates/quartus/ac_types/ac_channel.h +++ /dev/null @@ -1,555 +0,0 @@ -/************************************************************************** - * * - * Algorithmic C (tm) Datatypes * - * * - * Software Version: 4.0 * - * * - * Release Date : Sat Jun 13 12:35:18 PDT 2020 * - * Release Type : Production Release * - * Release Build : 4.0.0 * - * * - * Copyright 2004-2020, Mentor Graphics Corporation, * - * * - * All Rights Reserved. * - * * - ************************************************************************** - * Licensed under the Apache License, Version 2.0 (the "License"); * - * you may not use this file except in compliance with the License. * - * You may obtain a copy of the License at * - * * - * http://www.apache.org/licenses/LICENSE-2.0 * - * * - * Unless required by applicable law or agreed to in writing, software * - * distributed under the License is distributed on an "AS IS" BASIS, * - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * - * implied. * - * See the License for the specific language governing permissions and * - * limitations under the License. * - ************************************************************************** - * * - * The most recent version of this package is available at github. * - * * - *************************************************************************/ - -/* -// Source: ac_channel.h -// Description: templatized channel communication class -// Author: Andres Takach, Ph.D. -*/ - -#ifndef __AC_CHANNEL_H -#define __AC_CHANNEL_H - -#ifndef __cplusplus -# error C++ is required to include this header file -#endif - -#include -#include - -#if !defined(AC_USER_DEFINED_ASSERT) && !defined(AC_ASSERT_THROW_EXCEPTION) -# include -#endif - -// not directly used by this include -#include -#include - -// Macro Definitions (obsolete - provided here for backward compatibility) -#define AC_CHAN_CTOR(varname) varname -#define AC_CHAN_CTOR_INIT(varname,init) varname(init) -#define AC_CHAN_CTOR_VAL(varname,init,val) varname(init,val) - -//////////////////////////////////////////////// -// Struct: ac_exception / ac_channel_exception -//////////////////////////////////////////////// - -#ifndef __INCLUDED_AC_EXCEPTION -# define __INCLUDED_AC_EXCEPTION -struct ac_exception { - const char *const file; - const unsigned int line; - const int code; - const char *const msg; - ac_exception(const char *file_, const unsigned int &line_, const int &code_, const char *msg_) - : file(file_), line(line_), code(code_), msg(msg_) { } -}; -#endif - -struct ac_channel_exception { - enum { code_begin = 1024 }; - enum code { - read_from_empty_channel = code_begin, - fifo_not_empty_when_reset, - no_operator_sb_defined_for_channel_type, - no_insert_defined_for_channel_type, - no_size_in_connections, - no_num_free_in_connections, - no_output_empty_in_connections - }; - static inline const char *msg(const code &code_) { - static const char *const s[] = { - "Read from empty channel", - "fifo not empty when reset", - "No operator[] defined for channel type", - "No insert defined for channel type", - "Connections does not support size()", - "Connections does not support num_free()", - "Connections::Out does not support empty()" - }; - return s[code_-code_begin]; - } -}; - -/////////////////////////////////////////// -// Class: ac_channel -////////////////////////////////////////// - -template -class ac_channel { -public: - typedef T element_type; - - // constructors - ac_channel(); - ac_channel(int init); - ac_channel(int init, T val); - - T read() { return chan.read(); } - void read(T& t) { t = read(); } - bool nb_read(T& t) { return chan.nb_read(t); } - - void write(const T& t) { chan.write(t); } - bool nb_write(T& t) { - chan.incr_size_call_count(); - return chan.nb_write(t); - } - - unsigned int size() { - chan.incr_size_call_count(); - return chan.size(); - } - bool empty() { return chan.empty(); } - - // Return true if channel has at least k entries - bool available(unsigned int k) const { return chan.available(k); } - - void reset() { chan.reset(); } - - unsigned int debug_size() const { return chan.size(); } - - const T &operator[](unsigned int pos) const { return chan[pos]; } - - int get_size_call_count() { return chan.get_size_call_count(); } - -#ifdef SYSTEMC_INCLUDED - void bind(sc_fifo_in &f) { chan.bind(f); } - void bind(sc_fifo_out &f) { chan.bind(f); } -#endif - -#ifdef __CONNECTIONS__CONNECTIONS_H__ - void bind(Connections::Out& c) { chan.bind(c); } - void bind(Connections::In& c) { chan.bind(c); } - void bind(Connections::SyncIn &c) { chan.bind(c); } - void bind(Connections::SyncOut &c) { chan.bind(c); } -#endif - -private: -# ifndef AC_CHANNEL_ASSERT -# define AC_CHANNEL_ASSERT(cond, code) ac_assert(cond, __FILE__, __LINE__, code) - static inline void ac_assert(bool condition, const char *file, int line, const ac_channel_exception::code &code) { -# ifndef AC_USER_DEFINED_ASSERT - if(!condition) { - const ac_exception e(file, line, code, ac_channel_exception::msg(code)); -# ifdef AC_ASSERT_THROW_EXCEPTION -# ifdef AC_ASSERT_THROW_EXCEPTION_AS_CONST_CHAR - throw(e.msg); -# else - throw(e); -# endif -# else - std::cerr << "Assert"; - if(e.file) - std::cerr << " in file " << e.file << ":" << e.line; - std::cerr << " " << e.msg << std::endl; - assert(0); -# endif - } -# else - AC_USER_DEFINED_ASSERT(condition, file, line, ac_channel_exception::msg(code)); -# endif - } -# else -# error "private use only - AC_CHANNEL_ASSERT macro already defined" -# endif - -public: - class fifo { - enum fifo_type { - fifo_ac_channel_type, - fifo_sc_fifo_type, - fifo_connections_type, - fifo_connections_sync_type - }; - - struct fifo_abstract { - virtual ~fifo_abstract() {} - virtual fifo_type get_fifo_type() const = 0; - virtual T read() = 0; - virtual bool nb_read(T& t) = 0; - virtual void write(const T& t) = 0; - virtual bool nb_write(T& t) = 0; - virtual bool empty() = 0; - virtual bool available(unsigned int k) const = 0; - virtual unsigned int size() const = 0; - virtual unsigned int num_free() const = 0; - virtual void reset() = 0; - virtual const T &operator_sb(const unsigned int &pos, const T &default_value) const = 0; - }; - - struct fifo_ac_channel : fifo_abstract { - std::deque ch; - - ~fifo_ac_channel() {} - - static inline fifo_type ftype() { return fifo_ac_channel_type; } - - fifo_type get_fifo_type() const { return ftype(); } - - T read() { - { - // If you hit this assert you attempted a read on an empty channel. Perhaps - // you need to guard the execution of the read with a call to the available() - // function: - // if (myInputChan.available(2)) { - // // it is safe to read two values - // cout << myInputChan.read(); - // cout << myInputChan.read(); - // } - AC_CHANNEL_ASSERT(!empty(), ac_channel_exception::read_from_empty_channel); - } - T t = ch.front(); - ch.pop_front(); - return t; - } - bool nb_read(T& t) { return empty() ? false : (t = read(), true); } - - void write(const T& t) { ch.push_back(t); } - bool nb_write(T& t) { return !num_free() ? false : (write(t), true); } - - bool empty() { return size() == 0; } - bool available(unsigned int k) const { return size() >= k; } - unsigned int size() const { return (int)ch.size(); } - unsigned int num_free() const { return ch.max_size() - ch.size(); } - - void reset() { ch.clear(); } - - const T &operator_sb(const unsigned int &pos, const T &) const { - return ch[pos]; - } - }; - -#ifdef SYSTEMC_INCLUDED - struct fifo_sc_fifo : fifo_abstract { - sc_fifo_in *fifo_in; - sc_fifo_out *fifo_out; - - ~fifo_sc_fifo() {} - - static inline fifo_type ftype() { return fifo_sc_fifo_type; } - - fifo_type get_fifo_type() const { return ftype(); } - - T read() { return fifo_in->read(); } - bool nb_read(T& t) { return empty() ? false : (t = read(), true); } - - void write(const T& t) { fifo_out->write(t); } - bool nb_write(T& t) { return !num_free() ? false : (write(t), true); } - - bool empty() { return size() == 0; } - bool available(unsigned int k) const { return size() >= k; } - unsigned int size() const { return fifo_in->num_available(); } - unsigned int num_free() const { return fifo_out->num_free(); } - - void reset() { - AC_CHANNEL_ASSERT(empty(), ac_channel_exception::fifo_not_empty_when_reset); - } - - const T &operator_sb(const unsigned int &, const T &default_value) const { - AC_CHANNEL_ASSERT(0, ac_channel_exception::no_operator_sb_defined_for_channel_type); - return default_value; - } - }; -public: - void bind(sc_fifo_in &f) { get_fifo().fifo_in = &f; } - void bind(sc_fifo_out &f) { get_fifo().fifo_out = &f; } -private: -#endif - -#ifdef __CONNECTIONS__CONNECTIONS_H__ - struct fifo_connections : fifo_abstract { - Connections::In *fifo_in; - Connections::Out *fifo_out; - - ~fifo_connections() {} - static inline fifo_type ftype() { return fifo_connections_type; } - fifo_type get_fifo_type() const { return ftype(); } - - T read() { return fifo_in->Pop(); } - bool nb_read(T& t) { return fifo_in->PopNB(t); } - - void write(const T& t) { fifo_out->Push(t); } - bool nb_write(T& t) { return fifo_out->PushNB(t); } - - bool empty() { - if (fifo_in) - return fifo_in->Empty(); - else - AC_CHANNEL_ASSERT(0, ac_channel_exception::no_output_empty_in_connections); - return false; - } - bool available(unsigned int k) const { return true; } - unsigned int size() const { - AC_CHANNEL_ASSERT(0, ac_channel_exception::no_size_in_connections); - return 0; - } - unsigned int num_free() const { - AC_CHANNEL_ASSERT(0, ac_channel_exception::no_num_free_in_connections); - return 0; - } - - void reset() { - AC_CHANNEL_ASSERT(empty(), ac_channel_exception::fifo_not_empty_when_reset); - } - - const T &operator_sb(const unsigned int &, const T &default_value) const { - AC_CHANNEL_ASSERT(0, ac_channel_exception::no_operator_sb_defined_for_channel_type); - return default_value; - } - }; - - struct fifo_connections_sync : fifo_abstract { - Connections::SyncIn *sync_in; - Connections::SyncOut *sync_out; - - ~fifo_connections_sync() {} - static inline fifo_type ftype() { return fifo_connections_sync_type; } - fifo_type get_fifo_type() const { return ftype(); } - - bool read() { sync_in->sync_in(); return true; } - bool nb_read(T& t) { t=true; return(sync_in->nb_sync_in()); } - - void write(const T& t) { sync_out->sync_out(); } - bool nb_write(T& t) { sync_out->sync_out(); return true; } - - bool empty() { - AC_CHANNEL_ASSERT(0, ac_channel_exception::no_output_empty_in_connections); - return(false); - } - bool available(unsigned int k) const { return true; } - unsigned int size() const { - AC_CHANNEL_ASSERT(0, ac_channel_exception::no_size_in_connections); - return 0; - } - unsigned int num_free() const { - AC_CHANNEL_ASSERT(0, ac_channel_exception::no_num_free_in_connections); - return 0; - } - void reset() { - if (sync_in) sync_in->reset_sync_in(); - if (sync_out) sync_out->reset_sync_out(); - } - const T &operator_sb(const unsigned int &, const T &default_value) const { - AC_CHANNEL_ASSERT(0, ac_channel_exception::no_operator_sb_defined_for_channel_type); - return default_value; - } - }; - - - public: - void bind(Connections::In& c) { get_fifo().fifo_in = &c; } - void bind(Connections::Out& c) { get_fifo().fifo_out = &c; } - - void bind(Connections::SyncIn &c) { get_fifo().sync_in = &c; } - void bind(Connections::SyncOut &c) { get_fifo().sync_out = &c; } - - private: -#endif - - template - fifo_T &get_fifo() { - if (!f || f->get_fifo_type() != fifo_T::ftype()) { - if (f) { - AC_CHANNEL_ASSERT(f->empty(), ac_channel_exception::fifo_not_empty_when_reset); - delete f; - } - f = new fifo_T; - } - return static_cast(*f); - } - - fifo_abstract *f; - unsigned int rSz; // reset size - T rVal; // resetValue - int size_call_count; - - public: - fifo() : f(0), rSz(0), size_call_count(0) { get_fifo(); } - fifo(int init) : f(0), rSz(init), size_call_count(0) { get_fifo(); } - fifo(int init, T val) : f(0), rSz(init), rVal(val), size_call_count(0) { get_fifo(); } - ~fifo() { delete f; } - - inline T read() { return f->read(); } - inline bool nb_read(T& t) { return f->nb_read(t); } - - inline void write(const T& t) { f->write(t); } - inline bool nb_write(T& t) { return f->nb_write(t); } - - inline bool empty() { return f->empty(); } - inline bool available(unsigned int k) const { return f->available(k); } - inline unsigned int size() const { return f->size(); } - inline unsigned int num_free() const { return f->num_free(); } - - inline void reset() { - f->reset(); - for (int i=0; i<(int)rSz; i++) - write(rVal); - } - - inline const T &operator[](unsigned int pos) const { return f->operator_sb(pos, rVal); } - - void incr_size_call_count() { ++size_call_count; } - int get_size_call_count() { - int tmp=size_call_count; - size_call_count=0; - return tmp; - } - - // obsolete - provided here for backward compatibility with ac_channel - struct iterator { - iterator operator+(unsigned int pos_) const { - return iterator(itr, pos_); - } - private: - friend class fifo; - iterator(const typename std::deque::iterator &itr_, unsigned int pos=0) - : itr(itr_) { if (pos) itr += pos; } - typename std::deque::iterator itr; - }; - iterator begin() { - AC_CHANNEL_ASSERT(f->get_fifo_type() == fifo_ac_channel_type, ac_channel_exception::no_insert_defined_for_channel_type); - return iterator(get_fifo().ch.begin()); - } - void insert(iterator itr, const T& t) { - AC_CHANNEL_ASSERT(f->get_fifo_type() == fifo_ac_channel_type, ac_channel_exception::no_insert_defined_for_channel_type); - get_fifo().ch.insert(itr.itr,t); - } - }; - fifo chan; - -private: - // Prevent the compiler from autogenerating these. - // (This enforces that channels are always passed by reference.) - ac_channel(const ac_channel< T >&); - ac_channel& operator=(const ac_channel< T >&); -}; - -template -ac_channel::ac_channel() : chan() {} - -template -ac_channel::ac_channel(int init) : chan(init) -{ - for (int i=init; i>0; i--) { - T dc; - write(dc); - } -} - -template -ac_channel::ac_channel(int init, T val) : chan(init, val) -{ - for (int i=init; i>0; i--) - write(val); -} - -template -inline std::ostream& operator<< (std::ostream& os, ac_channel &a) -{ - for (unsigned int i=0; i 0) os << " "; - os << a[i]; - } - return os; -} - -// This general case is meant to cover non channel (or array of them) args -// Its result will be ignored -template -bool nb_read_chan_rdy(T &x) { return true; } - -template -bool nb_read_chan_rdy(ac_channel &chan) { return !chan.empty(); } - -template -bool nb_read_chan_rdy(ac_channel (&chan)[N] ) { - bool r = true; - for(int i=0; i 199711L -template -bool nb_read_chan_rdy(Args&... args) { - const int n_args = sizeof...(args); - // only every other arg is a channel (or an array of channels) - bool rdy[n_args] = { (nb_read_chan_rdy(args))... }; - bool r = true; - for(int i=0; i < n_args; i+=2) - r &= rdy[i]; - return r; -} -#endif - -template -void nb_read_r(ac_channel &chan, T &var) { - chan.nb_read(var); -} - -template -void nb_read_r(ac_channel (&chan)[N], T (&var)[N]) { - for(int i=0; i 199711L -template -void nb_read_r(ac_channel &chan, T &var, Args&... args) { - chan.nb_read(var); - nb_read_r(args...); -} - -template -void nb_read_r(ac_channel (&chan)[N], T (&var)[N], Args&... args) { - for(int i=0; i -bool nb_read_join(Args&... args) { - if(nb_read_chan_rdy(args...)) { - nb_read_r(args...); - return true; - } - return false; -} -#endif - -/* undo macro adjustments */ -#ifdef AC_CHANNEL_ASSERT -# undef AC_CHANNEL_ASSERT -#endif - -#endif diff --git a/hls4ml/templates/quartus/ac_types/ac_complex.h b/hls4ml/templates/quartus/ac_types/ac_complex.h deleted file mode 100644 index 56821a053d..0000000000 --- a/hls4ml/templates/quartus/ac_types/ac_complex.h +++ /dev/null @@ -1,445 +0,0 @@ -/************************************************************************** - * * - * Algorithmic C (tm) Datatypes * - * * - * Software Version: 4.0 * - * * - * Release Date : Sat Jun 13 12:35:18 PDT 2020 * - * Release Type : Production Release * - * Release Build : 4.0.0 * - * * - * Copyright 2008-2019, Mentor Graphics Corporation, * - * * - * All Rights Reserved. * - * * - ************************************************************************** - * Licensed under the Apache License, Version 2.0 (the "License"); * - * you may not use this file except in compliance with the License. * - * You may obtain a copy of the License at * - * * - * http://www.apache.org/licenses/LICENSE-2.0 * - * * - * Unless required by applicable law or agreed to in writing, software * - * distributed under the License is distributed on an "AS IS" BASIS, * - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * - * implied. * - * See the License for the specific language governing permissions and * - * limitations under the License. * - ************************************************************************** - * * - * The most recent version of this package is available at github. * - * * - *************************************************************************/ - -/* -// Source: ac_complex.h -// Description: complex type with parameterized type that can be: -// - C integer types -// - C floating point types -// - ac_int -// - ac_fixed -// - ac_float -// ac_complex based on C integers, ac_int, ac_fixed and ac_float can -// be mixed -// Author: Andres Takach, Ph.D. -*/ - -#ifndef __AC_COMPLEX_H -#define __AC_COMPLEX_H - -#include - -#ifdef __AC_NAMESPACE -namespace __AC_NAMESPACE { -#endif - -template class ac_complex; - -namespace ac_private { - // specializations after definition of ac_complex - template - struct rt_ac_complex_T { - template - struct op1 { - typedef typename T::template rt_T< ac_complex >::mult mult; - typedef typename T::template rt_T< ac_complex >::plus plus; - typedef typename T::template rt_T< ac_complex >::minus2 minus; - typedef typename T::template rt_T< ac_complex >::minus minus2; - typedef typename T::template rt_T< ac_complex >::logic logic; - typedef typename T::template rt_T< ac_complex >::div2 div; - typedef typename T::template rt_T< ac_complex >::div div2; - }; - }; -} // namespace ac_private - -template -class ac_complex { -public: // temporary workaround - T _r; - T _i; - typedef typename ac_private::map::t map_T; - typedef typename map_T::rt_unary::mag_sqr T_sqr; - typedef typename ac_private::map::t map_T_sqr; - typedef typename ac_private::map::t map_T_mag; -public: - typedef T element_type; - template - struct rt_T { - typedef typename ac_private::map::t map_T2; - typedef typename ac_private::rt_ac_complex_T::template op1::mult mult; - typedef typename ac_private::rt_ac_complex_T::template op1::plus plus; - typedef typename ac_private::rt_ac_complex_T::template op1::minus minus; - typedef typename ac_private::rt_ac_complex_T::template op1::minus2 minus2; - typedef typename ac_private::rt_ac_complex_T::template op1::logic logic; - typedef typename ac_private::rt_ac_complex_T::template op1::div div; - typedef typename ac_private::rt_ac_complex_T::template op1::div2 div2; - typedef ac_complex arg1; - }; - - struct rt_unary { - typedef typename map_T_sqr::template rt_T::plus mag_sqr; - typedef typename map_T_mag::template rt_T::plus mag; // overly conservative for signed - typedef ac_complex neg; - template - struct set { - typedef ac_complex::sum> sum; - }; - }; - - ac_complex() { } - template - ac_complex(const ac_complex &c) : _r(c.r()), _i(c.i()) {} - template - ac_complex(const T2 &r) : _r(r), _i(0) {} - template - ac_complex(const T2 &r, const T3 &i) : _r(r), _i(i) {} - const T &r() const { return _r; } - const T &i() const { return _i; } - T &r() { return _r; } - T &i() { return _i; } - const T &real() const { return _r; } - const T &imag() const { return _i; } - T &real() { return _r; } - T &imag() { return _i; } - template - void set_r(const T2 &r) { _r = r;} - template - void set_i(const T2 &i) { _i = i;} - - // const binary operators are global rather than members because of compiler errors due to ambiguity - // (would appear as a compiler bug) - - template - ac_complex &operator +=(const ac_complex &op2) { - _r += op2.r(); - _i += op2.i(); - return *this; - } - - template - ac_complex &operator +=(const T2 &op2) { - _r += op2; - return *this; - } - - template - ac_complex &operator -=(const ac_complex &op2) { - _r -= op2.r(); - _i -= op2.i(); - return *this; - } - - template - ac_complex &operator -=(const T2 &op2) { - _r -= op2; - return *this; - } - - template - ac_complex &operator *=(const ac_complex &op2) { - T r0 = _r*op2.r() - _i*op2.i(); - _i = _r*op2.i() + _i*op2.r(); - _r = r0; - return *this; - } - - template - ac_complex &operator *=(const T2 &op2) { - _r = _r*op2; - _i = _i*op2; - return *this; - } - - template - ac_complex &operator /=(const ac_complex &op2) { - typename ac_complex::rt_unary::mag_sqr d = op2.mag_sqr(); - T r0 = (_r*op2.r() + _i*op2.i())/d; - _i = (_i*op2.r() - _r*op2.i())/d; - _r = r0; - return *this; - } - - template - ac_complex &operator /=(const T2 &op2) { - _r = _r/op2; - _i = _i/op2; - return *this; - } - - // Arithmetic Unary -------------------------------------------------------- - ac_complex operator +() { - return *this; - } - typename rt_unary::neg operator -() const { - typename rt_unary::neg res(-_r, -_i); - return res; - } - - // ! ------------------------------------------------------------------------ - bool operator ! () const { - return !_r && !_i; - } - - typename rt_unary::neg conj() const { - typename rt_unary::neg res(_r, -_i); - return res; - } - - typename rt_unary::mag_sqr mag_sqr() const { - return _r*_r + _i*_i; - } - - ac_complex< ac_int<2,true> > sign_conj() const { - return ac_complex< ac_int<2,true> >( - _r ? (_r < 0 ? -1 : 1) : 0, - _i ? (_i < 0 ? 1 : -1) : 0 - ); - } - - inline static std::string type_name() { - typedef typename ac_private::map::t map_T; - std::string r = "ac_complex<"; - r += map_T::type_name(); - r += '>'; - return r; - } - -}; - -namespace ac_private { - // with T2 == ac_complex - template - struct rt_ac_complex_T< ac_complex > { - template - struct op1 { - typedef ac_complex::plus> plus; - typedef ac_complex::minus> minus; - typedef ac_complex::minus2> minus2; - typedef ac_complex::logic> logic; - typedef ac_complex::div> div; - typedef ac_complex::div2> div2; - typedef ac_complex::mult, typename ac::rt_2T::mult>::plus, - typename ac::rt_2T::mult, typename ac::rt_2T::mult>::minus - >::logic> mult; - }; - }; - // with T2 == ac_float - template< AC_FL_T0(2) > - struct rt_ac_complex_T< AC_FL0(2) > { - typedef AC_FL0(2) T2; - template - struct op1 { - typedef ac_complex::plus> plus; - typedef ac_complex::minus> minus; - typedef ac_complex::minus2> minus2; - typedef ac_complex::logic> logic; - typedef ac_complex::div> div; - typedef ac_complex::div2> div2; - typedef ac_complex::mult> mult; - }; - }; - // with T2 == ac_fixed - template - struct rt_ac_complex_T< ac_fixed > { - typedef ac_fixed T2; - template - struct op1 { - typedef ac_complex::plus> plus; - typedef ac_complex::minus> minus; - typedef ac_complex::minus2> minus2; - typedef ac_complex::logic> logic; - typedef ac_complex::div> div; - typedef ac_complex::div2> div2; - typedef ac_complex::mult> mult; - }; - }; - // with T2 == ac_int - template - struct rt_ac_complex_T< ac_int > { - typedef ac_int T2; - template - struct op1 { - typedef ac_complex::plus> plus; - typedef ac_complex::minus> minus; - typedef ac_complex::minus2> minus2; - typedef ac_complex::logic> logic; - typedef ac_complex::div> div; - typedef ac_complex::div2> div2; - typedef ac_complex::mult> mult; - }; - }; - // with T2 == c_type - template - struct rt_ac_complex_T< c_type > { - typedef c_type T2; - template - struct op1 { - typedef ac_complex::plus> plus; - typedef ac_complex::minus> minus; - typedef ac_complex::minus2> minus2; - typedef ac_complex::logic> logic; - typedef ac_complex::div> div; - typedef ac_complex::div2> div2; - typedef ac_complex::mult> mult; - }; - }; -} - -template -inline typename ac_complex::template rt_T >::plus operator +(const ac_complex &op, const ac_complex &op2) { - typename ac_complex::template rt_T >::plus res( op.r() + op2.r(), op.i() + op2.i() ); - return res; -} - -template -inline typename ac_complex::template rt_T::plus operator +(const T &op, const ac_complex &op2) { - typename ac_complex::template rt_T::plus res( op + op2.r(), op2.i() ); - return res; -} - -template -inline typename ac_complex::template rt_T::plus operator +(const ac_complex &op, const T2 &op2) { - typename ac_complex::template rt_T::plus res( op.r() + op2, op.i() ); - return res; -} - -template -inline typename ac_complex::template rt_T >::minus operator -(const ac_complex &op, const ac_complex &op2) { - typename ac_complex::template rt_T >::minus res( op.r() - op2.r(), op.i() - op2.i() ); - return res; -} - -template -inline typename ac_complex::template rt_T::minus2 operator -(const T &op, const ac_complex &op2) { - typename ac_complex::template rt_T::minus2 res( op - op2.r(), -op2.i() ); - return res; -} - -template -inline typename ac_complex::template rt_T::minus operator -(const ac_complex &op, const T2 &op2) { - typename ac_complex::template rt_T::minus res( op.r() - op2, op.i() ); - return res; -} - -template -inline typename ac_complex::template rt_T >::mult operator *(const ac_complex &op, const ac_complex &op2) { - typename ac_complex::template rt_T >::mult res( op.r()*op2.r() - op.i()*op2.i(), op.i()*op2.r() + op.r()*op2.i() ); - return res; -} - -template -inline typename ac_complex::template rt_T::mult operator *(const T &op, const ac_complex &op2) { - typename ac_complex::template rt_T::mult res( op*op2.r(), op*op2.i()); - return res; -} - -template -inline typename ac_complex::template rt_T::mult operator *(const ac_complex &op, const T2 &op2) { - typename ac_complex::template rt_T::mult res( op.r()*op2, op.i()*op2 ); - return res; -} - -template -inline typename ac_complex::template rt_T >::div operator /(const ac_complex &op, const ac_complex &op2) { - typename ac_complex::rt_unary::mag_sqr d = op2.mag_sqr(); - typename ac_complex::template rt_T >::div res((op.r()*op2.r() + op.i()*op2.i())/d, (op.i()*op2.r() - op.r()*op2.i())/d); - return res; -} - -template -inline typename ac_complex::template rt_T::div operator /(const ac_complex &op, const T2 &op2) { - typename ac_complex::template rt_T::div res( op.r()/op2, op.i()/op2 ); - return res; -} - -template -inline typename ac_complex::template rt_T::div2 operator /(const T &op, const ac_complex &op2) { - typename ac_complex::rt_unary::mag_sqr d = op2.mag_sqr(); - typename ac_complex::template rt_T::div2 res(op*op2.r()/d, - op*op2.i()/d); - return res; -} - -template -inline bool operator == (const ac_complex &op, const ac_complex &op2) { - return op.r() == op2.r() && op.i() == op2.i(); -} - -template -inline bool operator == (const T &op, const ac_complex &op2) { - return op == op2.r() && op2.i() == 0; -} - -template -inline bool operator == (const ac_complex &op, const T2 &op2) { - return op.r() == op2 && op.i() == 0; -} - -template -inline bool operator != (const ac_complex &op, const ac_complex &op2) { - return op.r() != op2.r() || op.i() != op2.i(); -} - -template -inline bool operator != (const T &op, const ac_complex &op2) { - return op != op2.r() || op2.i() != 0; -} - -template -inline bool operator != (const ac_complex &op, const T2 &op2) { - return op.r() != op2 || op.i() != 0; -} - -// Stream -------------------------------------------------------------------- - -template -inline std::ostream& operator << (std::ostream &os, const ac_complex &x) { -#ifndef __SYNTHESIS__ - os << "(" << x.r() << ", " << x.i() << ")"; -#endif - return os; -} - -template -inline ac_complex value(ac_complex) { - T val = value((T) 0); - ac_complex r(val, val); - return r; -} - -namespace ac { - template - inline bool init_array(ac_complex *a, int n) { - T val = value((T) 0); - ac_complex t(val, val); - for(int i=0; i < n; i++) - a[i] = t; - return true; - } -} - -#ifdef __AC_NAMESPACE -} -#endif - -#endif // __AC_COMPLEX_H diff --git a/hls4ml/templates/quartus/ac_types/ac_fixed.h b/hls4ml/templates/quartus/ac_types/ac_fixed.h deleted file mode 100644 index cb95db8d16..0000000000 --- a/hls4ml/templates/quartus/ac_types/ac_fixed.h +++ /dev/null @@ -1,1546 +0,0 @@ -/************************************************************************** - * * - * Algorithmic C (tm) Datatypes * - * * - * Software Version: 4.0 * - * * - * Release Date : Sat Jun 13 12:35:18 PDT 2020 * - * Release Type : Production Release * - * Release Build : 4.0.0 * - * * - * Copyright 2005-2020, Mentor Graphics Corporation, * - * * - * All Rights Reserved. * - * * - ************************************************************************** - * Licensed under the Apache License, Version 2.0 (the "License"); * - * you may not use this file except in compliance with the License. * - * You may obtain a copy of the License at * - * * - * http://www.apache.org/licenses/LICENSE-2.0 * - * * - * Unless required by applicable law or agreed to in writing, software * - * distributed under the License is distributed on an "AS IS" BASIS, * - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * - * implied. * - * See the License for the specific language governing permissions and * - * limitations under the License. * - ************************************************************************** - * * - * The most recent version of this package is available at github. * - * * - *************************************************************************/ - -/* -// Source: ac_fixed.h -// Description: class for fixed point operation handling in C++ -// Author: Andres Takach, Ph.D. -*/ - -#ifndef __AC_FIXED_H -#define __AC_FIXED_H - -#include "ac_int.h" - -#if (defined(__GNUC__) && __GNUC__ < 3 && !defined(__EDG__)) -#error GCC version 3 or greater is required to include this header file -#endif - -#if (defined(_MSC_VER) && _MSC_VER < 1400 && !defined(__EDG__)) -#error Microsoft Visual Studio 8 or newer is required to include this header file -#endif - -#if (defined(_MSC_VER) && !defined(__EDG__)) -#pragma warning( push ) -#pragma warning( disable: 4127 4308 4365 4514 4800 ) -#endif - -#ifndef __SYNTHESIS__ -#ifndef __AC_FIXED_UTILITY_BASE -#define __AC_FIXED_UTILITY_BASE -#endif - -#endif - -#ifdef __SYNTHESIS__ -#ifdef __AC_FIXED_NUMERICAL_ANALYSIS_BASE -#undef __AC_FIXED_NUMERICAL_ANALYSIS_BASE -#endif -#endif - -#ifdef __AC_NAMESPACE -namespace __AC_NAMESPACE { -#endif - -namespace ac_private { - template - struct rt_ac_fixed_T { - template - struct op1 { - typedef typename T::template rt_T< ac_fixed >::mult mult; - typedef typename T::template rt_T< ac_fixed >::plus plus; - typedef typename T::template rt_T< ac_fixed >::minus2 minus; - typedef typename T::template rt_T< ac_fixed >::minus minus2; - typedef typename T::template rt_T< ac_fixed >::logic logic; - typedef typename T::template rt_T< ac_fixed >::div2 div; - typedef typename T::template rt_T< ac_fixed >::div div2; - }; - }; - // specializations after definition of ac_fixed -} - -namespace ac { - template - class basic_num_ovf_base { - bool d_enable; - public: - basic_num_ovf_base() : d_enable(true) {} - void enable_ovf(bool a) { d_enable = a; } - bool is_enabled() const { return d_enable; } - template - void update(bool overflow, bool neg, const basic_num_ovf_base &op2) { -#ifndef __AC_OVERRIDE_OVF_UPDATE_BODY - if(d_enable) { - if(overflow) { - std::cerr << (neg ? "-" : "+") << "OVF: "; - std::cerr << type_name() << " ( " << basic_num_ovf_base::type_name(); - std::cerr << " ( " << op2.value().to_double() << " ) )" << std::endl; - } - } -#else - __AC_OVERRIDE_OVF_UPDATE_BODY -#endif - } - void update(bool overflow, bool neg, double op2) { -#ifndef __AC_OVERRIDE_OVF_UPDATE2_BODY - if(d_enable) { - if(overflow) { - std::cerr << (neg ? "-" : "+") << "OVF: "; - std::cerr << type_name() << " ( " << "double"; - std::cerr << " ( " << op2 << " ) )" << std::endl; - } - } -#else - __AC_OVERRIDE_OVF_UPDATE2_BODY -#endif - } - const ac_fixed &value() const; - static std::string type_name(); - }; -} - -////////////////////////////////////////////////////////////////////////////// -// ac_fixed -////////////////////////////////////////////////////////////////////////////// - -//enum ac_q_mode { AC_TRN, AC_RND, AC_TRN_ZERO, AC_RND_ZERO, AC_RND_INF, AC_RND_MIN_INF, AC_RND_CONV, AC_RND_CONV_ODD }; -//enum ac_o_mode { AC_WRAP, AC_SAT, AC_SAT_ZERO, AC_SAT_SYM }; - -template -class ac_fixed : private ac_private::iv<(W+31+!S)/32> -#ifndef __SYNTHESIS__ -__AC_FIXED_UTILITY_BASE -#endif -#ifdef __AC_FIXED_NUMERICAL_ANALYSIS_BASE -, public __AC_FIXED_NUMERICAL_ANALYSIS_BASE -#endif -{ -#if defined(__SYNTHESIS__) && !defined(AC_IGNORE_BUILTINS) -#pragma builtin -#endif - - enum {N=(W+31+!S)/32}; - - template - struct rt_priv { - enum {w_shiftl = AC_MAX(W+W2,1) }; - typedef ac_fixed shiftl; - }; - - typedef ac_private::iv Base; - - inline void bit_adjust() { - const unsigned rem = (32-W)&31; - Base::v[N-1] = S ? ((signed) ((unsigned)Base::v[N-1] << rem) >> rem) : (rem ? - ((unsigned) Base::v[N-1] << rem) >> rem : 0); - } - inline Base &base() { return *this; } - inline const Base &base() const { return *this; } - - inline void overflow_adjust(bool overflow, bool neg) { - if(O==AC_WRAP) { - bit_adjust(); - return; - } - else if(O==AC_SAT_ZERO) { - if(overflow) - ac_private::iv_extend(Base::v, 0); - else - bit_adjust(); - } - else if(S) { - if(overflow) { - if(!neg) { - ac_private::iv_extend(Base::v, ~0); - Base::v[N-1] = ~((unsigned)~0 << ((W-1)&31)); - } else { - ac_private::iv_extend(Base::v, 0); - Base::v[N-1] = ((unsigned)~0 << ((W-1)&31)); - if(O==AC_SAT_SYM) - Base::v[0] |= 1; - } - } else - bit_adjust(); - } - else { - if(overflow) { - if(!neg) { - ac_private::iv_extend(Base::v, ~0); - Base::v[N-1] = ~((unsigned)~0 << (W&31)); - } else - ac_private::iv_extend(Base::v, 0); - } else - bit_adjust(); - } - } - - inline bool quantization_adjust(bool qb, bool r, bool s) { - if(Q==AC_TRN) - return false; - if(Q==AC_RND_ZERO) - qb &= s || r; - else if(Q==AC_RND_MIN_INF) - qb &= r; - else if(Q==AC_RND_INF) - qb &= !s || r; - else if(Q==AC_RND_CONV) - qb &= (Base::v[0] & 1) || r; - else if(Q==AC_RND_CONV_ODD) - qb &= (!(Base::v[0] & 1)) || r; - else if(Q==AC_TRN_ZERO) - qb = s && ( qb || r ); - return ac_private::iv_uadd_carry(Base::v, qb, Base::v); - } - - inline bool is_neg() const { return S && Base::v[N-1] < 0; } - -public: - static const int width = W; - static const int i_width = I; - static const bool sign = S; - static const ac_o_mode o_mode = O; - static const ac_q_mode q_mode = Q; - static const int e_width = 0; -#ifdef __AC_FIXED_NUMERICAL_ANALYSIS_BASE - static const bool compute_overflow_for_wrap = true; -#else - static const bool compute_overflow_for_wrap = false; -#endif - - template - struct rt { - enum { - F=W-I, - F2=W2-I2, - mult_w = W+W2, - mult_i = I+I2, - mult_s = S||S2, - plus_w = AC_MAX(I+(S2&&!S),I2+(S&&!S2))+1+AC_MAX(F,F2), - plus_i = AC_MAX(I+(S2&&!S),I2+(S&&!S2))+1, - plus_s = S||S2, - minus_w = AC_MAX(I+(S2&&!S),I2+(S&&!S2))+1+AC_MAX(F,F2), - minus_i = AC_MAX(I+(S2&&!S),I2+(S&&!S2))+1, - minus_s = true, - div_w = W+AC_MAX(W2-I2,0)+S2, - div_i = I+(W2-I2)+S2, - div_s = S||S2, - logic_w = AC_MAX(I+(S2&&!S),I2+(S&&!S2))+AC_MAX(F,F2), - logic_i = AC_MAX(I+(S2&&!S),I2+(S&&!S2)), - logic_s = S||S2 - }; - typedef ac_fixed mult; - typedef ac_fixed plus; - typedef ac_fixed minus; - typedef ac_fixed logic; - typedef ac_fixed div; - typedef ac_fixed arg1; - }; - - template - struct rt_T { - typedef typename ac_private::map::t map_T; - typedef typename ac_private::rt_ac_fixed_T::template op1::mult mult; - typedef typename ac_private::rt_ac_fixed_T::template op1::plus plus; - typedef typename ac_private::rt_ac_fixed_T::template op1::minus minus; - typedef typename ac_private::rt_ac_fixed_T::template op1::minus2 minus2; - typedef typename ac_private::rt_ac_fixed_T::template op1::logic logic; - typedef typename ac_private::rt_ac_fixed_T::template op1::div div; - typedef typename ac_private::rt_ac_fixed_T::template op1::div2 div2; - typedef ac_fixed arg1; - }; - - struct rt_unary { - enum { - neg_w = W+1, - neg_i = I+1, - neg_s = true, - mag_sqr_w = 2*W-S, - mag_sqr_i = 2*I-S, - mag_sqr_s = false, - mag_w = W+S, - mag_i = I+S, - mag_s = false, - leading_sign_w = ac::log2_ceil::val, - leading_sign_s = false - }; - typedef ac_int leading_sign; - typedef ac_fixed neg; - typedef ac_fixed mag_sqr; - typedef ac_fixed mag; - template - struct set { - enum { sum_w = W + ac::log2_ceil::val, sum_i = (sum_w-W) + I, sum_s = S}; - typedef ac_fixed sum; - }; - }; - - ac_fixed(const ac_fixed &op): Base(op) { } - - template friend class ac_fixed; - ac_fixed() { -#if !defined(__SYNTHESIS__) && defined(AC_DEFAULT_IN_RANGE) - bit_adjust(); - if( O==AC_SAT_SYM && S && Base::v[N-1] < 0 && (W > 1 ? ac_private::iv_equal_zeros_to(Base::v) : true) ) - Base::v[0] |= 1; -#endif - } - template - inline ac_fixed (const ac_fixed &op) { - enum {N2=(W2+31+!S2)/32, F=W-I, F2=W2-I2, QUAN_INC = F2>F && !(Q==AC_TRN || (Q==AC_TRN_ZERO && !S2)) }; - bool carry = false; - // handle quantization - if(F2 == F) - Base::operator =(op); - else if(F2 > F) { - op.template const_shift_r(*this); -// ac_private::iv_const_shift_r(op.v, Base::v); - if(Q!=AC_TRN && !(Q==AC_TRN_ZERO && !S2)) { - bool qb = (F2-F > W2) ? (op.v[N2-1] < 0) : (bool) op[F2-F-1]; - bool r = (F2 > F+1) ? !ac_private::iv_equal_zeros_to(op.v) : false; - carry = quantization_adjust(qb, r, S2 && op.v[N2-1] < 0); - } - } - else // no quantization - op.template const_shift_l(*this); -// ac_private::iv_const_shift_l(op.v, Base::v); - // handle overflow - if((O!=AC_WRAP || compute_overflow_for_wrap) - && ((!S && S2) || I-S < I2-S2+(QUAN_INC || (S2 && O==AC_SAT_SYM && (O2 != AC_SAT_SYM || F2 > F) ))) - ) { // saturation - bool deleted_bits_zero = !(W&31)&S || !(Base::v[N-1] >> (W&31)); - bool deleted_bits_one = !(W&31)&S || !~(Base::v[N-1] >> (W&31)); - bool neg_src; - if(F2-F+32*N < W2) { - bool all_ones = ac_private::iv_equal_ones_from(op.v); - deleted_bits_zero = deleted_bits_zero && (carry ? all_ones : ac_private::iv_equal_zeros_from(op.v)); - deleted_bits_one = deleted_bits_one && (carry ? ac_private::iv_equal_ones_from<1+F2-F+32*N,N2>(op.v) && !op[F2-F+32*N] : all_ones); - neg_src = S2 && op.v[N2-1] < 0 && !(carry & all_ones); - } - else - neg_src = S2 && op.v[N2-1] < 0 && Base::v[N-1] < 0; - bool neg_trg = S && (bool) this->operator[](W-1); - bool overflow = !neg_src && (neg_trg || !deleted_bits_zero); - overflow |= neg_src && (!neg_trg || !deleted_bits_one); - if(O==AC_SAT_SYM && S && S2) - overflow |= neg_src && (W > 1 ? ac_private::iv_equal_zeros_to(Base::v) : true); - overflow_adjust(overflow, neg_src); -#ifdef __AC_FIXED_NUMERICAL_ANALYSIS_BASE - __AC_FIXED_NUMERICAL_ANALYSIS_BASE::update(overflow,neg_src,op); -#endif - } - else - bit_adjust(); - } - - template - inline ac_fixed (const ac_int &op) { - ac_fixed f_op; - f_op.base().operator =(op); - *this = f_op; - } - - template - typename rt_priv::shiftl shiftl() const { - typedef typename rt_priv::shiftl shiftl_t; - shiftl_t r; - Base::template const_shift_l(r); - return r; - } - - inline ac_fixed( bool b ) { *this = (ac_int<1,false>) b; } - inline ac_fixed( char b ) { *this = (ac_int<8,true>) b; } - inline ac_fixed( signed char b ) { *this = (ac_int<8,true>) b; } - inline ac_fixed( unsigned char b ) { *this = (ac_int<8,false>) b; } - inline ac_fixed( signed short b ) { *this = (ac_int<16,true>) b; } - inline ac_fixed( unsigned short b ) { *this = (ac_int<16,false>) b; } - inline ac_fixed( signed int b ) { *this = (ac_int<32,true>) b; } - inline ac_fixed( unsigned int b ) { *this = (ac_int<32,false>) b; } - inline ac_fixed( signed long b ) { *this = (ac_int) b; } - inline ac_fixed( unsigned long b ) { *this = (ac_int) b; } - inline ac_fixed( Slong b ) { *this = (ac_int<64,true>) b; } - inline ac_fixed( Ulong b ) { *this = (ac_int<64,false>) b; } - - inline ac_fixed( double d ) { - double di = ac_private::ldexpr<-(I+!S+((32-W-!S)&31))>(d); - bool o, qb, r; - bool neg_src = d < 0; - Base::conv_from_fraction(di, &qb, &r, &o); - quantization_adjust(qb, r, neg_src); - // a neg number may become non neg (0) after quantization - neg_src &= o || Base::v[N-1] < 0; - - if(O!=AC_WRAP || compute_overflow_for_wrap) { // saturation - bool overflow; - bool neg_trg = S && (bool) this->operator[](W-1); - if(o) { - overflow = true; - } else { - bool deleted_bits_zero = !(W&31)&S || !(Base::v[N-1] >> (W&31)); - bool deleted_bits_one = !(W&31)&S || !~(Base::v[N-1] >> (W&31)); - overflow = !neg_src && (neg_trg || !deleted_bits_zero); - overflow |= neg_src && (!neg_trg || !deleted_bits_one); - } - if(O==AC_SAT_SYM && S) - overflow |= neg_src && (W > 1 ? ac_private::iv_equal_zeros_to(Base::v) : true); - overflow_adjust(overflow, neg_src); -#ifdef __AC_FIXED_NUMERICAL_ANALYSIS_BASE - __AC_FIXED_NUMERICAL_ANALYSIS_BASE::update(overflow,neg_src,d); -#endif - } else - bit_adjust(); - } - -#if (defined(_MSC_VER) && !defined(__EDG__)) -#pragma warning( push ) -#pragma warning( disable: 4700 ) -#endif -#if (defined(__GNUC__) && ( __GNUC__ == 4 && __GNUC_MINOR__ >= 6 || __GNUC__ > 4 ) && !defined(__EDG__)) -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wuninitialized" -#endif -#if defined(__clang__) -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wuninitialized" -#endif - template - inline ac_fixed &set_val() { - if(V == AC_VAL_DC) { - ac_fixed r; - Base::operator =(r); - bit_adjust(); - } - else if(V == AC_VAL_0 || V == AC_VAL_MIN || V == AC_VAL_QUANTUM) { - Base::operator =(0); - if(S && V == AC_VAL_MIN) { - const unsigned rem = (W-1)&31; - Base::v[N-1] = ((unsigned)~0 << rem); - if(O == AC_SAT_SYM) { - if(W == 1) - Base::v[0] = 0; - else - Base::v[0] |= 1; - } - } else if(V == AC_VAL_QUANTUM) - Base::v[0] = 1; - } - else { // AC_VAL_MAX - Base::operator =(-1); - const unsigned int rem = (32-W - (unsigned) !S )&31; - Base::v[N-1] = ((unsigned) (-1) >> 1) >> rem; - } - return *this; - } -#if (defined(_MSC_VER) && !defined(__EDG__)) -#pragma warning( pop ) -#endif -#if (defined(__GNUC__) && ( __GNUC__ == 4 && __GNUC_MINOR__ >= 6 || __GNUC__ > 4 ) && !defined(__EDG__)) -#pragma GCC diagnostic pop -#endif -#if defined(__clang__) -#pragma clang diagnostic pop -#endif - - // Explicit conversion functions to ac_int that captures all integer bits (bits are truncated) - inline ac_int to_ac_int() const { return ((ac_fixed) *this).template slc(0); } - - // Explicit conversion functions to C built-in types ------------- - inline int to_int() const { return ((I-W) >= 32) ? 0 : (signed int) to_ac_int(); } - inline unsigned to_uint() const { return ((I-W) >= 32) ? 0 : (unsigned int) to_ac_int(); } - inline long to_long() const { return ((I-W) >= ac_private::long_w) ? 0 : (signed long) to_ac_int(); } - inline unsigned long to_ulong() const { return ((I-W) >= ac_private::long_w) ? 0 : (unsigned long) to_ac_int(); } - inline Slong to_int64() const { return ((I-W) >= 64) ? 0 : (Slong) to_ac_int(); } - inline Ulong to_uint64() const { return ((I-W) >= 64) ? 0 : (Ulong) to_ac_int(); } - inline double to_double() const { return ac_private::ldexpr(Base::to_double()); } - - inline int length() const { return W; } - - inline std::string to_string(ac_base_mode base_rep, bool sign_mag = false) const { - // base_rep == AC_DEC => sign_mag == don't care (always print decimal in sign magnitude) - char r[(W-AC_MIN(AC_MIN(W-I,I),0)+31)/32*32+5] = {0}; - int i = 0; - if(sign_mag) - r[i++] = is_neg() ? '-' : '+'; - else if (base_rep == AC_DEC && is_neg()) - r[i++] = '-'; - if(base_rep != AC_DEC) { - r[i++] = '0'; - r[i++] = base_rep == AC_BIN ? 'b' : (base_rep == AC_OCT ? 'o' : 'x'); - } - ac_fixed t; - if( (base_rep == AC_DEC || sign_mag) && is_neg() ) - t = operator -(); - else - t = *this; - ac_fixed i_part = t; - ac_fixed f_part = t; - i += ac_private::to_string(i_part.v, AC_MAX(I+1,1), sign_mag, base_rep, false, r+i); - if(W-I > 0) { - r[i++] = '.'; - if(!ac_private::to_string(f_part.v, W-I, false, base_rep, true, r+i)) - r[--i] = 0; - } - if(!i) { - r[0] = '0'; - r[1] = 0; - } - return std::string(r); - } - inline static std::string type_name() { - const char *tf[] = {"false", "true" }; - const char *q[] = {"AC_TRN", "AC_RND", "AC_TRN_ZERO", "AC_RND_ZERO", "AC_RND_INF", "AC_RND_MIN_INF", "AC_RND_CONV", "AC_RND_CONV_ODD" }; - const char *o[] = {"AC_WRAP", "AC_SAT", "AC_SAT_ZERO", "AC_SAT_SYM" }; - std::string r = "ac_fixed<"; - r += ac_int<32,true>(W).to_string(AC_DEC) + ','; - r += ac_int<32,true>(I).to_string(AC_DEC) + ','; - r += tf[S]; - r += ','; - r += q[Q]; - r += ','; - r += o[O]; - r += '>'; - return r; - } - - // Arithmetic : Binary ---------------------------------------------------- - template - typename rt::mult operator *( const ac_fixed &op2) const { - typename rt::mult r; - Base::mult(op2, r); - return r; - } - template - typename rt::plus operator +( const ac_fixed &op2) const { - enum { F=W-I, F2=W2-I2 }; - typename rt::plus r; - if(F == F2) - Base::add(op2, r); - else if(F > F2) - Base::add(op2.template shiftl(), r); - else - shiftl().add(op2, r); - return r; - } - template - typename rt::minus operator -( const ac_fixed &op2) const { - enum { F=W-I, F2=W2-I2 }; - typename rt::minus r; - if(F == F2) - Base::sub(op2, r); - else if(F > F2) - Base::sub(op2.template shiftl(), r); - else - shiftl().sub(op2, r); - return r; - } -#if (defined(__GNUC__) && ( __GNUC__ == 4 && __GNUC_MINOR__ >= 6 || __GNUC__ > 4 ) && !defined(__EDG__)) -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wenum-compare" -#endif - template - typename rt::div operator /( const ac_fixed &op2) const { - typename rt::div r; - enum { Num_w = W+AC_MAX(W2-I2,0), Num_i = I, Num_w_minus = Num_w+S, Num_i_minus = Num_i+S, - N1 = ac_fixed::N, N1minus = ac_fixed::N, - N2 = ac_fixed::N, N2minus = ac_fixed::N, - num_s = S + (N1minus > N1), den_s = S2 + (N2minus > N2), Nr = rt::div::N }; - ac_fixed t = *this; - t.template div(op2, r); - return r; - } -#if (defined(__GNUC__) && ( __GNUC__ == 4 && __GNUC_MINOR__ >= 6 || __GNUC__ > 4 ) && !defined(__EDG__)) -#pragma GCC diagnostic pop -#endif - // Arithmetic assign ------------------------------------------------------ - template - ac_fixed &operator *=( const ac_fixed &op2) { - *this = this->operator *(op2); - return *this; - } - template - ac_fixed &operator +=( const ac_fixed &op2) { - *this = this->operator +(op2); - return *this; - } - template - ac_fixed &operator -=( const ac_fixed &op2) { - *this = this->operator -(op2); - return *this; - } - template - ac_fixed &operator /=( const ac_fixed &op2) { - *this = this->operator /(op2); - return *this; - } - // increment/decrement by quantum (smallest difference that can be represented) - // Arithmetic prefix increment, decrement --------------------------------- - ac_fixed &operator ++() { - ac_fixed<1,I-W+1,false> q; - q.template set_val(); - operator += (q); - return *this; - } - ac_fixed &operator --() { - ac_fixed<1,I-W+1,false> q; - q.template set_val(); - operator -= (q); - return *this; - } - // Arithmetic postfix increment, decrement --------------------------------- - const ac_fixed operator ++(int) { - ac_fixed t = *this; - ac_fixed<1,I-W+1,false> q; - q.template set_val(); - operator += (q); - return t; - } - const ac_fixed operator --(int) { - ac_fixed t = *this; - ac_fixed<1,I-W+1,false> q; - q.template set_val(); - operator -= (q); - return t; - } - // Arithmetic Unary -------------------------------------------------------- - ac_fixed operator +() { - return *this; - } - typename rt_unary::neg operator -() const { - typename rt_unary::neg r; - Base::neg(r); - r.bit_adjust(); - return r; - } - // ! ------------------------------------------------------------------------ - bool operator ! () const { - return Base::equal_zero(); - } - - // Bitwise (arithmetic) unary: complement ----------------------------- - ac_fixed operator ~() const { - ac_fixed r; - Base::bitwise_complement(r); - return r; - } - // Bitwise (not arithmetic) bit complement ----------------------------- - ac_fixed bit_complement() const { - ac_fixed r; - Base::bitwise_complement(r); - r.bit_adjust(); - return r; - } - // Bitwise (not arithmetic): and, or, xor ---------------------------------- - template - typename rt::logic operator &( const ac_fixed &op2) const { - enum { F=W-I, F2=W2-I2 }; - typename rt::logic r; - if(F == F2) - Base::bitwise_and(op2, r); - else if(F > F2) - Base::bitwise_and(op2.template shiftl(), r); - else - shiftl().bitwise_and(op2, r); - return r; - } - template - typename rt::logic operator |( const ac_fixed &op2) const { - enum { F=W-I, F2=W2-I2 }; - typename rt::logic r; - if(F == F2) - Base::bitwise_or(op2, r); - else if(F > F2) - Base::bitwise_or(op2.template shiftl(), r); - else - shiftl().bitwise_or(op2, r); - return r; - } - template - typename rt::logic operator ^( const ac_fixed &op2) const { - enum { F=W-I, F2=W2-I2 }; - typename rt::logic r; - if(F == F2) - Base::bitwise_xor(op2, r); - else if(F > F2) - Base::bitwise_xor(op2.template shiftl(), r); - else - shiftl().bitwise_xor(op2, r); - return r; - } - // Bitwise assign (not arithmetic): and, or, xor ---------------------------- - template - ac_fixed &operator &= ( const ac_fixed &op2 ) { - *this = this->operator &(op2); - return *this; - } - template - ac_fixed &operator |= ( const ac_fixed &op2 ) { - *this = this->operator |(op2); - return *this; - } - template - ac_fixed &operator ^= ( const ac_fixed &op2 ) { - *this = this->operator ^(op2); - return *this; - } - // Shift (result constrained by left operand) ------------------------------- - template - ac_fixed operator << ( const ac_int &op2 ) const { - // currently not written to overflow or quantize (neg shift) - ac_fixed r; - Base::shift_l2(op2.to_int(), r); - r.bit_adjust(); - return r; - } - template - ac_fixed operator << ( const ac_int &op2 ) const { - // currently not written to overflow - ac_fixed r; - Base::shift_l(op2.to_uint(), r); - r.bit_adjust(); - return r; - } - template - ac_fixed operator >> ( const ac_int &op2 ) const { - // currently not written to quantize or overflow (neg shift) - ac_fixed r; - Base::shift_r2(op2.to_int(), r); - r.bit_adjust(); - return r; - } - template - ac_fixed operator >> ( const ac_int &op2 ) const { - // currently not written to quantize - ac_fixed r; - Base::shift_r(op2.to_uint(), r); - r.bit_adjust(); - return r; - } - // Shift assign ------------------------------------------------------------ - template - ac_fixed operator <<= ( const ac_int &op2 ) { - // currently not written to overflow or quantize (neg shift) - Base r; - Base::shift_l2(op2.to_int(), r); - Base::operator=(r); - bit_adjust(); - return *this; - } - template - ac_fixed operator <<= ( const ac_int &op2 ) { - // currently not written to overflow - Base r; - Base::shift_l(op2.to_uint(), r); - Base::operator=(r); - bit_adjust(); - return *this; - } - template - ac_fixed operator >>= ( const ac_int &op2 ) { - // currently not written to quantize or overflow (neg shift) - Base r; - Base::shift_r2(op2.to_int(), r); - Base::operator=(r); - bit_adjust(); - return *this; - } - template - ac_fixed operator >>= ( const ac_int &op2 ) { - // currently not written to quantize - Base r; - Base::shift_r(op2.to_uint(), r); - Base::operator=(r); - bit_adjust(); - return *this; - } - // Relational --------------------------------------------------------------- - template - bool operator == ( const ac_fixed &op2) const { - enum { F=W-I, F2=W2-I2 }; - if(F == F2) - return Base::equal(op2); - else if(F > F2) - return Base::equal(op2.template shiftl()); - else - return shiftl().equal(op2); - } - template - bool operator != ( const ac_fixed &op2) const { - enum { F=W-I, F2=W2-I2 }; - if(F == F2) - return ! Base::equal(op2); - else if(F > F2) - return ! Base::equal(op2.template shiftl()); - else - return ! shiftl().equal(op2); - } - template - bool operator < ( const ac_fixed &op2) const { - enum { F=W-I, F2=W2-I2 }; - if(F == F2) - return Base::less_than(op2); - else if(F > F2) - return Base::less_than(op2.template shiftl()); - else - return shiftl().less_than(op2); - } - template - bool operator >= ( const ac_fixed &op2) const { - enum { F=W-I, F2=W2-I2 }; - if(F == F2) - return ! Base::less_than(op2); - else if(F > F2) - return ! Base::less_than(op2.template shiftl()); - else - return ! shiftl().less_than(op2); - } - template - bool operator > ( const ac_fixed &op2) const { - enum { F=W-I, F2=W2-I2 }; - if(F == F2) - return Base::greater_than(op2); - else if(F > F2) - return Base::greater_than(op2.template shiftl()); - else - return shiftl().greater_than(op2); - } - template - bool operator <= ( const ac_fixed &op2) const { - enum { F=W-I, F2=W2-I2 }; - if(F == F2) - return ! Base::greater_than(op2); - else if(F > F2) - return ! Base::greater_than(op2.template shiftl()); - else - return ! shiftl().greater_than(op2); - } - bool operator == ( double d) const { - if(is_neg() != (d < 0.0)) - return false; - double di = ac_private::ldexpr<-(I+!S+((32-W-!S)&31))>(d); - bool overflow, qb, r; - ac_fixed t; - t.conv_from_fraction(di, &qb, &r, &overflow); - if(qb || r || overflow) - return false; - return operator == (t); - } - bool operator != ( double d) const { - return !operator == ( d ); - } - bool operator < ( double d) const { - if(is_neg() != (d < 0.0)) - return is_neg(); - double di = ac_private::ldexpr<-(I+!S+((32-W-!S)&31))>(d); - bool overflow, qb, r; - ac_fixed t; - t.conv_from_fraction(di, &qb, &r, &overflow); - if(is_neg() && overflow) - return false; - return (!is_neg() && overflow) || ((qb || r) && operator <= (t)) || operator < (t); - } - bool operator >= ( double d) const { - return !operator < ( d ); - } - bool operator > ( double d) const { - if(is_neg() != (d < 0.0)) - return !is_neg(); - double di = ac_private::ldexpr<-(I+!S+((32-W-!S)&31))>(d); - bool overflow, qb, r; - ac_fixed t; - t.conv_from_fraction(di, &qb, &r, &overflow); - if(!is_neg() && overflow ) - return false; - return (is_neg() && overflow) || operator > (t); - } - bool operator <= ( double d) const { - return !operator > ( d ); - } - - // Bit and Slice Select ----------------------------------------------------- - template - inline const ac_int slc(const ac_int &index) const { - ac_int r; - AC_ASSERT(index.to_int() >= 0, "Attempting to read slc with negative indeces"); - unsigned uindex = ac_int(index).to_uint(); - Base::shift_r(uindex, r); - r.bit_adjust(); - return r; - } - - template - inline const ac_int slc(signed index) const { - ac_int r; - AC_ASSERT(index >= 0, "Attempting to read slc with negative indeces"); - unsigned uindex = index & ((unsigned)~0 >> 1); - Base::shift_r(uindex, r); - r.bit_adjust(); - return r; - } - template - inline const ac_int slc(unsigned uindex) const { - ac_int r; - Base::shift_r(uindex, r); - r.bit_adjust(); - return r; - } - - template - inline ac_fixed &set_slc(const ac_int lsb, const ac_int &slc) { - AC_ASSERT(lsb.to_int() + W2 <= W && lsb.to_int() >= 0, "Out of bounds set_slc"); - if(W == W2) - Base::operator =(slc); - else { - unsigned ulsb = ac_int(lsb).to_uint(); - Base::set_slc(ulsb, W2, (ac_int) slc); - } - bit_adjust(); // in case sign bit was assigned - return *this; - } - template - inline ac_fixed &set_slc(signed lsb, const ac_int &slc) { - AC_ASSERT(lsb + W2 <= W && lsb >= 0, "Out of bounds set_slc"); - if(W == W2) - Base::operator =(slc); - else { - unsigned ulsb = lsb & ((unsigned)~0 >> 1); - Base::set_slc(ulsb, W2, (ac_int) slc); - } - bit_adjust(); // in case sign bit was assigned - return *this; - } - template - inline ac_fixed &set_slc(unsigned ulsb, const ac_int &slc) { - AC_ASSERT(ulsb + W2 <= W, "Out of bounds set_slc"); - if(W == W2) - Base::operator =(slc); - else - Base::set_slc(ulsb, W2, (ac_int) slc); - bit_adjust(); // in case sign bit was assigned - return *this; - } - - template - inline ac::sliceref range() { - #if __cplusplus > 199711L - static_assert(Msb-Lsb+1 > 0, "Range length not positive: MSB < LSB"); - static_assert(Lsb >= 0, "LSB is negative"); - static_assert(Msb < W, "MSB >= W"); - #endif - return ac::sliceref(Base::v); - } - - class ac_bitref { -# if defined(__SYNTHESIS__) && !defined(AC_IGNORE_BUILTINS) -# pragma builtin -# endif - ac_fixed &d_bv; - unsigned d_index; - public: - ac_bitref( ac_fixed *bv, unsigned index=0 ) : d_bv(*bv), d_index(index) {} - operator bool () const { return (d_index < W) ? (d_bv.v[d_index>>5]>>(d_index&31) & 1) : 0; } - - inline ac_bitref operator = ( int val ) { - // lsb of int (val&1) is written to bit - if(d_index < W) { - int *pval = &d_bv.v[d_index>>5]; - *pval ^= (*pval ^ ((unsigned) val << (d_index&31) )) & 1 << (d_index&31); - d_bv.bit_adjust(); // in case sign bit was assigned - } - return *this; - } - template - inline ac_bitref operator = ( const ac_int &val ) { - return operator =(val.to_int()); - } - inline ac_bitref operator = ( const ac_bitref &val ) { - return operator =((int) (bool) val); - } - }; - - ac_bitref operator [] ( unsigned int uindex) { - AC_ASSERT(uindex < W, "Attempting to read bit beyond MSB"); - ac_bitref bvh( this, uindex ); - return bvh; - } - ac_bitref operator [] ( int index) { - AC_ASSERT(index >= 0, "Attempting to read bit with negative index"); - unsigned uindex = index & ((unsigned)~0 >> 1); - AC_ASSERT(uindex < W, "Attempting to read bit beyond MSB"); - ac_bitref bvh( this, uindex ); - return bvh; - } - template - ac_bitref operator [] ( const ac_int &index) { - AC_ASSERT(index.to_int() >= 0, "Attempting to read bit with negative index"); - unsigned uindex = ac_int(index).to_uint(); - AC_ASSERT(uindex < W, "Attempting to read bit beyond MSB"); - ac_bitref bvh( this, uindex ); - return bvh; - } - - bool operator [] ( unsigned int uindex) const { - AC_ASSERT(uindex < W, "Attempting to read bit beyond MSB"); - return (uindex < W) ? (Base::v[uindex>>5]>>(uindex&31) & 1) : 0; - } - bool operator [] ( int index) const { - AC_ASSERT(index >= 0, "Attempting to read bit with negative index"); - unsigned uindex = index & ((unsigned)~0 >> 1); - AC_ASSERT(uindex < W, "Attempting to read bit beyond MSB"); - return (uindex < W) ? (Base::v[uindex>>5]>>(uindex&31) & 1) : 0; - } - template - bool operator [] ( const ac_int &index) const { - AC_ASSERT(index.to_int() >= 0, "Attempting to read bit with negative index"); - unsigned uindex = ac_int(index).to_uint(); - AC_ASSERT(uindex < W, "Attempting to read bit beyond MSB"); - return (uindex < W) ? (Base::v[uindex>>5]>>(uindex&31) & 1) : 0; - } - typename rt_unary::leading_sign leading_sign() const { - unsigned ls = Base::leading_bits(S & (Base::v[N-1] < 0)) - (32*N - W)-S; - return ls; - } - typename rt_unary::leading_sign leading_sign(bool &all_sign) const { - unsigned ls = Base::leading_bits(S & (Base::v[N-1] < 0)) - (32*N - W)-S; - all_sign = (ls == W-S); - return ls; - } - // returns false if number is denormal - template - bool normalize(ac_int &exp) { - ac_int m = this->template slc(0); - bool r = m.normalize(exp); - this->set_slc(0,m); - return r; - } - // returns false if number is denormal, minimum exponent is reserved (usually for encoding special values/errors) - template - bool normalize_RME(ac_int &exp) { - ac_int m = this->template slc(0); - bool r = m.normalize_RME(exp); - this->set_slc(0,m); - return r; - } - inline void bit_fill_hex(const char *str) { - // Zero Pads if str is too short, throws ms bits away if str is too long - // Asserts if anything other than 0-9a-fA-F is encountered - ac_int x; - x.bit_fill_hex(str); - set_slc(0, x); - } - template - inline void bit_fill(const int (&ivec)[N], bool bigendian=true) { - // bit_fill from integer vector - // if W > N*32, missing most significant bits are zeroed - // if W < N*32, additional bits in ivec are ignored (no overflow checking) - // - // Example: - // ac_fixed<80,40,false> x; int vec[] = { 0xffffa987, 0x6543210f, 0xedcba987 }; - // x.bit_fill(vec); // vec[0] fill bits 79-64 - ac_int x; - x.bit_fill(ivec, bigendian); - set_slc(0, x); - } -}; - -namespace ac { - template - struct ac_fixed_represent { - enum { t_w = ac_private::c_type_params::W, t_i = t_w, t_s = ac_private::c_type_params::S }; - typedef ac_fixed type; - }; - template<> struct ac_fixed_represent {}; - template<> struct ac_fixed_represent {}; - template - struct ac_fixed_represent< ac_int > { - typedef ac_fixed type; - }; - template - struct ac_fixed_represent< ac_fixed > { - typedef ac_fixed type; - }; -} - -namespace ac_private { - // with T == ac_fixed - template - struct rt_ac_fixed_T< ac_fixed > { - typedef ac_fixed fx2_t; - template - struct op1 { - typedef ac_fixed fx_t; - typedef typename fx_t::template rt::mult mult; - typedef typename fx_t::template rt::plus plus; - typedef typename fx_t::template rt::minus minus; - typedef typename fx2_t::template rt::minus minus2; - typedef typename fx_t::template rt::logic logic; - typedef typename fx_t::template rt::div div; - typedef typename fx2_t::template rt::div div2; - }; - }; - // with T == ac_int - template - struct rt_ac_fixed_T< ac_int > { - typedef ac_fixed fx2_t; - template - struct op1 { - typedef ac_fixed fx_t; - typedef typename fx_t::template rt::mult mult; - typedef typename fx_t::template rt::plus plus; - typedef typename fx_t::template rt::minus minus; - typedef typename fx2_t::template rt::minus minus2; - typedef typename fx_t::template rt::logic logic; - typedef typename fx_t::template rt::div div; - typedef typename fx2_t::template rt::div div2; - }; - }; - - template - struct rt_ac_fixed_T< c_type > { - typedef typename ac::ac_fixed_represent::type fx2_t; - enum { W2 = fx2_t::width, I2 = W2, S2 = fx2_t::sign }; - template - struct op1 { - typedef ac_fixed fx_t; - typedef typename fx_t::template rt::mult mult; - typedef typename fx_t::template rt::plus plus; - typedef typename fx_t::template rt::minus minus; - typedef typename fx2_t::template rt::minus minus2; - typedef typename fx_t::template rt::logic logic; - typedef typename fx_t::template rt::div div; - typedef typename fx2_t::template rt::div div2; - }; - }; -} - - -// Specializations for constructors on integers that bypass bit adjusting -// and are therefore more efficient -template<> inline ac_fixed<1,1,true,AC_TRN,AC_WRAP>::ac_fixed( bool b ) { v[0] = b ? -1 : 0; } - -template<> inline ac_fixed<1,1,false,AC_TRN,AC_WRAP>::ac_fixed( bool b ) { v[0] = b; } -template<> inline ac_fixed<1,1,false,AC_TRN,AC_WRAP>::ac_fixed( signed char b ) { v[0] = b&1; } -template<> inline ac_fixed<1,1,false,AC_TRN,AC_WRAP>::ac_fixed( unsigned char b ) { v[0] = b&1; } -template<> inline ac_fixed<1,1,false,AC_TRN,AC_WRAP>::ac_fixed( signed short b ) { v[0] = b&1; } -template<> inline ac_fixed<1,1,false,AC_TRN,AC_WRAP>::ac_fixed( unsigned short b ) { v[0] = b&1; } -template<> inline ac_fixed<1,1,false,AC_TRN,AC_WRAP>::ac_fixed( signed int b ) { v[0] = b&1; } -template<> inline ac_fixed<1,1,false,AC_TRN,AC_WRAP>::ac_fixed( unsigned int b ) { v[0] = b&1; } -template<> inline ac_fixed<1,1,false,AC_TRN,AC_WRAP>::ac_fixed( signed long b ) { v[0] = b&1; } -template<> inline ac_fixed<1,1,false,AC_TRN,AC_WRAP>::ac_fixed( unsigned long b ) { v[0] = b&1; } -template<> inline ac_fixed<1,1,false,AC_TRN,AC_WRAP>::ac_fixed( Ulong b ) { v[0] = (int) b&1; } -template<> inline ac_fixed<1,1,false,AC_TRN,AC_WRAP>::ac_fixed( Slong b ) { v[0] = (int) b&1; } - -template<> inline ac_fixed<8,8,true,AC_TRN,AC_WRAP>::ac_fixed( bool b ) { v[0] = b; } -template<> inline ac_fixed<8,8,false,AC_TRN,AC_WRAP>::ac_fixed( bool b ) { v[0] = b; } -template<> inline ac_fixed<8,8,true,AC_TRN,AC_WRAP>::ac_fixed( signed char b ) { v[0] = b; } -template<> inline ac_fixed<8,8,false,AC_TRN,AC_WRAP>::ac_fixed( unsigned char b ) { v[0] = b; } -template<> inline ac_fixed<8,8,true,AC_TRN,AC_WRAP>::ac_fixed( unsigned char b ) { v[0] = (signed char) b; } -template<> inline ac_fixed<8,8,false,AC_TRN,AC_WRAP>::ac_fixed( signed char b ) { v[0] = (unsigned char) b; } - -template<> inline ac_fixed<16,16,true,AC_TRN,AC_WRAP>::ac_fixed( bool b ) { v[0] = b; } -template<> inline ac_fixed<16,16,false,AC_TRN,AC_WRAP>::ac_fixed( bool b ) { v[0] = b; } -template<> inline ac_fixed<16,16,true,AC_TRN,AC_WRAP>::ac_fixed( signed char b ) { v[0] = b; } -template<> inline ac_fixed<16,16,false,AC_TRN,AC_WRAP>::ac_fixed( unsigned char b ) { v[0] = b; } -template<> inline ac_fixed<16,16,true,AC_TRN,AC_WRAP>::ac_fixed( unsigned char b ) { v[0] = b; } -template<> inline ac_fixed<16,16,false,AC_TRN,AC_WRAP>::ac_fixed( signed char b ) { v[0] = (unsigned short) b; } -template<> inline ac_fixed<16,16,true,AC_TRN,AC_WRAP>::ac_fixed( signed short b ) { v[0] = b; } -template<> inline ac_fixed<16,16,false,AC_TRN,AC_WRAP>::ac_fixed( unsigned short b ) { v[0] = b; } -template<> inline ac_fixed<16,16,true,AC_TRN,AC_WRAP>::ac_fixed( unsigned short b ) { v[0] = (signed short) b; } -template<> inline ac_fixed<16,16,false,AC_TRN,AC_WRAP>::ac_fixed( signed short b ) { v[0] = (unsigned short) b; } - -template<> inline ac_fixed<32,32,true,AC_TRN,AC_WRAP>::ac_fixed( signed int b ) { v[0] = b; } -template<> inline ac_fixed<32,32,true,AC_TRN,AC_WRAP>::ac_fixed( unsigned int b ) { v[0] = b; } -template<> inline ac_fixed<32,32,false,AC_TRN,AC_WRAP>::ac_fixed( signed int b ) { v[0] = b; v[1] = 0;} -template<> inline ac_fixed<32,32,false,AC_TRN,AC_WRAP>::ac_fixed( unsigned int b ) { v[0] = b; v[1] = 0;} - -template<> inline ac_fixed<32,32,true,AC_TRN,AC_WRAP>::ac_fixed( Slong b ) { v[0] = (int) b; } -template<> inline ac_fixed<32,32,true,AC_TRN,AC_WRAP>::ac_fixed( Ulong b ) { v[0] = (int) b; } -template<> inline ac_fixed<32,32,false,AC_TRN,AC_WRAP>::ac_fixed( Slong b ) { v[0] = (int) b; v[1] = 0;} -template<> inline ac_fixed<32,32,false,AC_TRN,AC_WRAP>::ac_fixed( Ulong b ) { v[0] = (int) b; v[1] = 0;} - -template<> inline ac_fixed<64,64,true,AC_TRN,AC_WRAP>::ac_fixed( Slong b ) { v[0] = (int) b; v[1] = (int) (b >> 32); } -template<> inline ac_fixed<64,64,true,AC_TRN,AC_WRAP>::ac_fixed( Ulong b ) { v[0] = (int) b; v[1] = (int) (b >> 32);} -template<> inline ac_fixed<64,64,false,AC_TRN,AC_WRAP>::ac_fixed( Slong b ) { v[0] = (int) b; v[1] = (int) ((Ulong) b >> 32); v[2] = 0; } -template<> inline ac_fixed<64,64,false,AC_TRN,AC_WRAP>::ac_fixed( Ulong b ) { v[0] = (int) b; v[1] = (int) (b >> 32); v[2] = 0; } - - -// Stream -------------------------------------------------------------------- - -template -inline std::ostream& operator << (std::ostream &os, const ac_fixed &x) { -#ifndef __SYNTHESIS__ - if ((os.flags() & std::ios::hex) != 0) { - os << x.to_string(AC_HEX); - } else if ((os.flags() & std::ios::oct) != 0) { - os << x.to_string(AC_OCT); - } else { - os << x.to_string(AC_DEC); - } -#endif - return os; -} - - -// Macros for Binary Operators with C Integers -------------------------------------------- - -#define FX_BIN_OP_WITH_INT_2I(BIN_OP, C_TYPE, WI, SI) \ - template \ - inline ac_fixed operator BIN_OP ( const ac_fixed &op, C_TYPE i_op) { \ - return op.operator BIN_OP (ac_int(i_op)); \ - } - -#define FX_BIN_OP_WITH_INT(BIN_OP, C_TYPE, WI, SI, RTYPE) \ - template \ - inline typename ac_fixed::template rt::RTYPE operator BIN_OP ( C_TYPE i_op, const ac_fixed &op) { \ - return ac_fixed(i_op).operator BIN_OP (op); \ - } \ - template \ - inline typename ac_fixed::template rt::RTYPE operator BIN_OP ( const ac_fixed &op, C_TYPE i_op) { \ - return op.operator BIN_OP (ac_fixed(i_op)); \ - } - -#define FX_REL_OP_WITH_INT(REL_OP, C_TYPE, W2, S2) \ - template \ - inline bool operator REL_OP ( const ac_fixed &op, C_TYPE op2) { \ - return op.operator REL_OP (ac_fixed(op2)); \ - } \ - template \ - inline bool operator REL_OP ( C_TYPE op2, const ac_fixed &op) { \ - return ac_fixed(op2).operator REL_OP (op); \ - } - -#define FX_ASSIGN_OP_WITH_INT_2(ASSIGN_OP, C_TYPE, W2, S2) \ - template \ - inline ac_fixed &operator ASSIGN_OP ( ac_fixed &op, C_TYPE op2) { \ - return op.operator ASSIGN_OP (ac_fixed(op2)); \ - } - -#define FX_ASSIGN_OP_WITH_INT_2I(ASSIGN_OP, C_TYPE, W2, S2) \ - template \ - inline ac_fixed operator ASSIGN_OP ( ac_fixed &op, C_TYPE op2) { \ - return op.operator ASSIGN_OP (ac_int(op2)); \ - } - -#define FX_OPS_WITH_INT(C_TYPE, WI, SI) \ - FX_BIN_OP_WITH_INT(*, C_TYPE, WI, SI, mult) \ - FX_BIN_OP_WITH_INT(+, C_TYPE, WI, SI, plus) \ - FX_BIN_OP_WITH_INT(-, C_TYPE, WI, SI, minus) \ - FX_BIN_OP_WITH_INT(/, C_TYPE, WI, SI, div) \ - FX_BIN_OP_WITH_INT_2I(>>, C_TYPE, WI, SI) \ - FX_BIN_OP_WITH_INT_2I(<<, C_TYPE, WI, SI) \ - FX_BIN_OP_WITH_INT(&, C_TYPE, WI, SI, logic) \ - FX_BIN_OP_WITH_INT(|, C_TYPE, WI, SI, logic) \ - FX_BIN_OP_WITH_INT(^, C_TYPE, WI, SI, logic) \ - \ - FX_REL_OP_WITH_INT(==, C_TYPE, WI, SI) \ - FX_REL_OP_WITH_INT(!=, C_TYPE, WI, SI) \ - FX_REL_OP_WITH_INT(>, C_TYPE, WI, SI) \ - FX_REL_OP_WITH_INT(>=, C_TYPE, WI, SI) \ - FX_REL_OP_WITH_INT(<, C_TYPE, WI, SI) \ - FX_REL_OP_WITH_INT(<=, C_TYPE, WI, SI) \ - \ - FX_ASSIGN_OP_WITH_INT_2(+=, C_TYPE, WI, SI) \ - FX_ASSIGN_OP_WITH_INT_2(-=, C_TYPE, WI, SI) \ - FX_ASSIGN_OP_WITH_INT_2(*=, C_TYPE, WI, SI) \ - FX_ASSIGN_OP_WITH_INT_2(/=, C_TYPE, WI, SI) \ - FX_ASSIGN_OP_WITH_INT_2I(>>=, C_TYPE, WI, SI) \ - FX_ASSIGN_OP_WITH_INT_2I(<<=, C_TYPE, WI, SI) \ - FX_ASSIGN_OP_WITH_INT_2(&=, C_TYPE, WI, SI) \ - FX_ASSIGN_OP_WITH_INT_2(|=, C_TYPE, WI, SI) \ - FX_ASSIGN_OP_WITH_INT_2(^=, C_TYPE, WI, SI) - -// --------------------------------------- End of Macros for Binary Operators with C Integers - -#ifdef AC_FIXED_NS_FOR_MIXED_OPERATORS -namespace ac { - namespace ops_with_other_types { -#endif - // Binary Operators with C Integers -------------------------------------------- - FX_OPS_WITH_INT(bool, 1, false) - FX_OPS_WITH_INT(char, 8, true) - FX_OPS_WITH_INT(signed char, 8, true) - FX_OPS_WITH_INT(unsigned char, 8, false) - FX_OPS_WITH_INT(short, 16, true) - FX_OPS_WITH_INT(unsigned short, 16, false) - FX_OPS_WITH_INT(int, 32, true) - FX_OPS_WITH_INT(unsigned int, 32, false) - FX_OPS_WITH_INT(long, ac_private::long_w, true) - FX_OPS_WITH_INT(unsigned long, ac_private::long_w, false) - FX_OPS_WITH_INT(Slong, 64, true) - FX_OPS_WITH_INT(Ulong, 64, false) - // -------------------------------------- End of Binary Operators with Integers -#ifdef AC_FIXED_NS_FOR_MIXED_OPERATORS - } // ops_with_other_types namespace -} // ac namespace -#endif - - -// Macros for Binary Operators with ac_int -------------------------------------------- - -#define FX_BIN_OP_WITH_AC_INT_1(BIN_OP, RTYPE) \ - template \ - inline typename ac_fixed::template rt::RTYPE operator BIN_OP ( const ac_int &i_op, const ac_fixed &op) { \ - return ac_fixed(i_op).operator BIN_OP (op); \ - } - -#define FX_BIN_OP_WITH_AC_INT_2(BIN_OP, RTYPE) \ - template \ - inline typename ac_fixed::template rt::RTYPE operator BIN_OP ( const ac_fixed &op, const ac_int &i_op) { \ - return op.operator BIN_OP (ac_fixed(i_op)); \ - } - -#define FX_BIN_OP_WITH_AC_INT(BIN_OP, RTYPE) \ - FX_BIN_OP_WITH_AC_INT_1(BIN_OP, RTYPE) \ - FX_BIN_OP_WITH_AC_INT_2(BIN_OP, RTYPE) - -#define FX_REL_OP_WITH_AC_INT(REL_OP) \ - template \ - inline bool operator REL_OP ( const ac_fixed &op, const ac_int &op2) { \ - return op.operator REL_OP (ac_fixed(op2)); \ - } \ - template \ - inline bool operator REL_OP ( ac_int &op2, const ac_fixed &op) { \ - return ac_fixed(op2).operator REL_OP (op); \ - } - -#define FX_ASSIGN_OP_WITH_AC_INT(ASSIGN_OP) \ - template \ - inline ac_fixed &operator ASSIGN_OP ( ac_fixed &op, const ac_int &op2) { \ - return op.operator ASSIGN_OP (ac_fixed(op2)); \ - } \ - template \ - inline ac_int &operator ASSIGN_OP ( ac_int &op, const ac_fixed &op2) { \ - return op.operator ASSIGN_OP (op2.to_ac_int()); \ - } - -// -------------------------------------------- End of Macros for Binary Operators with ac_int - -#ifdef AC_FIXED_NS_FOR_MIXED_OPERATORS -namespace ac { - namespace ops_with_other_types { -#endif - // Binary Operators with ac_int -------------------------------------------- - FX_BIN_OP_WITH_AC_INT(*, mult) - FX_BIN_OP_WITH_AC_INT(+, plus) - FX_BIN_OP_WITH_AC_INT(-, minus) - FX_BIN_OP_WITH_AC_INT(/, div) - FX_BIN_OP_WITH_AC_INT(&, logic) - FX_BIN_OP_WITH_AC_INT(|, logic) - FX_BIN_OP_WITH_AC_INT(^, logic) - - FX_REL_OP_WITH_AC_INT(==) - FX_REL_OP_WITH_AC_INT(!=) - FX_REL_OP_WITH_AC_INT(>) - FX_REL_OP_WITH_AC_INT(>=) - FX_REL_OP_WITH_AC_INT(<) - FX_REL_OP_WITH_AC_INT(<=) - - FX_ASSIGN_OP_WITH_AC_INT(+=) - FX_ASSIGN_OP_WITH_AC_INT(-=) - FX_ASSIGN_OP_WITH_AC_INT(*=) - FX_ASSIGN_OP_WITH_AC_INT(/=) - FX_ASSIGN_OP_WITH_AC_INT(&=) - FX_ASSIGN_OP_WITH_AC_INT(|=) - FX_ASSIGN_OP_WITH_AC_INT(^=) - // -------------------------------------- End of Binary Operators with ac_int - - // Relational Operators with double -------------------------------------- - template - inline bool operator == ( double op, const ac_fixed &op2) { - return op2.operator == (op); - } - template - inline bool operator != ( double op, const ac_fixed &op2) { - return op2.operator != (op); - } - template - inline bool operator > ( double op, const ac_fixed &op2) { - return op2.operator < (op); - } - template - inline bool operator < ( double op, const ac_fixed &op2) { - return op2.operator > (op); - } - template - inline bool operator <= ( double op, const ac_fixed &op2) { - return op2.operator >= (op); - } - template - inline bool operator >= ( double op, const ac_fixed &op2) { - return op2.operator <= (op); - } - // -------------------------------------- End of Relational Operators with double -#ifdef AC_FIXED_NS_FOR_MIXED_OPERATORS - } // ops_with_other_types namespace -} // ac namespace -using namespace ac::ops_with_other_types; -#endif - - -#if (defined(_MSC_VER) && !defined(__EDG__)) -#pragma warning( disable: 4700 ) -#endif -#if (defined(__GNUC__) && ( __GNUC__ == 4 && __GNUC_MINOR__ >= 6 || __GNUC__ > 4 ) && !defined(__EDG__)) -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wuninitialized" -#endif -#if defined(__clang__) -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wuninitialized" -#endif - -// Global templatized functions for easy initialization to special values -template -inline ac_fixed value(ac_fixed) { - ac_fixed r; - return r.template set_val(); -} - -namespace ac { -// PUBLIC FUNCTIONS -// function to initialize (or uninitialize) arrays - template - inline bool init_array(ac_fixed *a, int n) { - ac_fixed t; - t.template set_val(); - for(int i=0; i < n; i++) - a[i] = t; - return true; - } - - inline ac_fixed<54,2,true> frexp_d(double d, ac_int<11,true> &exp) { - enum {Min_Exp = -1022, Max_Exp = 1023, Mant_W = 52, Denorm_Min_Exp = Min_Exp - Mant_W}; - if(!d) { - exp = 0; - return 0; - } - int exp_i; - double f0 = frexp(d, &exp_i); - AC_ASSERT(exp_i <= Max_Exp+1, "Exponent greater than standard double-precision float exponent max (+1024). It is probably an extended double"); - AC_ASSERT(exp_i >= Denorm_Min_Exp+1, "Exponent less than standard double-precision float exponent min (-1021). It is probably an extended double"); - exp_i--; - int rshift = exp_i < Min_Exp ? Min_Exp - exp_i : (exp_i > Min_Exp && f0 < 0 && f0 >= -0.5) ? -1 : 0; - exp = exp_i + rshift; - ac_int f_i = f0 * ((Ulong) 1 << (Mant_W + 1 -rshift)); - ac_fixed r; - r.set_slc(0, f_i); - return r; - } - inline ac_fixed<25,2,true> frexp_f(float f, ac_int<8,true> &exp) { - enum {Min_Exp = -126, Max_Exp = 127, Mant_W = 23, Denorm_Min_Exp = Min_Exp - Mant_W}; - if(!f) { - exp = 0; - return 0; - } - int exp_i; - float f0 = frexpf(f, &exp_i); - AC_ASSERT(exp_i <= Max_Exp+1, "Exponent greater than standard single-precision float exponent max (+128). It is probably an extended float"); - AC_ASSERT(exp_i >= Denorm_Min_Exp+1, "Exponent less than standard single-precision float exponent min (-125). It is probably an extended float"); - exp_i--; - int rshift = exp_i < Min_Exp ? Min_Exp - exp_i : (exp_i >= Min_Exp && f0 < 0 && f0 >= -0.5) ? -1 : 0; - exp = exp_i + rshift; - ac_int f_i = f0 * (1 << (Mant_W + 1 - rshift)); - ac_fixed r; - r.set_slc(0, f_i); - return r; - } - - inline ac_fixed<53,1,false> frexp_sm_d(double d, ac_int<11,true> &exp, bool &sign) { - enum {Min_Exp = -1022, Max_Exp = 1023, Mant_W = 52, Denorm_Min_Exp = Min_Exp - Mant_W}; - if(!d) { - exp = 0; - sign = false; - return 0; - } - int exp_i; - bool s = d < 0; - double f0 = frexp(s ? -d : d, &exp_i); - AC_ASSERT(exp_i <= Max_Exp+1, "Exponent greater than standard double-precision float exponent max (+1024). It is probably an extended double"); - AC_ASSERT(exp_i >= Denorm_Min_Exp+1, "Exponent less than standard double-precision float exponent min (-1021). It is probably an extended double"); - exp_i--; - int rshift = exp_i < Min_Exp ? Min_Exp - exp_i : 0; - exp = exp_i + rshift; - ac_int f_i = f0 * ((Ulong) 1 << (Mant_W + 1 -rshift)); - ac_fixed r; - r.set_slc(0, f_i); - sign = s; - return r; - } - inline ac_fixed<24,1,false> frexp_sm_f(float f, ac_int<8,true> &exp, bool &sign) { - enum {Min_Exp = -126, Max_Exp = 127, Mant_W = 23, Denorm_Min_Exp = Min_Exp - Mant_W}; - if(!f) { - exp = 0; - sign = false; - return 0; - } - int exp_i; - bool s = f < 0; - float f0 = frexp(s ? -f : f, &exp_i); - AC_ASSERT(exp_i <= Max_Exp+1, "Exponent greater than standard single-precision float exponent max (+128). It is probably an extended float"); - AC_ASSERT(exp_i >= Denorm_Min_Exp+1, "Exponent less than standard single-precision float exponent min (-125). It is probably an extended float"); - exp_i--; - int rshift = exp_i < Min_Exp ? Min_Exp - exp_i : 0; - exp = exp_i + rshift; - ac_int<24,false> f_i = f0 * (1 << (Mant_W + 1 - rshift)); - ac_fixed<24,1,false> r; - r.set_slc(0, f_i); - sign = s; - return r; - } - - template - const ac_fixed &basic_num_ovf_base::value() const { - return (const ac_fixed &) *this; - } - - template std::string basic_num_ovf_base::type_name() { - return ac_fixed::type_name(); - } -} - - -/////////////////////////////////////////////////////////////////////////////// - -#if (defined(_MSC_VER) && !defined(__EDG__)) -#pragma warning( pop ) -#endif -#if (defined(__GNUC__) && ( __GNUC__ == 4 && __GNUC_MINOR__ >= 6 || __GNUC__ > 4 ) && !defined(__EDG__)) -#pragma GCC diagnostic pop -#endif -#if defined(__clang__) -#pragma clang diagnostic pop -#endif - -#ifdef __AC_NAMESPACE -} -#endif - -#endif // __AC_FIXED_H diff --git a/hls4ml/templates/quartus/ac_types/ac_float.h b/hls4ml/templates/quartus/ac_types/ac_float.h deleted file mode 100644 index 6174528d73..0000000000 --- a/hls4ml/templates/quartus/ac_types/ac_float.h +++ /dev/null @@ -1,1196 +0,0 @@ -/************************************************************************** - * * - * Algorithmic C (tm) Datatypes * - * * - * Software Version: 4.0 * - * * - * Release Date : Sat Jun 13 12:35:18 PDT 2020 * - * Release Type : Production Release * - * Release Build : 4.0.0 * - * * - * Copyright 2013-2019, Mentor Graphics Corporation, * - * * - * All Rights Reserved. * - * * - ************************************************************************** - * Licensed under the Apache License, Version 2.0 (the "License"); * - * you may not use this file except in compliance with the License. * - * You may obtain a copy of the License at * - * * - * http://www.apache.org/licenses/LICENSE-2.0 * - * * - * Unless required by applicable law or agreed to in writing, software * - * distributed under the License is distributed on an "AS IS" BASIS, * - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * - * implied. * - * See the License for the specific language governing permissions and * - * limitations under the License. * - ************************************************************************** - * * - * The most recent version of this package is available at github. * - * * - *************************************************************************/ - -// Source: ac_float.h -// Description: class for floating point operation handling in C++ -// Author: Andres Takach, Ph.D. - -#ifndef __AC_FLOAT_H -#define __AC_FLOAT_H - -#include - -#ifndef __SYNTHESIS__ -#include -#endif - -#if (defined(__GNUC__) && __GNUC__ < 3 && !defined(__EDG__)) -#error GCC version 3 or greater is required to include this header file -#endif - -#if (defined(_MSC_VER) && _MSC_VER < 1400 && !defined(__EDG__)) -#error Microsoft Visual Studio 8 or newer is required to include this header file -#endif - -#if (defined(_MSC_VER) && !defined(__EDG__)) -#pragma warning( push ) -#pragma warning( disable: 4003 4127 4308 4365 4514 4800 ) -#endif -#if (defined(__GNUC__) && ( __GNUC__ == 4 && __GNUC_MINOR__ >= 6 || __GNUC__ > 4 ) && !defined(__EDG__)) -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wparentheses" -#endif -#if defined(__clang__) -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wparentheses" -#pragma clang diagnostic ignored "-Wlogical-op-parentheses" -#pragma clang diagnostic ignored "-Wbitwise-op-parentheses" -#endif - -// for safety -#if (defined(E) || defined(WF) || defined(IF) || defined(SF)) -#error One or more of the following is defined: E, WF, IF, SF. Definition conflicts with their usage as template parameters. -#error DO NOT use defines before including third party header files. -#endif - -#define AC_FL(v) ac_float -#define AC_FL0(v) ac_float -#define AC_FL_T(v) int W##v, int I##v, int E##v, ac_q_mode Q##v -#define AC_FL_TV(v) W##v, I##v, E##v, Q##v -#define AC_FL_T0(v) int W##v, int I##v, int E##v -#define AC_FL_TV0(v) W##v, I##v, E##v - -#ifdef __AC_NAMESPACE -namespace __AC_NAMESPACE { -#endif - -template class ac_float; - -namespace ac_private { - - typedef ac_float<54,2,11> ac_float_cdouble_t; - typedef ac_float<25,2,8> ac_float_cfloat_t; - - template - struct rt_ac_float_T { - template< AC_FL_T0() > - struct op1 { - typedef AC_FL0() fl_t; - typedef typename T::template rt_T::mult mult; - typedef typename T::template rt_T::plus plus; - typedef typename T::template rt_T::minus2 minus; - typedef typename T::template rt_T::minus minus2; - typedef typename T::template rt_T::logic logic; - typedef typename T::template rt_T::div2 div; - typedef typename T::template rt_T::div div2; - }; - }; - // specializations after definition of ac_float - - inline ac_float_cdouble_t double_to_ac_float(double d); - inline ac_float_cfloat_t float_to_ac_float(float f); -} - -////////////////////////////////////////////////////////////////////////////// -// ac_float -////////////////////////////////////////////////////////////////////////////// - -template< AC_FL_T() > -class ac_float { - enum { NO_UN = true, S = true, S2 = true, SR = true }; -public: - typedef ac_fixed mant_t; - typedef ac_int exp_t; - mant_t m; - exp_t e; - - void set_mantissa(const ac_fixed &man) { m = man; } - void set_exp(const ac_int &exp) { if(E) e = exp; } - -private: - inline bool is_neg() const { return m < 0; } // is_neg would be more efficient - - enum {NZ_E = !!E, MIN_EXP = -(NZ_E << (E-NZ_E)), MAX_EXP = (1 << (E-NZ_E))-1}; - -public: - static const int width = W; - static const int i_width = I; - static const int e_width = E; - static const bool sign = S; - static const ac_q_mode q_mode = Q; - static const ac_o_mode o_mode = AC_SAT; - - template< AC_FL_T0(2) > - struct rt { - enum { - // need to validate - F=W-I, - F2=W2-I2, - mult_w = W+W2, - mult_i = I+I2, - mult_e = AC_MAX(E,E2)+1, - mult_s = S||S2, - plus_w = AC_MAX(I+(S2&&!S),I2+(S&&!S2))+1+AC_MAX(F,F2), - plus_i = AC_MAX(I+(S2&&!S),I2+(S&&!S2))+1, - plus_e = AC_MAX(E,E2), - plus_s = S||S2, - minus_w = AC_MAX(I+(S2&&!S),I2+(S&&!S2))+1+AC_MAX(F,F2), - minus_i = AC_MAX(I+(S2&&!S),I2+(S&&!S2))+1, - minus_e = AC_MAX(E,E2), - minus_s = true, - div_w = W+AC_MAX(W2-I2,0)+S2, - div_i = I+(W2-I2)+S2, - div_e = AC_MAX(E,E2)+1, - div_s = S||S2, - logic_w = AC_MAX(I+(S2&&!S),I2+(S&&!S2))+AC_MAX(F,F2), - logic_i = AC_MAX(I+(S2&&!S),I2+(S&&!S2)), - logic_s = S||S2, - logic_e = AC_MAX(E,E2) - }; - typedef ac_float mult; - typedef ac_float plus; - typedef ac_float minus; - typedef ac_float logic; - typedef ac_float div; - typedef ac_float arg1; - - }; - - template - struct rt_i { - enum { - lshift_w = W, - lshift_i = I, - lshift_s = S, - lshift_e_0 = exp_t::template rt::plus::width, - lshift_e = AC_MIN(lshift_e_0, 24), - rshift_w = W, - rshift_i = I, - rshift_s = S, - rshift_e_0 = exp_t::template rt::minus::width, - rshift_e = AC_MIN(rshift_e_0, 24) - }; - typedef ac_float lshift; - typedef ac_float rshift; - }; - - template - struct rt_T { - typedef typename ac_private::map::t map_T; - typedef typename ac_private::rt_ac_float_T::template op1< AC_FL_TV0() >::mult mult; - typedef typename ac_private::rt_ac_float_T::template op1< AC_FL_TV0() >::plus plus; - typedef typename ac_private::rt_ac_float_T::template op1< AC_FL_TV0() >::minus minus; - typedef typename ac_private::rt_ac_float_T::template op1< AC_FL_TV0() >::minus2 minus2; - typedef typename ac_private::rt_ac_float_T::template op1< AC_FL_TV0() >::logic logic; - typedef typename ac_private::rt_ac_float_T::template op1< AC_FL_TV0() >::div div; - typedef typename ac_private::rt_ac_float_T::template op1< AC_FL_TV0() >::div2 div2; - typedef ac_float arg1; - }; - - template - struct rt_T2 { - typedef typename ac_private::map::t map_T; - typedef typename ac_private::rt_ac_float_T::template op1< AC_FL_TV0() >::mult mult; - typedef typename ac_private::rt_ac_float_T::template op1< AC_FL_TV0() >::plus plus; - typedef typename ac_private::rt_ac_float_T::template op1< AC_FL_TV0() >::minus2 minus; - typedef typename ac_private::rt_ac_float_T::template op1< AC_FL_TV0() >::minus minus2; - typedef typename ac_private::rt_ac_float_T::template op1< AC_FL_TV0() >::logic logic; - typedef typename ac_private::rt_ac_float_T::template op1< AC_FL_TV0() >::div2 div; - typedef typename ac_private::rt_ac_float_T::template op1< AC_FL_TV0() >::div div2; - typedef ac_float arg1; - }; - - struct rt_unary { - enum { - neg_w = W+1, - neg_i = I+1, - neg_e = E, - neg_s = true, - mag_sqr_w = 2*W-S + NO_UN, - mag_sqr_i = 2*I-S + NO_UN, - mag_sqr_e = E, - mag_sqr_s = false | NO_UN, - mag_w = W+S + NO_UN, - mag_i = I+S + NO_UN, - mag_e = E, - mag_s = false | NO_UN, - to_fx_i = I + MAX_EXP, - to_fx_w = W + MAX_EXP - MIN_EXP, - to_fx_s = S, - to_i_w = AC_MAX(to_fx_i,1), - to_i_s = S - }; - typedef ac_float neg; - typedef ac_float mag_sqr; - typedef ac_float mag; - template - struct set { - enum { sum_w = W + ac::log2_ceil::val, sum_i = (sum_w-W) + I, sum_e = E, sum_s = S}; - typedef ac_float sum; - }; - typedef ac_fixed to_ac_fixed_t; - typedef ac_int to_ac_int_t; - }; - - template friend class ac_float; - - ac_float() { -#if defined(AC_DEFAULT_IN_RANGE) -#endif - } - ac_float(const ac_float &op) { - m = op.m; - e = op.e; - } - -private: - template - bool round(const ac_fixed &op2, bool assert_on_rounding=false) { - const bool rnd = Q!=AC_TRN && Q!=AC_TRN_ZERO && W2 > W; - bool rnd_ovfl = false; - m = 0; - if(rnd) { - ac_fixed m_1 = op2; - // overflow because of rounding would lead to go from 001111 to 01000 (extra bit prevents it) - // change from 01000 to 00100 and store 0100 in m - rnd_ovfl = !m_1[W] & m_1[W-1]; - m_1[W-1] = m_1[W-1] & !rnd_ovfl; - m_1[W-2] = m_1[W-2] | rnd_ovfl; - m.set_slc(0, m_1.template slc(0)); - if(assert_on_rounding) - AC_ASSERT(m == op2, "Loss of precision due to Rounding"); - return rnd_ovfl; - } else { - ac_fixed m_0 = op2; - m.set_slc(0, m_0.template slc(0)); - return false; - } - } - - template - void assign_from(const ac_fixed &m2, int e2, bool sticky_bit, bool normalize, bool assert_on_rounding=false) { - const bool rnd = Q!=AC_TRN & Q!=AC_TRN_ZERO & W2 > W; - const bool need_rnd_bit = Q != AC_TRN; - const bool need_rem_bits = need_rnd_bit && Q != AC_RND; - - const int msb_min_power = I-1 + MIN_EXP; - const int msb_min_power2 = I2-1 + min_exp2; - const int msb_min_power_dif = msb_min_power - msb_min_power2; - // if > 0: target has additional negative exponent range - // subnormal maybe be further normalized (done even if normalize==false) - // if < 0: target has less negative exponent range - // mantissa may need to be shifted right - // in either case if source is unnormalized - // normalization could take place - - const int msb_max_power = I-1 + MAX_EXP; - const int msb_max_power2 = I2-1 + max_exp2 + rnd; - const int msb_max_power_dif = msb_max_power - msb_max_power2; - - const bool may_shift_right = msb_min_power_dif > 0; - const int max_right_shift = may_shift_right ? msb_min_power_dif : 0; - const int t_width = W2 + (W >= W2 ? AC_MIN(W-W2+may_shift_right, max_right_shift) : 0); - - int e_t = e2; - e_t += I2-I; - typedef ac_fixed op2_t; - op2_t op2 = m2; - int ls = 0; - bool r_zero; - if(normalize) { - bool all_sign; - ls = m2.leading_sign(all_sign); - r_zero = all_sign & !m2[0]; - } else if(msb_min_power_dif < 0 || msb_max_power_dif < 0 || W2 > W) { - // msb_min_power_dif < 0: src exponent less negative than trg exp represents - // oportunity to further normalize value in trg representation - // msb_max_power_dif < 0: max target exp is less than max src exp - // if un-normalized exp may overflow resulting in incorrect saturation - // normalization is needed for correctness - // W2 > W - // if un-normalized, extra bits may be incorrectly quantized away - const int msb_range_dif = AC_MAX(-msb_min_power_dif, -msb_max_power_dif); - const int msb_range_dif_norm_w = AC_MIN(msb_range_dif,W2-1); - const int extra_bits = AC_MAX(W2-W,0); - const int norm_w = AC_MAX(msb_range_dif_norm_w, extra_bits) + 1; - bool all_sign; - ls = m2.template slc(W2-norm_w).leading_sign(all_sign); - r_zero = all_sign & !m2[W2-1] & !(m2 << norm_w); - } else { - r_zero = !m2; - } - int actual_max_shift_left = (1 << (E-1)) + e_t; - if(may_shift_right && actual_max_shift_left < 0) { - const int shift_r_w = ac::nbits::val; - ac_int shift_r = -actual_max_shift_left; - if((1 << (E-1)) + min_exp2 + I2-I < 0 && need_rem_bits) { - op2_t shifted_out_bits = op2; - shifted_out_bits &= ~((~op2_t(0)) << shift_r); - sticky_bit |= !!shifted_out_bits; - } - op2 >>= shift_r; - e_t += shift_r; - } else { - bool shift_exponent_limited = ls >= actual_max_shift_left; - int shift_l = shift_exponent_limited ? actual_max_shift_left : (int) ls; - op2 <<= shift_l; - e_t = shift_exponent_limited ? MIN_EXP : e_t - ls; - } - ac_fixed r_pre_rnd = 0; - r_pre_rnd.set_slc(need_rem_bits, op2.template slc(0)); - if(need_rem_bits) - r_pre_rnd[0] = sticky_bit; - - bool shift_r1 = round(r_pre_rnd); - e_t = r_zero ? 0 : e_t + shift_r1; - if(!(e_t < 0) & !!(e_t >> E-1)) { - e = MAX_EXP; - m = m < 0 ? value(m) : value(m); - } else { - e = e_t; - } - } - -public: - template - ac_float(const AC_FL(2) &op, bool assert_on_overflow=false, bool assert_on_rounding=false) { - typedef AC_FL(2) fl2_t; - const int min_exp2 = fl2_t::MIN_EXP; - const int max_exp2 = fl2_t::MAX_EXP; - assign_from(op.m, op.e, false, false); - } - - ac_float(const ac_fixed &m2, const ac_int &e2, bool normalize=true) { - m = m2; - e = e2; - if(normalize) - this->normalize(); - else - e &= ac_int<1,true>(!!m); - } - - template - ac_float(const ac_fixed &m2, const ac_int &e2, bool normalize=true) { - enum { WF2 = WFX+!SFX, IF2 = IFX+!SFX }; - ac_float f(ac_fixed(m2), e2, normalize); - *this = f; - } - - template - ac_float(const ac_fixed &op) { - assign_from<0,0>(ac_fixed(op), 0, false, true); - } - - template - ac_float(const ac_int &op) { - *this = ac_fixed(op); - } - - inline ac_float( bool b ) { *this = (ac_int<1,false>) b; } - inline ac_float( char b ) { *this = (ac_int<8,true>) b; } - inline ac_float( signed char b ) { *this = (ac_int<8,true>) b; } - inline ac_float( unsigned char b ) { *this = (ac_int<8,false>) b; } - inline ac_float( signed short b ) { *this = (ac_int<16,true>) b; } - inline ac_float( unsigned short b ) { *this = (ac_int<16,false>) b; } - inline ac_float( signed int b ) { *this = (ac_int<32,true>) b; } - inline ac_float( unsigned int b ) { *this = (ac_int<32,false>) b; } - inline ac_float( signed long b ) { *this = (ac_int) b; } - inline ac_float( unsigned long b ) { *this = (ac_int) b; } - inline ac_float( Slong b ) { *this = (ac_int<64,true>) b; } - inline ac_float( Ulong b ) { *this = (ac_int<64,false>) b; } - - // Explicit conversion functions to ac_int and ac_fixed - inline typename rt_unary::to_ac_fixed_t to_ac_fixed() const { - typename rt_unary::to_ac_fixed_t r = m; - r <<= e; - return r; - } - inline typename rt_unary::to_ac_int_t to_ac_int() const { - return to_ac_fixed().to_ac_int(); - } - - // Explicit conversion functions to C built-in types ------------- - inline int to_int() const { return to_ac_int().to_int(); } - inline unsigned to_uint() const { return to_ac_int().to_uint(); } - inline long to_long() const { return (signed long) to_ac_int().to_int64(); } - inline unsigned long to_ulong() const { return (unsigned long) to_ac_int().to_uint64(); } - inline Slong to_int64() const { return to_ac_int().to_int64(); } - inline Ulong to_uint64() const { return to_ac_int().to_uint64(); } - inline float to_float() const { return ldexpf(m.to_double(), exp()); } - inline double to_double() const { return ldexp(m.to_double(), exp()); } - - const ac_fixed mantissa() const { return m; } - const ac_int exp() const { return e; } - bool normalize() { - bool all_sign; - int ls = m.leading_sign(all_sign); - bool m_zero = all_sign & !m[0]; - const int max_shift_left = (1 << (E-1)) + e; - bool normal = ls <= max_shift_left; - int shift_l = normal ? ls : max_shift_left; - m <<= shift_l; - e = ac_int<1,true>(!m_zero) & (e - shift_l); - return normal; - } - - ac_float( double d, bool assert_on_overflow=false, bool assert_on_rounding=false ) { - enum { I_EXT = AC_MAX(I,1), W_EXT = ac_private::ac_float_cdouble_t::width + I_EXT - 1, }; - ac_private::ac_float_cdouble_t t = ac_private::double_to_ac_float(d); - ac_float r(t, assert_on_overflow, assert_on_rounding); - *this = r; - } - - ac_float( float f, bool assert_on_overflow=false, bool assert_on_rounding=false ) { - enum { I_EXT = AC_MAX(I,1), W_EXT = ac_private::ac_float_cfloat_t::width + I_EXT - 1, }; - ac_private::ac_float_cfloat_t t = ac_private::float_to_ac_float(f); - ac_float r(t, assert_on_overflow, assert_on_rounding); - *this = r; - } - - template - bool compare(const AC_FL(2) &op2, bool *gt) const { - typedef ac_fixed fx2_t; - typedef typename ac_fixed::template rt_T< fx2_t >::logic fx_t; - typedef ac_fixed fxu_t; - - fx2_t op2_m_0; - op2_m_0.set_slc(0, op2.m.template slc(0)); - - fx_t op1_m = m; - fx_t op2_m = op2_m_0; - int e_dif = exp() - op2.exp() + I - I2; - bool op2_m_neg = op2_m[fx_t::width-1]; - fx_t out_bits = op2_m ^ ((op2_m_neg & e_dif < 0) ? ~fx_t(0) : fx_t(0)); - out_bits &= ~(fxu_t(~fxu_t(0)) << e_dif); - op2_m >>= e_dif; - bool overflow = e_dif < 0 & !!out_bits | op2_m_neg ^ op2_m[fx_t::width-1]; - - *gt = overflow & op2_m_neg | !overflow & op1_m > op2_m; - bool eq = op1_m == op2_m & !overflow & !out_bits; - return eq; - } - - template - void plus_minus(const AC_FL(2) &op2, AC_FL(R) &r, bool sub=false) const { - typedef AC_FL(2) op2_t; - enum { IT = AC_MAX(I,I2) }; - typedef ac_fixed fx1_t; - typedef ac_fixed fx2_t; - // covers fx1_t and r mantissas (adds additional LSBs if WR > W) - typedef typename fx1_t::template rt_T< ac_fixed >::logic fx1r_t; - // covers fx2_t and r mantissas (adds additional LSBs if WR > W2) - typedef typename fx2_t::template rt_T< ac_fixed >::logic fx2r_t; - // mt_t adds one integer bit for the plus - // op1_m, op2_m, op_sl, sticky_bits - typedef typename fx1r_t::template rt_T::plus mt_t; - - const bool round_bit_needed = QR != AC_TRN; - const bool remaining_bits_needed = !(QR == AC_TRN || QR == AC_RND); - - const int w_r_with_round_bits = WR + round_bit_needed; - - // naming: sn = subnormal, n = normal, wc = worst case - // worst case (wc) normalize is when one operand has smallest subnormal - // and other operand is shifted right so that its MSB lines up with LSB of subnormal - const int power_smallest_sn1 = I - W - (1 << (E-1)); - const int power_smallest_sn2 = I2 - W2 - (1 << (E2-1)); - const int power_smallest_sn_dif1 = AC_MAX(0,power_smallest_sn2 - power_smallest_sn1); - const int power_smallest_sn_dif2 = AC_MAX(0,power_smallest_sn1 - power_smallest_sn2); - const int wc_norm_shift1 = W2-1 + AC_MIN(power_smallest_sn_dif1, W-1); - const int wc_norm_shift2 = W-1 + AC_MIN(power_smallest_sn_dif2, W2-1); - const int wc_sn_norm_shift = AC_MAX(wc_norm_shift1, wc_norm_shift2); - const int w_sn_overlap = wc_sn_norm_shift + 1; - - // cases when one operand is subnormal and other is shifted right and does not overlap bits - // subnormal op could be normalized by width-1 bits - const int w_sn_no_overlap1 = W + AC_MIN(w_r_with_round_bits, power_smallest_sn_dif2); - const int w_sn_no_overlap2 = W2 + AC_MIN(w_r_with_round_bits, power_smallest_sn_dif1); - const int w_sn_no_overlap = AC_MAX(w_sn_no_overlap1, w_sn_no_overlap2); - - const int w_sn = AC_MAX(w_sn_overlap, w_sn_no_overlap); - - // For example 0100 + (1000 0001 >> 1) = 0000 0000 1, wc_n_norm_shift = max(4,8) - const int msb0h1 = I-1 + (int) MAX_EXP; - const int msb1h1 = msb0h1-1; - const int msb0l1 = I-1 + (int) MIN_EXP; - const int msb1l1 = msb0h1-1; - const int msb0h2 = I2-1 + (int) op2_t::MAX_EXP; - const int msb1h2 = msb0h2-1; - const int msb0l2 = I2-1 + (int) op2_t::MIN_EXP; - const int msb1l2 = msb0h2-1; - // bit W-1 overlap with bit W2-2 - const bool msb_overlap1 = msb1h2 >= msb0h1 && msb0h1 <= msb1l2 - || msb1h2 >= msb0l1 && msb0l1 <= msb1l2 - || msb0h1 >= msb1h2 && msb1h2 >= msb0l1; - // bit W2-1 overlap with bit W1-2 - const bool msb_overlap2 = msb1h1 >= msb0h2 && msb0h2 <= msb1l1 - || msb1h1 >= msb0l2 && msb0l2 <= msb1l1 - || msb0h2 >= msb1h1 && msb1h1 >= msb0l2; - const bool msb_overlap = msb_overlap1 || msb_overlap2; - const int wc_n_norm_shift = AC_MAX(W,W2); - const int w_n_msb_overlap = msb_overlap ? wc_n_norm_shift + 1 : 0; - // addition of two numbers of different sign can result in a normalization by 1 (therefore + 1) - const int w_n_no_msb_overlap = w_r_with_round_bits + 1; - const int w_n = AC_MAX(w_n_msb_overlap, w_n_no_msb_overlap); - - // +1 is to prevent overflow during addition - const int tr_t_width = AC_MAX(w_n, w_sn) + 1; - typedef ac_fixed add_t; - - const int min_E = (int) MIN_EXP + I-IT; - const int min_E2 = (int) AC_FL(2)::MIN_EXP + I2-IT; - const int min_ET = AC_MIN(min_E, min_E2); - - const int max_E = (int) MAX_EXP + I-IT; - const int max_E2 = (int) AC_FL(2)::MAX_EXP + I2-IT; - const int max_ET = AC_MAX(max_E, max_E2); - - ac_fixed op1_m_0 = m; - mt_t op1_m = 0; - op1_m.set_slc(0, op1_m_0.template slc(0)); - int op1_e = exp() + I-IT; - - ac_fixed op2_m_0 = op2.m; - mt_t op2_m = 0; - op2_m.set_slc(0, op2_m_0.template slc(0)); - if(sub) - op2_m = -op2_m; - int op2_e = op2.exp() + I2-IT; - - bool op1_zero = operator !(); - bool op2_zero = !op2; - int e_dif = op1_e - op2_e; - bool e1_lt_e2 = e_dif < 0; - e_dif = (op1_zero | op2_zero) ? 0 : e1_lt_e2 ? -e_dif : e_dif; - - add_t op_lshift = e1_lt_e2 ? op1_m : op2_m; - mt_t op_no_shift = e1_lt_e2 ? op2_m : op1_m; - - bool sticky_bit = false; - if(remaining_bits_needed) { - mt_t shifted_out_bits = op_lshift; - // bits that are shifted out of a add_t (does not include potential 3 spare bits) - shifted_out_bits &= ~((~add_t(0)) << e_dif); - sticky_bit = !!shifted_out_bits; - } - op_lshift >>= e_dif; - - add_t add_r = op_lshift + op_no_shift; - int e_t = (e1_lt_e2 & !op2_zero | op1_zero ? op2_e : op1_e); - - r.template assign_from(add_r, e_t, sticky_bit, true); - } - - template - ac_float add(const AC_FL(1) &op1, const AC_FL(2) &op2) { - op1.plus_minus(op2, *this); - return *this; - } - - template - ac_float sub(const AC_FL(1) &op1, const AC_FL(2) &op2) { - op1.plus_minus(op2, *this, true); - return *this; - } - - typename rt_unary::neg abs() const { - typedef typename rt_unary::neg r_t; - r_t r; - r.m = is_neg() ? -m : r_t::mant_t(m); - r.e = e; - return r; - } - -#ifdef __AC_FLOAT_ENABLE_ALPHA - // These will be changed!!! For now only enable to explore integration with ac_complex - template - typename rt< AC_FL_TV0(2) >::plus operator +(const AC_FL(2) &op2) const { - typename rt< AC_FL_TV0(2) >::plus r; - plus_minus(op2, r); - return r; - } - template - typename rt< AC_FL_TV0(2) >::minus operator -(const AC_FL(2) &op2) const { - typename rt< AC_FL_TV0(2) >::minus r; - plus_minus(op2, r, true); - return r; - } -#endif - - template - typename rt< AC_FL_TV0(2) >::mult operator *(const AC_FL(2) &op2) const { - typedef typename rt< AC_FL_TV0(2) >::mult r_t; - r_t r(m*op2.m, exp()+op2.exp(), false); - return r; - } - - template - typename rt< AC_FL_TV0(2) >::div operator /(const AC_FL(2) &op2) const { - typename rt< AC_FL_TV0(2) >::div r(m/op2.m, exp()-op2.exp()); - return r; - } - template - ac_float &operator +=(const AC_FL(2) &op2) { - ac_float r; - plus_minus(op2, r); - *this = r; - return *this; - } - template - ac_float &operator -=(const AC_FL(2) &op2) { - ac_float r; - plus_minus(op2, r, true); - *this = r; - return *this; - } - template - ac_float &operator *=(const AC_FL(2) &op2) { - *this = *this * op2; - return *this; - } - template - ac_float &operator /=(const AC_FL(2) &op2) { - *this = *this / op2; - return *this; - } - ac_float operator + () const { - return *this; - } - typename rt_unary::neg operator - () const { - typename rt_unary::neg r; - r.m = -m; - r.e = e; - return r; - } - bool operator ! () const { - return !m; - } - - // Shift -------------------------------------------------------------------- - template - typename rt_i::lshift operator << ( const ac_int &op2 ) const { - typename rt_i::lshift r; - r.m = m; - r.e = e + op2; - return r; - } - template - typename rt_i::rshift operator >> ( const ac_int &op2 ) const { - typename rt_i::rshift r; - r.m = m; - r.e = e - op2; - return r; - } - // Shift assign ------------------------------------------------------------- - template - ac_float &operator <<= ( const ac_int &op2 ) { - *this = operator << (op2); - return *this; - } - template - ac_float &operator >>= ( const ac_int &op2 ) { - *this = operator >> (op2); - return *this; - } - - template - bool operator == (const AC_FL(2) &f) const { - bool gt; - return compare(f, >); - } - template - bool operator != (const AC_FL(2) &f) const { - return !operator == (f); - } - template - bool operator < (const AC_FL(2) &f) const { - bool gt; - bool eq = compare(f, >); - return !(eq | gt); - } - template - bool operator >= (const AC_FL(2) &f) const { - return !operator < (f); - } - template - bool operator > (const AC_FL(2) &f) const { - bool gt; - compare(f, >); - return gt; - } - template - bool operator <= (const AC_FL(2) &f) const { - return !operator > (f); - } - - inline std::string to_string(ac_base_mode base_rep, bool sign_mag = false, bool hw=true) const { - // TODO: printing decimal with exponent - if(!hw) { - ac_fixed mantissa; - mantissa.set_slc(0, m.template slc(0)); - std::string r = mantissa.to_string(base_rep, sign_mag); - r += "e2"; - r += (e + I).to_string(base_rep, sign_mag | base_rep == AC_DEC); - return r; - } else { - std::string r = m.to_string(base_rep, sign_mag); - if(base_rep != AC_DEC) - r += "_"; - r += "e2"; - if(base_rep != AC_DEC) - r += "_"; - if(E) - r += e.to_string(base_rep, sign_mag | base_rep == AC_DEC); - else - r += "0"; - return r; - } - } - - inline static std::string type_name() { - const char *tf[] = {"false", "true" }; - const char *q[] = {"AC_TRN", "AC_RND", "AC_TRN_ZERO", "AC_RND_ZERO", "AC_RND_INF", "AC_RND_MIN_INF", "AC_RND_CONV" }; - std::string r = "ac_float<"; - r += ac_int<32,true>(W).to_string(AC_DEC) + ','; - r += ac_int<32,true>(I).to_string(AC_DEC) + ','; - r += ac_int<32,true>(E).to_string(AC_DEC) + ','; - r += tf[S]; - r += ','; - r += q[Q]; - r += '>'; - return r; - } - - template - inline ac_float &set_val() { - m.template set_val(); - if(V == AC_VAL_MIN) - e.template set_val(); - else if(V == AC_VAL_QUANTUM) - e.template set_val(); - else - e.template set_val(); - return *this; - } -}; - -namespace ac_private { - template - bool ac_fpclassify(T x, bool &inf) { - bool nan = !(x==x); - if(!nan) { - T d = x - x; - inf = !(d==d); - } - return nan; - } - - inline ac_float_cdouble_t double_to_ac_float(double d) { - typedef ac_float_cdouble_t r_t; -#ifndef __SYNTHESIS__ - bool inf; - bool nan = ac_fpclassify(d, inf); - if(nan) - AC_ASSERT(0, "In conversion from double to ac_float: double is NaN"); - else if(inf) - AC_ASSERT(0, "In conversion from double to ac_float: double is Infinite"); -#endif - r_t::exp_t exp; - r_t::mant_t mant = ac::frexp_d(d, exp); - return r_t(mant, exp, false); - } - - inline ac_float_cfloat_t float_to_ac_float(float f) { - typedef ac_float_cfloat_t r_t; -#ifndef __SYNTHESIS__ - bool inf; - bool nan = ac_fpclassify(f, inf); - if(nan) - AC_ASSERT(0, "In conversion from float to ac_float: float is NaN"); - else if(inf) - AC_ASSERT(0, "In conversion from float to ac_float: float is Infinite"); -#endif - r_t::exp_t exp; - r_t::mant_t mant = ac::frexp_f(f, exp); - return r_t(mant, exp, false); - } -}; - -namespace ac { - template - struct ac_float_represent { - typedef typename ac_fixed_represent::type fx_t; - typedef ac_float type; - }; - template<> struct ac_float_represent { - typedef ac_private::ac_float_cfloat_t type; - }; - template<> struct ac_float_represent { - typedef ac_private::ac_float_cdouble_t type; - }; -} - -namespace ac_private { - // with T == ac_float - template< AC_FL_T0(2) > - struct rt_ac_float_T< AC_FL0(2) > { - typedef AC_FL0(2) fl2_t; - template< AC_FL_T0() > - struct op1 { - typedef AC_FL0() fl_t; - typedef typename fl_t::template rt< AC_FL_TV0(2) >::mult mult; - typedef typename fl_t::template rt< AC_FL_TV0(2) >::plus plus; - typedef typename fl_t::template rt< AC_FL_TV0(2) >::minus minus; - typedef typename fl2_t::template rt< AC_FL_TV0() >::minus minus2; - typedef typename fl_t::template rt< AC_FL_TV0(2) >::logic logic; - typedef typename fl_t::template rt< AC_FL_TV0(2) >::div div; - typedef typename fl2_t::template rt< AC_FL_TV0() >::div div2; - }; - }; - // with T == ac_fixed - template - struct rt_ac_float_T< ac_fixed > { - // For now E2 > 0 - enum { E2 = 1, S2 = true, W2 = WFX + !SFX, I2 = IFX + !SFX }; - typedef AC_FL0(2) fl2_t; - template< AC_FL_T0() > - struct op1 { - typedef AC_FL0() fl_t; - typedef typename fl_t::template rt< AC_FL_TV0(2) >::mult mult; - typedef typename fl_t::template rt< AC_FL_TV0(2) >::plus plus; - typedef typename fl_t::template rt< AC_FL_TV0(2) >::minus minus; - typedef typename fl2_t::template rt< AC_FL_TV0() >::minus minus2; - typedef typename fl_t::template rt< AC_FL_TV0(2) >::logic logic; - typedef typename fl_t::template rt< AC_FL_TV0(2) >::div div; - typedef typename fl2_t::template rt< AC_FL_TV0() >::div div2; - }; - }; - // with T == ac_int - template - struct rt_ac_float_T< ac_int > { - // For now E2 > 0 - enum { E2 = 1, S2 = true, I2 = WI + !SI, W2 = I2 }; - typedef AC_FL0(2) fl2_t; - template< AC_FL_T0() > - struct op1 { - typedef AC_FL0() fl_t; - typedef typename fl_t::template rt< AC_FL_TV0(2) >::mult mult; - typedef typename fl_t::template rt< AC_FL_TV0(2) >::plus plus; - typedef typename fl_t::template rt< AC_FL_TV0(2) >::minus minus; - typedef typename fl2_t::template rt< AC_FL_TV0() >::minus minus2; - typedef typename fl_t::template rt< AC_FL_TV0(2) >::logic logic; - typedef typename fl_t::template rt< AC_FL_TV0(2) >::div div; - typedef typename fl2_t::template rt< AC_FL_TV0() >::div div2; - }; - }; - - // Multiplication is optimizable, general operator +/- is not yet supported - template - struct rt_ac_float_T< c_type > { - // For now E2 > 0 - enum { SCT = c_type_params::S, S2 = true, W2 = c_type_params::W + !SCT, I2 = c_type_params::I + !SCT, E2 = AC_MAX(1, c_type_params::E) }; - typedef AC_FL0(2) fl2_t; - template< AC_FL_T0() > - struct op1 { - typedef AC_FL0() fl_t; - typedef typename fl_t::template rt< AC_FL_TV0(2) >::mult mult; - typedef typename fl_t::template rt< AC_FL_TV0(2) >::plus plus; - typedef typename fl_t::template rt< AC_FL_TV0(2) >::minus minus; - typedef typename fl2_t::template rt< AC_FL_TV0() >::minus minus2; - typedef typename fl_t::template rt< AC_FL_TV0(2) >::logic logic; - typedef typename fl_t::template rt< AC_FL_TV0(2) >::div div; - typedef typename fl2_t::template rt< AC_FL_TV0() >::div div2; - }; - }; -} - -// Stream -------------------------------------------------------------------- - -#ifndef __SYNTHESIS__ -template -inline std::ostream& operator << (std::ostream &os, const AC_FL() &x) { - os << x.to_string(AC_DEC); - return os; -} -#endif - -#define FL_BIN_OP_WITH_CTYPE(BIN_OP, C_TYPE, RTYPE) \ - template< AC_FL_T() > \ - inline typename AC_FL()::template rt_T2::RTYPE operator BIN_OP ( C_TYPE c_op, const AC_FL() &op) { \ - typedef typename ac::template ac_float_represent::type fl2_t; \ - return fl2_t(c_op).operator BIN_OP (op); \ - } \ - template< AC_FL_T() > \ - inline typename AC_FL()::template rt_T::RTYPE operator BIN_OP ( const AC_FL() &op, C_TYPE c_op) { \ - typedef typename ac::template ac_float_represent::type fl2_t; \ - return op.operator BIN_OP (fl2_t(c_op)); \ - } - -#define FL_REL_OP_WITH_CTYPE(REL_OP, C_TYPE) \ - template< AC_FL_T() > \ - inline bool operator REL_OP ( const AC_FL() &op, C_TYPE op2) { \ - typedef typename ac::template ac_float_represent::type fl2_t; \ - return op.operator REL_OP (fl2_t(op2)); \ - } \ - template< AC_FL_T() > \ - inline bool operator REL_OP ( C_TYPE op2, const AC_FL() &op) { \ - typedef typename ac::template ac_float_represent::type fl2_t; \ - return fl2_t(op2).operator REL_OP (op); \ - } - -#define FL_ASSIGN_OP_WITH_CTYPE_2(ASSIGN_OP, C_TYPE) \ - template< AC_FL_T() > \ - inline AC_FL() &operator ASSIGN_OP ( AC_FL() &op, C_TYPE op2) { \ - typedef typename ac::template ac_float_represent::type fl2_t; \ - return op.operator ASSIGN_OP (fl2_t(op2)); \ - } - -#ifdef __AC_FLOAT_ENABLE_ALPHA -#define FL_BIN_OP_WITH_CTYPE_ALPHA(C_TYPE) \ - FL_BIN_OP_WITH_CTYPE(+, C_TYPE, plus) \ - FL_BIN_OP_WITH_CTYPE(-, C_TYPE, minus) -#else -#define FL_BIN_OP_WITH_CTYPE_ALPHA(C_TYPE) -#endif - -#define FL_OPS_WITH_CTYPE(C_TYPE) \ - FL_BIN_OP_WITH_CTYPE_ALPHA(C_TYPE) \ - FL_BIN_OP_WITH_CTYPE(*, C_TYPE, mult) \ - FL_BIN_OP_WITH_CTYPE(/, C_TYPE, div) \ - \ - FL_REL_OP_WITH_CTYPE(==, C_TYPE) \ - FL_REL_OP_WITH_CTYPE(!=, C_TYPE) \ - FL_REL_OP_WITH_CTYPE(>, C_TYPE) \ - FL_REL_OP_WITH_CTYPE(>=, C_TYPE) \ - FL_REL_OP_WITH_CTYPE(<, C_TYPE) \ - FL_REL_OP_WITH_CTYPE(<=, C_TYPE) \ - \ - FL_ASSIGN_OP_WITH_CTYPE_2(+=, C_TYPE) \ - FL_ASSIGN_OP_WITH_CTYPE_2(-=, C_TYPE) \ - FL_ASSIGN_OP_WITH_CTYPE_2(*=, C_TYPE) \ - FL_ASSIGN_OP_WITH_CTYPE_2(/=, C_TYPE) - -#define FL_SHIFT_OP_WITH_INT_CTYPE(BIN_OP, C_TYPE, RTYPE) \ - template< AC_FL_T() > \ - inline typename AC_FL()::template rt_i< ac_private::c_type_params::W, ac_private::c_type_params::S >::RTYPE operator BIN_OP ( const AC_FL() &op, C_TYPE i_op) { \ - typedef typename ac::template ac_int_represent::type i_t; \ - return op.operator BIN_OP (i_t(i_op)); \ - } - -#define FL_SHIFT_ASSIGN_OP_WITH_INT_CTYPE(ASSIGN_OP, C_TYPE) \ - template< AC_FL_T() > \ - inline AC_FL() &operator ASSIGN_OP ( AC_FL() &op, C_TYPE i_op) { \ - typedef typename ac::template ac_int_represent::type i_t; \ - return op.operator ASSIGN_OP (i_t(i_op)); \ - } - -#define FL_SHIFT_OPS_WITH_INT_CTYPE(C_TYPE) \ - FL_SHIFT_OP_WITH_INT_CTYPE(>>, C_TYPE, rshift) \ - FL_SHIFT_OP_WITH_INT_CTYPE(<<, C_TYPE, lshift) \ - FL_SHIFT_ASSIGN_OP_WITH_INT_CTYPE(>>=, C_TYPE) \ - FL_SHIFT_ASSIGN_OP_WITH_INT_CTYPE(<<=, C_TYPE) - -#define FL_OPS_WITH_INT_CTYPE(C_TYPE) \ - FL_OPS_WITH_CTYPE(C_TYPE) \ - FL_SHIFT_OPS_WITH_INT_CTYPE(C_TYPE) - -// --------------------------------------- End of Macros for Binary Operators with C Floats - - // Binary Operators with C Floats -------------------------------------------- - FL_OPS_WITH_CTYPE(float) - FL_OPS_WITH_CTYPE(double) - FL_OPS_WITH_INT_CTYPE(bool) - FL_OPS_WITH_INT_CTYPE(char) - FL_OPS_WITH_INT_CTYPE(signed char) - FL_OPS_WITH_INT_CTYPE(unsigned char) - FL_OPS_WITH_INT_CTYPE(short) - FL_OPS_WITH_INT_CTYPE(unsigned short) - FL_OPS_WITH_INT_CTYPE(int) - FL_OPS_WITH_INT_CTYPE(unsigned int) - FL_OPS_WITH_INT_CTYPE(long) - FL_OPS_WITH_INT_CTYPE(unsigned long) - FL_OPS_WITH_INT_CTYPE(Slong) - FL_OPS_WITH_INT_CTYPE(Ulong) - // -------------------------------------- End of Binary Operators with C Floats - -// Macros for Binary Operators with ac_int -------------------------------------------- - -#define FL_BIN_OP_WITH_AC_INT_1(BIN_OP, RTYPE) \ - template< AC_FL_T(), int WI, bool SI> \ - inline typename AC_FL()::template rt_T2< ac_int >::RTYPE operator BIN_OP ( const ac_int &i_op, const AC_FL() &op) { \ - typedef typename ac::template ac_float_represent< ac_int >::type fl2_t; \ - return fl2_t(i_op).operator BIN_OP (op); \ - } - -#define FL_BIN_OP_WITH_AC_INT_2(BIN_OP, RTYPE) \ - template< AC_FL_T(), int WI, bool SI> \ - inline typename AC_FL()::template rt_T2< ac_int >::RTYPE operator BIN_OP ( const AC_FL() &op, const ac_int &i_op) { \ - typedef typename ac::template ac_float_represent< ac_int >::type fl2_t; \ - return op.operator BIN_OP (fl2_t(i_op)); \ - } - -#define FL_BIN_OP_WITH_AC_INT(BIN_OP, RTYPE) \ - FL_BIN_OP_WITH_AC_INT_1(BIN_OP, RTYPE) \ - FL_BIN_OP_WITH_AC_INT_2(BIN_OP, RTYPE) - -#define FL_REL_OP_WITH_AC_INT(REL_OP) \ - template< AC_FL_T(), int WI, bool SI> \ - inline bool operator REL_OP ( const AC_FL() &op, const ac_int &op2) { \ - typedef typename ac::template ac_float_represent< ac_int >::type fl2_t; \ - return op.operator REL_OP (fl2_t(op2)); \ - } \ - template< AC_FL_T(), int WI, bool SI> \ - inline bool operator REL_OP ( ac_int &op2, const AC_FL() &op) { \ - typedef typename ac::template ac_float_represent< ac_int >::type fl2_t; \ - return fl2_t(op2).operator REL_OP (op); \ - } - -#define FL_ASSIGN_OP_WITH_AC_INT(ASSIGN_OP) \ - template< AC_FL_T(), int WI, bool SI> \ - inline AC_FL() &operator ASSIGN_OP ( AC_FL() &op, const ac_int &op2) { \ - typedef typename ac::template ac_float_represent< ac_int >::type fl2_t; \ - return op.operator ASSIGN_OP (fl2_t(op2)); \ - } - -// -------------------------------------------- End of Macros for Binary Operators with ac_int - - // Binary Operators with ac_int -------------------------------------------- -#ifdef __AC_FLOAT_ENABLE_ALPHA - FL_BIN_OP_WITH_AC_INT(+, plus) - FL_BIN_OP_WITH_AC_INT(-, minus) -#endif - FL_BIN_OP_WITH_AC_INT(*, mult) - FL_BIN_OP_WITH_AC_INT(/, div) - - FL_REL_OP_WITH_AC_INT(==) - FL_REL_OP_WITH_AC_INT(!=) - FL_REL_OP_WITH_AC_INT(>) - FL_REL_OP_WITH_AC_INT(>=) - FL_REL_OP_WITH_AC_INT(<) - FL_REL_OP_WITH_AC_INT(<=) - - FL_ASSIGN_OP_WITH_AC_INT(+=) - FL_ASSIGN_OP_WITH_AC_INT(-=) - FL_ASSIGN_OP_WITH_AC_INT(*=) - FL_ASSIGN_OP_WITH_AC_INT(/=) - FL_ASSIGN_OP_WITH_AC_INT(%=) - // -------------------------------------- End of Binary Operators with ac_int - -// Macros for Binary Operators with ac_fixed -------------------------------------------- - -#define FL_BIN_OP_WITH_AC_FIXED_1(BIN_OP, RTYPE) \ - template< AC_FL_T(), int WF, int IF, bool SF, ac_q_mode QF, ac_o_mode OF> \ - inline typename AC_FL()::template rt_T2< ac_fixed >::RTYPE operator BIN_OP ( const ac_fixed &f_op, const AC_FL() &op) { \ - typedef typename ac::template ac_float_represent< ac_fixed >::type fl2_t; \ - return fl2_t(f_op).operator BIN_OP (op); \ - } - -#define FL_BIN_OP_WITH_AC_FIXED_2(BIN_OP, RTYPE) \ - template< AC_FL_T(), int WF, int IF, bool SF, ac_q_mode QF, ac_o_mode OF> \ - inline typename AC_FL()::template rt_T2< ac_fixed >::RTYPE operator BIN_OP ( const AC_FL() &op, const ac_fixed &f_op) { \ - typedef typename ac::template ac_float_represent< ac_fixed >::type fl2_t; \ - return op.operator BIN_OP (fl2_t(f_op)); \ - } - -#define FL_BIN_OP_WITH_AC_FIXED(BIN_OP, RTYPE) \ - FL_BIN_OP_WITH_AC_FIXED_1(BIN_OP, RTYPE) \ - FL_BIN_OP_WITH_AC_FIXED_2(BIN_OP, RTYPE) - -#define FL_REL_OP_WITH_AC_FIXED(REL_OP) \ - template< AC_FL_T(), int WF, int IF, bool SF, ac_q_mode QF, ac_o_mode OF> \ - inline bool operator REL_OP ( const AC_FL() &op, const ac_fixed &op2) { \ - typedef typename ac::template ac_float_represent< ac_fixed >::type fl2_t; \ - return op.operator REL_OP (fl2_t(op2)); \ - } \ - template< AC_FL_T(), int WF, int IF, bool SF, ac_q_mode QF, ac_o_mode OF> \ - inline bool operator REL_OP ( ac_fixed &op2, const AC_FL() &op) { \ - typedef typename ac::template ac_float_represent< ac_fixed >::type fl2_t; \ - return fl2_t(op2).operator REL_OP (op); \ - } - -#define FL_ASSIGN_OP_WITH_AC_FIXED(ASSIGN_OP) \ - template< AC_FL_T(), int WF, int IF, bool SF, ac_q_mode QF, ac_o_mode OF> \ - inline AC_FL() &operator ASSIGN_OP ( AC_FL() &op, const ac_fixed &op2) { \ - typedef typename ac::template ac_float_represent< ac_fixed >::type fl2_t; \ - return op.operator ASSIGN_OP (fl2_t(op2)); \ - } - -// -------------------------------------------- End of Macros for Binary Operators with ac_fixed - - // Binary Operators with ac_fixed -------------------------------------------- -#ifdef __AC_FLOAT_ENABLE_ALPHA - FL_BIN_OP_WITH_AC_FIXED(+, plus) - FL_BIN_OP_WITH_AC_FIXED(-, minus) -#endif - FL_BIN_OP_WITH_AC_FIXED(*, mult) - FL_BIN_OP_WITH_AC_FIXED(/, div) - - FL_REL_OP_WITH_AC_FIXED(==) - FL_REL_OP_WITH_AC_FIXED(!=) - FL_REL_OP_WITH_AC_FIXED(>) - FL_REL_OP_WITH_AC_FIXED(>=) - FL_REL_OP_WITH_AC_FIXED(<) - FL_REL_OP_WITH_AC_FIXED(<=) - - FL_ASSIGN_OP_WITH_AC_FIXED(+=) - FL_ASSIGN_OP_WITH_AC_FIXED(-=) - FL_ASSIGN_OP_WITH_AC_FIXED(*=) - FL_ASSIGN_OP_WITH_AC_FIXED(/=) - // -------------------------------------- End of Binary Operators with ac_fixed - -// Global templatized functions for easy initialization to special values -template -inline AC_FL() value( AC_FL() ) { - AC_FL() r; - return r.template set_val(); -} - -namespace ac { -// function to initialize (or uninitialize) arrays - template - inline bool init_array( AC_FL() *a, int n) { - AC_FL0() t; - t.template set_val(); - for(int i=0; i < n; i++) - a[i] = t; - return true; - } -} - -/////////////////////////////////////////////////////////////////////////////// - -#if (defined(_MSC_VER) && !defined(__EDG__)) -#pragma warning( pop ) -#endif -#if (defined(__GNUC__) && ( __GNUC__ == 4 && __GNUC_MINOR__ >= 6 || __GNUC__ > 4 ) && !defined(__EDG__)) -#pragma GCC diagnostic pop -#endif -#if defined(__clang__) -#pragma clang diagnostic pop -#endif - -#ifdef __AC_NAMESPACE -} -#endif - -#endif // __AC_FLOAT_H diff --git a/hls4ml/templates/quartus/ac_types/ac_int.h b/hls4ml/templates/quartus/ac_types/ac_int.h deleted file mode 100644 index 4651339169..0000000000 --- a/hls4ml/templates/quartus/ac_types/ac_int.h +++ /dev/null @@ -1,3099 +0,0 @@ -/************************************************************************** - * * - * Algorithmic C (tm) Datatypes * - * * - * Software Version: 4.0 * - * * - * Release Date : Sat Jun 13 12:35:18 PDT 2020 * - * Release Type : Production Release * - * Release Build : 4.0.0 * - * * - * Copyright 2004-2020, Mentor Graphics Corporation, * - * * - * All Rights Reserved. * - * * - ************************************************************************** - * Licensed under the Apache License, Version 2.0 (the "License"); * - * you may not use this file except in compliance with the License. * - * You may obtain a copy of the License at * - * * - * http://www.apache.org/licenses/LICENSE-2.0 * - * * - * Unless required by applicable law or agreed to in writing, software * - * distributed under the License is distributed on an "AS IS" BASIS, * - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * - * implied. * - * See the License for the specific language governing permissions and * - * limitations under the License. * - ************************************************************************** - * * - * The most recent version of this package is available at github. * - * * - *************************************************************************/ - -/* -// Source: ac_int.h -// Description: fast arbitrary-length bit-accurate integer types: -// - unsigned integer of length W: ac_int -// - signed integer of length W: ac_int -// Author: Andres Takach, Ph.D. -// Notes: -// - C++ Runtime: important to use optimization flag (for example -O3) -// -// - Compiler support: recent GNU compilers are required for correct -// template compilation -// -// - Most frequent migration issues: -// - need to cast to common type when using question mark operator: -// (a < 0) ? -a : a; // a is ac_int -// change to: -// (a < 0) ? -a : (ac_int) a; -// or -// (a < 0) ? (ac_int) -a : (ac_int) a; -// -// - left shift is not arithmetic ("a< b = a << 1; // a is ac_int -// is not equivalent to b=2*a. In order to get 2*a behavior change to: -// ac_int b = (ac_int)a << 1; -// -// - only static length read/write slices are supported: -// - read: x.slc<4>(k) => returns ac_int for 4-bit slice x(4+k-1 DOWNTO k) -// - write: x.set_slc(k,y) = writes bits of y to x starting at index k -*/ - -#ifndef __AC_INT_H -#define __AC_INT_H - -#define AC_VERSION 3 -#define AC_VERSION_MINOR 9 - -#ifndef __cplusplus -#error C++ is required to include this header file -#endif - -#if (defined(__GNUC__) && __GNUC__ < 3 && !defined(__EDG__)) -#error GCC version 3 or greater is required to include this header file -#endif - -#if (defined(_MSC_VER) && _MSC_VER < 1400 && !defined(__EDG__)) -#error Microsoft Visual Studio 8 or newer is required to include this header file -#endif - -#if (defined(_MSC_VER) && !defined(__EDG__)) -#pragma warning( push ) -#pragma warning( disable: 4127 4100 4244 4307 4310 4365 4514 4554 4706 4800 ) -#endif - -// for safety -#if (defined(N) || defined(N2)) -#error One or more of the following is defined: N, N2. Definition conflicts with their usage as template parameters. -#error DO NOT use defines before including third party header files. -#endif - -// for safety -#if (defined(W) || defined(I) || defined(S) || defined(W2) || defined(I2) || defined(S2)) -#error One or more of the following is defined: W, I, S, W2, I2, S2. Definition conflicts with their usage as template parameters. -#error DO NOT use defines before including third party header files. -#endif - -#if defined(true) -#warning The C++ keyword true is defined which may result in subtle compilation problems. Undefining it. -#undef true -#endif -#if defined(false) -#warning The C++ keyword false is defined which may result in subtle compilation problems. Undefining it. -#undef false -#endif - -#ifndef __ASSERT_H__ -#define __ASSERT_H__ -#include -#endif -#include -#ifndef AC_USER_DEFINED_ASSERT -#include -#else -#include -#endif -#include -#include - -#ifndef __SYNTHESIS__ -#ifndef __AC_INT_UTILITY_BASE -#define __AC_INT_UTILITY_BASE -#endif - -#endif - -#ifdef __AC_NAMESPACE -namespace __AC_NAMESPACE { -#endif - -#define AC_MAX(a,b) ((a) > (b) ? (a) : (b)) -#define AC_MIN(a,b) ((a) < (b) ? (a) : (b)) -#define AC_ABS(a) ((a) < 0 ? -(a) : (a)) - -#if defined(_MSC_VER) -typedef unsigned __int64 Ulong; -typedef signed __int64 Slong; -#else -typedef unsigned long long Ulong; -typedef signed long long Slong; -#endif - -enum ac_base_mode { AC_BIN=2, AC_OCT=8, AC_DEC=10, AC_HEX=16 }; -enum ac_special_val {AC_VAL_DC, AC_VAL_0, AC_VAL_MIN, AC_VAL_MAX, AC_VAL_QUANTUM}; - -template class ac_int; - -namespace ac_private { -#if defined(__SYNTHESIS__) && !defined(AC_IGNORE_BUILTINS) -#pragma builtin -#endif - - enum {long_w = std::numeric_limits::digits}; - const unsigned int all_ones = (unsigned) ~0; - - // PRIVATE FUNCTIONS in namespace: for implementing ac_int/ac_fixed - -#ifndef __SYNTHESIS__ - inline double mgc_floor(double d) { return floor(d); } -#else - inline double mgc_floor(double d) { return 0.0; } -#endif - - #define AC_ASSERT(cond, msg) ac_private::ac_assert(cond, __FILE__, __LINE__, msg) - inline void ac_assert(bool condition, const char *file=0, int line=0, const char *msg=0) { - #ifndef __SYNTHESIS__ - #ifndef AC_USER_DEFINED_ASSERT - if(!condition) { - std::cerr << "Assert"; - if(file) - std::cerr << " in file " << file << ":" << line; - if(msg) - std::cerr << " " << msg; - std::cerr << std::endl; - assert(0); - } - #else - AC_USER_DEFINED_ASSERT(condition, file, line, msg); - #endif - #endif - } - - // helper structs for statically computing log2 like functions (nbits, log2_floor, log2_ceil) - // using recursive templates - template - struct s_N { - template - struct s_X { - enum { - X2 = X >> N, - N_div_2 = N >> 1, - nbits = X ? (X2 ? N + (int) s_N::template s_X::nbits : (int) s_N::template s_X::nbits) : 0 - }; - }; - }; - template<> struct s_N<0> { - template - struct s_X { - enum {nbits = !!X }; - }; - }; - - template - inline double ldexpr32(double d) { - double d2 = d; - if(N < 0) - for(int i=0; i < -N; i++) - d2 /= (Ulong) 1 << 32; - else - for(int i=0; i < N; i++) - d2 *= (Ulong) 1 << 32; - return d2; - } - template<> inline double ldexpr32<0>(double d) { return d; } - template<> inline double ldexpr32<1>(double d) { return d * ((Ulong) 1 << 32); } - template<> inline double ldexpr32<-1>(double d) { return d / ((Ulong) 1 << 32); } - template<> inline double ldexpr32<2>(double d) { return (d * ((Ulong) 1 << 32)) * ((Ulong) 1 << 32); } - template<> inline double ldexpr32<-2>(double d) { return (d / ((Ulong) 1 << 32)) / ((Ulong) 1 << 32); } - - template - inline double ldexpr(double d) { - return ldexpr32( N < 0 ? d/( (unsigned) 1 << (-N & 31)) : d * ( (unsigned) 1 << (N & 31))); - } - - template - inline void iv_copy(const int *op, int *r) { - for(int i=0; i < N; i++) - r[i] = op[i]; - } - template<> inline void iv_copy<1>(const int *op, int *r) { - r[0] = op[0]; - } - template<> inline void iv_copy<2>(const int *op, int *r) { - r[0] = op[0]; - r[1] = op[1]; - } - - template - inline bool iv_equal_zero(const int *op){ - for(int i=0; i < N; i++) - if(op[i]) - return false; - return true; - } - template<> inline bool iv_equal_zero<0>(const int * /*op*/) { return true; } - template<> inline bool iv_equal_zero<1>(const int *op) { - return !op[0]; - } - template<> inline bool iv_equal_zero<2>(const int *op) { - return !(op[0] || op[1]); - } - - template - inline bool iv_equal_ones(const int *op){ - for(int i=0; i < N; i++) - if(~op[i]) - return false; - return true; - } - template<> inline bool iv_equal_ones<0>(const int * /*op*/) { return true; } - template<> inline bool iv_equal_ones<1>(const int *op) { - return !~op[0]; - } - template<> inline bool iv_equal_ones<2>(const int *op) { - return !(~op[0] || ~op[1]); - } - - template - inline bool iv_equal(const int *op1, const int *op2){ - const int M1 = AC_MAX(N1,N2); - const int M2 = AC_MIN(N1,N2); - const int *OP1 = N1 >= N2 ? op1 : op2; - const int *OP2 = N1 >= N2 ? op2 : op1; - for(int i=0; i < M2; i++) - if(OP1[i] != OP2[i]) - return false; - int ext = OP2[M2-1] < 0 ? ~0 : 0; - for(int i=M2; i < M1; i++) - if(OP1[i] != ext) - return false; - return true; - } - template<> inline bool iv_equal<1,1>(const int *op1, const int *op2) { - return op1[0] == op2[0]; - } - - template - inline bool iv_equal_ones_from(const int *op){ - if((B >= 32*N && op[N-1] >= 0) || (B&31 && ~(op[B/32] >> (B&31)))) - return false; - return iv_equal_ones(&op[(B+31)/32]); - } - template<> inline bool iv_equal_ones_from<0,1>(const int *op){ - return iv_equal_ones<1>(op); - } - template<> inline bool iv_equal_ones_from<0,2>(const int *op){ - return iv_equal_ones<2>(op); - } - - template - inline bool iv_equal_zeros_from(const int *op){ - if((B >= 32*N && op[N-1] < 0) || (B&31 && (op[B/32] >> (B&31)))) - return false; - return iv_equal_zero(&op[(B+31)/32]); - } - template<> inline bool iv_equal_zeros_from<0,1>(const int *op){ - return iv_equal_zero<1>(op); - } - template<> inline bool iv_equal_zeros_from<0,2>(const int *op){ - return iv_equal_zero<2>(op); - } - - template - inline bool iv_equal_ones_to(const int *op){ - if((B >= 32*N && op[N-1] >= 0) || (B&31 && ~(op[B/32] | (all_ones << (B&31))))) - return false; - return iv_equal_ones(op); - } - template<> inline bool iv_equal_ones_to<0,1>(const int *op){ - return iv_equal_ones<1>(op); - } - template<> inline bool iv_equal_ones_to<0,2>(const int *op){ - return iv_equal_ones<2>(op); - } - - template - inline bool iv_equal_zeros_to(const int *op){ - if((B >= 32*N && op[N-1] < 0) || (B&31 && (op[B/32] & ~(all_ones << (B&31))))) - return false; - return iv_equal_zero(op); - } - template<> inline bool iv_equal_zeros_to<0,1>(const int *op){ - return iv_equal_zero<1>(op); - } - template<> inline bool iv_equal_zeros_to<0,2>(const int *op){ - return iv_equal_zero<2>(op); - } - - template - inline bool iv_compare(const int *op1, const int *op2){ - const int M1 = AC_MAX(N1,N2); - const int M2 = AC_MIN(N1,N2); - const int *OP1 = N1 >= N2 ? op1 : op2; - const int *OP2 = N1 >= N2 ? op2 : op1; - const bool b = (N1 >= N2) == greater; - int ext = OP2[M2-1] < 0 ? ~0 : 0; - int i2 = M1 > M2 ? ext : OP2[M1-1]; - if(OP1[M1-1] != i2) - return b ^ (OP1[M1-1] < i2); - for(int i=M1-2; i >= M2; i--) { - if((unsigned) OP1[i] != (unsigned) ext) - return b ^ ((unsigned) OP1[i] < (unsigned) ext); - } - for(int i=M2-1; i >= 0; i--) { - if((unsigned) OP1[i] != (unsigned) OP2[i]) - return b ^ ((unsigned) OP1[i] < (unsigned) OP2[i]); - } - return false; - } - template<> inline bool iv_compare<1,1,true>(const int *op1, const int *op2) { - return op1[0] > op2[0]; - } - template<> inline bool iv_compare<1,1,false>(const int *op1, const int *op2) { - return op1[0] < op2[0]; - } - - template - inline void iv_extend(int *r, int ext) { - for(int i=0; i < N; i++) - r[i] = ext; - } - template<> inline void iv_extend<-2>(int * /*r*/, int /*ext*/) { } - template<> inline void iv_extend<-1>(int * /*r*/, int /*ext*/) { } - template<> inline void iv_extend<0>(int * /*r*/, int /*ext*/) { } - template<> inline void iv_extend<1>(int *r, int ext) { - r[0] = ext; - } - template<> inline void iv_extend<2>(int *r, int ext) { - r[0] = ext; - r[1] = ext; - } - - template - inline void iv_assign_int64(int *r, Slong l) { - r[0] = (int) l; - if(Nr > 1) { - r[1] = (int) (l >> 32); - iv_extend(r+2, (r[1] < 0) ? ~0 : 0); - } - } - template<> inline void iv_assign_int64<1>(int *r, Slong l) { - r[0] = (int) l; - } - template<> inline void iv_assign_int64<2>(int *r, Slong l) { - r[0] = (int) l; - r[1] = (int) (l >> 32); - } - - template - inline void iv_assign_uint64(int *r, Ulong l) { - r[0] = (int) l; - if(Nr > 1) { - r[1] = (int) (l >> 32); - iv_extend(r+2, 0); - } - } - template<> inline void iv_assign_uint64<1>(int *r, Ulong l) { - r[0] = (int) l; - } - template<> inline void iv_assign_uint64<2>(int *r, Ulong l) { - r[0] = (int) l; - r[1] = (int) (l >> 32); - } - - inline Ulong mult_u_u(int a, int b) { - return (Ulong) (unsigned) a * (Ulong) (unsigned) b; - } - inline Slong mult_u_s(int a, int b) { - return (Ulong) (unsigned) a * (Slong) (signed) b; - } - inline Slong mult_s_u(int a, int b) { - return (Slong) (signed) a * (Ulong) (unsigned) b; - } - inline Slong mult_s_s(int a, int b) { - return (Slong) (signed) a * (Slong) (signed) b; - } - inline void accumulate(Ulong a, Ulong &l1, Slong &l2) { - l1 += (Ulong) (unsigned) a; - l2 += a >> 32; - } - inline void accumulate(Slong a, Ulong &l1, Slong &l2) { - l1 += (Ulong) (unsigned) a; - l2 += a >> 32; - } - - template - inline void iv_mult(const int *op1, const int *op2, int *r) { - if(Nr==1) - r[0] = op1[0] * op2[0]; - else if(N1==1 && N2==1) - iv_assign_int64(r, ((Slong) op1[0]) * ((Slong) op2[0])); - else { - const int M1 = AC_MAX(N1,N2); - const int M2 = AC_MIN(N1,N2); - const int *OP1 = N1 >= N2 ? op1 : op2; - const int *OP2 = N1 >= N2 ? op2 : op1; - const int T1 = AC_MIN(M2-1,Nr); - const int T2 = AC_MIN(M1-1,Nr); - const int T3 = AC_MIN(M1+M2-2,Nr); - - Ulong l1 = 0; - Slong l2 = 0; - for(int k=0; k < T1; k++) { - for(int i=0; i < k+1; i++) - accumulate(mult_u_u(OP1[k-i], OP2[i]), l1, l2); - l2 += (Ulong) (unsigned) (l1 >> 32); - r[k] = (int) l1; - l1 = (unsigned) l2; - l2 >>= 32; - } - for(int k=T1; k < T2; k++) { - accumulate(mult_u_s(OP1[k-M2+1], OP2[M2-1]), l1, l2); - for(int i=0; i < M2-1; i++) - accumulate(mult_u_u(OP1[k-i], OP2[i]), l1, l2); - l2 += (Ulong) (unsigned) (l1 >> 32); - r[k] = (int) l1; - l1 = (unsigned) l2; - l2 >>= 32; - } - for(int k=T2; k < T3; k++) { - accumulate(mult_u_s(OP1[k-M2+1], OP2[M2-1]), l1, l2); - for(int i=k-T2+1; i < M2-1; i++) - accumulate(mult_u_u(OP1[k-i], OP2[i]), l1, l2); - accumulate(mult_s_u(OP1[M1-1], OP2[k-M1+1]), l1, l2); - l2 += (Ulong) (unsigned) (l1 >> 32); - r[k] = (int) l1; - l1 = (unsigned) l2; - l2 >>= 32; - } - if(Nr >= M1+M2-1) { - accumulate(mult_s_s(OP1[M1-1], OP2[M2-1]), l1, l2); - r[M1+M2-2] = (int) l1; - if(Nr >= M1+M2) { - l2 += (Ulong) (unsigned) (l1 >> 32); - r[M1+M2-1] = (int) l2; - iv_extend(r+M1+M2, (r[M1+M2-1] < 0) ? ~0 : 0); - } - } - } - } - template<> inline void iv_mult<1,1,1>(const int *op1, const int *op2, int *r) { - r[0] = op1[0] * op2[0]; - } - template<> inline void iv_mult<1,1,2>(const int *op1, const int *op2, int *r) { - iv_assign_int64<2>(r, ((Slong) op1[0]) * ((Slong) op2[0])); - } - - template - inline bool iv_uadd_carry(const int *op1, bool carry, int *r) { - Slong l = carry; - for(int i=0; i < N; i++) { - l += (Ulong) (unsigned) op1[i]; - r[i] = (int) l; - l >>= 32; - } - return l != 0; - } - template<> inline bool iv_uadd_carry<0>(const int * /*op1*/, bool carry, int * /*r*/) { return carry; } - template<> inline bool iv_uadd_carry<1>(const int *op1, bool carry, int *r) { - Ulong l = carry + (Ulong) (unsigned) op1[0]; - r[0] = (int) l; - return (l >> 32) & 1; - } - - template - inline bool iv_add_int_carry(const int *op1, int op2, bool carry, int *r) { - if(N==0) - return carry; - if(N==1) { - Ulong l = carry + (Slong) op1[0] + (Slong) op2; - r[0] = (int) l; - return (l >> 32) & 1; - } - Slong l = carry + (Ulong) (unsigned) op1[0] + (Slong) op2; - r[0] = (int) l; - l >>= 32; - for(int i=1; i < N-1; i++) { - l += (Ulong) (unsigned) op1[i]; - r[i] = (int) l; - l >>= 32; - } - l += (Slong) op1[N-1]; - r[N-1] = (int) l; - return (l >> 32) & 1; - } - template<> inline bool iv_add_int_carry<0>(const int * /*op1*/, int /*op2*/, bool carry, int * /*r*/) { return carry; } - template<> inline bool iv_add_int_carry<1>(const int *op1, int op2, bool carry, int *r) { - Ulong l = carry + (Slong) op1[0] + (Slong) op2; - r[0] = (int) l; - return (l >> 32) & 1; - } - - template - inline bool iv_uadd_n(const int *op1, const int *op2, int *r) { - Ulong l = 0; - for(int i=0; i < N; i++) { - l += (Ulong)(unsigned) op1[i] + (Ulong)(unsigned) op2[i]; - r[i] = (int) l; - l >>= 32; - } - return l & 1; - } - template<> inline bool iv_uadd_n<0>(const int * /*op1*/, const int * /*op2*/, int * /*r*/) { return false; } - template<> inline bool iv_uadd_n<1>(const int *op1, const int *op2, int *r) { - Ulong l = (Ulong) (unsigned) op1[0] + (Ulong) (unsigned) op2[0]; - r[0] = (int) l; - return (l >> 32) & 1; - } - template<> inline bool iv_uadd_n<2>(const int *op1, const int *op2, int *r) { - Ulong l = (Ulong) (unsigned) op1[0] + (Ulong) (unsigned) op2[0]; - r[0] = (int) l; - l >>= 32; - l += (Ulong) (unsigned) op1[1] + (Ulong) (unsigned) op2[1]; - r[1] = (int) l; - return (l >> 32) & 1; - } - - template - inline void iv_add(const int *op1, const int *op2, int *r) { - if(Nr==1) - r[0] = op1[0] + op2[0]; - else { - const int M1 = AC_MAX(N1,N2); - const int M2 = AC_MIN(N1,N2); - const int *OP1 = N1 >= N2 ? op1 : op2; - const int *OP2 = N1 >= N2 ? op2 : op1; - const int T1 = AC_MIN(M2-1,Nr); - const int T2 = AC_MIN(M1,Nr); - - bool carry = iv_uadd_n(OP1, OP2, r); - carry = iv_add_int_carry(OP1+T1, OP2[T1], carry, r+T1); - iv_extend(r+T2, carry ? ~0 : 0); - } - } - template<> inline void iv_add<1,1,1>(const int *op1, const int *op2, int *r) { - r[0] = op1[0] + op2[0]; - } - template<> inline void iv_add<1,1,2>(const int *op1, const int *op2, int *r) { - iv_assign_int64<2>(r, (Slong) op1[0] + (Slong) op2[0]); - } - - template - inline bool iv_sub_int_borrow(const int *op1, int op2, bool borrow, int *r) { - if(N==1) { - Ulong l = (Slong) op1[0] - (Slong) op2 - borrow; - r[0] = (int) l; - return (l >> 32) & 1; - } - Slong l = (Ulong) (unsigned) op1[0] - (Slong) op2 - borrow; - r[0] = (int) l; - l >>= 32; - for(int i=1; i < N-1; i++) { - l += (Ulong) (unsigned) op1[i]; - r[i] = (int) l; - l >>= 32; - } - l += (Slong) op1[N-1]; - r[N-1] = (int) l; - return (l >> 32) & 1; - } - template<> inline bool iv_sub_int_borrow<0>(const int * /*op1*/, int /*op2*/, bool borrow, int * /*r*/) { return borrow; } - template<> inline bool iv_sub_int_borrow<1>(const int *op1, int op2, bool borrow, int *r) { - Ulong l = (Slong) op1[0] - (Slong) op2 - borrow; - r[0] = (int) l; - return (l >> 32) & 1; - } - - template - inline bool iv_sub_int_borrow(int op1, const int *op2, bool borrow, int *r) { - if(N==1) { - Ulong l = (Slong) op1 - (Slong) op2[0] - borrow; - r[0] = (int) l; - return (l >> 32) & 1; - } - Slong l = (Slong) op1 - (Ulong) (unsigned) op2[0] - borrow; - r[0] = (int) l; - l >>= 32; - for(int i=1; i < N-1; i++) { - l -= (Ulong) (unsigned) op2[i]; - r[i] = (int) l; - l >>= 32; - } - l -= (Slong) op2[N-1]; - r[N-1] = (int) l; - return (l >> 32) & 1; - } - template<> inline bool iv_sub_int_borrow<0>(int /*op1*/, const int * /*op2*/, bool borrow, int * /*r*/) { return borrow; } - template<> inline bool iv_sub_int_borrow<1>(int op1, const int *op2, bool borrow, int *r) { - Ulong l = (Slong) op1 - (Slong) op2[0] - borrow; - r[0] = (int) l; - return (l >> 32) & 1; - } - - template - inline bool iv_usub_n(const int *op1, const int *op2, int *r) { - Slong l = 0; - for(int i=0; i < N; i++) { - l += (Ulong)(unsigned) op1[i] - (Ulong)(unsigned) op2[i]; - r[i] = (int) l; - l >>= 32; - } - return l & 1; - } - template<> inline bool iv_usub_n<1>(const int *op1, const int *op2, int *r) { - Ulong l = (Ulong) (unsigned) op1[0] - (Ulong) (unsigned) op2[0]; - r[0] = (int) l; - return (l >> 32) & 1; - } - template<> inline bool iv_usub_n<2>(const int *op1, const int *op2, int *r) { - Slong l = (Ulong) (unsigned) op1[0] - (Ulong) (unsigned) op2[0]; - r[0] = (int) l; - l >>= 32; - l += (Ulong) (unsigned) op1[1] - (Ulong) (unsigned) op2[1]; - r[1] = (int) l; - return (l >> 32) & 1; - } - - template - inline void iv_sub(const int *op1, const int *op2, int *r) { - if(Nr==1) - r[0] = op1[0] - op2[0]; - else { - const int M1 = AC_MAX(N1,N2); - const int M2 = AC_MIN(N1,N2); - const int T1 = AC_MIN(M2-1,Nr); - const int T2 = AC_MIN(M1,Nr); - bool borrow = iv_usub_n(op1, op2, r); - if(N1 > N2) - borrow = iv_sub_int_borrow(op1+T1, op2[T1], borrow, r+T1); - else - borrow = iv_sub_int_borrow(op1[T1], op2+T1, borrow, r+T1); - iv_extend(r+T2, borrow ? ~0 : 0); - } - } - template<> inline void iv_sub<1,1,1>(const int *op1, const int *op2, int *r) { - r[0] = op1[0] - op2[0]; - } - template<> inline void iv_sub<1,1,2>(const int *op1, const int *op2, int *r) { - iv_assign_int64<2>(r, (Slong) op1[0] - (Slong) op2[0]); - } - - template - inline bool iv_all_bits_same(const int *op, bool bit) { - int t = bit ? ~0 : 0; - for(int i=0; i < N; i++) - if(op[i] != t) - return false; - return true; - } - template<> inline bool iv_all_bits_same<0>(const int * /*op*/, bool /*bit*/) { return true; } - template<> inline bool iv_all_bits_same<1>(const int *op, bool bit) { - return op[0] == (bit ? ~0 : 0); - } - - template - void iv_neg(const int *op1, int *r) { - Slong l = 0; - for(int k = 0; k < AC_MIN(N,Nr); k++) { - l -= (Ulong) (unsigned) op1[k]; - r[k] = (unsigned) l; - l >>= 32; - } - if(Nr > N) { - r[N] = (unsigned) (l - (op1[N-1] < 0 ? ~0 : 0)); - iv_extend(r+N+1, r[N] < 0 ? ~0 : 0); - } - } - - template - void iv_abs(const int *op1, int *r) { - if( S && op1[N-1] < 0) { - iv_neg(op1, r); - } else { - iv_copy(op1, r); - iv_extend(r+N, 0); - } - } - - template - void iv_udiv(const sw2 *n, const sw2 *d, sw2 *q, sw2 *r) { - const int w2_length = 2*w1_length; - int d_msi; // most significant int for d - for(d_msi = D-1; d_msi > 0 && !d[d_msi]; d_msi--) {} - uw4 d1 = 0; - if(!d_msi && !d[0]) { - d1 = n[0]/d[0]; // d is zero => divide by zero - return; - } - int n_msi; // most significant int for n - for(n_msi = N-1; n_msi > 0 && !n[n_msi]; n_msi--) {} - for(int i=0; i < Q; i++) - q[i] = 0; - for(int i=0; i < R; i++) - r[i] = n[i]; - // write most significant "words" into d1 - bool d_mss_odd = (bool) (d[d_msi] >> w1_length); - int d_mss= 2*d_msi + d_mss_odd; // index to most significant short (16-bit) - d1 = (uw4) (uw2) d[d_msi] << (w1_length << (int) !d_mss_odd); - if(d_msi) - d1 |= (uw2) d[d_msi-1] >> (d_mss_odd ? w1_length : 0); - bool n_mss_odd = (bool) (n[n_msi] >> w1_length); - int n_mss = 2*n_msi + n_mss_odd; - if(n_mss < d_mss) { - // q already initialized to 0 - if(R) { - int r_msi = AC_MIN(R-1, n_msi); - for(int j = 0; j <= r_msi; j++) - r[j] = n[j]; - for(int j = r_msi+1; j < R; j++) - r[j] = 0; - } - } else { - uw2 r1[N+1]; - r1[n_msi+1] = 0; - for(int k = n_msi; k >= 0; k--) - r1[k] = n[k]; - for(int k = n_mss; k >=d_mss; k--) { - int k_msi = k >> 1; - bool odd = k & 1; - uw2 r1m1 = k_msi > 0 ? r1[k_msi-1] : (uw2) 0; - uw4 n1 = odd ? - (uw4) ((r1[k_msi+1] << w1_length) | (r1[k_msi] >> w1_length)) << w2_length | ((r1[k_msi] << w1_length) | (r1m1 >> w1_length)) : - (uw4) r1[k_msi] << w2_length | r1m1; - uw2 q1 = n1/d1; - if(q1 >> w1_length) - q1--; - AC_ASSERT(!(q1 >> w1_length), "Problem detected in long division algorithm, Please report"); - unsigned k2 = k - d_mss; - unsigned k2_i = k2 >> 1; - bool odd_2 = k2 & 1; - uw2 q2 = q1 << (odd_2 ? w1_length : 0); - sw4 l = 0; - for(int j = 0; j <= d_msi; j++) { - l += r1[k2_i + j]; - bool l_sign = l < 0; - sw4 prod = (uw4) (uw2) d[j] * (uw4) q2; - l -= prod; - bool ov1 = (l >= 0) & ((prod < 0) | l_sign); - bool ov2 = (l < 0) & (prod < 0) & l_sign; - r1[k2_i + j] = (uw2) l; - l >>= w2_length; - if(ov1) - l |= ((uw4) -1 << w2_length); - if(ov2) - l ^= ((sw4) 1 << w2_length); - } - if(odd_2 | d_mss_odd) { - l += r1[k2_i + d_msi + 1]; - r1[k2_i + d_msi + 1] = (uw2) l; - } - if(l < 0) { - l = 0; - for(int j = 0; j <= d_msi; j++) { - l += (sw4) (uw2) d[j] << (odd_2 ? w1_length : 0); - l += r1[k2_i + j]; - r1[k2_i + j] = (uw2) l; - l >>= w2_length; - } - if(odd_2 | d_mss_odd) - r1[k2_i + d_msi + 1] += (uw2) l; - q1--; - } - if(Q && k2_i < Q) { - if(odd_2) - q[k2_i] = q1 << w1_length; - else - q[k2_i] |= q1; - } - } - if(R) { - int r_msi = AC_MIN(R-1, n_msi); - for(int j = 0; j <= r_msi; j++) - r[j] = r1[j]; - for(int j = r_msi+1; j < R; j++) - r[j] = 0; - } - } - } - - template - inline void iv_div(const int *op1, const int *op2, int *r) { - enum { N1_over = N1+(Den_s && (Num_s==2)) }; - if(N1_over==1 && N2==1) { - r[0] = op1[0] / op2[0]; - iv_extend(r+1, ((Num_s || Den_s) && (r[0] < 0)) ? ~0 : 0); - } - else if(N1_over==1 && N2==2) - iv_assign_int64(r, ( (Slong) op1[0]) / (((Slong) op2[1] << 32) | (unsigned) op2[0]) ); - else if(N1_over==2 && N2==1) - if(N1 == 1) - iv_assign_int64(r, ( (Slong) op1[0]) / ( (Slong) op2[0]) ); - else - iv_assign_int64(r, (((Slong) op1[1] << 32) | (unsigned) op1[0]) / ( (Slong) op2[0]) ); - else if(N1_over==2 && N2==2) - if(N1 == 1) - iv_assign_int64(r, ( (Slong) op1[0]) / (((Slong) op2[1] << 32) | (unsigned) op2[0]) ); - else - iv_assign_int64(r, (((Slong) op1[1] << 32) | (unsigned) op1[0]) / (((Slong) op2[1] << 32) | (unsigned) op2[0]) ); - else if(!Num_s && !Den_s) { - iv_udiv(op1, op2, r, 0); - } - else { - enum { N1_neg = N1+(Num_s==2), N2_neg = N2+(Den_s==2)}; - int numerator[N1_neg]; - int denominator[N2_neg]; - int quotient[N1_neg]; - iv_abs(op1, numerator); - iv_abs(op2, denominator); - iv_udiv(numerator, denominator, quotient, 0); - if( (Num_s && op1[N1-1] < 0) ^ (Den_s && op2[N2-1] < 0) ) - iv_neg(quotient, r); - else { - iv_copy(quotient, r); - iv_extend(r+N1_neg, (Num_s || Den_s) && r[N1_neg-1] < 0 ? ~0 : 0); - } - } - } - - template - inline void iv_rem(const int *op1, const int *op2, int *r) { - enum { N1_over = N1+(Den_s && (Num_s==2)) }; // N1_over corresponds to the division - if(N1_over==1 && N2==1) { - r[0] = op1[0] % op2[0]; - iv_extend(r+1, Num_s && r[0] < 0 ? ~0 : 0); - } - else if(N1_over==1 && N2==2) - iv_assign_int64(r, ( (Slong) op1[0]) % (((Slong) op2[1] << 32) | (unsigned) op2[0]) ); - else if(N1_over==2 && N2==1) - if(N1 == 1) - iv_assign_int64(r, ( (Slong) op1[0]) % ( (Slong) op2[0]) ); - else - iv_assign_int64(r, (((Slong) op1[1] << 32) | (unsigned) op1[0]) % ( (Slong) op2[0]) ); - else if(N1_over==2 && N2==2) - if(N1 == 1) - iv_assign_int64(r, ( (Slong) op1[0]) % (((Slong) op2[1] << 32) | (unsigned) op2[0]) ); - else - iv_assign_int64(r, (((Slong) op1[1] << 32) | (unsigned) op1[0]) % (((Slong) op2[1] << 32) | (unsigned) op2[0]) ); - else if(!Num_s && !Den_s) { - iv_udiv(op1, op2, 0, r); - } - else { - enum { N1_neg = N1+(Num_s==2), N2_neg = N2+(Den_s==2)}; - int numerator[N1_neg]; - int denominator[N2_neg]; - int remainder[N2]; - iv_abs(op1, numerator); - iv_abs(op2, denominator); - iv_udiv(numerator, denominator, 0, remainder); - if( (Num_s && op1[N1-1] < 0) ) - iv_neg(remainder, r); - else { - iv_copy(remainder, r); - iv_extend(r+N2, Num_s && r[N2-1] < 0 ? ~0 : 0); - } - } - } - - template - inline void iv_bitwise_complement_n(const int *op, int *r) { - for(int i=0; i < N; i++) - r[i] = ~op[i]; - } - template<> inline void iv_bitwise_complement_n<1>(const int *op, int *r) { - r[0] = ~op[0]; - } - template<> inline void iv_bitwise_complement_n<2>(const int *op, int *r) { - r[0] = ~op[0]; - r[1] = ~op[1]; - } - - template - inline void iv_bitwise_complement(const int *op, int *r) { - const int M = AC_MIN(N,Nr); - iv_bitwise_complement_n(op, r); - iv_extend(r+M, (r[M-1] < 0) ? ~0 : 0); - } - - template - inline void iv_bitwise_and_n(const int *op1, const int *op2, int *r) { - for(int i=0; i < N; i++) - r[i] = op1[i] & op2[i]; - } - template<> inline void iv_bitwise_and_n<1>(const int *op1, const int *op2, int *r) { - r[0] = op1[0] & op2[0]; - } - template<> inline void iv_bitwise_and_n<2>(const int *op1, const int *op2, int *r) { - r[0] = op1[0] & op2[0]; - r[1] = op1[1] & op2[1]; - } - - template - inline void iv_bitwise_and(const int *op1, const int *op2, int *r) { - const int M1 = AC_MIN(AC_MAX(N1,N2), Nr); - const int M2 = AC_MIN(AC_MIN(N1,N2), Nr); - const int *OP1 = N1 > N2 ? op1 : op2; - const int *OP2 = N1 > N2 ? op2 : op1; - - iv_bitwise_and_n(op1, op2, r); - if(OP2[M2-1] < 0) - iv_copy(OP1+M2, r+M2); - else - iv_extend(r+M2, 0); - iv_extend(r+M1, (r[M1-1] < 0) ? ~0 : 0); - } - - template - inline void iv_bitwise_or_n(const int *op1, const int *op2, int *r) { - for(int i=0; i < N; i++) - r[i] = op1[i] | op2[i]; - } - template<> inline void iv_bitwise_or_n<1>(const int *op1, const int *op2, int *r) { - r[0] = op1[0] | op2[0]; - } - template<> inline void iv_bitwise_or_n<2>(const int *op1, const int *op2, int *r) { - r[0] = op1[0] | op2[0]; - r[1] = op1[1] | op2[1]; - } - - template - inline void iv_bitwise_or(const int *op1, const int *op2, int *r) { - const int M1 = AC_MIN(AC_MAX(N1,N2), Nr); - const int M2 = AC_MIN(AC_MIN(N1,N2), Nr); - const int *OP1 = N1 >= N2 ? op1 : op2; - const int *OP2 = N1 >= N2 ? op2 : op1; - - iv_bitwise_or_n(op1, op2, r); - if(OP2[M2-1] < 0) - iv_extend(r+M2, ~0); - else - iv_copy(OP1+M2, r+M2); - iv_extend(r+M1, (r[M1-1] < 0) ? ~0 : 0); - } - - template - inline void iv_bitwise_xor_n(const int *op1, const int *op2, int *r) { - for(int i=0; i < N; i++) - r[i] = op1[i] ^ op2[i]; - } - template<> inline void iv_bitwise_xor_n<1>(const int *op1, const int *op2, int *r) { - r[0] = op1[0] ^ op2[0]; - } - template<> inline void iv_bitwise_xor_n<2>(const int *op1, const int *op2, int *r) { - r[0] = op1[0] ^ op2[0]; - r[1] = op1[1] ^ op2[1]; - } - - template - inline void iv_bitwise_xor(const int *op1, const int *op2, int *r) { - const int M1 = AC_MIN(AC_MAX(N1,N2), Nr); - const int M2 = AC_MIN(AC_MIN(N1,N2), Nr); - const int *OP1 = N1 >= N2 ? op1 : op2; - const int *OP2 = N1 >= N2 ? op2 : op1; - - iv_bitwise_xor_n(op1, op2, r); - if(OP2[M2-1] < 0) - iv_bitwise_complement_n(OP1+M2, r+M2); - else - iv_copy(OP1+M2, r+M2); - iv_extend(r+M1, (r[M1-1] < 0) ? ~0 : 0); - } - - template - inline void iv_shift_l(const int *op1, unsigned op2, int *r) { - AC_ASSERT(Nr <= N, "iv_shift_l, incorrect usage Nr > N"); - unsigned s31 = op2 & 31; - unsigned ishift = (op2 >> 5) > Nr ? Nr : (op2 >> 5); - if(s31 && ishift!=Nr) { - unsigned lw = 0; - for(unsigned i=0; i < Nr; i++) { - unsigned hw = (i >= ishift) ? op1[i-ishift] : 0; - r[i] = (hw << s31) | (lw >> (32-s31)); - lw = hw; - } - } else { - for(unsigned i=0; i < Nr ; i++) - r[i] = (i >= ishift) ? op1[i-ishift] : 0; - } - } - - template - inline void iv_shift_r(const int *op1, unsigned op2, int *r) { - unsigned s31 = op2 & 31; - unsigned ishift = (op2 >> 5) > N ? N : (op2 >> 5); - int ext = op1[N-1] < 0 ? ~0 : 0; - if(s31 && ishift!=N) { - unsigned lw = (ishift < N) ? op1[ishift] : ext; - for(unsigned i=0; i < Nr; i++) { - unsigned hw = (i+ishift+1 < N) ? op1[i+ishift+1] : ext; - r[i] = (lw >> s31) | (hw << (32-s31)); - lw = hw; - } - } else { - for(unsigned i=0; i < Nr ; i++) - r[i] = (i+ishift < N) ? op1[i+ishift] : ext; - } - } - - template - inline void iv_shift_l2(const int *op1, signed op2, int *r) { - if(S && op2 < 0) - iv_shift_r(op1, -op2, r); - else - iv_shift_l(op1, op2, r); - } - - template<> inline void iv_shift_l2<1,1,false>(const int *op1, signed op2, int *r) { - r[0] = (op2 < 32) ? ( (unsigned) op1[0] << op2) : 0; - } - template<> inline void iv_shift_l2<1,1,true>(const int *op1, signed op2, int *r) { - r[0] = (op2 >= 0) ? - (op2 < 32) ? ( (unsigned) op1[0] << op2) : 0 : - (op2 > -32) ? (op1[0] >> -op2) : (op1[0] >> 31); - } - - template - inline void iv_shift_r2(const int *op1, signed op2, int *r) { - if(S && op2 < 0) - iv_shift_l(op1, -op2, r); - else - iv_shift_r(op1, op2, r); - } - - template<> inline void iv_shift_r2<1,1,false>(const int *op1, signed op2, int *r) { - r[0] = (op2 < 32) ? (op1[0] >> op2) : (op1[0] >> 31); - } - template<> inline void iv_shift_r2<1,1,true>(const int *op1, signed op2, int *r) { - r[0] = (op2 >= 0) ? - (op2 < 32) ? (op1[0] >> op2) : (op1[0] >> 31) : - (op2 > -32) ? ( (unsigned) op1[0] << -op2) : 0; - } - - template - inline void iv_const_shift_l(const int *op1, int *r) { - // B >= 0 - if(!B) { - const int M1 = AC_MIN(N,Nr); - iv_copy(op1, r); - iv_extend(r+M1, r[M1-1] < 0 ? -1 : 0); - } - else { - const unsigned s31 = B & 31; - const int ishift = (((B >> 5) > Nr) ? Nr : (B >> 5)); - iv_extend(r, 0); - const int M1 = AC_MIN(N+ishift,Nr); - if(s31) { - unsigned lw = 0; - for(int i=ishift; i < M1; i++) { - unsigned hw = op1[i-ishift]; - r[i] = (hw << s31) | (lw >> ((32-s31)&31)); // &31 is to quiet compilers - lw = hw; - } - if(Nr > M1) { - r[M1] = (signed) lw >> ((32-s31)&31); // &31 is to quiet compilers - iv_extend(r+M1+1, r[M1] < 0 ? ~0 : 0); - } - } else { - for(int i=ishift; i < M1 ; i++) - r[i] = op1[i-ishift]; - iv_extend(r+M1, r[M1-1] < 0 ? -1 : 0); - } - } - } - template<> inline void iv_const_shift_l<1,1,0>(const int *op1, int *r) { - r[0] = op1[0]; - } - template<> inline void iv_const_shift_l<2,1,0>(const int *op1, int *r) { - r[0] = op1[0]; - } - - template - inline void iv_const_shift_r(const int *op1, int *r) { - if(!B) { - const int M1 = AC_MIN(N,Nr); - iv_copy(op1, r); - iv_extend(r+M1, r[M1-1] < 0 ? ~0 : 0); - } - else { - const unsigned s31 = B & 31; - const int ishift = (((B >> 5) > N) ? N : (B >> 5)); - int ext = op1[N-1] < 0 ? ~0 : 0; - if(s31 && ishift!=N) { - unsigned lw = (ishift < N) ? op1[ishift] : ext; - for(int i=0; i < Nr; i++) { - unsigned hw = (i+ishift+1 < N) ? op1[i+ishift+1] : ext; - r[i] = (lw >> s31) | (hw << ((32-s31)&31)); // &31 is to quiet compilers - lw = hw; - } - } else { - for(int i=0; i < Nr ; i++) - r[i] = (i+ishift < N) ? op1[i+ishift] : ext; - } - } - } - template<> inline void iv_const_shift_r<1,1,0>(const int *op1, int *r) { - r[0] = op1[0]; - } - template<> inline void iv_const_shift_r<2,1,0>(const int *op1, int *r) { - r[0] = op1[0]; - } - - template - inline void iv_conv_from_fraction(double d, int *r, bool *qb, bool *rbits, bool *o) { - bool b = d < 0; - double d2 = b ? -d : d; - double dfloor = mgc_floor(d2); - *o = dfloor != 0.0; - d2 = d2 - dfloor; - for(int i=N-1; i >=0; i--) { - d2 *= (Ulong) 1 << 32; - unsigned k = (unsigned int) d2; - r[i] = b ? ~k : k; - d2 -= k; - } - d2 *= 2; - bool k = ((int) d2) != 0; // is 0 or 1 - d2 -= k; - *rbits = d2 != 0.0; - *qb = (b && *rbits) ^ k; - if(b && !*rbits && !*qb) - iv_uadd_carry(r, true, r); - *o |= b ^ (r[N-1] < 0); - } - - template - inline int to_str(int *v, int w, bool left_just, char *r) { - const char digits[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'}; - const unsigned char B = b==AC_BIN ? 1 : (b==AC_OCT ? 3 : (b==AC_HEX ? 4 : 0)); - int k = (w+B-1)/B; - int n = (w+31) >> 5; - int bits = 0; - if(b != AC_BIN && left_just) { - if( (bits = -(w % B)) ) - r[--k] = 0; - } - for(int i = 0; i < n; i++) { - if (b != AC_BIN && bits < 0) - r[k] += (unsigned char) (( (unsigned) v[i] << (B+bits)) & (b-1)); - unsigned int m = (unsigned) v[i] >> -bits; - for(bits += 32; bits > 0 && k; bits -= B) { - r[--k] = (char) (m & (b-1)); - m >>= B; - } - } - for(int i=0; i < (w+B-1)/B; i++) - r[i] = digits[(int)r[i]]; - return (w+B-1)/B; - } - template<> inline int to_str(int *v, int w, bool left_just, char *r) { - int k = 0; - int msw = (w-1) >> 5; - if(left_just) { - unsigned bits_msw = w & 31; - if(bits_msw) { - unsigned left_shift = 32 - bits_msw; - for(int i=msw; i > 0; i--) - v[i] = (unsigned) v[i] << left_shift | (unsigned) v[i-1] >> bits_msw; - v[0] = (unsigned) v[0] << left_shift; - } - int lsw = 0; - while(lsw < msw || v[msw] ) { - Ulong l = 0; - for(int i=lsw; i <= msw; i++) { - l += (Ulong) (unsigned) v[i] * 10; - v[i] = l; - l >>= 32; - if(i==lsw && !v[i]) - lsw++; - } - r[k++] = (char) ('0' + (int) l); - } - } else { - const unsigned d = 1000000000; // 10E9 - for(; msw > 0 && !v[msw]; msw--) {} - while(msw >= 0) { - Ulong nl = 0; - for(int i = msw; i >= 0; i--) { - nl <<= 32; - nl |= (unsigned) v[i]; - unsigned q = nl/d; - nl -= (Ulong) q * d; - v[i] = q; - } - if(!v[msw]) - msw--; - bool last = msw == -1; - unsigned rem = (unsigned) nl; - for(int i=0; (i < 9 && !last) || rem; i++) { - r[k++] = (char) ('0' + (int) (rem % 10)); - rem /= 10; - } - } - for(int i=0; i < k/2; i++) { - char c = r[i]; - r[i] = r[k-1-i]; - r[k-1-i] = c; - } - } - r[k] = 0; - return k; - } - - inline int to_string(int *v, int w, bool sign_mag, ac_base_mode base, bool left_just, char *r) { - int n = (w+31) >> 5; - bool neg = !sign_mag && v[n-1] < 0; - if(!left_just) { - while(n-- && v[n] == (neg ? ~0 : 0)) {} - int w2 = 32*(n+1); - if(w2) { - int m = v[n]; - for(int i = 16; i > 0; i >>= 1) { - if((m >> i) == (neg ? ~0 : 0)) - w2 -= i; - else - m >>= i; - } - } - if(w2 < w) - w = w2; - w += !sign_mag; - } - if(base == AC_DEC) - return to_str(v, w, left_just, r); - else if (base == AC_HEX) - return to_str(v, w, left_just, r); - else if (base == AC_OCT) - return to_str(v, w, left_just, r); - else if (base == AC_BIN) - return to_str(v, w, left_just, r); - return 0; - } - - template - inline unsigned iv_leading_bits(const int *op, bool bit); - - template<> inline unsigned iv_leading_bits<1>(const int *op, bool bit) { - const unsigned char tab[] = {4, 3, 2, 2, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0}; - unsigned t = bit ? ~*op : *op; - unsigned cnt = 0; - if(t >> 16) - t >>= 16; - else - cnt += 16; - if(t >> 8) - t >>= 8; - else - cnt += 8; - if(t >> 4) - t >>= 4; - else - cnt += 4; - cnt += tab[t]; - return cnt; - } - - template - inline unsigned iv_leading_bits(const int *op, bool bit) { - int ext_sign = bit ? -1 : 0; - int k; - for(k = N-1; k >= 0 && op[k] == ext_sign; k--) {} - return 32*(N-1-k) + (k < 0 ? 0 : iv_leading_bits<1>(op+k, bit)); - } - - ////////////////////////////////////////////////////////////////////////////// - // Integer Vector class: iv - ////////////////////////////////////////////////////////////////////////////// - template - class iv { - protected: - int v[N]; - public: - template friend class iv; - iv() {} - template - iv ( const iv &b ) { - const int M = AC_MIN(N,N2); - iv_copy(b.v, v); - iv_extend(v+M, (v[M-1] < 0) ? ~0 : 0); - } - iv ( Slong t) { - iv_assign_int64(v, t); - } - iv ( Ulong t) { - iv_assign_uint64(v, t); - } - iv ( int t) { - v[0] = t; - iv_extend(v+1, (t < 0) ? ~0 : 0); - } - iv ( unsigned int t) { - v[0] = t; - iv_extend(v+1, 0); - } - iv ( long t) { - if(long_w == 32) { - v[0] = t; - iv_extend(v+1, (t < 0) ? ~0 : 0); - } else - iv_assign_int64(v, t); - } - iv ( unsigned long t) { - if(long_w == 32) { - v[0] = t; - iv_extend(v+1, 0); - } else - iv_assign_uint64(v, t); - } - iv ( double d ) { - double d2 = ldexpr32<-N>(d); - bool qb, rbits, o; - iv_conv_from_fraction(d2, v, &qb, &rbits, &o); - } - - // Explicit conversion functions to C built-in types ------------- - inline Slong to_int64() const { return N==1 ? v[0] : ((Ulong)v[1] << 32) | (Ulong) (unsigned) v[0]; } - inline Ulong to_uint64() const { return N==1 ? (Ulong) v[0] : ((Ulong)v[1] << 32) | (Ulong) (unsigned) v[0]; } - inline double to_double() const { - double a = v[N-1]; - for(int i=N-2; i >= 0; i--) { - a *= (Ulong) 1 << 32; - a += (unsigned) v[i]; - } - return a; - } - inline void conv_from_fraction(double d, bool *qb, bool *rbits, bool *o) { - iv_conv_from_fraction(d, v, qb, rbits, o); - } - - template - inline void mult(const iv &op2, iv &r) const { - iv_mult(v, op2.v, r.v); - } - template - void add(const iv &op2, iv &r) const { - iv_add(v, op2.v, r.v); - } - template - void sub(const iv &op2, iv &r) const { - iv_sub(v, op2.v, r.v); - } - template - void div(const iv &op2, iv &r) const { - iv_div(v, op2.v, r.v); - } - template - void rem(const iv &op2, iv &r) const { - iv_rem(v, op2.v, r.v); - } - void increment() { - iv_uadd_carry(v, true, v); - } - void decrement() { - iv_sub_int_borrow(v, 0, true, v); - } - template - void neg(iv &r) const { - iv_neg(v, r.v); - } - template - void shift_l(unsigned op2, iv &r) const { - iv_shift_l(v, op2, r.v); - } - template - void shift_l2(signed op2, iv &r) const { - iv_shift_l2(v, op2, r.v); - } - template - void shift_r(unsigned op2, iv &r) const { - iv_shift_r(v, op2, r.v); - } - template - void shift_r2(signed op2, iv &r) const { - iv_shift_r2(v, op2, r.v); - } - template - void const_shift_l(iv &r) const { - iv_const_shift_l(v, r.v); - } - template - void const_shift_r(iv &r) const { - iv_const_shift_r(v, r.v); - } - template - void bitwise_complement(iv &r) const { - iv_bitwise_complement(v, r.v); - } - template - void bitwise_and(const iv &op2, iv &r) const { - iv_bitwise_and(v, op2.v, r.v); - } - template - void bitwise_or(const iv &op2, iv &r) const { - iv_bitwise_or(v, op2.v, r.v); - } - template - void bitwise_xor(const iv &op2, iv &r) const { - iv_bitwise_xor(v, op2.v, r.v); - } - template - bool equal(const iv &op2) const { - return iv_equal(v, op2.v); - } - template - bool greater_than(const iv &op2) const { - return iv_compare(v, op2.v); - } - template - bool less_than(const iv &op2) const { - return iv_compare(v, op2.v); - } - bool equal_zero() const { - return iv_equal_zero(v); - } - template - void set_slc(unsigned lsb, int WS, const iv &op2) { - AC_ASSERT((31+WS)/32 == N2, "Bad usage: WS greater than length of slice"); - unsigned msb = lsb+WS-1; - unsigned lsb_v = lsb >> 5; - unsigned lsb_b = lsb & 31; - unsigned msb_v = msb >> 5; - unsigned msb_b = msb & 31; - if(N2==1) { - if(msb_v == lsb_v) - v[lsb_v] ^= (v[lsb_v] ^ ((unsigned) op2.v[0] << lsb_b)) & (~(WS==32 ? 0 : all_ones<> 1) >> (31-lsb_b)); - v[msb_v] ^= (v[msb_v] ^ m) & ~((all_ones<<1)<> 1) >> (31-lsb_b)); - unsigned t = ((unsigned) op2.v[N2-1] << lsb_b) | (((unsigned) op2.v[N2-2] >> 1) >> (31-lsb_b)); - unsigned m; - if(msb_v-lsb_v == N2) { - v[msb_v-1] = t; - m = (((unsigned) op2.v[N2-1] >> 1) >> (31-lsb_b)); - } - else - m = t; - v[msb_v] ^= (v[msb_v] ^ m) & ~((all_ones<<1)<(v, bit); - } - }; - - template<> inline Slong iv<1>::to_int64() const { return v[0]; } - template<> inline Ulong iv<1>::to_uint64() const { return v[0]; } - - template<> inline Slong iv<2>::to_int64() const { - return ((Ulong)v[1] << 32) | (Ulong) (unsigned) v[0]; - } - template<> inline Ulong iv<2>::to_uint64() const { - return ((Ulong)v[1] << 32) | (Ulong) (unsigned) v[0]; - } - - template<> template<> inline void iv<1>::set_slc(unsigned lsb, int WS, const iv<1> &op2) { - v[0] ^= (v[0] ^ ((unsigned) op2.v[0] << lsb)) & (~(WS==32 ? 0 : all_ones< template<> inline void iv<2>::set_slc(unsigned lsb, int WS, const iv<1> &op2) { - Ulong l = to_uint64(); - Ulong l2 = op2.to_uint64(); - l ^= (l ^ (l2 << lsb)) & (~((~(Ulong)0)< template<> inline void iv<2>::set_slc(unsigned lsb, int WS, const iv<2> &op2) { - Ulong l = to_uint64(); - Ulong l2 = op2.to_uint64(); - l ^= (l ^ (l2 << lsb)) & (~(WS==64 ? (Ulong) 0 : ~(Ulong)0< - class iv_conv : public iv { - protected: - iv_conv() {} - template iv_conv(const T& t) : iv(t) {} - }; - - template - class iv_conv : public iv { - public: - operator Ulong () const { return iv::to_uint64(); } - protected: - iv_conv() {} - template iv_conv(const T& t) : iv(t) {} - }; - - template - class iv_conv : public iv { - public: - operator Slong () const { return iv::to_int64(); } - protected: - iv_conv() {} - template iv_conv(const T& t) : iv(t) {} - }; - - // Set default to promote to int as this is the case for almost all types - // create exceptions using specializations - template - struct c_prom { - typedef int promoted_type; - }; - template<> struct c_prom { - typedef unsigned promoted_type; - }; - template<> struct c_prom { - typedef long promoted_type; - }; - template<> struct c_prom { - typedef unsigned long promoted_type; - }; - template<> struct c_prom { - typedef Slong promoted_type; - }; - template<> struct c_prom { - typedef Ulong promoted_type; - }; - template<> struct c_prom { - typedef float promoted_type; - }; - template<> struct c_prom { - typedef double promoted_type; - }; - - template - struct c_arith { - // will error out for pairs of T and T2 that are not defined through specialization - }; - template struct c_arith { - typedef T arith_conv; - }; - - #define C_ARITH(C_TYPE1, C_TYPE2) \ - template<> struct c_arith { \ - typedef C_TYPE1 arith_conv; \ - }; \ - template<> struct c_arith { \ - typedef C_TYPE1 arith_conv; \ - }; - - C_ARITH(double, float) - C_ARITH(double, int) - C_ARITH(double, unsigned) - C_ARITH(double, long) - C_ARITH(double, unsigned long) - C_ARITH(double, Slong) - C_ARITH(double, Ulong) - C_ARITH(float, int) - C_ARITH(float, unsigned) - C_ARITH(float, long) - C_ARITH(float, unsigned long) - C_ARITH(float, Slong) - C_ARITH(float, Ulong) - - C_ARITH(Slong, int) - C_ARITH(Slong, unsigned) - C_ARITH(Ulong, int) - C_ARITH(Ulong, unsigned) - - template - struct map { - typedef T t; - }; - template - struct c_type_params { - // will error out for T for which this template struct is not specialized - }; - - template inline const char *c_type_name() { return "unknown"; } - template<> inline const char *c_type_name() { return "bool";} - template<> inline const char *c_type_name() { return "char";} - template<> inline const char *c_type_name() { return "signed char";} - template<> inline const char *c_type_name() { return "unsigned char";} - template<> inline const char *c_type_name() { return "signed short";} - template<> inline const char *c_type_name() { return "unsigned short";} - template<> inline const char *c_type_name() { return "int";} - template<> inline const char *c_type_name() { return "unsigned";} - template<> inline const char *c_type_name() { return "signed long";} - template<> inline const char *c_type_name() { return "unsigned long";} - template<> inline const char *c_type_name() { return "signed long long";} - template<> inline const char *c_type_name() { return "unsigned long long";} - template<> inline const char *c_type_name() { return "float";} - template<> inline const char *c_type_name() { return "double";} - - template struct c_type; - - template - struct rt_c_type_T { - template - struct op1 { - typedef typename T::template rt_T< c_type >::mult mult; - typedef typename T::template rt_T< c_type >::plus plus; - typedef typename T::template rt_T< c_type >::minus2 minus; - typedef typename T::template rt_T< c_type >::minus minus2; - typedef typename T::template rt_T< c_type >::logic logic; - typedef typename T::template rt_T< c_type >::div2 div; - typedef typename T::template rt_T< c_type >::div div2; - }; - }; - template - struct c_type { - typedef typename c_prom::promoted_type c_prom_T; - struct rt_unary { - typedef c_prom_T neg; - typedef c_prom_T mag_sqr; - typedef c_prom_T mag; - template - struct set { - typedef c_prom_T sum; - }; - }; - template - struct rt_T { - typedef typename rt_c_type_T::template op1::mult mult; - typedef typename rt_c_type_T::template op1::plus plus; - typedef typename rt_c_type_T::template op1::minus minus; - typedef typename rt_c_type_T::template op1::minus2 minus2; - typedef typename rt_c_type_T::template op1::logic logic; - typedef typename rt_c_type_T::template op1::div div; - typedef typename rt_c_type_T::template op1::div2 div2; - }; - inline static std::string type_name() { - std::string r = c_type_name(); - return r; - } - - }; - // with T == c_type - template - struct rt_c_type_T< c_type > { - typedef typename c_prom::promoted_type c_prom_T; - template - struct op1 { - typedef typename c_prom::promoted_type c_prom_T2; - typedef typename c_arith< c_prom_T, c_prom_T2 >::arith_conv mult; - typedef typename c_arith< c_prom_T, c_prom_T2 >::arith_conv plus; - typedef typename c_arith< c_prom_T, c_prom_T2 >::arith_conv minus; - typedef typename c_arith< c_prom_T, c_prom_T2 >::arith_conv minus2; - typedef typename c_arith< c_prom_T, c_prom_T2 >::arith_conv logic; - typedef typename c_arith< c_prom_T, c_prom_T2 >::arith_conv div; - typedef typename c_arith< c_prom_T, c_prom_T2 >::arith_conv div2; - }; - }; - - #define C_TYPE_MAP(C_TYPE) \ - template<> struct map { \ - typedef c_type t; \ - }; - - #define C_TYPE_PARAMS(C_TYPE, WI, SI) \ - template<> struct c_type_params { \ - enum { W = WI, I = WI, E = 0, S = SI, floating_point = 0 }; \ - }; - - #define C_TYPE_MAP_INT(C_TYPE, WI, SI) \ - C_TYPE_MAP(C_TYPE) \ - C_TYPE_PARAMS(C_TYPE, WI, SI) - - #define C_TYPE_MAP_FLOAT(C_TYPE, FP, WFP, IFP, EFP) \ - C_TYPE_MAP(C_TYPE) \ - template<> struct c_type_params { \ - enum { W = WFP, I = IFP, E = EFP, S = true, floating_point = FP }; \ - }; - - C_TYPE_MAP_INT(bool, 1, false) - C_TYPE_MAP_INT(char, 8, true) - C_TYPE_MAP_INT(signed char, 8, true) - C_TYPE_MAP_INT(unsigned char, 8, false) - C_TYPE_MAP_INT(signed short, 16, true) - C_TYPE_MAP_INT(unsigned short, 16, false) - C_TYPE_MAP_INT(signed int, 32, true) - C_TYPE_MAP_INT(unsigned int, 32, false) - C_TYPE_MAP_INT(signed long, ac_private::long_w, true) - C_TYPE_MAP_INT(unsigned long, ac_private::long_w, false) - C_TYPE_MAP_INT(signed long long, 64, true) - C_TYPE_MAP_INT(unsigned long long, 64, false) - C_TYPE_MAP_FLOAT(float, 1, 25, 1, 8) - C_TYPE_MAP_FLOAT(double, 2, 54, 1, 11) - - #undef C_TYPE_INT - #undef C_TYPE_PARAMS - #undef C_TYPE_FLOAT - #undef C_TYPE_MAP - - // specializations for following struct declared/defined after definition of ac_int - template - struct rt_ac_int_T { - template - struct op1 { - typedef typename T::template rt_T< ac_int >::mult mult; - typedef typename T::template rt_T< ac_int >::plus plus; - typedef typename T::template rt_T< ac_int >::minus2 minus; - typedef typename T::template rt_T< ac_int >::minus minus2; - typedef typename T::template rt_T< ac_int >::logic logic; - typedef typename T::template rt_T< ac_int >::div2 div; - typedef typename T::template rt_T< ac_int >::div div2; - }; - }; -} - -namespace ac { - // compiler time constant for log2 like functions - template - struct nbits { - enum { val = X ? ac_private::s_N<16>::s_X::nbits : 1 }; - }; - - template - struct log2_floor { - enum { val = nbits::val - 1 }; - }; - - // log2 of 0 is not defined: generate compiler error - template<> struct log2_floor<0> {}; - - template - struct log2_ceil { - enum { lf = log2_floor::val, val = (X == (1 << lf) ? lf : lf+1) }; - }; - - // log2 of 0 is not defined: generate compiler error - template<> struct log2_ceil<0> {}; - - template - struct int_range { - enum { l_s = (LowerBound < 0), u_s = (UpperBound < 0), - signedness = l_s || u_s, - l_nbits = nbits::val, - u_nbits = nbits::val, - nbits = AC_MAX(l_nbits, u_nbits + (!u_s && signedness)) - }; - typedef ac_int type; - }; - - template - class sliceref { -# if defined(__SYNTHESIS__) && !defined(AC_IGNORE_BUILTINS) -# pragma builtin -# endif - int *d_iv; - template friend class sliceref; - public: - sliceref( int *iv ) : d_iv(iv) {} - - inline const sliceref operator = ( const sliceref &val ) { - return operator=(val); - } - - template - inline const sliceref operator = ( const sliceref &val ) { - const int src_lsi = P2/32; - const int src_msi = (P2+W-1)/32; - const int trg_lsi = P/32; - const int trg_msi = (P+W-1)/32; - const int trg_lsb = P&31; - const int trg_msb = (P+W-1)&31; - const int N = src_msi-src_lsi+1; - const int Nr = trg_msi-trg_lsi+1; - const int rshift = (P2&31) - (P&31); - int shifted_src[Nr]; - int *aligned_src = val.d_iv+src_lsi; - if(rshift) { - if(rshift < 0) - ac_private::iv_shift_l(aligned_src, -rshift, shifted_src); - else - ac_private::iv_shift_r(aligned_src, rshift, shifted_src); - aligned_src = shifted_src; - } - unsigned mask_lsi = ac_private::all_ones << trg_lsb; - unsigned mask_msi = ac_private::all_ones >> (31-trg_msb); - if(Nr==1) - mask_lsi &= mask_msi; - int *v = d_iv+trg_lsi; - v[0] ^= (v[0] ^ ((unsigned) aligned_src[0])) & mask_lsi; - for(int k=1; k < Nr-1; k++) - v[k] = aligned_src[k]; - if(Nr > 1) - v[Nr-1] ^= (v[Nr-1] ^ ((unsigned) aligned_src[Nr-1])) & mask_msi; - if(Is_MSB) { - const unsigned rem = 31-trg_msb; - if(rem) { - v[Nr-1] = S ? ((signed) ((unsigned) v[Nr-1] << rem) >> rem) - : ((unsigned) v[Nr-1] << rem) >> rem; - } else if(!S) { - v[Nr] = 0; - } - } - return *this; - } - }; -} - -enum ac_q_mode { AC_TRN, AC_RND, AC_TRN_ZERO, AC_RND_ZERO, AC_RND_INF, AC_RND_MIN_INF, AC_RND_CONV, AC_RND_CONV_ODD }; -enum ac_o_mode { AC_WRAP, AC_SAT, AC_SAT_ZERO, AC_SAT_SYM }; -template class ac_fixed; - -////////////////////////////////////////////////////////////////////////////// -// Arbitrary-Length Integer: ac_int -////////////////////////////////////////////////////////////////////////////// - -template -class ac_int : public ac_private::iv_conv<(W+31+!S)/32, S, W<=64> -#ifndef __SYNTHESIS__ -__AC_INT_UTILITY_BASE -#endif -{ -#if defined(__SYNTHESIS__) && !defined(AC_IGNORE_BUILTINS) -#pragma builtin -#endif - - enum {N=(W+31+!S)/32}; - typedef ac_private::iv_conv ConvBase; - typedef ac_private::iv Base; - - inline void bit_adjust() { - const unsigned rem = (32-W)&31; - Base::v[N-1] = S ? ((signed) ((unsigned) Base::v[N-1] << rem) >> rem) : (rem ? - ((unsigned) Base::v[N-1] << rem) >> rem : 0); - } - - inline bool is_neg() const { return S && Base::v[N-1] < 0; } - - // returns false if number is denormal - template - bool normalize_private(ac_int &exp, bool reserved_min_exp=false) { - int expt = exp; - int lshift = leading_sign(); - bool fully_normalized = true; - ac_int min_exp; - min_exp.template set_val(); - int max_shift = exp - min_exp - reserved_min_exp; - if(lshift > max_shift) { - lshift = ac_int(max_shift); - expt = min_exp + reserved_min_exp; - fully_normalized = false; - } else { - expt -= lshift; - } - if(Base::equal_zero()) { - expt = 0; - fully_normalized = true; - } - exp = expt; - Base r; - Base::shift_l(lshift, r); - Base::operator=(r); - bit_adjust(); - return fully_normalized; - } - -public: - static const int width = W; - static const int i_width = W; - static const bool sign = S; - static const ac_q_mode q_mode = AC_TRN; - static const ac_o_mode o_mode = AC_WRAP; - static const int e_width = 0; - - template - struct rt { - enum { - mult_w = W+W2, - mult_s = S||S2, - plus_w = AC_MAX(W+(S2&&!S),W2+(S&&!S2))+1, - plus_s = S||S2, - minus_w = AC_MAX(W+(S2&&!S),W2+(S&&!S2))+1, - minus_s = true, - div_w = W+S2, - div_s = S||S2, - mod_w = AC_MIN(W,W2+(!S2&&S)), - mod_s = S, - logic_w = AC_MAX(W+(S2&&!S),W2+(S&&!S2)), - logic_s = S||S2 - }; - typedef ac_int mult; - typedef ac_int plus; - typedef ac_int minus; - typedef ac_int logic; - typedef ac_int div; - typedef ac_int mod; - typedef ac_int arg1; - }; - - template - struct rt_T { - typedef typename ac_private::map::t map_T; - typedef typename ac_private::rt_ac_int_T::template op1::mult mult; - typedef typename ac_private::rt_ac_int_T::template op1::plus plus; - typedef typename ac_private::rt_ac_int_T::template op1::minus minus; - typedef typename ac_private::rt_ac_int_T::template op1::minus2 minus2; - typedef typename ac_private::rt_ac_int_T::template op1::logic logic; - typedef typename ac_private::rt_ac_int_T::template op1::div div; - typedef typename ac_private::rt_ac_int_T::template op1::div2 div2; - typedef ac_int arg1; - }; - - struct rt_unary { - enum { - neg_w = W+1, - neg_s = true, - mag_sqr_w = 2*W-S, - mag_sqr_s = false, - mag_w = W+S, - mag_s = false, - leading_sign_w = ac::log2_ceil::val, - leading_sign_s = false - }; - typedef ac_int neg; - typedef ac_int mag_sqr; - typedef ac_int mag; - typedef ac_int leading_sign; - template - struct set { - enum { sum_w = W + ac::log2_ceil::val, sum_s = S}; - typedef ac_int sum; - }; - }; - - template friend class ac_int; - template friend class ac_fixed; - ac_int() { -#if !defined(__SYNTHESIS__) && defined(AC_DEFAULT_IN_RANGE) - bit_adjust(); -#endif - } - template - inline ac_int (const ac_int &op) { - Base::operator =(op); - bit_adjust(); - } - - inline ac_int( bool b ) : ConvBase(b) { bit_adjust(); } - inline ac_int( char b ) : ConvBase(b) { bit_adjust(); } - inline ac_int( signed char b ) : ConvBase(b) { bit_adjust(); } - inline ac_int( unsigned char b ) : ConvBase(b) { bit_adjust(); } - inline ac_int( signed short b ) : ConvBase(b) { bit_adjust(); } - inline ac_int( unsigned short b ) : ConvBase(b) { bit_adjust(); } - inline ac_int( signed int b ) : ConvBase(b) { bit_adjust(); } - inline ac_int( unsigned int b ) : ConvBase(b) { bit_adjust(); } - inline ac_int( signed long b ) : ConvBase(b) { bit_adjust(); } - inline ac_int( unsigned long b ) : ConvBase(b) { bit_adjust(); } - inline ac_int( Slong b ) : ConvBase(b) { bit_adjust(); } - inline ac_int( Ulong b ) : ConvBase(b) { bit_adjust(); } - inline ac_int( double d ) : ConvBase(d) { bit_adjust(); } - - -#if (defined(_MSC_VER) && !defined(__EDG__)) -#pragma warning( push ) -#pragma warning( disable: 4700 ) -#endif -#if (defined(__GNUC__) && ( __GNUC__ == 4 && __GNUC_MINOR__ >= 6 || __GNUC__ > 4 ) && !defined(__EDG__)) -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wuninitialized" -#endif -#if defined(__clang__) -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wuninitialized" -#endif - template - inline ac_int &set_val() { - const unsigned int all_ones = (unsigned) ~0; - if(V == AC_VAL_DC) { - ac_int r; - Base::operator =(r); - bit_adjust(); - } - else if(V == AC_VAL_0 || V == AC_VAL_MIN || V == AC_VAL_QUANTUM) { - Base::operator =(0); - if(S && V == AC_VAL_MIN) { - const unsigned int rem = (W-1)&31; - Base::v[N-1] = (all_ones << rem); - } else if(V == AC_VAL_QUANTUM) - Base::v[0] = 1; - } - else { // AC_VAL_MAX - Base::operator =(-1); - const unsigned int rem = (32-W - !S )&31; - Base::v[N-1] = (all_ones >> 1) >> rem; - } - return *this; - } -#if (defined(_MSC_VER) && !defined(__EDG__)) -#pragma warning( pop ) -#endif -#if (defined(__GNUC__) && ( __GNUC__ == 4 && __GNUC_MINOR__ >= 6 || __GNUC__ > 4 ) && !defined(__EDG__)) -#pragma GCC diagnostic pop -#endif -#if defined(__clang__) -#pragma clang diagnostic pop -#endif - - // Explicit conversion functions to C built-in types ------------- - inline int to_int() const { return Base::v[0]; } - inline unsigned to_uint() const { return Base::v[0]; } - inline long to_long() const { - return ac_private::long_w == 32 ? (long) Base::v[0] : (long) Base::to_int64(); - } - inline unsigned long to_ulong() const { - return ac_private::long_w == 32 ? (unsigned long) Base::v[0] : (unsigned long) Base::to_uint64(); - } - inline Slong to_int64() const { return Base::to_int64(); } - inline Ulong to_uint64() const { return Base::to_uint64(); } - inline double to_double() const { return Base::to_double(); } - - inline int length() const { return W; } - - inline std::string to_string(ac_base_mode base_rep, bool sign_mag = false) const { - // base_rep == AC_DEC => sign_mag == don't care (always print decimal in sign magnitude) - char r[N*32+4] = {0}; - int i = 0; - if(sign_mag) - r[i++] = is_neg() ? '-' : '+'; - else if (base_rep == AC_DEC && is_neg()) - r[i++] = '-'; - if(base_rep != AC_DEC) { - r[i++] = '0'; - r[i++] = base_rep == AC_BIN ? 'b' : (base_rep == AC_OCT ? 'o' : 'x'); - } - int str_w; - if( (base_rep == AC_DEC || sign_mag) && is_neg() ) { - ac_int mag = operator -(); - str_w = ac_private::to_string(mag.v, W+1, sign_mag, base_rep, false, r+i); - } else { - ac_int tmp = *this; - str_w = ac_private::to_string(tmp.v, W+!S, sign_mag, base_rep, false, r+i); - } - if(!str_w) { - r[i] = '0'; - r[i+1] = 0; - } - return std::string(r); - } - inline static std::string type_name() { - const char *tf[] = {",false>", ",true>"}; - std::string r = "ac_int<"; - r += ac_int<32,true>(W).to_string(AC_DEC); - r += tf[S]; - return r; - } - - // Arithmetic : Binary ---------------------------------------------------- - template - typename rt::mult operator *( const ac_int &op2) const { - typename rt::mult r; - Base::mult(op2, r); - return r; - } - template - typename rt::plus operator +( const ac_int &op2) const { - typename rt::plus r; - Base::add(op2, r); - return r; - } - template - typename rt::minus operator -( const ac_int &op2) const { - typename rt::minus r; - Base::sub(op2, r); - return r; - } -#if (defined(__GNUC__) && ( __GNUC__ == 4 && __GNUC_MINOR__ >= 6 || __GNUC__ > 4 ) && !defined(__EDG__)) -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wenum-compare" -#endif - template - typename rt::div operator /( const ac_int &op2) const { - typename rt::div r; - enum {Nminus = ac_int::N, N2 = ac_int::N, N2minus = ac_int::N, - num_s = S + (Nminus > N), den_s = S2 + (N2minus > N2), Nr = rt::div::N }; - Base::template div(op2, r); - return r; - } - template - typename rt::mod operator %( const ac_int &op2) const { - typename rt::mod r; - enum {Nminus = ac_int::N, N2 = ac_int::N, N2minus = ac_int::N, - num_s = S + (Nminus > N), den_s = S2 + (N2minus > N2), Nr = rt::mod::N }; - Base::template rem(op2, r); - return r; - } -#if (defined(__GNUC__) && ( __GNUC__ == 4 && __GNUC_MINOR__ >= 6 || __GNUC__ > 4 ) && !defined(__EDG__)) -#pragma GCC diagnostic pop -#endif - // Arithmetic assign ------------------------------------------------------ - template - ac_int &operator *=( const ac_int &op2) { - Base r; - Base::mult(op2, r); - Base::operator=(r); - bit_adjust(); - return *this; - } - template - ac_int &operator +=( const ac_int &op2) { - Base r; - Base::add(op2, r); - Base::operator=(r); - bit_adjust(); - return *this; - } - template - ac_int &operator -=( const ac_int &op2) { - Base r; - Base::sub(op2, r); - Base::operator=(r); - bit_adjust(); - return *this; - } -#if (defined(__GNUC__) && ( __GNUC__ == 4 && __GNUC_MINOR__ >= 6 || __GNUC__ > 4 ) && !defined(__EDG__)) -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wenum-compare" -#endif - template - ac_int &operator /=( const ac_int &op2) { - enum {Nminus = ac_int::N, N2 = ac_int::N, N2minus = ac_int::N, - num_s = S + (Nminus > N), den_s = S2 + (N2minus > N2), Nr = N }; - Base r; - Base::template div(op2, r); - Base::operator=(r); - bit_adjust(); - return *this; - } - template - ac_int &operator %=( const ac_int &op2) { - enum {Nminus = ac_int::N, N2 = ac_int::N, N2minus = ac_int::N, - num_s = S + (Nminus > N), den_s = S2 + (N2minus > N2), Nr = N }; - Base r; - Base::template rem(op2, r); - Base::operator=(r); - bit_adjust(); - return *this; - } -#if (defined(__GNUC__) && ( __GNUC__ == 4 && __GNUC_MINOR__ >= 6 || __GNUC__ > 4 ) && !defined(__EDG__)) -#pragma GCC diagnostic pop -#endif - // Arithmetic prefix increment, decrement ---------------------------------- - ac_int &operator ++() { - Base::increment(); - bit_adjust(); - return *this; - } - ac_int &operator --() { - Base::decrement(); - bit_adjust(); - return *this; - } - // Arithmetic postfix increment, decrement --------------------------------- - const ac_int operator ++(int) { - ac_int t = *this; - Base::increment(); - bit_adjust(); - return t; - } - const ac_int operator --(int) { - ac_int t = *this; - Base::decrement(); - bit_adjust(); - return t; - } - // Arithmetic Unary -------------------------------------------------------- - ac_int operator +() { - return *this; - } - typename rt_unary::neg operator -() const { - typename rt_unary::neg r; - Base::neg(r); - r.bit_adjust(); - return r; - } - // ! ------------------------------------------------------------------------ - bool operator ! () const { - return Base::equal_zero(); - } - - // Bitwise (arithmetic) unary: complement ----------------------------- - ac_int operator ~() const { - ac_int r; - Base::bitwise_complement(r); - return r; - } - // Bitwise (non-arithmetic) bit_complement ----------------------------- - ac_int bit_complement() const { - ac_int r; - Base::bitwise_complement(r); - r.bit_adjust(); - return r; - } - // Bitwise (arithmetic): and, or, xor ---------------------------------- - template - typename rt::logic operator & ( const ac_int &op2) const { - typename rt::logic r; - Base::bitwise_and(op2, r); - return r; - } - template - typename rt::logic operator | ( const ac_int &op2) const { - typename rt::logic r; - Base::bitwise_or(op2, r); - return r; - } - template - typename rt::logic operator ^ ( const ac_int &op2) const { - typename rt::logic r; - Base::bitwise_xor(op2, r); - return r; - } - // Bitwise assign (not arithmetic): and, or, xor ---------------------------- - template - ac_int &operator &= ( const ac_int &op2 ) { - Base r; - Base::bitwise_and(op2, r); - Base::operator=(r); - bit_adjust(); - return *this; - } - template - ac_int &operator |= ( const ac_int &op2 ) { - Base r; - Base::bitwise_or(op2, r); - Base::operator=(r); - bit_adjust(); - return *this; - } - template - ac_int &operator ^= ( const ac_int &op2 ) { - Base r; - Base::bitwise_xor(op2, r); - Base::operator=(r); - bit_adjust(); - return *this; - } - // Shift (result constrained by left operand) ------------------------------- - template - ac_int operator << ( const ac_int &op2 ) const { - ac_int r; - Base::shift_l2(op2.to_int(), r); - r.bit_adjust(); - return r; - } - template - ac_int operator << ( const ac_int &op2 ) const { - ac_int r; - Base::shift_l(op2.to_uint(), r); - r.bit_adjust(); - return r; - } - template - ac_int operator >> ( const ac_int &op2 ) const { - ac_int r; - Base::shift_r2(op2.to_int(), r); - r.bit_adjust(); - return r; - } - template - ac_int operator >> ( const ac_int &op2 ) const { - ac_int r; - Base::shift_r(op2.to_uint(), r); - r.bit_adjust(); - return r; - } - // Shift assign ------------------------------------------------------------ - template - ac_int &operator <<= ( const ac_int &op2 ) { - Base r; - Base::shift_l2(op2.to_int(), r); - Base::operator=(r); - bit_adjust(); - return *this; - } - template - ac_int &operator <<= ( const ac_int &op2 ) { - Base r; - Base::shift_l(op2.to_uint(), r); - Base::operator=(r); - bit_adjust(); - return *this; - } - template - ac_int &operator >>= ( const ac_int &op2 ) { - Base r; - Base::shift_r2(op2.to_int(), r); - Base::operator=(r); - bit_adjust(); - return *this; - } - template - ac_int &operator >>= ( const ac_int &op2 ) { - Base r; - Base::shift_r(op2.to_uint(), r); - Base::operator=(r); - bit_adjust(); - return *this; - } - // Relational --------------------------------------------------------------- - template - bool operator == ( const ac_int &op2) const { - return Base::equal(op2); - } - template - bool operator != ( const ac_int &op2) const { - return !Base::equal(op2); - } - template - bool operator < ( const ac_int &op2) const { - return Base::less_than(op2); - } - template - bool operator >= ( const ac_int &op2) const { - return !Base::less_than(op2); - } - template - bool operator > ( const ac_int &op2) const { - return Base::greater_than(op2); - } - template - bool operator <= ( const ac_int &op2) const { - return !Base::greater_than(op2); - } - - // Bit and Slice Select ----------------------------------------------------- - template - inline const ac_int slc(const ac_int &index) const { - ac_int r; - AC_ASSERT(index.to_int() >= 0, "Attempting to read slc with negative indeces"); - unsigned uindex = ac_int(index).to_uint(); - Base::shift_r(uindex, r); - r.bit_adjust(); - return r; - } - - template - inline const ac_int slc(signed index) const { - ac_int r; - AC_ASSERT(index >= 0, "Attempting to read slc with negative indeces"); - unsigned uindex = index & ((unsigned)~0 >> 1); - Base::shift_r(uindex, r); - r.bit_adjust(); - return r; - } - template - inline const ac_int slc(unsigned uindex) const { - ac_int r; - Base::shift_r(uindex, r); - r.bit_adjust(); - return r; - } - - template - inline ac_int &set_slc(const ac_int lsb, const ac_int &slc) { - AC_ASSERT(lsb.to_int() + W2 <= W && lsb.to_int() >= 0, "Out of bounds set_slc"); - if(W == W2) - Base::operator =(slc); - else { - unsigned ulsb = ac_int(lsb).to_uint(); - Base::set_slc(ulsb, W2, (ac_int) slc); - } - bit_adjust(); // in case sign bit was assigned - return *this; - } - template - inline ac_int &set_slc(signed lsb, const ac_int &slc) { - AC_ASSERT(lsb + W2 <= W && lsb >= 0, "Out of bounds set_slc"); - if(W == W2) - Base::operator =(slc); - else { - unsigned ulsb = lsb & ((unsigned)~0 >> 1); - Base::set_slc(ulsb, W2, (ac_int) slc); - } - bit_adjust(); // in case sign bit was assigned - return *this; - } - template - inline ac_int &set_slc(unsigned ulsb, const ac_int &slc) { - AC_ASSERT(ulsb + W2 <= W, "Out of bounds set_slc"); - if(W == W2) - Base::operator =(slc); - else - Base::set_slc(ulsb, W2, (ac_int) slc); - bit_adjust(); // in case sign bit was assigned - return *this; - } - - template - inline ac::sliceref range() { - #if __cplusplus > 199711L - static_assert(Msb-Lsb+1 > 0, "Range length not positive: MSB < LSB"); - static_assert(Lsb >= 0, "LSB is negative"); - static_assert(Msb < W, "MSB >= W"); - #endif - return ac::sliceref(Base::v); - } - - class ac_bitref { -# if defined(__SYNTHESIS__) && !defined(AC_IGNORE_BUILTINS) -# pragma builtin -# endif - ac_int &d_bv; - unsigned d_index; - public: - ac_bitref( ac_int *bv, unsigned index=0 ) : d_bv(*bv), d_index(index) {} - operator bool () const { return (d_index < W) ? (d_bv.v[d_index>>5]>>(d_index&31) & 1) : 0; } - - template - operator ac_int () const { return operator bool (); } - - inline ac_bitref operator = ( int val ) { - // lsb of int (val&1) is written to bit - if(d_index < W) { - int *pval = &d_bv.v[d_index>>5]; - *pval ^= (*pval ^ ( (unsigned) val << (d_index&31) )) & 1 << (d_index&31); - d_bv.bit_adjust(); // in case sign bit was assigned - } - return *this; - } - template - inline ac_bitref operator = ( const ac_int &val ) { - return operator =(val.to_int()); - } - inline ac_bitref operator = ( const ac_bitref &val ) { - return operator =((int) (bool) val); - } - }; - - ac_bitref operator [] ( unsigned int uindex) { - AC_ASSERT(uindex < W, "Attempting to read bit beyond MSB"); - ac_bitref bvh( this, uindex ); - return bvh; - } - ac_bitref operator [] ( int index) { - AC_ASSERT(index >= 0, "Attempting to read bit with negative index"); - unsigned uindex = index & ((unsigned)~0 >> 1); - AC_ASSERT(uindex < W, "Attempting to read bit beyond MSB"); - ac_bitref bvh( this, uindex ); - return bvh; - } - template - ac_bitref operator [] ( const ac_int &index) { - AC_ASSERT(index.to_int() >= 0, "Attempting to read bit with negative index"); - unsigned uindex = ac_int(index).to_uint(); - AC_ASSERT(uindex < W, "Attempting to read bit beyond MSB"); - ac_bitref bvh( this, uindex ); - return bvh; - } - bool operator [] ( unsigned int uindex) const { - AC_ASSERT(uindex < W, "Attempting to read bit beyond MSB"); - return (uindex < W) ? (Base::v[uindex>>5]>>(uindex&31) & 1) : 0; - } - bool operator [] ( int index) const { - AC_ASSERT(index >= 0, "Attempting to read bit with negative index"); - unsigned uindex = index & ((unsigned)~0 >> 1); - AC_ASSERT(uindex < W, "Attempting to read bit beyond MSB"); - return (uindex < W) ? (Base::v[uindex>>5]>>(uindex&31) & 1) : 0; - } - template - bool operator [] ( const ac_int &index) const { - AC_ASSERT(index.to_int() >= 0, "Attempting to read bit with negative index"); - unsigned uindex = ac_int(index).to_uint(); - AC_ASSERT(uindex < W, "Attempting to read bit beyond MSB"); - return (uindex < W) ? (Base::v[uindex>>5]>>(uindex&31) & 1) : 0; - } - - typename rt_unary::leading_sign leading_sign() const { - unsigned ls = Base::leading_bits(S & (Base::v[N-1] < 0)) - (32*N - W)-S; - return ls; - } - typename rt_unary::leading_sign leading_sign(bool &all_sign) const { - unsigned ls = Base::leading_bits(S & (Base::v[N-1] < 0)) - (32*N - W)-S; - all_sign = (ls == W-S); - return ls; - } - // returns false if number is denormal - template - bool normalize(ac_int &exp) { - return normalize_private(exp); - } - // returns false if number is denormal, minimum exponent is reserved (usually for encoding special values/errors) - template - bool normalize_RME(ac_int &exp) { - return normalize_private(exp, true); - } - bool and_reduce() const { - return ac_private::iv_equal_ones_to(Base::v); - } - bool or_reduce() const { - return !Base::equal_zero(); - } - bool xor_reduce() const { - unsigned r = Base::v[N-1]; - if(S) { - const unsigned rem = (32-W)&31; - r = (r << rem) >> rem; - } - if(N > 1) - r ^= Base::v[N-2]; - if(N > 2) { - for(int i=0; i 16) - r ^= r >> 16; - if(W > 8) - r ^= r >> 8; - if(W > 4) - r ^= r >> 4; - if(W > 2) - r ^= r >> 2; - if(W > 1) - r ^= r >> 1; - return r&1; - } - - inline void bit_fill_hex(const char *str) { - // Zero Pads if str is too short, throws ms bits away if str is too long - // Asserts if anything other than 0-9a-fA-F is encountered - ac_int res = 0; - while(*str) { - char c = *str; - int h = 0; - if(c >= '0' && c <= '9') - h = c - '0'; - else if(c >= 'A' && c <= 'F') - h = c - 'A' + 10; - else if(c >= 'a' && c <= 'f') - h = c - 'a' + 10; - else { - AC_ASSERT(!c, "Invalid hex digit"); - break; - } - res <<= ac_int<3,false>(4); - res |= ac_int<4,false>(h); - str++; - } - *this = res; - } - - template - inline void bit_fill(const int (&ivec)[Na], bool bigendian=true) { - // bit_fill from integer vector - // if W > N*32, missing most significant bits are zeroed - // if W < N*32, additional bits in ivec are ignored (no overflow checking) - // Example: - // ac_int<80,false> x; int vec[] = { 0xffffa987, 0x6543210f, 0xedcba987 }; - // x.bit_fill(vec); // vec[0] fill bits 79-64 - enum { N0 = (W+31)/32, M = AC_MIN(N0,Na) }; - ac_int res = 0; - for(int i=0; i < M; i++) - res.set_slc(i*32, ac_int<32>(ivec[bigendian ? M-1-i : i])); - *this = res; - } -}; - -namespace ac { - template - struct rt_2T { - typedef typename ac_private::map::t map_T; - typedef typename ac_private::map::t map_T2; - typedef typename map_T::template rt_T< map_T2 >::mult mult; - typedef typename map_T::template rt_T< map_T2 >::plus plus; - typedef typename map_T::template rt_T< map_T2 >::minus minus; - typedef typename map_T::template rt_T< map_T2 >::minus2 minus2; - typedef typename map_T::template rt_T< map_T2 >::logic logic; - typedef typename map_T::template rt_T< map_T2 >::div div; - typedef typename map_T::template rt_T< map_T2 >::div2 div2; - }; -} - -namespace ac { - template - struct ac_int_represent { - enum { t_w = ac_private::c_type_params::W, t_s = ac_private::c_type_params::S }; - typedef ac_int type; - }; - template<> struct ac_int_represent {}; - template<> struct ac_int_represent {}; - template - struct ac_int_represent< ac_int > { - typedef ac_int type; - }; -} - -namespace ac_private { - template - struct rt_ac_int_T< ac_int > { - typedef ac_int i2_t; - template - struct op1 { - typedef ac_int i_t; - typedef typename i_t::template rt::mult mult; - typedef typename i_t::template rt::plus plus; - typedef typename i_t::template rt::minus minus; - typedef typename i2_t::template rt::minus minus2; - typedef typename i_t::template rt::logic logic; - typedef typename i_t::template rt::div div; - typedef typename i2_t::template rt::div div2; - typedef typename i_t::template rt::mod mod; - typedef typename i2_t::template rt::mod mod2; - }; - }; - - template - struct rt_ac_int_T< c_type > { - typedef typename ac::ac_int_represent::type i2_t; - enum { W2 = i2_t::width, S2 = i2_t::sign }; - template - struct op1 { - typedef ac_int i_t; - typedef typename i_t::template rt::mult mult; - typedef typename i_t::template rt::plus plus; - typedef typename i_t::template rt::minus minus; - typedef typename i2_t::template rt::minus minus2; - typedef typename i_t::template rt::logic logic; - typedef typename i_t::template rt::div div; - typedef typename i2_t::template rt::div div2; - typedef typename i_t::template rt::mod mod; - typedef typename i2_t::template rt::mod mod2; - }; - }; -} - - -// Specializations for constructors on integers that bypass bit adjusting -// and are therefore more efficient -template<> inline ac_int<1,true>::ac_int( bool b ) { v[0] = b ? -1 : 0; } - -template<> inline ac_int<1,false>::ac_int( bool b ) { v[0] = b; } -template<> inline ac_int<1,false>::ac_int( signed char b ) { v[0] = b&1; } -template<> inline ac_int<1,false>::ac_int( unsigned char b ) { v[0] = b&1; } -template<> inline ac_int<1,false>::ac_int( signed short b ) { v[0] = b&1; } -template<> inline ac_int<1,false>::ac_int( unsigned short b ) { v[0] = b&1; } -template<> inline ac_int<1,false>::ac_int( signed int b ) { v[0] = b&1; } -template<> inline ac_int<1,false>::ac_int( unsigned int b ) { v[0] = b&1; } -template<> inline ac_int<1,false>::ac_int( signed long b ) { v[0] = b&1; } -template<> inline ac_int<1,false>::ac_int( unsigned long b ) { v[0] = b&1; } -template<> inline ac_int<1,false>::ac_int( Ulong b ) { v[0] = (int) b&1; } -template<> inline ac_int<1,false>::ac_int( Slong b ) { v[0] = (int) b&1; } - -template<> inline ac_int<8,true>::ac_int( bool b ) { v[0] = b; } -template<> inline ac_int<8,false>::ac_int( bool b ) { v[0] = b; } -template<> inline ac_int<8,true>::ac_int( signed char b ) { v[0] = b; } -template<> inline ac_int<8,false>::ac_int( unsigned char b ) { v[0] = b; } -template<> inline ac_int<8,true>::ac_int( unsigned char b ) { v[0] = (signed char) b; } -template<> inline ac_int<8,false>::ac_int( signed char b ) { v[0] = (unsigned char) b; } - -template<> inline ac_int<16,true>::ac_int( bool b ) { v[0] = b; } -template<> inline ac_int<16,false>::ac_int( bool b ) { v[0] = b; } -template<> inline ac_int<16,true>::ac_int( signed char b ) { v[0] = b; } -template<> inline ac_int<16,false>::ac_int( unsigned char b ) { v[0] = b; } -template<> inline ac_int<16,true>::ac_int( unsigned char b ) { v[0] = b; } -template<> inline ac_int<16,false>::ac_int( signed char b ) { v[0] = (unsigned short) b; } -template<> inline ac_int<16,true>::ac_int( signed short b ) { v[0] = b; } -template<> inline ac_int<16,false>::ac_int( unsigned short b ) { v[0] = b; } -template<> inline ac_int<16,true>::ac_int( unsigned short b ) { v[0] = (signed short) b; } -template<> inline ac_int<16,false>::ac_int( signed short b ) { v[0] = (unsigned short) b; } - -template<> inline ac_int<32,true>::ac_int( signed int b ) { v[0] = b; } -template<> inline ac_int<32,true>::ac_int( unsigned int b ) { v[0] = b; } -template<> inline ac_int<32,false>::ac_int( signed int b ) { v[0] = b; v[1] = 0;} -template<> inline ac_int<32,false>::ac_int( unsigned int b ) { v[0] = b; v[1] = 0;} - -template<> inline ac_int<32,true>::ac_int( Slong b ) { v[0] = (int) b; } -template<> inline ac_int<32,true>::ac_int( Ulong b ) { v[0] = (int) b; } -template<> inline ac_int<32,false>::ac_int( Slong b ) { v[0] = (int) b; v[1] = 0;} -template<> inline ac_int<32,false>::ac_int( Ulong b ) { v[0] = (int) b; v[1] = 0;} - -template<> inline ac_int<64,true>::ac_int( Slong b ) { v[0] = (int) b; v[1] = (int) (b >> 32); } -template<> inline ac_int<64,true>::ac_int( Ulong b ) { v[0] = (int) b; v[1] = (int) (b >> 32);} -template<> inline ac_int<64,false>::ac_int( Slong b ) { v[0] = (int) b; v[1] = (int) ((Ulong) b >> 32); v[2] = 0; } -template<> inline ac_int<64,false>::ac_int( Ulong b ) { v[0] = (int) b; v[1] = (int) (b >> 32); v[2] = 0; } - -// Stream -------------------------------------------------------------------- - -template -inline std::ostream& operator << (std::ostream &os, const ac_int &x) { -#ifndef __SYNTHESIS__ - if ((os.flags() & std::ios::hex) != 0) { - os << x.to_string(AC_HEX); - } else if ((os.flags() & std::ios::oct) != 0) { - os << x.to_string(AC_OCT); - } else { - os << x.to_string(AC_DEC); - } -#endif - return os; -} - -// Macros for Binary Operators with Integers -------------------------------------------- - -#define BIN_OP_WITH_INT(BIN_OP, C_TYPE, WI, SI, RTYPE) \ - template \ - inline typename ac_int::template rt::RTYPE operator BIN_OP ( C_TYPE i_op, const ac_int &op) { \ - return ac_int(i_op).operator BIN_OP (op); \ - } \ - template \ - inline typename ac_int::template rt::RTYPE operator BIN_OP ( const ac_int &op, C_TYPE i_op) { \ - return op.operator BIN_OP (ac_int(i_op)); \ - } - -#define REL_OP_WITH_INT(REL_OP, C_TYPE, W2, S2) \ - template \ - inline bool operator REL_OP ( const ac_int &op, C_TYPE op2) { \ - return op.operator REL_OP (ac_int(op2)); \ - } \ - template \ - inline bool operator REL_OP ( C_TYPE op2, const ac_int &op) { \ - return ac_int(op2).operator REL_OP (op); \ - } - -#define ASSIGN_OP_WITH_INT(ASSIGN_OP, C_TYPE, W2, S2) \ - template \ - inline ac_int &operator ASSIGN_OP ( ac_int &op, C_TYPE op2) { \ - return op.operator ASSIGN_OP (ac_int(op2)); \ - } - -#define OPS_WITH_INT(C_TYPE, WI, SI) \ - BIN_OP_WITH_INT(*, C_TYPE, WI, SI, mult) \ - BIN_OP_WITH_INT(+, C_TYPE, WI, SI, plus) \ - BIN_OP_WITH_INT(-, C_TYPE, WI, SI, minus) \ - BIN_OP_WITH_INT(/, C_TYPE, WI, SI, div) \ - BIN_OP_WITH_INT(%, C_TYPE, WI, SI, mod) \ - BIN_OP_WITH_INT(>>, C_TYPE, WI, SI, arg1) \ - BIN_OP_WITH_INT(<<, C_TYPE, WI, SI, arg1) \ - BIN_OP_WITH_INT(&, C_TYPE, WI, SI, logic) \ - BIN_OP_WITH_INT(|, C_TYPE, WI, SI, logic) \ - BIN_OP_WITH_INT(^, C_TYPE, WI, SI, logic) \ - \ - REL_OP_WITH_INT(==, C_TYPE, WI, SI) \ - REL_OP_WITH_INT(!=, C_TYPE, WI, SI) \ - REL_OP_WITH_INT(>, C_TYPE, WI, SI) \ - REL_OP_WITH_INT(>=, C_TYPE, WI, SI) \ - REL_OP_WITH_INT(<, C_TYPE, WI, SI) \ - REL_OP_WITH_INT(<=, C_TYPE, WI, SI) \ - \ - ASSIGN_OP_WITH_INT(+=, C_TYPE, WI, SI) \ - ASSIGN_OP_WITH_INT(-=, C_TYPE, WI, SI) \ - ASSIGN_OP_WITH_INT(*=, C_TYPE, WI, SI) \ - ASSIGN_OP_WITH_INT(/=, C_TYPE, WI, SI) \ - ASSIGN_OP_WITH_INT(%=, C_TYPE, WI, SI) \ - ASSIGN_OP_WITH_INT(>>=, C_TYPE, WI, SI) \ - ASSIGN_OP_WITH_INT(<<=, C_TYPE, WI, SI) \ - ASSIGN_OP_WITH_INT(&=, C_TYPE, WI, SI) \ - ASSIGN_OP_WITH_INT(|=, C_TYPE, WI, SI) \ - ASSIGN_OP_WITH_INT(^=, C_TYPE, WI, SI) - -// ------------------------------------- End of Macros for Binary Operators with Integers - -// for backward compatability with v3.9.0 and earlier define following macro -#ifdef AC_INT_NS_FOR_MIXED_OPERATORS -namespace ac { - namespace ops_with_other_types { -#endif -// Mixed Operators with Integers ----------------------------------------------- -OPS_WITH_INT(bool, 1, false) -OPS_WITH_INT(char, 8, true) -OPS_WITH_INT(signed char, 8, true) -OPS_WITH_INT(unsigned char, 8, false) -OPS_WITH_INT(short, 16, true) -OPS_WITH_INT(unsigned short, 16, false) -OPS_WITH_INT(int, 32, true) -OPS_WITH_INT(unsigned int, 32, false) -OPS_WITH_INT(long, ac_private::long_w, true) -OPS_WITH_INT(unsigned long, ac_private::long_w, false) -OPS_WITH_INT(Slong, 64, true) -OPS_WITH_INT(Ulong, 64, false) -// ----------------------------------------- End of Mixed Operators with Integers -#ifdef AC_INT_NS_FOR_MIXED_OPERATORS - } // ops_with_other_types namespace -} -using namespace ac::ops_with_other_types; -#endif - -namespace ac { - // Functions to fill bits - - template - inline T bit_fill_hex(const char *str) { - T res; - res.bit_fill_hex(str); - return res; - } - - // returns bit_fill for type - // example: - // ac_int<80,false> x = ac::bit_fill< ac_int<80,false> > ((int [3]) {0xffffa987, 0x6543210f, 0xedcba987 }); - template - inline T bit_fill(const int (&ivec)[N], bool bigendian=true) { - T res; - res.bit_fill(ivec, bigendian); - return res; - } - -} // ac namespace - -// Mixed Operators with Pointers ----------------------------------------------- - -// Addition of ac_int and pointer -template -T *operator +(T *ptr, const ac_int &op2) { - return ptr + op2.to_int64(); -} -template -T *operator +(const ac_int &op2, T *ptr) { - return ptr + op2.to_int64(); -} -// Subtraction of ac_int from pointer -template -T *operator -(T *ptr, const ac_int &op2) { - return ptr - op2.to_int64(); -} -// ----------------------------------------- End of Mixed Operators with Pointers - -namespace ac_intN { - /////////////////////////////////////////////////////////////////////////////// - // Predefined for ease of use - /////////////////////////////////////////////////////////////////////////////// - typedef ac_int<1, true> int1; - typedef ac_int<1, false> uint1; - typedef ac_int<2, true> int2; - typedef ac_int<2, false> uint2; - typedef ac_int<3, true> int3; - typedef ac_int<3, false> uint3; - typedef ac_int<4, true> int4; - typedef ac_int<4, false> uint4; - typedef ac_int<5, true> int5; - typedef ac_int<5, false> uint5; - typedef ac_int<6, true> int6; - typedef ac_int<6, false> uint6; - typedef ac_int<7, true> int7; - typedef ac_int<7, false> uint7; - typedef ac_int<8, true> int8; - typedef ac_int<8, false> uint8; - typedef ac_int<9, true> int9; - typedef ac_int<9, false> uint9; - typedef ac_int<10, true> int10; - typedef ac_int<10, false> uint10; - typedef ac_int<11, true> int11; - typedef ac_int<11, false> uint11; - typedef ac_int<12, true> int12; - typedef ac_int<12, false> uint12; - typedef ac_int<13, true> int13; - typedef ac_int<13, false> uint13; - typedef ac_int<14, true> int14; - typedef ac_int<14, false> uint14; - typedef ac_int<15, true> int15; - typedef ac_int<15, false> uint15; - typedef ac_int<16, true> int16; - typedef ac_int<16, false> uint16; - typedef ac_int<17, true> int17; - typedef ac_int<17, false> uint17; - typedef ac_int<18, true> int18; - typedef ac_int<18, false> uint18; - typedef ac_int<19, true> int19; - typedef ac_int<19, false> uint19; - typedef ac_int<20, true> int20; - typedef ac_int<20, false> uint20; - typedef ac_int<21, true> int21; - typedef ac_int<21, false> uint21; - typedef ac_int<22, true> int22; - typedef ac_int<22, false> uint22; - typedef ac_int<23, true> int23; - typedef ac_int<23, false> uint23; - typedef ac_int<24, true> int24; - typedef ac_int<24, false> uint24; - typedef ac_int<25, true> int25; - typedef ac_int<25, false> uint25; - typedef ac_int<26, true> int26; - typedef ac_int<26, false> uint26; - typedef ac_int<27, true> int27; - typedef ac_int<27, false> uint27; - typedef ac_int<28, true> int28; - typedef ac_int<28, false> uint28; - typedef ac_int<29, true> int29; - typedef ac_int<29, false> uint29; - typedef ac_int<30, true> int30; - typedef ac_int<30, false> uint30; - typedef ac_int<31, true> int31; - typedef ac_int<31, false> uint31; - typedef ac_int<32, true> int32; - typedef ac_int<32, false> uint32; - typedef ac_int<33, true> int33; - typedef ac_int<33, false> uint33; - typedef ac_int<34, true> int34; - typedef ac_int<34, false> uint34; - typedef ac_int<35, true> int35; - typedef ac_int<35, false> uint35; - typedef ac_int<36, true> int36; - typedef ac_int<36, false> uint36; - typedef ac_int<37, true> int37; - typedef ac_int<37, false> uint37; - typedef ac_int<38, true> int38; - typedef ac_int<38, false> uint38; - typedef ac_int<39, true> int39; - typedef ac_int<39, false> uint39; - typedef ac_int<40, true> int40; - typedef ac_int<40, false> uint40; - typedef ac_int<41, true> int41; - typedef ac_int<41, false> uint41; - typedef ac_int<42, true> int42; - typedef ac_int<42, false> uint42; - typedef ac_int<43, true> int43; - typedef ac_int<43, false> uint43; - typedef ac_int<44, true> int44; - typedef ac_int<44, false> uint44; - typedef ac_int<45, true> int45; - typedef ac_int<45, false> uint45; - typedef ac_int<46, true> int46; - typedef ac_int<46, false> uint46; - typedef ac_int<47, true> int47; - typedef ac_int<47, false> uint47; - typedef ac_int<48, true> int48; - typedef ac_int<48, false> uint48; - typedef ac_int<49, true> int49; - typedef ac_int<49, false> uint49; - typedef ac_int<50, true> int50; - typedef ac_int<50, false> uint50; - typedef ac_int<51, true> int51; - typedef ac_int<51, false> uint51; - typedef ac_int<52, true> int52; - typedef ac_int<52, false> uint52; - typedef ac_int<53, true> int53; - typedef ac_int<53, false> uint53; - typedef ac_int<54, true> int54; - typedef ac_int<54, false> uint54; - typedef ac_int<55, true> int55; - typedef ac_int<55, false> uint55; - typedef ac_int<56, true> int56; - typedef ac_int<56, false> uint56; - typedef ac_int<57, true> int57; - typedef ac_int<57, false> uint57; - typedef ac_int<58, true> int58; - typedef ac_int<58, false> uint58; - typedef ac_int<59, true> int59; - typedef ac_int<59, false> uint59; - typedef ac_int<60, true> int60; - typedef ac_int<60, false> uint60; - typedef ac_int<61, true> int61; - typedef ac_int<61, false> uint61; - typedef ac_int<62, true> int62; - typedef ac_int<62, false> uint62; - typedef ac_int<63, true> int63; - typedef ac_int<63, false> uint63; -} // namespace ac_intN - -#ifndef AC_NOT_USING_INTN -using namespace ac_intN; -#endif - -/////////////////////////////////////////////////////////////////////////////// - -#if (defined(_MSC_VER) && !defined(__EDG__)) -#pragma warning( disable: 4700 ) -#endif -#if (defined(__GNUC__) && ( __GNUC__ == 4 && __GNUC_MINOR__ >= 6 || __GNUC__ > 4 ) && !defined(__EDG__)) -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wuninitialized" -#endif -#if defined(__clang__) -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wuninitialized" -#endif - -// Global templatized functions for easy initialization to special values -template -inline ac_int value(ac_int) { - ac_int r; - return r.template set_val(); -} -// forward declaration, otherwise GCC errors when calling init_array -template -inline ac_fixed value(ac_fixed); - -#define SPECIAL_VAL_FOR_INTS_DC(C_TYPE, WI, SI) \ -template<> inline C_TYPE value(C_TYPE) { C_TYPE x; return x; } - -// -- C int types ----------------------------------------------------------------- -#define SPECIAL_VAL_FOR_INTS(C_TYPE, WI, SI) \ -template inline C_TYPE value(C_TYPE); \ -template<> inline C_TYPE value(C_TYPE) { return (C_TYPE)0; } \ -SPECIAL_VAL_FOR_INTS_DC(C_TYPE, WI, SI) \ -template<> inline C_TYPE value(C_TYPE) { return (C_TYPE)1; } \ -template<> inline C_TYPE value(C_TYPE) { return (C_TYPE)(SI ? ~(((C_TYPE) 1) << (WI-1)) : (C_TYPE) -1); } \ -template<> inline C_TYPE value(C_TYPE) { return (C_TYPE)(SI ? ((C_TYPE) 1) << (WI-1) : (C_TYPE) 0); } - -SPECIAL_VAL_FOR_INTS(bool, 1, false) -SPECIAL_VAL_FOR_INTS(char, 8, true) -SPECIAL_VAL_FOR_INTS(signed char, 8, true) -SPECIAL_VAL_FOR_INTS(unsigned char, 8, false) -SPECIAL_VAL_FOR_INTS(short, 16, true) -SPECIAL_VAL_FOR_INTS(unsigned short, 16, false) -SPECIAL_VAL_FOR_INTS(int, 32, true) -SPECIAL_VAL_FOR_INTS(unsigned int, 32, false) -SPECIAL_VAL_FOR_INTS(long, ac_private::long_w, true) -SPECIAL_VAL_FOR_INTS(unsigned long, ac_private::long_w, false) -SPECIAL_VAL_FOR_INTS(Slong, 64, true) -SPECIAL_VAL_FOR_INTS(Ulong, 64, false) - -#define INIT_ARRAY_SPECIAL_VAL_FOR_INTS(C_TYPE) \ - template \ - inline bool init_array(C_TYPE *a, int n) { \ - C_TYPE t = value((C_TYPE) 0); \ - for(int i=0; i < n; i++) \ - a[i] = t; \ - return true; \ - } - -namespace ac { -// PUBLIC FUNCTIONS -// function to initialize (or uninitialize) arrays - template - inline bool init_array(ac_int *a, int n) { - ac_int t; - t.template set_val(); - for(int i=0; i < n; i++) - a[i] = t; - return true; - } - - INIT_ARRAY_SPECIAL_VAL_FOR_INTS(bool) - INIT_ARRAY_SPECIAL_VAL_FOR_INTS(char) - INIT_ARRAY_SPECIAL_VAL_FOR_INTS(signed char) - INIT_ARRAY_SPECIAL_VAL_FOR_INTS(unsigned char) - INIT_ARRAY_SPECIAL_VAL_FOR_INTS(signed short) - INIT_ARRAY_SPECIAL_VAL_FOR_INTS(unsigned short) - INIT_ARRAY_SPECIAL_VAL_FOR_INTS(signed int) - INIT_ARRAY_SPECIAL_VAL_FOR_INTS(unsigned int) - INIT_ARRAY_SPECIAL_VAL_FOR_INTS(signed long) - INIT_ARRAY_SPECIAL_VAL_FOR_INTS(unsigned long) - INIT_ARRAY_SPECIAL_VAL_FOR_INTS(signed long long) - INIT_ARRAY_SPECIAL_VAL_FOR_INTS(unsigned long long) -} - -#if (defined(_MSC_VER) && !defined(__EDG__)) -#pragma warning( pop ) -#endif -#if (defined(__GNUC__) && ( __GNUC__ == 4 && __GNUC_MINOR__ >= 6 || __GNUC__ > 4 ) && !defined(__EDG__)) -#pragma GCC diagnostic pop -#endif -#if defined(__clang__) -#pragma clang diagnostic pop -#endif - -#ifdef __AC_NAMESPACE -} -#endif - -#endif // __AC_INT_H diff --git a/hls4ml/templates/quartus/ac_types/ac_sc.h b/hls4ml/templates/quartus/ac_types/ac_sc.h deleted file mode 100644 index 01601a5a4a..0000000000 --- a/hls4ml/templates/quartus/ac_types/ac_sc.h +++ /dev/null @@ -1,552 +0,0 @@ -/************************************************************************** - * * - * Algorithmic C (tm) Datatypes * - * * - * Software Version: 4.0 * - * * - * Release Date : Sat Jun 13 12:35:18 PDT 2020 * - * Release Type : Production Release * - * Release Build : 4.0.0 * - * * - * Copyright 2004-2019, Mentor Graphics Corporation, * - * * - * All Rights Reserved. * - * * - ************************************************************************** - * Licensed under the Apache License, Version 2.0 (the "License"); * - * you may not use this file except in compliance with the License. * - * You may obtain a copy of the License at * - * * - * http://www.apache.org/licenses/LICENSE-2.0 * - * * - * Unless required by applicable law or agreed to in writing, software * - * distributed under the License is distributed on an "AS IS" BASIS, * - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * - * implied. * - * See the License for the specific language governing permissions and * - * limitations under the License. * - ************************************************************************** - * * - * The most recent version of this package is available at github. * - * * - *************************************************************************/ - -#ifndef __AC_SC_H -#define __AC_SC_H - -#ifndef __cplusplus -#error C++ is required to include this header file -#endif - -#if !defined(IEEE_1666_SYSTEMC) && !defined(SYSTEMC_VERSION) && !defined(SC_API_VERSION_STRING) -#error SystemC header file needs to be included before the ac_sc is included -#endif - -#include - -#ifdef __AC_NAMESPACE -namespace __AC_NAMESPACE { -#endif - -// Explicit conversion functions from ac to sc and viceversa -template -ac_int to_ac(const sc_dt::sc_bigint &val){ - enum {N = (W+31)/32 }; - sc_dt::sc_bigint v = val; - ac_int r = 0; -#ifdef __SYNTHESIS__ -#pragma UNROLL y -#endif - for(int i = 0; i < N; i++) { - r.set_slc(i*32, ac_int<32,true>(v.to_int())); - v >>= 32; - } - return ac_int(r); -} - -template -ac_int to_ac(const sc_dt::sc_biguint &val){ - enum {N = (W+31)/32 }; - sc_dt::sc_biguint v = val; - ac_int r = 0; -#ifdef __SYNTHESIS__ -#pragma UNROLL y -#endif - for(int i = 0; i < N; i++) { - r.set_slc(i*32, ac_int<32,true>(v.to_int())); - v >>= 32; - } - return ac_int(r); -} - -template -sc_dt::sc_bigint to_sc(const ac_int &val) { - enum {N = (W+31)/32 }; - ac_int v = val; - sc_dt::sc_bigint r; -#ifdef __SYNTHESIS__ -#pragma UNROLL y -#endif - for(int i = N-1; i >= 0; i--) { - r <<= 32; - r.range(31, 0) = (v.template slc<32>(i*32)).to_int(); - } - return sc_dt::sc_bigint(r); -} - -template -sc_dt::sc_biguint to_sc(const ac_int &val) { - enum {N = (W+31)/32 }; - ac_int v = val; - sc_dt::sc_biguint r; -#ifdef __SYNTHESIS__ -#pragma UNROLL y -#endif - for(int i = N-1; i >= 0; i--) { - r <<= 32; - r.range(31, 0) = (v.template slc<32>(i*32)).to_int(); - } - return sc_dt::sc_biguint(r); -} - -#ifdef SC_INCLUDE_FX -template -ac_fixed to_ac(const sc_dt::sc_fixed &val){ - ac_fixed r = 0; - sc_dt::sc_fixed fv; - fv.range(W-1,0) = val.range(W-1,0); - sc_dt::sc_bigint v(fv); - r.set_slc(0, to_ac(v)); - return r; -} - -template -ac_fixed to_ac(const sc_dt::sc_ufixed &val){ - ac_fixed r = 0; - sc_dt::sc_ufixed fv; - fv.range(W-1,0) = val.range(W-1,0); - sc_dt::sc_biguint v(fv); - r.set_slc(0, to_ac(v)); - return r; -} - -template -sc_dt::sc_fixed to_sc(const ac_fixed &val) { - ac_int v = val.template slc(0); - sc_dt::sc_bigint i = to_sc(v); - sc_dt::sc_fixed f(i); - sc_dt::sc_fixed r; - r.range(W-1,0) = f.range(W-1,0); - return r; -} - -template -sc_dt::sc_ufixed to_sc(const ac_fixed &val) { - ac_int v = val.template slc(0); - sc_dt::sc_biguint i = to_sc(v); - sc_dt::sc_ufixed f(i); - sc_dt::sc_ufixed r; - r.range(W-1,0) = f.range(W-1,0); - return r; -} -#endif - -// Utility global functions for initialization - -template -inline sc_dt::sc_int value(sc_dt::sc_int) { - sc_dt::sc_int r; - if(V == AC_VAL_DC) { - int t; - r = t; - } else if(V == AC_VAL_0 || V == AC_VAL_MIN || V == AC_VAL_QUANTUM) { - r = 0; - if(V == AC_VAL_MIN) - r[W-1] = 1; - else if(V == AC_VAL_QUANTUM) - r[0] = 1; - } else if(AC_VAL_MAX) { - r = -1; - r[W-1] = 0; - } - return r; -} - -template -inline sc_dt::sc_uint value(sc_dt::sc_uint) { - sc_dt::sc_uint r; - if(V == AC_VAL_DC) { - int t; - r = t; - } else if(V == AC_VAL_0 || V == AC_VAL_MIN || V == AC_VAL_QUANTUM) { - r = 0; - if(V == AC_VAL_QUANTUM) - r[0] = 1; - } else if(AC_VAL_MAX) - r = -1; - return r; -} - -template -inline sc_dt::sc_bigint value(sc_dt::sc_bigint) { - sc_dt::sc_bigint r; - if(V == AC_VAL_DC) { - int t; - r = t; - } else if(V == AC_VAL_0 || V == AC_VAL_MIN || V == AC_VAL_QUANTUM) { - r = 0; - if(V == AC_VAL_MIN) - r[W-1] = 1; - else if(V == AC_VAL_QUANTUM) - r[0] = 1; - } else if(AC_VAL_MAX) { - r = -1; - r[W-1] = 0; - } - return r; -} - -template -inline sc_dt::sc_biguint value(sc_dt::sc_biguint) { - sc_dt::sc_biguint r; - if(V == AC_VAL_DC) { - int t; - r = t; - } else if(V == AC_VAL_0 || V == AC_VAL_MIN || V == AC_VAL_QUANTUM) { - r = 0; - if(V == AC_VAL_QUANTUM) - r[0] = 1; - } else if(AC_VAL_MAX) - r = -1; - return r; -} - -#ifdef SC_INCLUDE_FX -template -inline sc_dt::sc_fixed value(sc_dt::sc_fixed) { - sc_dt::sc_fixed r; - if(V == AC_VAL_DC) { - int t; - r = t; - } else if(V == AC_VAL_0 || V == AC_VAL_MIN || V == AC_VAL_QUANTUM) { - r = 0; - if(V == AC_VAL_MIN) - r[W-1] = 1; - else if(V == AC_VAL_QUANTUM) - r[0] = 1; - } else if(AC_VAL_MAX) { - r = ~ (sc_dt::sc_fixed) 0; - r[W-1] = 0; - } - return r; -} - -template -inline sc_dt::sc_ufixed value(sc_dt::sc_ufixed) { - sc_dt::sc_ufixed r; - if(V == AC_VAL_DC) { - int t; - r = t; - } else if(V == AC_VAL_0 || V == AC_VAL_MIN || V == AC_VAL_QUANTUM) { - r = 0; - if(V == AC_VAL_QUANTUM) - r[0] = 1; - } else if(AC_VAL_MAX) - r = ~ (sc_dt::sc_ufixed) 0; - return r; -} -#endif - - -namespace ac { -// PUBLIC FUNCTIONS -// function to initialize (or uninitialize) arrays - template - inline bool init_array(sc_dt::sc_int *a, int n) { - sc_dt::sc_int t = value(*a); - for(int i=0; i < n; i++) - a[i] = t; - return true; - } - template - inline bool init_array(sc_dt::sc_uint *a, int n) { - sc_dt::sc_uint t = value(*a); - for(int i=0; i < n; i++) - a[i] = t; - return true; - } - template - inline bool init_array(sc_dt::sc_bigint *a, int n) { - sc_dt::sc_bigint t = value(*a); - for(int i=0; i < n; i++) - a[i] = t; - return true; - } - template - inline bool init_array(sc_dt::sc_biguint *a, int n) { - sc_dt::sc_biguint t = value(*a); - for(int i=0; i < n; i++) - a[i] = t; - return true; - } -#ifdef SC_INCLUDE_FX - template - inline bool init_array(sc_dt::sc_fixed *a, int n) { - sc_dt::sc_fixed t = value(*a); - for(int i=0; i < n; i++) - a[i] = t; - return true; - } - template - inline bool init_array(sc_dt::sc_ufixed *a, int n) { - sc_dt::sc_ufixed t = value(*a); - for(int i=0; i < n; i++) - a[i] = t; - return true; - } -#endif -} - -#ifdef __AC_NAMESPACE -} -#endif - - -// TRACE FUNCTIONS - -// SystemC Versions - 2.2.0 20070314 -// 2.3.0 20120701 -// 2.3.1 20140417 -// 2.3.2 20171012 - -#if !defined(NCSC) -#if (SYSTEMC_VERSION >= 20140417) && !defined(SC_TRACE_FILE_BASE_H_INCLUDED_) -namespace sc_core { -class vcd_trace; -class sc_trace_file_base - : public sc_trace_file -{ -public: - enum vcd_enum {VCD_WIRE=0, VCD_REAL, VCD_EVENT, VCD_TIME, VCD_LAST}; - virtual void do_initialize() = 0; - FILE* fp; -#if (SYSTEMC_VERSION >= 20171012) - sc_time::value_type trace_unit_fs, kernel_unit_fs; -#else - double timescale_unit; -#endif - bool timescale_set_by_user; - std::string filename_; - bool initialized_; - bool trace_delta_cycles_; - virtual ~sc_trace_file_base(); -}; -class vcd_trace_file - : public sc_trace_file_base -{ -public: - ~vcd_trace_file(); - std::string obtain_name(); - virtual void do_initialize(); - unsigned vcd_name_index; -#if (SYSTEMC_VERSION >= 20171012) - sc_time::value_type previous_time_units_low, previous_time_units_high; -#else - unsigned previous_time_units_low, previous_time_units_high; -#endif - std::vector traces; -}; -} -#endif - -namespace sc_core { -//============================================================================== -// The following block of code is copied from the file sc_vcd_trace.cpp in the -// SystemC distribution. This code should have been placed in the file -// sc_vcd_trace.h to allow proper C++ derivation. -class vcd_trace -{ -public: - vcd_trace(const std::string& name_, const std::string& vcd_name_); - virtual void write(FILE* f) = 0; - virtual void set_width(); - virtual bool changed() = 0; -#if (SYSTEMC_VERSION >= 20171012) - virtual void print_variable_declaration_line(FILE* f, const char* scoped_name); -#else - virtual void print_variable_declaration_line(FILE* f); -#endif - void compose_data_line(char* rawdata, char* compdata); - -#if (SYSTEMC_VERSION >= 20140417) - std::string compose_line(const std::string& data); -#else - std::string compose_line(const std::string data); -#endif - virtual ~vcd_trace(); - const std::string name; - const std::string vcd_name; -#if (SYSTEMC_VERSION >= 20171012) - vcd_trace_file::vcd_enum vcd_var_type; -#else - const char* vcd_var_typ_name; -#endif - int bit_width; -}; -} -#endif - -#ifdef __AC_NAMESPACE -namespace __AC_NAMESPACE { -#endif - -namespace ac_tracing { - -//============================================================================== -// TRACING SUPPORT FOR AC_INT -template -class vcd_ac_int_trace : public sc_core::vcd_trace -{ -public: - vcd_ac_int_trace(const ac_int &object_, const std::string& name_, const std::string& vcd_name_) : - vcd_trace(name_, vcd_name_), object(object_) - { -#if (SYSTEMC_VERSION >= 20171012) - vcd_var_type = sc_core::vcd_trace_file::VCD_WIRE; -#else - vcd_var_typ_name = "wire"; // SystemC does not expose vcd_types[] in sc_vcd_trace.h -#endif - bit_width = W; // bit_width defined in base class 'vcd_trace' - } - - virtual void write(FILE* f) { - // The function to_string(AC_BIN) returns a string with the zero-radix prefix (i.e. "0b"). - // Strip that prefix off because compose_line will add its own. - std::fprintf(f, "%s", compose_line(((ac_int)object).to_string(AC_BIN,true).substr(3)).c_str()); - old_value = object; - } - - virtual void set_width() { bit_width = W; } - - // Comparison function needs to be pure virtual too - virtual bool changed() { return !(object == old_value); } - - virtual ~vcd_ac_int_trace() {} -protected: - const ac_int &object; - ac_int old_value; -}; - -template -inline void sc_trace(sc_core::sc_trace_file *tf, const ac_int &a, const std::string &name) -{ - using namespace sc_core; - if (tf) { - vcd_trace *t = (vcd_trace*) new vcd_ac_int_trace(a,name,((vcd_trace_file*)tf)->obtain_name()); - ((vcd_trace_file*)tf)->traces.push_back(t); - } -} -//============================================================================== - -#if !defined(__AC_FIXED_MTI_H) -// The ac_fixed.h shipped with ModelSim/QuestaSim has a stub for sc_trace() for ac_fixed so -// this code is not used. The stub should be removed in a future release of the simulator. -#if defined(__AC_FIXED_H) && !defined(SC_TRACE_AC_FIXED) -#define SC_TRACE_AC_FIXED -//============================================================================== -// TRACING SUPPORT FOR AC_FIXED -template -inline void sc_trace(sc_core::sc_trace_file *tf, const ac_fixed &a, const std::string &name) -{ - const int iv_N = (W+31+!S)/32; - typedef typename ac_private::template iv CommonBase_t; - sc_trace(tf, *(const ac_int*)(const CommonBase_t*) &a, name); -} -//============================================================================== -#endif -#endif - -#if defined(__AC_FLOAT_H) && !defined(SC_TRACE_AC_FLOAT) -#define SC_TRACE_AC_FLOAT -//============================================================================== -// TRACING SUPPORT FOR AC_FLOAT -template -inline void sc_trace(sc_core::sc_trace_file *tf, const ac_float &a, const std::string &name) -{ - sc_trace(tf, a.m, name + ".m"); - sc_trace(tf, a.e, name + ".e"); -} -//============================================================================== -#endif - -#if defined(__AC_STD_FLOAT_H) && !defined(SC_TRACE_AC_STD_FLOAT) -#define SC_TRACE_AC_STD_FLOAT -//============================================================================== -// TRACING SUPPORT FOR AC_STD_FLOAT -template -inline void sc_trace(sc_core::sc_trace_file *tf, const ac_std_float &a, const std::string &name) -{ - sc_trace(tf, a.data(), name + ".d"); -} -//============================================================================== -//============================================================================== -// TRACING SUPPORT FOR AC_IEEE_FLOAT -inline void sc_trace(sc_core::sc_trace_file *tf, const ac_ieee_float &a, const std::string &name) -{ - sc_trace(tf, a.data(), name + ".d"); -} -inline void sc_trace(sc_core::sc_trace_file *tf, const ac_ieee_float &a, const std::string &name) -{ - sc_trace(tf, *(const int*) &a.data(), name + ".d"); -} -inline void sc_trace(sc_core::sc_trace_file *tf, const ac_ieee_float &a, const std::string &name) -{ - sc_trace(tf, *(const long long*) &a.data(), name + ".d"); -} -inline void sc_trace(sc_core::sc_trace_file *tf, const ac_ieee_float &a, const std::string &name) -{ - sc_trace(tf, ((const long long*) &a.data())[0], name + ".d0"); - sc_trace(tf, ((const long long*) &a.data())[1], name + ".d1"); -} -inline void sc_trace(sc_core::sc_trace_file *tf, const ac_ieee_float &a, const std::string &name) -{ - sc_trace(tf, ((const long long*) &a.data())[0], name + ".d0"); - sc_trace(tf, ((const long long*) &a.data())[1], name + ".d1"); - sc_trace(tf, ((const long long*) &a.data())[2], name + ".d2"); - sc_trace(tf, ((const long long*) &a.data())[3], name + ".d3"); -} -// TRACING SUPPORT FOR AC::BFLOAT16 -inline void sc_trace(sc_core::sc_trace_file *tf, const ac::bfloat16 &a, const std::string &name) -{ - sc_trace(tf, a.data(), name + ".d"); -} -//============================================================================== -#endif - -#if defined(__AC_COMPLEX_H) && !defined(SC_TRACE_AC_COMPLEX) -#define SC_TRACE_AC_COMPLEX -//============================================================================== -// TRACING SUPPORT FOR AC_COMPLEX -template -inline void sc_trace(sc_core::sc_trace_file *tf, const ac_complex &a, const std::string &name) -{ - sc_trace(tf, a.real(), name + ".r"); - sc_trace(tf, a.imag(), name + ".i"); -} -#endif - -} // namespace ac_tracing - -#ifdef __AC_NAMESPACE -} -#endif - -namespace sc_core { -#ifdef __AC_NAMESPACE - using __AC_NAMESPACE::ac_tracing::sc_trace; -#else - using ac_tracing::sc_trace; -#endif -} - -#endif diff --git a/hls4ml/templates/quartus/ac_types/ac_std_float.h b/hls4ml/templates/quartus/ac_types/ac_std_float.h deleted file mode 100644 index 3b335b971b..0000000000 --- a/hls4ml/templates/quartus/ac_types/ac_std_float.h +++ /dev/null @@ -1,2318 +0,0 @@ -/************************************************************************** - * * - * Algorithmic C (tm) Datatypes * - * * - * Software Version: 4.0 * - * * - * Release Date : Sat Jun 13 12:35:18 PDT 2020 * - * Release Type : Production Release * - * Release Build : 4.0.0 * - * * - * Copyright 2018-2020, Mentor Graphics Corporation, * - * * - * All Rights Reserved. * - * * - ************************************************************************** - * Licensed under the Apache License, Version 2.0 (the "License"); * - * you may not use this file except in compliance with the License. * - * You may obtain a copy of the License at * - * * - * http://www.apache.org/licenses/LICENSE-2.0 * - * * - * Unless required by applicable law or agreed to in writing, software * - * distributed under the License is distributed on an "AS IS" BASIS, * - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * - * implied. * - * See the License for the specific language governing permissions and * - * limitations under the License. * - ************************************************************************** - * * - * The most recent version of this package is available at github. * - * * - *************************************************************************/ - -/* Source: ac_std_float.h - * Description: class for floating point operation handling in C++ - * Author: Andres Takach, Ph.D. - -Overview: this header defines three classes - - ac_ieee_float - Meant to store floats in IEEE standard binary format - Format indicate width: - binary16: (half float) uses short - binary32: (float) uses int - binary64: (double) uses array of long long with one element - binary128: (long double in some platforms) uses array of long long with two elements - binary256: uses array of long long with four elements - - ac::bfloat16 - Implements Google's tensorflow::bfloat16 - Stores data as "short" - - ac_std_float - Superset of ac_ieee_float in that any bit width and exponent width is - allowed - This is used by ac_ieee_float and ac::bfloat16 - - Uses an ac_int that holds the bit pattern for a standard (IEEE) style binary - float: - 1) sign-magnitude representation, sign is MSB - 2) mantissa (significand) with implied bit for normal numbers - 3) E is not restricted to IEEE widths, another class ac_ieee_float does that - - Provides easy way to conver to/from the closest covering ac_float: - Constructor from ac_float - Most two negative exponents of ac_float are not representable: shift - significand futher to the right (for now no attempt to round) - Most negative mantissa of ac_float (in two's complement) when converted - to sign-magnitute requires a right shift (add +1 to exponent) - If exponent is already max, two alternatives: - - "saturate" (store most negative number) - - Store as -Inf (currently this option not available) - Exponent is offset - Mantissa implied bit is removed from normal numbers - - Explicit convertion to_ac_float - Ignores exceptions (Inf, NaN) - Does inverse as above to obtain ac_float -*/ - -#ifndef __AC_STD_FLOAT_H -#define __AC_STD_FLOAT_H -#include -#include -// Inclusion of cmath undefs all macros such as signbit etc that some parsers may define for C -#include - -#ifdef __SYNTHESIS__ -#ifdef AC_IEEE_FLOAT_USE_BUILTIN -#undef AC_IEEE_FLOAT_USE_BUILTIN -#endif -#endif - -#ifdef __AC_NAMESPACE -namespace __AC_NAMESPACE { -#endif - -// For now make data members public since SCVerify needs it -//#ifdef __AC_MAKE_PRIVATE_DATA_PUBLIC -#if 1 -#define __AC_DATA_PRIVATE public: -#else -#define __AC_DATA_PRIVATE private: -#endif - -namespace ac_private { - template - struct check_rounding { enum {Only_symmetrical_roundings_or_truncations_supported}; }; - template<> struct check_rounding {}; - - template - void check_supported() { - // only symmetrical roundings supported - const bool supported = Q==AC_RND_CONV || Q==AC_TRN_ZERO || Q==AC_RND_INF || Q == AC_RND_CONV_ODD; -#if __cplusplus > 199711L - static_assert(supported, "Only symmetrical roundings/truncations supported"); -#else - (void) check_rounding::Only_symmetrical_roundings_or_truncations_supported; -#endif - } - - template - struct check_rounding2 { enum {Only_round_to_even_supported_when_using_BUILTIN}; }; - template<> struct check_rounding2 {}; - - template - void check_supported2() { -#ifdef AC_IEEE_FLOAT_USE_BUILTIN - const bool supported = Q==AC_RND_CONV; -#if __cplusplus > 199711L - static_assert(supported, "Only round to even supported"); -#else - (void) check_rounding2::Only_round_to_even_supported_when_using_BUILTIN; -#endif -#endif - } - - template - struct rt_closed_T { - }; - template - struct rt_closed_T { - typedef T type; - }; -} - -namespace ac { - #pragma hls_design ccore - #pragma hls_ccore_type sequential - template - void fx_div(ac_int op1, ac_int op2, ac_int "ient, bool &exact) { - ac_int R = op1; - bool R_neg = false; - ac_int D = op2; - ac_int neg_D = -D; - ac_int Q = 0; - for(int i=0; i < W+2; i++) { - // take MSB of N, shift it in from right to R - R += ( R_neg ? (ac_int) D : neg_D ); - Q = (Q << 1) | ((R >= 0) & 1); - R_neg = R[W]; - R <<= 1; - } - quotient = Q; - exact = !R | R_neg & (R >> 1) == neg_D; - } - - template - void fx_div_sim(ac_int op1, ac_int op2, ac_int "ient, bool &exact) { - // need to compute extra rnd bit, - // +2 because we may need to shift left by 1 (mant divisor > mant dividend) - ac_int<2*W+1,false> op1_mi = op1; - op1_mi <<= W+1; - // +1 bit to compute rnd bit - quotient = (op1_mi / op2); - exact = !(op1_mi % op2); - } - - #pragma hls_design ccore - #pragma hls_ccore_type sequential - template - bool fx_sqrt( ac_int x, ac_int &sqrt) { - // x is ac_fixed, sqrt is ac_fixed - const bool W_odd = W&1; - const int ZW = W + W_odd; // make it even - ac_int z = x; - z <<= W_odd; - // masks used only to hint synthesis on precision - ac_int mask_d = 0; - ac_int d = 0; - ac_int r = 0; - unsigned int z_shift = ZW-2; - for(int i = WR-1; i >= 0; i--) { - r <<= 1; - mask_d = (mask_d << 2) | 0x3; - d = (mask_d & (d << 2)) | ((z >> z_shift) & 0x3 ); - ac_int t = d - (( ((ac_int)r) << 1) | 0x1); - if( !t[WR+1] ) { // since t is unsigned, look at MSB - r |= 0x1; - d = mask_d & t; - } - z <<= 2; - } - - bool rem = (d != 0) || ((z >> 2*W) != 0); - sqrt = r; - return rem; - } -} - -#ifndef AC_STD_FLOAT_FX_DIV_OVERRIDE -#ifdef __SYNTHESIS__ -#define AC_STD_FLOAT_FX_DIV_OVERRIDE ac::fx_div -#else -#define AC_STD_FLOAT_FX_DIV_OVERRIDE ac::fx_div_sim -#endif -#endif - -template class ac_std_float; - -#ifdef __AC_NAMESPACE -} -#endif - -#ifdef AC_STD_FLOAT_OVERRIDE_NAMESPACE -#define AC_STD_FLOAT_OVERRIDE_NS ::AC_STD_FLOAT_OVERRIDE_NAMESPACE:: -namespace AC_STD_FLOAT_OVERRIDE_NAMESPACE { -#ifdef __AC_NAMESPACE - using __AC_NAMESPACE::ac_q_mode; - using __AC_NAMESPACE::ac_std_float; -#endif -#else -#define AC_STD_FLOAT_OVERRIDE_NS -#endif - -#ifdef AC_STD_FLOAT_ADD_OVERRIDE -template -ac_std_float AC_STD_FLOAT_ADD_OVERRIDE(const ac_std_float &op, const ac_std_float &op2); -#endif - -#ifdef AC_STD_FLOAT_MULT_OVERRIDE -template -ac_std_float AC_STD_FLOAT_MULT_OVERRIDE(const ac_std_float &op, const ac_std_float &op2); -#endif - -#ifdef AC_STD_FLOAT_DIV_OVERRIDE -template -ac_std_float AC_STD_FLOAT_DIV_OVERRIDE(const ac_std_float &op, const ac_std_float &op2); -#endif - -#ifdef AC_STD_FLOAT_FMA_OVERRIDE -template -ac_std_float AC_STD_FLOAT_FMA_OVERRIDE(const ac_std_float &op, const ac_std_float &op2, const ac_std_float &op3); -#endif - -#ifdef AC_STD_FLOAT_SQRT_OVERRIDE -template -ac_std_float AC_STD_FLOAT_SQRT_OVERRIDE(const ac_std_float &op); -#endif - -#ifdef AC_STD_FLOAT_OVERRIDE_NAMESPACE -} -#endif - -#ifdef __AC_NAMESPACE -namespace __AC_NAMESPACE { -#endif - -namespace ac { - inline void copy_bits(float a, float *b) { *b = a; } - inline void copy_bits(double a, double *b) { *b = a; } - - inline void copy_bits(short a, short *b) { *b = a; } - inline void copy_bits(const ac_int<16,true> &a, short *b) { *b = (short) a.to_int(); } - inline void copy_bits(short a, ac_int<16,true> *b) { *b = ac_int<16,true>(a); } - inline void copy_bits(int a, int *b) { *b = a; } - inline void copy_bits(const ac_int<32,true> &a, int *b) { *b = a.to_int(); } - inline void copy_bits(int a, ac_int<32,true> *b) { *b = ac_int<32,true>(a); } - inline void copy_bits(long long a, long long *b) { *b = a; } - inline void copy_bits(const ac_int<64,true> &a, long long *b) { *b = a.to_int64(); } - inline void copy_bits(long long a, ac_int<64,true> *b) { *b = ac_int<64,true>(a); } - inline void copy_bits(const long long a[2], long long (*b)[2]) { - (*b)[0] = a[0]; - (*b)[1] = a[1]; - } - inline void copy_bits(const ac_int<128,true> &a, long long (*b)[2]) { - (*b)[0] = a.to_int64(); - (*b)[1] = a.slc<64>(64).to_int64(); - } - inline void copy_bits(const long long a[2], ac_int<128,true> *b) { - *b = 0; - b->set_slc(0,ac_int<64,true>(a[0])); - b->set_slc(64,ac_int<64,true>(a[1])); - } - inline void copy_bits(const long long a[4], long long (*b)[4]) { - (*b)[0] = a[0]; - (*b)[1] = a[1]; - (*b)[2] = a[2]; - (*b)[3] = a[3]; - } - inline void copy_bits(const ac_int<256,true> &a, long long (*b)[4]) { - (*b)[0] = a.to_int64(); - (*b)[1] = a.slc<64>(64).to_int64(); - (*b)[2] = a.slc<64>(128).to_int64(); - (*b)[3] = a.slc<64>(192).to_int64(); - } - inline void copy_bits(const long long a[4], ac_int<256,true> *b) { - *b = 0; - b->set_slc(0,ac_int<64,true>(a[0])); - b->set_slc(64,ac_int<64,true>(a[1])); - b->set_slc(128,ac_int<64,true>(a[2])); - b->set_slc(192,ac_int<64,true>(a[3])); - } - inline void copy_bits(float f, int *x); - inline void copy_bits(double f, long long *x); - inline void copy_bits(int x, float *f); - inline void copy_bits(long long x, double *f); - - inline void copy_bits(float f, ac_int<32,true> *x) { - int x_i; - copy_bits(f, &x_i); - *x = x_i; - } - inline void copy_bits(double f, ac_int<64,true> *x) { - long long x_i; - copy_bits(f, &x_i); - *x = x_i; - } - inline void copy_bits(const ac_int<32,true> &x, float *f) { copy_bits(x.to_int(), f); } - inline void copy_bits(const ac_int<64,true> &x, double *f) { copy_bits(x.to_int64(), f); } -} - -enum ac_ieee_float_format { binary16, binary32, binary64, binary128, binary256}; - -// Forward declarations for ac_ieee_float and bfloat16 -template -class ac_ieee_float; -namespace ac { - class bfloat16; -} - -template -class ac_std_float { -__AC_DATA_PRIVATE - ac_int d; -public: - static const int width = W; - static const int e_width = E; - static const int mant_bits = W - E - 1; - static const int exp_bias = (1 << (E-1)) - 1; - static const int min_exp = -exp_bias + 1; - static const int max_exp = exp_bias; - static const int mu_bits = mant_bits + 1; -private: - typedef ac_int mu_t; - typedef ac_int mu1_t; - typedef ac_int mu2_t; - typedef ac_int m_t; // mantissa in two's complement representation -public: - typedef ac_int e_t; - typedef ac_float ac_float_t; - static ac_std_float nan() { - ac_std_float r; - r.d = 0; - r.d.set_slc(mant_bits-1, ac_int(-1)); - return r; - } - static ac_std_float inf() { - ac_std_float r; - r.d = 0; - r.d.set_slc(mant_bits, ac_int(-1)); - return r; - } - static ac_std_float denorm_min() { // smallest positive non zero value (subnorm if supported) - ac_std_float r; - r.d = 1; - return r; - } - static ac_std_float min() { // smallest NORMAL positive non zero value - ac_std_float r; - r.d = 0; - r.d[width-1-e_width] = true; - return r; - } - static ac_std_float max() { // largest pos finite value - ac_std_float r; - r.d = -1; - r.d[width-1] = false; - r.d[width-1-e_width] = false; - return r; - } - static ac_std_float epsilon() { - ac_int exp = -mant_bits + exp_bias; - ac_std_float r; - r.d = 0; - r.d.set_slc(mant_bits, exp); - return r; - } - ac_std_float() {} - ac_std_float(const ac_std_float &f) : d(f.d) {} - template - ac_std_float convert() const { - ac_private::check_supported(); - ac_std_float r; - if(W <= WR) { - r.d = 0; - r.d.set_slc(WR-W, d); - } else { - typedef ac_std_float r_t; - const int r_mant_bits = r_t::mant_bits; - const int r_mu_bits = r_t::mu_bits; - e_t f_e = d.template slc(mant_bits); - bool f_normal = !!f_e; - mu_t mu = d; - mu[r_mant_bits] = f_normal; - ac_fixed r_rnd = mu; - bool rnd_ovf = r_rnd[r_mu_bits]; - ac_int m_r = r_rnd.template slc(0); - e_t e_r = f_e + rnd_ovf; - r.d = m_r; - r.d.set_slc(r_mant_bits, e_r); - r.d[WR-1] = d[W-1]; - } - return r; - } - - template - ac_fixed convert_to_ac_fixed(bool map_inf=false) const { - static const bool rnd = QFX!=AC_TRN && QFX!=AC_TRN_ZERO; - static const bool need_rnd_bit = QFX != AC_TRN; - static const bool need_rem_bits = need_rnd_bit && QFX != AC_RND; - static const bool need_ovf = OFX != AC_WRAP; - static const int t_width = AC_MAX(mu_bits+1, WFX+!SFX) + need_rnd_bit + need_ovf; - - bool f_sign, f_normal, f_zero, f_inf, f_nan; - mu_t f_mu; - e_t f_e; - extract(f_mu, f_e, f_sign, f_normal, f_zero, f_inf, f_nan); - if(map_inf) { - ac_fixed rv; - if(f_sign) - rv.template set_val(); - else - rv.template set_val(); - return rv; - } - AC_ASSERT(!f_inf && !f_nan, "Expects finite float (not Nan or Inf)"); - m_t f_m = f_sign ? m_t(-f_mu) : m_t(f_mu); - typedef ac_int t_t; - typedef ac_int t2_t; - t_t t = f_m; - t <<= need_rnd_bit; - static const int lsb_src = -mant_bits; - static const int lsb_trg = IFX-WFX; - int rshift = lsb_trg - lsb_src - (int)f_e; - - bool sticky_bit_rnd = false; - bool rshift_neg = rshift < 0; - if(need_rem_bits) { - t_t shifted_out_bits = t; - typedef ac_int< ac::template nbits< AC_MAX(lsb_trg - lsb_src - min_exp,1) >::val, false> shift_ut; - shifted_out_bits &= ~(t_t(0).bit_complement() << (shift_ut) rshift); - sticky_bit_rnd = !!shifted_out_bits & !rshift_neg; - } - bool ovf = false; - if(need_ovf) { - t_t shifted_out_bits = t < 0 ? t_t(~t) : t; - // shift right by -rshift + 1 - // +1 is OK since added extra MSB - typedef ac_int< ac::template nbits< AC_MAX(-(lsb_trg - lsb_src - max_exp + 1),1) >::val, false> shift_ut; - shifted_out_bits &= ~((t_t(0).bit_complement() >> 2) >> (shift_ut) ~rshift); - ovf = !!shifted_out_bits & rshift_neg; - } - - t >>= rshift; - - t[t_width-1] = t[t_width-1] ^ (ovf & (t[t_width-1] ^ f_sign)); - t[t_width-2] = t[t_width-2] ^ (ovf & (t[t_width-2] ^ !f_sign)); - t2_t t2 = t; - if(need_rem_bits) { - t2 <<= 1; - t2[0] = t2[0] | sticky_bit_rnd; - } - - ac_fixed ri = t2; - ac_fixed r = 0; - r.set_slc(0,ri.template slc(0)); - return r; - } - - template - explicit ac_std_float(const ac_std_float &f) { - *this = f.template convert(); - } - template - ac_std_float convert() const { - ac_private::check_supported(); - typedef ac_std_float r_t; - typedef typename r_t::e_t r_e_t; - int const r_mu_bits = r_t::mu_bits; - int const r_mant_bits = r_t::mant_bits; - int const r_min_exp = r_t::min_exp; - int const r_max_exp = r_t::max_exp; - int const r_exp_bias = r_t::exp_bias; - bool f_sign, f_normal, f_zero, f_inf, f_nan; - mu_t f_mu; - e_t f_e; - r_t r; - extract(f_mu, f_e, f_sign, f_normal, f_zero, f_inf, f_nan); - int exp = f_e; - ac_fixed r_rnd; - if(ER >= E) { - if(ER > E && !f_normal) { - int ls = f_mu.leading_sign(); - int max_shift_left = f_e - r_min_exp + 1; - bool shift_exponent_limited = ls >= max_shift_left; - int shift_l = shift_exponent_limited ? max_shift_left : ls; - f_mu <<= shift_l; - exp -= shift_l; - } - r_rnd = f_mu; - } else { - int shift_r = r_min_exp - f_e; - typedef ac_fixed t_t; - t_t r_t = f_mu; - bool sticky_bit = !!(f_mu & ~((~mu_t(0)) << mant_bits-r_mant_bits-1)); - if(shift_r > 0) { - t_t shifted_out_bits = r_t; - shifted_out_bits &= ~((~t_t(0)) << shift_r); - sticky_bit |= !!shifted_out_bits; - r_t >>= shift_r; - exp += shift_r; - } - ac_fixed r_t2 = r_t; - r_t2[0] = sticky_bit; - r_rnd = r_t2; - } - bool rnd_ovf = r_rnd[r_mu_bits]; - ac_int r_m = r_rnd.template slc(0); - bool r_normal = r_rnd[r_mant_bits] | rnd_ovf; - exp += rnd_ovf; - bool exception = f_inf | f_nan | (exp > r_max_exp); - r_e_t r_e = exception ? -1 : (f_zero | !r_normal) ? 0 : exp + r_exp_bias; - if(exception) { - r_m = 0; - r_m[r_mant_bits-1] = f_nan; - } - r.d = r_m; - r.d.set_slc(r_mant_bits, r_e); - r.d[WR-1] = d[W-1]; - return r; - } - template - explicit ac_std_float(const ac_std_float &f) { - *this = f.template convert(); - } - template - explicit ac_std_float(const ac_ieee_float &f); - - explicit ac_std_float(const ac::bfloat16 &f); - - template - explicit ac_std_float(const ac_float &f) { - bool sign = f.mantissa() < 0; - m_t m_s = f.m.template slc(0); - mu1_t m_u = sign ? (mu1_t) -m_s : (mu1_t) m_s; - bool most_neg_m = m_u[mu_bits]; - bool is_max_exp = f.exp() == (1 << (E-1)) - 1; - ac_int e = f.exp() + exp_bias + (most_neg_m & !is_max_exp); - mu_t m = m_u | ac_int<1,true>(most_neg_m & is_max_exp); - m[mant_bits] = m[mant_bits] | most_neg_m; - bool exp_dont_map = !e | e==-1; - m >>= !e; - m >>= 2*(e==-1); - // exp_dont_map guarantees subnornal => e = 0 - e &= ac_int<1,true>(!exp_dont_map & !!m); - d = m.template slc(0); - d.set_slc(mant_bits, e); - d[W-1] = sign; - } - template - void assign_from(const ac_fixed &fx) { - ac_private::check_supported(); - bool sign = fx < 0.0; - ac_fixed x = 0; - x.set_slc(0,fx.template slc(0)); - bool all_sign; - int ls = x.leading_sign(all_sign); - int max_shift_left = IFX-1 - min_exp + 1; - bool shift_exponent_limited = ls >= max_shift_left; - int shift_l = shift_exponent_limited ? max_shift_left : ls; - ac_fixed x_u = sign ? (ac_fixed) -x : (ac_fixed) x; - x_u <<= shift_l; - int exp = IFX-1; - exp -= shift_l; - ac_fixed m_rnd = x_u; - mu1_t m_u = 0; m_u.set_slc(0, m_rnd.template slc(0)); - bool shiftr1 = m_u[mu_bits]; // msb - bool r_normal = m_u[mu_bits] | m_u[mu_bits-1]; - m_u >>= shiftr1; - exp += shiftr1; - bool fx_zero = all_sign & !sign; - bool r_inf = (exp > max_exp) & !fx_zero; - if(Q==AC_TRN_ZERO) { - exp = r_inf ? max_exp + exp_bias : exp; - m_u |= ac_int<1,true>(-r_inf); // saturate (set all bits to 1) if r_inf - r_inf = false; - } - e_t e = r_inf ? -1 : (!r_normal) ? 0 : exp + exp_bias; - m_u &= ac_int<1,true>(!r_inf); - e &= ac_int<1,true>(r_normal); - d = m_u.template slc(0); - d.set_slc(mant_bits, e); - d[W-1] = sign; - } - template - void assign_from(const ac_int &x) { - this->template assign_from(ac_fixed(x)); - } - template - explicit ac_std_float(const ac_fixed &fx) { - assign_from(fx); - } - explicit ac_std_float(float f) { - const int w_bits = sizeof(f)*8; - const int m_bits = std::numeric_limits::digits; - const int e_bits = w_bits - m_bits; - ac_int t_i; - ac::copy_bits(f, &t_i); - ac_std_float t; - t.set_data(t_i); - *this = ac_std_float(t); - } - explicit ac_std_float(double f) { - const int w_bits = sizeof(f)*8; - const int m_bits = std::numeric_limits::digits; - const int e_bits = w_bits - m_bits; - ac_int t_i; - ac::copy_bits(f, &t_i); - ac_std_float t; - t.set_data(t_i); - *this = ac_std_float(t); - } - explicit ac_std_float(int x) { - *this = ac_std_float(ac_fixed<32,32,true>(x)); - } - explicit ac_std_float(long long x) { - *this = ac_std_float(ac_fixed<64,64,true>(x)); - } - const ac_int &data() const { return d; } - void set_data(const ac_int &data, bool assert_on_nan=false, bool assert_on_inf=false) { - d = data; - if(assert_on_nan) - AC_ASSERT(!isnan(), "Float is NaN"); - if(assert_on_inf) - AC_ASSERT(!isinf(), "Float is Inf"); - } - int fpclassify() const { - ac_int e = d.template slc(mant_bits); - if(e) { - if(e == -1) - return !(ac_int)d ? FP_INFINITE : FP_NAN; - else - return FP_NORMAL; - } - else - return !(ac_int)d ? FP_ZERO : FP_SUBNORMAL; - } - bool isfinite() const { - ac_int e = d.template slc(mant_bits); - return e != -1; - } - bool isnormal() const { - ac_int e = d.template slc(mant_bits); - return (e || !(ac_int)d)&& e != -1; - } - bool isnan() const { - if(isfinite()) - return false; - ac_int m = d; - return !!m; - } - bool isinf() const { - if(isfinite()) - return false; - ac_int m = d; - return !m; - } - const ac_float to_ac_float() const { - ac_int e = d.template slc(mant_bits); - bool normal = !!e; - bool sign = d[W-1]; - bool inf = e == -1; - ac_int m = d; - ac_int m1 = m; - m1[mant_bits] = normal; - ac_int m_s = sign ? -m1 : (ac_int) m1; - ac_fixed fx = 0; - fx.set_slc(0, m_s); - e -= exp_bias; - // if number is subnormal, e will be MIN_EXP + 1 (10...01), but it needs to be - // MIN_EXP + 2 (10...010) - e[0] = e[0] & normal; - e[1] = e[1] | !normal; - // normalization by at most 2 places - bool shiftl1 = !(fx[mant_bits+1] ^ fx[mant_bits]); - bool shiftl2 = shiftl1 & !(fx[mant_bits+1] ^ fx[mant_bits-1]); - fx <<= shiftl1; - fx <<= shiftl2; - e -= shiftl1 + shiftl2; - e = inf ? value(e) : e; - fx = inf ? (sign ? value(fx) : value(fx)) : fx; - return ac_float(fx, e, false); - } - float to_float() const { - ac_std_float<32,8> t(*this); - float f; - ac::copy_bits(t.d, &f); - return f; - } - double to_double() const { - ac_std_float<64,11> t(*this); - double f; - ac::copy_bits(t.d, &f); - return f; - } -private: - void extract(mu_t &m, e_t &e, bool &sign, bool &normal, bool &zero, bool &inf, bool &nan, bool biased_exp=false, bool no_subnormals=false) const { - e = d.template slc(mant_bits); - bool exception = e == -1; - normal = !!e | no_subnormals; - m = d; - bool m_zero = !m.template slc(0); - zero = (!e) & (no_subnormals | m_zero); - m[mant_bits] = !!e; - if(!biased_exp) { - e -= exp_bias; - e += !normal; - } - sign = d[W-1]; - inf = exception & m_zero; - nan = exception & !m_zero; - } -public: - static ac_std_float zero() { - ac_std_float r; - r.d = 0; - return r; - } - static ac_std_float one() { - ac_std_float r; - r.d = 0; - r.d.set_slc(mant_bits, ac_int(exp_bias)); - return r; - } - template - ac_std_float add_generic(const ac_std_float &op2) const { - ac_private::check_supported(); - // +1 for possible negation, +1 for bit growth due to addition - const int tr_t_iwidth = mu_bits + 1 + 1; - // extra bit for rounding, extra bit for left shift - const int tr_t_width = tr_t_iwidth + 1 + 1; - typedef ac_fixed add_t; - typedef ac_fixed r_un_t; - e_t op1_e, op2_e; - bool op1_normal, op1_sign, op1_zero, op2_normal, op2_sign, op2_zero; - bool op1_inf, op1_nan, op2_inf, op2_nan; - mu_t op1_mu, op2_mu; - extract(op1_mu, op1_e, op1_sign, op1_normal, op1_zero, op1_inf, op1_nan, true, No_SubNormals); - m_t op1_m = op1_sign ? m_t(-op1_mu) : m_t(op1_mu); - op1_m &= m_t(No_SubNormals & op1_zero ? 0 : -1); - op2.extract(op2_mu, op2_e, op2_sign, op2_normal, op2_zero, op2_inf, op2_nan, true, No_SubNormals); - m_t op2_m = op2_sign ? m_t(-op2_mu) : m_t(op2_mu); - op2_m &= m_t(No_SubNormals & op2_zero ? 0 : -1); - - unsigned op1_e_b = ac_int(op1_e) + !op1_normal; - unsigned op2_e_b = ac_int(op2_e) + !op2_normal; - int e_dif = op1_e_b - op2_e_b; - bool e1_lt_e2 = e_dif < 0; - e_dif = (op1_zero | op2_zero) ? 0 : e1_lt_e2 ? -e_dif : e_dif; - - add_t op_lshift = e1_lt_e2 ? op1_m : op2_m; - m_t op_no_shift = e1_lt_e2 ? op2_m : op1_m; - add_t shifted_out_bits = op_lshift; - shifted_out_bits &= ~((~add_t(0)) << (unsigned) e_dif); - bool sticky_bit = !!shifted_out_bits; - - op_lshift >>= (unsigned) e_dif; - add_t add_r = op_lshift + op_no_shift; - int exp = ( (e1_lt_e2 & !op2_zero) | op1_zero ? op2_e_b : op1_e_b); - bool all_sign; - int ls = add_r.leading_sign(all_sign); - bool r_zero = !add_r[0] & all_sign; - // +1 to account for bit growth of add_r - int max_shift_left = exp + (- min_exp - exp_bias + 1); - bool shift_exponent_limited = ls >= max_shift_left; - int shift_l = shift_exponent_limited ? max_shift_left : ls; - add_r <<= shift_l; - add_r[0] = add_r[0] | sticky_bit; - ac_fixed r_rnd = add_r; - typedef ac_int t_h; - t_h t = add_r.to_ac_int(); - bool rnd_ovf = QR == AC_RND_CONV && t == t_h(-1); - bool r_sign = r_rnd[mu_bits] ^ rnd_ovf; - bool shift_r = rnd_ovf | (r_sign & !r_rnd.template slc(0)); - r_un_t r_un = r_sign ? (r_un_t) -r_rnd : (r_un_t) r_rnd; - // get rid of implied bit, assign to ac_int - bool r_normal = r_un[mant_bits] | shift_r; - r_zero |= No_SubNormals & !r_normal; - ac_int m_r = r_un.template slc(0); - exp = (shift_exponent_limited ? min_exp + exp_bias : exp - ls + 1) + shift_r; - bool r_inf = exp > max_exp + exp_bias; - if(QR==AC_TRN_ZERO) { - exp = r_inf ? max_exp + exp_bias : exp; - m_r |= ac_int<1,true>(-r_inf); // saturate (set all bits to 1) if r_inf - r_inf = false; - } - bool r_nan = op1_nan | op2_nan | ((op1_inf & op2_inf) & (op1_sign ^ op2_sign)); - bool exception = op1_inf | op2_inf | op1_nan | op2_nan | r_inf; - ac_int e_r = exception ? -1 : (r_zero | !r_normal) ? 0 : exp; - if(exception | r_zero) { - m_r = 0; - m_r[mant_bits-1] = r_nan; - } - ac_int d_r = m_r; - d_r.set_slc(mant_bits, e_r); - d_r[W-1] = r_sign; - ac_std_float r; - r.set_data(d_r); - return r; - } - template - ac_std_float add(const ac_std_float &op2) const { -#ifndef AC_STD_FLOAT_ADD_OVERRIDE - return add_generic(op2); -#else - return AC_STD_FLOAT_OVERRIDE_NS AC_STD_FLOAT_ADD_OVERRIDE(*this, op2); -#endif - } - template - ac_std_float sub(const ac_std_float &op2) const { - return add(-op2); - } - template - ac_std_float mult_generic(const ac_std_float &op2) const { - ac_private::check_supported(); - e_t op1_e, op2_e; - bool op1_normal, op1_sign, op1_zero, op2_normal, op2_sign, op2_zero; - bool op1_inf, op1_nan, op2_inf, op2_nan; - mu_t op1_mu, op2_mu; - extract(op1_mu, op1_e, op1_sign, op1_normal, op1_zero, op1_inf, op1_nan, true, No_SubNormals); - op2.extract(op2_mu, op2_e, op2_sign, op2_normal, op2_zero, op2_inf, op2_nan, true, No_SubNormals); - bool r_sign = op1_sign ^ op2_sign; - bool r_nan = op1_nan | op2_nan | (op1_inf & op2_zero) | (op1_zero & op2_inf); - bool r_zero = op1_zero | op2_zero; // r_nan takes precedence later on - int exp = ac_int(op1_e) + ac_int(op2_e) + !op1_normal + !op2_normal - exp_bias; - ac_int<2*mu_bits,false> p = op1_mu * op2_mu; - int max_shift_left = exp + (- min_exp - exp_bias + 1); - int shift_l = 0; - bool shift_l_1 = false; - typedef ac_int t_h; - typedef ac_int t_l; - t_h p_h; - t_l p_l = p; - bool r_normal; - bool r_inf; - ac_fixed r_rnd; - ac_int m_r; - if(max_shift_left >= 0) { - r_inf = exp > max_exp + exp_bias; - bool exp_is_max = exp == max_exp + exp_bias; - bool exp_is_max_m1 = exp == max_exp + exp_bias - 1; - unsigned ls = No_SubNormals ? 0 : (unsigned) (op1_normal ? op2_mu : op1_mu).leading_sign(); - bool shift_exponent_limited = ls >= (unsigned) max_shift_left; - shift_l = shift_exponent_limited ? (unsigned) max_shift_left : ls; - p <<= (unsigned) shift_l; - exp -= shift_l; - shift_l_1 = !(shift_exponent_limited | p[2*mu_bits-1]); - p = shift_l_1 ? p << 1 : p; - exp += !shift_l_1; - p_h = p >> (mu_bits-1); - p_l &= (t_l(-1) >> shift_l) >> shift_l_1; - ac_int p_bef_rnd = p_h; - p_bef_rnd <<= 1; - p_bef_rnd[0] = !!p_l; - r_rnd = p_bef_rnd; - m_r = r_rnd.template slc(0); - bool rnd_ovf = QR == AC_RND_CONV && p_h == t_h(-1); - exp += rnd_ovf; - r_inf |= (exp_is_max & (!shift_l_1 | rnd_ovf)) | (exp_is_max_m1 & !shift_l_1 & rnd_ovf); - r_normal = r_rnd[mant_bits] | rnd_ovf; - r_zero |= !r_normal & No_SubNormals; - if(QR==AC_TRN_ZERO) { - exp = r_inf ? max_exp + exp_bias : exp; - m_r |= ac_int<1,true>(-r_inf); // saturate (set all bits to 1) if r_inf - r_inf = false; - } - } else { - shift_l = max_shift_left; - exp -= shift_l; - unsigned shift_r_m1 = ~shift_l; - p_h = p >> (mu_bits-1); - t_h shifted_out_bits = p_h; - shifted_out_bits &= ~((~t_h(1)) << shift_r_m1); - p_h >>= shift_r_m1; - p_h >>= 1; - ac_int p_bef_rnd = p_h; - p_bef_rnd <<= 1; - p_bef_rnd[0] = !!p_l | !!shifted_out_bits; - r_rnd = p_bef_rnd; - m_r = r_rnd.template slc(0); - r_normal = false; - r_inf = false; - r_zero |= No_SubNormals; - } - bool exception = op1_inf | op2_inf | op1_nan | op2_nan | r_inf; - ac_int e_r = exception ? -1 : (r_zero | !r_normal) ? 0 : exp; - if(exception | r_zero) { - m_r = 0; - m_r[mant_bits-1] = r_nan; - } - ac_int d_r = m_r; - d_r.set_slc(mant_bits, e_r); - d_r[W-1] = r_sign; - ac_std_float r; - r.set_data(d_r); - return r; - } - template - ac_std_float mult(const ac_std_float &op2) const { -#ifndef AC_STD_FLOAT_MULT_OVERRIDE - return mult_generic(op2); -#else - return AC_STD_FLOAT_OVERRIDE_NS AC_STD_FLOAT_MULT_OVERRIDE(*this, op2); -#endif - } - template - ac_std_float div_generic(const ac_std_float &op2) const { - ac_private::check_supported(); - e_t op1_e, op2_e; - bool op1_normal, op1_sign, op1_zero, op2_normal, op2_sign, op2_zero; - bool op1_inf, op1_nan, op2_inf, op2_nan; - mu_t op1_mu, op2_mu; - extract(op1_mu, op1_e, op1_sign, op1_normal, op1_zero, op1_inf, op1_nan, true, No_SubNormals); - op2.extract(op2_mu, op2_e, op2_sign, op2_normal, op2_zero, op2_inf, op2_nan, true, No_SubNormals); - bool r_sign = op1_sign ^ op2_sign; - int ls_op1 = No_SubNormals ? 0 : (unsigned) op1_mu.leading_sign(); - op1_mu <<= ls_op1; - int ls_op2 = No_SubNormals ? 0 : (unsigned) op2_mu.leading_sign(); - op2_mu <<= ls_op2; - int exp = ac_int(op1_e) - ac_int(op2_e) + !op1_normal - !op2_normal - ls_op1 + ls_op2 + exp_bias; - ac_int q0 = 0; - bool exact = true; - bool div_by_zero = op2_zero; -#ifdef __SYNTHESIS__ - div_by_zero = false; -#endif - if(!div_by_zero) { - AC_STD_FLOAT_FX_DIV_OVERRIDE(op1_mu, op2_mu, q0, exact); - } - ac_int q = q0; - q <<= 1; - int shift_r = min_exp + exp_bias - exp; - bool sticky_bit = !exact; - if(shift_r >= 0) { - typedef ac_int t_t; - t_t shifted_out_bits = q; - shifted_out_bits &= ~((~t_t(0)) << shift_r); - sticky_bit |= !!shifted_out_bits; - q >>= shift_r; - exp += shift_r; - } else { - bool shift_l = !q[mu_bits+2]; - q <<= shift_l; - exp -= shift_l; - } - q[0] = q[0] | sticky_bit; - ac_fixed r_rnd = q; - bool rnd_ovf = r_rnd[mu_bits]; - ac_int m_r = r_rnd.template slc(0); - bool r_normal = r_rnd[mant_bits] | rnd_ovf; - bool r_nan = op1_nan | op2_nan | (op1_zero & op2_zero) | (op1_inf & op2_inf); - bool r_zero = op1_zero | op2_inf; - r_zero |= !r_normal & No_SubNormals; - exp += rnd_ovf; - bool r_inf0 = op1_inf | op2_zero; // this is not affected by rounding - bool r_inf = (!r_zero & (exp > max_exp + exp_bias)) | r_inf0; - if(QR==AC_TRN_ZERO && !r_inf0) { - exp = r_inf ? max_exp + exp_bias : exp; - m_r |= ac_int<1,true>(-r_inf); // saturate (set all bits to 1) if r_inf - r_inf = false; - } - bool exception = r_nan | r_inf; - ac_int e_r = exception ? -1 : (r_zero | !r_normal) ? 0 : exp; - if(exception | r_zero) { - m_r = 0; - m_r[mant_bits-1] = r_nan; - } - ac_int d_r = m_r; - d_r.set_slc(mant_bits, e_r); - d_r[W-1] = r_sign; - ac_std_float r; - r.set_data(d_r); - return r; - } - template - ac_std_float div(const ac_std_float &op2) const { -#ifndef AC_STD_FLOAT_DIV_OVERRIDE - return div_generic(op2); -#else - return AC_STD_FLOAT_OVERRIDE_NS AC_STD_FLOAT_DIV_OVERRIDE(*this, op2); -#endif - } - template - ac_std_float fma_generic(const ac_std_float &op2, const ac_std_float &op3) const { - ac_private::check_supported(); - e_t op1_e, op2_e, op3_e; - bool op1_normal, op1_sign, op1_zero, op2_normal, op2_sign, op2_zero, op3_normal, op3_sign, op3_zero; - bool op1_inf, op1_nan, op2_inf, op2_nan, op3_inf, op3_nan; - mu_t op1_mu, op2_mu, op3_mu; - extract(op1_mu, op1_e, op1_sign, op1_normal, op1_zero, op1_inf, op1_nan, true, No_SubNormals); - op2.extract(op2_mu, op2_e, op2_sign, op2_normal, op2_zero, op2_inf, op2_nan, true, No_SubNormals); - op3.extract(op3_mu, op3_e, op3_sign, op3_normal, op3_zero, op3_inf, op3_nan, true, No_SubNormals); - if(No_SubNormals) - op3_mu &= mu_t(op3_zero ? 0 : -1); - bool mult_sign = (op1_sign ^ op2_sign) | (op1_zero & op2_inf) | (op1_inf & op1_zero); - bool mult_nan = op1_nan | op2_nan | (op1_zero & op2_inf) | (op1_inf & op2_zero); - bool mult_zero = op1_zero | op2_zero; // mult_nan has precedence later on - int mult_exp_b = ac_int(op1_e) + ac_int(op2_e) + !op1_normal + !op2_normal - exp_bias; - mult_exp_b |= ac_int( op1_inf | op2_inf ? -1 : 0 ); - ac_int<2*mu_bits,false> p = op1_mu * op2_mu; - if(No_SubNormals) - p &= ac_int<2*mu_bits,false>(mult_zero ? 0 : -1); - bool mult_inf = op1_inf | op2_inf; - - bool diff_signs = mult_sign ^ op3_sign; - bool toggle_r_sign = mult_sign; - m_t op3_m = diff_signs ? m_t(-op3_mu) : m_t(op3_mu); - unsigned op3_e_b = ac_int(op3_e) + !op3_normal; - - int e_dif = mult_exp_b - op3_e_b; - bool emult_lt_e3 = e_dif < 0; - e_dif = (mult_zero | op3_zero) ? 0 : emult_lt_e3 ? -e_dif : e_dif; - - typedef ac_int<2*mu_bits+4,true> add_t; - add_t op3_m_s = op3_m; - op3_m_s <<= mu_bits+1; // mult: ii.ffff, op3: i.ff - add_t p_s = p; - p_s <<= 2; - add_t op_lshift = emult_lt_e3 ? p_s : op3_m_s; - add_t op_no_shift = emult_lt_e3 ? op3_m_s : p_s; - - add_t shifted_out_bits = op_lshift; - shifted_out_bits &= ~((~add_t(0)) << (unsigned) e_dif); - bool sticky_bit = !!shifted_out_bits; - - op_lshift >>= (unsigned) e_dif; - add_t add_r = op_lshift + op_no_shift; - int exp = ( (emult_lt_e3 & !op3_zero) | mult_zero ? op3_e_b : mult_exp_b); - - bool all_sign; - int ls = add_r.leading_sign(all_sign); - // no bit growth of add_r - int max_shift_left = exp + (- min_exp - exp_bias + 2); - bool shift_exponent_limited = ls >= max_shift_left; - int shift_l = shift_exponent_limited ? max_shift_left : ls; - add_r <<= shift_l; - add_r[0] = add_r[0] | sticky_bit; - - ac_fixed r_rnd = add_r; - - typedef ac_int t_h; - t_h t = add_r.template slc(mu_bits+2); - bool rnd_ovf = QR == AC_RND_CONV && !add_r[2*mu_bits+3] && t == t_h(-1); - bool r_neg = r_rnd[mu_bits] ^ rnd_ovf; - bool r_sign = op3_inf ? op3_sign : mult_inf ? mult_sign : r_neg ^ toggle_r_sign; - ac_int r_rnd_i = r_rnd.template slc(0); - bool r_zero = !rnd_ovf & !r_rnd_i; - bool shift_r = rnd_ovf | (r_neg & !r_rnd_i.template slc(0)); - typedef ac_int r_un_t; - r_un_t r_un = r_neg ? (r_un_t) -r_rnd_i : (r_un_t) r_rnd_i; - // get rid of implied bit, assign to ac_int - bool r_normal = r_un[mant_bits] | shift_r; - r_zero |= No_SubNormals & !r_normal; - ac_int m_r = r_un.template slc(0); - exp = (shift_exponent_limited ? min_exp + exp_bias : exp - ls + 2) + shift_r; - bool r_inf = mult_inf | op3_inf | (exp > max_exp + exp_bias); - if(QR==AC_TRN_ZERO) { - exp = r_inf ? max_exp + exp_bias : exp; - m_r |= ac_int<1,true>(-r_inf); // saturate (set all bits to 1) if r_inf - r_inf = false; - } - bool r_nan = op3_nan | mult_nan | ((op3_inf & (op1_inf | op2_inf)) & (op3_sign ^ mult_sign)); - bool exception = op3_inf | mult_inf | op3_nan | mult_nan | r_inf; - ac_int e_r = exception ? -1 : (r_zero | !r_normal) ? 0 : exp; - if(exception | r_zero) { - m_r = 0; - m_r[mant_bits-1] = r_nan; - } - ac_int d_r = m_r; - d_r.set_slc(mant_bits, e_r); - d_r[W-1] = r_sign; - ac_std_float r; - r.set_data(d_r); - return r; - } - template - ac_std_float fma(const ac_std_float &op2, const ac_std_float &op3) const { -#ifndef AC_STD_FLOAT_FMA_OVERRIDE - return fma_generic(op2,op3); -#else - return AC_STD_FLOAT_OVERRIDE_NS AC_STD_FLOAT_FMA_OVERRIDE(*this,op2,op3); -#endif - } - template - ac_std_float sqrt_generic() const { - ac_private::check_supported(); - const bool rnd = QR != AC_TRN_ZERO; // need msb(rounded bits) - const bool rbits = QR != AC_TRN_ZERO; // need bits after msb(rounded bits) - e_t op1_e; - bool op1_normal, op1_sign, op1_zero; - bool op1_inf, op1_nan; - mu_t op1_mu; - extract(op1_mu, op1_e, op1_sign, op1_normal, op1_zero, op1_inf, op1_nan, true, No_SubNormals); - int ls_op1 = No_SubNormals ? 0 : (unsigned) op1_mu.leading_sign(); - op1_mu <<= ls_op1; - op1_mu[mu_bits-1] = true; // Since it is normalized, zero is captured by op1_zero - - bool exp_odd = (op1_e ^ !op1_normal ^ ls_op1 ^ exp_bias) & 1; - - int exp = ac_int(op1_e) + !op1_normal - ls_op1 - exp_bias; - exp >>= 1; // divide by 2, truncate towards -inf - - ac_int op1_mi = op1_mu; - op1_mi <<= exp_odd; - ac_int sq_rt; - bool sticky_bit = ac::fx_sqrt(op1_mi, sq_rt); - bool r_normal = true; // true for most practical cases on W,E - if(mant_bits > -min_exp) { - int exp_over = min_exp - exp; - if(exp_over > 0) { - if(rbits) { - typedef ac_int t_t; - t_t shifted_out_bits = sq_rt; - shifted_out_bits &= ~((~t_t(0)) << exp_over); - sticky_bit |= !!shifted_out_bits; - } - sq_rt >>= exp_over; - exp = min_exp; - r_normal = false; - } - } - // rounding should not trigger overflow (unless truncate towards +inf which is currently not supported) - ac_fixed sq_rt_rnd = 0; - if(rbits) - sq_rt_rnd[0] = sq_rt_rnd[0] | sticky_bit; - sq_rt_rnd.set_slc(rbits, sq_rt); - ac_fixed sq_rt_fx = sq_rt_rnd; - - ac_int m_r = sq_rt_fx.template slc(0); - bool r_nan = op1_nan | (op1_sign & !op1_zero); - bool r_zero = op1_zero; - r_zero |= !r_normal & No_SubNormals; - bool r_inf = op1_inf; - bool exception = r_nan | r_inf; - exp += exp_bias; - ac_int e_r = exception ? -1 : (r_zero | !r_normal) ? 0 : exp; - if(exception | r_zero) { - m_r = 0; - m_r[mant_bits-1] = r_nan; - } - ac_int d_r = m_r; - d_r.set_slc(mant_bits, e_r); - ac_std_float r; - r.set_data(d_r); - return r; - } - template - ac_std_float sqrt() const { -#ifndef AC_STD_FLOAT_SQRT_OVERRIDE - return sqrt_generic(); -#else - return AC_STD_FLOAT_OVERRIDE_NS AC_STD_FLOAT_SQRT_OVERRIDE(*this); -#endif - } - ac_std_float operator +(const ac_std_float &op2) const { - return add(op2); - } - ac_std_float operator -(const ac_std_float &op2) const { - return sub(op2); - } - ac_std_float operator *(const ac_std_float &op2) const { - return mult(op2); - } - ac_std_float operator /(const ac_std_float &op2) const { - return div(op2); - } - ac_std_float &operator +=(const ac_std_float &op2) { - *this = operator +(op2); - return *this; - } - ac_std_float &operator -=(const ac_std_float &op2) { - *this = operator -(op2); - return *this; - } - ac_std_float &operator *=(const ac_std_float &op2) { - *this = operator *(op2); - } - ac_std_float &operator /=(const ac_std_float &op2) { - *this = operator /(op2); - return *this; - } - bool operator ==(const ac_std_float &op2) const { - return ((d == op2.d) && !isnan()) || (operator !() && op2.operator !()); - } - bool operator !=(const ac_std_float &op2) const { - return !operator ==(op2); - } - bool magnitude_lt(const ac_std_float &op2) const { - return ac_int(d) < ac_int(op2.d); - } - bool neg() const { return d[W-1]; } - bool operator <(const ac_std_float &op2) const { - return - operator !=(op2) && ( (neg() && !op2.neg()) || (!(neg() ^ op2.neg()) && neg() ^ magnitude_lt(op2)) ) - && !isnan() && !op2.isnan(); - } - bool operator >=(const ac_std_float &op2) const { - return - (operator ==(op2) || (!neg() && op2.neg()) || (!(neg() ^ op2.neg()) && !neg() ^ magnitude_lt(op2)) ) - && !isnan() && !op2.isnan(); - } - bool operator >(const ac_std_float &op2) const { - return - operator !=(op2) - && ( (!neg() && op2.neg()) || (!(neg() ^ op2.neg()) && !neg() ^ magnitude_lt(op2)) ) - && !isnan() && !op2.isnan(); - } - bool operator <=(const ac_std_float &op2) const { - return - (operator == (op2) || (neg() && !op2.neg()) || (!neg() ^ op2.neg() && neg() ^ magnitude_lt(op2)) ) - && !isnan() && !op2.isnan(); - } - bool operator !() const { return !ac_int(d); } - ac_std_float operator -() const { - ac_std_float r(*this); - r.d[W-1] = !d[W-1]; - return r; - } - ac_std_float operator +() const { - return ac_std_float(*this); - } - ac_std_float abs() const { - ac_std_float r(*this); - r.d[W-1] = false; - return r; - } - ac_std_float copysign(const ac_std_float &op2) const { - ac_std_float r(*this); - r.d[W-1] = op2.d[W-1]; - return r; - } - bool signbit() const { - return d[W-1]; - } - void set_signbit(bool s) { - d[W-1] = s; - } - ac_std_float ceil() const { - ac_int e = d.template slc(mant_bits); - bool sign = d[W-1]; - if(!d.template slc(0)) - return *this; - if(e < exp_bias) { - return sign ? zero() : one(); - } else { - ac_std_float r(*this); - int e_dif = mant_bits + exp_bias - e; - if((e_dif < 0) | (e == ac_int(-1))) - return r; - else { - typedef ac_int mant_t; - mant_t m = d; - mant_t mask = (~mant_t(0)) << e_dif; - bool non_zero_fractional = !!(m & ~mask); - if(!sign) { - m |= ~mask; - mu_t mu = m + mant_t(non_zero_fractional); - e += mu[mant_bits]; - r.d.set_slc(mant_bits, e); - m = mu; - } - m &= mask; // truncate fractional bits - r.d.set_slc(0, m); - return r; - } - } - } - ac_std_float floor() const { - ac_int e = d.template slc(mant_bits); - bool sign = d[W-1]; - if(!d.template slc(0)) - return *this; - if(e < exp_bias) { - return sign ? -one() : zero(); - } else { - ac_std_float r(*this); - int e_dif = mant_bits + exp_bias - e; - if((e_dif < 0) | (e == ac_int(-1))) - return r; - else { - typedef ac_int mant_t; - mant_t m = d; - mant_t mask = (~mant_t(0)) << e_dif; - bool non_zero_fractional = !!(m & ~mask); - if(sign) { - m |= ~mask; - mu_t mu = m + mant_t(non_zero_fractional); - e += mu[mant_bits]; - r.d.set_slc(mant_bits, e); - m = mu; - } - m &= mask; // truncate fractional bits - r.d.set_slc(0, m); - return r; - } - } - } - ac_std_float trunc() const { - ac_int e = d.template slc(mant_bits); - if(e < exp_bias) { - return zero(); - } else { - ac_std_float r(*this); - int e_dif = mant_bits + exp_bias - e; - if((e_dif < 0) | (e == ac_int(-1))) - return r; - else { - typedef ac_int mant_t; - mant_t m = d; - mant_t mask = (~mant_t(0)) << e_dif; - m &= mask; // truncate fractional bits - r.d.set_slc(0, m); - return r; - } - } - } - ac_std_float round() const { - ac_int e = d.template slc(mant_bits); - if(e < exp_bias-1) { - return zero(); - } else { - ac_std_float r(*this); - int e_dif = mant_bits + exp_bias -1 - e; - if((e_dif < 0) | (e == ac_int(-1))) - return r; - else { - typedef ac_int mant_t; - mant_t m = d; - mant_t mask = (~mant_t(0)) << e_dif; - m |= ~mask; - mu_t mu = m + mant_t(1); - e += mu[mant_bits]; - r.d.set_slc(mant_bits, e); - m = mu; - m &= mask << 1; // truncate fractional bits - r.d.set_slc(0, m); - return r; - } - } - } -}; - -template -inline std::ostream& operator << (std::ostream &os, const ac_std_float &x) { - // for now just print the raw ac_int for it - os << x.data().to_string(AC_HEX); - return os; -} - -namespace ac { - // Type punning: using memcpy to avoid strict aliasing - inline void copy_bits(float f, int *x) { - std::memcpy(x, &f, sizeof(int)); - } - inline void copy_bits(double f, long long *x) { - std::memcpy(x, &f, sizeof(long long)); - } - inline void copy_bits(int x, float *f) { - std::memcpy(f, &x, sizeof(float)); - } - inline void copy_bits(long long x, double *f) { - std::memcpy(f, &x, sizeof(double)); - } - - inline void copy_bits(const ac_std_float<32,8> &x, float *f) { - copy_bits(x.data().to_int(), f); - } - inline void copy_bits(const ac_std_float<64,11> &x, double *f) { - copy_bits(x.data().to_int64(), f); - } -} - -template -class ac_ieee_float_base { -public: - static const int width = 1 << ((int)Format + 4); - // exponents are {5,8,11,15,19}, but the first three are specialized elsewhere - static const int e_width = 11 + ((int)Format - binary64)*4; // 11, 15, 19 - static const int lls = width >> 6; - typedef long long (data_t)[lls]; - typedef ac_std_float ac_std_float_t; - typedef ac_std_float helper_t; - typedef ac_float ac_float_t; - data_t d; - ac_ieee_float_base() {} - ac_ieee_float_base(const ac_ieee_float_base &f) { - ac::copy_bits(f.d, &d); - } - explicit ac_ieee_float_base(const helper_t &op) { - ac::copy_bits(op.data(), &d); - } - explicit ac_ieee_float_base(double f); -protected: - helper_t to_helper_t() const { - ac_int dat; - ac::copy_bits(d, &dat); - helper_t x; - x.set_data(dat); - return x; - } -public: - void set_data(const data_t &op) { ac::copy_bits(op, &d); } - void set_data(const ac_int &op) { ac::copy_bits(op, &d); } - const data_t &data() const { return d; } - ac_int data_ac_int() const { - ac_int x; - ac::copy_bits(d, &x); - return x; - } - bool signbit() const { return d[lls-1] < 0; } - void set_signbit(bool s) { - ac_int<64,true> t(d[lls-1]); - t[63] = s; - d[lls-1] = t.to_int64(); - } -}; - -template -inline std::ostream& operator << (std::ostream &os, const ac_ieee_float_base &x) { - // for now print the 128 and 256 as raw ac_int - os << x.data_ac_int().to_string(AC_HEX); - return os; -} - -template<> class ac_ieee_float_base { -public: - static const int width = 16; - static const int e_width = 5; - typedef ac_std_float ac_std_float_t; - typedef short data_t; - typedef ac_std_float helper_t; - typedef ac_float ac_float_t; - data_t d; - ac_ieee_float_base() {} - ac_ieee_float_base(const ac_ieee_float_base &f) : d(f.d) {} - explicit ac_ieee_float_base(const helper_t &op) : d(op.data()) {} - explicit ac_ieee_float_base(float f) : d((short)ac_std_float(f).data().to_int()) {} -protected: - helper_t to_helper_t() const { - helper_t x; - x.set_data(d); - return x; - } -public: - float to_float() const { - ac_std_float_t t; - t.set_data(this->data_ac_int()); - return t.to_float(); - } -#if __cplusplus > 199711L - explicit operator float() const { return to_float(); } -#endif - void set_data(short op) { ac::copy_bits(op, &d); } - void set_data(const ac_int &op) { ac::copy_bits(op, &d); } - const data_t &data() const { return d; } - ac_int data_ac_int() const { - ac_int x; - ac::copy_bits(d, &x); - return x; - } - bool signbit() const { return d < 0; } - void set_signbit(bool s) { - ac_int t(d); - t[width-1] = s; - d = t; - } -}; - -inline std::ostream& operator << (std::ostream &os, const ac_ieee_float_base &x) { - os << x.to_float(); - return os; -} - -struct float_helper { - float d; - float_helper() {} - float_helper(float f) { d = f; } - float_helper(const float_helper &f) { d = f.d; } - float_helper(const float_helper &f, bool no_subnormals) { - d = no_subnormals && f.fpclassify() == FP_SUBNORMAL ? std::signbit(f.d) ? -0.0 : 0.0 : f.d; - } - float_helper(const ac_std_float<32,8> &f) { set_data(f.data().to_int()); } - template - float_helper(const ac_float<25,2,8,Q> &f) : d(f.to_float()) {} - const float &data() const { return d; } - void set_data(int data) { ac::copy_bits(data, &d); } - void set_data(float data) { d = data; } - operator float() const { return d; } - float to_float() const { return d; } - int fpclassify() const { return std::fpclassify(d); } - bool isfinite() const { return std::isfinite(d); } - bool isnormal() const { return std::isnormal(d); } - bool isinf() const { return std::isinf(d); } - bool isnan() const { return std::isnan(d); } - static float nan() { return ac_std_float<32,8>::nan().to_float(); } - static float inf() { return ac_std_float<32,8>::inf().to_float(); } - static float denorm_min() { return ac_std_float<32,8>::denorm_min().to_float(); } - static float min() { return ac_std_float<32,8>::min().to_float(); } - static float max() { return ac_std_float<32,8>::max().to_float(); } - static float epsilon() { return ac_std_float<32,8>::epsilon().to_float(); } - template - float_helper add(const float_helper &op2) const { - ac_private::check_supported2(); - return float_helper( float_helper(*this, No_SubNormals) + float_helper(op2, No_SubNormals), No_SubNormals); - } - template - float_helper sub(const float_helper &op2) const { - ac_private::check_supported2(); - return float_helper( float_helper(*this, No_SubNormals) - float_helper(op2, No_SubNormals), No_SubNormals); - } - template - float_helper mult(const float_helper &op2) const { - ac_private::check_supported2(); - return float_helper( float_helper(*this, No_SubNormals) * float_helper(op2, No_SubNormals), No_SubNormals); - } - template - float_helper div(const float_helper &op2) const { - ac_private::check_supported2(); - return float_helper( float_helper(*this, No_SubNormals) / float_helper(op2, No_SubNormals), No_SubNormals); - } - template - float_helper fma(const float_helper &op2, const float_helper &op3) const { - ac_private::check_supported2(); - return float_helper( ::fmaf(float_helper(*this, No_SubNormals), float_helper(op2, No_SubNormals), float_helper(op3, No_SubNormals)), No_SubNormals); - } - template - float_helper sqrt() const { - ac_private::check_supported2(); - return float_helper( ::sqrtf(float_helper(*this, No_SubNormals)), No_SubNormals); - } - float_helper ceil() const { return float_helper(std::ceil(d)); } - float_helper floor() const { return float_helper(std::floor(d)); } - float_helper trunc() const { return float_helper(::truncf(d)); } - float_helper round() const { return float_helper(::roundf(d)); } -}; - -template<> class ac_ieee_float_base { -public: - static const int width = 32; - static const int e_width = 8; - typedef ac_std_float ac_std_float_t; -#ifdef AC_IEEE_FLOAT_USE_BUILTIN - typedef float data_t; - typedef float_helper helper_t; -#else - typedef int data_t; - typedef ac_std_float helper_t; -#endif - typedef ac_float ac_float_t; - data_t d; - ac_ieee_float_base() {} - ac_ieee_float_base(const ac_ieee_float_base &f) : d(f.d) {} - explicit ac_ieee_float_base(const helper_t &op) : d(op.data()) {} - explicit ac_ieee_float_base(float f) { ac::copy_bits(f, &d); } -protected: - helper_t to_helper_t() const { - helper_t x; - x.set_data(d); - return x; - } -public: -#if __cplusplus > 199711L - explicit operator float() const { - float f; - ac::copy_bits(d, &f); - return f; - } -#endif - float to_float() const { - float f; - ac::copy_bits(d, &f); - return f; - } - void set_data(int op) { ac::copy_bits(op, &d); } - void set_data(float op) { ac::copy_bits(op, &d); } - void set_data(const ac_int &op) { ac::copy_bits(op, &d); } - const data_t &data() const { return d; } - ac_int data_ac_int() const { - ac_int x; - ac::copy_bits(d, &x); - return x; - } - bool signbit() const { - int x; ac::copy_bits(d, &x); - return x < 0; - } - void set_signbit(bool s) { - ac_int t; - ac::copy_bits(d, &t); - t[width-1] = s; - ac::copy_bits(t, &d); - } -}; - -inline std::ostream& operator << (std::ostream &os, const ac_ieee_float_base &x) { - os << x.to_float(); - return os; -} - -struct double_helper { - double d; - double_helper() {} - double_helper(double f) { d = f; } - double_helper(const float_helper &f) { d = f.d; } - double_helper(const double_helper &f, bool no_subnormals) { - d = no_subnormals && f.fpclassify() == FP_SUBNORMAL ? std::signbit(f.d) ? -0.0 : 0.0 : f.d; - } - double_helper(const ac_std_float<64,11> &f) { set_data(f.data().to_int64()); } - template - double_helper(const ac_float<54,2,11,Q> &f) : d(f.to_double()) {} - const double &data() const { return d; } - void set_data(long long data) { - ac::copy_bits(data, &d); - } - void set_data(double data) { d = data; } - operator double() const { return d; } - double to_double() const { return d; } - int fpclassify() const { return std::fpclassify(d); } - bool isfinite() const { return std::isfinite(d); } - bool isnormal() const { return std::isnormal(d); } - bool isinf() const { return std::isinf(d); } - bool isnan() const { return std::isnan(d); } - static double nan() { return ac_std_float<64,11>::nan().to_double(); } - static double inf() { return ac_std_float<64,11>::inf().to_double(); } - static double denorm_min() { return ac_std_float<64,11>::denorm_min().to_double(); } - static double min() { return ac_std_float<64,11>::min().to_double(); } - static double max() { return ac_std_float<64,11>::max().to_double(); } - static double epsilon() { return ac_std_float<64,11>::epsilon().to_double(); } - template - double_helper add(const double_helper &op2) const { - ac_private::check_supported2(); - return double_helper( double_helper(*this, No_SubNormals) + double_helper(op2, No_SubNormals), No_SubNormals); - } - template - double_helper sub(const double_helper &op2) const { - ac_private::check_supported2(); - return double_helper( double_helper(*this, No_SubNormals) - double_helper(op2, No_SubNormals), No_SubNormals); - } - template - double_helper mult(const double_helper &op2) const { - ac_private::check_supported2(); - return double_helper( double_helper(*this, No_SubNormals) * double_helper(op2, No_SubNormals), No_SubNormals); - } - template - double_helper div(const double_helper &op2) const { - ac_private::check_supported2(); - return double_helper( double_helper(*this, No_SubNormals) / double_helper(op2, No_SubNormals), No_SubNormals); - } - template - double_helper fma(const double_helper &op2, const double_helper &op3) const { - ac_private::check_supported2(); - return double_helper( ::fma((double) double_helper(*this, No_SubNormals), (double) double_helper(op2, No_SubNormals), (double) double_helper(op3, No_SubNormals)), No_SubNormals); - } - template - double_helper sqrt() const { - ac_private::check_supported2(); - return double_helper( ::sqrt((double) double_helper(*this, No_SubNormals)), No_SubNormals); - } - double_helper ceil() const { return double_helper(std::ceil(d)); } - double_helper floor() const { return double_helper(std::floor(d)); } - double_helper trunc() const { return double_helper(::trunc(d)); } - double_helper round() const { return double_helper(::round(d)); } -}; - -template<> class ac_ieee_float_base { -public: - static const int width = 64; - static const int e_width = 11; - typedef ac_std_float ac_std_float_t; -#ifdef AC_IEEE_FLOAT_USE_BUILTIN - typedef double data_t; - typedef double_helper helper_t; -#else - typedef long long data_t; - typedef ac_std_float helper_t; -#endif - typedef ac_float ac_float_t; - data_t d; - ac_ieee_float_base() {} - ac_ieee_float_base(const ac_ieee_float_base &f) : d(f.d) {} - explicit ac_ieee_float_base(const helper_t &op) : d(op.data()) {} - explicit ac_ieee_float_base(double f) { ac::copy_bits(f, &d); } -protected: - helper_t to_helper_t() const { - helper_t x; - x.set_data(d); - return x; - } -public: -#if __cplusplus > 199711L - explicit operator double() const { - double f; - ac::copy_bits(d, &f); - return f; - } -#endif - double to_double() const { - double f; - ac::copy_bits(d, &f); - return f; - } - void set_data(long long op) { ac::copy_bits(op, &d); } - void set_data(double op) { ac::copy_bits(op, &d); } - void set_data(const ac_int &op) { ac::copy_bits(op, &d); } - const data_t &data() const { return d; } - ac_int data_ac_int() const { - ac_int x; - ac::copy_bits(d, &x); - return x; - } - bool signbit() const { - long long x; ac::copy_bits(d, &x); - return x < 0; - } - void set_signbit(bool s) { - ac_int t; - ac::copy_bits(d, &t); - t[width-1] = s; - ac::copy_bits(t, &d); - } -}; - -inline std::ostream& operator << (std::ostream &os, const ac_ieee_float_base &x) { - os << x.to_double(); - return os; -} - -namespace ac_private { - template - struct ac_ieee_float_constructor {}; - template<> struct ac_ieee_float_constructor { - typedef int type_explicit; - }; - template<> struct ac_ieee_float_constructor { - typedef int type_explicit; - }; - template<> struct ac_ieee_float_constructor { - typedef int type; - }; - template<> struct ac_ieee_float_constructor { - typedef int type_explicit; - }; - template<> struct ac_ieee_float_constructor { - typedef int type_explicit; - }; - template<> struct ac_ieee_float_constructor { - typedef int type; - }; - template<> struct ac_ieee_float_constructor { - typedef int type_explicit; - }; - template<> struct ac_ieee_float_constructor { - typedef int type_explicit; - }; - template<> struct ac_ieee_float_constructor { - typedef int type_explicit; - }; - template<> struct ac_ieee_float_constructor { - typedef int type_explicit; - }; -} - -template -class ac_ieee_float : public ac_ieee_float_base { -public: - typedef ac_ieee_float_base Base; - template - struct rt_T { - typedef typename ac_private::rt_closed_T::type mult; - typedef typename ac_private::rt_closed_T::type plus; - typedef typename ac_private::rt_closed_T::type minus; - typedef typename ac_private::rt_closed_T::type minus2; - typedef typename ac_private::rt_closed_T::type logic; - typedef typename ac_private::rt_closed_T::type div; - typedef typename ac_private::rt_closed_T::type div2; - }; - struct rt_unary { - typedef ac_ieee_float neg; - typedef ac_ieee_float mag_sqr; - typedef ac_ieee_float mag; - }; - static const int width = Base::width; - static const int e_width = Base::e_width; - static const int lls = width >> 6; - typedef typename Base::data_t data_t; - typedef typename Base::helper_t helper_t; - typedef typename Base::ac_float_t ac_float_t; - typedef ac_std_float ac_std_float_t; -public: - static ac_ieee_float nan() { return ac_ieee_float(helper_t::nan()); } - static ac_ieee_float inf() { return ac_ieee_float(helper_t::inf()); } - static ac_ieee_float denorm_min() { return ac_ieee_float(helper_t::denorm_min()); } - static ac_ieee_float min() { return ac_ieee_float(helper_t::min()); } - static ac_ieee_float max() { return ac_ieee_float(helper_t::max()); } - static ac_ieee_float epsilon() { return ac_ieee_float(helper_t::epsilon()); } - static ac_ieee_float zero() { return ac_ieee_float(ac_std_float_t::zero()); } - static ac_ieee_float one() { return ac_ieee_float(ac_std_float_t::one()); } - ac_ieee_float() {} -private: - ac_ieee_float(const Base &f) : Base(f) {} -public: - ac_ieee_float(const ac_std_float &f) : Base(f) {} - ac_ieee_float(const ac_ieee_float &f) : Base(f) {} - template - explicit ac_ieee_float(const ac_ieee_float &f) : Base(ac_std_float_t(f.to_ac_std_float())) {} - template - explicit ac_ieee_float(const ac_std_float &f) : Base(ac_std_float_t(f)) {} - explicit ac_ieee_float(const ac::bfloat16 &f); - explicit ac_ieee_float(const ac_float_t &f) : Base(ac_std_float_t(f)) {} - template - explicit ac_ieee_float(const ac_fixed &fx) : Base(ac_std_float_t(fx)) {} - template - explicit ac_ieee_float(const ac_float &f) : Base(ac_std_float_t(f)) {} - template - ac_ieee_float to_ac_ieee_float() const { return ac_ieee_float(*this); } - const ac_float_t to_ac_float() const { - return to_ac_std_float().to_ac_float(); - } - const ac_std_float to_ac_std_float() const { - ac_std_float r; - r.set_data(data_ac_int()); - return r; - } - template - ac_fixed convert_to_ac_fixed(bool map_inf=false) const { - return to_ac_std_float().template convert_to_ac_fixed(map_inf); - } - void set_data(const data_t &data) { - Base::set_data(data); - } - const ac_int data_ac_int() const { return Base::data_ac_int(); } - const data_t &data() const { return Base::d; } - template - ac_ieee_float(const T &f, typename ac_private::template ac_ieee_float_constructor::type d = 0) : Base(ac_std_float_t(f)) {} - template - explicit ac_ieee_float(const T &f, typename ac_private::template ac_ieee_float_constructor::type_explicit d = 0) : Base(ac_std_float_t(f)) {} - explicit ac_ieee_float(int x) { - *this = ac_ieee_float(ac_fixed<32,32,true>(x)); - } - explicit ac_ieee_float(long long x) { - *this = ac_ieee_float(ac_fixed<64,64,true>(x)); - } - int fpclassify() const { return Base::to_helper_t().fpclassify(); } - bool isfinite() const { return Base::to_helper_t().isfinite(); } - bool isnormal() const { return Base::to_helper_t().isnormal(); } - bool isinf() const { return Base::to_helper_t().isinf(); } - bool isnan() const { return Base::to_helper_t().isnan(); } - - template - ac_ieee_float add(const ac_ieee_float &op2) const { - return ac_ieee_float(Base(Base::to_helper_t().template add(op2.Base::to_helper_t()))); - } - template - ac_ieee_float sub(const ac_ieee_float &op2) const { - return ac_ieee_float(Base(Base::to_helper_t().template sub(op2.Base::to_helper_t()))); - } - template - ac_ieee_float mult(const ac_ieee_float &op2) const { - return ac_ieee_float(Base(Base::to_helper_t().template mult(op2.Base::to_helper_t()))); - } - template - ac_ieee_float div(const ac_ieee_float &op2) const { - return ac_ieee_float(Base(Base::to_helper_t().template div(op2.Base::to_helper_t()))); - } - template - ac_ieee_float fma(const ac_ieee_float &op2, const ac_ieee_float &op3) const { - return ac_ieee_float(Base(Base::to_helper_t().template fma(op2.Base::to_helper_t(), op3.Base::to_helper_t()))); - } - template - ac_ieee_float sqrt() const { - return ac_ieee_float(Base(Base::to_helper_t().template sqrt())); - } - - ac_ieee_float operator +(const ac_ieee_float &op2) const { - return ac_ieee_float(Base(Base::to_helper_t() + op2.Base::to_helper_t())); - } - ac_ieee_float operator -(const ac_ieee_float &op2) const { - return ac_ieee_float(Base(Base::to_helper_t() - op2.Base::to_helper_t())); - } - ac_ieee_float operator *(const ac_ieee_float &op2) const { - return ac_ieee_float(Base(Base::to_helper_t() * op2.Base::to_helper_t())); - } - ac_ieee_float operator /(const ac_ieee_float &op2) const { - return ac_ieee_float(Base(Base::to_helper_t() / op2.Base::to_helper_t())); - } - - ac_ieee_float &operator +=(const ac_ieee_float &op2) { - return *this = operator +(op2); - } - ac_ieee_float &operator -=(const ac_ieee_float &op2) { - return *this = operator -(op2); - } - ac_ieee_float &operator *=(const ac_ieee_float &op2) { - return *this = operator *(op2); - } - ac_ieee_float &operator /=(const ac_ieee_float &op2) { - return *this = operator /(op2); - } - - bool operator ==(const ac_ieee_float &op2) const { - return Base::to_helper_t() == op2.Base::to_helper_t(); - } - bool operator !=(const ac_ieee_float &op2) const { - return Base::to_helper_t() != op2.Base::to_helper_t(); - } - bool operator <(const ac_ieee_float &op2) const { - return Base::to_helper_t() < op2.Base::to_helper_t(); - } - bool operator >=(const ac_ieee_float &op2) const { - return Base::to_helper_t() >= op2.Base::to_helper_t(); - } - bool operator >(const ac_ieee_float &op2) const { - return Base::to_helper_t() > op2.Base::to_helper_t(); - } - bool operator <=(const ac_ieee_float &op2) const { - return Base::to_helper_t() <= op2.Base::to_helper_t(); - } - - ac_ieee_float operator -() const { - ac_ieee_float r(*this); - r.set_signbit(!this->signbit()); - return r; - } - ac_ieee_float operator +() const { - return ac_ieee_float(*this); - } - ac_ieee_float abs() const { - ac_ieee_float r(*this); - r.set_signbit(false); - return r; - } - ac_ieee_float copysign(const ac_ieee_float &op2) const { - ac_ieee_float r(*this); - r.set_signbit(this->signbit()); - return r; - } - bool signbit() const { return Base::signbit(); } - ac_ieee_float add(const ac_ieee_float &op1, const ac_ieee_float &op2) { - return *this = op1 + op2; - } - ac_ieee_float ceil() const { - return ac_ieee_float(Base(Base::to_helper_t().ceil())); - } - ac_ieee_float floor() const { - return ac_ieee_float(Base(Base::to_helper_t().floor())); - } - ac_ieee_float trunc() const { - return ac_ieee_float(Base(Base::to_helper_t().trunc())); - } - ac_ieee_float round() const { - return ac_ieee_float(Base(Base::to_helper_t().round())); - } - ac_ieee_float sub(const ac_ieee_float &op1, const ac_ieee_float &op2) { - return *this = op1 - op2; - } - ac_ieee_float mult(const ac_ieee_float &op1, const ac_ieee_float &op2) { - return *this = op1 * op2; - } - ac_ieee_float div(const ac_ieee_float &op1, const ac_ieee_float &op2) { - return *this = op1 / op2; - } -}; - -template -inline std::ostream& operator << (std::ostream &os, const ac_ieee_float &x) { - os << (const ac_ieee_float_base&) x; - return os; -} - -namespace ac { -class bfloat16 { -public: - template - struct rt_T { - typedef typename ac_private::rt_closed_T::type mult; - typedef typename ac_private::rt_closed_T::type plus; - typedef typename ac_private::rt_closed_T::type minus; - typedef typename ac_private::rt_closed_T::type minus2; - typedef typename ac_private::rt_closed_T::type logic; - typedef typename ac_private::rt_closed_T::type div; - typedef typename ac_private::rt_closed_T::type div2; - }; - struct rt_unary { - typedef bfloat16 neg; - typedef bfloat16 mag_sqr; - typedef bfloat16 mag; - }; - static const int width = 16; - static const int e_width = 8; - static bfloat16 nan() { return bfloat16(helper_t::nan()); } - static bfloat16 inf() { return bfloat16(helper_t::inf()); } - static bfloat16 denorm_min() { return bfloat16(helper_t::denorm_min()); } - static bfloat16 min() { return bfloat16(helper_t::min()); } - static bfloat16 max() { return bfloat16(helper_t::max()); } - static bfloat16 epsilon() { return bfloat16(helper_t::epsilon()); } - static bfloat16 zero() { return bfloat16(ac_std_float_t::zero()); } - static bfloat16 one() { return bfloat16(ac_std_float_t::one()); } - typedef ac_std_float helper_t; - typedef short data_t; - typedef ac_float ac_float_t; - typedef ac_std_float ac_std_float_t; - data_t d; - bfloat16() {} - bfloat16(const bfloat16 &f) : d(f.d) {} - bfloat16(const ac_std_float_t &op) : d(op.data()) {} - bfloat16(float f) { int x; ac::copy_bits(f, &x); d = (short) (x >> 16); } - template - explicit bfloat16(const ac_std_float &f) { - *this = f.template convert(); - } - template - explicit bfloat16(const ac_std_float &f) { - *this = f.template convert(); - } - template - explicit bfloat16(const ac_ieee_float &f) { - *this = f.to_ac_std_float().template convert(); - } - template - explicit bfloat16(const ac_fixed &fx) { - ac_std_float_t x; - x.assign_from(fx); - *this = x; - } -private: - const helper_t to_helper_t() const { - helper_t x; - x.set_data(d); - return x; - } -public: - const ac_std_float_t to_ac_std_float() const { - ac_std_float_t x; - x.set_data(d); - return x; - } - const ac_float_t to_ac_float() const { - return ac_std_float_t().to_ac_float(); - } - template - ac_fixed convert_to_ac_fixed(bool map_inf=false) const { - return to_ac_std_float().template convert_to_ac_fixed(map_inf); - } - float to_float() const { - return to_ac_std_float().to_float(); - } - double to_double() const { - return to_ac_std_float().to_double(); - } - // operator is efficient since E is identical and mantissa is longer -#if __cplusplus > 199711L - explicit operator float() const { return to_float(); } -#endif - int fpclassify() const { return to_helper_t().fpclassify(); } - bool isfinite() const { return to_helper_t().isfinite(); } - bool isnormal() const { return to_helper_t().isnormal(); } - bool isinf() const { return to_helper_t().isinf(); } - bool isnan() const { return to_helper_t().isnan(); } - void set_data(short op) { ac::copy_bits(op, &d); } - void set_data(const ac_int &op) { ac::copy_bits(op, &d); } - const data_t &data() const { return d; } - ac_int<16,true> data_ac_int() const { return ac_int<16,true>(d); } - - // mirroed most constructors in tensorflow implementation (except template version) - // tensorflow uses static_cast - // this implementation goes through ac_std_float so there is no dependency on rounding mode -// template -// explicit bfloat16(const T& val) { *this = bfloat16(static_cast(val)); } - explicit bfloat16(unsigned short val) { - ac_std_float_t t; - t.assign_from( ac_int<16,false>(val) ); - *this = t; - } - explicit bfloat16(int val) { - ac_std_float_t t; - t.assign_from( ac_int<32,true>(val) ); - *this = t; - } - explicit bfloat16(unsigned int val) { - ac_std_float_t t; - t.assign_from( ac_int<32,false>(val) ); - *this = t; - } - explicit bfloat16(long val) { - const int long_w = ac_private::long_w; - ac_std_float_t t; - t.assign_from( ac_int(val) ); - *this = t; - } - explicit bfloat16(long long val) { - ac_std_float_t t; - t.assign_from( ac_int<64,false>(val) ); - *this = t; - } - explicit bfloat16(double val) { *this = bfloat16(ac_ieee_float(val)); } - - template - bfloat16 add(const bfloat16 &op2) const { - return bfloat16(to_helper_t().add(op2.to_helper_t())); - } - template - bfloat16 sub(const bfloat16 &op2) const { - return bfloat16(to_helper_t().sub(op2.to_helper_t())); - } - template - bfloat16 mult(const bfloat16 &op2) const { - return bfloat16(to_helper_t().mult(op2.to_helper_t())); - } - template - bfloat16 div(const bfloat16 &op2) const { - return bfloat16(to_helper_t().div(op2.to_helper_t())); - } - template - bfloat16 fma(const bfloat16 &op2, const bfloat16 &op3) const { - return bfloat16(to_helper_t().fma(op2.to_helper_t(), op3.to_helper_t())); - } - template - bfloat16 sqrt() const { - return bfloat16(to_helper_t().sqrt()); - } - - bfloat16 operator +(const bfloat16 &op2) const { - return bfloat16(to_helper_t().add(op2.to_helper_t())); - } - bfloat16 operator -(const bfloat16 &op2) const { - return bfloat16(to_helper_t().sub(op2.to_helper_t())); - } - bfloat16 operator *(const bfloat16 &op2) const { - return bfloat16(to_helper_t().mult(op2.to_helper_t())); - } - bfloat16 operator /(const bfloat16 &op2) const { - return bfloat16(to_helper_t().div(op2.to_helper_t())); - } - bfloat16 &operator +=(const bfloat16 &op2) { - return *this = operator +(op2); - } - bfloat16 &operator -=(const bfloat16 &op2) { - return *this = operator -(op2); - } - bfloat16 &operator *=(const bfloat16 &op2) { - return *this = operator *(op2); - } - bfloat16 &operator /=(const bfloat16 &op2) { - return *this = operator /(op2); - } - - bool operator ==(const bfloat16 &op2) const { - return to_helper_t() == op2.to_helper_t(); - } - bool operator !=(const bfloat16 &op2) const { - return to_helper_t() != op2.to_helper_t(); - } - bool operator <(const bfloat16 &op2) const { - return to_helper_t() < op2.to_helper_t(); - } - bool operator >=(const bfloat16 &op2) const { - return to_helper_t() >= op2.to_helper_t(); - } - bool operator >(const bfloat16 &op2) const { - return to_helper_t() > op2.to_helper_t(); - } - bool operator <=(const bfloat16 &op2) const { - return to_helper_t() <= op2.to_helper_t(); - } - - bfloat16 operator -() const { - bfloat16 r(*this); - r.set_signbit(!this->signbit()); - return r; - } - bfloat16 operator +() const { - return bfloat16(*this); - } - bfloat16 abs() const { - bfloat16 r(*this); - r.set_signbit(false); - return r; - } - bfloat16 copysign(const bfloat16 &op2) const { - bfloat16 r(*this); - r.set_signbit(this->signbit()); - return r; - } - bool signbit() const { return d < 0; } - void set_signbit(bool s) { - ac_int t(d); - t[width-1] = s; - d = t; - } - bfloat16 ceil() const { return to_helper_t().ceil(); } - bfloat16 floor() const { return to_helper_t().floor(); } - bfloat16 trunc() const { return to_helper_t().trunc(); } - bfloat16 round() const { return to_helper_t().round(); } -}; - -inline std::ostream& operator << (std::ostream &os, const ac::bfloat16 &x) { - os << x.to_float(); - return os; -} - -} - -template -template -inline ac_std_float::ac_std_float(const ac_ieee_float &f) { - *this = ac_std_float(f.to_ac_std_float()); -} - -template -inline ac_std_float::ac_std_float(const ac::bfloat16 &f) { - *this = ac_std_float(f.to_ac_std_float()); -} - -template -inline ac_ieee_float::ac_ieee_float(const ac::bfloat16 &f) { - *this = ac_ieee_float(f.to_ac_std_float()); -} - -typedef ac_ieee_float ac_ieee_float16; -typedef ac_ieee_float ac_ieee_float32; -typedef ac_ieee_float ac_ieee_float64; -typedef ac_ieee_float ac_ieee_float128; -typedef ac_ieee_float ac_ieee_float256; - - -#ifdef __AC_NAMESPACE -} -#endif - -// Global functions for ac_ieee_float -namespace std { -#ifdef __AC_NAMESPACE -using namespace __AC_NAMESPACE; -#endif -template -inline ac_ieee_float abs(const ac_ieee_float &x) { return x.abs(); } -template -inline ac_ieee_float fabs(const ac_ieee_float &x) { return x.abs(); } - -template -inline ac_ieee_float copysign(const ac_ieee_float &x, const ac_ieee_float &y) { return x.copysign(y); } - -template -inline int fpclassify(const ac_ieee_float &x) { return x.fpclassify(); } -template -inline bool isfinite(const ac_ieee_float &x) { return x.isfinite(); } -template -inline bool isnormal(const ac_ieee_float &x) { return x.isnormal(); } -template -inline bool isinf(const ac_ieee_float &x) { return x.isinf(); } -template -inline bool isnan(const ac_ieee_float &x) { return x.isnan(); } - -// Don't do "long double" versions since they are 80-bits, it is an extended presicion -// TODO: fmod, fmodf, fmodl -// TODO: fmod, remainder, remquo, fma, fmax, fmin, fdim -// remainder(x,y), x - n*y, where n = x/y rounded to the nearest integer (RND_CONV) -// remquo(x,y, int *quo), returns same as remainder, unclear what quo is, also Nan, inf etc -// fmax, fmin: if one number is Nan, the other is returned -// fdim(x,y) returns max(x-y,0), if x or y is NaN, a NaN is returned, if result overflows, HUGE_VAL is returned -// TODO: ceil, floor, trunc, round, lround, nearbyint, rint, lrint, llround, llrint -// if x is +0, -0, NaN or Inf, x is returned -// ceil(x), floor(x), trunc(x) -// round(x) : RND_INF -// nearbyint: depends on rounding mode -// rint, same as nearbyint, but may raise inexaxt exception (FE_INEXACT) -// TODO: frexp, ldexp, modf, nextafter, nexttoward, copysign -// modf(x, *iptr), modff break into integral (*iptr) and fractional (returned) values, -// Don't cause exception: isgreater, isgreaterequal, isless, islessequal, islessgreater, isunordered -// isunordered: x or y is NaN -template -inline bool signbit(const ac_ieee_float &x) { return x.signbit(); } - -// Global functions for bfloat16 -inline bool signbit(const ac::bfloat16 &x) { return x.signbit(); } - -inline int fpclassify(const ac::bfloat16 &x) { return x.fpclassify(); } -inline bool isfinite(const ac::bfloat16 &x) { return x.isfinite(); } -inline bool isnormal(const ac::bfloat16 &x) { return x.isnormal(); } -inline bool isinf(const ac::bfloat16 &x) { return x.isinf(); } -inline bool isnan(const ac::bfloat16 &x) { return x.isnan(); } -} - -#undef __AC_DATA_PRIVATE -#undef AC_STD_FLOAT_FX_DIV_OVERRIDE - -#endif diff --git a/hls4ml/templates/quartus/ac_types/stream.h b/hls4ml/templates/quartus/ac_types/stream.h deleted file mode 100644 index b19ad74d66..0000000000 --- a/hls4ml/templates/quartus/ac_types/stream.h +++ /dev/null @@ -1,36 +0,0 @@ -#ifndef NNET_STREAM_H -#define NNET_STREAM_H - -#include - -namespace nnet { - -/* -* A struct with the same high-level functionality as Intel's HLS ihc::stream -* This struct is used during GCC compilation / hls4ml model.predict(...) -* This is because GCC does not have access to HLS source files (ihc::stream) -* Software-wise, this struct behaves like a first-in, first-out (FIFO) buffer -* However, it cannot be used for HLS synthesis, since it uses dynamic memory allocation (deque) -*/ -template -struct stream { - private: - std::deque _data; - - public: - stream() {} - - T read() { - T element = _data.front(); - _data.pop_front(); - return element; - } - - void write(const T& element) { - _data.push_back(element); - } -}; - -} - -#endif \ No newline at end of file diff --git a/hls4ml/templates/quartus/build_lib.sh b/hls4ml/templates/quartus/build_lib.sh deleted file mode 100755 index 2a0d4498b1..0000000000 --- a/hls4ml/templates/quartus/build_lib.sh +++ /dev/null @@ -1,20 +0,0 @@ -#!/bin/bash -set -e - -CC=g++ -CFLAGS="-O3 -fPIC -std=c++11" - -# Include -fno-gnu-unique if it is there -if echo "" | ${CC} -Werror -fsyntax-only -fno-gnu-unique -xc++ - -o /dev/null &> /dev/null; then - CFLAGS+=" -fno-gnu-unique" -fi - -LDFLAGS= -INCFLAGS="-Ifirmware/ac_types/ -Ifirmware/ap_types/" -PROJECT=myproject -LIB_STAMP=mystamp - -${CC} ${CFLAGS} ${INCFLAGS} -c firmware/${PROJECT}.cpp -o ${PROJECT}.o -${CC} ${CFLAGS} ${INCFLAGS} -c ${PROJECT}_bridge.cpp -o ${PROJECT}_bridge.o -${CC} ${CFLAGS} ${INCFLAGS} -shared ${PROJECT}.o ${PROJECT}_bridge.o -o firmware/${PROJECT}-${LIB_STAMP}.so -rm -f *.o diff --git a/hls4ml/templates/quartus/firmware/defines.h b/hls4ml/templates/quartus/firmware/defines.h deleted file mode 100644 index e74fde83e9..0000000000 --- a/hls4ml/templates/quartus/firmware/defines.h +++ /dev/null @@ -1,49 +0,0 @@ -#ifndef DEFINES_H_ -#define DEFINES_H_ - -/* - * Intel HLS makes use of three streaming interfaces: - * (1) stream_in - used as the main input to a component - * (2) stream_out - used as the main output of a component - * (3) stream - allows both reading and writing; used for inter-component connections - * ihc::stream has a implicitly deleted constructor and therefore, cannot be used as the output of a function/component - * Therefore, variables of type 'stream' are always passed by reference - */ - -#ifndef __INTELFPGA_COMPILER__ - -#include "ac_fixed.h" -#include "ac_float.h" -#include "ac_int.h" -#define hls_register - -#include "stream.h" -template using stream = nnet::stream; -template using stream_in = nnet::stream; -template using stream_out = nnet::stream; - -#else - -#include "HLS/ac_fixed.h" -#include "HLS/ac_float.h" -#include "HLS/ac_int.h" -#include "HLS/hls.h" - -template using stream = ihc::stream; -template using stream_in = ihc::stream_in; -template using stream_out = ihc::stream_out; - -#endif - -// Include nnet::array - a custom array-like struct, mainly used with io_stream -#include "nnet_utils/nnet_types.h" - -// hls-fpga-machine-learning insert numbers - -// hls-fpga-machine-learning insert layer-precision - -#define DIV_ROUNDUP(n, d) ((n + d - 1) / d) -#define MIN(n, d) (n > d ? d : n) -#define MAX(n, d) (n < d ? d : n) - -#endif diff --git a/hls4ml/templates/quartus/firmware/myproject.cpp b/hls4ml/templates/quartus/firmware/myproject.cpp deleted file mode 100644 index acdde092ff..0000000000 --- a/hls4ml/templates/quartus/firmware/myproject.cpp +++ /dev/null @@ -1,48 +0,0 @@ -#include "myproject.h" -#include "parameters.h" - -// hls-fpga-machine-learning insert weights - -/* - * Intel HLS requires that all 'stream' types are: - * (1) Passed by reference to the top-level entity or - * (2) Declared as global variables, outside of the main function - * Therefore, layer inputs/output (connections betweenn individual layers) are declared here - */ -// hls-fpga-machine-learning insert inter-task streams - -#ifndef __INTELFPGA_COMPILER__ -/* -* The top-level function used during GCC compilation / hls4ml.predic(...) goes here -* An important distinction is made between io_stream and io_parallel: -* (1) io_parallel: - - Top-level function takes a struct containing an array as function argument - - Returns a struct containing an array - the prediction - (2) io_stream: - - Top-level function is 'void' - no return value - - Instead, both the input and output are passed by reference - - This is due the HLS Streaming Interfaces; stream cannot be copied (implicitly deleted copy constructor) -* This distinction is handled in quartus_writer.py -*/ -// hls-fpga-machine-learning instantiate GCC top-level -#else -// Maximum initiation interval, concurrency and frequency for HLS syntheis are defined here -// hls-fpga-machine-learning insert cpragmas - -/* - * The top-level function used during HLS Synthesis goes here - * In a similar manner to GCC, there is a distinction between io_stream & io_parallel - */ -// hls-fpga-machine-learning instantiate HLS top-level -#endif -// If using io_parallel, the output needs to be initialised and returned at the end of this function -// If using io_stream, no output is initialised, as it is passed by reference to the top-level function -// hls-fpga-machine-learning initialize input/output - -// **************************************** -// NETWORK INSTANTIATION -// **************************************** - -// hls-fpga-machine-learning insert layers - -// hls-fpga-machine-learning return diff --git a/hls4ml/templates/quartus/firmware/myproject.h b/hls4ml/templates/quartus/firmware/myproject.h deleted file mode 100644 index d0f577d14d..0000000000 --- a/hls4ml/templates/quartus/firmware/myproject.h +++ /dev/null @@ -1,48 +0,0 @@ -#ifndef MYPROJECT_H_ -#define MYPROJECT_H_ - -#ifndef __INTELFPGA_COMPILER__ -#include "ac_fixed.h" -#include "ac_int.h" -#define hls_register -#else -#include "HLS/ac_fixed.h" -#include "HLS/ac_int.h" -#include "HLS/hls.h" -#endif - -// Streams are explicitly defined in defines.h, which are included for parameters.h -// Defining them again in this file will cause compile-time errors -#include "defines.h" - -// If using io_parallel, inputs and output need to be initialised before calling the top-level function -// If using io_stream, no inputs/outputs are initialised, as they are passed by reference to the top-level function -// hls-fpga-machine-learning insert inputs -// hls-fpga-machine-learning insert outputs - -#ifndef __INTELFPGA_COMPILER__ -/* -* The top-level function used during GCC compilation / hls4ml.predic(...) goes here -* An important distinction is made between io_stream and io_parallel: -* (1) io_parallel: - - Top-level function takes a struct containing an array as function argument - - Returns a struct containing an array - the prediction - (2) io_stream: - - Top-level function is 'void' - no return value - - Instead, both the input and output are passed by reference - - This is due the HLS Streaming Interfaces; stream cannot be copied (implicitly deleted copy constructor) -* This distinction is handled in quartus_writer.py -*/ -// hls-fpga-machine-learning instantiate GCC top-level -#else -// Maximum initiation interval, concurrency and frequency for HLS syntheis are defined here -// hls-fpga-machine-learning insert cpragmas - -/* - * The top-level function used during HLS Synthesis goes here - * In a similar manner to GCC, there is a distinction between io_stream & io_parallel - */ -// hls-fpga-machine-learning instantiate HLS top-level -#endif - -#endif diff --git a/hls4ml/templates/quartus/firmware/nnet_utils/nnet_activation.h b/hls4ml/templates/quartus/firmware/nnet_utils/nnet_activation.h deleted file mode 100644 index 1dea511c10..0000000000 --- a/hls4ml/templates/quartus/firmware/nnet_utils/nnet_activation.h +++ /dev/null @@ -1,516 +0,0 @@ -#ifndef NNET_ACTIVATION_H_ -#define NNET_ACTIVATION_H_ - -#include "nnet_common.h" - -namespace nnet { - -struct activ_config { - // IO size - static const unsigned n_in = 10; - - // Internal info - static const unsigned table_size = 512; - - // Resource reuse info - static const unsigned io_type = io_parallel; - static const unsigned reuse_factor = 1; - - // Internal data type definitions - typedef ac_fixed<16, 8> table_t; -}; - -// ************************************************* -// LINEAR Activation -- See Issue 53 -// ************************************************* -template void linear(data_T data[CONFIG_T::n_in], res_T res[CONFIG_T::n_in]) { - #pragma unroll - for (int ii = 0; ii < CONFIG_T::n_in; ii++) { - data_T datareg = data[ii]; - res[ii] = datareg; - } -} - -// ************************************************* -// RELU Activation -// ************************************************* -template void relu(data_T data[CONFIG_T::n_in], res_T res[CONFIG_T::n_in]) { - #pragma unroll - for (int ii = 0; ii < CONFIG_T::n_in; ii++) { - data_T datareg = data[ii]; - if (datareg > 0) - res[ii] = datareg; - else - res[ii] = 0; - } -} - -template -void relu_max(data_T data[CONFIG_T::n_in], res_T res[CONFIG_T::n_in]) { - #pragma unroll - for (int ii = 0; ii < CONFIG_T::n_in; ii++) { - data_T datareg = data[ii]; - if (datareg < 0) - res[ii] = 0; - else if (datareg > MAX_INT) - res[ii] = MAX_INT; - else - res[ii] = datareg; - } -} - -template void relu6(data_T data[CONFIG_T::n_in], res_T res[CONFIG_T::n_in]) { - relu_max(data, res); -} - -template void relu1(data_T data[CONFIG_T::n_in], res_T res[CONFIG_T::n_in]) { - relu_max(data, res); -} - -// ************************************************* -// Sigmoid Activation -// ************************************************* -template -void sigmoid(data_T data[CONFIG_T::n_in], res_T res[CONFIG_T::n_in]) { - static const int MAX_VALUE = 8; -#include "activation_tables/sigmoid_table.tb" - #pragma unroll - for (int ii = 0; ii < CONFIG_T::n_in; ii++) { - data_T absoluteValue hls_register; - res_T temp2 hls_register; - if (data[ii] < 0) { - absoluteValue = -data[ii]; - } else { - absoluteValue = data[ii]; - } - int index = (absoluteValue * (CONFIG_T::table_size / MAX_VALUE)).to_int(); - if (absoluteValue > MAX_VALUE) - index = CONFIG_T::table_size - 1; - temp2 = (res_T)sigmoid_table[index]; - if (data[ii] < 0) { - res[ii] = 1 - temp2; - } else { - res[ii] = temp2; - } - } -} - -// ************************************************* -// Softmax Activation -// ************************************************* - -enum class softmax_implementation { latency = 0, legacy = 1, stable = 2, argmax = 3 }; - -template inline unsigned softmax_stable_idx_from_real_val(const data_T x) { - // Number of address bits for table - static constexpr int N = ceillog2(CONFIG_T::table_size); - - // Slice the top N bits of the input - hls_register ac_int y = x.template slc(x.width - N - 1); - // If x is the most negative value, the slice will be 0, so we need to set the 0-th bit to ensure correctness - if (x != 0 && y == 0) - y[0] = 1; - return y.to_uint(); -} - -template inline unsigned softmax_latency_idx_from_real_val(const data_T x) { - // Number of address bits for table - static constexpr int N = ceillog2(CONFIG_T::table_size); - - // Slice the top N bits of the input - hls_register ac_int y = x.template slc(x.width - N); - return y.to_uint(); -} - -template -void softmax_stable(data_T data[CONFIG_T::n_in], res_T res[CONFIG_T::n_in]) { -// Look-up tables -#include "activation_tables/exp_table.tb" -#include "activation_tables/invert_table.tb" - - // Find maximum - Op_max op_max; - hls_register data_T x_max = reduce>(data, op_max); - - // For the diffs, use the same type as the input but force rounding and saturation - hls_register ac_fixed d_xi_xmax[CONFIG_T::n_in]; - #pragma unroll - for (unsigned i = 0; i < CONFIG_T::n_in; i++) { - d_xi_xmax[i] = data[i] - x_max; - } - - // Calculate all the e^x's - hls_register typename CONFIG_T::exp_table_t exp_res[CONFIG_T::n_in]; - #pragma unroll - for (unsigned i = 0; i < CONFIG_T::n_in; i++) { - exp_res[i] = exp_table[softmax_stable_idx_from_real_val(d_xi_xmax[i])]; - } - - // Explicitly sum previously calculated exponentials with an adder tree - Op_add op_add; - hls_register typename CONFIG_T::exp_table_t exp_sum = - reduce>(exp_res, op_add); - - // Multiply previously calculated exponetials with the reciprocal of the sum - hls_register typename CONFIG_T::inv_table_t inv_exp_sum = - invert_table[softmax_stable_idx_from_real_val(exp_sum)]; - #pragma unroll - for (unsigned i = 0; i < CONFIG_T::n_in; i++) { - res[i] = exp_res[i] * inv_exp_sum; - } -} - -// TODO - Improve accuracy -template -void softmax_latency(data_T data[CONFIG_T::n_in], res_T res[CONFIG_T::n_in]) { -#include "activation_tables/exp_table_latency.tb" -#include "activation_tables/invert_table_latency.tb" - - // Calculate all the e^x's - hls_register typename CONFIG_T::exp_table_t exp_res[CONFIG_T::n_in]; - #pragma unroll - for (unsigned i = 0; i < CONFIG_T::n_in; i++) { - exp_res[i] = exp_table_latency[softmax_latency_idx_from_real_val(data[i])]; - } - - // Explicitly sum the results with an adder tree. - Op_add op_add; - hls_register typename CONFIG_T::exp_table_t exp_sum = - reduce>(exp_res, op_add); - - // Multiply previously calculated exponetials with the reciprocal of the sum - hls_register typename CONFIG_T::inv_table_t inv_exp_sum = - invert_table_latency[softmax_latency_idx_from_real_val(exp_sum)]; - #pragma unroll - for (unsigned i = 0; i < CONFIG_T::n_in; i++) { - res[i] = exp_res[i] * inv_exp_sum; - } -} - -template -void softmax_legacy(data_T data[CONFIG_T::n_in], res_T res[CONFIG_T::n_in]) { -#include "activation_tables/exp_table_legacy.tb" -#include "activation_tables/invert_table_legacy.tb" - - hls_register int data_round[CONFIG_T::n_in]; -New_loop: - #pragma unroll - for (int ii = 0; ii < CONFIG_T::n_in; ii++) { - data_round[ii] = (data[ii] * CONFIG_T::table_size / 16).to_int(); - } -NN_Outer: - #pragma unroll - for (int ii = 0; ii < CONFIG_T::n_in; ii++) { - typename CONFIG_T::exp_table_t exp_res_temp = 0; - NN_Inner: - #pragma unroll - for (int jj = 0; jj < CONFIG_T::n_in; jj++) { - if (ii == jj) { - exp_res_temp += 1; - } else { - int _data_cache = (data_round[jj] - data_round[ii]); - int index = _data_cache + 8 * CONFIG_T::table_size / 16; - - if (index < 0) - index = 0; - if (index > CONFIG_T::table_size - 1) - index = CONFIG_T::table_size - 1; - - typename CONFIG_T::exp_table_t temp_exp = exp_table_legacy[index]; - exp_res_temp += temp_exp; - } - } - int exp_res_index = (exp_res_temp * CONFIG_T::table_size / 64).to_int(); - if (exp_res_index < 0) - exp_res_index = 0; - if (exp_res_index > CONFIG_T::table_size - 1) - exp_res_index = CONFIG_T::table_size - 1; - res[ii] = invert_table_legacy[exp_res_index]; - } -} - -template -void softmax_argmax(data_T data[CONFIG_T::n_in], res_T res[CONFIG_T::n_in]) { - #pragma unroll - for (int i = 0; i < CONFIG_T::n_in; i++) { - res[i] = (res_T)0; - } - - hls_register data_T maximum = data[0]; - hls_register int idx = 0; - - #pragma ii 1 - for (int i = 1; i < CONFIG_T::n_in; i++) { - if (data[i] > maximum) { - maximum = data[i]; - idx = i; - } - } - - res[idx] = (res_T)1; -} - -template -inline void softmax(data_T data[CONFIG_T::n_in], res_T res[CONFIG_T::n_in]) { - switch (CONFIG_T::implementation) { - case softmax_implementation::stable: - softmax_stable(data, res); - break; - case softmax_implementation::latency: - softmax_latency(data, res); - break; - case softmax_implementation::legacy: - softmax_legacy(data, res); - break; - default: - softmax_stable(data, res); - break; - case softmax_implementation::argmax: - softmax_argmax(data, res); - break; - } -} - -// ************************************************* -// TanH Activation -// ************************************************* -template -void dense_tanh(data_T data[CONFIG_T::n_in], res_T res[CONFIG_T::n_in]) { - static const int MAX_VALUE = 4; -// Initialize the lookup table -#include "activation_tables/tanh_table.tb" - // Index into the lookup table based on data - #pragma unroll - for (int ii = 0; ii < CONFIG_T::n_in; ii++) { - data_T temp hls_register; - res_T temp2 hls_register; - if (data[ii] < 0) { - temp = -data[ii]; - } else { - temp = data[ii]; - } - ac_int<16> index = (temp * (CONFIG_T::table_size / MAX_VALUE)).to_int(); - if (temp > MAX_VALUE) - index = CONFIG_T::table_size - 1; - temp2 = (res_T)tanh_table[index]; - if (data[ii] < 0) { - res[ii] = -temp2; - } else { - res[ii] = temp2; - } - } -} - -// ************************************************* -// Hard sigmoid Activation -// ************************************************* -template -void hard_sigmoid(data_T data[CONFIG_T::n_in], res_T res[CONFIG_T::n_in]) { - #pragma unroll - for (int ii = 0; ii < CONFIG_T::n_in; ii++) { - auto datareg = CONFIG_T::slope * data[ii] + CONFIG_T::shift; - if (datareg > 1) - datareg = 1; - else if (datareg < 0) - datareg = 0; - res[ii] = datareg; - } -} - -template -void hard_tanh(data_T data[CONFIG_T::n_in], res_T res[CONFIG_T::n_in]) { - #pragma unroll - for (int ii = 0; ii < CONFIG_T::n_in; ii++) { - auto sigmoid = CONFIG_T::slope * data[ii] + CONFIG_T::shift; - if (sigmoid > 1) - sigmoid = 1; - else if (sigmoid < 0) - sigmoid = 0; - res[ii] = 2 * sigmoid - 1; - } -} - -// ************************************************* -// Leaky RELU Activation -// ************************************************* -template -void leaky_relu(data_T data[CONFIG_T::n_in], param_T alpha, res_T res[CONFIG_T::n_in]) { - #pragma unroll - for (int ii = 0; ii < CONFIG_T::n_in; ii++) { - data_T datareg = data[ii]; - if (datareg > 0) - res[ii] = datareg; - else - res[ii] = alpha * datareg; - } -} - -// ************************************************* -// Thresholded RELU Activation -// ************************************************* -template -void thresholded_relu(data_T data[CONFIG_T::n_in], param_T theta, res_T res[CONFIG_T::n_in]) { - #pragma unroll - for (int ii = 0; ii < CONFIG_T::n_in; ii++) { - data_T datareg = data[ii]; - if (datareg > theta) - res[ii] = datareg; - else - res[ii] = 0; - } -} - -// ************************************************* -// Softplus Activation -// ************************************************* -template -void softplus(data_T data[CONFIG_T::n_in], res_T res[CONFIG_T::n_in]) { -// Initialize the lookup table -#include "activation_tables/softplus_table.tb" - // Index into the lookup table based on data - #pragma unroll - for (int ii = 0; ii < CONFIG_T::n_in; ii++) { - ac_int<16> data_round = (data[ii] * CONFIG_T::table_size / 16).to_int(); - ac_int<16> index = data_round + 8 * CONFIG_T::table_size / 16; - if (index < 0) - index = 0; - if (index > CONFIG_T::table_size - 1) - index = CONFIG_T::table_size - 1; - res[ii] = (res_T)softplus_table[index]; - } -} - -// ************************************************* -// Softsign Activation -// ************************************************* -template -void softsign(data_T data[CONFIG_T::n_in], res_T res[CONFIG_T::n_in]) { - static const int MAX_VALUE = 8; -// Initialize the lookup table -#include "activation_tables/softsign_table.tb" - - // Index into the lookup table based on data - #pragma unroll - for (int ii = 0; ii < CONFIG_T::n_in; ii++) { - data_T temp hls_register; - res_T temp2 hls_register; - if (data[ii] < 0) { - temp = -data[ii]; - } else { - temp = data[ii]; - } - ac_int<16> index = (temp * CONFIG_T::table_size / MAX_VALUE).to_int(); - if (temp > MAX_VALUE) - index = CONFIG_T::table_size - 1; - temp2 = (res_T)softsign_table[index]; - if (data[ii] < 0) { - res[ii] = -temp2; - } else { - res[ii] = temp2; - } - } -} - -// ************************************************* -// ELU Activation -// ************************************************* -template -void elu(data_T data[CONFIG_T::n_in], const param_T alpha, res_T res[CONFIG_T::n_in]) { -// Initialize the lookup table -#include "activation_tables/elu_table.tb" - // Index into the lookup table based on data - #pragma unroll - for (int ii = 0; ii < CONFIG_T::n_in; ii++) { - data_T datareg = data[ii]; - if (datareg >= 0) { - res[ii] = datareg; - } else { - ac_int<16> index = (datareg * CONFIG_T::table_size / -8).to_int(); - if (index > CONFIG_T::table_size - 1) - index = CONFIG_T::table_size - 1; - res[ii] = alpha * elu_table[index]; - } - } -} - -template void elu(data_T data[CONFIG_T::n_in], res_T res[CONFIG_T::n_in]) { - elu, res_T, CONFIG_T>(data, 1.0, res); -} - -// ************************************************* -// SELU Activation -// ************************************************* -template void selu(data_T data[CONFIG_T::n_in], res_T res[CONFIG_T::n_in]) { -// Initialize the lookup table -#include "activation_tables/selu_table.tb" - // Index into the lookup table based on data - #pragma unroll - for (int ii = 0; ii < CONFIG_T::n_in; ii++) { - data_T datareg = data[ii]; - if (datareg >= 0) { - res[ii] = res_T(1.0507009873554804934193349852946) * datareg; - } else { - ac_int<16> index = (datareg * CONFIG_T::table_size / -8).to_int(); - if (index > CONFIG_T::table_size - 1) - index = CONFIG_T::table_size - 1; - res[ii] = selu_table[index]; - } - } -} - -// ************************************************* -// PReLU Activation -// ************************************************* -template -void prelu(data_T data[CONFIG_T::n_in], const param_T alpha[CONFIG_T::n_in], res_T res[CONFIG_T::n_in]) { - #pragma unroll - for (int ii = 0; ii < CONFIG_T::n_in; ii++) { - data_T datareg = data[ii]; - if (datareg > 0) - res[ii] = datareg; - else - res[ii] = alpha[ii] * datareg; - } -} - -// ************************************************* -// Binary TanH Activation -// ************************************************* -template -void binary_tanh(data_T data[CONFIG_T::n_in], res_T res[CONFIG_T::n_in]) { - #pragma unroll - for (int ii = 0; ii < CONFIG_T::n_in; ii++) { - data_T datareg = data[ii]; - res_T cache; - if (datareg > 0) - cache = 1; - else - cache = -1; - - res[ii] = (res_T)cache; - } -} - -// ************************************************* -// Ternary TanH Activation -// ************************************************* -template -void ternary_tanh(data_T data[CONFIG_T::n_in], res_T res[CONFIG_T::n_in]) { - #pragma unroll - for (int ii = 0; ii < CONFIG_T::n_in; ii++) { - data_T datareg = 2 * data[ii]; - res_T cache; - if (datareg > 1) - cache = 1; - else if (datareg > -1 && datareg <= 1) - cache = 0; - else - cache = -1; - - res[ii] = (res_T)cache; - } -} - -} // namespace nnet - -#endif diff --git a/hls4ml/templates/quartus/firmware/nnet_utils/nnet_activation_stream.h b/hls4ml/templates/quartus/firmware/nnet_utils/nnet_activation_stream.h deleted file mode 100644 index e29592d1e1..0000000000 --- a/hls4ml/templates/quartus/firmware/nnet_utils/nnet_activation_stream.h +++ /dev/null @@ -1,676 +0,0 @@ -#ifndef NNET_ACTIVATION_STREAM_H_ -#define NNET_ACTIVATION_STREAM_H_ - -#include "nnet_common.h" -#include "nnet_types.h" - -namespace nnet { - -// ************************************************* -// Linear Activation -// ************************************************* -template void linear(stream &data, stream &res) { -LinearActLoop: - #pragma ii 1 - for (int i = 0; i < CONFIG_T::n_in / res_T::size; i++) { - data_T in_data = data.read(); - res_T out_data; - - LinearPackLoop: - #pragma unroll - for (int j = 0; j < res_T::size; j++) { - out_data[j] = in_data[j]; - } - - res.write(out_data); - } -} - -// ************************************************* -// ReLU Activation -// ************************************************* -template void relu(stream &data, stream &res) { -ReLUActLoop: - #pragma ii 1 - for (int i = 0; i < CONFIG_T::n_in / res_T::size; i++) { - data_T in_data = data.read(); - res_T out_data; - - ReLUPackLoop: - #pragma unroll - for (int j = 0; j < res_T::size; j++) { - if (in_data[j] > 0) - out_data[j] = in_data[j]; - else - out_data[j] = 0; - } - - res.write(out_data); - } -} - -// ************************************************* -// Leaky RELU Activation -// ************************************************* -template -void leaky_relu(stream &data, param_T alpha, stream &res) { - constexpr unsigned multiplier_limit = DIV_ROUNDUP(data_T::size, CONFIG_T::reuse_factor); - constexpr unsigned pipeline = data_T::size / multiplier_limit; - -LeakyReLUActLoop: - #pragma ii pipeline - for (int i = 0; i < CONFIG_T::n_in / res_T::size; i++) { - data_T in_data = data.read(); - res_T out_data; - - LeakyReLUPackLoop: - #pragma unroll - for (int j = 0; j < res_T::size; j++) { - if (in_data[j] > 0) - out_data[j] = in_data[j]; - else - out_data[j] = alpha * in_data[j]; - } - - res.write(out_data); - } -} - -// ************************************************* -// Thresholded RELU Activation -// ************************************************* -template -void thresholded_relu(stream &data, param_T theta, stream &res) { -ThresholdedReLUActLoop: - #pragma ii 1 - for (int i = 0; i < CONFIG_T::n_in / res_T::size; i++) { - data_T in_data = data.read(); - res_T out_data; - - ThresholdedReLUPackLoop: - #pragma unroll - for (int j = 0; j < res_T::size; j++) { - if (in_data[j] > theta) - out_data[j] = in_data[j]; - else - out_data[j] = 0; - } - - res.write(out_data); - } -} - -// ************************************************* -// ELU Activation -// ************************************************* -template -void elu(stream &data, param_T alpha, stream &res) { -#include "activation_tables/elu_table.tb" - - constexpr unsigned multiplier_limit = DIV_ROUNDUP(data_T::size, CONFIG_T::reuse_factor); - constexpr unsigned pipeline = data_T::size / multiplier_limit; - -EluActLoop: - #pragma ii pipeline - for (int i = 0; i < CONFIG_T::n_in / res_T::size; i++) { - data_T in_data = data.read(); - res_T out_data; - - EluPackLoop: - #pragma unroll - for (int j = 0; j < res_T::size; j++) { - hls_register typename data_T::value_type datareg = in_data[j]; - if (datareg >= 0) { - out_data[j] = datareg; - } else { - int index = (datareg * CONFIG_T::table_size / -8).to_int(); - if (index > CONFIG_T::table_size - 1) - index = CONFIG_T::table_size - 1; - out_data[j] = alpha * elu_table[index]; - } - } - - res.write(out_data); - } -} - -template void elu(stream &data, stream &res) { - elu, res_T, CONFIG_T>(data, 1.0, res); -} - -// ************************************************* -// SeLU Activation -// ************************************************* -template void selu(stream &data, stream &res) { -#include "activation_tables/selu_table.tb" - -SeluActLoop: - #pragma ii 1 - for (int i = 0; i < CONFIG_T::n_in / res_T::size; i++) { - data_T in_data = data.read(); - res_T out_data; - - SeluPackLoop: - #pragma unroll - for (int j = 0; j < res_T::size; j++) { - hls_register typename data_T::value_type datareg = in_data[j]; - if (datareg >= 0) { - out_data[j] = typename data_T::value_type(1.0507009873554804934193349852946) * datareg; - } else { - int index = (datareg * CONFIG_T::table_size / -8).to_int(); - if (index > CONFIG_T::table_size - 1) - index = CONFIG_T::table_size - 1; - out_data[j] = selu_table[index]; - } - } - - res.write(out_data); - } -} - -// ************************************************* -// PReLU Activation -// ************************************************* -template -void prelu(stream &data, const param_T alpha[CONFIG_T::n_in], stream &res) { - constexpr unsigned multiplier_limit = DIV_ROUNDUP(data_T::size, CONFIG_T::reuse_factor); - constexpr unsigned pipeline = data_T::size / multiplier_limit; - -PReLUActLoop: - #pragma ii pipeline - for (int i = 0; i < CONFIG_T::n_in / res_T::size; i++) { - data_T in_data = data.read(); - res_T out_data; - - PReLUPackLoop: - #pragma unroll - for (int j = 0; j < res_T::size; j++) { - if (in_data[j] > 0) - out_data[j] = in_data[j]; - else - out_data[j] = alpha[i * res_T::size + j] * in_data[j]; - } - - res.write(out_data); - } -} - -// ************************************************* -// Softplus Activation -// ************************************************* -template void softplus(stream &data, stream &res) { -#include "activation_tables/softplus_table.tb" - -SoftplusActLoop: - #pragma ii 1 - for (int i = 0; i < CONFIG_T::n_in / res_T::size; i++) { - data_T in_data = data.read(); - res_T out_data; - - SoftplusPackLoop: - #pragma unroll - for (int j = 0; j < res_T::size; j++) { - hls_register int data_round = (in_data[j] * CONFIG_T::table_size / 16).to_int(); - hls_register int index = data_round + 8 * CONFIG_T::table_size / 16; - if (index < 0) - index = 0; - else if (index > CONFIG_T::table_size - 1) - index = CONFIG_T::table_size - 1; - out_data[j] = softplus_table[index]; - } - - res.write(out_data); - } -} - -// ************************************************* -// Softsign Activation -// ************************************************* -template void softsign(stream &data, stream &res) { -#include "activation_tables/softsign_table.tb" - - static const int MAX_VALUE = 8; - -SoftsignActLoop: - #pragma ii 1 - for (int i = 0; i < CONFIG_T::n_in / res_T::size; i++) { - data_T in_data = data.read(); - res_T out_data; - - SoftsignPackLoop: - #pragma unroll - for (int j = 0; j < res_T::size; j++) { - hls_register typename data_T::value_type absValue; - ; - if (in_data[j] < 0) { - absValue = -in_data[j]; - } else { - absValue = in_data[j]; - } - ac_int<16> index = (absValue * CONFIG_T::table_size / MAX_VALUE).to_int(); - if (absValue > MAX_VALUE) - index = CONFIG_T::table_size - 1; - if (in_data[j] < 0) { - out_data[j] = -(typename res_T::value_type)softsign_table[index]; - } else { - out_data[j] = (typename res_T::value_type)softsign_table[index]; - } - } - - res.write(out_data); - } -} - -// ************************************************* -// Softmax Activation -// ************************************************* - -template void softmax_stable(stream &data, stream &res) { -#include "activation_tables/exp_table.tb" -#include "activation_tables/invert_table.tb" - - constexpr unsigned multiplier_limit = DIV_ROUNDUP(data_T::size, CONFIG_T::reuse_factor); - constexpr unsigned pipeline = data_T::size / multiplier_limit; - - hls_register typename data_T::value_type data_array[data_T::size]; - -SoftmaxArrayLoop: - #pragma ii pipeline - for (unsigned i = 0; i < CONFIG_T::n_in / data_T::size; i++) { - data_T in_pack = data.read(); - - SoftmaxArrayPackLoop: - #pragma unroll - for (unsigned j = 0; j < data_T::size; j++) { - data_array[j] = in_pack[j]; - } - - // Find the max and compute all delta(x_i, x_max) - Op_max op_max; - hls_register typename data_T::value_type x_max = - reduce>(data_array, op_max); - - // For the diffs, use the same type as the input but force rounding and saturation - hls_register ac_fixed - d_xi_xmax[data_T::size]; - #pragma unroll - for (unsigned j = 0; j < data_T::size; j++) { - d_xi_xmax[j] = data_array[j] - x_max; - } - - // Calculate all the e^x's - hls_register typename CONFIG_T::exp_table_t exp_res[data_T::size]; - #pragma unroll - for (unsigned j = 0; j < data_T::size; j++) { - exp_res[j] = exp_table[softmax_stable_idx_from_real_val(d_xi_xmax[j])]; - } - - // Explicitly sum the results with an adder tree. - // Rounding & Saturation mode, which improve accuracy, prevent Vivado from expression balancing - Op_add op_add; - hls_register typename CONFIG_T::exp_table_t exp_sum = - reduce>(exp_res, op_add); - - hls_register typename CONFIG_T::inv_table_t inv_exp_sum = - invert_table[softmax_stable_idx_from_real_val(exp_sum)]; - res_T out_pack; - - SoftmaxInvPackLoop: - #pragma unroll - for (unsigned j = 0; j < res_T::size; j++) { - - // TODO - Find Quartus-equivalent pragma - // #pragma HLS ALLOCATION instances=mul limit=multiplier_limit operation - - out_pack[j] = exp_res[j] * inv_exp_sum; - } - - res.write(out_pack); - } -} - -template void softmax_latency(stream &data, stream &res) { -#include "activation_tables/exp_table_latency.tb" -#include "activation_tables/invert_table_latency.tb" - - constexpr unsigned multiplier_limit = DIV_ROUNDUP(data_T::size, CONFIG_T::reuse_factor); - constexpr unsigned pipeline = data_T::size / multiplier_limit; - - // Calculate all the e^x's - hls_register typename CONFIG_T::exp_table_t exp_res[data_T::size]; - -SoftmaxExpLoop: - #pragma ii pipeline - for (unsigned i = 0; i < CONFIG_T::n_in / data_T::size; i++) { - data_T in_pack = data.read(); - - SoftmaxExpPackLoop: - #pragma unroll - for (unsigned j = 0; j < data_T::size; j++) { - exp_res[j] = - exp_table_latency[softmax_latency_idx_from_real_val(in_pack[j])]; - } - - // Explicitly sum the results with an adder tree. - // Rounding & Saturation mode, which improve accuracy, prevent Vivado from expression balancing - Op_add op_add; - hls_register typename CONFIG_T::exp_table_t exp_sum = - reduce>(exp_res, op_add); - - // Multiply previously calculated exponetials with the reciprocal of the sum - hls_register typename CONFIG_T::inv_table_t inv_exp_sum = - invert_table_latency[softmax_latency_idx_from_real_val(exp_sum)]; - - res_T out_pack; - SoftmaxInvPackLoop: - #pragma unroll - for (unsigned j = 0; j < res_T::size; j++) { - // #pragma HLS ALLOCATION instances=mul limit=multiplier_limit operation - out_pack[j] = exp_res[j] * inv_exp_sum; - } - - res.write(out_pack); - } -} - -template void softmax_legacy(stream &data, stream &res) { -#include "activation_tables/exp_table_legacy.tb" -#include "activation_tables/invert_table_legacy.tb" - - // Index into the lookup table based on data for exponentials - hls_register typename CONFIG_T::table_t exp_res[data_T::size]; - hls_register typename CONFIG_T::table_t exp_diff_res; - hls_register typename data_T::value_type data_cache[data_T::size]; - -SoftmaxInitLoop: - #pragma ii 1 - for (unsigned s = 0; s < CONFIG_T::n_in / data_T::size; s++) { - data_T in_pack = data.read(); - - SoftmaxInitPackLoop: - #pragma unroll - for (unsigned j = 0; j < data_T::size; j++) { - data_cache[j] = in_pack[j]; - exp_res[j] = 0; - } - - SoftmaxExpLoop: - #pragma unroll - for (int i = 0; i < data_T::size; i++) { - SoftmaxExpInner: - #pragma unroll - for (int j = 0; j < data_T::size; j++) { - if (i == j) { - exp_diff_res = 1; - } else { - int data_round = ((data_cache[j] - data_cache[i]) * CONFIG_T::table_size / 16).to_int(); - int index = data_round + 8 * CONFIG_T::table_size / 16; - if (index < 0) - index = 0; - if (index > CONFIG_T::table_size - 1) - index = CONFIG_T::table_size - 1; - exp_diff_res = exp_table_legacy[index]; - } - exp_res[i] += exp_diff_res; - } - } - - res_T out_pack; - SoftmaxInvPackLoop: - #pragma unroll - for (unsigned j = 0; j < res_T::size; j++) { - int exp_res_index = (exp_res[j] * CONFIG_T::table_size / 64).to_int(); - if (exp_res_index < 0) - exp_res_index = 0; - if (exp_res_index > CONFIG_T::table_size - 1) - exp_res_index = CONFIG_T::table_size - 1; - out_pack[j] = (typename res_T::value_type)invert_table_legacy[exp_res_index]; - } - - res.write(out_pack); - } -} - -template void softmax_argmax(stream &data, stream &res) { - #pragma ii 1 - for (int i = 0; i < CONFIG_T::n_in / res_T::size; i++) { - data_T in_data = data.read(); - res_T out_data; - - #pragma unroll - for (int i = 0; i < res_T::size; i++) { - out_data[i] = (typename res_T::value_type)0; - } - - hls_register typename data_T::value_type maximum = in_data[0]; - hls_register int idx = 0; - - #pragma ii 1 - for (int i = 1; i < res_T::size; i++) { - if (in_data[i] > maximum) { - maximum = in_data[i]; - idx = i; - } - } - - out_data[idx] = (typename res_T::value_type)1; - res.write(out_data); - } -} - -template void softmax(stream &data, stream &res) { - switch (CONFIG_T::implementation) { - case softmax_implementation::latency: - softmax_latency(data, res); - break; - case softmax_implementation::stable: - softmax_stable(data, res); - break; - case softmax_implementation::legacy: - softmax_legacy(data, res); - break; - case softmax_implementation::argmax: - softmax_argmax(data, res); - break; - default: - softmax_stable(data, res); - break; - } -} - -// ************************************************* -// TanH Activation -// ************************************************* -template void dense_tanh(stream &data, stream &res) { -#include "activation_tables/tanh_table.tb" - static const int MAX_VALUE = 4; - - constexpr unsigned multiplier_limit = DIV_ROUNDUP(data_T::size, CONFIG_T::reuse_factor); - constexpr unsigned pipeline = data_T::size / multiplier_limit; - -TanHActLoop: - #pragma ii pipeline - for (int i = 0; i < CONFIG_T::n_in / res_T::size; i++) { - - data_T in_data = data.read(); - res_T out_data; - - TanHPackLoop: - #pragma unroll - for (int j = 0; j < res_T::size; j++) { - hls_register typename data_T::value_type absoluteValue; - - if (in_data[j] < 0) - absoluteValue = (-1) * in_data[j]; - else - absoluteValue = in_data[j]; - - hls_register int index; - if (absoluteValue <= MAX_VALUE) - index = (absoluteValue * (CONFIG_T::table_size / MAX_VALUE)).to_int(); - else - index = CONFIG_T::table_size - 1; - - if (in_data[j] > 0) - out_data[j] = tanh_table[index]; - else - out_data[j] = -tanh_table[index]; - } - - res.write(out_data); - } -} - -// ************************************************* -// Sigmoid Activation -// ************************************************* -template void sigmoid(stream &data, stream &res) { -#include "activation_tables/sigmoid_table.tb" - static const int MAX_VALUE = 8; - - constexpr unsigned multiplier_limit = DIV_ROUNDUP(data_T::size, CONFIG_T::reuse_factor); - constexpr unsigned pipeline = data_T::size / multiplier_limit; - -SigmoidActLoop: - #pragma ii pipeline - for (int i = 0; i < CONFIG_T::n_in / res_T::size; i++) { - data_T in_data = data.read(); - res_T out_data; - - SigmoidPackLoop: - #pragma unroll - for (int j = 0; j < res_T::size; j++) { - hls_register typename data_T::value_type absoluteValue; - - if (in_data[j] < 0) - absoluteValue = (-1) * in_data[j]; - else - absoluteValue = in_data[j]; - - hls_register int index; - if (absoluteValue <= MAX_VALUE) - index = (absoluteValue * (CONFIG_T::table_size / MAX_VALUE)).to_int(); - else - index = CONFIG_T::table_size - 1; - - if (in_data[j] > 0) - out_data[j] = sigmoid_table[index]; - else - out_data[j] = 1 - sigmoid_table[index]; - } - - res.write(out_data); - } -} - -// ************************************************* -// Hard sigmoid Activation -// ************************************************* -// Note - Theano and Tensorflow might have different definitions for hard sigmoid; could provide two implementations -template void hard_sigmoid(stream &data, stream &res) { - - constexpr unsigned multiplier_limit = DIV_ROUNDUP(data_T::size, CONFIG_T::reuse_factor); - constexpr unsigned pipeline = data_T::size / multiplier_limit; - -HardSigmoidActLoop: - #pragma ii pipeline - for (int i = 0; i < CONFIG_T::n_in / res_T::size; i++) { - - data_T in_data = data.read(); - res_T out_data; - - HardSigmoidPackLoop: - #pragma unroll - for (int j = 0; j < res_T::size; j++) { - hls_register auto datareg = CONFIG_T::slope * in_data[j] + CONFIG_T::shift; - if (datareg > 1) - datareg = 1; - else if (datareg < 0) - datareg = 0; - out_data[j] = datareg; - } - - res.write(out_data); - } -} - -template void hard_tanh(stream &data, stream &res) { - - constexpr unsigned multiplier_limit = DIV_ROUNDUP(data_T::size, CONFIG_T::reuse_factor); - constexpr unsigned pipeline = data_T::size / multiplier_limit; - -HardSigmoidActLoop: - #pragma ii pipeline - for (int i = 0; i < CONFIG_T::n_in / res_T::size; i++) { - - data_T in_data = data.read(); - res_T out_data; - - HardSigmoidPackLoop: - #pragma unroll - for (int j = 0; j < res_T::size; j++) { - auto sigmoid = CONFIG_T::slope * in_data[j] + CONFIG_T::shift; - if (sigmoid > 1) - sigmoid = 1; - else if (sigmoid < 0) - sigmoid = 0; - out_data[j] = 2 * sigmoid - 1; - } - - res.write(out_data); - } -} - -// ************************************************* -// Binary TanH Activation -// ************************************************* -template void binary_tanh(stream &data, stream &res) { -BinaryTanHActLoop: - #pragma ii 1 - for (int i = 0; i < CONFIG_T::n_in / res_T::size; i++) { - - hls_register data_T in_data = data.read(); - hls_register res_T out_data; - - BinaryTanHPackLoop: - #pragma unroll - for (int j = 0; j < res_T::size; j++) { - if (in_data[j] > 0) - out_data[j] = (typename res_T::value_type)1; - else - out_data[j] = (typename res_T::value_type) - 1; - } - - res.write(out_data); - } -} - -// ************************************************* -// Ternary TanH Activation -// ************************************************* -template void ternary_tanh(stream &data, stream &res) { -TernaryTanHActLoop: - #pragma ii 1 - for (int i = 0; i < CONFIG_T::n_in / res_T::size; i++) { - - hls_register data_T in_data = data.read(); - hls_register res_T out_data; - - TernaryTanHPackLoop: - #pragma unroll - for (int j = 0; j < res_T::size; j++) { - if (in_data[j] > 1) - out_data[j] = (typename res_T::value_type)1; - else if (in_data[j] <= -1) - out_data[j] = (typename res_T::value_type) - 1; - else - out_data[j] = (typename res_T::value_type)0; - } - - res.write(out_data); - } -} - -} // namespace nnet - -#endif diff --git a/hls4ml/templates/quartus/firmware/nnet_utils/nnet_batchnorm.h b/hls4ml/templates/quartus/firmware/nnet_utils/nnet_batchnorm.h deleted file mode 100644 index 5b0c46ba8d..0000000000 --- a/hls4ml/templates/quartus/firmware/nnet_utils/nnet_batchnorm.h +++ /dev/null @@ -1,104 +0,0 @@ -#ifndef NNET_BATCHNORM_H_ -#define NNET_BATCHNORM_H_ - -#include "nnet_common.h" -#include "nnet_helpers.h" -#include "nnet_mult.h" - -namespace nnet { - -struct batchnorm_config { - // Internal data type definitions - typedef float bias_t; - typedef float scale_t; - - // Layer Sizes - static const unsigned n_in = 10; - static const unsigned n_filt = -1; - static const unsigned n_scale_bias = 10; - - // Resource reuse info - static const unsigned io_type = io_parallel; - static const unsigned reuse_factor = 1; - static const bool store_weights_in_bram = false; - static const unsigned n_zeros = 0; - // partitioning arrays cyclically to go with roll factors? - - // Default multiplication - template using product = nnet::product::mult; -}; - -template -void normalize(data_T data[CONFIG_T::n_in], res_T res[CONFIG_T::n_in], - const typename CONFIG_T::scale_t scale[CONFIG_T::n_scale_bias], - const typename CONFIG_T::bias_t bias[CONFIG_T::n_scale_bias]) { -// Calculate result -Result: - #pragma unroll - for (int ires = 0; ires < CONFIG_T::n_in; ires++) { - if (CONFIG_T::n_filt == -1) { - res[ires] = CONFIG_T::template product::product(data[ires], scale[ires]) + - bias[ires]; - } else { - int norm_index = ires % CONFIG_T::n_filt; - res[ires] = - CONFIG_T::template product::product(data[ires], scale[norm_index]) + - bias[norm_index]; - } - } -} - -// **************************************************** -// Merged Batch Normalization and Quantized Tanh -// **************************************************** -struct batchnorm_quantized_tanh_config { - // Layer Sizes - static const unsigned n_in = 10; - static const unsigned n_filt = -1; - static const unsigned n_scale_bias = 10; - - // Resource reuse info - static const unsigned io_type = io_parallel; - static const unsigned reuse_factor = 1; - static const unsigned n_zeros = 0; -}; - -template -void normalize_binary_tanh(data_T data[CONFIG_T::n_in], ac_int<1, false> res[CONFIG_T::n_in], - const data_T threshold[CONFIG_T::n_scale_bias]) { - #pragma unroll - for (int ii = 0; ii < CONFIG_T::n_in; ii++) { - ac_int<1, false> cache; - data_T datareg = data[ii]; - int norm_index = CONFIG_T::n_filt == -1 ? ii : ii % CONFIG_T::n_filt; - if (datareg >= threshold[norm_index]) - cache = 1; - else - cache = 0; - - res[ii] = cache; - } -} - -template -void normalize_ternary_tanh(data_T data[CONFIG_T::n_in], ac_int<2, true> res[CONFIG_T::n_in], - const data_T threshold_hi[CONFIG_T::n_scale_bias], - const data_T threshold_lo[CONFIG_T::n_scale_bias]) { - #pragma unroll - for (int ii = 0; ii < CONFIG_T::n_in; ii++) { - ac_int<2, true> cache; - data_T datareg = data[ii]; - int norm_index = CONFIG_T::n_filt == -1 ? ii : ii % CONFIG_T::n_filt; - if (datareg > threshold_hi[norm_index]) - cache = 1; - else if (datareg <= threshold_lo[norm_index]) - cache = -1; - else - cache = 0; - res[ii] = cache; - } -} - -} // namespace nnet - -#endif diff --git a/hls4ml/templates/quartus/firmware/nnet_utils/nnet_batchnorm_stream.h b/hls4ml/templates/quartus/firmware/nnet_utils/nnet_batchnorm_stream.h deleted file mode 100644 index 7a1f48d22e..0000000000 --- a/hls4ml/templates/quartus/firmware/nnet_utils/nnet_batchnorm_stream.h +++ /dev/null @@ -1,108 +0,0 @@ -#ifndef NNET_BATCHNORM_STREAM_H_ -#define NNET_BATCHNORM_STREAM_H_ - -#include "nnet_common.h" -#include "nnet_helpers.h" -#include "nnet_mult.h" -#include "nnet_types.h" - -namespace nnet { - -// **************************************************** -// Streaming Batch Normalization -// **************************************************** -template -void normalize(stream &data, stream &res, const typename CONFIG_T::scale_t scale[CONFIG_T::n_scale_bias], - const typename CONFIG_T::bias_t bias[CONFIG_T::n_scale_bias]) { - - constexpr unsigned multiplier_limit = DIV_ROUNDUP(CONFIG_T::n_in, CONFIG_T::reuse_factor); - constexpr unsigned pipeline = CONFIG_T::n_in / multiplier_limit; - CONFIG_T::template product::limit(multiplier_limit); - -BatchNormLoop: - #pragma ii pipeline - for (int i = 0; i < CONFIG_T::n_in / data_T::size; i++) { - data_T in_data = data.read(); - res_T out_data; - - BatchNormpack: - #pragma unroll - for (int j = 0; j < data_T::size; j++) { - int norm_index; - if (CONFIG_T::n_filt == -1) - norm_index = i * data_T::size + j; - else - norm_index = j % CONFIG_T::n_filt; - out_data[j] = CONFIG_T::template product::product( - in_data[j], scale[norm_index]) + - bias[norm_index]; - } - - res.write(out_data); - } -} - -// **************************************************** -// Merged Batch Normalization and Quantized Tanh -// **************************************************** -template -void normalize_binary_tanh(stream &data, stream, CONFIG_T::n_scale_bias>> &res, - const typename data_T::value_type threshold[CONFIG_T::n_scale_bias]) { - -BinaryNormLoop: - #pragma ii 1 - for (int i = 0; i < CONFIG_T::n_in / data_T::size; i++) { - data_T in_data = data.read(); - nnet::array, CONFIG_T::n_scale_bias> out_data; - - BatchNormPack: - #pragma unroll - for (int j = 0; j < data_T::size; j++) { - int norm_index; - if (CONFIG_T::n_filt == -1) - norm_index = i * data_T::size + j; - else - norm_index = j % CONFIG_T::n_filt; - - out_data[j] = (in_data[j] >= threshold[norm_index]) ? 1 : 0; - } - - res.write(out_data); - } -} - -template -void normalize_ternary_tanh(stream &data, stream, CONFIG_T::n_scale_bias>> &res, - const typename data_T::value_type threshold_hi[CONFIG_T::n_scale_bias], - const typename data_T::value_type threshold_lo[CONFIG_T::n_scale_bias]) { - -TernaryNormLoop: - #pragma ii 1 - for (int i = 0; i < CONFIG_T::n_in / data_T::size; i++) { - data_T in_data = data.read(); - nnet::array, CONFIG_T::n_scale_bias> out_data; - - BatchNormPack: - #pragma unroll - for (int j = 0; j < data_T::size; j++) { - int norm_index; - if (CONFIG_T::n_filt == -1) - norm_index = i * data_T::size + j; - else - norm_index = j % CONFIG_T::n_filt; - - if (in_data[j] > threshold_hi[norm_index]) - out_data[j] = 1; - else if (in_data[j] <= threshold_lo[norm_index]) - out_data[j] = -1; - else - out_data[j] = 0; - } - - res.write(out_data); - } -} - -} // namespace nnet - -#endif diff --git a/hls4ml/templates/quartus/firmware/nnet_utils/nnet_common.h b/hls4ml/templates/quartus/firmware/nnet_utils/nnet_common.h deleted file mode 100644 index 1af60ab0c5..0000000000 --- a/hls4ml/templates/quartus/firmware/nnet_utils/nnet_common.h +++ /dev/null @@ -1,71 +0,0 @@ -#ifndef NNET_COMMON_H_ -#define NNET_COMMON_H_ - -#ifndef __INTELFPGA_COMPILER__ -#include "ac_fixed.h" -#include "ac_int.h" -#include "math.h" -#else -#include "HLS/ac_fixed.h" -#include "HLS/ac_int.h" -#include "HLS/math.h" -#endif - -#include "nnet_helpers.h" - -typedef ac_fixed<16, 6> table_default_t; - -namespace nnet { - -// Common type definitions -enum io_type { io_parallel = 0, io_stream }; - -// Default data types (??) TODO: Deprecate -typedef ac_fixed<16, 4> weight_t_def; -typedef ac_fixed<16, 4> bias_t_def; -typedef ac_fixed<32, 10> accum_t_def; - -template void merge(data_T data1[NIN1], data_T data2[NIN2], data_T res[NIN1 + NIN2]) { - #pragma unroll - for (int ii = 0; ii < NIN1; ii++) { - res[ii] = data1[ii]; - } - #pragma unroll - for (int ii = 0; ii < NIN2; ii++) { - res[NIN1 + ii] = data2[ii]; - } -} - -/* --- - * Balanced tree reduce implementation. - * For use in scenarios where Quartus cannot expression balance - * Reduces an array of inputs to a single value using the template binary operator 'Op', - * for example summing all elements with Op_add, or finding the maximum with Op_max - * Use only when the input array is fully unrolled. Or, slice out a fully unrolled section - * before applying and accumulate the result over the rolled dimension. - * --- */ -template T reduce(const T *x, Op op) { - static constexpr int leftN = pow2(floorlog2(N - 1)) > 0 ? pow2(floorlog2(N - 1)) : 0; - static constexpr int rightN = N - leftN > 0 ? N - leftN : 0; - if (N == 1) { - return x[0]; - } - if (N == 2) { - return op(x[0], x[1]); - } - return op(reduce(x, op), reduce(x + leftN, op)); -} - -template class Op_add { - public: - T operator()(T a, T b) { return a + b; } -}; - -template class Op_max { - public: - T operator()(T a, T b) { return a >= b ? a : b; } -}; - -} // namespace nnet - -#endif diff --git a/hls4ml/templates/quartus/firmware/nnet_utils/nnet_conv1d.h b/hls4ml/templates/quartus/firmware/nnet_utils/nnet_conv1d.h deleted file mode 100644 index 005b84217d..0000000000 --- a/hls4ml/templates/quartus/firmware/nnet_utils/nnet_conv1d.h +++ /dev/null @@ -1,64 +0,0 @@ -#ifndef NNET_CONV1D_H_ -#define NNET_CONV1D_H_ - -#include "nnet_common.h" -#include "nnet_conv1d_resource.h" - -namespace nnet { - -struct conv1d_config { - // I/O sizes - static const unsigned in_width = 10; - static const unsigned out_width = 10; - - // Number of channels, filters - static const unsigned n_chan = 1; - static const unsigned n_filt = 1; - - // Original filter size - static const unsigned filt_width = 1; - static const unsigned kernel_size = filt_width; - - // Modified filter size (post-Wionograd transformation, if applied) - static const unsigned impl_filt_height = 1; - static const unsigned impl_filt_width = 1; - - // Padding, stride, dilation - static const unsigned pad_left = 0; - static const unsigned pad_right = 0; - static const unsigned stride_width = 1; - static const unsigned dilation = 1; - - // Run-time Configuration - static const unsigned n_zeros = 0; - static const unsigned reuse_factor = 1; - static const unsigned parallelization_factor = 1; - - // TODO: BRAM Storage on Quartus - static const bool store_weights_in_bram = false; - - // Internal data type definitions - typedef float bias_t; - typedef float weight_t; - typedef float accum_t; -}; - -template -void conv_1d_cl(data_T data[CONFIG_T::in_width * CONFIG_T::n_chan], res_T res[CONFIG_T::out_width * CONFIG_T::n_filt], - const typename CONFIG_T::weight_t weights[CONFIG_T::filt_width * CONFIG_T::n_chan * CONFIG_T::n_filt], - const typename CONFIG_T::bias_t biases[CONFIG_T::n_filt]) { - conv_1d_resource_cl(data, res, weights, biases); -} - -template -void pointwise_conv_1d_cl(data_T data[CONFIG_T::in_width * CONFIG_T::n_chan], - res_T res[CONFIG_T::out_width * CONFIG_T::n_filt], - const typename CONFIG_T::weight_t weights[CONFIG_T::n_chan * CONFIG_T::n_filt], - const typename CONFIG_T::bias_t biases[CONFIG_T::n_filt]) { - assert(CONFIG_T::filt_width == 1); - pointwise_conv_1d_resource_cl(data, res, weights, biases); -} - -} // namespace nnet - -#endif diff --git a/hls4ml/templates/quartus/firmware/nnet_utils/nnet_conv1d_resource.h b/hls4ml/templates/quartus/firmware/nnet_utils/nnet_conv1d_resource.h deleted file mode 100644 index a630a0b4e5..0000000000 --- a/hls4ml/templates/quartus/firmware/nnet_utils/nnet_conv1d_resource.h +++ /dev/null @@ -1,242 +0,0 @@ -#ifndef NNET_CONV1D_RESOURCE_H_ -#define NNET_CONV1D_RESOURCE_H_ - -#include "nnet_common.h" -#include "nnet_dense.h" -#include - -namespace nnet { - -enum class conv1d_implementation { combination, im2col, winograd }; - -// **************************************************************** -// im2col - General-purpose 1D Convolution algorithm -// **************************************************************** - -template -void im2col_1d_cl(data_T data[CONFIG_T::in_width * CONFIG_T::n_chan], - data_T data_col[CONFIG_T::impl_filt_width * CONFIG_T::n_chan], const int col) { - // im2col can be unrolled fully, since number of parallel executions = filt_w x n_chann ~ O(100) and very little DSP - // usage - - hls_register int index = 0; - -KernelLoop: - #pragma unroll - for (int kernel_col = 0; kernel_col < CONFIG_T::impl_filt_width; kernel_col++) { - ChannelLoop: - #pragma unroll - for (int channel = 0; channel < CONFIG_T::n_chan; channel++) { - hls_register int index_data = - (col * CONFIG_T::stride_width + kernel_col - CONFIG_T::pad_left) * CONFIG_T::n_chan + channel; - if (index_data >= 0 && index_data < CONFIG_T::in_width * CONFIG_T::n_chan) { - data_col[index++] = data[index_data]; - } else { - data_col[index++] = 0; - } - } - } -} - -template -void conv_1d_im2col_cl( - data_T data[CONFIG_T::in_width * CONFIG_T::n_chan], res_T res[CONFIG_T::out_width * CONFIG_T::n_filt], - const typename CONFIG_T::weight_t weights[CONFIG_T::impl_filt_width * CONFIG_T::n_chan * CONFIG_T::n_filt], - const typename CONFIG_T::bias_t biases[CONFIG_T::n_filt]) { - // im2col performs no filter transformations; therefore, filter size remains constant - assert(CONFIG_T::filt_width == CONFIG_T::impl_filt_width); - - // Unroll factor for loop traversing input image, derived from parallelization_factor - static constexpr int pf = MIN(CONFIG_T::parallelization_factor, CONFIG_T::out_width); - -ColLoop: - #pragma unroll pf - #pragma ii CONFIG_T::reuse_factor - for (int i = 0; i < CONFIG_T::out_width; i++) { - // Loop variables should always be declared in the deepest scope available - // See Intel's HLS - Loop Best Practices - // https://www.intel.com/content/www/us/en/docs/programmable/683152/22-2/declare-variables-in-the-deepest-scope.html - - hls_register data_T data_col[CONFIG_T::impl_filt_width * CONFIG_T::n_chan]; - im2col_1d_cl(data, data_col, i); - - hls_register res_T res_col[CONFIG_T::n_filt]; - dense_resource(data_col, res_col, weights, biases); - - // Unroll fully, since - // (1) n_filt is usually low in io_parallel (< 32) - // (2) no complex operations handled in loop, this loop performs a simple register writing operation - FiltLoop: - #pragma unroll - for (int j = 0; j < CONFIG_T::n_filt; j++) { - res[i * CONFIG_T::n_filt + j] = res_col[j]; - } - } -} - -// **************************************************************** -// 1D Convolution for 3x1 kernels from Winograd's algoirithm -// **************************************************************** - -// Explicitly transformed input (B'dB) needed for Winograd convolution, as explained by Lavin & Gray (2015) -template -inline void winograd_transform_input_tile_3x1_kernel(const data_T I[4], res_T D[4]) { - D[0] = I[0] - I[2]; - D[1] = I[1] + I[2]; - D[2] = -I[1] + I[2]; - D[3] = I[1] - I[3]; -} - -template -void winograd_conv1d_3x1_kernel_cl( - data_T data[CONFIG_T::in_width * CONFIG_T::n_chan], res_T res[CONFIG_T::out_width * CONFIG_T::n_filt], - const typename CONFIG_T::weight_t weights[CONFIG_T::impl_filt_width * CONFIG_T::n_chan * CONFIG_T::n_filt], - const typename CONFIG_T::bias_t biases[CONFIG_T::n_filt]) { - // Ensure Winograd conditions are met - assert(CONFIG_T::filt_width == 3); - assert(CONFIG_T::stride_width == 1); - assert(CONFIG_T::out_width > 2); - - // Unroll factor for loop traversing input image, derived from parallelization_factor - static constexpr int pf = MIN(CONFIG_T::parallelization_factor, CONFIG_T::out_width); - - // Initialise result to bias - // Unroll fully, as loop performs a simple operation - assigning the outputs to a constant value - #pragma unroll - for (int i = 0; i < CONFIG_T::out_width; i++) { - int offset = CONFIG_T::n_filt * i; - #pragma unroll - for (int f = 0; f < CONFIG_T::n_filt; f++) { - res[offset + f] = static_cast(biases[f]); - } - } - -WidthLoop: - #pragma unroll pf - for (int col = 0; col < CONFIG_T::out_width; col += 2) { - ChannelLoop: - #pragma unroll - for (int channel = 0; channel < CONFIG_T::n_chan; channel++) { - // Get current 4x1 tile - hls_register data_T T[16]; - hls_register uint8_t p = 0; - - #pragma unroll - for (int c = col - (int)CONFIG_T::pad_left; c < col + 4 - (int)CONFIG_T::pad_left; c++) { - if (c < CONFIG_T::in_width && c >= 0) { - T[p++] = data[c * CONFIG_T::n_chan + channel]; - } else { - T[p++] = 0; - } - } - - // Transform input tile - hls_register typename CONFIG_T::accum_t D[4]; - winograd_transform_input_tile_3x1_kernel(T, D); - - #pragma unroll - for (int filter = 0; filter < CONFIG_T::n_filt; filter++) { - hls_register int filter_offset = 4 * (CONFIG_T::n_chan * filter + channel); - - // Hadamard product between transformed input tile and kernel - hls_register typename CONFIG_T::accum_t Y[4]; - #pragma unroll - for (int i = 0; i < 4; i++) { - Y[i] = static_cast(D[i] * weights[filter_offset + i]); - } - - // Explicitly transform intermediate result Z = A'YA and save to output - res[CONFIG_T::n_filt * col + filter] += static_cast(Y[0] + Y[1] + Y[2]); - if ((col + 1) < CONFIG_T::out_width) - res[CONFIG_T::n_filt * (col + 1) + filter] += static_cast(Y[1] - Y[2] - Y[3]); - } - } - } -} - -// **************************************************************** -// 1D Convolution for 1x1 kernels using optimized im2col -// **************************************************************** - -template -void im2col_1d_pointwise_cl(data_T data[CONFIG_T::in_width * CONFIG_T::n_chan], data_T data_col[CONFIG_T::n_chan], - const int col) { - // pointwise_im2col can be unrolled fully, only one loop with n_chan iterations - - hls_register int index = 0; - -ChannelLoop: - #pragma unroll - for (int channel = 0; channel < CONFIG_T::n_chan; channel++) { - hls_register int index_data = (col * CONFIG_T::stride_width - CONFIG_T::pad_left) * CONFIG_T::n_chan + channel; - if (index_data >= 0 && index_data < CONFIG_T::in_width * CONFIG_T::n_chan) { - data_col[index++] = data[index_data]; - } else { - data_col[index++] = 0; - } - } -} - -template -void pointwise_conv_1d_resource_cl(data_T data[CONFIG_T::in_width * CONFIG_T::n_chan], - res_T res[CONFIG_T::out_width * CONFIG_T::n_filt], - const typename CONFIG_T::weight_t weights[CONFIG_T::n_chan * CONFIG_T::n_filt], - const typename CONFIG_T::bias_t biases[CONFIG_T::n_filt]) { - assert(CONFIG_T::filt_width == 1); - - // Unroll factor for loop traversing input image, derived from parallelization_factor - static constexpr int pf = MIN(CONFIG_T::parallelization_factor, CONFIG_T::out_width); - -ColLoop: - #pragma unroll pf - #pragma ii CONFIG_T::reuse_factor - for (int col = 0; col < CONFIG_T::out_width; col++) { - // Loop variables should always be declared in the deepest scope available - // See Intel's HLS - Loop Best Practices - // https://www.intel.com/content/www/us/en/docs/programmable/683152/22-2/declare-variables-in-the-deepest-scope.html - - hls_register data_T data_col[CONFIG_T::n_chan]; - im2col_1d_pointwise_cl(data, data_col, col); - - hls_register res_T res_col[CONFIG_T::n_filt]; - dense_resource(data_col, res_col, weights, biases); - - // Unroll fully, since - // (1) n_filt is usually low in io_parallel (< 32) - // (2) no complex operations handled in loop, this loop performs a simple register writing operation - FiltLoop: - #pragma unroll - for (int k = 0; k < CONFIG_T::n_filt; k++) { - res[col * CONFIG_T::n_filt + k] = res_col[k]; - } - } -} - -// **************************************************************** -// Top-level function - handles different implementations -// **************************************************************** -template -void conv_1d_resource_cl( - data_T data[CONFIG_T::in_width * CONFIG_T::n_chan], res_T res[CONFIG_T::out_width * CONFIG_T::n_filt], - const typename CONFIG_T::weight_t weights[CONFIG_T::impl_filt_width * CONFIG_T::n_chan * CONFIG_T::n_filt], - const typename CONFIG_T::bias_t biases[CONFIG_T::n_filt]) { - static constexpr bool winograd_conditions = - // Winograd's minimal filtering algorithm not applicable to stride != 1 - CONFIG_T::stride_width == 1 && - - // Intel HLS will fail to pipeline the entire component if the Winograd loop only runs once - CONFIG_T::out_width > 2 && - - // Verify user opted for Winograd - CONFIG_T::implementation == nnet::conv1d_implementation::combination || - CONFIG_T::implementation == nnet::conv1d_implementation::winograd; - - if (CONFIG_T::filt_width == 3 && winograd_conditions) { - winograd_conv1d_3x1_kernel_cl(data, res, weights, biases); - } else { - conv_1d_im2col_cl(data, res, weights, biases); - } -} - -} // namespace nnet -#endif diff --git a/hls4ml/templates/quartus/firmware/nnet_utils/nnet_conv1d_stream.h b/hls4ml/templates/quartus/firmware/nnet_utils/nnet_conv1d_stream.h deleted file mode 100644 index e4a2e6d1e9..0000000000 --- a/hls4ml/templates/quartus/firmware/nnet_utils/nnet_conv1d_stream.h +++ /dev/null @@ -1,172 +0,0 @@ -#ifndef NNET_CONV1D_STREAM_H_ -#define NNET_CONV1D_STREAM_H_ - -#include "nnet_dense.h" -#include "nnet_types.h" - -namespace nnet { - -/* - * void kernel_shift(shift_buffer, kernel_window) - * - * Args: - * shift_buffer - array elements popped from the line the buffer during the shift line buffer operation - * kernel_window - array of values from the input currently being convolved with the kernel - * - * Values from shift_buffer are inserted into kernel_window, updating the values to be convolved - */ -template -void kernel_shift_1d(typename data_T::value_type shift_buffer[CONFIG_T::n_chan], - typename data_T::value_type kernel_window[CONFIG_T::filt_width * CONFIG_T::n_chan]) { -/* - * Manually shift kernel_window by one step to the left - * Not possible to use nnet::shift_reg as the kernel window is convolved with the kernel weights using dense matrix - * multiplication Dense matrix multiplication is only implemented for arrays However, provided certain timing constrains are - * met, Intel HLS automatically infers a shift operation and implements kernel_window as a shift register To verify, see - * synthesis report in report.html > Area Analysis of System - */ -KernelShiftWidth: - #pragma unroll - for (int col = 0; col < CONFIG_T::filt_width - 1; col++) { - KernelShiftChannel: - #pragma unroll - for (int channel = 0; channel < CONFIG_T::n_chan; channel++) { - kernel_window[col * CONFIG_T::n_chan + channel] = kernel_window[(col + 1) * CONFIG_T::n_chan + channel]; - } - } - -// Insert shift_buffer values into the last column of the kernel window -KernelPushChannel: - #pragma unroll - for (int channel = 0; channel < CONFIG_T::n_chan; channel++) { - kernel_window[(CONFIG_T::filt_width - 1) * CONFIG_T::n_chan + channel] = shift_buffer[channel]; - } -} - -/* - * void shift_line_buffer(in_element, line_buffer, shift_buffer) - * - * Args: - * in_element - current elements from input image, data_T type is usually nnet::array, size of array corresponds to number - * of channels line_buffer - chained array of shift registers, one for each row of the kernel and channel shift_buffer - - * array elements popped from the line the buffer during the shift operation - * - * Values from in_element are inserted into the line buffer, causing all other elements to be shifted by one - * Popped elements are later used to update the kernel window, during the kernel_shift operation - */ -template -void shift_line_buffer_1d( - const data_T &in_elem, - nnet::shift_reg - line_buffer[CONFIG_T::n_chan], - typename data_T::value_type shift_buffer[CONFIG_T::n_chan]) { -// For every channel, insert the incoming pixel at end of the shift buffer -UpdateBuffer: - #pragma unroll - for (int channel = 0; channel < CONFIG_T::n_chan; channel++) { - shift_buffer[channel] = in_elem[channel]; - } -} - -/* - * void compute_output_buffer(in_element, res_stream, line_buffer, kernel_window, weights, biases) - * - * Args: - * in_element - current elements from input image, data_T type is usually nnet::array, size of array corresponds to number - * of channels res_stream - output stream, passed by reference to allow direct writing line_buffer - chained array of shift - * registers, one for each row of the kernel and channel kernel_window - array of values from the input currently convolved - * with the kernel weights - Conv1D layer weights biases - Conv1D layer biases - * - * Function executes 4 steps: - * (1) Shift line buffer - updates the contents of the chained shift registers, inserting the new inputs and removing last - * elements (2) Kernel shift - updates the elements of the kernel window, by storing the new inputs and popped elements from - * the line buffer (3) Matrix multiplication - performs dense matrix multiplication between the current input window and - * kernel weights (4) Counter housekeeping - keeps track of current pixel and stride - */ -template -void compute_output_buffer_1d( - const data_T &in_elem, stream &res_stream, - nnet::shift_reg - line_buffer[CONFIG_T::n_chan], - typename data_T::value_type kernel_window[CONFIG_T::filt_width * CONFIG_T::n_chan], - const typename CONFIG_T::weight_t weights[CONFIG_T::kernel_size * CONFIG_T::n_chan * CONFIG_T::n_filt], - const typename CONFIG_T::bias_t biases[CONFIG_T::n_filt]) { - // Thresholds - static constexpr int lShiftX = CONFIG_T::filt_width - 1; - - // X position pixel - static int pX = 0; - - // X strides - static int sX = 0; - - // Step 1 - Shift line buffer - hls_register typename data_T::value_type shift_buffer[CONFIG_T::n_chan]; - nnet::shift_line_buffer_1d(in_elem, line_buffer, shift_buffer); - - // Step 2 - Kernel shift - nnet::kernel_shift_1d(shift_buffer, kernel_window); - - // Check to see if we have a full kernel - if ((sX - lShiftX) == 0 && pX > (lShiftX - 1)) { - // Step 3 - Dense matrix multiplication - hls_register typename res_T::value_type res_out[CONFIG_T::n_filt]; - dense_resource( - kernel_window, res_out, weights, biases); - - // Write result to output stream - hls_register res_T res_pack; - CastLoop: - #pragma unroll - for (int channel = 0; channel < CONFIG_T::n_filt; channel++) { - res_pack[channel] = res_out[channel]; - } - res_stream.write(res_pack); - } - - // Reached end of image - if ((pX + 1) == (CONFIG_T::in_width + CONFIG_T::pad_left + CONFIG_T::pad_right)) { - pX = 0; - sX = 0; - // Move to the right - } else { - pX++; - sX = ((sX - lShiftX) == 0) ? (sX - CONFIG_T::stride_width + 1) : (sX + 1); - } -} - -template -void conv_1d_cl(stream &data, stream &res, - const typename CONFIG_T::weight_t weights[CONFIG_T::filt_width * CONFIG_T::n_chan * CONFIG_T::n_filt], - const typename CONFIG_T::bias_t biases[CONFIG_T::n_filt]) { - // Line buffer and kernel window - hls_register static nnet::shift_reg - line_buffer[CONFIG_T::n_chan]; - hls_register static typename data_T::value_type kernel_window[CONFIG_T::filt_width * CONFIG_T::n_chan]; - - // An array of length CONFIG_T::n_chan, with elements set to zero (padding for each channel) - static const data_T padds(0); - -// Input image left-side padding -PaddingLeftWidth: - for (int col = 0; col < CONFIG_T::pad_left; col++) { - compute_output_buffer_1d(padds, res, line_buffer, kernel_window, weights, biases); - } - -// Read input image -ReadInputWidth: - for (int col = 0; col < CONFIG_T::in_width; col++) { - compute_output_buffer_1d(data.read(), res, line_buffer, kernel_window, weights, biases); - } - -// Input image right-side padding -PaddingRightWidth: - for (int col = 0; col < CONFIG_T::pad_right; col++) { - compute_output_buffer_1d(padds, res, line_buffer, kernel_window, weights, biases); - } -} - -} // namespace nnet - -#endif diff --git a/hls4ml/templates/quartus/firmware/nnet_utils/nnet_conv2d.h b/hls4ml/templates/quartus/firmware/nnet_utils/nnet_conv2d.h deleted file mode 100644 index 55b635908b..0000000000 --- a/hls4ml/templates/quartus/firmware/nnet_utils/nnet_conv2d.h +++ /dev/null @@ -1,72 +0,0 @@ -#ifndef NNET_CONV2D_H_ -#define NNET_CONV2D_H_ - -#include "nnet_conv2d_resource.h" - -namespace nnet { - -struct conv2d_config { - // I/O sizes - static const unsigned in_height = 10; - static const unsigned in_width = 10; - static const unsigned out_height = 10; - static const unsigned out_width = 10; - - // Number of channels, filters - static const unsigned n_chan = 1; - static const unsigned n_filt = 1; - - // Original filter size - static const unsigned filt_height = 1; - static const unsigned filt_width = 1; - static const unsigned kernel_size = filt_height * filt_width; - - // Modified filter size (post-Wionograd transformation, if applied) - static const unsigned impl_filt_height = 1; - static const unsigned impl_filt_width = 1; - - // Padding, stride, dilation - static const unsigned pad_top = 0; - static const unsigned pad_bottom = 0; - static const unsigned pad_left = 0; - static const unsigned pad_right = 0; - static const unsigned stride_height = 1; - static const unsigned stride_width = 1; - static const unsigned dilation_height = 1; - static const unsigned dilation_width = 1; - - // Run-time configuration - static const unsigned n_zeros = 0; - static const unsigned reuse_factor = 1; - static const unsigned parallelization_factor = 1; - - // TODO: BRAM Storage on Quartus - static const bool store_weights_in_bram = false; - - // Internal data type definitions - typedef float bias_t; - typedef float weight_t; - typedef float accum_t; -}; - -template -void conv_2d_cl(data_T data[CONFIG_T::in_height * CONFIG_T::in_width * CONFIG_T::n_chan], - res_T res[CONFIG_T::out_height * CONFIG_T::out_width * CONFIG_T::n_filt], - const typename CONFIG_T::weight_t - weights[CONFIG_T::impl_filt_height * CONFIG_T::impl_filt_width * CONFIG_T::n_chan * CONFIG_T::n_filt], - const typename CONFIG_T::bias_t biases[CONFIG_T::n_filt]) { - conv_2d_resource_cl(data, res, weights, biases); -} - -template -void pointwise_conv_2d_cl(data_T data[CONFIG_T::in_height * CONFIG_T::in_width * CONFIG_T::n_chan], - res_T res[CONFIG_T::out_height * CONFIG_T::out_width * CONFIG_T::n_filt], - const typename CONFIG_T::weight_t weights[CONFIG_T::n_chan * CONFIG_T::n_filt], - const typename CONFIG_T::bias_t biases[CONFIG_T::n_filt]) { - assert(CONFIG_T::filt_height == 1 && CONFIG_T::filt_width == 1); - pointwise_conv_2d_resource_cl(data, res, weights, biases); -} - -} // namespace nnet - -#endif diff --git a/hls4ml/templates/quartus/firmware/nnet_utils/nnet_conv2d_resource.h b/hls4ml/templates/quartus/firmware/nnet_utils/nnet_conv2d_resource.h deleted file mode 100644 index 623325dd31..0000000000 --- a/hls4ml/templates/quartus/firmware/nnet_utils/nnet_conv2d_resource.h +++ /dev/null @@ -1,306 +0,0 @@ -#ifndef NNET_CONV2D_RESOURCE_H_ -#define NNET_CONV2D_RESOURCE_H_ - -#include - -#include "nnet_common.h" -#include "nnet_dense.h" -#include "nnet_helpers.h" -#include - -namespace nnet { - -enum class conv2d_implementation { combination, im2col, winograd }; - -// **************************************************************** -// im2col - General-purpose 2D Convolution algorithm -// **************************************************************** - -template -void im2col_2d_cl(data_T data[CONFIG_T::in_height * CONFIG_T::in_width * CONFIG_T::n_chan], - data_T data_col[CONFIG_T::impl_filt_height * CONFIG_T::impl_filt_width * CONFIG_T::n_chan], const int row, - const int col) { - // im2col can be unrolled fully, since number of parallel executions = filt_h x filt_w x n_chann ~ O(100) and very little - // DSP usage - - hls_register int index = 0; - -FiltHeightLoop: - #pragma unroll - for (int kernel_row = 0; kernel_row < CONFIG_T::impl_filt_height; kernel_row++) { - hls_register int input_row = - -CONFIG_T::pad_top + kernel_row * CONFIG_T::dilation_height + row * CONFIG_T::stride_height; - - FiltWidthLoop: - #pragma unroll - for (int kernel_col = 0; kernel_col < CONFIG_T::impl_filt_width; kernel_col++) { - hls_register int input_col = - -CONFIG_T::pad_left + kernel_col * CONFIG_T::dilation_width + col * CONFIG_T::stride_width; - - ChannelLoop: - #pragma unroll - for (int channel = 0; channel < CONFIG_T::n_chan; channel++) { - if (input_row >= 0 && input_row < CONFIG_T::in_height && input_col >= 0 && input_col < CONFIG_T::in_width) { - data_col[index++] = - data[input_row * CONFIG_T::in_width * CONFIG_T::n_chan + input_col * CONFIG_T::n_chan + channel]; - } else { - data_col[index++] = 0; - } - } - } - } -} - -template -void conv_2d_im2col_cl(data_T data[CONFIG_T::in_height * CONFIG_T::in_width * CONFIG_T::n_chan], - res_T res[CONFIG_T::out_height * CONFIG_T::out_width * CONFIG_T::n_filt], - const typename CONFIG_T::weight_t weights[CONFIG_T::impl_filt_height * CONFIG_T::impl_filt_width * - CONFIG_T::n_chan * CONFIG_T::n_filt], - const typename CONFIG_T::bias_t biases[CONFIG_T::n_filt]) { - // im2col performs no filter transformations; therefore, filter size remains constant - assert(CONFIG_T::filt_height == CONFIG_T::impl_filt_height && CONFIG_T::filt_width == CONFIG_T::impl_filt_width); - - // Unroll factors for loop traversing input image, derived from parallelization_factor - // Outer loop only gets unrolled after inner loop is fully unrolled - static constexpr int pfc = MIN(CONFIG_T::parallelization_factor, CONFIG_T::out_width); - static constexpr int pfr = MIN((CONFIG_T::parallelization_factor / pfc), CONFIG_T::out_height); - -HeightLoop: - #pragma unroll pfr - for (int i = 0; i < CONFIG_T::out_height; i++) { - WidthLoop: - #pragma unroll pfc - #pragma ii CONFIG_T::reuse_factor - for (int j = 0; j < CONFIG_T::out_width; j++) { - // Loop variables should always be declared in the deepest scope available - // See Intel's HLS - Loop Best Practices - // https://www.intel.com/content/www/us/en/docs/programmable/683152/22-2/declare-variables-in-the-deepest-scope.html - - hls_register data_T data_col[CONFIG_T::impl_filt_height * CONFIG_T::impl_filt_width * CONFIG_T::n_chan]; - im2col_2d_cl(data, data_col, i, j); - - hls_register res_T res_col[CONFIG_T::n_filt]; - dense_resource(data_col, res_col, weights, biases); - - // Unroll fully, since - // (1) n_filt is usually low in io_parallel (< 32) - // (2) no complex operations handled in loop, this loop performs a simple register writing operation - FiltLoop: - #pragma unroll - for (int k = 0; k < CONFIG_T::n_filt; k++) { - res[i * CONFIG_T::out_width * CONFIG_T::n_filt + j * CONFIG_T::n_filt + k] = res_col[k]; - } - } - } -} - -// **************************************************************** -// 2D Convolution for 3x3 kernels from Winograd's algoirithm -// **************************************************************** - -// Explicitly transformed input (B'dB) needed for Winograd calculation, as explained by Lavin & Gray, 2015 -template -inline void winograd_transform_input_tile_3x3_kernel(const data_T I[16], res_T D[16]) { - D[0] = I[0] - I[2] - I[8] + I[10]; - D[1] = I[1] + I[2] - I[9] - I[10]; - D[2] = -I[1] + I[2] + I[9] - I[10]; - D[3] = I[1] - I[3] - I[9] + I[11]; - - D[4] = I[4] - I[6] + I[8] - I[10]; - D[5] = I[5] + I[6] + I[9] + I[10]; - D[6] = -I[5] + I[6] - I[9] + I[10]; - D[7] = I[5] - I[7] + I[9] - I[11]; - - D[8] = -I[4] + I[6] + I[8] - I[10]; - D[9] = -I[5] - I[6] + I[9] + I[10]; - D[10] = I[5] - I[6] - I[9] + I[10]; - D[11] = -I[5] + I[7] + I[9] - I[11]; - - D[12] = I[4] - I[6] - I[12] + I[14]; - D[13] = I[5] + I[6] - I[13] - I[14]; - D[14] = I[6] - I[5] + I[13] - I[14]; - D[15] = I[5] - I[7] - I[13] + I[15]; -} - -template -void winograd_conv2d_3x3_kernel_cl( - data_T data[CONFIG_T::in_height * CONFIG_T::in_width * CONFIG_T::n_chan], - res_T res[CONFIG_T::out_height * CONFIG_T::out_width * CONFIG_T::n_filt], - const typename CONFIG_T::weight_t - weights[CONFIG_T::n_filt * CONFIG_T::n_chan * CONFIG_T::impl_filt_height * CONFIG_T::impl_filt_width], - const typename CONFIG_T::bias_t biases[CONFIG_T::n_filt]) { - // Ensure Winograd conditions are met - assert(CONFIG_T::filt_height == 3 && CONFIG_T::filt_width == 3); - assert(CONFIG_T::stride_height == 1 && CONFIG_T::stride_width == 1); - assert(CONFIG_T::pad_left == CONFIG_T::pad_right && CONFIG_T::pad_top == CONFIG_T::pad_bottom); - assert(CONFIG_T::out_height > 2 && CONFIG_T::out_width > 2); - - // Unroll factor for loop traversing input image, derived from parallelization_factor - // Outer loop only gets unrolled after inner loop is fully unrolled - static constexpr int pfc = MIN(CONFIG_T::parallelization_factor, DIV_ROUNDUP(CONFIG_T::out_width, 2)); - static constexpr int pfr = MIN((CONFIG_T::parallelization_factor / pfc), DIV_ROUNDUP(CONFIG_T::out_height, 2)); - - // Initialise result to bias - // Unroll fully, as loop performs a simple operation - assigning the outputs to a constant value - #pragma unroll - for (int i = 0; i < CONFIG_T::out_height * CONFIG_T::out_width; i++) { - int offset = CONFIG_T::n_filt * i; - #pragma unroll - for (int f = 0; f < CONFIG_T::n_filt; f++) { - res[offset + f] = static_cast(biases[f]); - } - } - -HeightLoop: - #pragma unroll pfr - for (int row = 0; row < CONFIG_T::out_height; row += 2) { - WidthLoop: - #pragma unroll pfc - for (int col = 0; col < CONFIG_T::out_width; col += 2) { - ChannelLoop: - #pragma unroll - for (int channel = 0; channel < CONFIG_T::n_chan; channel++) { - // Get current 4x4 tile - hls_register data_T T[16]; - hls_register typename CONFIG_T::accum_t D[16]; - hls_register uint8_t p = 0; - - #pragma unroll - for (int r = row - (int)CONFIG_T::pad_top; r < row + 4 - (int)CONFIG_T::pad_top; r++) { - #pragma unroll - for (int c = col - (int)CONFIG_T::pad_left; c < col + 4 - (int)CONFIG_T::pad_left; c++) { - if (r < CONFIG_T::in_height && r >= 0 && c < CONFIG_T::in_width && c >= 0) { - T[p++] = data[r * CONFIG_T::in_width * CONFIG_T::n_chan + c * CONFIG_T::n_chan + channel]; - } else { - T[p++] = 0; - } - } - } - - // Transform input tile - winograd_transform_input_tile_3x3_kernel(T, D); - - #pragma unroll - for (int filter = 0; filter < CONFIG_T::n_filt; filter++) { - hls_register int filter_offset = 16 * (CONFIG_T::n_chan * filter + channel); - - // Hadamard product between transformed input tile and kernel - hls_register typename CONFIG_T::accum_t Y[16]; - #pragma unroll - for (int i = 0; i < 16; i++) { - Y[i] = static_cast(D[i] * weights[filter_offset + i]); - } - - // Explicitly transform intermediate result Z = A'YA and save to output - res[CONFIG_T::n_filt * (row * CONFIG_T::out_width + col) + filter] += - static_cast(Y[0] + Y[1] + Y[2] + Y[4] + Y[5] + Y[6] + Y[8] + Y[9] + Y[10]); - if ((col + 1) < CONFIG_T::out_height) - res[CONFIG_T::n_filt * (row * CONFIG_T::out_width + (col + 1)) + filter] += - static_cast(Y[1] - Y[2] - Y[3] + Y[5] - Y[6] - Y[7] + Y[9] - Y[10] - Y[11]); - if ((row + 1) < CONFIG_T::out_width) - res[CONFIG_T::n_filt * ((row + 1) * CONFIG_T::out_width + col) + filter] += - static_cast(Y[4] + Y[5] + Y[6] - Y[8] - Y[9] - Y[10] - Y[12] - Y[13] - Y[14]); - if ((row + 1) < (CONFIG_T::out_width) && (col + 1) < CONFIG_T::out_height) - res[CONFIG_T::n_filt * ((row + 1) * CONFIG_T::out_width + (col + 1)) + filter] += - static_cast(Y[5] - Y[6] - Y[7] - Y[9] + Y[10] + Y[11] + Y[15] - Y[13] + Y[14]); - } - } - } - } -} - -// **************************************************************** -// 2D Convolution for 1x1 kernels using optimized im2col -// **************************************************************** - -template -void im2col_2d_pointwise_cl(data_T data[CONFIG_T::in_height * CONFIG_T::in_width * CONFIG_T::n_chan], - data_T data_col[CONFIG_T::n_chan], const int row, const int col) { - // pointwise_im2col can be unrolled fully, only one loop with n_chan iterations - - hls_register int index = 0; - -ChannelLoop: - #pragma unroll - for (int channel = 0; channel < CONFIG_T::n_chan; channel++) { - - hls_register int input_row = -CONFIG_T::pad_top + row * CONFIG_T::stride_height; - hls_register int input_col = -CONFIG_T::pad_left + col * CONFIG_T::stride_width; - - if (input_row >= 0 && input_row < CONFIG_T::in_height && input_col >= 0 && input_col < CONFIG_T::in_width) { - data_col[index++] = - data[input_row * CONFIG_T::in_width * CONFIG_T::n_chan + input_col * CONFIG_T::n_chan + channel]; - } else { - data_col[index++] = 0; - } - } -} - -template -void pointwise_conv_2d_resource_cl(data_T data[CONFIG_T::in_height * CONFIG_T::in_width * CONFIG_T::n_chan], - res_T res[CONFIG_T::out_height * CONFIG_T::out_width * CONFIG_T::n_filt], - const typename CONFIG_T::weight_t weights[CONFIG_T::n_chan * CONFIG_T::n_filt], - const typename CONFIG_T::bias_t biases[CONFIG_T::n_filt]) { - assert(CONFIG_T::filt_height == 1 && CONFIG_T::filt_width == 1); - - // Unroll factors for loop traversing input image, derived from parallelization_factor - // Outer loop only gets unrolled after inner loop is fully unrolled - static constexpr int pfc = MIN(CONFIG_T::parallelization_factor, CONFIG_T::out_width); - static constexpr int pfr = MIN((CONFIG_T::parallelization_factor / pfc), CONFIG_T::out_height); - -HeightLoop: - #pragma unroll pfr - for (int row = 0; row < CONFIG_T::out_height; row++) { - WidthLoop: - #pragma unroll pfc - #pragma ii CONFIG_T::reuse_factor - for (int col = 0; col < CONFIG_T::out_width; col++) { - // Loop variables should always be declared in the deepest scope available - // See Intel's HLS - Loop Best Practices - // https://www.intel.com/content/www/us/en/docs/programmable/683152/22-2/declare-variables-in-the-deepest-scope.html - - hls_register data_T data_col[CONFIG_T::n_chan]; - im2col_2d_pointwise_cl(data, data_col, row, col); - - hls_register res_T res_col[CONFIG_T::n_filt]; - dense_resource(data_col, res_col, weights, biases); - - FiltLoop: - #pragma unroll - for (int k = 0; k < CONFIG_T::n_filt; k++) { - res[row * CONFIG_T::out_width * CONFIG_T::n_filt + col * CONFIG_T::n_filt + k] = res_col[k]; - } - } - } -} - -// **************************************************************** -// Top-level function - handles different implementations -// **************************************************************** -template -void conv_2d_resource_cl(data_T data[CONFIG_T::in_height * CONFIG_T::in_width * CONFIG_T::n_chan], - res_T res[CONFIG_T::out_height * CONFIG_T::out_width * CONFIG_T::n_filt], - const typename CONFIG_T::weight_t weights[CONFIG_T::impl_filt_height * CONFIG_T::impl_filt_width * - CONFIG_T::n_chan * CONFIG_T::n_filt], - const typename CONFIG_T::bias_t biases[CONFIG_T::n_filt]) { - static constexpr bool winograd_conditions = - // Winograd's minimal filtering algorithm not applicable to stride != 1 - CONFIG_T::stride_height == 1 && CONFIG_T::stride_width == 1 && - - // Intel HLS will fail to pipeline the entire component if the Winograd loop only runs once - CONFIG_T::out_height > 2 && CONFIG_T::out_width > 2 && - - // Verify user opted for Winograd - CONFIG_T::implementation == nnet::conv2d_implementation::combination || - CONFIG_T::implementation == nnet::conv2d_implementation::winograd; - - if (CONFIG_T::filt_height == 3 && CONFIG_T::filt_width == 3 && winograd_conditions) { - winograd_conv2d_3x3_kernel_cl(data, res, weights, biases); - } else { - conv_2d_im2col_cl(data, res, weights, biases); - } -} - -} // namespace nnet - -#endif diff --git a/hls4ml/templates/quartus/firmware/nnet_utils/nnet_conv2d_stream.h b/hls4ml/templates/quartus/firmware/nnet_utils/nnet_conv2d_stream.h deleted file mode 100644 index db9742807d..0000000000 --- a/hls4ml/templates/quartus/firmware/nnet_utils/nnet_conv2d_stream.h +++ /dev/null @@ -1,238 +0,0 @@ -#ifndef NNET_CONV2D_STREAM_H_ -#define NNET_CONV2D_STREAM_H_ - -#include "nnet_dense.h" -#include "nnet_types.h" - -namespace nnet { - -/* - * void kernel_shift(shift_buffer, kernel_window) - * - * Args: - * shift_buffer - array elements popped from the line the buffer during the shift line buffer operation - * kernel_window - array of values from the input currently being convolved with the kernel - * - * Values from shift_buffer are inserted into kernel_window, updating the values to be convolved - */ -template -void kernel_shift_2d( - typename data_T::value_type shift_buffer[CONFIG_T::filt_height][CONFIG_T::n_chan], - typename data_T::value_type kernel_window[CONFIG_T::filt_width * CONFIG_T::filt_height * CONFIG_T::n_chan]) { -/* - * Manually shift kernel_window by one step to the left - * Not possible to use nnet::shift_reg as the kernel window is convolved with the kernel weights using dense matrix - * multiplication Dense matrix multiplication is only implemented for arrays However, provided certain timing constrains are - * met, Intel HLS automatically infers a shift operation and implements kernel_window as a shift register To verify, see - * synthesis report in report.html > Area Analysis of System - */ -KernelShiftWidth: - #pragma unroll - for (int col = 0; col < CONFIG_T::filt_width - 1; col++) { - KernelShiftHeight: - #pragma unroll - for (int row = 0; row < CONFIG_T::filt_height; row++) { - KernelShiftChannel: - #pragma unroll - for (int channel = 0; channel < CONFIG_T::n_chan; channel++) { - kernel_window[row * CONFIG_T::filt_width * CONFIG_T::n_chan + col * CONFIG_T::n_chan + channel] = - kernel_window[row * CONFIG_T::filt_width * CONFIG_T::n_chan + (col + 1) * CONFIG_T::n_chan + channel]; - } - } - } - -// Insert shift_buffer values into the last column of the kernel window -KernelPushHeight: - #pragma unroll - for (int col = 0; col < CONFIG_T::filt_height; col++) { - KernelPushChannel: - #pragma unroll - for (int channel = 0; channel < CONFIG_T::n_chan; channel++) { - kernel_window[(CONFIG_T::filt_width - 1) * CONFIG_T::n_chan + col * CONFIG_T::filt_width * CONFIG_T::n_chan + - channel] = shift_buffer[col][channel]; - } - } -} - -/* - * void shift_line_buffer(in_element, line_buffer, shift_buffer) - * - * Args: - * in_element - current elements from input image, data_T type is usually nnet::array, size of array corresponds to number - * of channels line_buffer - chained array of shift registers, one for each row of the kernel and channel shift_buffer - - * array elements popped from the line the buffer during the shift operation - * - * Values from in_element are inserted into the line buffer, causing all other elements to be shifted by one - * Popped elements are later used to update the kernel window, during the kernel_shift operation - */ -template -void shift_line_buffer_2d( - const data_T &in_elem, - nnet::shift_reg - line_buffer[MAX(CONFIG_T::filt_height - 1, 1)][CONFIG_T::n_chan], - typename data_T::value_type shift_buffer[CONFIG_T::filt_height][CONFIG_T::n_chan]) { -// For every channel, insert the incoming pixel at end of the shift buffer -UpdateBuffer: - #pragma unroll - for (int channel = 0; channel < CONFIG_T::n_chan; channel++) { - shift_buffer[CONFIG_T::filt_height - 1][channel] = in_elem[channel]; - } - -// Shift line buffer and save popped values to shift buffer -LineBufferDataIn: - #pragma unroll - for (int channel = 0; channel < CONFIG_T::n_chan; channel++) { - LineBufferShift: - #pragma unroll - for (unsigned col = 1; col < CONFIG_T::filt_height; col++) { - // Shift the line buffer, return the popped pixel - typename data_T::value_type pop = - line_buffer[col - 1][channel].shift(shift_buffer[CONFIG_T::filt_height - col][channel]); - - // Place popped pixed into the shift buffer, one row above - shift_buffer[CONFIG_T::filt_height - col - 1][channel] = pop; - } - } -} - -/* - * void compute_output_buffer(in_element, res_stream, line_buffer, kernel_window, weights, biases) - * - * Args: - * in_element - current elements from input image, data_T type is usually nnet::array, size of array corresponds to number - * of channels res_stream - output stream, passed by reference to allow direct writing line_buffer - chained array of shift - * registers, one for each row of the kernel and channel kernel_window - array of values from the input currently convolved - * with the kernel weights - Conv1D/Conv2D layer weights biases - Conv1D/Conv2D layer biases - * - * Function executes 4 steps: - * (1) Shift line buffer - updates the contents of the chained shift registers, inserting the new inputs and removing last - * elements (2) Kernel shift - updates the elements of the kernel window, by storing the new inputs and popped elements from - * the line buffer (3) Matrix multiplication - performs dense matrix multiplication between the current input window and - * kernel weights (4) Counter housekeeping - keeps track of current pixel and stride - */ -template -void compute_output_buffer_2d( - const data_T &in_elem, stream &res_stream, - nnet::shift_reg - line_buffer[MAX(CONFIG_T::filt_height - 1, 1)][CONFIG_T::n_chan], - typename data_T::value_type kernel_window[CONFIG_T::filt_height * CONFIG_T::filt_width * CONFIG_T::n_chan], - const typename CONFIG_T::weight_t weights[CONFIG_T::kernel_size * CONFIG_T::n_chan * CONFIG_T::n_filt], - const typename CONFIG_T::bias_t biases[CONFIG_T::n_filt]) { - // Thresholds - static constexpr int lShiftX = CONFIG_T::filt_width - 1; - static constexpr int lShiftY = CONFIG_T::filt_height - 1; - - // X, Y position pixels - static int pX = 0; - static int pY = 0; - - // X, Y strides - static int sX = 0; - static int sY = 0; - - // Step 1 - Shift line buffer - hls_register typename data_T::value_type shift_buffer[CONFIG_T::filt_height][CONFIG_T::n_chan]; - nnet::shift_line_buffer_2d(in_elem, line_buffer, shift_buffer); - - // Step 2 - Kernel shift - nnet::kernel_shift_2d(shift_buffer, kernel_window); - - // Check to see if we have a full kernel - if ((sX - lShiftX) == 0 && (sY - lShiftY) == 0 && pY > (lShiftY - 1) && pX > (lShiftX - 1)) { - // Step 3 - Dense matrix multiplication - hls_register typename res_T::value_type res_out[CONFIG_T::n_filt]; - dense_resource( - kernel_window, res_out, weights, biases); - - // Write result to output stream - hls_register res_T res_pack; - CastLoop: - #pragma unroll - for (int channel = 0; channel < CONFIG_T::n_filt; channel++) { - res_pack[channel] = res_out[channel]; - } - res_stream.write(res_pack); - } - - // Reached end of image - if ((pX + 1) == (CONFIG_T::in_width + CONFIG_T::pad_left + CONFIG_T::pad_right) && - (pY + 1) == (CONFIG_T::in_height + CONFIG_T::pad_top + CONFIG_T::pad_bottom)) { - pX = 0; - sX = 0; - pY = 0; - sY = 0; - // Reached end of row - } else if ((pX + 1) == (CONFIG_T::in_width + CONFIG_T::pad_left + CONFIG_T::pad_right)) { - pX = 0; - sX = 0; - pY++; - sY = ((sY - lShiftY) == 0) ? (sY - CONFIG_T::stride_height + 1) : (sY + 1); - // Same row, same column, therefore, move to the right - } else { - pX++; - sX = ((sX - lShiftX) == 0) ? (sX - CONFIG_T::stride_width + 1) : (sX + 1); - } -} - -template -void conv_2d_cl(stream &data, stream &res, - const typename CONFIG_T::weight_t - weights[CONFIG_T::filt_height * CONFIG_T::filt_width * CONFIG_T::n_chan * CONFIG_T::n_filt], - const typename CONFIG_T::bias_t biases[CONFIG_T::n_filt]) { - - // Line buffer and kernel window - hls_register static nnet::shift_reg - line_buffer[MAX(CONFIG_T::filt_height - 1, 1)][CONFIG_T::n_chan]; - hls_register static - typename data_T::value_type kernel_window[CONFIG_T::filt_height * CONFIG_T::filt_width * CONFIG_T::n_chan]; - - // An array of length CONFIG_T::n_chan, with elements set to zero (padding for each channel) - static const data_T padds(0); - -// Padding above input image -PaddingTopHeight: - #pragma loop_coalesce 2 - for (int row = 0; row < CONFIG_T::pad_top; row++) { - PaddingTopWidth: - for (int col = 0; col < CONFIG_T::pad_left + CONFIG_T::in_width + CONFIG_T::pad_right; col++) { - compute_output_buffer_2d(padds, res, line_buffer, kernel_window, weights, biases); - } - } - -ReadInputHeight: - #pragma loop_coalesce 2 - for (int row = 0; row < CONFIG_T::in_height; row++) { - // Input image left-side padding - PaddingLeftWidth: - for (int col = 0; col < CONFIG_T::pad_left; col++) { - compute_output_buffer_2d(padds, res, line_buffer, kernel_window, weights, biases); - } - - // Read input image - ReadInputWidth: - for (int col = 0; col < CONFIG_T::in_width; col++) { - compute_output_buffer_2d(data.read(), res, line_buffer, kernel_window, weights, biases); - } - - // Input image right-side padding - PaddingRightWidth: - for (int col = 0; col < CONFIG_T::pad_right; col++) { - compute_output_buffer_2d(padds, res, line_buffer, kernel_window, weights, biases); - } - } - -// Padding below input image -PaddingBottomHeight: - #pragma loop_coalesce 2 - for (int row = 0; row < CONFIG_T::pad_bottom; row++) { - PaddingBottomWidth: - for (int col = 0; col < CONFIG_T::pad_left + CONFIG_T::in_width + CONFIG_T::pad_right; col++) { - compute_output_buffer_2d(padds, res, line_buffer, kernel_window, weights, biases); - } - } -} - -} // namespace nnet - -#endif diff --git a/hls4ml/templates/quartus/firmware/nnet_utils/nnet_dense.h b/hls4ml/templates/quartus/firmware/nnet_utils/nnet_dense.h deleted file mode 100644 index aba0803989..0000000000 --- a/hls4ml/templates/quartus/firmware/nnet_utils/nnet_dense.h +++ /dev/null @@ -1,169 +0,0 @@ -#ifndef NNET_DENSE_LARGE_H_ -#define NNET_DENSE_LARGE_H_ - -#include "nnet_common.h" -#include "nnet_helpers.h" -#include "nnet_mult.h" - -namespace nnet { - -struct dense_config { - // Internal data type definitions - typedef float bias_t; - typedef float weight_t; - typedef float accum_t; - - // Layer Sizes - static const unsigned n_in = 10; - static const unsigned n_out = 10; - - static const unsigned reuse_factor = 1; - static const unsigned block_factor = 1; // DIV_ROUNDUP(CONFIG_T::n_in*CONFIG_T::n_out, CONFIG_T::reuse_factor); - static const unsigned multiplier_limit = 1; // DIV_ROUNDUP(CONFIG_T::n_in*CONFIG_T::n_out, multfactor) - static const unsigned multiplier_factor = 1; // min n_in, rf - static const unsigned multiplier_scale = 1; // M_LIMIT/CONFIG_T::n_out; - static const unsigned reciprocal = 1; // 2^35 / 25 - static const unsigned rf_pad = 0; - static const unsigned bf_pad = 0; - // Resource reuse info - static const unsigned io_type = io_parallel; - static const bool store_weights_in_bram = false; - static const unsigned n_zeros = 0; - // partitioning arrays cyclically to go with roll factors? - - // Default multiplication - template using product = nnet::product::mult; -}; - -template -void dense_rf_gt(data_T data[CONFIG_T::n_in], res_T res[CONFIG_T::n_out], - const typename CONFIG_T::weight_t weights[CONFIG_T::reuse_factor_rounded * CONFIG_T::block_factor_rounded], - const typename CONFIG_T::bias_t biases[CONFIG_T::n_out]) { - assert((CONFIG_T::multiplier_limit % CONFIG_T::n_out == 0 || CONFIG_T::reuse_factor >= CONFIG_T::n_in) && - "The current Reuse Factor is not allowed"); - assert((CONFIG_T::reuse_factor > CONFIG_T::n_in) && "This function is correct only for RF > N_IN"); - //#pragma ii CONFIG_T::reuse_factor - hls_register typename CONFIG_T::accum_t acc[CONFIG_T::n_out]; -Load: - #pragma unroll - for (int iacc = 0; iacc < CONFIG_T::n_out; iacc++) { - acc[iacc] = (typename CONFIG_T::accum_t)biases[iacc]; - } - hls_register int out_index[CONFIG_T::reuse_factor][CONFIG_T::block_factor]; - hls_register int d_index[CONFIG_T::reuse_factor][CONFIG_T::block_factor]; - - #pragma unroll - for (int ir = 0; ir < CONFIG_T::reuse_factor; ir++) { - #pragma unroll - for (int im = 0; im < CONFIG_T::block_factor; im++) { - uint32 w_index = ir + CONFIG_T::reuse_factor * im; - out_index[ir][im] = (w_index / CONFIG_T::multiplier_factor).to_int(); - d_index[ir][im] = w_index % CONFIG_T::n_in; - } - } -Product1: - #pragma nofusion - #pragma speculated_iterations 0 - for (int ir = 0; ir < CONFIG_T::reuse_factor; ir++) { - hls_register typename CONFIG_T::accum_t tmp_acc[CONFIG_T::block_factor]; - Product2: - #pragma unroll - for (int im = 0; im < CONFIG_T::block_factor; im++) { - uint32 w_index = ir + (CONFIG_T::reuse_factor_rounded)*im; - if (w_index >= CONFIG_T::reuse_factor_rounded * CONFIG_T::block_factor_rounded) - continue; - int data_index = d_index[ir][im]; - // Modified this - tmp_acc[im] = - CONFIG_T::template product::product(data[data_index], weights[w_index]); - } - hls_register typename CONFIG_T::accum_t mult[CONFIG_T::multiplier_limit]; - ResetMult: - #pragma unroll - for (int imult = 0; imult < CONFIG_T::multiplier_limit; imult++) { - mult[imult] = 0; - } - AccumLoop1: - #pragma unroll - for (int im = 0; im < CONFIG_T::block_factor; im++) { - int o_index = out_index[ir][im]; - if (o_index >= CONFIG_T::n_out) - continue; // check out of bounds - mult[o_index] += tmp_acc[im]; - } - AccumLoop2: - #pragma unroll - for (int im = 0; im < CONFIG_T::multiplier_limit; im++) { - acc[im] += mult[im]; - } - } -Store: - #pragma unroll - for (int ires = 0; ires < CONFIG_T::n_out; ires++) { - res[ires] = cast(acc[ires]); // acc[jj]; - } -} -template -void dense_rf_lt(data_T data[CONFIG_T::n_in], res_T res[CONFIG_T::n_out], - const typename CONFIG_T::weight_t weights[CONFIG_T::reuse_factor_rounded * CONFIG_T::block_factor_rounded], - const typename CONFIG_T::bias_t biases[CONFIG_T::n_out]) { - assert((CONFIG_T::multiplier_limit % CONFIG_T::n_out == 0 || CONFIG_T::reuse_factor >= CONFIG_T::n_in) && - "The current Reuse Factor is not allowed"); - assert((CONFIG_T::multiplier_limit == CONFIG_T::block_factor) && "This function is correct only for RF <= N_IN"); - - hls_register typename CONFIG_T::accum_t acc[CONFIG_T::n_out]; -InitAccum: - #pragma unroll - for (int iacc = 0; iacc < CONFIG_T::n_out; iacc++) { - acc[iacc] = (typename CONFIG_T::accum_t)biases[iacc]; - } -ReuseLoop: - #pragma nofusion - #pragma speculated_iterations 0 - for (int ir = 0; ir < CONFIG_T::reuse_factor; ir++) { - hls_register typename CONFIG_T::accum_t mult[CONFIG_T::block_factor]; - MultLoop: - #pragma unroll - for (int im = 0, in_index = ir; im < CONFIG_T::block_factor; im++) { - uint32 w_index = ir + (CONFIG_T::reuse_factor_rounded)*im; - if (ir + CONFIG_T::reuse_factor * im >= CONFIG_T::n_in * CONFIG_T::n_out) - continue; - // Modified this - mult[im] = - CONFIG_T::template product::product(data[in_index], weights[w_index]); - in_index += CONFIG_T::reuse_factor; - if (in_index >= CONFIG_T::n_in) - in_index = ir; - } - AccumLoop: - #pragma unroll - for (int im = 0, out_index = 0, acc_step = 0; im < CONFIG_T::block_factor; im++) { - acc[out_index] += mult[im]; - if (acc_step + 1 >= CONFIG_T::multiplier_scale) { - acc_step = 0; - out_index++; - } else { - acc_step++; - } - } - } -// Cast to "res_t" type -Result: - #pragma unroll - for (int ires = 0; ires < CONFIG_T::n_out; ires++) { - res[ires] = cast(acc[ires]); - } -} -template -void dense_resource( - data_T data[CONFIG_T::n_in], res_T res[CONFIG_T::n_out], - const typename CONFIG_T::weight_t weights[CONFIG_T::reuse_factor_rounded * CONFIG_T::block_factor_rounded], - const typename CONFIG_T::bias_t biases[CONFIG_T::n_out]) { - if (CONFIG_T::reuse_factor <= CONFIG_T::n_in) { - dense_rf_lt(data, res, weights, biases); - } else { - dense_rf_gt(data, res, weights, biases); - } -} -} // namespace nnet -#endif diff --git a/hls4ml/templates/quartus/firmware/nnet_utils/nnet_dense_compressed.h b/hls4ml/templates/quartus/firmware/nnet_utils/nnet_dense_compressed.h deleted file mode 100644 index 5619e299fb..0000000000 --- a/hls4ml/templates/quartus/firmware/nnet_utils/nnet_dense_compressed.h +++ /dev/null @@ -1,80 +0,0 @@ -#ifndef NNET_COMPRESSED_LAYER_H_ -#define NNET_COMPRESSED_LAYER_H_ - -#include "nnet_common.h" -#include "nnet_dense.h" - -namespace nnet { - -template -void dense_compressed(data_T data[CONFIG_T::n_in], res_T res[CONFIG_T::n_out], - const typename CONFIG_T::weight_t weights[CONFIG_T::n_nonzeros], - const typename CONFIG_T::bias_t biases[CONFIG_T::n_out]) { - - hls_register typename CONFIG_T::accum_t acc[CONFIG_T::n_out]; - -InitAccum: - #pragma unroll - for (int i = 0; i < CONFIG_T::n_out; i++) { - acc[i] = (typename CONFIG_T::accum_t)(biases[i]); - } - - hls_register int out_index[CONFIG_T::reuse_factor][CONFIG_T::compressed_block_factor]; - hls_register data_T inputs[CONFIG_T::reuse_factor][CONFIG_T::compressed_block_factor]; - - #pragma unroll - for (int ir = 0; ir < CONFIG_T::reuse_factor; ir++) { - #pragma unroll - for (int im = 0; im < CONFIG_T::compressed_block_factor; im++) { - uint32 w = ir + CONFIG_T::reuse_factor * im; - inputs[ir][im] = data[weights[w].row_index]; - out_index[ir][im] = weights[w].col_index; - } - } -ReuseLoop: - #pragma nofusion - #pragma speculated_iterations 0 - for (int ir = 0; ir < CONFIG_T::reuse_factor; ir++) { - hls_register typename CONFIG_T::accum_t mult[CONFIG_T::compressed_block_factor]; - CompressedMultLoop: - #pragma unroll - for (int im = 0; im < CONFIG_T::compressed_block_factor; im++) { - uint32 w = ir + CONFIG_T::reuse_factor * im; - // if (w >= CONFIG_T::reuse_factor*CONFIG_T::compressed_block_factor) continue; - typename CONFIG_T::accum_t prod = mult[im] = - CONFIG_T::template product::product(inputs[0][im], weights[w].weight); - #pragma unroll - for (int is = 0; is < CONFIG_T::reuse_factor - 1; is++) { - inputs[is][im] = inputs[is + 1][im]; - } - } - hls_register typename CONFIG_T::accum_t tmp_acc[CONFIG_T::n_out]; - ResetMult: - #pragma unroll - for (int tacc = 0; tacc < CONFIG_T::n_out; tacc++) { - tmp_acc[tacc] = 0; - } - AccumLoop1: - #pragma unroll - for (int im = 0; im < CONFIG_T::compressed_block_factor; im++) { - int col = out_index[ir][im]; - tmp_acc[col] += mult[im]; - } - AccumLoop2: - #pragma unroll - for (int im = 0; im < CONFIG_T::n_out; im++) { - acc[im] += tmp_acc[im]; - } - } - -// Cast to "res_t" type -ResultLoop: - #pragma unroll - for (unsigned i = 0; i < CONFIG_T::n_out; i++) { - res[i] = cast(acc[i]); - } -} - -} // namespace nnet - -#endif diff --git a/hls4ml/templates/quartus/firmware/nnet_utils/nnet_dense_stream.h b/hls4ml/templates/quartus/firmware/nnet_utils/nnet_dense_stream.h deleted file mode 100644 index 5a9b385530..0000000000 --- a/hls4ml/templates/quartus/firmware/nnet_utils/nnet_dense_stream.h +++ /dev/null @@ -1,46 +0,0 @@ -#ifndef NNET_DENSE_STREAM_H_ -#define NNET_DENSE_STREAM_H_ - -#include "nnet_common.h" -#include "nnet_dense.h" -#include "nnet_types.h" - -namespace nnet { - -template -void dense_resource(stream &data_stream, stream &res_stream, - const typename CONFIG_T::weight_t weights[CONFIG_T::n_in * CONFIG_T::n_out], - const typename CONFIG_T::bias_t biases[CONFIG_T::n_out]) { - hls_register typename data_T::value_type data[CONFIG_T::n_in]; - hls_register typename res_T::value_type res[CONFIG_T::n_out]; - -DataPrepare: - #pragma ii 1 - for (int i_in = 0; i_in < CONFIG_T::n_in / data_T::size; i_in++) { - data_T data_pack = data_stream.read(); - DataPack: - #pragma unroll - for (int i_pack = 0; i_pack < data_T::size; i_pack++) { - data[i_in * data_T::size + i_pack] = data_pack[i_pack]; - } - } - - dense_resource(data, res, weights, biases); - -ResWrite: - #pragma ii 1 - for (unsigned i_out = 0; i_out < CONFIG_T::n_out / res_T::size; i_out++) { - res_T res_pack; - ResPack: - #pragma unroll - for (int i_pack = 0; i_pack < res_T::size; i_pack++) { - res_pack[i_pack] = res[i_out * res_T::size + i_pack]; - } - - res_stream.write(res_pack); - } -} - -} // namespace nnet - -#endif diff --git a/hls4ml/templates/quartus/firmware/nnet_utils/nnet_embed.h b/hls4ml/templates/quartus/firmware/nnet_utils/nnet_embed.h deleted file mode 100644 index 5191239b6d..0000000000 --- a/hls4ml/templates/quartus/firmware/nnet_utils/nnet_embed.h +++ /dev/null @@ -1,45 +0,0 @@ -#ifndef NNET_EMBED_H_ -#define NNET_EMBED_H_ - -#include "nnet_common.h" -#include "nnet_helpers.h" - -namespace nnet { - -struct embed_config { - // Internal data type definitions - typedef float embeddings_t; - - // (Default layer sizes, overwritten form the backend - static const unsigned n_in = 10; - static const unsigned n_out = 16; - static const unsigned vocab_size = 50; - - // Resource reuse info - static const unsigned io_type = io_parallel; - static const unsigned reuse_factor = 1; -}; - -template -void embedding(data_T data[CONFIG_T::n_in], res_T res[CONFIG_T::n_in * CONFIG_T::n_out], - const typename CONFIG_T::embeddings_t embeddings[CONFIG_T::vocab_size * CONFIG_T::n_out]) { - - /* - * Can store embeddings[] in a register, but a large multiiplexer - * is created due to a non-constant access pattern - */ - -InputSequence: - #pragma ii CONFIG_T::reuse_factor - #pragma unroll - for (int j = 0; j < CONFIG_T::n_in; j++) { - DenseEmbedding: - #pragma unroll - for (int i = 0; i < CONFIG_T::n_out; i++) { - res[j * CONFIG_T::n_out + i] = embeddings[data[j].to_uint() * CONFIG_T::n_out + i]; - } - } -} - -} // namespace nnet -#endif diff --git a/hls4ml/templates/quartus/firmware/nnet_utils/nnet_embed_stream.h b/hls4ml/templates/quartus/firmware/nnet_utils/nnet_embed_stream.h deleted file mode 100644 index 51e54e991e..0000000000 --- a/hls4ml/templates/quartus/firmware/nnet_utils/nnet_embed_stream.h +++ /dev/null @@ -1,29 +0,0 @@ -#ifndef NNET_EMBED_STREAM_H_ -#define NNET_EMBED_STREAM_H_ - -namespace nnet { - -template -void embedding(stream &data, stream &res, - const typename CONFIG_T::embeddings_t embeddings[CONFIG_T::vocab_size * CONFIG_T::n_out]) { - data_T in_data = data.read(); - -InputSequence: - #pragma ii CONFIG_T::reuse_factor - for (int j = 0; j < data_T::size; j++) { - - res_T res_pack; - - DenseEmbedding: - #pragma unroll - for (int i = 0; i < CONFIG_T::n_out; i++) { - res_pack[i] = embeddings[in_data[j] * CONFIG_T::n_out + i]; - } - - res.write(res_pack); - } -} - -} // namespace nnet - -#endif diff --git a/hls4ml/templates/quartus/firmware/nnet_utils/nnet_helpers.h b/hls4ml/templates/quartus/firmware/nnet_utils/nnet_helpers.h deleted file mode 100644 index 3bd78c7a84..0000000000 --- a/hls4ml/templates/quartus/firmware/nnet_utils/nnet_helpers.h +++ /dev/null @@ -1,140 +0,0 @@ -#ifndef NNET_HELPERS_H -#define NNET_HELPERS_H - -#include -#include -#include -#include -#include -#include -#include -#include - -namespace nnet { - -template void convert_data(srcType *src, dstType *dst) { - for (size_t i = 0; i < SIZE; i++) { - dst[i] = dstType(src[i]); - } -} - -template void convert_data_back(srcType *src, dstType *dst) { - for (size_t i = 0; i < SIZE; i++) { - dst[i] = static_cast(src[i].to_double()); - } -} - -template void convert_data(srcType *src, stream_in &dst) { - for (size_t i = 0; i < SIZE / dstType::size; i++) { - dstType ctype; - for (size_t j = 0; j < dstType::size; j++) { - ctype[j] = typename dstType::value_type(src[i * dstType::size + j]); - } - dst.write(ctype); - } -} - -template void convert_data_back(stream_out &src, dstType *dst) { - for (size_t i = 0; i < SIZE / srcType::size; i++) { - srcType ctype = src.read(); - for (size_t j = 0; j < srcType::size; j++) { - dst[i * srcType::size + j] = dstType(ctype[j].to_double()); - } - } -} - -extern bool trace_enabled; -extern std::map *trace_outputs; -extern size_t trace_type_size; - -constexpr int ceillog2(int x) { return (x <= 2) ? 1 : 1 + ceillog2((x + 1) / 2); } - -constexpr int floorlog2(int x) { return (x < 2) ? 0 : 1 + floorlog2(x / 2); } - -constexpr int pow2(int x) { return x == 0 ? 1 : 2 * pow2(x - 1); } - -template void save_output_array(data_T *data, save_T *ptr, size_t layer_size) { - for (int i = 0; i < layer_size; i++) { - ptr[i] = static_cast(data[i].to_double()); - } -} - -template void save_output_array(stream &data, save_T *ptr, size_t layer_size) { - for (size_t i = 0; i < layer_size / data_T::size; i++) { - data_T ctype = data.read(); - for (size_t j = 0; j < data_T::size; j++) { - ptr[i * data_T::size + j] = static_cast(ctype[j].to_double()); - } - data.write(ctype); - } -} - -// We don't want to include save_T in this function because it will be inserted into myproject.cpp -// so a workaround with element size is used -template void save_layer_output(data_T *data, const char *layer_name, size_t layer_size) { - if (!trace_enabled) - return; - - if (trace_outputs) { - if (trace_outputs->count(layer_name) > 0) { - if (trace_type_size == 4) { - save_output_array(data, (float *)(*trace_outputs)[layer_name], layer_size); - } else if (trace_type_size == 8) { - save_output_array(data, (double *)(*trace_outputs)[layer_name], layer_size); - } else { - std::cout << "Unknown trace type!" << std::endl; - } - } else { - std::cout << "Layer name: " << layer_name << " not found in debug storage!" << std::endl; - } - } else { - std::ostringstream filename; - filename << "./tb_data/" << layer_name << "_output.log"; // TODO if run as a shared lib, path should be ../tb_data - std::fstream out; - out.open(filename.str(), std::ios::app); - assert(out.is_open()); - for (int i = 0; i < layer_size; i++) { - out << data[i] << " "; // We don't care about precision in text files - } - out << std::endl; - out.close(); - } -} - -template void save_layer_output(stream &data, const char *layer_name, size_t layer_size) { - if (!trace_enabled) - return; - - if (trace_outputs) { - if (trace_outputs->count(layer_name) > 0) { - if (trace_type_size == 4) { - save_output_array(data, (float *)(*trace_outputs)[layer_name], layer_size); - } else if (trace_type_size == 8) { - save_output_array(data, (double *)(*trace_outputs)[layer_name], layer_size); - } else { - std::cout << "Unknown trace type!" << std::endl; - } - } else { - std::cout << "Layer name: " << layer_name << " not found in debug storage!" << std::endl; - } - } else { - std::ostringstream filename; - filename << "./tb_data/" << layer_name << "_output.log"; // TODO if run as a shared lib, path should be ../tb_data - std::fstream out; - out.open(filename.str(), std::ios::app); - assert(out.is_open()); - for (size_t i = 0; i < layer_size / data_T::size; i++) { - data_T ctype = data.read(); - for (size_t j = 0; j < data_T::size; j++) { - out << ctype[j] << " "; - } - data.write(ctype); - } - out << std::endl; - out.close(); - } -} - -} // namespace nnet - -#endif diff --git a/hls4ml/templates/quartus/firmware/nnet_utils/nnet_merge.h b/hls4ml/templates/quartus/firmware/nnet_utils/nnet_merge.h deleted file mode 100644 index a1e177a1b7..0000000000 --- a/hls4ml/templates/quartus/firmware/nnet_utils/nnet_merge.h +++ /dev/null @@ -1,250 +0,0 @@ -#ifndef NNET_MERGE_H_ -#define NNET_MERGE_H_ - -#include "nnet_mult.h" - -namespace nnet { - -struct merge_config { - static const unsigned n_elem = 10; - static const unsigned reuse_factor = 1; -}; - -struct dot_config { - static const unsigned n_in = 10; - static const unsigned n_out = 1; - - static const unsigned reuse_factor = 1; - - typedef float accum_t; - - template using product = nnet::product::mult; -}; - -struct concat_config { - static const unsigned n_elem1_0 = 10; - static const unsigned n_elem1_1 = 10; - static const unsigned n_elem1_2 = 10; - static const unsigned n_elem2_0 = 10; - static const unsigned n_elem2_1 = 10; - static const unsigned n_elem2_2 = 10; - - static const unsigned axis = -1; -}; - -template -void add(input1_T data1[CONFIG_T::n_elem], input2_T data2[CONFIG_T::n_elem], res_T res[CONFIG_T::n_elem]) { - #pragma unroll - for (int i = 0; i < CONFIG_T::n_elem; i++) { - res[i] = static_cast(data1[i] + data2[i]); - } -} - -template -void subtract(input1_T data1[CONFIG_T::n_elem], input2_T data2[CONFIG_T::n_elem], res_T res[CONFIG_T::n_elem]) { - #pragma unroll - for (int i = 0; i < CONFIG_T::n_elem; i++) { - res[i] = static_cast(data1[i] - data2[i]); - } -} - -template -void multiply(input1_T data1[CONFIG_T::n_elem], input2_T data2[CONFIG_T::n_elem], res_T res[CONFIG_T::n_elem]) { - #pragma unroll - for (int i = 0; i < CONFIG_T::n_elem; i++) { - res[i] = static_cast(data1[i] * data2[i]); - } -} - -template -void average(input1_T data1[CONFIG_T::n_elem], input2_T data2[CONFIG_T::n_elem], res_T res[CONFIG_T::n_elem]) { - #pragma unroll - for (int i = 0; i < CONFIG_T::n_elem; i++) { - res[i] = static_cast((data1[i] + data2[i]) * ac_fixed<1, 0, false>(0.5)); - } -} - -template -void maximum(input1_T data1[CONFIG_T::n_elem], input2_T data2[CONFIG_T::n_elem], res_T res[CONFIG_T::n_elem]) { - #pragma unroll - for (int i = 0; i < CONFIG_T::n_elem; i++) { - res[i] = (data1[i] > data2[i]) ? static_cast(data1[i]) : static_cast(data2[i]); - } -} - -template -void minimum(input1_T data1[CONFIG_T::n_elem], input2_T data2[CONFIG_T::n_elem], res_T res[CONFIG_T::n_elem]) { - #pragma unroll - for (int i = 0; i < CONFIG_T::n_elem; i++) { - res[i] = (data1[i] < data2[i]) ? static_cast(data1[i]) : static_cast(data2[i]); - } -} - -template -void dot1d(input1_T data1[CONFIG_T::n_in], input2_T data2[CONFIG_T::n_in], res_T res[CONFIG_T::n_out]) { - constexpr unsigned multiplier_limit = DIV_ROUNDUP(CONFIG_T::n_in, CONFIG_T::reuse_factor); - - hls_register typename CONFIG_T::accum_t mult[CONFIG_T::n_in]; -Product: - #pragma unroll multiplier_limit - for (int i = 0; i < CONFIG_T::n_in; i++) { - mult[i] = CONFIG_T::template product::product(data1[i], data2[i]); - } - - hls_register typename CONFIG_T::accum_t acc = 0; -Accum: - #pragma unroll - for (int i = 0; i < CONFIG_T::n_in; i++) { - acc += mult[i]; - } - - res[0] = static_cast(acc); -} - -template -void concatenate1d(input1_T data1[CONFIG_T::n_elem1_0], input2_T data2[CONFIG_T::n_elem2_0], - res_T res[CONFIG_T::n_elem1_0 + CONFIG_T::n_elem2_0]) { - #pragma unroll - for (int i = 0; i < CONFIG_T::n_elem1_0; i++) { - res[i] = static_cast(data1[i]); - } - - #pragma unroll - for (int i = 0; i < CONFIG_T::n_elem2_0; i++) { - res[CONFIG_T::n_elem1_0 + i] = static_cast(data2[i]); - } -} - -template -void concatenate2d_0(input1_T data1[CONFIG_T::n_elem1_0 * CONFIG_T::n_elem1_1], - input2_T data2[CONFIG_T::n_elem2_0 * CONFIG_T::n_elem2_1], - res_T res[CONFIG_T::n_elem1_0 * CONFIG_T::n_elem1_1 + CONFIG_T::n_elem2_0 * CONFIG_T::n_elem2_1]) { - #pragma unroll - for (int i = 0; i < CONFIG_T::n_elem1_0 * CONFIG_T::n_elem1_1; i++) { - res[i] = static_cast(data1[i]); - } - - #pragma unroll - for (int i = 0; i < CONFIG_T::n_elem2_0 * CONFIG_T::n_elem2_1; i++) { - res[CONFIG_T::n_elem1_0 * CONFIG_T::n_elem1_1 + i] = static_cast(data2[i]); - } -} - -template -void concatenate2d_1(input1_T data1[CONFIG_T::n_elem1_0 * CONFIG_T::n_elem1_1], - input2_T data2[CONFIG_T::n_elem2_0 * CONFIG_T::n_elem2_1], - res_T res[CONFIG_T::n_elem1_0 * CONFIG_T::n_elem1_1 + CONFIG_T::n_elem2_0 * CONFIG_T::n_elem2_1]) { - for (int i = 0; i < CONFIG_T::n_elem1_0; i++) { - #pragma unroll - for (int j = 0; j < CONFIG_T::n_elem1_1; j++) { - res[i * (CONFIG_T::n_elem1_1 + CONFIG_T::n_elem2_1) + j] = - static_cast(data1[i * CONFIG_T::n_elem1_1 + j]); - } - - #pragma unroll - for (int j = 0; j < CONFIG_T::n_elem2_1; j++) { - res[i * (CONFIG_T::n_elem1_1 + CONFIG_T::n_elem2_1) + CONFIG_T::n_elem1_1 + j] = - static_cast(data2[i * CONFIG_T::n_elem2_1 + j]); - } - } -} - -template -void concatenate2d(input1_T data1[CONFIG_T::n_elem1_0 * CONFIG_T::n_elem1_1], - input2_T data2[CONFIG_T::n_elem2_0 * CONFIG_T::n_elem2_1], - res_T res[CONFIG_T::n_elem1_0 * CONFIG_T::n_elem1_1 + CONFIG_T::n_elem2_0 * CONFIG_T::n_elem2_1]) { - if (CONFIG_T::axis == 2 || CONFIG_T::axis == -1) { - concatenate2d_1(data1, data2, res); - } else { - concatenate2d_0(data1, data2, res); - } -} - -template -void concatenate3d_0(input1_T data1[CONFIG_T::n_elem1_0 * CONFIG_T::n_elem1_1 * CONFIG_T::n_elem1_2], - input2_T data2[CONFIG_T::n_elem2_0 * CONFIG_T::n_elem2_1 * CONFIG_T::n_elem2_2], - res_T res[CONFIG_T::n_elem1_0 * CONFIG_T::n_elem1_1 * CONFIG_T::n_elem1_2 + - CONFIG_T::n_elem2_0 * CONFIG_T::n_elem2_1 * CONFIG_T::n_elem2_2]) { - #pragma unroll - for (int i = 0; i < CONFIG_T::n_elem1_0 * CONFIG_T::n_elem1_1 * CONFIG_T::n_elem1_2; i++) { - res[i] = static_cast(data1[i]); - } - - #pragma unroll - for (int i = 0; i < CONFIG_T::n_elem2_0 * CONFIG_T::n_elem2_1 * CONFIG_T::n_elem2_2; i++) { - res[CONFIG_T::n_elem1_0 * CONFIG_T::n_elem1_1 * CONFIG_T::n_elem1_2 + i] = static_cast(data2[i]); - } -} - -template -void concatenate3d_1(input1_T data1[CONFIG_T::n_elem1_0 * CONFIG_T::n_elem1_1 * CONFIG_T::n_elem1_2], - input2_T data2[CONFIG_T::n_elem2_0 * CONFIG_T::n_elem2_1 * CONFIG_T::n_elem2_2], - res_T res[CONFIG_T::n_elem1_0 * CONFIG_T::n_elem1_1 * CONFIG_T::n_elem1_2 + - CONFIG_T::n_elem2_0 * CONFIG_T::n_elem2_1 * CONFIG_T::n_elem2_2]) { - for (int i = 0; i < CONFIG_T::n_elem1_0; i++) { - for (int j = 0; j < CONFIG_T::n_elem1_1; j++) { - #pragma unroll - for (int k = 0; k < CONFIG_T::n_elem1_2; k++) { - int res_idx = - i * (CONFIG_T::n_elem1_1 + CONFIG_T::n_elem2_1) * CONFIG_T::n_elem1_2 + j * CONFIG_T::n_elem1_2 + k; - int data_idx = i * CONFIG_T::n_elem1_1 * CONFIG_T::n_elem1_2 + j * CONFIG_T::n_elem1_2 + k; - res[res_idx] = static_cast(data1[data_idx]); - } - } - - for (int j = 0; j < CONFIG_T::n_elem2_1; j++) { - #pragma unroll - for (int k = 0; k < CONFIG_T::n_elem2_2; k++) { - int res_idx = i * (CONFIG_T::n_elem1_1 + CONFIG_T::n_elem2_1) * CONFIG_T::n_elem1_2 + - (j + CONFIG_T::n_elem1_1) * CONFIG_T::n_elem1_2 + k; - int data_idx = i * CONFIG_T::n_elem2_1 * CONFIG_T::n_elem2_2 + j * CONFIG_T::n_elem2_2 + k; - res[res_idx] = static_cast(data2[data_idx]); - } - } - } -} - -template -void concatenate3d_2(input1_T data1[CONFIG_T::n_elem1_0 * CONFIG_T::n_elem1_1 * CONFIG_T::n_elem1_2], - input2_T data2[CONFIG_T::n_elem2_0 * CONFIG_T::n_elem2_1 * CONFIG_T::n_elem2_2], - res_T res[CONFIG_T::n_elem1_0 * CONFIG_T::n_elem1_1 * CONFIG_T::n_elem1_2 + - CONFIG_T::n_elem2_0 * CONFIG_T::n_elem2_1 * CONFIG_T::n_elem2_2]) { - for (int i = 0; i < CONFIG_T::n_elem1_0; i++) { - for (int j = 0; j < CONFIG_T::n_elem1_1; j++) { - - #pragma unroll - for (int k = 0; k < CONFIG_T::n_elem1_2; k++) { - int res_idx = i * CONFIG_T::n_elem1_1 * (CONFIG_T::n_elem1_2 + CONFIG_T::n_elem2_2) + - j * (CONFIG_T::n_elem1_2 + CONFIG_T::n_elem2_2) + k; - int data_idx = i * CONFIG_T::n_elem1_1 * CONFIG_T::n_elem1_2 + j * CONFIG_T::n_elem1_2 + k; - res[res_idx] = static_cast(data1[data_idx]); - } - - #pragma unroll - for (int k = 0; k < CONFIG_T::n_elem2_2; k++) { - int res_idx = i * CONFIG_T::n_elem1_1 * (CONFIG_T::n_elem1_2 + CONFIG_T::n_elem2_2) + - j * (CONFIG_T::n_elem1_2 + CONFIG_T::n_elem2_2) + k + CONFIG_T::n_elem1_2; - int data_idx = i * CONFIG_T::n_elem2_1 * CONFIG_T::n_elem2_2 + j * CONFIG_T::n_elem2_2 + k; - res[res_idx] = static_cast(data2[data_idx]); - } - } - } -} - -template -void concatenate3d(input1_T data1[CONFIG_T::n_elem1_0 * CONFIG_T::n_elem1_1 * CONFIG_T::n_elem1_2], - input2_T data2[CONFIG_T::n_elem2_0 * CONFIG_T::n_elem2_1 * CONFIG_T::n_elem2_2], - res_T res[CONFIG_T::n_elem1_0 * CONFIG_T::n_elem1_1 * CONFIG_T::n_elem1_2 + - CONFIG_T::n_elem2_0 * CONFIG_T::n_elem2_1 * CONFIG_T::n_elem2_2]) { - if (CONFIG_T::axis == 3 || CONFIG_T::axis == -1) { - concatenate3d_2(data1, data2, res); - } else if (CONFIG_T::axis == 2 || CONFIG_T::axis == -2) { - concatenate3d_1(data1, data2, res); - } else { - concatenate3d_0(data1, data2, res); - } -} - -} // namespace nnet - -#endif diff --git a/hls4ml/templates/quartus/firmware/nnet_utils/nnet_merge_stream.h b/hls4ml/templates/quartus/firmware/nnet_utils/nnet_merge_stream.h deleted file mode 100644 index 58fc77180b..0000000000 --- a/hls4ml/templates/quartus/firmware/nnet_utils/nnet_merge_stream.h +++ /dev/null @@ -1,356 +0,0 @@ -#ifndef NNET_MERGE_STREAM_H_ -#define NNET_MERGE_STREAM_H_ - -namespace nnet { - -template -void add(stream &data1, stream &data2, stream &res) { - assert(input1_T::size == input2_T::size && input1_T::size == res_T::size); - -AddLoop: - #pragma ii 1 - for (int i = 0; i < CONFIG_T::n_elem / input1_T::size; i++) { - hls_register input1_T in_data1 = data1.read(); - hls_register input2_T in_data2 = data2.read(); - - hls_register res_T out_data; - - AddPack: - #pragma unroll - for (int j = 0; j < res_T::size; j++) { - out_data[j] = static_cast(in_data1[j] + in_data2[j]); - } - - res.write(out_data); - } -} - -template -void subtract(stream &data1, stream &data2, stream &res) { - assert(input1_T::size == input2_T::size && input1_T::size == res_T::size); - -SubtractLoop: - #pragma ii 1 - for (int i = 0; i < CONFIG_T::n_elem / input1_T::size; i++) { - hls_register input1_T in_data1 = data1.read(); - hls_register input2_T in_data2 = data2.read(); - - hls_register res_T out_data; - - SubtractPack: - #pragma unroll - for (int j = 0; j < res_T::size; j++) { - out_data[j] = static_cast(in_data1[j] - in_data2[j]); - } - - res.write(out_data); - } -} - -template -void multiply(stream &data1, stream &data2, stream &res) { - assert(input1_T::size == input2_T::size && input1_T::size == res_T::size); - -MultLoop: - #pragma ii 1 - for (int i = 0; i < CONFIG_T::n_elem / input1_T::size; i++) { - hls_register input1_T in_data1 = data1.read(); - hls_register input2_T in_data2 = data2.read(); - - hls_register res_T out_data; - - MultPack: - #pragma unroll - for (int j = 0; j < res_T::size; j++) { - out_data[j] = static_cast(in_data1[j] * in_data2[j]); - } - - res.write(out_data); - } -} - -template -void average(stream &data1, stream &data2, stream &res) { - assert(input1_T::size == input2_T::size && input1_T::size == res_T::size); - -AvgLoop: - #pragma ii 1 - for (int i = 0; i < CONFIG_T::n_elem / input1_T::size; i++) { - hls_register input1_T in_data1 = data1.read(); - hls_register input2_T in_data2 = data2.read(); - - hls_register res_T out_data; - - AvgPack: - #pragma unroll - for (int j = 0; j < res_T::size; j++) { - out_data[j] = static_cast((in_data1[j] + in_data2[j]) * ac_fixed<1, 0, false>(0.5)); - } - - res.write(out_data); - } -} - -template -void maximum(stream &data1, stream &data2, stream &res) { - assert(input1_T::size == input2_T::size && input1_T::size == res_T::size); - -MaxLoop: - #pragma ii 1 - for (int i = 0; i < CONFIG_T::n_elem / input1_T::size; i++) { - hls_register input1_T in_data1 = data1.read(); - hls_register input2_T in_data2 = data2.read(); - - hls_register res_T out_data; - - MaxPack: - #pragma unroll - for (int j = 0; j < res_T::size; j++) { - out_data[j] = (in_data1[j] > in_data2[j]) ? static_cast(in_data1[j]) - : static_cast(in_data2[j]); - } - - res.write(out_data); - } -} - -template -void minimum(stream &data1, stream &data2, stream &res) { - assert(input1_T::size == input2_T::size && input1_T::size == res_T::size); - -MinLoop: - #pragma ii 1 - for (int i = 0; i < CONFIG_T::n_elem / input1_T::size; i++) { - hls_register input1_T in_data1 = data1.read(); - hls_register input2_T in_data2 = data2.read(); - - hls_register res_T out_data; - - MinPack: - #pragma unroll - for (int j = 0; j < res_T::size; j++) { - out_data[j] = (in_data1[j] < in_data2[j]) ? static_cast(in_data1[j]) - : static_cast(in_data2[j]); - } - - res.write(out_data); - } -} - -template -void concatenate1d(stream &data1, stream &data2, stream &res) { - hls_register res_T out_data; - -ConcatLoop1: - #pragma ii 1 - for (int i = 0; i < CONFIG_T::n_elem1_0 / input1_T::size; i++) { - hls_register input1_T in_data1 = data1.read(); - ConcatPack1: - #pragma unroll - for (int j = 0; j < input1_T::size; j++) { - out_data[j + (i * input1_T::size)] = static_cast(in_data1[j]); - } - } - -ConcatLoop2: - #pragma ii 1 - for (int i = 0; i < CONFIG_T::n_elem2_0 / input2_T::size; i++) { - hls_register input2_T in_data2 = data2.read(); - ConcatPack2: - #pragma unroll - for (int j = 0; j < input2_T::size; j++) { - out_data[j + (i * input2_T::size) + (CONFIG_T::n_elem1_0)] = - static_cast(in_data2[j]); - } - } - res.write(out_data); -} - -template -void concatenate2d_0(stream &data1, stream &data2, stream &res) { -ConcatLoopHeight1: - #pragma ii 1 - for (int i = 0; i < CONFIG_T::n_elem1_0; i++) { - - hls_register input1_T in_data1 = data1.read(); - hls_register res_T out_data; - - ConcatPackInput1: - #pragma unroll - for (int k = 0; k < input1_T::size; k++) { - out_data[k] = static_cast(in_data1[k]); - } - - res.write(out_data); - } - -ConcatLoopHeight2: - #pragma ii 1 - for (int i = 0; i < CONFIG_T::n_elem2_0; i++) { - hls_register input2_T in_data2 = data2.read(); - hls_register res_T out_data; - - ConcatPackInput2: - #pragma unroll - for (int k = 0; k < input2_T::size; k++) { - out_data[k] = static_cast(in_data2[k]); - } - - res.write(out_data); - } -} - -template -void concatenate2d_1(stream &data1, stream &data2, stream &res) { -ConcatLoopHeight: - #pragma ii 1 - for (int i = 0; i < CONFIG_T::n_elem1_0; i++) { - hls_register input1_T in_data1 = data1.read(); - hls_register input2_T in_data2 = data2.read(); - hls_register res_T out_data; - - ConcatPackInput1: - #pragma unroll - for (int k = 0; k < input1_T::size; k++) { - out_data[k] = static_cast(in_data1[k]); - } - - ConcatPackInput2: - #pragma unroll - for (int k = 0; k < input2_T::size; k++) { - out_data[input1_T::size + k] = static_cast(in_data2[k]); - } - - res.write(out_data); - } -} - -template -void concatenate2d(stream &data1, stream &data2, stream &res) { - if (CONFIG_T::axis == 2 || CONFIG_T::axis == -1) { - concatenate2d_1(data1, data2, res); - } else { - concatenate2d_0(data1, data2, res); - } -} - -template -void concatenate3d_0(stream &data1, stream &data2, stream &res) { -ConcatLoopHeight1: - for (int i = 0; i < CONFIG_T::n_elem1_0; i++) { - ConcatLoopWidth1: - #pragma ii 1 - for (int j = 0; j < CONFIG_T::n_elem1_1; j++) { - - hls_register input1_T in_data1 = data1.read(); - hls_register res_T out_data; - ConcatPackInput1: - #pragma unroll - for (int k = 0; k < input1_T::size; k++) { - out_data[k] = static_cast(in_data1[k]); - } - - res.write(out_data); - } - } - -ConcatLoopHeight2: - for (int i = 0; i < CONFIG_T::n_elem2_0; i++) { - ConcatLoopWidth2: - #pragma ii 1 - for (int j = 0; j < CONFIG_T::n_elem2_1; j++) { - - hls_register input2_T in_data2 = data2.read(); - hls_register res_T out_data; - - ConcatPackInput2: - #pragma unroll - for (int k = 0; k < input2_T::size; k++) { - out_data[k] = static_cast(in_data2[k]); - } - - res.write(out_data); - } - } -} - -template -void concatenate3d_1(stream &data1, stream &data2, stream &res) { -ConcatLoopHeight: - for (int i = 0; i < CONFIG_T::n_elem1_0; i++) { - ConcatLoopWidth1: - #pragma ii 1 - for (int j = 0; j < CONFIG_T::n_elem1_1; j++) { - - hls_register input1_T in_data1 = data1.read(); - hls_register res_T out_data; - - ConcatPackInput1: - #pragma unroll - for (int k = 0; k < input1_T::size; k++) { - out_data[k] = static_cast(in_data1[k]); - } - - res.write(out_data); - } - ConcatLoopWidth2: - #pragma ii 1 - for (int j = 0; j < CONFIG_T::n_elem2_1; j++) { - - hls_register input2_T in_data2 = data2.read(); - hls_register res_T out_data; - - ConcatPackInput2: - #pragma unroll - for (int k = 0; k < input2_T::size; k++) { - out_data[k] = static_cast(in_data2[k]); - } - - res.write(out_data); - } - } -} - -template -void concatenate3d_2(stream &data1, stream &data2, stream &res) { -ConcatLoopHeight: - for (int i = 0; i < CONFIG_T::n_elem1_0; i++) { - ConcatLoopWidth: - #pragma ii 1 - for (int j = 0; j < CONFIG_T::n_elem1_1; j++) { - - hls_register input1_T in_data1 = data1.read(); - hls_register input2_T in_data2 = data2.read(); - hls_register res_T out_data; - - ConcatPackInput1: - #pragma unroll - for (int k = 0; k < input1_T::size; k++) { - out_data[k] = static_cast(in_data1[k]); - } - - ConcatPackInput2: - #pragma unroll - for (int k = 0; k < input2_T::size; k++) { - out_data[input1_T::size + k] = static_cast(in_data2[k]); - } - - res.write(out_data); - } - } -} - -template -void concatenate3d(stream &data1, stream &data2, stream &res) { - if (CONFIG_T::axis == 3 || CONFIG_T::axis == -1) { - concatenate3d_2(data1, data2, res); - } else if (CONFIG_T::axis == 2 || CONFIG_T::axis == -2) { - concatenate3d_1(data1, data2, res); - } else { - concatenate3d_0(data1, data2, res); - } -} - -} // namespace nnet - -#endif diff --git a/hls4ml/templates/quartus/firmware/nnet_utils/nnet_mult.h b/hls4ml/templates/quartus/firmware/nnet_utils/nnet_mult.h deleted file mode 100644 index 5be7728323..0000000000 --- a/hls4ml/templates/quartus/firmware/nnet_utils/nnet_mult.h +++ /dev/null @@ -1,113 +0,0 @@ -#ifndef NNET_MULT_H_ -#define NNET_MULT_H_ - -#include "nnet_common.h" -#include "nnet_helpers.h" -#include - -namespace nnet { - -// Different methods to perform the product of input and weight, depending on their types. -namespace product { - -class Product { - public: - static void limit(unsigned multiplier_limit) {} -}; - -template class both_binary : public Product { - public: - inline static x_T product(x_T a, w_T w) { - // specialisation for 1-bit weights and incoming data - return a == w; - } -}; - -template class weight_binary : public Product { - public: - inline static auto product(x_T a, w_T w) -> decltype(-a) { - // Specialisation for 1-bit weights, arbitrary data - if (w == 0) - return -a; - else - return a; - } -}; - -template class data_binary : public Product { - public: - inline static auto product(x_T a, w_T w) -> decltype(-w) { - // Specialisation for 1-bit data, arbitrary weight - if (a == 0) - return -w; - else - return w; - } -}; - -template class weight_ternary : public Product { - public: - inline static auto product(x_T a, w_T w) -> decltype(-a) { - // Specialisation for 2-bit weights, arbitrary data - if (w == 0) - return 0; - else if (w == -1) - return -a; - else - return a; // if(w == 1) - } -}; - -template class mult : public Product { - public: - inline static auto product(x_T a, w_T w) -> decltype(a * w) { - // 'Normal' product - return a * w; - } - static void limit(unsigned multiplier_limit) { - // TODO: Implement for Quartus - // #pragma HLS ALLOCATION instances=mul limit=multiplier_limit operation > Vivado-only, replace with Intel HLS - // pragma - } -}; - -template class weight_exponential : public Product { - public: - using r_T = ac_fixed<2 * (decltype(w_T::weight)::width + x_T::width), (decltype(w_T::weight)::width + x_T::width), true>; - inline static r_T product(x_T a, w_T w) { - // Shift product for exponential weights - // Shift by the exponent. Negative weights shift right - r_T y = static_cast(a) << w.weight; - - // Negate or not depending on weight sign - return w.sign == 1 ? y : static_cast(-y); - } -}; -} // namespace product - -// TO-DO: These may need extra variants if ac_int types are used in more places -template -inline typename std::enable_if>::value && - std::is_same>::value, - ac_int>::type -cast(typename CONFIG_T::accum_t x) { - return static_cast>(((x - CONFIG_T::n_in / 2) * 2).to_ac_int()); -} - -template -inline typename std::enable_if>::value && - !std::is_same>::value, - res_T>::type -cast(typename CONFIG_T::accum_t x) { - return static_cast(x); -} - -template -inline typename std::enable_if<(!std::is_same>::value), res_T>::type -cast(typename CONFIG_T::accum_t x) { - return static_cast(x); -} - -} // namespace nnet - -#endif diff --git a/hls4ml/templates/quartus/firmware/nnet_utils/nnet_padding.h b/hls4ml/templates/quartus/firmware/nnet_utils/nnet_padding.h deleted file mode 100644 index a95f9ab003..0000000000 --- a/hls4ml/templates/quartus/firmware/nnet_utils/nnet_padding.h +++ /dev/null @@ -1,99 +0,0 @@ -#ifndef NNET_PADDING_H_ -#define NNET_PADDING_H_ - -namespace nnet { - -struct padding1d_config { - static const unsigned in_width = 10; - static const unsigned out_width = 10; - static const unsigned n_chan = 10; - - static const unsigned pad_left = 0; - static const unsigned pad_right = 0; -}; - -template -void zeropad1d_cl(data_T data[CONFIG_T::n_chan * CONFIG_T::in_width], res_T res[CONFIG_T::n_chan * CONFIG_T::out_width]) { - for (int i = 0; i < CONFIG_T::pad_left; i++) { - #pragma unroll - for (int j = 0; j < CONFIG_T::n_chan; j++) { - *(res++) = 0; - } - } - - for (int i = 0; i < CONFIG_T::in_width; i++) { - #pragma unroll - for (int j = 0; j < CONFIG_T::n_chan; j++) { - *(res++) = (res_T) * (data++); - } - } - - for (int i = 0; i < CONFIG_T::pad_right; i++) { - #pragma unroll - for (int j = 0; j < CONFIG_T::n_chan; j++) { - *(res++) = 0; - } - } -} - -struct padding2d_config { - static const unsigned in_height = 10; - static const unsigned in_width = 10; - - static const unsigned out_height = 10; - static const unsigned out_width = 10; - - static const unsigned n_chan = 10; - - static const unsigned pad_top = 0; - static const unsigned pad_bottom = 0; - static const unsigned pad_left = 0; - static const unsigned pad_right = 0; -}; - -template -void zeropad2d_cl(data_T data[CONFIG_T::n_chan * CONFIG_T::in_height * CONFIG_T::in_width], - res_T res[CONFIG_T::n_chan * CONFIG_T::out_height * CONFIG_T::out_width]) { - for (int i = 0; i < CONFIG_T::pad_top; i++) { - for (int j = 0; j < CONFIG_T::out_width; j++) { - #pragma unroll - for (int k = 0; k < CONFIG_T::n_chan; k++) { - *(res++) = 0; - } - } - } - - for (int i = 0; i < CONFIG_T::in_height; i++) { - for (int j = 0; j < CONFIG_T::pad_left; j++) { - #pragma unroll - for (int k = 0; k < CONFIG_T::n_chan; k++) { - *(res++) = 0; - } - } - for (int j = 0; j < CONFIG_T::in_width; j++) { - #pragma unroll - for (int k = 0; k < CONFIG_T::n_chan; k++) { - *(res++) = (res_T) * (data++); - } - } - for (int j = 0; j < CONFIG_T::pad_right; j++) { - #pragma unroll - for (int k = 0; k < CONFIG_T::n_chan; k++) { - *(res++) = 0; - } - } - } - - for (int i = 0; i < CONFIG_T::pad_bottom; i++) { - for (int j = 0; j < CONFIG_T::out_width; j++) { - #pragma unroll - for (int k = 0; k < CONFIG_T::n_chan; k++) { - *(res++) = 0; - } - } - } -} - -} // namespace nnet - -#endif diff --git a/hls4ml/templates/quartus/firmware/nnet_utils/nnet_padding_stream.h b/hls4ml/templates/quartus/firmware/nnet_utils/nnet_padding_stream.h deleted file mode 100644 index 6d40bd2043..0000000000 --- a/hls4ml/templates/quartus/firmware/nnet_utils/nnet_padding_stream.h +++ /dev/null @@ -1,83 +0,0 @@ -#ifndef NNET_PADDING_STREAM_H_ -#define NNET_PADDING_STREAM_H_ - -namespace nnet { - -template inline void fill_zero(stream &res) { - hls_register res_T res_part; - #pragma unroll - for (int i = 0; i < CONFIG_T::n_chan; i++) { - res_part[i] = 0; - } - res.write(res_part); -} - -template inline void fill_data(stream &data, stream &res) { - hls_register data_T data_part = data.read(); - hls_register res_T res_part; - #pragma unroll - for (int i = 0; i < CONFIG_T::n_chan; i++) { - res_part[i] = data_part[i]; - } - res.write(res_part); -} - -template void zeropad1d_cl(stream &data, stream &res) { -PadLeft: - for (int i = 0; i < CONFIG_T::pad_left; i++) { - fill_zero(res); - } - -CopyMain: - for (int i = 0; i < CONFIG_T::in_width; i++) { - fill_data(data, res); - } - -PadRight: - for (int i = 0; i < CONFIG_T::pad_right; i++) { - fill_zero(res); - } -} - -template void zeropad2d_cl(stream &data, stream &res) { -PadTop: - #pragma loop_coalesce 2 - for (int i = 0; i < CONFIG_T::pad_top; i++) { - PadTopWidth: - for (int j = 0; j < CONFIG_T::out_width; j++) { - fill_zero(res); - } - } - -PadMain: - #pragma loop_coalesce 2 - for (int i = 0; i < CONFIG_T::in_height; i++) { - - PadLeft: - for (int j = 0; j < CONFIG_T::pad_left; j++) { - fill_zero(res); - } - - CopyMain: - for (int j = 0; j < CONFIG_T::in_width; j++) { - fill_data(data, res); - } - - PadRight: - for (int j = 0; j < CONFIG_T::pad_right; j++) { - fill_zero(res); - } - } - -PadBottom: - for (int i = 0; i < CONFIG_T::pad_bottom; i++) { - PadBottomWidth: - for (int j = 0; j < CONFIG_T::out_width; j++) { - fill_zero(res); - } - } -} - -} // namespace nnet - -#endif diff --git a/hls4ml/templates/quartus/firmware/nnet_utils/nnet_pooling.h b/hls4ml/templates/quartus/firmware/nnet_utils/nnet_pooling.h deleted file mode 100644 index 087478bbd2..0000000000 --- a/hls4ml/templates/quartus/firmware/nnet_utils/nnet_pooling.h +++ /dev/null @@ -1,316 +0,0 @@ -#ifndef NNET_POOLING_H_ -#define NNET_POOLING_H_ - -#include "nnet_common.h" - -namespace nnet { - -// Returns the maximum value from an array of size N -template T max(T x[N]) { - hls_register T y = x[0]; - - // Due to loop dependencies, pipelining & unrolling is not possible - // Explicitly disabling pipeline significantly reduces resource usage - #pragma disable_loop_pipelining - for (int i = 1; i < N; i++) { - if (x[i] > y) - y = x[i]; - } - - return y; -} - -// Returns the mean value of an array of size N -template T avg(T (&x)[N]) { - hls_register T y = 0; - - // Due to loop dependencies, pipelining & unrolling is not possible - // Explicitly disabling pipeline significantly reduces resource usage - #pragma disable_loop_pipelining - for (int i = 0; i < N; i++) { - y += x[i]; - } - - y /= N; - return y; -} - -// Returns the mean value of an array of size N -// Overload of the above function; using a wider accumulator than the input to avoid overflow -template ac_int avg(ac_int (&x)[N]) { - hls_register ac_int tmp = 0; - - // Due to loop dependencies, pipelining & unrolling is not possible - // Explicitly disabling pipeline significantly reduces resource usage - #pragma disable_loop_pipelining - for (int i = 0; i < N; i++) { - tmp += x[i]; - } - - tmp /= N; - - // Cast back to original type - ac_int y = static_cast>(tmp); - return tmp; -} - -// Returns the mean value of an array of size N -// Overload of the above function; using a wider accumulator than the input to avoid overflow -template ac_fixed avg(ac_fixed (&x)[N]) { - hls_register ac_fixed tmp = 0; - - // Due to loop dependencies, pipelining & unrolling is not possible - // Explicitly disabling pipeline significantly reduces resource usage - #pragma disable_loop_pipelining - for (int i = 0; i < N; i++) { - tmp += x[i]; - } - - tmp /= N; - - // Cast back to original type - ac_fixed y = tmp; - return y; -} - -// Enumeration for pooling functions -enum Pool_Op { Max, Average }; -template T pool_op(T (&x)[N]) { - switch (op) { - case Max: - return max(x); - case Average: - return avg(x); - } -} - -/* - * In Tensorflow, pooling ignores the value in the padded cells - * For Avg pooling, return 0 (the divisor is modified to the area overlapping the unpadded image.) - * For ax pooling, return the most negative value for the type. - */ -template inline T pad_val() { - switch (op) { - case Max: { - T x = 0; - x[x.width - 1] = 1; - return x; - } - case Average: - return 0; - } -} - -struct pooling1d_config { - // Pooling parameters - static const unsigned pool_width = 2; - static const unsigned stride_width = 2; - - // I/O sizes - static const unsigned n_in = 10; - static const unsigned n_out = (n_in - pool_width) / stride_width + 1; - static const unsigned n_filt = 4; - - // Padding - static const unsigned pad_left = 0; - static const unsigned pad_right = 0; - static const bool count_pad = false; - - // Pooling function - static const Pool_Op pool_op = Max; -}; - -template -void pooling1d_cl(data_T data[CONFIG_T::n_in * CONFIG_T::n_filt], res_T res[CONFIG_T::n_out * CONFIG_T::n_filt]) { - // Add padding and reduce input width to area covered by pooling function - static constexpr int full_padded_width = CONFIG_T::n_in + CONFIG_T::pad_left + CONFIG_T::pad_right; - static constexpr int restricted_padded_width = - (full_padded_width - CONFIG_T::pool_width) / CONFIG_T::stride_width * CONFIG_T::stride_width + 1; -FiltLoop: - #pragma unroll - #pragma disable_loop_pipelining - for (int filt = 0; filt < CONFIG_T::n_filt; filt++) { - InputWidthLoop: - #pragma unroll - #pragma disable_loop_pipelining - for (int inp_col = 0; inp_col < restricted_padded_width; inp_col += CONFIG_T::stride_width) { - hls_register data_T pool[CONFIG_T::pool_width]; - - // Keep track of number of pixels in image vs padding region; needed for rescaling Average Pooling - hls_register unsigned img_overlap = 0; - - PoolWidthLoop: - #pragma unroll - #pragma disable_loop_pipelining - for (int pool_col = 0; pool_col < CONFIG_T::pool_width; pool_col++) { - if (inp_col + pool_col < CONFIG_T::pad_left || - inp_col + pool_col >= (full_padded_width - CONFIG_T::pad_right)) { - // Add padding - pool[pool_col] = pad_val(); - if (CONFIG_T::count_pad) - img_overlap++; - } else { - // Current element is from input image - pool[pool_col] = data[(inp_col + pool_col - CONFIG_T::pad_left) * CONFIG_T::n_filt + filt]; - img_overlap++; - } - } - - // Pooling operation - res[(inp_col / CONFIG_T::stride_width) * CONFIG_T::n_filt + filt] = - static_cast(pool_op(pool)); - - // If the pool op is Average, the zero-padding needs to be removed from the results - if (CONFIG_T::pool_op == Average) - res[(inp_col / CONFIG_T::stride_width) * CONFIG_T::n_filt + filt] *= - (static_cast(CONFIG_T::pool_width) / img_overlap); - } - } -} - -template -void global_pooling1d_cl(data_T data[CONFIG_T::n_in * CONFIG_T::n_filt], res_T res[CONFIG_T::n_filt]) { - assert(CONFIG_T::pad_left == 0 && CONFIG_T::pad_right == 0); - assert(CONFIG_T::pool_width == CONFIG_T::stride_width); - -FiltLoop: - #pragma unroll - #pragma disable_loop_pipelining - for (int filt = 0; filt < CONFIG_T::n_filt; filt++) { - hls_register data_T pool[CONFIG_T::n_in]; - - InputWidthLoop: - #pragma unroll - #pragma disable_loop_pipelining - for (int col = 0; col < CONFIG_T::n_in; col++) { - pool[col] = data[col * CONFIG_T::n_filt + filt]; - } - - res[filt] = static_cast(pool_op(pool)); - } -} - -struct pooling2d_config { - // Pooling parameters - static const unsigned stride_height = 2; - static const unsigned stride_width = 2; - static const unsigned pool_height = 2; - static const unsigned pool_width = 2; - - // I/O sizes - static const unsigned in_height = 10; - static const unsigned in_width = 10; - static const unsigned n_filt = 4; - - static const unsigned out_height = (in_height - pool_height) / stride_height + 1; - static const unsigned out_width = (in_width - pool_width) / stride_width + 1; - - // Padding - static const unsigned pad_top = 0; - static const unsigned pad_bottom = 0; - static const unsigned pad_left = 0; - static const unsigned pad_right = 0; - static const bool count_pad = false; - - // Pooling function - static const Pool_Op pool_op = Max; -}; - -template -void pooling2d_cl(data_T data[CONFIG_T::in_height * CONFIG_T::in_width * CONFIG_T::n_filt], - res_T res[CONFIG_T::out_height * CONFIG_T::out_width * CONFIG_T::n_filt]) { - // Add padding and reduce input width to area covered by pooling function - static constexpr int full_padded_width = CONFIG_T::in_width + CONFIG_T::pad_left + CONFIG_T::pad_right; - static constexpr int full_padded_height = CONFIG_T::in_height + CONFIG_T::pad_top + CONFIG_T::pad_bottom; - static constexpr int restricted_padded_width = - (full_padded_width - CONFIG_T::pool_width) / CONFIG_T::stride_width * CONFIG_T::stride_width + 1; - static constexpr int restricted_padded_height = - (full_padded_height - CONFIG_T::pool_height) / CONFIG_T::stride_height * CONFIG_T::stride_height + 1; -FiltLoop: - #pragma unroll - #pragma disable_loop_pipelining - for (int filt = 0; filt < CONFIG_T::n_filt; filt++) { - InputHeightLoop: - #pragma unroll - #pragma disable_loop_pipelining - for (int inp_col = 0; inp_col < restricted_padded_height; inp_col += CONFIG_T::stride_height) { - InputWidthLoop: - #pragma unroll - #pragma disable_loop_pipelining - for (int inp_width = 0; inp_width < restricted_padded_width; inp_width += CONFIG_T::stride_width) { - hls_register data_T pool[CONFIG_T::pool_height * CONFIG_T::pool_width]; - - // Keep track of number of pixels in image vs padding region; needed for rescaling Average Pooling - hls_register unsigned img_overlap = 0; - - PoolHeightLoop: - #pragma unroll - #pragma disable_loop_pipelining - for (int pool_col = 0; pool_col < CONFIG_T::pool_height; pool_col++) { - PoolWidthLoop: - #pragma unroll - #pragma disable_loop_pipelining - for (int pool_row = 0; pool_row < CONFIG_T::pool_width; pool_row++) { - if (inp_col + pool_col < CONFIG_T::pad_top || - inp_col + pool_col >= (full_padded_height - CONFIG_T::pad_bottom) || - inp_width + pool_row < CONFIG_T::pad_left || - inp_width + pool_row >= (full_padded_width - CONFIG_T::pad_right)) { - // Add padding - pool[pool_col * CONFIG_T::pool_width + pool_row] = pad_val(); - if (CONFIG_T::count_pad) - img_overlap++; - } else { - // Current element is from input image - pool[pool_col * CONFIG_T::pool_width + pool_row] = - data[(inp_col + pool_col - CONFIG_T::pad_top) * CONFIG_T::in_width * CONFIG_T::n_filt + - (inp_width + pool_row - CONFIG_T::pad_left) * CONFIG_T::n_filt + filt]; - img_overlap++; - } - } - } - - // Pooling operation - res[(inp_col / CONFIG_T::stride_height) * CONFIG_T::out_width * CONFIG_T::n_filt + - (inp_width / CONFIG_T::stride_width) * CONFIG_T::n_filt + filt] = - static_cast( - pool_op(pool)); - - // If the pool op is Average, the zero-padding needs to be removed from the results - if (CONFIG_T::pool_op == Average) - res[(inp_col / CONFIG_T::stride_height) * CONFIG_T::out_width * CONFIG_T::n_filt + - (inp_width / CONFIG_T::stride_width) * CONFIG_T::n_filt + filt] *= - (static_cast(CONFIG_T::pool_height) * static_cast(CONFIG_T::pool_width) / - img_overlap); - } - } - } -} - -template -void global_pooling2d_cl(data_T data[CONFIG_T::in_height * CONFIG_T::in_width * CONFIG_T::n_filt], - res_T res[CONFIG_T::n_filt]) { - assert(CONFIG_T::pad_left == 0 && CONFIG_T::pad_right == 0); - assert(CONFIG_T::pad_top == 0 && CONFIG_T::pad_bottom == 0); - assert(CONFIG_T::pool_width == CONFIG_T::stride_width); - assert(CONFIG_T::pool_height == CONFIG_T::stride_height); - -FiltLoop: - #pragma unroll - #pragma disable_loop_pipelining - for (int filt = 0; filt < CONFIG_T::n_filt; filt++) { - hls_register data_T pool[CONFIG_T::in_height * CONFIG_T::in_width]; - - InputLoop: - #pragma unroll - #pragma disable_loop_pipelining - for (int i = 0; i < CONFIG_T::in_height * CONFIG_T::in_width; i++) { - pool[i] = data[i * CONFIG_T::n_filt + filt]; - } - - res[filt] = static_cast(pool_op(pool)); - } -} - -} // namespace nnet - -#endif diff --git a/hls4ml/templates/quartus/firmware/nnet_utils/nnet_pooling_stream.h b/hls4ml/templates/quartus/firmware/nnet_utils/nnet_pooling_stream.h deleted file mode 100644 index 2da805c201..0000000000 --- a/hls4ml/templates/quartus/firmware/nnet_utils/nnet_pooling_stream.h +++ /dev/null @@ -1,316 +0,0 @@ -#ifndef NNET_POOLING_STREAM_H_ -#define NNET_POOLING_STREAM_H_ - -#include "nnet_conv1d_stream.h" -#include "nnet_conv2d_stream.h" -#include "nnet_pooling.h" -#include "nnet_types.h" - -namespace nnet { - -/* - * void compute_pool_buffer_1d(in_element, res_stream, line_buffer, kernel_window) - * - * Args: - * in_element - current elements from input image, data_T type is usually nnet::array, size of array corresponds to number - * of channels res_stream - output stream, passed by reference to allow direct writing line_buffer - chained array of shift - * registers, one for each row of the pool and channel kernel_window - array of values from the input currently being pooled - * - * Function executes 4 steps: - * (1) Shift line buffer - updates the contents of the chained shift registers, inserting the new inputs and removing last - * elements (2) Kernel shift - updates the elements of the kernel window, by storing the new inputs and popped elements from - * the line buffer (3) Pooling - performs dense matrix multiplication between the current input window and kernel weights (4) - * Counter housekeeping - performs the required pooling operation - * - */ -template -void compute_pool_buffer_1d(const data_T &in_elem, stream &res_stream, - nnet::shift_reg line_buffer[CONFIG_T::n_filt], - typename data_T::value_type kernel_window[CONFIG_T::pool_width * CONFIG_T::n_filt]) { - // Thresholds - static constexpr int lShiftX = CONFIG_T::pool_width - 1; - - // X position pixels - static int pX = 0; - - // X strides - static int sX = 0; - - // Step 1 - Shift line buffer - hls_register typename data_T::value_type shift_buffer[CONFIG_T::n_filt]; - nnet::shift_line_buffer_1d(in_elem, line_buffer, shift_buffer); - - // Step 2 - Kernel shift - nnet::kernel_shift_1d(shift_buffer, kernel_window); - - // Check to see if we have a full pool window - if ((sX - lShiftX) == 0 && pX > (lShiftX - 1)) { - hls_register res_T res_pack; - - FiltLoop: - #pragma unroll - for (int filter = 0; filter < CONFIG_T::n_filt; filter++) { - hls_register typename data_T::value_type pool_window[CONFIG_T::pool_width]; - - // Retrieve data for current channel - PoolLoop: - #pragma unroll - for (int i = 0; i < CONFIG_T::pool_width; i++) { - pool_window[i] = kernel_window[i * CONFIG_T::n_filt + filter]; - } - - // Step 3 - Pooling - res_pack[filter] = static_cast( - pool_op(pool_window)); - } - - // Write result to output stream - res_stream.write(res_pack); - } - - // Reached end of image - if ((pX + 1) == (CONFIG_T::in_width + CONFIG_T::pad_left + CONFIG_T::pad_right)) { - pX = 0; - sX = 0; - // Move to the right - } else { - pX++; - sX = ((sX - lShiftX) == 0) ? (sX - CONFIG_T::stride_width + 1) : (sX + 1); - } -} - -template void pooling1d_cl(stream &data, stream &res) { - assert(CONFIG_T::pool_width == CONFIG_T::stride_width); - assert(CONFIG_T::pad_left == 0 && CONFIG_T::pad_right == 0); - - // Line buffer and kernel window - hls_register static nnet::shift_reg line_buffer[CONFIG_T::n_filt]; - hls_register static typename data_T::value_type kernel_window[CONFIG_T::pool_width * CONFIG_T::n_filt]; - -// Read input image -ReadInputWidth: - for (int col = 0; col < CONFIG_T::in_width; col++) { - compute_pool_buffer_1d(data.read(), res, line_buffer, kernel_window); - } -} - -/* - * void compute_pool_buffer_2d(in_element, res_stream, line_buffer, kernel_window) - * - * Args: - * in_element - current elements from input image, data_T type is usually nnet::array, size of array corresponds to number - * of channels res_stream - output stream, passed by reference to allow direct writing line_buffer - chained array of shift - * registers, one for each row of the pool and channel kernel_window - array of values from the input currently being pooled - * - * Function executes 4 steps: - * (1) Shift line buffer - updates the contents of the chained shift registers, inserting the new inputs and removing last - * elements (2) Kernel shift - updates the elements of the kernel window, by storing the new inputs and popped elements from - * the line buffer (3) Pooling - performs dense matrix multiplication between the current input window and kernel weights (4) - * Counter housekeeping - performs the required pooling operation - * - */ -template -void compute_pool_buffer_2d( - const data_T &in_elem, stream &res_stream, - nnet::shift_reg line_buffer[CONFIG_T::pool_height - 1] - [CONFIG_T::n_filt], - typename data_T::value_type kernel_window[CONFIG_T::pool_height * CONFIG_T::pool_width * CONFIG_T::n_filt]) { - // Thresholds - static constexpr int lShiftX = CONFIG_T::pool_width - 1; - static constexpr int lShiftY = CONFIG_T::pool_height - 1; - - // X, Y position pixels - static int pX = 0; - static int pY = 0; - - // X, Y strides - static int sX = 0; - static int sY = 0; - - // Step 1 - Shift line buffer - hls_register typename data_T::value_type shift_buffer[CONFIG_T::pool_height][CONFIG_T::n_filt]; - nnet::shift_line_buffer_2d(in_elem, line_buffer, shift_buffer); - - // Step 2 - Kernel shift - nnet::kernel_shift_2d(shift_buffer, kernel_window); - - // Check to see if we have a full pool window - if ((sX - lShiftX) == 0 && (sY - lShiftY) == 0 && pY > (lShiftY - 1) && pX > (lShiftX - 1)) { - hls_register res_T res_pack; - - FiltLoop: - #pragma unroll - for (int filter = 0; filter < CONFIG_T::n_filt; filter++) { - hls_register typename data_T::value_type pool_window[CONFIG_T::pool_height * CONFIG_T::pool_width]; - - // Retrieve data for current channel - PoolLoop: - #pragma unroll - for (int i = 0; i < CONFIG_T::pool_height * CONFIG_T::pool_width; i++) { - pool_window[i] = kernel_window[i * CONFIG_T::n_filt + filter]; - } - - // Step 3 - Pooling - res_pack[filter] = static_cast( - pool_op( - pool_window)); - } - - // Write result to output stream - res_stream.write(res_pack); - } - - // Reached end of image - if ((pX + 1) == (CONFIG_T::in_width + CONFIG_T::pad_left + CONFIG_T::pad_right) && - (pY + 1) == (CONFIG_T::in_height + CONFIG_T::pad_top + CONFIG_T::pad_bottom)) { - pX = 0; - sX = 0; - pY = 0; - sY = 0; - // Reached end of row - } else if ((pX + 1) == (CONFIG_T::in_width + CONFIG_T::pad_left + CONFIG_T::pad_right)) { - pX = 0; - sX = 0; - pY++; - sY = ((sY - lShiftY) == 0) ? (sY - CONFIG_T::stride_height + 1) : (sY + 1); - // Same row, same column, therefore, move to the right - } else { - pX++; - sX = ((sX - lShiftX) == 0) ? (sX - CONFIG_T::stride_width + 1) : (sX + 1); - } -} - -template void pooling2d_cl(stream &data, stream &res) { - assert(CONFIG_T::pool_height == CONFIG_T::stride_height && CONFIG_T::pool_width == CONFIG_T::stride_width); - assert(CONFIG_T::pad_left == 0 && CONFIG_T::pad_right == 0); - assert(CONFIG_T::pad_top == 0 && CONFIG_T::pad_bottom == 0); - - // Line buffer and kernel window - hls_register static nnet::shift_reg - line_buffer[MAX(CONFIG_T::pool_height - 1, 1)][CONFIG_T::n_filt]; - hls_register static - typename data_T::value_type kernel_window[CONFIG_T::pool_height * CONFIG_T::pool_width * CONFIG_T::n_filt]; - -ReadInputHeight: - #pragma loop_coalesce 2 - for (int row = 0; row < CONFIG_T::in_height; row++) { - // Read input image - ReadInputWidth: - for (int col = 0; col < CONFIG_T::in_width; col++) { - compute_pool_buffer_2d(data.read(), res, line_buffer, kernel_window); - } - } -} - -/* - * A function used with Global Pooling - * Returns the value before pooling - * Max : Return the minimal possible value - * Avg : Return 0 - */ -template inline T init_pool_value() { - switch (op) { - case Max: { - T x = 0; - x[x.width - 1] = 1; - return x; - } - case Average: - return 0; - } -} - -/* - * A function used with Global Pooling - * Updates the output pooling value - * Max : Return the maximum between the previous maximum and current input - * Avg : Returns the cumulative sum - */ -template inline T_y reduce_global_pool(T_y y, T_x x) { - if (op == Max) { - return (x > y) ? (T_y)x : y; - } else { - return (T_y)(x + y); - } -} - -/* - * A function used with Global Pooling - * For every filter, it updates the value by summing the current input (Average) or updating the maximum value (Max) - */ -template -void compute_global_pool(const data_T &in_elem, typename CONFIG_T::accum_t data_input[CONFIG_T::n_filt]) { - #pragma unroll - for (unsigned i = 0; i < CONFIG_T::n_filt; i++) { - data_input[i] = reduce_global_pool( - data_input[i], in_elem[i]); - } -} - -template void global_pooling1d_cl(stream &data, stream &res) { - assert(CONFIG_T::pad_left == 0 && CONFIG_T::pad_right == 0); - - hls_register typename CONFIG_T::accum_t data_input[CONFIG_T::n_filt]; - - #pragma unroll - for (int i = 0; i < CONFIG_T::n_filt; i++) { - data_input[i] = init_pool_value(); - } - - for (int i = 0; i < CONFIG_T::n_in; i++) { - compute_global_pool(data.read(), data_input); - } - - hls_register res_T res_pack; - if (CONFIG_T::pool_op == Average) { - #pragma unroll - for (int i = 0; i < CONFIG_T::n_filt; i++) { - res_pack[i] = static_cast(data_input[i] / CONFIG_T::n_in); - } - } else { - #pragma unroll - for (int i = 0; i < CONFIG_T::n_filt; i++) { - res_pack[i] = static_cast(data_input[i]); - } - } - - res.write(res_pack); -} - -template void global_pooling2d_cl(stream &data, stream &res) { - assert(CONFIG_T::pad_left == 0 && CONFIG_T::pad_right == 0); - assert(CONFIG_T::pad_top == 0 && CONFIG_T::pad_bottom == 0); - - hls_register typename CONFIG_T::accum_t data_input[CONFIG_T::n_filt]; - - #pragma unroll - for (int i = 0; i < CONFIG_T::n_filt; i++) { - data_input[i] = init_pool_value(); - } - - for (int i = 0; i < CONFIG_T::in_height; i++) { - for (int j = 0; j < CONFIG_T::in_width; j++) { - compute_global_pool(data.read(), data_input); - } - } - - hls_register res_T res_pack; - if (CONFIG_T::pool_op == Average) { - #pragma unroll - for (int i = 0; i < CONFIG_T::n_filt; i++) { - res_pack[i] = - static_cast(data_input[i] / (CONFIG_T::in_width * CONFIG_T::in_height)); - } - } else { - #pragma unroll - for (int i = 0; i < CONFIG_T::n_filt; i++) { - res_pack[i] = static_cast(data_input[i]); - } - } - - res.write(res_pack); -} - -} // namespace nnet - -#endif diff --git a/hls4ml/templates/quartus/firmware/nnet_utils/nnet_recurrent.h b/hls4ml/templates/quartus/firmware/nnet_utils/nnet_recurrent.h deleted file mode 100644 index 5fbabd4e57..0000000000 --- a/hls4ml/templates/quartus/firmware/nnet_utils/nnet_recurrent.h +++ /dev/null @@ -1,868 +0,0 @@ -#ifndef NNET_RECURRENT_H_ -#define NNET_RECURRENT_H_ - -#include "nnet_common.h" -#include "nnet_dense.h" -#include "nnet_recurrent_activation.h" - -namespace nnet { - -//---------------------- -// Utils -//---------------------- - -template -void multiply_W(data_T input[N_IN], res_T out[N_OUT], const weight_t weight[N_IN * N_OUT]) { -MULTIPLY_W_LOOP_I: - #pragma unroll - for (int i = 0; i < N_OUT; i++) { - out[i] = 0; - - MULTIPLY_W_LOOP_J: - #pragma unroll - for (int j = 0; j < N_IN; j++) { - out[i] += input[j] * weight[i * N_IN + j]; - } - } -} - -template -void multiply_U(data_T input[N_OUT], res_T out[N_OUT], const weight_t weight[N_OUT * N_OUT]) { -MULTIPLY_U_LOOP_I: - #pragma unroll - for (int i = 0; i < N_OUT; i++) { - out[i] = 0; - - MULTIPLY_U_LOOP_J: - #pragma unroll - for (int j = 0; j < N_OUT; j++) { - out[i] += input[j] * weight[i * N_OUT + j]; - } - } -} - -template -void add_bias(data_T inputs[N], res_T out[N], const bias_t bias[N]) { -ADD_BIAS_LOOP: - #pragma unroll - for (int i = 0; i < N; i++) { - out[i] = inputs[i] + bias[i]; - } -} - -template void multiply_vectors(data_T in1[N], data_T in2[N], res_T out[N]) { -MULTIPLY_VECT_LOOP: - #pragma unroll - for (int i = 0; i < N; i++) { - out[i] = in1[i] * in2[i]; - } -} - -template void add_vectors(data_T in1[N], data_T in2[N], res_T out[N]) { -ADD_VECTOR_LOOP: - #pragma unroll - for (int i = 0; i < N; i++) { - out[i] = in1[i] + in2[i]; - } -} - -//---------------------- -// GRU -//---------------------- - -struct gru_config { - // Internal data type definitions - typedef float weight_t; - typedef float bias_t; - typedef float accum_t; - - // Layer Sizes - static const unsigned n_in = 1; - static const unsigned n_out = 1; - static const unsigned n_units = 1; - static const unsigned n_timesteps = 1; - static const unsigned n_outputs = 1; - static const bool return_sequences = false; - - // Resource reuse info - static const unsigned io_type = io_parallel; - static const unsigned reuse_factor = 1; - static const bool pytorch_order = false; - static const bool store_weights_in_bram = false; - - // Activation - template using activation_recr = nnet::activation::relu; - - template using activation = nnet::activation::relu; -}; - -template -void gru_cell(data_T x[CONFIG_T::n_in], res_T h[CONFIG_T::n_units], - const typename CONFIG_T::weight_t weights[3 * CONFIG_T::n_units * CONFIG_T::n_in], - const typename CONFIG_T::weight_t recurrent_weights[3 * CONFIG_T::n_units * CONFIG_T::n_units], - const typename CONFIG_T::bias_t bias[3 * CONFIG_T::n_units], - const typename CONFIG_T::bias_t recurrent_bias[3 * CONFIG_T::n_units]) { - static constexpr int recurrent_unroll_factor = CONFIG_T::n_units / CONFIG_T::reuse_factor; - // A matrix containing the values of matrix product between input (x) and weights (weights), for update, reset and - // candidate state gates, for each of the units - hls_register typename CONFIG_T::accum_t mat_mul_x_w[3 * CONFIG_T::n_units]; - nnet::dense_resource(x, mat_mul_x_w, weights, - bias); - - // A matrix containing the values of matrix product between previous state (h) and recurrent weights (recurrent_weights), - // for update, reset and candidate state gates, for each of the units - hls_register typename CONFIG_T::accum_t mat_mul_h_wr[3 * CONFIG_T::n_units]; - nnet::dense_resource( - h, mat_mul_h_wr, recurrent_weights, recurrent_bias); - - // A vector containing both the values of z(t) and r(t) for every state - hls_register typename CONFIG_T::accum_t z_r[2 * CONFIG_T::n_units]; - - // Add the individual vectors from the multiplication of mat_mul_x_w = Wx*x(t) and mat_mul_h_wr = Wh*h(t-1) - // Unrolled fully, no DSPs used - #pragma unroll - for (int i = 0; i < (2 * CONFIG_T::n_units); i++) { - z_r[i] = mat_mul_x_w[i] + mat_mul_h_wr[i]; - } - - // Activation on z(t) and r(t) - hls_register typename CONFIG_T::accum_t z_r_act[2 * CONFIG_T::n_units]; - CONFIG_T::template activation_recr::activation(z_r, z_r_act); - - // A matrix containing the values of Hadamard product between r(t) = z_r_act[n_units:2*n_units] and h(t-1) = h - hls_register typename CONFIG_T::accum_t hadamard_r_h[CONFIG_T::n_units]; - #pragma unroll recurrent_unroll_factor - for (int i = 0; i < (CONFIG_T::n_units); i++) { - if (CONFIG_T::pytorch_order) - hadamard_r_h[i] = z_r_act[i] * mat_mul_h_wr[i + 2 * CONFIG_T::n_units]; - else - hadamard_r_h[i] = z_r_act[i + CONFIG_T::n_units] * mat_mul_h_wr[i + 2 * CONFIG_T::n_units]; - } - - // The candidate state; X * W_{hx} + hadmard(r(t), h_(t-1)) * W_{hh} + b_{h} - typename CONFIG_T::accum_t h_cand[CONFIG_T::n_units]; - // Addition - can unroll fully; no DSPs used here - #pragma unroll - for (int i = 0; i < (CONFIG_T::n_units); i++) { - h_cand[i] = mat_mul_x_w[i + 2 * CONFIG_T::n_units] + hadamard_r_h[i]; - } - - // Activation on candidate state - hls_register typename CONFIG_T::accum_t h_cand_act[CONFIG_T::n_units]; - CONFIG_T::template activation::activation(h_cand, h_cand_act); - - // Update state - #pragma unroll recurrent_unroll_factor - for (int i = 0; i < (CONFIG_T::n_units); i++) { - if (CONFIG_T::pytorch_order) - h[i] = static_cast(h_cand_act[i] * (1 - z_r_act[i + CONFIG_T::n_units]) + - h[i] * z_r_act[i + CONFIG_T::n_units]); - else - h[i] = static_cast(h_cand_act[i] * (1 - z_r_act[i]) + h[i] * z_r_act[i]); - } -} - -template -void gru(data_T data[CONFIG_T::n_in], res_T res[CONFIG_T::n_outputs * CONFIG_T::n_units], - const typename CONFIG_T::weight_t weights[3 * CONFIG_T::n_units * CONFIG_T::n_in], - const typename CONFIG_T::weight_t recurrent_weights[3 * CONFIG_T::n_units * CONFIG_T::n_units], - const typename CONFIG_T::bias_t bias[3 * CONFIG_T::n_units], - const typename CONFIG_T::bias_t recurrent_bias[3 * CONFIG_T::n_units]) { - - hls_register data_T x[CONFIG_T::n_in]; - hls_register res_T h[CONFIG_T::n_units]; - - #pragma unroll - for (int i = 0; i < CONFIG_T::n_units; i++) { - h[i] = 0; - } - - // Loop dependency - cannot pipeline - #pragma disable_loop_pipelining - for (int t = 0; t < CONFIG_T::n_timesteps; t++) { - // Get data at current time step - #pragma unroll - for (int j = 0; j < CONFIG_T::n_in; j++) { - x[j] = data[j + t * CONFIG_T::n_in]; - } - - nnet::gru_cell(x, h, weights, recurrent_weights, bias, recurrent_bias); - - if (CONFIG_T::return_sequences) { - #pragma unroll - for (int i = 0; i < CONFIG_T::n_units; i++) { - res[CONFIG_T::n_units * t + i] = h[i]; - } - } - } - - if (!CONFIG_T::return_sequences) { - #pragma unroll - for (int i = 0; i < (CONFIG_T::n_units); i++) { - res[i] = h[i]; - } - } -} - -template -void gru(data_T data[CONFIG_T::n_in], h_T h[CONFIG_T::n_units], res_T res[CONFIG_T::n_outputs * CONFIG_T::n_units], - const typename CONFIG_T::weight_t weights[3 * CONFIG_T::n_units * CONFIG_T::n_in], - const typename CONFIG_T::weight_t recurrent_weights[3 * CONFIG_T::n_units * CONFIG_T::n_units], - const typename CONFIG_T::bias_t bias[3 * CONFIG_T::n_units], - const typename CONFIG_T::bias_t recurrent_bias[3 * CONFIG_T::n_units]) { - - hls_register data_T x[CONFIG_T::n_in]; - // hls_register res_T h[CONFIG_T::n_units]; - - // #pragma unroll - // for (int i = 0; i < CONFIG_T::n_units; i++) { - // h[i] = 0; - // } - - // Loop dependency - cannot pipeline - #pragma disable_loop_pipelining - for (int t = 0; t < CONFIG_T::n_timesteps; t++) { - // Get data at current time step - #pragma unroll - for (int j = 0; j < CONFIG_T::n_in; j++) { - x[j] = data[j + t * CONFIG_T::n_in]; - } - - nnet::gru_cell(x, h, weights, recurrent_weights, bias, recurrent_bias); - - if (CONFIG_T::return_sequences) { - #pragma unroll - for (int i = 0; i < CONFIG_T::n_units; i++) { - res[CONFIG_T::n_units * t + i] = h[i]; - } - } - } - - if (!CONFIG_T::return_sequences) { - #pragma unroll - for (int i = 0; i < (CONFIG_T::n_units); i++) { - res[i] = h[i]; - } - } -} -//---------------------- -// SimpleRNN -//---------------------- - -struct simple_rnn_config { - // Internal data type definitions - typedef float weight_t; - typedef float bias_t; - typedef float accum_t; - - // Layer Sizes - static const unsigned n_in = 1; - static const unsigned n_out = 1; - static const unsigned n_outputs = 1; - static const unsigned n_timesteps = 1; - static const bool return_sequences = false; - - // Resource reuse info - static const unsigned io_type = io_parallel; - static const unsigned reuse_factor = 1; - static const bool store_weights_in_bram = false; - - // Activation - template using activation_recr = nnet::activation::relu; - - template using activation = nnet::activation::relu; -}; - -template -void simple_rnn_cell(data_T inputs[CONFIG_T::n_in], res_T hidden_state[CONFIG_T::n_out], - res_T hidden_state_o[CONFIG_T::n_out], - const typename CONFIG_T::weight_t kernel[CONFIG_T::n_in * CONFIG_T::n_out], - const typename CONFIG_T::weight_t rec_kernel[CONFIG_T::n_out * CONFIG_T::n_out], - const typename CONFIG_T::bias_t bias[CONFIG_T::n_out]) { - // Weight multiplication - typename CONFIG_T::accum_t afterW[CONFIG_T::n_out] hls_register; - multiply_W( - inputs, afterW, kernel); - - // Bias addition - typename CONFIG_T::accum_t afterBias[CONFIG_T::n_out] hls_register; - add_bias( - afterW, afterBias, bias); - - // Hidden state - typename CONFIG_T::accum_t hiddenCand[CONFIG_T::n_out] hls_register; - multiply_U(hidden_state, hiddenCand, - rec_kernel); - - // Vector addition - typename CONFIG_T::accum_t afterAdd[CONFIG_T::n_out]; - add_vectors(afterBias, hiddenCand, afterAdd); - - // Activation - CONFIG_T::template activation::activation( - afterAdd, hidden_state_o); -} - -template -void simple_rnn(data_T data[CONFIG_T::n_timesteps * CONFIG_T::n_in], res_T res[CONFIG_T::n_outputs * CONFIG_T::n_out], - const typename CONFIG_T::weight_t kernel[CONFIG_T::n_in * CONFIG_T::n_out], - const typename CONFIG_T::weight_t rec_kernel[CONFIG_T::n_out * CONFIG_T::n_out], - const typename CONFIG_T::bias_t bias[CONFIG_T::n_out]) { - res_T hidden_state[CONFIG_T::n_out][CONFIG_T::n_timesteps + 1] hls_register; - res_T hidden_state_temp[CONFIG_T::n_out] hls_register; - res_T h[CONFIG_T::n_out] hls_register; - data_T in[CONFIG_T::n_in] hls_register; - -// Set initially hidden state (output) to zero -INIT_LOOP: - #pragma unroll - for (int x = 0; x < CONFIG_T::n_out; x++) { - hidden_state[x][0] = 0; - } - - #pragma disable_loop_pipelining - for (int i = 0; i < CONFIG_T::n_timesteps; i++) { - - // Data at current time step - #pragma unroll - for (int x = 0; x < CONFIG_T::n_in; x++) { - in[x] = data[x + i * CONFIG_T::n_in]; - } - - // Hidden state at current time step - #pragma unroll - for (int x = 0; x < CONFIG_T::n_out; x++) { - hidden_state_temp[x] = hidden_state[x][i]; - } - - // Do SimpleRNN - simple_rnn_cell(in, hidden_state_temp, h, kernel, rec_kernel, bias); - - // Write result - #pragma unroll - for (int x = 0; x < CONFIG_T::n_out; x++) { - hidden_state[x][i + 1] = h[x]; - } - } - - if (CONFIG_T::return_sequences == 0) { - // Output when return_sequences is false - #pragma unroll - for (int x = 0; x < CONFIG_T::n_out; x++) { - res[x] = hidden_state[x][CONFIG_T::n_timesteps]; - } - } else { - // Output when return_sequences is true - #pragma unroll - for (int x = 0; x < CONFIG_T::n_timesteps; x++) { - #pragma unroll - for (int h = 0; h < CONFIG_T::n_out; h++) { - res[x * CONFIG_T::n_out + h] = hidden_state[h][x + 1]; - } - } - } -} -//---------------------- -// SimpleRNN with pytorch biases -//---------------------- - -template -void simple_rnn_pytorch_cell(data_T inputs[CONFIG_T::n_in], res_T hidden_state[CONFIG_T::n_out], - res_T hidden_state_o[CONFIG_T::n_out], - const typename CONFIG_T::weight_t kernel[CONFIG_T::n_in * CONFIG_T::n_out], - const typename CONFIG_T::weight_t rec_kernel[CONFIG_T::n_out * CONFIG_T::n_out], - const typename CONFIG_T::bias_t bias[CONFIG_T::n_out], - const typename CONFIG_T::bias_t rec_bias[CONFIG_T::n_out]) { - // Weight multiplication - typename CONFIG_T::accum_t afterW[CONFIG_T::n_out] hls_register; - multiply_W( - inputs, afterW, kernel); - - // Bias addition - typename CONFIG_T::accum_t afterBias[CONFIG_T::n_out] hls_register; - add_bias( - afterW, afterBias, bias); - - // Hidden state - typename CONFIG_T::accum_t hiddenCand[CONFIG_T::n_out] hls_register; - multiply_U(hidden_state, hiddenCand, - rec_kernel); - - // Hidden state bias addition - typename CONFIG_T::accum_t hiddenBias[CONFIG_T::n_out] hls_register; - add_bias( - hiddenCand, hiddenBias, rec_bias); - - // Vector addition - typename CONFIG_T::accum_t afterAdd[CONFIG_T::n_out]; - add_vectors(afterBias, hiddenBias, afterAdd); - - // Activation - CONFIG_T::template activation::activation( - afterAdd, hidden_state_o); -} - -template -void simple_rnn_pytorch(data_T data[CONFIG_T::n_timesteps * CONFIG_T::n_in], - res_T res[CONFIG_T::n_outputs * CONFIG_T::n_out], - const typename CONFIG_T::weight_t kernel[CONFIG_T::n_in * CONFIG_T::n_out], - const typename CONFIG_T::weight_t rec_kernel[CONFIG_T::n_out * CONFIG_T::n_out], - const typename CONFIG_T::bias_t bias[CONFIG_T::n_out], - const typename CONFIG_T::bias_t rec_bias[CONFIG_T::n_out]) { - res_T hidden_state[CONFIG_T::n_out][CONFIG_T::n_timesteps + 1] hls_register; - res_T hidden_state_temp[CONFIG_T::n_out] hls_register; - res_T h[CONFIG_T::n_out] hls_register; - data_T in[CONFIG_T::n_in] hls_register; - -// Set initially hidden state (output) to zero -INIT_LOOP: - #pragma unroll - for (int x = 0; x < CONFIG_T::n_out; x++) { - hidden_state[x][0] = 0; - } - - #pragma disable_loop_pipelining - for (int i = 0; i < CONFIG_T::n_timesteps; i++) { - - // Data at current time step - #pragma unroll - for (int x = 0; x < CONFIG_T::n_in; x++) { - in[x] = data[x + i * CONFIG_T::n_in]; - } - - // Hidden state at current time step - #pragma unroll - for (int x = 0; x < CONFIG_T::n_out; x++) { - hidden_state_temp[x] = hidden_state[x][i]; - } - - // Do SimpleRNN - simple_rnn_pytorch_cell(in, hidden_state_temp, h, kernel, rec_kernel, bias, rec_bias); - - // Write result - #pragma unroll - for (int x = 0; x < CONFIG_T::n_out; x++) { - hidden_state[x][i + 1] = h[x]; - } - } - - if (CONFIG_T::return_sequences == 0) { - // Output when return_sequences is false - #pragma unroll - for (int x = 0; x < CONFIG_T::n_out; x++) { - res[x] = hidden_state[x][CONFIG_T::n_timesteps]; - } - } else { - // Output when return_sequences is true - #pragma unroll - for (int x = 0; x < CONFIG_T::n_timesteps; x++) { - #pragma unroll - for (int h = 0; h < CONFIG_T::n_out; h++) { - res[x * CONFIG_T::n_out + h] = hidden_state[h][x + 1]; - } - } - } -} - -template -void simple_rnn_pytorch(data_T data[CONFIG_T::n_timesteps * CONFIG_T::n_in], h_T hin[CONFIG_T::n_out], - res_T res[CONFIG_T::n_outputs * CONFIG_T::n_out], - const typename CONFIG_T::weight_t kernel[CONFIG_T::n_in * CONFIG_T::n_out], - const typename CONFIG_T::weight_t rec_kernel[CONFIG_T::n_out * CONFIG_T::n_out], - const typename CONFIG_T::bias_t bias[CONFIG_T::n_out], - const typename CONFIG_T::bias_t rec_bias[CONFIG_T::n_out]) { - h_T hidden_state[CONFIG_T::n_out][CONFIG_T::n_timesteps + 1] hls_register; - h_T hidden_state_temp[CONFIG_T::n_out] hls_register; - h_T h[CONFIG_T::n_out] hls_register; - data_T in[CONFIG_T::n_in] hls_register; - -// Set initially hidden state (output) to zero -INIT_LOOP: - #pragma unroll - for (int x = 0; x < CONFIG_T::n_out; x++) { - hidden_state[x][0] = hin[x]; - } - - #pragma disable_loop_pipelining - for (int i = 0; i < CONFIG_T::n_timesteps; i++) { - - // Data at current time step - #pragma unroll - for (int x = 0; x < CONFIG_T::n_in; x++) { - in[x] = data[x + i * CONFIG_T::n_in]; - } - - // Hidden state at current time step - #pragma unroll - for (int x = 0; x < CONFIG_T::n_out; x++) { - hidden_state_temp[x] = hidden_state[x][i]; - } - - // Do SimpleRNN - simple_rnn_pytorch_cell(in, hidden_state_temp, h, kernel, rec_kernel, bias, rec_bias); - - // Write result - #pragma unroll - for (int x = 0; x < CONFIG_T::n_out; x++) { - hidden_state[x][i + 1] = h[x]; - } - } - - if (CONFIG_T::return_sequences == 0) { - // Output when return_sequences is false - #pragma unroll - for (int x = 0; x < CONFIG_T::n_out; x++) { - res[x] = hidden_state[x][CONFIG_T::n_timesteps]; - } - } else { - // Output when return_sequences is true - #pragma unroll - for (int x = 0; x < CONFIG_T::n_timesteps; x++) { - #pragma unroll - for (int h = 0; h < CONFIG_T::n_out; h++) { - res[x * CONFIG_T::n_out + h] = hidden_state[h][x + 1]; - } - } - } -} - -//---------------------- -// LSTM -//---------------------- - -struct lstm_config { - // Internal data type definitions - typedef float weight_t; - typedef float bias_t; - typedef float accum_t; - - // Layer Sizes - static const unsigned n_in = 1; - static const unsigned n_out = 1; - static const unsigned n_outputs = 1; - - static const unsigned n_timesteps = 1; - static const bool return_sequences = false; - - // Resource reuse info - static const unsigned io_type = io_parallel; - static const unsigned reuse_factor = 1; - static const bool store_weights_in_bram = false; - - // Activation - template using activation_recr = nnet::activation::relu; - - template using activation = nnet::activation::relu; -}; - -template -void lstm_cell(data_T inputs[CONFIG_T::n_in], res_T hidden_state[CONFIG_T::n_out], res_T hidden_state_o[CONFIG_T::n_out], - res_T cell_state[CONFIG_T::n_out], res_T cell_state_o[CONFIG_T::n_out], - const typename CONFIG_T::weight_t WI[CONFIG_T::n_in * CONFIG_T::n_out], - const typename CONFIG_T::weight_t WF[CONFIG_T::n_in * CONFIG_T::n_out], - const typename CONFIG_T::weight_t WC[CONFIG_T::n_in * CONFIG_T::n_out], - const typename CONFIG_T::weight_t WO[CONFIG_T::n_in * CONFIG_T::n_out], - const typename CONFIG_T::weight_t RWI[CONFIG_T::n_out * CONFIG_T::n_out], - const typename CONFIG_T::weight_t RWF[CONFIG_T::n_out * CONFIG_T::n_out], - const typename CONFIG_T::weight_t RWC[CONFIG_T::n_out * CONFIG_T::n_out], - const typename CONFIG_T::weight_t RWO[CONFIG_T::n_out * CONFIG_T::n_out], - const typename CONFIG_T::bias_t BI[CONFIG_T::n_out], const typename CONFIG_T::bias_t BF[CONFIG_T::n_out], - const typename CONFIG_T::bias_t BC[CONFIG_T::n_out], const typename CONFIG_T::bias_t BO[CONFIG_T::n_out]) { - - // Internals definitions - typename CONFIG_T::accum_t i_afterW[CONFIG_T::n_out] hls_register; - typename CONFIG_T::accum_t i_afterBias[CONFIG_T::n_out] hls_register; - typename CONFIG_T::accum_t c_afterW[CONFIG_T::n_out] hls_register; - typename CONFIG_T::accum_t c_afterBias[CONFIG_T::n_out] hls_register; - typename CONFIG_T::accum_t o_afterW[CONFIG_T::n_out] hls_register; - typename CONFIG_T::accum_t o_afterBias[CONFIG_T::n_out] hls_register; - typename CONFIG_T::accum_t f_afterW[CONFIG_T::n_out] hls_register; - typename CONFIG_T::accum_t f_afterBias[CONFIG_T::n_out] hls_register; - - // Hidden state Gate candidates, intermediate variables - typename CONFIG_T::accum_t i_hiddenCand[CONFIG_T::n_out] hls_register; - typename CONFIG_T::accum_t f_hiddenCand[CONFIG_T::n_out] hls_register; - typename CONFIG_T::accum_t c_hiddenCand[CONFIG_T::n_out] hls_register; - typename CONFIG_T::accum_t o_hiddenCand[CONFIG_T::n_out] hls_register; - - // After addition, intermediate variables - typename CONFIG_T::accum_t i_afterAdd[CONFIG_T::n_out] hls_register; - typename CONFIG_T::accum_t f_afterAdd[CONFIG_T::n_out] hls_register; - typename CONFIG_T::accum_t c_afterAdd[CONFIG_T::n_out] hls_register; - typename CONFIG_T::accum_t o_afterAdd[CONFIG_T::n_out] hls_register; - - // Gate outputs - typename CONFIG_T::accum_t gate_i[CONFIG_T::n_out] hls_register; - typename CONFIG_T::accum_t gate_f[CONFIG_T::n_out] hls_register; - typename CONFIG_T::accum_t gate_c[CONFIG_T::n_out] hls_register; - typename CONFIG_T::accum_t gate_o[CONFIG_T::n_out] hls_register; - typename CONFIG_T::accum_t gate_ic[CONFIG_T::n_out] hls_register; - typename CONFIG_T::accum_t gate_forget[CONFIG_T::n_out] hls_register; - typename CONFIG_T::accum_t h[CONFIG_T::n_out] hls_register; - - // Intermediate variable cell calculation - typename CONFIG_T::accum_t cell_act_multp[CONFIG_T::n_out] hls_register; - typename CONFIG_T::accum_t cell_act_add[CONFIG_T::n_out] hls_register; - - //-----------Gate I Calculations - // Weight multiplication - multiply_W( - inputs, i_afterW, WI); - - // Bias addition - add_bias( - i_afterW, i_afterBias, BI); - - // Hidden Candidate - multiply_U(hidden_state, i_hiddenCand, - RWI); - - // Vector addition - add_vectors(i_afterBias, i_hiddenCand, - i_afterAdd); - - // Activation - CONFIG_T::template activation_recr::activation(i_afterAdd, gate_i); - - //-----------Gate F Calculations - // Weight multiplication - multiply_W( - inputs, f_afterW, WF); - - // Bias addition - add_bias( - f_afterW, f_afterBias, BF); - - // Hidden Candidate - multiply_U(hidden_state, f_hiddenCand, - RWF); - - // Vector addition - add_vectors(f_afterBias, f_hiddenCand, - f_afterAdd); - - // Activation - CONFIG_T::template activation_recr::activation(f_afterAdd, gate_f); - - //-----------Gate C Calculations - // Weight multiplication - multiply_W( - inputs, c_afterW, WC); - - // Bias addition - add_bias( - c_afterW, c_afterBias, BC); - - // Hidden Candidate - multiply_U(hidden_state, c_hiddenCand, - RWC); - - // Vector addition - add_vectors(c_afterBias, c_hiddenCand, - c_afterAdd); - - // Activation - CONFIG_T::template activation::activation(c_afterAdd, gate_c); - - //-----------gate I and C multiply - // Vector multiplication - multiply_vectors(gate_i, gate_c, gate_ic); - - //-----------Gate O Calculations - // Weight multiplication - multiply_W( - inputs, o_afterW, WO); - - // Bias addition - add_bias( - o_afterW, o_afterBias, BO); - - // Hidden Candidate - multiply_U(hidden_state, o_hiddenCand, - RWO); - - // Vector addition - add_vectors(o_afterBias, o_hiddenCand, - o_afterAdd); - - // Activation - CONFIG_T::template activation_recr::activation(o_afterAdd, gate_o); - - //-----------Cell State Calculation - // Vector multiplication - multiply_vectors(gate_f, cell_state, cell_act_multp); - - // Vector addition - add_vectors(gate_ic, cell_act_multp, - cell_act_add); - - //-----------Forget gate Calculation - // Activation - CONFIG_T::template activation::activation(cell_act_add, gate_forget); - - // Vector multiplication - multiply_vectors(gate_o, gate_forget, h); - -OUTPUT_WRITE_LOOP: - #pragma unroll - for (int x = (CONFIG_T::n_out - 1); x >= 0; x--) { - hidden_state_o[x] = h[x]; - cell_state_o[x] = cell_act_add[x]; - } -} - -template -void lstm(data_T data[CONFIG_T::n_timesteps * CONFIG_T::n_in], res_T res[CONFIG_T::n_outputs * CONFIG_T::n_out], - const typename CONFIG_T::weight_t WI[CONFIG_T::n_in * CONFIG_T::n_out], - const typename CONFIG_T::weight_t WF[CONFIG_T::n_in * CONFIG_T::n_out], - const typename CONFIG_T::weight_t WC[CONFIG_T::n_in * CONFIG_T::n_out], - const typename CONFIG_T::weight_t WO[CONFIG_T::n_in * CONFIG_T::n_out], - const typename CONFIG_T::weight_t RWI[CONFIG_T::n_out * CONFIG_T::n_out], - const typename CONFIG_T::weight_t RWF[CONFIG_T::n_out * CONFIG_T::n_out], - const typename CONFIG_T::weight_t RWC[CONFIG_T::n_out * CONFIG_T::n_out], - const typename CONFIG_T::weight_t RWO[CONFIG_T::n_out * CONFIG_T::n_out], - const typename CONFIG_T::bias_t BI[CONFIG_T::n_out], const typename CONFIG_T::bias_t BF[CONFIG_T::n_out], - const typename CONFIG_T::bias_t BC[CONFIG_T::n_out], const typename CONFIG_T::bias_t BO[CONFIG_T::n_out]) { - res_T hidden_state[CONFIG_T::n_out][CONFIG_T::n_timesteps + 1] hls_register; - res_T hidden_state_temp[CONFIG_T::n_out] hls_register; - res_T cell_state[CONFIG_T::n_out][CONFIG_T::n_timesteps + 1] hls_register; - res_T cell_state_temp[CONFIG_T::n_out] hls_register; - res_T h[CONFIG_T::n_out] hls_register; - res_T c[CONFIG_T::n_out] hls_register; - data_T in[CONFIG_T::n_in] hls_register; - -// Set initially hidden state (output) to zero -INIT_LOOP: - #pragma unroll - for (int x = 0; x < CONFIG_T::n_out; x++) { - hidden_state[x][0] = 0; - cell_state[x][0] = 0; - } - - // Input dimension - #pragma disable_loop_pipelining - for (int i = 0; i < CONFIG_T::n_timesteps; i++) { - // Data at current time step - for (int x = 0; x < CONFIG_T::n_in; x++) { - in[x] = data[x + i * CONFIG_T::n_in]; - } - - // Hidden state at current time step - #pragma unroll - for (int x = 0; x < CONFIG_T::n_out; x++) { - hidden_state_temp[x] = hidden_state[x][i]; - cell_state_temp[x] = cell_state[x][i]; - } - - // Do LSTM - lstm_cell(in, hidden_state_temp, h, cell_state_temp, c, WI, WF, WC, WO, RWI, RWF, RWC, RWO, - BI, BF, BC, BO); - - // Write result - #pragma unroll - for (int x = 0; x < CONFIG_T::n_out; x++) { - hidden_state[x][i + 1] = h[x]; - cell_state[x][i + 1] = c[x]; - } - } - - if (CONFIG_T::return_sequences == 0) { - // Output when return_sequences is false - #pragma unroll - for (int x = 0; x < CONFIG_T::n_out; x++) { - res[x] = hidden_state[x][CONFIG_T::n_timesteps]; - } - } else { - // Output when return_sequences is true - #pragma unroll - for (int x = 0; x < CONFIG_T::n_timesteps; x++) { - for (int h = 0; h < CONFIG_T::n_out; h++) { - res[x * CONFIG_T::n_out + h] = hidden_state[h][x + 1]; - } - } - } -} - -template -void lstm(data_T data[CONFIG_T::n_timesteps * CONFIG_T::n_in], h_T hidden_state_initial[CONFIG_T::n_out], - s_T cell_state_initial[CONFIG_T::n_out], res_T res[CONFIG_T::n_outputs * CONFIG_T::n_out], - const typename CONFIG_T::weight_t WI[CONFIG_T::n_in * CONFIG_T::n_out], - const typename CONFIG_T::weight_t WF[CONFIG_T::n_in * CONFIG_T::n_out], - const typename CONFIG_T::weight_t WC[CONFIG_T::n_in * CONFIG_T::n_out], - const typename CONFIG_T::weight_t WO[CONFIG_T::n_in * CONFIG_T::n_out], - const typename CONFIG_T::weight_t RWI[CONFIG_T::n_out * CONFIG_T::n_out], - const typename CONFIG_T::weight_t RWF[CONFIG_T::n_out * CONFIG_T::n_out], - const typename CONFIG_T::weight_t RWC[CONFIG_T::n_out * CONFIG_T::n_out], - const typename CONFIG_T::weight_t RWO[CONFIG_T::n_out * CONFIG_T::n_out], - const typename CONFIG_T::bias_t BI[CONFIG_T::n_out], const typename CONFIG_T::bias_t BF[CONFIG_T::n_out], - const typename CONFIG_T::bias_t BC[CONFIG_T::n_out], const typename CONFIG_T::bias_t BO[CONFIG_T::n_out]) { - h_T hidden_state[CONFIG_T::n_out][CONFIG_T::n_timesteps + 1] hls_register; - h_T hidden_state_temp[CONFIG_T::n_out] hls_register; - s_T cell_state[CONFIG_T::n_out][CONFIG_T::n_timesteps + 1] hls_register; - s_T cell_state_temp[CONFIG_T::n_out] hls_register; - h_T h[CONFIG_T::n_out] hls_register; - s_T c[CONFIG_T::n_out] hls_register; - data_T in[CONFIG_T::n_in] hls_register; - -// Set initially hidden state (output) to zero -INIT_LOOP: - #pragma unroll - for (int x = 0; x < CONFIG_T::n_out; x++) { - hidden_state[x][0] = hidden_state_initial[x]; - cell_state[x][0] = cell_state_initial[x]; - } - - // Input dimension - #pragma disable_loop_pipelining - for (int i = 0; i < CONFIG_T::n_timesteps; i++) { - // Data at current time step - for (int x = 0; x < CONFIG_T::n_in; x++) { - in[x] = data[x + i * CONFIG_T::n_in]; - } - - // Hidden state at current time step - #pragma unroll - for (int x = 0; x < CONFIG_T::n_out; x++) { - hidden_state_temp[x] = hidden_state[x][i]; - cell_state_temp[x] = cell_state[x][i]; - } - - // Do LSTM - lstm_cell(in, hidden_state_temp, h, cell_state_temp, c, WI, WF, WC, WO, RWI, RWF, RWC, RWO, - BI, BF, BC, BO); - - // Write result - #pragma unroll - for (int x = 0; x < CONFIG_T::n_out; x++) { - hidden_state[x][i + 1] = h[x]; - cell_state[x][i + 1] = c[x]; - } - } - - if (CONFIG_T::return_sequences == 0) { - // Output when return_sequences is false - #pragma unroll - for (int x = 0; x < CONFIG_T::n_out; x++) { - res[x] = hidden_state[x][CONFIG_T::n_timesteps]; - } - } else { - // Output when return_sequences is true - #pragma unroll - for (int x = 0; x < CONFIG_T::n_timesteps; x++) { - for (int h = 0; h < CONFIG_T::n_out; h++) { - res[x * CONFIG_T::n_out + h] = hidden_state[h][x + 1]; - } - } - } -} - -} // namespace nnet - -#endif diff --git a/hls4ml/templates/quartus/firmware/nnet_utils/nnet_recurrent_activation.h b/hls4ml/templates/quartus/firmware/nnet_utils/nnet_recurrent_activation.h deleted file mode 100644 index e5896e6da6..0000000000 --- a/hls4ml/templates/quartus/firmware/nnet_utils/nnet_recurrent_activation.h +++ /dev/null @@ -1,53 +0,0 @@ -#ifndef NNET_RECR_ACTIVATION_H_ -#define NNET_RECR_ACTIVATION_H_ - -#include "nnet_activation.h" -#include "nnet_common.h" - -namespace nnet { - -namespace activation { - -template class Activation { - public: - // ************************************************* - // Blank Activation - // ************************************************* - static void activation(data_T data[CONFIG_T::n_in], res_T res[CONFIG_T::n_in]) {} -}; - -template class relu : public Activation { - public: - // ************************************************* - // Relu Activation - // ************************************************* - static void activation(data_T data[CONFIG_T::n_in], res_T res[CONFIG_T::n_in]) { - nnet::relu(data, res); - } -}; - -template class sigmoid : public Activation { - public: - // ************************************************* - // Sigmoid Activation - // ************************************************* - static void activation(data_T data[CONFIG_T::n_in], res_T res[CONFIG_T::n_in]) { - nnet::sigmoid(data, res); - } -}; - -template class tanh : public Activation { - public: - // ************************************************* - // TanH Activation - // ************************************************* - static void activation(data_T data[CONFIG_T::n_in], res_T res[CONFIG_T::n_in]) { - nnet::dense_tanh(data, res); - } -}; - -} // namespace activation - -} // namespace nnet - -#endif diff --git a/hls4ml/templates/quartus/firmware/nnet_utils/nnet_recurrent_stream.h b/hls4ml/templates/quartus/firmware/nnet_utils/nnet_recurrent_stream.h deleted file mode 100644 index d439428350..0000000000 --- a/hls4ml/templates/quartus/firmware/nnet_utils/nnet_recurrent_stream.h +++ /dev/null @@ -1,65 +0,0 @@ -#ifndef NNET_RECURRENT_STREAM_H_ -#define NNET_RECURRENT_STREAM_H_ - -#include "nnet_common.h" -#include "nnet_dense.h" -#include "nnet_recurrent_activation.h" - -namespace nnet { -template -void gru(stream &data_stream, stream &res_stream, - const typename CONFIG_T::weight_t weights[3 * CONFIG_T::n_units * CONFIG_T::n_in], - const typename CONFIG_T::weight_t recurrent_weights[3 * CONFIG_T::n_units * CONFIG_T::n_units], - const typename CONFIG_T::bias_t bias[3 * CONFIG_T::n_units], - const typename CONFIG_T::bias_t recurrent_bias[3 * CONFIG_T::n_units]) { - - hls_register typename res_T::value_type h[CONFIG_T::n_units]; - #pragma unroll - for (int i = 0; i < CONFIG_T::n_units; i++) { - h[i] = 0; - } - - hls_register typename data_T::value_type x[CONFIG_T::n_in]; - -DataPropagation: - for (int i_in = 0; i_in < CONFIG_T::n_timesteps * CONFIG_T::n_in / data_T::size; i_in++) { - data_T data_pack = data_stream.read(); - - DataPack: - #pragma unroll - for (int i_pack = 0; i_pack < data_T::size; i_pack++) { - x[i_pack] = data_pack[i_pack]; - } - - nnet::gru_cell(x, h, weights, recurrent_weights, - bias, recurrent_bias); - - if (CONFIG_T::return_sequences) { - res_T res_pack; - - ResPackRetSeq: - #pragma unroll - for (int i_pack = 0; i_pack < res_T::size; i_pack++) { - res_pack[i_pack] = h[i_pack]; - } - - res_stream.write(res_pack); - } - } - - if (!CONFIG_T::return_sequences) { - res_T res_pack; - - ResPackNoRetSeq: - #pragma unroll - for (int i_pack = 0; i_pack < res_T::size; i_pack++) { - res_pack[i_pack] = h[i_pack]; - } - - res_stream.write(res_pack); - } -} - -} // namespace nnet - -#endif diff --git a/hls4ml/templates/quartus/firmware/nnet_utils/nnet_resize.h b/hls4ml/templates/quartus/firmware/nnet_utils/nnet_resize.h deleted file mode 100644 index a8e3ffe85e..0000000000 --- a/hls4ml/templates/quartus/firmware/nnet_utils/nnet_resize.h +++ /dev/null @@ -1,38 +0,0 @@ -#ifndef NNET_IMAGE_H_ -#define NNET_IMAGE_H_ - -namespace nnet { - -struct resize_config { - static const unsigned in_height = 10; - static const unsigned in_width = 10; - - static const unsigned out_height = 10; - static const unsigned out_width = 10; - - static const unsigned n_chan = 10; -}; - -template -void resize_nearest(data_T image[CONFIG_T::height * CONFIG_T::width * CONFIG_T::n_chan], - data_T resized[CONFIG_T::new_height * CONFIG_T::new_width * CONFIG_T::n_chan]) { - int y_ratio = (int)((CONFIG_T::height << 16) / CONFIG_T::new_height) + 1; - int x_ratio = (int)((CONFIG_T::width << 16) / CONFIG_T::new_width) + 1; - - for (int i = 0; i < CONFIG_T::new_height; i++) { - for (int j = 0; j < CONFIG_T::new_width; j++) { - int x = ((j * x_ratio) >> 16); - int y = ((i * y_ratio) >> 16); - - #pragma unroll - for (int k = 0; k < CONFIG_T::n_chan; k++) { - resized[(i * CONFIG_T::new_width * CONFIG_T::n_chan) + j * CONFIG_T::n_chan + k] = - image[(y * CONFIG_T::width * CONFIG_T::n_chan) + x * CONFIG_T::n_chan + k]; - } - } - } -} - -} // namespace nnet - -#endif diff --git a/hls4ml/templates/quartus/firmware/nnet_utils/nnet_resize_stream.h b/hls4ml/templates/quartus/firmware/nnet_utils/nnet_resize_stream.h deleted file mode 100644 index 1b8864dab2..0000000000 --- a/hls4ml/templates/quartus/firmware/nnet_utils/nnet_resize_stream.h +++ /dev/null @@ -1,56 +0,0 @@ -#ifndef NNET_IMAGE_STREAM_H_ -#define NNET_IMAGE_STREAM_H_ - -#include "nnet_common.h" - -namespace nnet { - -template void resize_nearest(stream &image, stream &resized) { - assert(CONFIG_T::new_height % CONFIG_T::height == 0); - assert(CONFIG_T::new_width % CONFIG_T::width == 0); - - constexpr unsigned ratio_height = CONFIG_T::new_height / CONFIG_T::height; - constexpr unsigned ratio_width = CONFIG_T::new_width / CONFIG_T::width; - -ImageHeight: - for (unsigned h = 0; h < CONFIG_T::height; h++) { - hls_register data_T data_in_row[CONFIG_T::width]; - - ImageWidth: - for (unsigned i = 0; i < CONFIG_T::width; i++) { - hls_register data_T in_data = image.read(); - - ImageChan: - #pragma unroll - for (unsigned j = 0; j < CONFIG_T::n_chan; j++) { - data_in_row[i][j] = in_data[j]; - } - } - - ResizeHeight: - for (unsigned i = 0; i < ratio_height; i++) { - - ImageWidth2: - for (unsigned l = 0; l < CONFIG_T::width; l++) { - - ResizeWidth: - for (unsigned j = 0; j < ratio_width; j++) { - - hls_register data_T out_data; - - ResizeChan: - #pragma unroll - for (unsigned k = 0; k < CONFIG_T::n_chan; k++) { - out_data[k] = data_in_row[l][k]; - } - - resized.write(out_data); - } - } - } - } -} - -} // namespace nnet - -#endif diff --git a/hls4ml/templates/quartus/firmware/nnet_utils/nnet_stream.h b/hls4ml/templates/quartus/firmware/nnet_utils/nnet_stream.h deleted file mode 100644 index f907859450..0000000000 --- a/hls4ml/templates/quartus/firmware/nnet_utils/nnet_stream.h +++ /dev/null @@ -1,251 +0,0 @@ -#ifndef NNET_CLONE_H -#define NNET_CLONE_H - -#include "nnet_common.h" -#include - -namespace nnet { - -struct broadcast_config { - static const unsigned in_height = 10; - static const unsigned in_width = 10; - static const unsigned n_chan = 1; - static const unsigned n_dupl = 2; -}; - -template -void clone_stream(stream &data, stream &res1, stream &res2) { -CloneLoop: - #pragma ii 1 - for (int i = 0; i < N / data_T::size; i++) { - data_T in_data = data.read(); - res_T out_data1; - res_T out_data2; - - ClonePack: - #pragma unroll - for (int j = 0; j < data_T::size; j++) { - out_data1[j] = in_data[j]; - out_data2[j] = in_data[j]; - } - - res1.write(out_data1); - res2.write(out_data2); - } -} - -template -void clone_stream(stream &data, stream &res1, stream &res2, stream &res3) { -CloneLoop: - #pragma ii 1 - for (int i = 0; i < N / data_T::size; i++) { - data_T in_data = data.read(); - res_T out_data1; - res_T out_data2; - res_T out_data3; - - ClonePack: - #pragma unroll - for (int j = 0; j < data_T::size; j++) { - out_data1[j] = in_data[j]; - out_data2[j] = in_data[j]; - out_data3[j] = in_data[j]; - } - - res1.write(out_data1); - res2.write(out_data2); - res3.write(out_data3); - } -} - -template -void clone_stream(stream &data, stream &res1, stream &res2, stream &res3, stream &res4) { -CloneLoop: - #pragma ii 1 - for (int i = 0; i < N / data_T::size; i++) { - data_T in_data = data.read(); - res_T out_data1; - res_T out_data2; - res_T out_data3; - res_T out_data4; - - ClonePack: - #pragma unroll - for (int j = 0; j < data_T::size; j++) { - out_data1[j] = in_data[j]; - out_data2[j] = in_data[j]; - out_data3[j] = in_data[j]; - out_data4[j] = in_data[j]; - } - - res1.write(out_data1); - res2.write(out_data2); - res3.write(out_data3); - res4.write(out_data4); - } -} - -template -void clone_stream(stream &data, stream &res1, stream &res2, stream &res3, stream &res4, - stream &res5) { -CloneLoop: - #pragma ii 1 - for (int i = 0; i < N / data_T::size; i++) { - data_T in_data = data.read(); - res_T out_data1; - res_T out_data2; - res_T out_data3; - res_T out_data4; - res_T out_data5; - - ClonePack: - #pragma unroll - for (int j = 0; j < data_T::size; j++) { - out_data1[j] = in_data[j]; - out_data2[j] = in_data[j]; - out_data3[j] = in_data[j]; - out_data4[j] = in_data[j]; - out_data5[j] = in_data[j]; - } - - res1.write(out_data1); - res2.write(out_data2); - res3.write(out_data3); - res4.write(out_data4); - res5.write(out_data5); - } -} - -template -void clone_stream(stream &data, stream &res1, stream &res2, stream &res3, stream &res4, - stream &res5, stream &res6) { -CloneLoop: - #pragma ii 1 - for (int i = 0; i < N / data_T::size; i++) { - data_T in_data = data.read(); - res_T out_data1; - res_T out_data2; - res_T out_data3; - res_T out_data4; - res_T out_data5; - res_T out_data6; - - ClonePack: - #pragma unroll - for (int j = 0; j < data_T::size; j++) { - out_data1[j] = in_data[j]; - out_data2[j] = in_data[j]; - out_data3[j] = in_data[j]; - out_data4[j] = in_data[j]; - out_data5[j] = in_data[j]; - out_data6[j] = in_data[j]; - } - - res1.write(out_data1); - res2.write(out_data2); - res3.write(out_data3); - res4.write(out_data4); - res5.write(out_data5); - res6.write(out_data6); - } -} - -template -void clone_stream(stream &data, stream &res1, stream &res2, stream &res3, stream &res4, - stream &res5, stream &res6, stream &res7) { -CloneLoop: - #pragma ii 1 - for (int i = 0; i < N / data_T::size; i++) { - data_T in_data = data.read(); - res_T out_data1; - res_T out_data2; - res_T out_data3; - res_T out_data4; - res_T out_data5; - res_T out_data6; - res_T out_data7; - - ClonePack: - #pragma unroll - for (int j = 0; j < data_T::size; j++) { - out_data1[j] = in_data[j]; - out_data2[j] = in_data[j]; - out_data3[j] = in_data[j]; - out_data4[j] = in_data[j]; - out_data5[j] = in_data[j]; - out_data6[j] = in_data[j]; - out_data7[j] = in_data[j]; - } - - res1.write(out_data1); - res2.write(out_data2); - res3.write(out_data3); - res4.write(out_data4); - res5.write(out_data5); - res6.write(out_data6); - res7.write(out_data7); - } -} - -template void repack_stream(stream &data, stream &res) { - if (data_T::size == res_T::size) { - #pragma ii 1 - for (int i = 0; i < N / data_T::size; i++) { - - data_T in_data = data.read(); - res_T out_data; - - #pragma unroll - for (int j = 0; j < data_T::size; j++) { - out_data[j] = in_data[j]; - } - - res.write(out_data); - } - } else if (data_T::size > res_T::size) { - constexpr unsigned pack_diff = data_T::size / res_T::size; - - for (int i = 0; i < N / data_T::size; i++) { - - data_T in_data = data.read(); - res_T out_data; - - #pragma ii 1 - for (int j = 0; j < pack_diff; j++) { - - res_T out_data; - - #pragma unroll - for (int k = 0; k < res_T::size; k++) { - out_data[k] = in_data[j * res_T::size + k]; - } - res.write(out_data); - } - } - } else { // data_T::size < res_T::size - res_T out_data; - constexpr unsigned pack_diff = res_T::size / data_T::size; - unsigned pack_cnt = 0; - #pragma ii 1 - for (int i = 0; i < N / data_T::size; i++) { - - data_T in_data = data.read(); - - #pragma unroll - for (int j = 0; j < data_T::size; j++) { - out_data[pack_cnt * data_T::size + j] = in_data[j]; - } - - if (pack_cnt == pack_diff - 1) { - res.write(out_data); - pack_cnt = 0; - } else { - pack_cnt++; - } - } - } -} - -} // namespace nnet - -#endif diff --git a/hls4ml/templates/quartus/firmware/nnet_utils/nnet_transpose.h b/hls4ml/templates/quartus/firmware/nnet_utils/nnet_transpose.h deleted file mode 100644 index 05fd5fe76a..0000000000 --- a/hls4ml/templates/quartus/firmware/nnet_utils/nnet_transpose.h +++ /dev/null @@ -1,50 +0,0 @@ -#ifndef NNET_TRANSPOSE_H_ -#define NNET_TRANSPOSE_H_ - -namespace nnet { - -struct transpose_config { - static const unsigned height = 10; - static const unsigned width = 10; - static const unsigned depth = 10; - static constexpr unsigned perm[3] = {2, 0, 1}; -}; - -template -void transpose_2d(data_T data[CONFIG_T::height * CONFIG_T::width], res_T res[CONFIG_T::height * CONFIG_T::width]) { - for (int i = 0; i < CONFIG_T::height; i++) { - #pragma unroll - for (int j = 0; j < CONFIG_T::width; j++) { - res[j * CONFIG_T::height + i] = static_cast(data[i * CONFIG_T::width + j]); - } - } -} - -template -void transpose_3d(data_T data[CONFIG_T::depth * CONFIG_T::height * CONFIG_T::width], - res_T res[CONFIG_T::depth * CONFIG_T::height * CONFIG_T::width]) { - static constexpr unsigned dim_data[3] = {CONFIG_T::depth, CONFIG_T::height, CONFIG_T::width}; - static constexpr unsigned dim_res[3] = {dim_data[CONFIG_T::perm[0]], dim_data[CONFIG_T::perm[1]], - dim_data[CONFIG_T::perm[2]]}; - - int index_data[3] = {0}, index_res[3] = {0}; - - for (index_data[0] = 0; index_data[0] < dim_data[0]; index_data[0]++) { - #pragma unroll - for (index_data[1] = 0; index_data[1] < dim_data[1]; index_data[1]++) { - #pragma unroll - for (index_data[2] = 0; index_data[2] < dim_data[2]; index_data[2]++) { - index_res[0] = index_data[CONFIG_T::perm[0]]; - index_res[1] = index_data[CONFIG_T::perm[1]]; - index_res[2] = index_data[CONFIG_T::perm[2]]; - - res[index_res[0] * dim_res[1] * dim_res[2] + index_res[1] * dim_res[2] + index_res[2]] = static_cast( - data[index_data[0] * dim_data[1] * dim_data[2] + index_data[1] * dim_data[2] + index_data[2]]); - } - } - } -} - -} // namespace nnet - -#endif diff --git a/hls4ml/templates/quartus/firmware/nnet_utils/nnet_transpose_stream.h b/hls4ml/templates/quartus/firmware/nnet_utils/nnet_transpose_stream.h deleted file mode 100644 index cc6cd8104b..0000000000 --- a/hls4ml/templates/quartus/firmware/nnet_utils/nnet_transpose_stream.h +++ /dev/null @@ -1,32 +0,0 @@ -#ifndef NNET_TRANSPOSE_STREAM_H_ -#define NNET_TRANSPOSE_STREAM_H_ - -namespace nnet { - -template void transpose_2d(stream &data, stream &res) { - hls_register typename data_T::value_type data_array[CONFIG_T::height * CONFIG_T::width]; - - for (int i = 0; i < CONFIG_T::height * CONFIG_T::width / data_T::size; i++) { - hls_register data_T in_data = data.read(); - - #pragma unroll - for (int j = 0; j < data_T::size; j++) { - data_array[i * data_T::size + j] = typename data_T::value_type(in_data[j]); - } - } - - for (int i = 0; i < CONFIG_T::height * CONFIG_T::width / res_T::size; i++) { - hls_register res_T out_data; - - #pragma unroll - for (int j = 0; j < res_T::size; j++) { - out_data[j] = typename res_T::value_type(data_array[j * data_T::size + i]); - } - - res.write(out_data); - } -} - -} // namespace nnet - -#endif diff --git a/hls4ml/templates/quartus/firmware/nnet_utils/nnet_types.h b/hls4ml/templates/quartus/firmware/nnet_utils/nnet_types.h deleted file mode 100644 index 3209b9a265..0000000000 --- a/hls4ml/templates/quartus/firmware/nnet_utils/nnet_types.h +++ /dev/null @@ -1,78 +0,0 @@ -#ifndef NNET_TYPES_H_ -#define NNET_TYPES_H_ - -#include -#include -#include - -namespace nnet { - -// Fixed-size array -template struct array { - typedef T value_type; - static const unsigned size = N; - - T data[N]; - - array() {} - - array(T x) { - #pragma unroll - for (int i = 0; i < N; i++) { - data[i] = x; - } - } - - T &operator[](size_t pos) { return data[pos]; } - - const T &operator[](size_t pos) const { return data[pos]; } - - array &operator=(const array &other) { - if (&other == this) - return *this; - - assert(N == other.size && "Array sizes must match."); - - #pragma unroll - for (unsigned i = 0; i < N; i++) { - data[i] = other[i]; - } - return *this; - } -}; - -/* - * HLS Shift Register Implementation - * To verify a shift register is used in hardware, go to report.html > Area Analysis of System - * Unrolling the shift loop minimizes resource usage and latency at the same time - * The shift loop should be either fully unrolled or not unrolled at all - * Unrolling with a specific unroll factor or pipelining with certain ii's, can cause an irregular access pattern, which - * wouldn't allow shift register usage in RTL - */ -template struct shift_reg { - private: - T data[N]; - - public: - // Default constructor - shift_reg() {} - - // Shift queue, insert new element and return element from the front - T shift(T inp) { - T out = data[N - 1]; - - #pragma unroll - for (int i = N - 1; i > 0; i--) { - data[i] = data[i - 1]; - } - data[0] = inp; - - return out; - } - - T read(int pos) { return data[pos]; } -}; - -} // namespace nnet - -#endif diff --git a/hls4ml/templates/quartus/firmware/parameters.h b/hls4ml/templates/quartus/firmware/parameters.h deleted file mode 100644 index e23ca9770f..0000000000 --- a/hls4ml/templates/quartus/firmware/parameters.h +++ /dev/null @@ -1,11 +0,0 @@ -#ifndef PARAMETERS_H_ -#define PARAMETERS_H_ - -#include "defines.h" - -#include "nnet_utils/nnet_helpers.h" -// hls-fpga-machine-learning insert includes - -// hls-fpga-machine-learning insert layer-config - -#endif diff --git a/hls4ml/templates/quartus/myproject_bridge.cpp b/hls4ml/templates/quartus/myproject_bridge.cpp deleted file mode 100644 index 35c1997f62..0000000000 --- a/hls4ml/templates/quartus/myproject_bridge.cpp +++ /dev/null @@ -1,66 +0,0 @@ -#ifndef MYPROJECT_BRIDGE_H_ -#define MYPROJECT_BRIDGE_H_ - -#include "firmware/myproject.h" -#include "firmware/nnet_utils/nnet_helpers.h" -#include -#include - -// hls-fpga-machine-learning insert bram - -namespace nnet { -bool trace_enabled = false; -std::map *trace_outputs = NULL; -size_t trace_type_size = sizeof(double); -} // namespace nnet - -extern "C" { - -struct trace_data { - const char *name; - void *data; -}; - -void allocate_trace_storage(size_t element_size) { - nnet::trace_enabled = true; - nnet::trace_outputs = new std::map; - nnet::trace_type_size = element_size; - // hls-fpga-machine-learning insert trace_outputs -} - -void free_trace_storage() { - for (std::map::iterator i = nnet::trace_outputs->begin(); i != nnet::trace_outputs->end(); i++) { - void *ptr = i->second; - free(ptr); - } - nnet::trace_outputs->clear(); - delete nnet::trace_outputs; - nnet::trace_outputs = NULL; - nnet::trace_enabled = false; -} - -void collect_trace_output(struct trace_data *c_trace_outputs) { - int ii = 0; - for (std::map::iterator i = nnet::trace_outputs->begin(); i != nnet::trace_outputs->end(); i++) { - c_trace_outputs[ii].name = i->first.c_str(); - c_trace_outputs[ii].data = i->second; - ii++; - } -} - -// Wrapper of top level function for Python bridge -void myproject_float( - // hls-fpga-machine-learning insert header #float -) { - - // hls-fpga-machine-learning insert wrapper #float -} - -void myproject_double( - // hls-fpga-machine-learning insert header #double -) { - // hls-fpga-machine-learning insert wrapper #double -} -} - -#endif diff --git a/hls4ml/templates/quartus/myproject_test_parallel.cpp b/hls4ml/templates/quartus/myproject_test_parallel.cpp deleted file mode 100644 index 8df2674318..0000000000 --- a/hls4ml/templates/quartus/myproject_test_parallel.cpp +++ /dev/null @@ -1,112 +0,0 @@ -#include -#include -#include -#include -#include -#include - -#include "firmware/myproject.h" -#include "firmware/parameters.h" - -// hls-fpga-machine-learning insert bram - -#define CHECKPOINT 5000 - -// This function is written to avoid stringstream, which is -// not supported in cosim 20.1, and because strtok -// requires a const_cast or allocation to use with std::strings. -// This function returns the next float (by argument) at position pos, -// updating pos. True is returned if conversion done, false if the string -// has ended, and std::invalid_argument exception if the string was bad. -bool nextToken(const std::string &str, std::size_t &pos, float &val) { - while (pos < str.size() && std::isspace(static_cast(str[pos]))) { - pos++; - } - if (pos >= str.size()) { - return false; - } - std::size_t offset = 0; - val = std::stof(str.substr(pos), &offset); - pos += offset; - return true; -} - -int main(int argc, char **argv) { - // load input data from text file - std::ifstream fin("tb_data/tb_input_features.dat"); - // load predictions from text file - std::ifstream fpr("tb_data/tb_output_predictions.dat"); - - std::string RESULTS_LOG = "tb_data/results.log"; - std::ofstream fout(RESULTS_LOG); - - std::string iline; - std::string pline; - - std::vector inputs; - std::vector outputs; - - if (fin.is_open() && fpr.is_open()) { - std::vector> predictions; - unsigned int num_iterations = 0; - for (; std::getline(fin, iline) && std::getline(fpr, pline); num_iterations++) { - if (num_iterations % CHECKPOINT == 0) { - std::cout << "Processing input " << num_iterations << std::endl; - } - - std::vector in; - std::vector pr; - float current; - - std::size_t pos = 0; - while (nextToken(iline, pos, current)) { - in.push_back(current); - } - - pos = 0; - while (nextToken(pline, pos, current)) { - pr.push_back(current); - } - - // hls-fpga-machine-learning insert data - predictions.push_back(std::move(pr)); - } - - // Do this separately to avoid vector reallocation - // hls-fpga-machine-learning insert top-level-function - - // hls-fpga-machine-learning insert run - - for (int j = 0; j < num_iterations; j++) { - // hls-fpga-machine-learning insert tb-output - if (j % CHECKPOINT == 0) { - std::cout << "Predictions" << std::endl; - // hls-fpga-machine-learning insert predictions - std::cout << "Quantized predictions" << std::endl; - // hls-fpga-machine-learning insert quantized - } - } - fin.close(); - fpr.close(); - } else { - const unsigned int num_iterations = 10; - std::cout << "INFO: Unable to open input/predictions file, using default input with " << num_iterations - << " invocations." << std::endl; - // hls-fpga-machine-learning insert zero - - // hls-fpga-machine-learning insert top-level-function - - // hls-fpga-machine-learning insert run - - for (int j = 0; j < num_iterations; j++) { - // hls-fpga-machine-learning insert output - - // hls-fpga-machine-learning insert tb-output - } - } - - fout.close(); - std::cout << "INFO: Saved inference results to file: " << RESULTS_LOG << std::endl; - - return 0; -} diff --git a/hls4ml/templates/quartus/myproject_test_stream.cpp b/hls4ml/templates/quartus/myproject_test_stream.cpp deleted file mode 100644 index a7f360641b..0000000000 --- a/hls4ml/templates/quartus/myproject_test_stream.cpp +++ /dev/null @@ -1,129 +0,0 @@ -#include -#include -#include -#include -#include -#include - -#include "firmware/myproject.h" -#include "firmware/parameters.h" - -#include "firmware/nnet_utils/nnet_helpers.h" - -// hls-fpga-machine-learning insert bram - -#define CHECKPOINT 5000 - -// This function is written to avoid stringstream, which is -// not supported in cosim 20.1, and because strtok -// requires a const_cast or allocation to use with std::strings. -// This function returns the next float (by argument) at position pos, -// updating pos. True is returned if conversion done, false if the string -// has ended, and std::invalid_argument exception if the string was bad. -bool nextToken(const std::string &str, std::size_t &pos, float &val) { - while (pos < str.size() && std::isspace(static_cast(str[pos]))) { - pos++; - } - if (pos >= str.size()) { - return false; - } - std::size_t offset = 0; - val = std::stof(str.substr(pos), &offset); - pos += offset; - return true; -} - -int main(int argc, char **argv) { - // Load input data from text file - std::ifstream fin("tb_data/tb_input_features.dat"); - std::string iline; - - // Load predictions from text file - std::ifstream fpr("tb_data/tb_output_predictions.dat"); - std::string pline; - - // Output log - std::string RESULTS_LOG = "tb_data/results.log"; - std::ofstream fout(RESULTS_LOG); - - if (fin.is_open() && fpr.is_open()) { - std::vector> predictions; - - unsigned int iteration = 0; - while (std::getline(fin, iline) && std::getline(fpr, pline)) { - if (iteration % CHECKPOINT == 0) { - std::cout << "Processing input " << iteration << std::endl; - } - - // hls-fpga-machine learning instantiate inputs and outputs - - std::vector in; - std::vector pr; - float current; - - std::size_t pos = 0; - while (nextToken(iline, pos, current)) { - in.push_back(current); - } - - pos = 0; - while (nextToken(pline, pos, current)) { - pr.push_back(current); - } - - // hls-fpga-machine-learning insert data - - predictions.push_back(std::move(pr)); - - // hls-fpga-machine-learning insert top-level-function - - // hls-fpga-machine-learning insert run - - // hls-fpga-machine-learning convert output - - // hls-fpga-machine-learning insert tb-output - - if (iteration % CHECKPOINT == 0) { - std::cout << "Python Predictions" << std::endl; - // hls-fpga-machine-learning print predictions - - std::cout << "HLS predictions" << std::endl; - // hls-fpga-machine-learning print output - } - - iteration++; - } - - fin.close(); - fpr.close(); - - } else { - const unsigned int num_iterations = 10; - std::cout << "INFO: Unable to open input/predictions file, using default input with " << num_iterations - << " invocations." << std::endl; - - for (int iteration = 0; iteration < num_iterations; iteration++) { - // hls-fpga-machine learning instantiate inputs and outputs - - // hls-fpga-machine-learning insert zero - - // hls-fpga-machine-learning insert top-level-function - - // hls-fpga-machine-learning insert run - - // hls-fpga-machine-learning convert output - - // hls-fpga-machine-learning insert tb-output - - if (iteration % CHECKPOINT == 0) { - std::cout << "HLS predictions" << std::endl; - // hls-fpga-machine-learning print output - } - } - } - - fout.close(); - std::cout << "INFO: Saved inference results to file: " << RESULTS_LOG << std::endl; - - return 0; -} diff --git a/hls4ml/writer/__init__.py b/hls4ml/writer/__init__.py index 66fbe3e23a..e6272c2996 100644 --- a/hls4ml/writer/__init__.py +++ b/hls4ml/writer/__init__.py @@ -2,7 +2,6 @@ from hls4ml.writer.catapult_writer import CatapultWriter from hls4ml.writer.coyote_writer import CoyoteWriter from hls4ml.writer.libero_writer import LiberoWriter -from hls4ml.writer.quartus_writer import QuartusWriter from hls4ml.writer.symbolic_writer import SymbolicExpressionWriter from hls4ml.writer.vitis_unified_writer import VitisUnifiedWriter from hls4ml.writer.vitis_writer import VitisWriter @@ -15,7 +14,6 @@ register_writer('VivadoAccelerator', VivadoAcceleratorWriter) register_writer('Vitis', VitisWriter) register_writer('VitisUnified', VitisUnifiedWriter) -register_writer('Quartus', QuartusWriter) register_writer('Altera', AlteraWriter) register_writer('Catapult', CatapultWriter) register_writer('Libero', LiberoWriter) diff --git a/hls4ml/writer/quartus_writer.py b/hls4ml/writer/quartus_writer.py deleted file mode 100644 index e0d6338ac3..0000000000 --- a/hls4ml/writer/quartus_writer.py +++ /dev/null @@ -1,1354 +0,0 @@ -import glob -import os -import stat -import tarfile -from collections import OrderedDict -from pathlib import Path -from shutil import copyfile, copytree, rmtree - -import numpy as np -import yaml - -from hls4ml.backends import get_backend -from hls4ml.model.layers import Conv1D, Conv2D, Conv2DBatchnorm, Dense -from hls4ml.utils.fixed_point_utils import FixedPointEmulator, ceil_log2, uint_to_binary -from hls4ml.writer.writers import Writer - -config_filename = 'hls4ml_config.yml' - - -class QuartusWriter(Writer): - def next_pow2(self, x): - return 1 << (x - 1).bit_length() - - def __make_dat_file(self, original_path, project_path): - """ - Convert other input/output data types into a dat file, which is - a text file with the falttened matrix printed out. Note that ' ' is - assumed to be the delimiter. - """ - - # Take in data from current supported data files - if original_path[-3:] == 'npy': - data = np.load(original_path) - else: - raise Exception('Unsupported input/output data files.') - - # Faltten data, just keep first dimension - data = data.reshape(data.shape[0], -1) - - def print_data(f): - for i in range(data.shape[0]): - for j in range(data.shape[1]): - f.write(str(data[i][j]) + ' ') - f.write('\n') - - # Print out in dat file - with open(project_path, 'w') as f: - print_data(f) - - def get_max_reuse_factor(self, model): - max_rf = 0 - for layer in model.get_layers(): - rf = int(layer.get_attr('reuse_factor')) - if rf > max_rf: - max_rf = rf - return max_rf - - def print_array_to_cpp(self, var, layer, odir): - """Write a weights array to C++ header files. - - Args: - var (WeightVariable): Weight to write - layer (Layer): Instance of the layer to which the weights belong - odir (str): Output directory - """ - h_file = open(f'{odir}/firmware/weights/{var.name}.h', 'w') - - # meta data - h_file.write(f'//Numpy array shape {var.shape}\n') - h_file.write(f'//Min {np.min(var.min):.12f}\n') - h_file.write(f'//Max {np.max(var.max):.12f}\n') - h_file.write(f'//Number of zeros {var.nzeros}\n') - h_file.write('\n') - - h_file.write(f'#ifndef {var.name.upper()}_H_\n') - h_file.write(f'#define {var.name.upper()}_H_\n') - h_file.write('\n') - - rf = int(layer.get_attr('reuse_factor', 1)) - weight_header = '#ifdef __INTELFPGA_COMPILER__\n' - - weight_size = 0 - if isinstance(layer, (Conv2D, Conv2DBatchnorm)): - weight_size = ( - layer.get_attr('impl_filt_height') - * layer.get_attr('impl_filt_width') - * layer.get_attr('n_filt') - * layer.get_attr('n_chan') - ) - elif isinstance(layer, (Conv1D)): - weight_size = layer.get_attr('impl_filt_width') * layer.get_attr('n_filt') * layer.get_attr('n_chan') - elif isinstance(layer, (Dense)): - weight_size = layer.get_attr('n_in') * layer.get_attr('n_out') - - if rf == 1 or var.name[0] == 'b' or weight_size <= 2048 or (var.name[0] == 'w' and var.type.precision.width < 3): - weight_header += 'hls_init_on_powerup\n' - else: - block_factor = (layer.get_attr('n_in') * layer.get_attr('n_out')) / rf - nbanks = int(2 ** np.ceil(np.log2(block_factor)) / 2) - var_width = int(np.ceil(var.type.precision.width / 8)) - bwidth = self.next_pow2(var_width) - weight_header += ( - f'hls_bankwidth({bwidth})\nhls_numbanks({nbanks})\nhls_max_replicates(1)\nhls_memory_impl("BLOCK_RAM")\n' - ) - weight_header += '#endif\n' - if var.storage.lower() == 'bram': - weight_header += 'static ' - else: - weight_header += 'static const ' - h_file.write(weight_header + var.definition_cpp() + ' = {') - - # fill c++ array. - # not including internal brackets for multidimensional case - sep = '' - for x in var: - h_file.write(sep + x) - sep = ', ' - h_file.write('};\n') - h_file.write('\n#endif\n') - h_file.close() - - def write_project_dir(self, model): - """Write the base project directory - - Args: - model (ModelGraph): the hls4ml model. - """ - if not os.path.isdir(f'{model.config.get_output_dir()}/firmware/weights'): - os.makedirs(f'{model.config.get_output_dir()}/firmware/weights') - - def write_project_cpp(self, model): - """Write the main architecture source file (myproject.cpp) - - Args: - model (ModelGraph): the hls4ml model. - """ - project_name = model.config.get_project_name() - - filedir = os.path.dirname(os.path.abspath(__file__)) - f = open(os.path.join(filedir, '../templates/quartus/firmware/myproject.cpp')) - fout = open(f'{model.config.get_output_dir()}/firmware/{project_name}.cpp', 'w') - - model_inputs = model.get_input_variables() - model_outputs = model.get_output_variables() - model_brams = [var for var in model.get_weight_variables() if var.storage.lower() == 'bram'] - - io_type = model.config.get_config_value('IOType') - indent = ' ' - brams_str = ', \n'.join([indent + b.definition_cpp(as_reference=False) for b in model_brams]) - - for line in f.readlines(): - # Add headers to weights and biases - if 'myproject' in line: - newline = line.replace('myproject', project_name) - - # Intel HLS 'streams' need to be passed by reference to top-level entity or declared as global variables - # Streams cannot be declared inside a function - # Therefore, layer connections (inputs/outputs) are declared here - elif '// hls-fpga-machine-learning insert inter-task streams' in line: - newline = line - if io_type == 'io_stream': - for layer in model.get_layers(): - vars = layer.get_variables() - for var in vars: - def_cpp = var.definition_cpp() - if def_cpp is not None: - newline += def_cpp + ';\n' - - # Instantiate GCC top-level function, to be used during GCC compilation / hls4ml.predict() - elif '// hls-fpga-machine-learning instantiate GCC top-level' in line: - newline = line - if io_type == 'io_stream': - newline += f'void {project_name}(\n' - for inp in model_inputs: - newline += indent + f'stream_in<{inp.type.name}> &{inp.name}_stream,\n' - for out in model_outputs: - newline += indent + f'stream_out<{out.type.name}> &{out.name}_stream,\n' - newline = newline[:-2] # Remove the tailing ',\n' - if model_brams: - newline += ',\n' + brams_str - newline += '\n) {\n' - if io_type == 'io_parallel': - newline = f'output_data {project_name}(\n' - newline += indent + 'input_data inputs' - if model_brams: - newline += ',\n' + brams_str - newline += '\n) {\n' - - # Instantiate HLS top-level function, to be used during HLS synthesis - elif '// hls-fpga-machine-learning instantiate HLS top-level' in line: - newline = line - if io_type == 'io_stream': - newline += f'component void {project_name}(\n' - for inp in model_inputs: - newline += indent + f'stream_in<{inp.type.name}> &{inp.name}_stream,\n' - for out in model_outputs: - newline += indent + f'stream_out<{out.type.name}> &{out.name}_stream,\n' - newline = newline[:-2] # Remove the tailing ',\n'\ - if model_brams: - newline += ',\n' + brams_str - newline += '\n) {\n' - if io_type == 'io_parallel': - newline += f'component output_data {project_name}(\n' - newline += indent + 'input_data inputs' - if model_brams: - newline += ',\n' + brams_str - newline += '\n) {\n' - - # Insert HLS pragmas such as maximum frequency, initiation interval etc. - elif '// hls-fpga-machine-learning insert cpragmas' in line: - newline = line - if io_type == 'io_parallel': - newline += 'hls_max_concurrency(0)\n' - newline += f'hls_component_ii({self.get_max_reuse_factor(model)})\n' - clock_mhz = 1000 / (model.config.get_config_value('ClockPeriod')) - newline += f'hls_scheduler_target_fmax_mhz({np.ceil(clock_mhz).astype(int)})\n' - - # In io_parallel, an output (struct) is returned from the top-level function - # Therefore, it needs to be initialised before returning - # In io_stream, the input is of type 'stream_in' and output is of type 'stream_out' - # However, individual layers accept the type 'stream' - # Therefore, data is first read from 'stream_in', written to 'stream' and propagated through network - elif '// hls-fpga-machine-learning initialize input/output' in line: - if io_type == 'io_stream': - newline = line - for inp in model_inputs: - newline += indent + f'for (size_t i = 0; i < {inp.size_cpp()} / {inp.type.name}::size; i++) {{\n' - newline += indent + f' {inp.type.name} tmp = {inp.name}_stream.read();\n' - newline += indent + f' {inp.name}.write(tmp);\n' - newline += indent + '}\n' - else: - newline = line - newline += indent + 'hls_register output_data outputs;\n' - - # Insert weights - elif '// hls-fpga-machine-learning insert weights' in line: - newline = line - for layer in model.get_layers(): - for w in layer.get_weights(): - if w not in model_brams: - newline += f'#include "weights/{w.name}.h"\n' - - # Insert test weights - elif '// hls-fpga-machine-learning insert test weights' in line: - newline = line - for layer in model.get_layers(): - for w in layer.get_weights(): - newline += f'#include "weights/{w.name}_test.h"\n' - - # Neural net instantiation - elif '// hls-fpga-machine-learning insert layers' in line: - newline = line + '\n' - model_inputs = model.get_input_variables() - model_outputs = model.get_output_variables() - for layer in model.get_layers(): - if io_type != 'io_stream': - vars = layer.get_variables() - for var in vars: - if var not in model_inputs and var not in model_outputs: - def_cpp = var.definition_cpp() - if def_cpp is not None: - newline += ' ' + def_cpp + ';\n' - func = layer.get_attr('function_cpp', None) - if func: - newline += ' ' + func + '\n' - if model.config.trace_output and layer.get_attr('trace', False): - newline += '#ifndef HLS_SYNTHESIS\n' - for var in vars: - newline += ' nnet::save_layer_output<{}>({}, "{}", {});\n'.format( - var.type.name, var.name, layer.name, var.size_cpp() - ) - newline += '#endif\n' - newline += '\n' - - # In io_parallel, a return is required; for more details see myproject.cpp & myproject.h - elif '// hls-fpga-machine-learning return' in line: - if io_type == 'io_stream': - newline = line - for out in model_outputs: - newline += indent + f'for (size_t i = 0; i < {out.size_cpp()} / {out.type.name}::size; i++) {{\n' - newline += indent + f' {out.type.name} tmp = {out.name}.read();\n' - newline += indent + f' {out.name}_stream.write(tmp);\n' - newline += indent + '}\n' - newline += '}\n' - else: - newline = line - newline += indent + 'return outputs;\n' - newline += '}\n' - - # Just copy line - else: - newline = line - - fout.write(newline) - - f.close() - fout.close() - - def write_project_header(self, model): - """Write the main architecture header file (myproject.h) - - Args: - model (ModelGraph): the hls4ml model. - """ - - project_name = model.config.get_project_name() - - filedir = os.path.dirname(os.path.abspath(__file__)) - f = open(os.path.join(filedir, '../templates/quartus/firmware/myproject.h')) - fout = open(f'{model.config.get_output_dir()}/firmware/{project_name}.h', 'w') - - model_inputs = model.get_input_variables() - model_outputs = model.get_output_variables() - model_brams = [var for var in model.get_weight_variables() if var.storage.lower() == 'bram'] - - # io_parallel and io_stream instantiate the top-level function differently - io_type = model.config.get_config_value('IOType') - indent = ' ' - brams_str = ', \n'.join([indent + b.definition_cpp(as_reference=False) for b in model_brams]) - - for line in f.readlines(): - if 'MYPROJECT' in line: - newline = line.replace('MYPROJECT', format(project_name.upper())) - - elif 'myproject' in line: - newline = line.replace('myproject', project_name) - - elif '// hls-fpga-machine-learning instantiate GCC top-level' in line: - newline = line - # For io_stream, input and output are passed by reference; see myproject.h & myproject.cpp for more details - - if io_type == 'io_stream': - newline += f'void {project_name}(\n' - for inp in model_inputs: - newline += indent + f'stream_in<{inp.type.name}> &{inp.name}_stream,\n' - for out in model_outputs: - newline += indent + f'stream_out<{out.type.name}> &{out.name}_stream,\n' - newline = newline[:-2] # Remove the tailing ',\n' - if model_brams: - newline += ',\n' + brams_str - newline += '\n);\n' - # In io_parallel, a struct is returned; see myproject.h & myproject.cpp for more details - else: - newline += f'output_data {project_name}(\n' - newline += indent + 'input_data inputs' - if model_brams: - newline += ',\n' + brams_str - newline += '\n);\n' - - # Similar to GCC instantiation, but with the keyword 'component' - elif '// hls-fpga-machine-learning instantiate HLS top-level' in line: - newline = line - if io_type == 'io_stream': - newline += f'component void {project_name}(\n' - for inp in model_inputs: - newline += indent + f'stream_in<{inp.type.name}> &{inp.name}_stream,\n' - for out in model_outputs: - newline += indent + f'stream_out<{out.type.name}> &{out.name}_stream,\n' - newline = newline[:-2] # Remove the tailing ',\n' - if model_brams: - newline += ',\n' + brams_str - newline += '\n);\n' - else: - newline += f'component output_data {project_name}(\n' - newline += indent + 'input_data inputs' - if model_brams: - newline += ',\n' + brams_str - newline += '\n);\n' - - elif '// hls-fpga-machine-learning insert cpragmas' in line: - newline = line - if io_type == 'io_parallel': - newline += 'hls_max_concurrency(0)\n' - newline += f'hls_component_ii({self.get_max_reuse_factor(model)})\n' - clock_mhz = 1000 / (model.config.get_config_value('ClockPeriod')) - newline += f'hls_scheduler_target_fmax_mhz({np.ceil(clock_mhz).astype(int)})\n' - - # For io_stream, no inputs/outputs are instantiated, as they are passed by reference - # For io_parallel, input/output structs are required - elif '// hls-fpga-machine-learning insert inputs' in line: - newline = line - if io_type != 'io_stream': - newline += 'struct input_data { \n' - for inp in model_inputs: - newline += indent + inp.definition_cpp() + ';\n' - newline += '};\n' - elif '// hls-fpga-machine-learning insert outputs' in line: - newline = line - if io_type != 'io_stream': - newline += 'struct output_data { \n' - for out in model_outputs: - newline += indent + out.definition_cpp() + ';\n' - newline += '};\n' - # Simply copy line, if no inserts are required - else: - newline = line - - fout.write(newline) - - f.close() - fout.close() - - def write_defines(self, model): - """Write the C++ type definitions file (defines.h) - - Args: - model (ModelGraph): the hls4ml model. - """ - filedir = os.path.dirname(os.path.abspath(__file__)) - f = open(os.path.join(filedir, '../templates/quartus/firmware/defines.h')) - fout = open(f'{model.config.get_output_dir()}/firmware/defines.h', 'w') - - for line in f.readlines(): - if '// hls-fpga-machine-learning insert layer-precision' in line: - newline = line - all_precision = OrderedDict() - for layer in model.get_layers(): - layer_precision = layer.get_layer_precision() - for type_name, type_var in layer_precision.items(): - # Ensure that layer's types doesn't override existing types - # This can happen in case of InplaceVariable types - if type_name not in all_precision: - all_precision[type_name] = type_var - for used_type in all_precision.values(): - newline += used_type.definition_cpp() - - else: - newline = line - fout.write(newline) - f.close() - fout.close() - - def write_parameters(self, model): - """Write the C++ layer config file (parameters.h) - - Args: - model (ModelGraph): the hls4ml model. - """ - filedir = os.path.dirname(os.path.abspath(__file__)) - f = open(os.path.join(filedir, '../templates/quartus/firmware/parameters.h')) - fout = open(f'{model.config.get_output_dir()}/firmware/parameters.h', 'w') - - for line in f.readlines(): - if '// hls-fpga-machine-learning insert includes' in line: - newline = line - for include in sorted(set(sum((layer.get_attr('include_header', []) for layer in model.get_layers()), []))): - newline += '#include "%s"\n' % include - - elif '// hls-fpga-machine-learning insert layer-config' in line: - newline = line - for layer in model.get_layers(): - config = layer.get_attr('config_cpp', None) - if config: - newline += config + '\n' - else: - newline = line - fout.write(newline) - f.close() - fout.close() - - def write_weights(self, model): - """Write the weights into header files - - Args: - model (ModelGraph): the hls4ml model. - """ - for layer in model.get_layers(): - for weights in layer.get_weights(): - self.print_array_to_cpp(weights, layer, model.config.get_output_dir()) - - def write_testbench_parallel(self, model): - """Write the testbench file for io_parallel (myproject_test.cpp and input/output .dat files) - - Args: - model (ModelGraph): the hls4ml model. - """ - if len(model.get_output_variables()) != 1: - print('WARNING: The testbench only supports one output variable. Leaving empty testbench') - return - - outvar = model.get_output_variables()[0] - - filedir = os.path.dirname(os.path.abspath(__file__)) - - if not os.path.exists(f'{model.config.get_output_dir()}/tb_data/'): - os.mkdir(f'{model.config.get_output_dir()}/tb_data/') - - input_data = model.config.get_config_value('InputData') - output_predictions = model.config.get_config_value('OutputPredictions') - - if input_data: - if input_data[-3:] == 'dat': - copyfile(input_data, f'{model.config.get_output_dir()}/tb_data/tb_input_features.dat') - else: - self.__make_dat_file(input_data, f'{model.config.get_output_dir()}/tb_data/tb_input_features.dat') - - if output_predictions: - if output_predictions[-3:] == 'dat': - copyfile(output_predictions, f'{model.config.get_output_dir()}/tb_data/tb_output_predictions.dat') - else: - self.__make_dat_file( - output_predictions, f'{model.config.get_output_dir()}/tb_data/tb_output_predictions.dat' - ) - - f = open(os.path.join(filedir, '../templates/quartus/myproject_test_parallel.cpp')) - fout = open(f'{model.config.get_output_dir()}/{model.config.get_project_name()}_test.cpp', 'w') - - model_brams = [var for var in model.get_weight_variables() if var.storage.lower() == 'bram'] - - for line in f.readlines(): - indent = ' ' * (len(line) - len(line.lstrip(' '))) - - if 'myproject' in line: - newline = line.replace('myproject', model.config.get_project_name()) - elif '// hls-fpga-machine-learning insert bram' in line: - newline = line - for bram in model_brams: - newline += f'#include "firmware/weights/{bram.name}.h"\n' - elif '// hls-fpga-machine-learning insert data' in line: - newline = line - newline += ' std::vector::const_iterator in_begin = in.cbegin();\n' - newline += ' std::vector::const_iterator in_end;\n' - newline += ' inputs.emplace_back();\n' - for inp in model.get_input_variables(): - newline += f' in_end = in_begin + ({inp.size_cpp()});\n' - newline += f' std::copy(in_begin, in_end, inputs.back().{inp.member_name});\n' - newline += ' in_begin = in_end;\n' - newline += ' outputs.emplace_back();\n' - elif '// hls-fpga-machine-learning insert zero' in line: - newline = line - newline += indent + 'for(int i = 0; i < num_iterations; i++) {\n' - for inp in model.get_input_variables(): - newline += indent + ' inputs.emplace_back();\n' - newline += indent + ' outputs.emplace_back();\n' - newline += indent + f' std::fill_n(inputs[i].{inp.member_name}, {inp.size_cpp()}, 0.0);\n' - newline += indent + '}\n' - - elif '// hls-fpga-machine-learning insert top-level-function' in line: - newline = line - newline += indent + 'for(int i = 0; i < num_iterations; i++) {\n' - newline += indent + f' ihc_hls_enqueue(&outputs[i], {model.config.get_project_name()}, inputs[i]' - if model_brams: - bram_vars = ','.join([b.name for b in model_brams]) - newline += f', {bram_vars});\n' - else: - newline += ');\n' - newline += indent + '}\n' - elif 'hls-fpga-machine-learning insert run' in line: - newline = line - newline += ' ' + f'ihc_hls_component_run_all({model.config.get_project_name()});\n' - elif '// hls-fpga-machine-learning insert predictions' in line: - newline = line - newline += indent + f'for(int i = 0; i < {outvar.size_cpp()}; i++) {{\n' - newline += indent + ' std::cout << predictions[j][i] << " ";\n' - newline += indent + '}\n' - newline += indent + 'std::cout << std::endl;\n' - elif '// hls-fpga-machine-learning insert tb-output' in line: - newline = line - newline += indent + f'for(int i = 0; i < {outvar.size_cpp()}; i++) {{\n' - newline += indent + f' fout << outputs[j].{outvar.member_name}[i] << " ";\n' - newline += indent + '}\n' - newline += indent + 'fout << std::endl;\n' - elif ( - '// hls-fpga-machine-learning insert output' in line - or '// hls-fpga-machine-learning insert quantized' in line - ): - newline = line - newline += indent + f'for(int i = 0; i < {outvar.size_cpp()}; i++) {{\n' - newline += indent + f' std::cout << outputs[j].{outvar.member_name}[i] << " ";\n' - newline += indent + '}\n' - newline += indent + 'std::cout << std::endl;\n' - else: - newline = line - - fout.write(newline) - - f.close() - fout.close() - - def write_testbench_stream(self, model): - """Write the testbench file for io_stream (myproject_test.cpp and input/output .dat files) - - Args: - model (ModelGraph): the hls4ml model. - """ - if len(model.get_output_variables()) != 1: - print('WARNING: The testbench only supports one output variable. Leaving empty testbench') - return - - outvar = model.get_output_variables()[0] - - model_inputs = model.get_input_variables() - model_outputs = model.get_output_variables() - - filedir = os.path.dirname(os.path.abspath(__file__)) - - if not os.path.exists(f'{model.config.get_output_dir()}/tb_data/'): - os.mkdir(f'{model.config.get_output_dir()}/tb_data/') - - input_data = model.config.get_config_value('InputData') - output_predictions = model.config.get_config_value('OutputPredictions') - - if input_data: - if input_data[-3:] == 'dat': - copyfile(input_data, f'{model.config.get_output_dir()}/tb_data/tb_input_features.dat') - else: - self.__make_dat_file(input_data, f'{model.config.get_output_dir()}/tb_data/tb_input_features.dat') - - if output_predictions: - if output_predictions[-3:] == 'dat': - copyfile(output_predictions, f'{model.config.get_output_dir()}/tb_data/tb_output_predictions.dat') - else: - self.__make_dat_file( - output_predictions, f'{model.config.get_output_dir()}/tb_data/tb_output_predictions.dat' - ) - - f = open(os.path.join(filedir, '../templates/quartus/myproject_test_stream.cpp')) - fout = open(f'{model.config.get_output_dir()}/{model.config.get_project_name()}_test.cpp', 'w') - - model_brams = [var for var in model.get_weight_variables() if var.storage.lower() == 'bram'] - - for line in f.readlines(): - indent = ' ' * (len(line) - len(line.lstrip(' '))) - - if 'myproject' in line: - newline = line.replace('myproject', model.config.get_project_name()) - - elif '// hls-fpga-machine-learning insert bram' in line: - newline = line - for bram in model_brams: - newline += f'#include "firmware/weights/{bram.name}.h"\n' - - elif '// hls-fpga-machine learning instantiate inputs and outputs' in line: - newline = line - for inp in model_inputs: - newline += indent + f'stream_in<{inp.type.name}> {inp.name}_input;\n' - for out in model_outputs: - newline += indent + f'stream_out<{out.type.name}> {out.name}_output;\n' - - # TODO - This is one-input specific (are multiple model inputs needed at all?) - elif '// hls-fpga-machine-learning insert data' in line: - newline = line - c = 0 - for inp in model_inputs: - newline += indent + f'float vals_{c}[{inp.size_cpp()}]; \n' - newline += indent + f'for (int j = 0 ; j < {inp.size_cpp()} ; j++) {{\n' - newline += indent + indent + f'vals_{c}[j] = in[j]; \n' - newline += indent + '}\n' - newline += ( - indent - + f'nnet::convert_data(vals_{c}, {inp.name}_input);\n' - ) - c += 1 - - elif '// hls-fpga-machine-learning insert zero' in line: - newline = line - c = 0 - for inp in model_inputs: - newline += indent + f'float vals_{c}[{inp.size_cpp()}]; \n' - newline += indent + f'for (int j = 0 ; j < {inp.size_cpp()} ; j++) {{\n' - newline += indent + indent + f'vals_{c}[j] = 0.0; \n' - newline += indent + '}\n' - newline += ( - indent - + f'nnet::convert_data(vals_{c}, {inp.name}_input);\n' - ) - c += 1 - - elif '// hls-fpga-machine-learning insert top-level-function' in line: - newline = line - input_params = ', '.join([f'{i.name}_input' for i in model_inputs]) - output_params = ', '.join([f'{o.name}_output' for o in model_outputs]) - newline += ( - indent + f'ihc_hls_enqueue_noret(&{model.config.get_project_name()}, {input_params}, {output_params}' - ) - if model_brams: - bram_vars = ','.join([b.name for b in model_brams]) - newline += f', {bram_vars});\n' - else: - newline += ');\n' - - elif 'hls-fpga-machine-learning insert run' in line: - newline = line - newline += indent + f'ihc_hls_component_run_all({model.config.get_project_name()});\n' - - elif '// hls-fpga-machine-learning convert output' in line: - newline = line - newline += indent + f'float res[{outvar.size_cpp()}];\n' - newline += indent + 'nnet::convert_data_back<{}, float, {}>({}_output, res);\n'.format( - outvar.type.name, outvar.size_cpp(), outvar.name - ) - - elif '// hls-fpga-machine-learning insert tb-output' in line: - newline += indent + f'for(int i = 0; i < {outvar.size_cpp()}; i++) {{\n' - newline += indent + ' fout << res[i] << " ";\n' - newline += indent + '}\n' - newline += indent + 'fout << std::endl;\n' - - elif '// hls-fpga-machine-learning print predictions' in line: - newline = line - newline += indent + f'for(int i = 0; i < {outvar.size_cpp()}; i++) {{\n' - newline += indent + ' std::cout << predictions[iteration][i] << " ";\n' - newline += indent + '}\n' - newline += indent + 'std::cout << std::endl;\n' - - elif '// hls-fpga-machine-learning print output' in line: - newline = line - newline += indent + f'for(int i = 0; i < {outvar.size_cpp()}; i++) {{\n' - newline += indent + ' std::cout << res[i] << " "; \n' - newline += indent + '} \n' - newline += indent + 'std::cout << std::endl; \n' - else: - newline = line - - fout.write(newline) - - f.close() - fout.close() - - def write_test_bench(self, model): - """Write the testbench - - Args: - model (ModelGraph): the hls4ml model. - """ - # TODO - This function only works with one model input - # (NOT one data point - it works as expected with multiple data points) - io_type = model.config.get_config_value('IOType') - if io_type == 'io_parallel': - self.write_testbench_parallel(model) - elif io_type == 'io_stream': - self.write_testbench_stream(model) - - def write_bridge(self, model): - """Write the Python-C++ bridge (myproject_bridge.cpp) - - Args: - model (ModelGraph): the hls4ml model. - """ - - filedir = os.path.dirname(os.path.abspath(__file__)) - f = open(os.path.join(filedir, '../templates/quartus/myproject_bridge.cpp')) - fout = open(f'{model.config.get_output_dir()}/{model.config.get_project_name()}_bridge.cpp', 'w') - - model_inputs = model.get_input_variables() - model_outputs = model.get_output_variables() - model_brams = [var for var in model.get_weight_variables() if var.storage.lower() == 'bram'] - - io_type = model.config.get_config_value('IOType') - indent = ' ' - - for line in f.readlines(): - if 'MYPROJECT' in line: - newline = line.replace('MYPROJECT', format(model.config.get_project_name().upper())) - - elif 'myproject' in line: - newline = line.replace('myproject', format(model.config.get_project_name())) - - elif '// hls-fpga-machine-learning insert bram' in line: - newline = line - for bram in model_brams: - newline += f'#include "firmware/weights/{bram.name}.h"\n' - - elif '// hls-fpga-machine-learning insert header' in line: - dtype = line.split('#', 1)[1].strip() - if io_type == 'io_stream': - inputs_str = ', '.join([f'{dtype} {i.name}[{i.size_cpp()}]' for i in model_inputs]) - outputs_str = ', '.join([f'{dtype} {o.name}[{o.size_cpp()}]' for o in model_outputs]) - else: - inputs_str = ', '.join([f'{dtype} {i.member_name}[{i.size_cpp()}]' for i in model_inputs]) - outputs_str = ', '.join([f'{dtype} {o.member_name}[{o.size_cpp()}]' for o in model_outputs]) - - insize_str = ', '.join([f'unsigned short &const_size_in_{i}' for i in range(1, len(model_inputs) + 1)]) - outsize_str = ', '.join([f'unsigned short &const_size_out_{o}' for o in range(1, len(model_outputs) + 1)]) - - newline = '' - newline += indent + inputs_str + ',\n' - newline += indent + outputs_str + ',\n' - newline += indent + insize_str + ',\n' - newline += indent + outsize_str + '\n' - - elif '// hls-fpga-machine-learning insert wrapper' in line: - bram_params = '' - if model_brams: - bram_params = ', ' + ','.join([b.name for b in model_brams]) - - dtype = line.split('#', 1)[1].strip() - if io_type == 'io_stream': - newline = '' - for i in model_inputs: - # Initialise stream object and store input data (C-array) to a 'stream' object - newline += indent + f'stream_in<{i.type.name}> {i.name}_input;\n' - newline += indent + 'nnet::convert_data<{}, {}, {}>({}, {}_input);\n'.format( - dtype, i.type.name, i.size_cpp(), i.name, i.name - ) - - # Initialise stream output - for o in model_outputs: - newline += '\n' - newline += indent + f'stream_out<{o.type.name}> {o.name}_output;\n' - - # Execute top-level function - input_params = ', '.join([f'{i.name}_input' for i in model_inputs]) - output_params = ', '.join([f'{o.name}_output' for o in model_outputs]) - - top_level = ( - indent + f'{model.config.get_project_name()}({input_params}, {output_params}{bram_params});\n' - ) - newline += top_level - newline += '\n' - - # Store data from 'stream' output to C-array, to be then returned and handled in Python - for o in model_outputs: - newline += indent + 'nnet::convert_data_back<{}, {}, {}>({}_output, {});\n'.format( - o.type.name, dtype, o.size_cpp(), o.name, o.name - ) - - else: - # Convert input data from C-array to HLS type - newline = '' - newline += indent + 'input_data inputs_ap;\n' - for i in model_inputs: - newline += indent + 'nnet::convert_data<{}, {}, {}>({}, inputs_ap.{});\n'.format( - dtype, i.type.name, i.size_cpp(), i.member_name, i.member_name - ) - newline += '\n' - - # Initialise HLS output - newline += indent + 'output_data outputs_ap;\n' - - # Execute top-level function - top_level = indent + f'outputs_ap = {model.config.get_project_name()}(inputs_ap{bram_params});\n' - newline += top_level - newline += '\n' - - # Convert HLS outputs back to C-array - for o in model_outputs: - newline += indent + 'nnet::convert_data_back<{}, {}, {}>(outputs_ap.{}, {});\n'.format( - o.type.name, dtype, o.size_cpp(), o.member_name, o.member_name - ) - elif '// hls-fpga-machine-learning insert trace_outputs' in line: - newline = '' - for layer in model.get_layers(): - func = layer.get_attr('function_cpp') - if func and model.config.trace_output and layer.get_attr('trace', False): - vars = layer.get_variables() - for var in vars: - newline += ( - indent - + 'nnet::trace_outputs->insert(std::pair(' - + f'"{layer.name}", (void *) malloc({var.size_cpp()} * element_size)));\n' - ) - - else: - newline = line - fout.write(newline) - - f.close() - fout.close() - - def write_build_script(self, model): - """Write the build scripts (Makefile, build_lib.sh) - - Args: - model (ModelGraph): the hls4ml model. - """ - - filedir = Path(__file__).parent - - # Makefile - makefile_src = (filedir / '../templates/quartus/Makefile').resolve() - makefile_dst = Path(f'{model.config.get_output_dir()}/Makefile').resolve() - with open(makefile_src) as src, open(makefile_dst, 'w') as dst: - for line in src.readlines(): - line = line.replace('myproject', model.config.get_project_name()) - - if 'DEVICE :=' in line: - line = 'DEVICE := {}\n'.format(model.config.get_config_value('Part')) - - dst.write(line) - - # build_lib.sh - build_lib_src = (filedir / '../templates/quartus/build_lib.sh').resolve() - build_lib_dst = Path(f'{model.config.get_output_dir()}/build_lib.sh').resolve() - with open(build_lib_src) as src, open(build_lib_dst, 'w') as dst: - for line in src.readlines(): - line = line.replace('myproject', model.config.get_project_name()) - line = line.replace('mystamp', model.config.get_config_value('Stamp')) - - dst.write(line) - build_lib_dst.chmod(build_lib_dst.stat().st_mode | stat.S_IEXEC) - - def write_nnet_utils(self, model): - """Copy the nnet_utils, AP types headers and any custom source to the project output directory - - Args: - model (ModelGraph): the hls4ml model. - """ - - # nnet_utils - filedir = os.path.dirname(os.path.abspath(__file__)) - - srcpath = os.path.join(filedir, '../templates/quartus/firmware/nnet_utils/') - dstpath = f'{model.config.get_output_dir()}/firmware/nnet_utils/' - - if not os.path.exists(dstpath): - os.mkdir(dstpath) - - headers = [os.path.basename(h) for h in glob.glob(srcpath + '*.h')] - - for h in headers: - copyfile(srcpath + h, dstpath + h) - - # ac_types - filedir = os.path.dirname(os.path.abspath(__file__)) - - srcpath = os.path.join(filedir, '../templates/quartus/ac_types/') - dstpath = f'{model.config.get_output_dir()}/firmware/ac_types/' - - if os.path.exists(dstpath): - rmtree(dstpath) - - copytree(srcpath, dstpath) - - # custom source - filedir = os.path.dirname(os.path.abspath(__file__)) - - custom_source = get_backend('Quartus').get_custom_source() - for dst, srcpath in custom_source.items(): - dstpath = f'{model.config.get_output_dir()}/firmware/{dst}' - copyfile(srcpath, dstpath) - - def __get_table_size(self, model, activation): - for layer in model.get_layers(): - if ( - layer.get_attr('activation') == activation or layer.get_attr('recurrent_activation') == activation - ) and layer.get_attr('table_size') is not None: - return int(layer.get_attr('table_size')) - return 1024 - - def __get_table_header(self, table_name, table_size): - table_header = '#ifdef __INTELFPGA_COMPILER__\n' - table_header += 'hls_init_on_powerup\n' - table_header += '#endif\n' - table_header += f'static const typename CONFIG_T::table_t {table_name}[{table_size}] = {{' - return table_header - - def __write_elu_table(self, model, path): - table_name = 'elu_table' - table_size = self.__get_table_size(model, 'elu') - - h_file = open(f'{path}/{table_name}.tb', 'w') - h_file.write(self.__get_table_header(table_name, table_size)) - - sep = '' - for i in range(table_size): - in_val = -8.0 * i / float(table_size) - real_val = np.exp(in_val) - 1.0 - h_file.write(sep + str(real_val)) - sep = ', ' - - h_file.write('};\n') - h_file.close() - - def __write_sigmoid_table(self, model, path): - MAX_VALUE = 8 - MIN_VALUE = 0 - - table_name = 'sigmoid_table' - table_size = self.__get_table_size(model, 'sigmoid') - - h_file = open(f'{path}/{table_name}.tb', 'w') - h_file.write(self.__get_table_header(table_name, table_size)) - - sep = '' - for i in range(int(table_size)): - in_val = ( - i * (MAX_VALUE - MIN_VALUE) / float(table_size) - + (MAX_VALUE - MIN_VALUE) / (float(table_size) * 2) - + MIN_VALUE - ) - real_val = 1.0 / (1 + np.exp(-in_val)) - if real_val >= 0.5: - h_file.write(sep + str(real_val)) - sep = ', ' - - h_file.write('};\n') - h_file.close() - - def __write_tanh_table(self, model, path): - MAX_VALUE = 4 - MIN_VALUE = 0 - - table_name = 'tanh_table' - table_size = self.__get_table_size(model, 'tanh') - - h_file = open(f'{path}/{table_name}.tb', 'w') - h_file.write(self.__get_table_header(table_name, table_size)) - - sep = '' - for i in range(table_size): - in_val = ( - i * (MAX_VALUE - MIN_VALUE) / float(table_size) - + (MAX_VALUE - MIN_VALUE) / (float(table_size) * 2) - + MIN_VALUE - ) - real_val = np.tanh(in_val) - if real_val >= 0: - h_file.write(sep + str(real_val)) - sep = ', ' - - h_file.write('};\n') - h_file.close() - - def __write_softplus_table(self, model, path): - table_name = 'softplus_table' - table_size = self.__get_table_size(model, 'softplus') - - h_file = open(f'{path}/{table_name}.tb', 'w') - h_file.write(self.__get_table_header(table_name, table_size)) - - sep = '' - for i in range(table_size): - in_val = 2 * 8.0 * (i - float(table_size) / 2.0) / float(table_size) - real_val = np.log(np.exp(in_val) + 1.0) - h_file.write(sep + str(real_val)) - sep = ', ' - - h_file.write('};\n') - h_file.close() - - def __write_softsign_table(self, model, path): - MAX_VALUE = 8 - MIN_VALUE = 0 - table_name = 'softsign_table' - table_size = self.__get_table_size(model, 'softsign') - - h_file = open(f'{path}/{table_name}.tb', 'w') - h_file.write(self.__get_table_header(table_name, table_size)) - - sep = '' - for i in range(table_size): - in_val = ( - i * (MAX_VALUE - MIN_VALUE) / float(table_size) - + (MAX_VALUE - MIN_VALUE) / (float(table_size) * 2) - + MIN_VALUE - ) - - real_val = in_val / (np.fabs(in_val) + 1.0) - if real_val >= 0: - h_file.write(sep + str(real_val)) - sep = ', ' - - h_file.write('};\n') - h_file.close() - - def __write_selu_table(self, model, path): - table_name = 'selu_table' - table_size = self.__get_table_size(model, 'selu') - - h_file = open(f'{path}/{table_name}.tb', 'w') - h_file.write(self.__get_table_header(table_name, table_size)) - - sep = '' - for i in range(table_size): - in_val = -8.0 * i / float(table_size) - real_val = 1.0507009873554804934193349852946 * (1.6732632423543772848170429916717 * (np.exp(in_val) - 1.0)) - h_file.write(sep + str(real_val)) - sep = ', ' - - h_file.write('};\n') - h_file.close() - - def __write_exp_table(self, model, path): - table_name = 'exp_table' - table_size = self.__get_table_size(model, 'softmax') - - h_file = open(f'{path}/{table_name}.tb', 'w') - h_file.write(self.__get_table_header(table_name, table_size)) - - # Default fixed point precision - # 6 bits for integer part, 10 bits for decimal - total, 16 - fp_bits = 16 - fp_integer = 6 - fp_signed = True - - # Exp table should use the same precision as exp_table, as seen in Vivado code - # init_exp_table(exp_table); - for layer in model.get_layers(): - if layer.name == 'softmax': - ac_type = layer.get_input_variable().type - if ac_type is not None: - try: - fp_bits = ac_type.precision.integer + ac_type.precision.fractional - fp_integer = ac_type.precision.integer - fp_signed = ac_type.precision.signed - except Exception: - # FixedPrecisionType wasn't correctly stored in layer attributes, use default values - pass - if fp_signed is False: - raise Exception('Softmax types need to be signed') - - sep = '' - N = ceil_log2(table_size) - for i in range(table_size): - f = FixedPointEmulator(fp_bits, fp_integer, signed=fp_signed) - b = uint_to_binary(i, N) - if i == 0: - b.insert(0, 0) - else: - b.insert(0, 1) - f.set_msb_bits(b) - real_val = f.exp_float() - h_file.write(sep + str(real_val)) - sep = ', ' - - h_file.write('};\n') - h_file.close() - - def __write_invert_table(self, model, path): - table_name = 'invert_table' - table_size = self.__get_table_size(model, 'softmax') - - h_file = open(f'{path}/{table_name}.tb', 'w') - h_file.write(self.__get_table_header(table_name, table_size)) - - # Default fixed point precision, in case values from layer attributes cannot be extracted - # 8 bits for integer part, 10 bits for decimal - total, 18 - fp_bits = 18 - fp_integer = 8 - fp_signed = True - - # Invert table should use the same precision as exp_table, as seen in Vivado code - # init_invert_table(invert_table); - for layer in model.get_layers(): - if layer.name == 'softmax': - ac_type = layer.get_attr('exp_table_t') - if ac_type is not None: - try: - fp_bits = ac_type.precision.integer + ac_type.precision.fractional - fp_integer = ac_type.precision.integer - fp_signed = ac_type.precision.signed - except Exception: - # FixedPrecisionType wasn't correctly stored in layer attributes, use default values - pass - if fp_signed is False: - raise Exception('Softmax types need to be signed') - - sep = '' - N = ceil_log2(table_size) - for i in range(table_size): - f = FixedPointEmulator(fp_bits, fp_integer, signed=fp_signed) - b = uint_to_binary(i, N) - b.insert(0, 0) - f.set_msb_bits(b) - real_val = f.inv_float() - h_file.write(sep + str(real_val)) - sep = ', ' - - h_file.write('};\n') - h_file.close() - - def __write_exp_table_latency(self, model, path): - table_name = 'exp_table_latency' - table_size = self.__get_table_size(model, 'softmax') - - h_file = open(f'{path}/{table_name}.tb', 'w') - h_file.write(self.__get_table_header(table_name, table_size)) - - # Default fixed point precision - # 6 bits for integer part, 10 bits for decimal - total, 16 - fp_bits = 16 - fp_integer = 6 - fp_signed = True - - # Exp table should use the same precision as exp_table, as seen in Vivado code - # init_exp_table(exp_table); - for layer in model.get_layers(): - if layer.name == 'softmax': - ac_type = layer.get_input_variable().type - if ac_type is not None: - try: - fp_bits = ac_type.precision.integer + ac_type.precision.fractional - fp_integer = ac_type.precision.integer - fp_signed = ac_type.precision.signed - except Exception: - # FixedPrecisionType wasn't correctly stored in layer attributes, use default values - pass - - sep = '' - N = ceil_log2(table_size) - for i in range(table_size): - f = FixedPointEmulator(fp_bits, fp_integer, signed=fp_signed) - f.set_msb_bits(uint_to_binary(i, N)) - real_val = f.exp_float() - h_file.write(sep + str(real_val)) - sep = ', ' - - h_file.write('};\n') - h_file.close() - - def __write_invert_table_latency(self, model, path): - table_name = 'invert_table_latency' - table_size = self.__get_table_size(model, 'softmax') - - h_file = open(f'{path}/{table_name}.tb', 'w') - h_file.write(self.__get_table_header(table_name, table_size)) - - # Default fixed point precision, in case values from layer attributes cannot be extracted - # 8 bits for integer part, 10 bits for decimal - total, 18 - fp_bits = 18 - fp_integer = 8 - fp_signed = True - - # Invert table should use the same precision as exp_table, as seen in Vivado code - # init_invert_table(invert_table); - for layer in model.get_layers(): - if layer.name == 'softmax': - ac_type = layer.get_attr('exp_table_t') - if ac_type is not None: - try: - fp_bits = ac_type.precision.integer + ac_type.precision.fractional - fp_integer = ac_type.precision.integer - fp_signed = ac_type.precision.signed - except Exception: - # FixedPrecisionType wasn't correctly stored in layer attributes, use default values - pass - - sep = '' - N = ceil_log2(table_size) - for i in range(table_size): - f = FixedPointEmulator(fp_bits, fp_integer, signed=fp_signed) - f.set_msb_bits(uint_to_binary(i, N)) - real_val = f.inv_float() - h_file.write(sep + str(real_val)) - sep = ', ' - - h_file.write('};\n') - h_file.close() - - def __write_exp_table_legacy(self, model, path): - table_name = 'exp_table_legacy' - table_size = self.__get_table_size(model, 'softmax') - - h_file = open(f'{path}/{table_name}.tb', 'w') - h_file.write(self.__get_table_header(table_name, table_size)) - - sep = '' - for i in range(table_size): - in_val = 2 * 8.0 * (i - float(table_size) / 2.0) / float(table_size) - real_val = np.exp(in_val) - h_file.write(sep + str(real_val)) - sep = ', ' - - h_file.write('};\n') - h_file.close() - - def __write_invert_table_legacy(self, model, path): - table_name = 'invert_table_legacy' - table_size = self.__get_table_size(model, 'softmax') - - h_file = open(f'{path}/{table_name}.tb', 'w') - h_file.write(self.__get_table_header(table_name, table_size)) - - sep = '' - for i in range(table_size): - real_val = 0 - in_val = 64.0 * i / float(table_size) - if in_val > 0.0: - real_val = 1.0 / in_val - h_file.write(sep + str(real_val)) - sep = ', ' - - h_file.write('};\n') - h_file.close() - - def write_activation_tables(self, model): - """Write the lookup tables for activation functions - - Args: - model (ModelGraph): the hls4ml model. - """ - # Output path - dstpath = f'{model.config.get_output_dir()}/firmware/nnet_utils/activation_tables' - if not os.path.exists(dstpath): - os.mkdir(dstpath) - - # Tables - # TODO - Only write tables needed by model, not all of them - self.__write_elu_table(model, dstpath) - self.__write_sigmoid_table(model, dstpath) - self.__write_tanh_table(model, dstpath) - self.__write_softplus_table(model, dstpath) - self.__write_softsign_table(model, dstpath) - self.__write_selu_table(model, dstpath) - self.__write_exp_table(model, dstpath) - self.__write_invert_table(model, dstpath) - self.__write_exp_table_latency(model, dstpath) - self.__write_invert_table_latency(model, dstpath) - self.__write_exp_table_legacy(model, dstpath) - self.__write_invert_table_legacy(model, dstpath) - - def write_yml(self, model): - """Write the config to the YAML file - - Args: - model (ModelGraph): the hls4ml model. - """ - - def keras_model_representer(dumper, keras_model): - model_path = model.config.get_output_dir() + '/keras_model.keras' - keras_model.save(model_path) - return dumper.represent_scalar('!keras_model', model_path) - - try: - import keras - - KerasModel = keras.models.Model - - yaml.add_multi_representer(KerasModel, keras_model_representer) - except Exception: - pass - - with open(model.config.get_output_dir() + '/' + config_filename, 'w') as file: - yaml.dump(model.config.config, file) - - def write_tar(self, model): - """Write the generated project as a .tar.gz archive - - Args: - model (ModelGraph): the hls4ml model. - """ - - if model.config.get_writer_config().get('WriteTar', False): - tar_path = model.config.get_output_dir() + '.tar.gz' - if os.path.exists(tar_path): - os.remove(tar_path) - with tarfile.open(model.config.get_output_dir() + '.tar.gz', mode='w:gz') as archive: - archive.add(model.config.get_output_dir(), recursive=True) - - def write_hls(self, model): - self.write_project_dir(model) - self.write_project_cpp(model) - self.write_project_header(model) - self.write_weights(model) - self.write_defines(model) - self.write_parameters(model) - self.write_test_bench(model) - self.write_bridge(model) - self.write_build_script(model) - self.write_nnet_utils(model) - self.write_activation_tables(model) - self.write_yml(model) - self.write_tar(model) diff --git a/pyproject.toml b/pyproject.toml index 7059836305..cc731ab703 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -53,7 +53,6 @@ optional-dependencies.qkeras = [ "tensorflow-model-optimization<=0.7.5", ] optional-dependencies.qkeras-v3 = [ "qkeras-v3" ] -optional-dependencies.quartus-report = [ "calmjs-parse", "tabulate" ] optional-dependencies.snn = [ "snntorch", "torch" ] optional-dependencies.sparsepixels = [ "sparsepixels>=0.3" ] optional-dependencies.sr = [ "sympy>=1.13.1" ] diff --git a/test/pytest/conftest.py b/test/pytest/conftest.py index 7f16c3c53a..e585bc69eb 100644 --- a/test/pytest/conftest.py +++ b/test/pytest/conftest.py @@ -59,14 +59,12 @@ def synthesis_config(): 'Vivado': os.getenv('VIVADO_VERSION', '2020.1'), 'Vitis': os.getenv('VITIS_VERSION', '2024.1'), 'VitisUnified': os.getenv('VITIS_VERSION', '2024.1'), - 'Quartus': os.getenv('QUARTUS_VERSION', 'latest'), 'Altera': os.getenv('ALTERA_VERSION', '2026.1.0'), }, 'build_args': { 'Vivado': {'csim': False, 'synth': True, 'export': False}, 'Vitis': {'csim': False, 'synth': True, 'export': False}, 'VitisUnified': {'csim': False, 'synth': True}, - 'Quartus': {'synth': True, 'fpgasynth': False}, 'Altera': {'build_type': 'report', 'run': False}, }, } diff --git a/test/pytest/synthesis_helpers.py b/test/pytest/synthesis_helpers.py index 27d6dd4829..fe9f388d2b 100644 --- a/test/pytest/synthesis_helpers.py +++ b/test/pytest/synthesis_helpers.py @@ -152,8 +152,8 @@ def run_synthesis_test(config, hls_model, baseline_file_name, backend): if not config.get('run_synthesis', False): return - # Skip Quartus and XLS backends - if backend in ['Quartus', 'XLS']: + # XLS does not produce a synthesis report for comparison. + if backend == 'XLS': return # Run synthesis diff --git a/test/pytest/test_activations.py b/test/pytest/test_activations.py index fe66c0dda5..643a1d30fe 100644 --- a/test/pytest/test_activations.py +++ b/test/pytest/test_activations.py @@ -13,7 +13,7 @@ # Variable 'name' is simply used as an identifier for the activation -@pytest.mark.parametrize('backend', ['Vivado', 'Vitis', 'Catapult', 'Quartus', 'Altera', 'XLS']) +@pytest.mark.parametrize('backend', ['Vivado', 'Vitis', 'Catapult', 'Altera', 'XLS']) @pytest.mark.parametrize('shape, io_type', [((8,), 'io_parallel'), ((8,), 'io_stream'), ((8, 8, 3), 'io_stream')]) @pytest.mark.parametrize( 'activation, name', diff --git a/test/pytest/test_auto_precision.py b/test/pytest/test_auto_precision.py index 284ca24f56..d75a36a42b 100644 --- a/test/pytest/test_auto_precision.py +++ b/test/pytest/test_auto_precision.py @@ -144,7 +144,7 @@ def keras_model_sepconv2d(backend): @pytest.mark.parametrize('io_type', ['io_stream', 'io_parallel']) -@pytest.mark.parametrize('backend', ['Vivado', 'Vitis', 'Quartus', 'XLS']) +@pytest.mark.parametrize('backend', ['Vivado', 'Vitis', 'XLS']) @pytest.mark.parametrize('model_type', ['conv1d', 'conv2d']) def test_auto_precision_conv( test_case_id, keras_model_conv1d, keras_model_conv2d, data_2d, data_3d, model_type, io_type, backend @@ -197,7 +197,7 @@ def test_auto_precision_conv( @pytest.mark.parametrize('io_type', ['io_stream']) # Until we implement SeparableConv1D/2D for io_parallel -@pytest.mark.parametrize('backend', ['Vivado', 'Vitis']) # No SeparableConv1D/2D in Quartus +@pytest.mark.parametrize('backend', ['Vivado', 'Vitis']) @pytest.mark.parametrize('model_type', ['sepconv1d', 'sepconv2d']) def test_auto_precision_sepconv( test_case_id, keras_model_sepconv1d, keras_model_sepconv2d, data_2d, data_3d, model_type, io_type, backend @@ -246,7 +246,7 @@ def test_auto_precision_sepconv( @pytest.mark.parametrize('io_type', ['io_stream', 'io_parallel']) -@pytest.mark.parametrize('backend', ['Vivado', 'Vitis', 'Quartus', 'XLS']) +@pytest.mark.parametrize('backend', ['Vivado', 'Vitis', 'XLS']) def test_auto_precision_dense(test_case_id, keras_model_dense, data_1d, io_type, backend): if backend == 'XLS' and io_type != 'io_parallel': pytest.skip(f'XLS backend only supports IOType: io_parallel, but got: {io_type}') diff --git a/test/pytest/test_backend_config.py b/test/pytest/test_backend_config.py index a57ce55f49..64a410daf7 100644 --- a/test/pytest/test_backend_config.py +++ b/test/pytest/test_backend_config.py @@ -1,6 +1,5 @@ from pathlib import Path -import numpy as np import pytest import tensorflow as tf import torch @@ -11,7 +10,7 @@ @pytest.mark.parametrize('framework', ['keras', 'pytorch']) -@pytest.mark.parametrize('backend', ['Vivado', 'Vitis', 'Quartus']) +@pytest.mark.parametrize('backend', ['Vivado', 'Vitis']) @pytest.mark.parametrize('part', ['some_part', None]) @pytest.mark.parametrize('clock_period', [8, None]) @pytest.mark.parametrize('clock_unc', ['15%', None]) @@ -64,10 +63,8 @@ def test_backend_config(test_case_id, framework, backend, part, clock_period, cl read_part = hls_model.config.get_config_value('Part') expected_part = part - if backend in ['Vivado', 'Vitis'] and part is None: + if part is None: expected_part = 'xcvu13p-flga2577-2-e' - elif backend == 'Quartus' and part is None: - expected_part = 'Arria10' assert read_part == expected_part read_clock_period = hls_model.config.get_config_value('ClockPeriod') @@ -80,42 +77,21 @@ def test_backend_config(test_case_id, framework, backend, part, clock_period, cl expected_unc = '12.5%' elif backend == 'Vitis' and clock_unc is None: expected_unc = '27%' - elif backend in ['Vivado', 'Vitis']: + else: expected_unc = clock_unc - elif backend == 'Quartus': - expected_unc = None assert read_clock_unc == expected_unc # Check if Writer properly wrote makefiles and tcl scripts - if backend in ['Vivado', 'Vitis']: - part_ok = period_ok = unc_ok = False - - prj_tcl_path = output_dir / 'project.tcl' - with open(prj_tcl_path) as f: - for line in f.readlines(): - if 'set part' in line and expected_part in line: - part_ok = True - if f'set clock_period {expected_period}' in line: - period_ok = True - if f'set clock_uncertainty {expected_unc}' in line: - unc_ok = True - - assert part_ok and period_ok and unc_ok - elif backend == 'Quartus': - part_ok = period_ok = False - - makefile_path = output_dir / 'Makefile' - with open(makefile_path) as f: - for line in f.readlines(): - if 'DEVICE :=' in line and expected_part in line: - part_ok = True - - main_cpp_path = output_dir / f'firmware/{hls_model.config.get_project_name()}.cpp' - with open(main_cpp_path) as f: - clock_mhz = 1000 / (hls_model.config.get_config_value('ClockPeriod')) - clock_mhz = np.ceil(clock_mhz).astype(int) - for line in f.readlines(): - if f'hls_scheduler_target_fmax_mhz({clock_mhz})' in line: - period_ok = True - - assert part_ok and period_ok + part_ok = period_ok = unc_ok = False + + prj_tcl_path = output_dir / 'project.tcl' + with open(prj_tcl_path) as f: + for line in f.readlines(): + if 'set part' in line and expected_part in line: + part_ok = True + if f'set clock_period {expected_period}' in line: + period_ok = True + if f'set clock_uncertainty {expected_unc}' in line: + unc_ok = True + + assert part_ok and period_ok and unc_ok diff --git a/test/pytest/test_batchnorm.py b/test/pytest/test_batchnorm.py index e4989f33d6..125aba97f2 100644 --- a/test/pytest/test_batchnorm.py +++ b/test/pytest/test_batchnorm.py @@ -29,7 +29,7 @@ def model(request): @pytest.mark.parametrize('io_type', ['io_parallel', 'io_stream']) -@pytest.mark.parametrize('backend', ['Vivado', 'Vitis', 'Quartus', 'Catapult', 'Altera']) +@pytest.mark.parametrize('backend', ['Vivado', 'Vitis', 'Catapult', 'Altera']) @pytest.mark.parametrize('model', [True, False], indirect=True) def test_batchnorm(test_case_id, model, data, backend, io_type): default_precision = 'fixed<32, 1>' diff --git a/test/pytest/test_batchnorm_pytorch.py b/test/pytest/test_batchnorm_pytorch.py index e1b9cf4597..1e53e961ea 100644 --- a/test/pytest/test_batchnorm_pytorch.py +++ b/test/pytest/test_batchnorm_pytorch.py @@ -30,17 +30,15 @@ def fusion_data(): @pytest.mark.parametrize('io_type', ['io_parallel', 'io_stream']) -@pytest.mark.parametrize('backend', ['Vivado', 'Vitis', 'Quartus', 'Catapult']) +@pytest.mark.parametrize('backend', ['Vivado', 'Vitis', 'Catapult']) def test_batchnorm(test_case_id, data, backend, io_type): model = nn.Sequential( nn.BatchNorm1d(in_shape), ).to() model.eval() - default_precision = 'ac_fixed<32, 1, true>' if backend == 'Quartus' else 'ac_fixed<32, 1>' - config = hls4ml.utils.config_from_pytorch_model( - model, (in_shape,), default_precision=default_precision, granularity='name', backend=backend + model, (in_shape,), default_precision='ac_fixed<32, 1>', granularity='name', backend=backend ) output_dir = str(test_root_path / test_case_id) hls_model = hls4ml.converters.convert_from_pytorch_model( @@ -79,7 +77,7 @@ def forward(self, x): @pytest.mark.parametrize('io_type', ['io_parallel', 'io_stream']) -@pytest.mark.parametrize('backend', ['Vivado', 'Vitis', 'Quartus', 'Catapult']) +@pytest.mark.parametrize('backend', ['Vivado', 'Vitis', 'Catapult']) def test_batchnorm_fusion(test_case_id, fusion_data, backend, io_type): n_in = 2 momentum = 0.99 diff --git a/test/pytest/test_binary_cnn.py b/test/pytest/test_binary_cnn.py index d5667ca4c2..904e025e61 100644 --- a/test/pytest/test_binary_cnn.py +++ b/test/pytest/test_binary_cnn.py @@ -15,8 +15,6 @@ @pytest.mark.parametrize( 'backend,io_type,strategy', [ - ('Quartus', 'io_parallel', 'resource'), - ('Quartus', 'io_stream', 'resource'), ('Vivado', 'io_parallel', 'resource'), ('Vivado', 'io_parallel', 'latency'), ('Vivado', 'io_stream', 'latency'), @@ -86,12 +84,6 @@ def test_binary_cnn(test_case_id, backend, io_type, strategy): hls_config['LayerName']['q_dense_6']['ReuseFactor'] = 2000 hls_config['LayerName']['q_dense_7']['ReuseFactor'] = 100 - if backend == 'Quartus' and io_type == 'io_parallel': - # Winegrad imp[lementation does not support binary - hls_config['LayerName']['conv2d_1']['Implementation'] = 'im2col' - hls_config['LayerName']['conv2d_2']['Implementation'] = 'im2col' - hls_config['LayerName']['conv2d_3']['Implementation'] = 'im2col' - output_dir = str(test_root_path / test_case_id) hls_model = hls4ml.converters.convert_from_keras_model( model2, diff --git a/test/pytest/test_bram_factor.py b/test/pytest/test_bram_factor.py index 17b937d917..6621ad22b1 100644 --- a/test/pytest/test_bram_factor.py +++ b/test/pytest/test_bram_factor.py @@ -10,7 +10,7 @@ test_root_path = Path(__file__).parent -@pytest.mark.parametrize('backend', ['Vivado', 'Quartus']) +@pytest.mark.parametrize('backend', ['Vivado']) @pytest.mark.parametrize('io_type', ['io_parallel', 'io_stream']) def test_bram_factor(test_case_id, backend, io_type): """A copy of the test_dense from test_keras_api.py with BramFactor set to 0""" diff --git a/test/pytest/test_causalpadding.py b/test/pytest/test_causalpadding.py index 49051c7c59..1eaac879ca 100644 --- a/test/pytest/test_causalpadding.py +++ b/test/pytest/test_causalpadding.py @@ -13,7 +13,7 @@ @pytest.mark.parametrize('io_type', ['io_stream', 'io_parallel']) -@pytest.mark.parametrize('backend', ['Vivado', 'Vitis', 'Quartus', 'XLS']) +@pytest.mark.parametrize('backend', ['Vivado', 'Vitis', 'XLS']) def test_causalpadding(test_case_id, io_type, backend): if backend == 'XLS' and io_type != 'io_parallel': pytest.skip(f'XLS backend only supports IOType: io_parallel, but got: {io_type}') diff --git a/test/pytest/test_clone_flatten.py b/test/pytest/test_clone_flatten.py index 65dfa45378..aec2c1d57c 100644 --- a/test/pytest/test_clone_flatten.py +++ b/test/pytest/test_clone_flatten.py @@ -47,9 +47,9 @@ def hls_model(keras_model, request, test_case_id): @pytest.mark.parametrize( 'hls_model', - [('io_stream', 'Vivado'), ('io_stream', 'Quartus')], + [('io_stream', 'Vivado')], indirect=True, - ids=['io_stream_Vivado', 'io_stream_Quartus'], + ids=['io_stream_Vivado'], ) def test_accuracy(data, keras_model, hls_model): X = data diff --git a/test/pytest/test_cnn_mnist_qkeras.py b/test/pytest/test_cnn_mnist_qkeras.py index b0031e3ce6..684892e1dd 100644 --- a/test/pytest/test_cnn_mnist_qkeras.py +++ b/test/pytest/test_cnn_mnist_qkeras.py @@ -60,8 +60,6 @@ def hls_model(mnist_model, request, test_case_id): @pytest.mark.parametrize( 'hls_model', [ - ('Quartus', 'io_parallel', 'resource'), - ('Quartus', 'io_stream', 'resource'), ('Vivado', 'io_parallel', 'resource'), ('Vivado', 'io_parallel', 'latency'), ('Vivado', 'io_stream', 'latency'), @@ -73,8 +71,6 @@ def hls_model(mnist_model, request, test_case_id): ], indirect=True, ids=[ - 'Quartus_io_parallel_resource', - 'Quartus_io_stream_resource', 'Vivado_io_parallel_resource', 'Vivado_io_parallel_latency', 'Vivado_io_stream_latency', diff --git a/test/pytest/test_conv1d.py b/test/pytest/test_conv1d.py index baed79be05..d6f1059e5e 100644 --- a/test/pytest/test_conv1d.py +++ b/test/pytest/test_conv1d.py @@ -66,8 +66,6 @@ def hls_model(keras_model, request, test_case_id): @pytest.mark.parametrize( 'hls_model', [ - ('Quartus', 'io_parallel', 'resource'), - ('Quartus', 'io_stream', 'resource'), ('Altera', 'io_parallel', 'resource'), ('Altera', 'io_stream', 'resource'), ('Vivado', 'io_parallel', 'resource'), @@ -83,8 +81,6 @@ def hls_model(keras_model, request, test_case_id): ], indirect=True, ids=[ - 'Quartus_io_parallel_resource', - 'Quartus_io_stream_resource', 'Altera_io_parallel_resource', 'Altera_io_stream_resource', 'Vivado_io_parallel_resource', diff --git a/test/pytest/test_embed.py b/test/pytest/test_embed.py index 2b332315ea..90cd4b869c 100644 --- a/test/pytest/test_embed.py +++ b/test/pytest/test_embed.py @@ -45,24 +45,20 @@ def hls_model(keras_model, request, test_case_id): [ ('Vivado', 'io_parallel'), ('Vitis', 'io_parallel'), - ('Quartus', 'io_parallel'), ('Catapult', 'io_parallel'), ('Altera', 'io_parallel'), ('Vivado', 'io_stream'), ('Vitis', 'io_stream'), - ('Quartus', 'io_stream'), ('Catapult', 'io_stream'), ('Altera', 'io_stream'), ], ids=[ 'vivado_parallel', 'vitis_parallel', - 'quartus_parallel', 'catapult_parallel', 'altera_parallel', 'vivado_stream', 'vitis_stream', - 'quartus_stream', 'catapult_stream', 'altera_stream', ], diff --git a/test/pytest/test_extensions.py b/test/pytest/test_extensions.py index b8b3624d62..7b732a7111 100644 --- a/test/pytest/test_extensions.py +++ b/test/pytest/test_extensions.py @@ -129,7 +129,7 @@ def register_custom_layer(): hls4ml.model.layers.register_layer('HReverse', HReverse) -@pytest.mark.parametrize('backend_id', ['Vivado', 'Vitis', 'Quartus']) +@pytest.mark.parametrize('backend_id', ['Vivado', 'Vitis']) def test_extensions(test_case_id, tmp_path, backend_id): # Register the optimization passes (if any) backend = hls4ml.backends.get_backend(backend_id) diff --git a/test/pytest/test_extensions_pytorch.py b/test/pytest/test_extensions_pytorch.py index 48af7fdf63..ab81ea6330 100644 --- a/test/pytest/test_extensions_pytorch.py +++ b/test/pytest/test_extensions_pytorch.py @@ -129,7 +129,7 @@ def register_custom_layer(): hls4ml.model.layers.register_layer('HReverseTorch', HReverseTorch) -@pytest.mark.parametrize('backend_id', ['Vivado', 'Vitis', 'Quartus']) +@pytest.mark.parametrize('backend_id', ['Vivado', 'Vitis']) def test_extensions_pytorch(test_case_id, tmp_path, backend_id): # Register the optimization passes (if any) backend = hls4ml.backends.get_backend(backend_id) diff --git a/test/pytest/test_fetch_example.py b/test/pytest/test_fetch_example.py index 8ffed127a5..6ef2808281 100644 --- a/test/pytest/test_fetch_example.py +++ b/test/pytest/test_fetch_example.py @@ -10,7 +10,7 @@ test_root_path = Path(__file__).parent -@pytest.mark.parametrize('backend', ['Vivado', 'Vitis', 'Quartus']) +@pytest.mark.parametrize('backend', ['Vivado', 'Vitis']) def test_fetch_example_utils(test_case_id, backend): f = io.StringIO() with redirect_stdout(f): diff --git a/test/pytest/test_globalpooling.py b/test/pytest/test_globalpooling.py index 94e72c3e44..658965f1b3 100644 --- a/test/pytest/test_globalpooling.py +++ b/test/pytest/test_globalpooling.py @@ -32,7 +32,7 @@ def keras_model_1d(request): return model, model_type, keepdims -@pytest.mark.parametrize('backend', ['Quartus', 'Vitis', 'Vivado', 'Catapult', 'Altera']) +@pytest.mark.parametrize('backend', ['Vitis', 'Vivado', 'Catapult', 'Altera']) @pytest.mark.parametrize( 'keras_model_1d', [ @@ -92,7 +92,7 @@ def keras_model_2d(request): return model, model_type, keepdims -@pytest.mark.parametrize('backend', ['Quartus', 'Vitis', 'Vivado', 'Catapult', 'Altera']) +@pytest.mark.parametrize('backend', ['Vitis', 'Vivado', 'Catapult', 'Altera']) @pytest.mark.parametrize( 'keras_model_2d', [ diff --git a/test/pytest/test_keras_api.py b/test/pytest/test_keras_api.py index 5371d6b48a..8bc7bb9ce1 100644 --- a/test/pytest/test_keras_api.py +++ b/test/pytest/test_keras_api.py @@ -27,7 +27,7 @@ test_root_path = Path(__file__).parent -@pytest.mark.parametrize('backend', ['Vivado', 'Vitis', 'Quartus', 'Altera', 'XLS']) +@pytest.mark.parametrize('backend', ['Vivado', 'Vitis', 'Altera', 'XLS']) @pytest.mark.parametrize('io_type', ['io_parallel', 'io_stream']) def test_dense(test_case_id, backend, io_type, synthesis_config): if backend == 'XLS' and io_type != 'io_parallel': @@ -98,7 +98,7 @@ def test_dense(test_case_id, backend, io_type, synthesis_config): ids=['relu', 'leaky_relu', 'elu', 'prelu', 'sigmoid'], ) # ThresholdedReLU(theta=1.0)]) -@pytest.mark.parametrize('backend', ['Vivado', 'Vitis', 'Quartus', 'Altera', 'XLS']) +@pytest.mark.parametrize('backend', ['Vivado', 'Vitis', 'Altera', 'XLS']) @pytest.mark.parametrize('io_type', ['io_parallel', 'io_stream']) def test_activations(test_case_id, activation_function, backend, io_type, synthesis_config): if backend == 'XLS' and io_type != 'io_parallel': @@ -140,7 +140,6 @@ def test_activations(test_case_id, activation_function, backend, io_type, synthe ('Vivado', 'Latency'), ('Vitis', 'Resource'), ('Vitis', 'Latency'), - ('Quartus', 'Resource'), ('Altera', 'Resource'), ('XLS', 'Latency'), ], @@ -236,7 +235,6 @@ def test_conv1d(test_case_id, padds, backend, strategy, io_type, synthesis_confi ('Vivado', 'Latency'), ('Vitis', 'Resource'), ('Vitis', 'Latency'), - ('Quartus', 'Resource'), ('Altera', 'Resource'), ('XLS', 'Latency'), ], @@ -432,7 +430,7 @@ def test_depthwise1d(test_case_id, backend, io_type, synthesis_config): ) @pytest.mark.parametrize('padds', padds_options) @pytest.mark.parametrize('chans', chans_options) -@pytest.mark.parametrize('backend', ['Vivado', 'Vitis', 'Quartus', 'Altera', 'XLS']) +@pytest.mark.parametrize('backend', ['Vivado', 'Vitis', 'Altera', 'XLS']) def test_pooling(test_case_id, pooling, padds, chans, backend, synthesis_config): assert '1D' in pooling.__name__ or '2D' in pooling.__name__ diff --git a/test/pytest/test_keras_h5_loader.py b/test/pytest/test_keras_h5_loader.py index 0fc09e24ac..bfbf6a1484 100644 --- a/test/pytest/test_keras_h5_loader.py +++ b/test/pytest/test_keras_h5_loader.py @@ -9,7 +9,7 @@ test_root_path = Path(__file__).parent -@pytest.mark.parametrize('backend', ['Vivado', 'Vitis', 'Quartus', 'Catapult']) +@pytest.mark.parametrize('backend', ['Vivado', 'Vitis', 'Catapult']) def test_keras_h5_loader(test_case_id, backend): input_shape = (10,) model = tf.keras.models.Sequential( diff --git a/test/pytest/test_keras_nested_model.py b/test/pytest/test_keras_nested_model.py index be187afc81..2a72b34505 100755 --- a/test/pytest/test_keras_nested_model.py +++ b/test/pytest/test_keras_nested_model.py @@ -126,7 +126,7 @@ def randX_20_15(): return randX(20, 15) -@pytest.mark.parametrize('backend', ['Vivado', 'Quartus', 'Catapult']) +@pytest.mark.parametrize('backend', ['Vivado', 'Catapult']) @pytest.mark.parametrize('io_type', ['io_parallel', 'io_stream']) def test_nested_model(test_case_id, randX_20_15, backend, io_type): n_in = 15 @@ -148,7 +148,7 @@ def test_nested_model(test_case_id, randX_20_15, backend, io_type): np.testing.assert_allclose(y_keras.ravel(), y_hls4ml.ravel(), rtol=1e-2, atol=0.02) -@pytest.mark.parametrize('backend', ['Vivado', 'Quartus', 'Catapult']) +@pytest.mark.parametrize('backend', ['Vivado', 'Catapult']) @pytest.mark.parametrize('io_type', ['io_parallel', 'io_stream']) def test_sub_nested_model(test_case_id, randX_20_15, backend, io_type): n_in = 15 diff --git a/test/pytest/test_keras_v3_api.py b/test/pytest/test_keras_v3_api.py index 6703520dec..ce47e5dd94 100644 --- a/test/pytest/test_keras_v3_api.py +++ b/test/pytest/test_keras_v3_api.py @@ -29,7 +29,7 @@ test_root_path = Path(__file__).parent -@pytest.mark.parametrize('backend', ['Vivado', 'Vitis', 'Quartus', 'Altera', 'Catapult', 'XLS']) +@pytest.mark.parametrize('backend', ['Vivado', 'Vitis', 'Altera', 'Catapult', 'XLS']) @pytest.mark.parametrize('io_type', ['io_parallel', 'io_stream']) def test_dense(test_case_id, backend, io_type): if backend == 'XLS' and io_type != 'io_parallel': @@ -93,7 +93,7 @@ def test_dense(test_case_id, backend, io_type): Activation(activation='sigmoid', name='sigmoid'), ], ) -@pytest.mark.parametrize('backend', ['Vivado', 'Vitis', 'Quartus', 'Altera', 'XLS']) +@pytest.mark.parametrize('backend', ['Vivado', 'Vitis', 'Altera', 'XLS']) @pytest.mark.parametrize('io_type', ['io_parallel', 'io_stream']) def test_activations(test_case_id, activation_function, backend, io_type): if backend == 'XLS' and io_type != 'io_parallel': @@ -129,7 +129,7 @@ def test_activations(test_case_id, activation_function, backend, io_type): @pytest.mark.parametrize('padds', padds_options) -@pytest.mark.parametrize('backend', ['Vivado', 'Vitis', 'Quartus', 'Altera', 'Catapult', 'XLS']) +@pytest.mark.parametrize('backend', ['Vivado', 'Vitis', 'Altera', 'Catapult', 'XLS']) @pytest.mark.parametrize('io_type', ['io_parallel', 'io_stream']) @pytest.mark.parametrize('activation', ['elu', 'relu']) def test_conv1d(test_case_id, padds, backend, io_type, activation): @@ -216,7 +216,7 @@ def test_conv1d(test_case_id, padds, backend, io_type, activation): @pytest.mark.parametrize('chans', chans_options) @pytest.mark.parametrize('padds', padds_options) -@pytest.mark.parametrize('backend', ['Vivado', 'Vitis', 'Quartus', 'Altera', 'Catapult', 'XLS']) +@pytest.mark.parametrize('backend', ['Vivado', 'Vitis', 'Altera', 'Catapult', 'XLS']) @pytest.mark.parametrize('io_type', ['io_parallel', 'io_stream']) def test_conv2d(test_case_id, chans, padds, backend, io_type): if backend == 'XLS' and io_type != 'io_parallel': @@ -383,7 +383,7 @@ def test_depthwise1d(test_case_id, backend, io_type): @pytest.mark.parametrize('pooling', pooling_layers) @pytest.mark.parametrize('padds', padds_options) @pytest.mark.parametrize('chans', chans_options) -@pytest.mark.parametrize('backend', ['Vivado', 'Vitis', 'Quartus', 'Altera', 'Catapult', 'XLS']) +@pytest.mark.parametrize('backend', ['Vivado', 'Vitis', 'Altera', 'Catapult', 'XLS']) def test_pooling(test_case_id, pooling, padds, chans, backend): assert '1D' in pooling.__name__ or '2D' in pooling.__name__ @@ -504,7 +504,7 @@ def test_pooling(test_case_id, pooling, padds, chans, backend): # assert hls_pool.attributes['pad_right'] == 0 -@pytest.mark.parametrize('backend', ['Vivado', 'Vitis', 'Quartus', 'Catapult', 'Altera', 'XLS']) +@pytest.mark.parametrize('backend', ['Vivado', 'Vitis', 'Catapult', 'Altera', 'XLS']) @pytest.mark.parametrize('io_type', ['io_parallel', 'io_stream']) def test_reused_layer(test_case_id, backend, io_type): if backend == 'XLS' and io_type != 'io_parallel': diff --git a/test/pytest/test_merge.py b/test/pytest/test_merge.py index 5af567147d..0a16088430 100644 --- a/test/pytest/test_merge.py +++ b/test/pytest/test_merge.py @@ -12,7 +12,7 @@ @pytest.mark.parametrize('merge_layer', [Add, Average, Maximum, Minimum, Multiply, Subtract]) @pytest.mark.parametrize('io_type', ['io_parallel', 'io_stream']) -@pytest.mark.parametrize('backend', ['Vivado', 'Vitis', 'Quartus', 'Altera', 'XLS']) +@pytest.mark.parametrize('backend', ['Vivado', 'Vitis', 'Altera', 'XLS']) @pytest.mark.parametrize('swap_inputs', [True, False]) def test_merge(test_case_id, merge_layer, io_type, backend, swap_inputs): if backend == 'XLS' and io_type != 'io_parallel': @@ -50,7 +50,7 @@ def test_merge(test_case_id, merge_layer, io_type, backend, swap_inputs): @pytest.mark.parametrize('axes', [1]) @pytest.mark.parametrize('io_type', ['io_parallel']) # No io_stream implementation yet -@pytest.mark.parametrize('backend', ['Vivado', 'Vitis', 'Quartus', 'Altera', 'XLS']) +@pytest.mark.parametrize('backend', ['Vivado', 'Vitis', 'Altera', 'XLS']) def test_dot(test_case_id, axes, io_type, backend): if backend == 'XLS' and io_type != 'io_parallel': pytest.skip(f'XLS backend only supports IOType: io_parallel, but got: {io_type}') @@ -82,7 +82,7 @@ def test_dot(test_case_id, axes, io_type, backend): @pytest.mark.parametrize('io_type', ['io_parallel', 'io_stream']) -@pytest.mark.parametrize('backend', ['Vivado', 'Vitis', 'Quartus', 'Altera', 'XLS']) +@pytest.mark.parametrize('backend', ['Vivado', 'Vitis', 'Altera', 'XLS']) def test_concatenate1d(test_case_id, io_type, backend): if backend == 'XLS' and io_type != 'io_parallel': pytest.skip(f'XLS backend only supports IOType: io_parallel, but got: {io_type}') @@ -115,7 +115,7 @@ def test_concatenate1d(test_case_id, io_type, backend): @pytest.mark.parametrize('axis', [1, 2]) @pytest.mark.parametrize('io_type', ['io_parallel', 'io_stream']) -@pytest.mark.parametrize('backend', ['Vivado', 'Vitis', 'Quartus', 'Altera', 'XLS']) +@pytest.mark.parametrize('backend', ['Vivado', 'Vitis', 'Altera', 'XLS']) def test_concatenate2d(test_case_id, axis, io_type, backend): if backend == 'XLS' and io_type != 'io_parallel': pytest.skip(f'XLS backend only supports IOType: io_parallel, but got: {io_type}') @@ -151,7 +151,7 @@ def test_concatenate2d(test_case_id, axis, io_type, backend): @pytest.mark.parametrize('axis', [1, 2, 3]) @pytest.mark.parametrize('io_type', ['io_parallel', 'io_stream']) -@pytest.mark.parametrize('backend', ['Vivado', 'Vitis', 'Quartus', 'Altera', 'XLS']) +@pytest.mark.parametrize('backend', ['Vivado', 'Vitis', 'Altera', 'XLS']) def test_concatenate3d(test_case_id, axis, io_type, backend): if backend == 'XLS' and io_type != 'io_parallel': pytest.skip(f'XLS backend only supports IOType: io_parallel, but got: {io_type}') diff --git a/test/pytest/test_merge_pytorch.py b/test/pytest/test_merge_pytorch.py index 850e04f329..64911d101a 100644 --- a/test/pytest/test_merge_pytorch.py +++ b/test/pytest/test_merge_pytorch.py @@ -31,7 +31,7 @@ def forward(self, x, y): @pytest.mark.parametrize('merge_op', ['cat', 'add', 'mul', 'sub', 'minimum', 'maximum']) @pytest.mark.parametrize('io_type', ['io_parallel', 'io_stream']) -@pytest.mark.parametrize('backend', ['Vivado', 'Vitis', 'Quartus']) +@pytest.mark.parametrize('backend', ['Vivado', 'Vitis']) def test_merge(test_case_id, merge_op, io_type, backend): input_shape = (3, 10, 10) diff --git a/test/pytest/test_multi_dense.py b/test/pytest/test_multi_dense.py index d712c0f52a..f0326bb13c 100644 --- a/test/pytest/test_multi_dense.py +++ b/test/pytest/test_multi_dense.py @@ -17,7 +17,6 @@ ('Vivado', 'Resource'), ('Vitis', 'Latency'), ('Vitis', 'Resource'), - ('Quartus', 'Resource'), ('Altera', 'Resource'), ('Catapult', 'Latency'), ('Catapult', 'Resource'), diff --git a/test/pytest/test_multiout_network.py b/test/pytest/test_multiout_network.py index f14d8a9d4d..0c3156f617 100644 --- a/test/pytest/test_multiout_network.py +++ b/test/pytest/test_multiout_network.py @@ -50,7 +50,7 @@ def data_corner_cases(): return X1, X2 -@pytest.mark.parametrize('backend', ['Vivado', 'Quartus', 'Vitis']) +@pytest.mark.parametrize('backend', ['Vivado', 'Vitis']) @pytest.mark.parametrize('io_type', ['io_parallel', 'io_stream']) def test_multi_output_nn(test_case_id, model, data, backend: str, io_type: str): output_dir = str(test_root_path / test_case_id) @@ -69,7 +69,7 @@ def test_multi_output_nn(test_case_id, model, data, backend: str, io_type: str): assert np.allclose(r_hls[1], r_keras[1], atol=1e-5, rtol=0) -@pytest.mark.parametrize('backend', ['Vivado', 'Quartus', 'Vitis', 'Catapult', 'Altera']) +@pytest.mark.parametrize('backend', ['Vivado', 'Vitis', 'Catapult', 'Altera']) @pytest.mark.parametrize('io_type', ['io_parallel', 'io_stream']) @pytest.mark.parametrize('strategy', ['latency', 'resource']) def test_multi_output_nn_corner_cases( diff --git a/test/pytest/test_pointwiseconv.py b/test/pytest/test_pointwiseconv.py index 68cb615e6f..fe50e2fc29 100644 --- a/test/pytest/test_pointwiseconv.py +++ b/test/pytest/test_pointwiseconv.py @@ -21,8 +21,6 @@ @pytest.mark.parametrize( 'backend, io_type, strategy, rf', [ - ('Quartus', 'io_parallel', 'resource', 1), - ('Quartus', 'io_stream', 'resource', 1), ('Altera', 'io_parallel', 'resource', 1), ('Altera', 'io_stream', 'resource', 1), ('Vivado', 'io_parallel', 'resource', 1), @@ -79,8 +77,7 @@ def test_pointwiseconv1d(test_case_id, chans, padds, strides, backend, io_type, hls_model.compile() hls_prediction = hls_model.predict(X_input).reshape(keras_prediction.shape) - if backend != 'XLS' and not (backend in ['Quartus', 'Altera'] and io_type == 'io_stream'): - # Quartus io_stream does not currently have a special pointwise implementation + if backend != 'XLS' and not (backend == 'Altera' and io_type == 'io_stream'): assert 'Pointwise' in list(hls_model.graph.values())[1].class_name np.testing.assert_allclose(hls_prediction, keras_prediction, rtol=0, atol=0.001) @@ -91,8 +88,6 @@ def test_pointwiseconv1d(test_case_id, chans, padds, strides, backend, io_type, @pytest.mark.parametrize( 'backend, io_type, strategy', [ - ('Quartus', 'io_parallel', 'resource'), - ('Quartus', 'io_stream', 'resource'), ('Altera', 'io_parallel', 'resource'), ('Altera', 'io_stream', 'resource'), ('Vivado', 'io_parallel', 'resource'), @@ -144,8 +139,7 @@ def test_pointwiseconv2d(test_case_id, chans, padds, strides, backend, io_type, hls_model.compile() hls_prediction = hls_model.predict(X_input).reshape(keras_prediction.shape) - if backend != 'XLS' and not (backend in ['Quartus', 'Altera'] and io_type == 'io_stream'): - # Quartus io_stream does not currently have a special pointwise implementation + if backend != 'XLS' and not (backend == 'Altera' and io_type == 'io_stream'): assert 'Pointwise' in list(hls_model.graph.values())[1].class_name np.testing.assert_allclose(hls_prediction, keras_prediction, rtol=0, atol=0.001) diff --git a/test/pytest/test_pooling.py b/test/pytest/test_pooling.py index af140d9d69..b4e26b2cc7 100644 --- a/test/pytest/test_pooling.py +++ b/test/pytest/test_pooling.py @@ -49,7 +49,7 @@ def keras_model_1d(request, backend): return model, model_type, pads, strides -@pytest.mark.parametrize('backend', ['Quartus', 'Vitis', 'Vivado', 'Catapult', 'Altera', 'XLS']) +@pytest.mark.parametrize('backend', ['Vitis', 'Vivado', 'Catapult', 'Altera', 'XLS']) @pytest.mark.parametrize( 'keras_model_1d', [ @@ -92,7 +92,7 @@ def test_pool1d(test_case_id, backend, keras_model_1d, data_1d, io_type): np.testing.assert_allclose(y_keras, y_hls, rtol=0, atol=atol, verbose=True) -@pytest.mark.parametrize('backend', ['Quartus', 'Vitis', 'Vivado', 'Altera']) +@pytest.mark.parametrize('backend', ['Vitis', 'Vivado', 'Altera']) @pytest.mark.parametrize( 'keras_model_1d', [ @@ -149,7 +149,7 @@ def keras_model_2d(request, backend): return model, model_type, pads, strides -@pytest.mark.parametrize('backend', ['Quartus', 'Vitis', 'Vivado', 'Catapult', 'Altera', 'XLS']) +@pytest.mark.parametrize('backend', ['Vitis', 'Vivado', 'Catapult', 'Altera', 'XLS']) @pytest.mark.parametrize( 'keras_model_2d', [ @@ -192,7 +192,7 @@ def test_pool2d(test_case_id, backend, keras_model_2d, data_2d, io_type): np.testing.assert_allclose(y_keras, y_hls, rtol=0, atol=atol, verbose=True) -@pytest.mark.parametrize('backend', ['Quartus', 'Vitis', 'Vivado', 'Altera']) +@pytest.mark.parametrize('backend', ['Vitis', 'Vivado', 'Altera']) @pytest.mark.parametrize( 'keras_model_2d', [ diff --git a/test/pytest/test_pytorch_api.py b/test/pytest/test_pytorch_api.py index e0c61ddfb5..38678f059c 100644 --- a/test/pytest/test_pytorch_api.py +++ b/test/pytest/test_pytorch_api.py @@ -22,7 +22,7 @@ def forward(self, x): return self.linear(x) -@pytest.mark.parametrize('backend', ['Vivado', 'Vitis', 'Quartus', 'Altera', 'XLS']) +@pytest.mark.parametrize('backend', ['Vivado', 'Vitis', 'Altera', 'XLS']) @pytest.mark.parametrize('io_type', ['io_parallel', 'io_stream']) def test_linear(test_case_id, backend, io_type): if backend == 'XLS' and io_type != 'io_parallel': @@ -76,7 +76,7 @@ def test_linear(test_case_id, backend, io_type): ], ids=['softmax', 'relu', 'tanh', 'leaky_relu', 'elu', 'prelu', 'sigmoid', 'threshold'], ) -@pytest.mark.parametrize('backend', ['Vivado', 'Vitis', 'Quartus', 'Altera', 'XLS']) +@pytest.mark.parametrize('backend', ['Vivado', 'Vitis', 'Altera', 'XLS']) @pytest.mark.parametrize('io_type', ['io_parallel', 'io_stream']) def test_activations(test_case_id, activation_function, backend, io_type): if backend == 'XLS' and io_type != 'io_parallel': @@ -191,7 +191,7 @@ def forward(self, x): ], ids=['softmax', 'relu', 'tanh', 'leaky_relu', 'elu', 'sigmoid', 'threshold'], ) -@pytest.mark.parametrize('backend', ['Vivado', 'Vitis', 'Quartus', 'Altera', 'XLS']) +@pytest.mark.parametrize('backend', ['Vivado', 'Vitis', 'Altera', 'XLS']) @pytest.mark.parametrize('io_type', ['io_parallel', 'io_stream']) def test_activation_functionals(test_case_id, activation_function, backend, io_type): if backend == 'XLS' and io_type != 'io_parallel': @@ -227,7 +227,7 @@ def test_activation_functionals(test_case_id, activation_function, backend, io_t @pytest.mark.parametrize('padds', padds_options) -@pytest.mark.parametrize('backend', ['Vivado', 'Vitis', 'Quartus', 'Altera', 'XLS']) +@pytest.mark.parametrize('backend', ['Vivado', 'Vitis', 'Altera', 'XLS']) @pytest.mark.parametrize('io_type', ['io_parallel', 'io_stream']) def test_conv1d(test_case_id, padds, backend, io_type): if backend == 'XLS' and io_type != 'io_parallel': @@ -336,7 +336,7 @@ def test_conv1d(test_case_id, padds, backend, io_type): @pytest.mark.parametrize('padds', padds_options) -@pytest.mark.parametrize('backend', ['Vivado', 'Vitis', 'Quartus', 'Altera', 'XLS']) +@pytest.mark.parametrize('backend', ['Vivado', 'Vitis', 'Altera', 'XLS']) @pytest.mark.parametrize('io_type', ['io_parallel', 'io_stream']) def test_conv2d(test_case_id, padds, backend, io_type): if backend == 'XLS' and io_type != 'io_parallel': @@ -493,7 +493,7 @@ def test_conv2d(test_case_id, padds, backend, io_type): @pytest.mark.parametrize('pooling', pooling_layers, ids=['MaxPool1d', 'MaxPool2d', 'AvgPool1d', 'AvgPool2d']) @pytest.mark.parametrize('padds', padds_options) -@pytest.mark.parametrize('backend', ['Vivado', 'Vitis', 'Quartus', 'Altera', 'XLS']) +@pytest.mark.parametrize('backend', ['Vivado', 'Vitis', 'Altera', 'XLS']) def test_pooling(test_case_id, pooling, padds, backend): assert '1d' in pooling.__name__ or '2d' in pooling.__name__ @@ -613,7 +613,7 @@ def forward(self, x): return self.bn(x) -@pytest.mark.parametrize('backend', ['Vivado', 'Vitis', 'Quartus', 'Altera', 'XLS']) +@pytest.mark.parametrize('backend', ['Vivado', 'Vitis', 'Altera', 'XLS']) @pytest.mark.parametrize('io_type', ['io_parallel', 'io_stream']) def test_bn(test_case_id, backend, io_type): if backend == 'XLS' and io_type != 'io_parallel': @@ -657,7 +657,7 @@ def forward(self, x): # TODO: this test fails for XLS due to PyTorch weights shape mismatch. -@pytest.mark.parametrize('backend', ['Vivado', 'Vitis', 'Quartus', 'Altera']) +@pytest.mark.parametrize('backend', ['Vivado', 'Vitis', 'Altera']) @pytest.mark.parametrize('io_type', ['io_parallel', 'io_stream']) def test_squeeze(test_case_id, backend, io_type): model = SqueezeModel() @@ -692,7 +692,7 @@ def test_squeeze(test_case_id, backend, io_type): assert list(hls_model.get_layers())[3].attributes['target_shape'] == [3] -@pytest.mark.parametrize('backend', ['Vivado', 'Vitis', 'Quartus', 'Altera', 'XLS']) +@pytest.mark.parametrize('backend', ['Vivado', 'Vitis', 'Altera', 'XLS']) def test_flatten(test_case_id, backend): input = torch.randn(1, 1, 5, 5) model = nn.Sequential(nn.Conv2d(1, 32, 5, 1, 1), nn.Flatten(), nn.ReLU()) @@ -736,7 +736,7 @@ def forward(self, x): return x -@pytest.mark.parametrize('backend', ['Vivado', 'Vitis', 'Quartus', 'Altera', 'XLS']) +@pytest.mark.parametrize('backend', ['Vivado', 'Vitis', 'Altera', 'XLS']) @pytest.mark.parametrize('io_type', ['io_parallel', 'io_stream']) def test_skipped_layers(test_case_id, backend, io_type): if backend == 'XLS' and io_type != 'io_parallel': @@ -771,7 +771,7 @@ def test_skipped_layers(test_case_id, backend, io_type): np.testing.assert_allclose(hls_prediction, pytorch_prediction, rtol=0, atol=5e-2) -@pytest.mark.parametrize('backend', ['Vivado', 'Vitis', 'Quartus', 'Altera', 'XLS']) +@pytest.mark.parametrize('backend', ['Vivado', 'Vitis', 'Altera', 'XLS']) @pytest.mark.parametrize('io_type', ['io_parallel']) # Only io_parallel for now @pytest.mark.parametrize('tensor_rank', [2, 3]) def test_remove_transpose(test_case_id, backend, io_type, tensor_rank): @@ -838,7 +838,7 @@ def forward(self, x): np.testing.assert_allclose(hls_prediction, pytorch_prediction, rtol=0, atol=5e-2) -@pytest.mark.parametrize('backend', ['Vivado', 'Vitis', 'Quartus', 'Altera', 'XLS']) +@pytest.mark.parametrize('backend', ['Vivado', 'Vitis', 'Altera', 'XLS']) @pytest.mark.parametrize('io_type', ['io_parallel', 'io_stream']) def test_view(test_case_id, backend, io_type): if backend == 'XLS' and io_type != 'io_parallel': diff --git a/test/pytest/test_qkeras.py b/test/pytest/test_qkeras.py index e726801414..a93f75d576 100644 --- a/test/pytest/test_qkeras.py +++ b/test/pytest/test_qkeras.py @@ -69,7 +69,6 @@ def load_jettagging_model(): return model -# TODO - Parameterize for Quartus (different strategies?) @pytest.fixture def convert(load_jettagging_model, request, test_case_id): """ @@ -136,7 +135,7 @@ def randX_100_16(): # https://github.com/fastmachinelearning/hls4ml/issues/381 # @pytest.mark.parametrize('bits', [4, 6, 8]) @pytest.mark.parametrize('bits,alpha', [(4, 1), (4, 'auto_po2')]) -@pytest.mark.parametrize('backend', ['Vivado', 'Vitis', 'Quartus', 'Altera', 'XLS']) +@pytest.mark.parametrize('backend', ['Vivado', 'Vitis', 'Altera', 'XLS']) @pytest.mark.parametrize('io_type', ['io_parallel', 'io_stream']) def test_single_dense_activation_exact(test_case_id, randX_100_16, bits, alpha, backend, io_type): """ @@ -201,7 +200,7 @@ def randX_100_10(): @pytest.mark.parametrize( 'quantizer', [(quantized_tanh(8)), (quantized_sigmoid(5)), (quantized_sigmoid(7, use_real_sigmoid=True))] ) -@pytest.mark.parametrize('backend', ['Vivado', 'Quartus', 'Altera']) +@pytest.mark.parametrize('backend', ['Vivado', 'Altera']) @pytest.mark.parametrize('io_type', ['io_parallel', 'io_stream']) def test_quantizer_special(test_case_id, randX_1000_1, quantizer, backend, io_type): """ @@ -240,7 +239,7 @@ def test_quantizer_special(test_case_id, randX_1000_1, quantizer, backend, io_ty (7, 10, binary(), quantized_bits(5, 2), binary(), False, True), ], ) -@pytest.mark.parametrize('backend', ['Vivado', 'Vitis', 'Quartus', 'Altera']) +@pytest.mark.parametrize('backend', ['Vivado', 'Vitis', 'Altera']) @pytest.mark.parametrize('io_type', ['io_parallel', 'io_stream']) def test_btnn(test_case_id, make_btnn, randX_100_10, backend, io_type): model, is_xnor, test_no = make_btnn @@ -283,7 +282,7 @@ def randX_1000_1(): (quantized_relu(10, 5)), ], ) -@pytest.mark.parametrize('backend', ['Vivado', 'Vitis', 'Quartus', 'Altera']) +@pytest.mark.parametrize('backend', ['Vivado', 'Vitis', 'Altera']) @pytest.mark.parametrize('io_type', ['io_parallel', 'io_stream']) def test_quantizer(test_case_id, randX_1000_1, quantizer, backend, io_type): """ @@ -318,7 +317,7 @@ def test_quantizer(test_case_id, randX_1000_1, quantizer, backend, io_type): (quantized_relu(10, 2, negative_slope=0.25)), ], ) -@pytest.mark.parametrize('backend', ['Vivado', 'Vitis', 'Quartus', 'Altera']) +@pytest.mark.parametrize('backend', ['Vivado', 'Vitis', 'Altera']) @pytest.mark.parametrize('io_type', ['io_parallel', 'io_stream']) def test_relu_negative_slope(test_case_id, randX_1000_1, quantizer, backend, io_type): """ @@ -399,7 +398,7 @@ def test_qactivation_kwarg(test_case_id, randX_100_10, activation_quantizer, wei assert sum(wrong) / len(wrong) <= 0.005 -ALL_BACKENDS = ['Vivado', 'Vitis', 'Quartus', 'Altera'] +ALL_BACKENDS = ['Vivado', 'Vitis', 'Altera'] RNN_LAYERS = ('QSimpleRNN', 'QLSTM', 'QGRU') # Q layers built without quantizers: name -> (input shape, layer factory, supported backends) @@ -414,11 +413,11 @@ def test_qactivation_kwarg(test_case_id, randX_100_10, activation_quantizer, wei 'QDepthwiseConv2D': ((8, 8, 3), lambda: QDepthwiseConv2D((3, 3)), ['Vivado', 'Vitis']), 'QSeparableConv1D': ((8, 3), lambda: QSeparableConv1D(4, 3), ['Vivado', 'Vitis']), 'QSeparableConv2D': ((8, 8, 3), lambda: QSeparableConv2D(4, (3, 3)), ['Vivado', 'Vitis']), - 'QSimpleRNN': ((5, 3), lambda: QSimpleRNN(4, activation='relu'), ['Quartus', 'Altera']), + 'QSimpleRNN': ((5, 3), lambda: QSimpleRNN(4, activation='relu'), ['Altera']), 'QLSTM': ( (5, 3), lambda: QLSTM(4, activation='tanh', recurrent_activation='sigmoid'), - ['Vivado', 'Quartus', 'Altera'], + ['Vivado', 'Altera'], ), 'QGRU': ( (5, 3), @@ -431,7 +430,7 @@ def test_qactivation_kwarg(test_case_id, randX_100_10, activation_quantizer, wei reset_after=True, recurrent_quantizer='quantized_bits(24, 7, alpha=1)', ), - ['Vivado', 'Quartus', 'Altera'], + ['Vivado', 'Altera'], ), 'QBatchNormalization': ( (10,), @@ -482,7 +481,7 @@ def randX_100_8_8_1(): return np.random.rand(100, 8, 8, 1) -@pytest.mark.parametrize('backend', ['Vivado', 'Vitis', 'Quartus', 'Altera']) +@pytest.mark.parametrize('backend', ['Vivado', 'Vitis', 'Altera']) @pytest.mark.parametrize('io_type', ['io_parallel', 'io_stream']) def test_qconv2dbn(test_case_id, randX_100_8_8_1, backend, io_type): """ @@ -520,7 +519,7 @@ def test_qconv2dbn(test_case_id, randX_100_8_8_1, backend, io_type): np.testing.assert_array_equal(y_qkeras, y_hls4ml.reshape(y_qkeras.shape)) -@pytest.mark.parametrize('backend', ['Vivado', 'Vitis', 'Quartus', 'Altera']) +@pytest.mark.parametrize('backend', ['Vivado', 'Vitis', 'Altera']) @pytest.mark.parametrize('io_type', ['io_parallel', 'io_stream']) def test_qconv2dbn_no_quantizer(test_case_id, randX_100_8_8_1, backend, io_type): """ @@ -607,7 +606,7 @@ def test_qdepthwiseconv2d(test_case_id, randX_10_32_32_3, backend, io_type): np.testing.assert_allclose(y_qkeras, y_hls4ml.reshape(y_qkeras.shape), rtol=1e-2, atol=0.01) -@pytest.mark.parametrize('backend', ['Vivado', 'Vitis', 'Quartus', 'Altera']) +@pytest.mark.parametrize('backend', ['Vivado', 'Vitis', 'Altera']) @pytest.mark.parametrize('io_type', ['io_parallel', 'io_stream']) @pytest.mark.parametrize('strategy', ['Latency', 'Resource']) def test_quantised_po2_bit_width(test_case_id, backend, io_type, strategy): @@ -645,7 +644,7 @@ def test_quantised_po2_bit_width(test_case_id, backend, io_type, strategy): np.testing.assert_allclose(y_hls.flatten(), y_keras.flatten(), rtol=2e-2) -@pytest.mark.parametrize('backend', ['Quartus', 'Altera']) +@pytest.mark.parametrize('backend', ['Altera']) def test_qsimplernn(test_case_id, backend): """ Test proper handling of QSimpleRNN. @@ -680,7 +679,7 @@ def test_qsimplernn(test_case_id, backend): np.testing.assert_allclose(y_qkeras, y_hls4ml.reshape(y_qkeras.shape), atol=0.1) -@pytest.mark.parametrize('backend', ['Vivado', 'Quartus', 'Altera']) +@pytest.mark.parametrize('backend', ['Vivado', 'Altera']) def test_qlstm(test_case_id, backend): """ Test proper handling of QLSTM. @@ -716,7 +715,7 @@ def test_qlstm(test_case_id, backend): np.testing.assert_allclose(y_qkeras, y_hls4ml.reshape(y_qkeras.shape), atol=0.1) -@pytest.mark.parametrize('backend', ['Vivado', 'Quartus', 'Altera']) +@pytest.mark.parametrize('backend', ['Vivado', 'Altera']) def test_qgru(test_case_id, backend): """ Test proper handling of QGRU. diff --git a/test/pytest/test_qkerasV3.py b/test/pytest/test_qkerasV3.py index 82472949ca..ff7c27b048 100644 --- a/test/pytest/test_qkerasV3.py +++ b/test/pytest/test_qkerasV3.py @@ -81,7 +81,6 @@ def load_jettagging_model(): return model -# TODO - Parameterize for Quartus (different strategies?) @pytest.fixture def convert(load_jettagging_model, request, test_case_id): """ @@ -148,7 +147,7 @@ def randX_100_16(): # https://github.com/fastmachinelearning/hls4ml/issues/381 # @pytest.mark.parametrize('bits', [4, 6, 8]) @pytest.mark.parametrize('bits,alpha', [(4, 1), (4, 'auto_po2')]) -@pytest.mark.parametrize('backend', ['Vivado', 'Vitis', 'Quartus', 'Altera']) +@pytest.mark.parametrize('backend', ['Vivado', 'Vitis', 'Altera']) @pytest.mark.parametrize('io_type', ['io_parallel', 'io_stream']) def test_single_dense_activation_exact(test_case_id, randX_100_16, bits, alpha, backend, io_type): """ @@ -211,7 +210,7 @@ def randX_100_10(): @pytest.mark.parametrize( 'quantizer', [(quantized_tanh(8)), (quantized_sigmoid(5)), (quantized_sigmoid(7, use_real_sigmoid=True))] ) -@pytest.mark.parametrize('backend', ['Vivado', 'Quartus', 'Altera']) +@pytest.mark.parametrize('backend', ['Vivado', 'Altera']) @pytest.mark.parametrize('io_type', ['io_parallel', 'io_stream']) def test_quantizer_special(test_case_id, randX_1000_1, quantizer, backend, io_type): """ @@ -250,7 +249,7 @@ def test_quantizer_special(test_case_id, randX_1000_1, quantizer, backend, io_ty (7, 10, binary(), quantized_bits(5, 2), binary(), False, True), ], ) -@pytest.mark.parametrize('backend', ['Vivado', 'Vitis', 'Quartus', 'Altera']) +@pytest.mark.parametrize('backend', ['Vivado', 'Vitis', 'Altera']) @pytest.mark.parametrize('io_type', ['io_parallel', 'io_stream']) def test_btnn(test_case_id, make_btnn, randX_100_10, backend, io_type): model, is_xnor, test_no = make_btnn @@ -293,7 +292,7 @@ def randX_1000_1(): (quantized_relu(10, 5)), ], ) -@pytest.mark.parametrize('backend', ['Vivado', 'Vitis', 'Quartus', 'Altera']) +@pytest.mark.parametrize('backend', ['Vivado', 'Vitis', 'Altera']) @pytest.mark.parametrize('io_type', ['io_parallel', 'io_stream']) def test_quantizer(test_case_id, randX_1000_1, quantizer, backend, io_type): """ @@ -328,7 +327,7 @@ def test_quantizer(test_case_id, randX_1000_1, quantizer, backend, io_type): (quantized_relu(10, 2, negative_slope=0.25)), ], ) -@pytest.mark.parametrize('backend', ['Vivado', 'Vitis', 'Quartus', 'Altera']) +@pytest.mark.parametrize('backend', ['Vivado', 'Vitis', 'Altera']) @pytest.mark.parametrize('io_type', ['io_parallel', 'io_stream']) def test_relu_negative_slope(test_case_id, randX_1000_1, quantizer, backend, io_type): """ @@ -440,7 +439,7 @@ def test_qkeras_einsum_dense(test_case_id, randX_100_10, backend, io_type): np.testing.assert_array_equal(y_qkeras, y_hls4ml.reshape(y_qkeras.shape)) -@pytest.mark.parametrize('backend', ['Vivado', 'Vitis', 'Quartus', 'Altera']) +@pytest.mark.parametrize('backend', ['Vivado', 'Vitis', 'Altera']) @pytest.mark.parametrize('io_type', ['io_parallel', 'io_stream']) def test_quantizer_parsing(test_case_id, randX_100_10, backend, io_type): X = randX_100_10 @@ -479,7 +478,7 @@ def randX_100_8_8_1(): return np.random.rand(100, 8, 8, 1) -@pytest.mark.parametrize('backend', ['Vivado', 'Vitis', 'Quartus', 'Altera']) +@pytest.mark.parametrize('backend', ['Vivado', 'Vitis', 'Altera']) @pytest.mark.parametrize('io_type', ['io_parallel', 'io_stream']) @pytest.mark.parametrize( 'qconv_layer,input_shape,input_data_shape,layer_kwargs', @@ -531,7 +530,7 @@ def test_qconv_activation_kwarg(test_case_id, qconv_layer, input_shape, input_da np.testing.assert_array_equal(y_qkeras, y_hls4ml.reshape(y_qkeras.shape)) -@pytest.mark.parametrize('backend', ['Vivado', 'Vitis', 'Quartus', 'Altera']) +@pytest.mark.parametrize('backend', ['Vivado', 'Vitis', 'Altera']) @pytest.mark.parametrize('io_type', ['io_parallel', 'io_stream']) def test_qconv2dbn(test_case_id, randX_100_8_8_1, backend, io_type): """ @@ -621,7 +620,7 @@ def test_qdepthwiseconv2d(test_case_id, randX_10_32_32_3, backend, io_type, quan np.testing.assert_allclose(y_qkeras, y_hls4ml.reshape(y_qkeras.shape), rtol=1e-2, atol=0.01) -@pytest.mark.parametrize('backend', ['Vivado', 'Vitis', 'Quartus', 'Altera']) +@pytest.mark.parametrize('backend', ['Vivado', 'Vitis', 'Altera']) @pytest.mark.parametrize('io_type', ['io_parallel', 'io_stream']) @pytest.mark.parametrize('strategy', ['Latency', 'Resource']) def test_quantised_po2_bit_width(test_case_id, backend, io_type, strategy): @@ -659,7 +658,7 @@ def test_quantised_po2_bit_width(test_case_id, backend, io_type, strategy): np.testing.assert_allclose(y_hls.flatten(), y_keras.flatten(), rtol=2e-2) -@pytest.mark.parametrize('backend', ['Quartus', 'Altera']) +@pytest.mark.parametrize('backend', ['Altera']) def test_qsimplernn(test_case_id, backend): """ Test proper handling of QSimpleRNN. @@ -694,7 +693,7 @@ def test_qsimplernn(test_case_id, backend): np.testing.assert_allclose(y_qkeras, y_hls4ml.reshape(y_qkeras.shape), atol=0.1) -@pytest.mark.parametrize('backend', ['Vivado', 'Quartus', 'Altera']) +@pytest.mark.parametrize('backend', ['Vivado', 'Altera']) def test_qlstm(test_case_id, backend): """ Test proper handling of QLSTM. @@ -730,7 +729,7 @@ def test_qlstm(test_case_id, backend): np.testing.assert_allclose(y_qkeras, y_hls4ml.reshape(y_qkeras.shape), atol=0.1) -@pytest.mark.parametrize('backend', ['Vivado', 'Quartus', 'Altera']) +@pytest.mark.parametrize('backend', ['Vivado', 'Altera']) def test_qgru(test_case_id, backend): """ Test proper handling of QGRU. diff --git a/test/pytest/test_qonnx.py b/test/pytest/test_qonnx.py index 5e9dadc597..3d94b55f70 100644 --- a/test/pytest/test_qonnx.py +++ b/test/pytest/test_qonnx.py @@ -300,7 +300,7 @@ def bnn_fc_small_qonnx_model_scale_nonunit2(bnn_fc_small_qonnx_model): # The actual tests -@pytest.mark.parametrize('backend', ['Vivado', 'Vitis', 'Quartus']) +@pytest.mark.parametrize('backend', ['Vivado', 'Vitis']) def test_tfc_2w2a(test_case_id, tfc_2w2a_model, backend): model = tfc_2w2a_model @@ -351,7 +351,7 @@ def test_cnv_2w2a(test_case_id, cnv_2w2a_model, backend): np.testing.assert_allclose(y_qonnx.ravel(), y_hls4ml.ravel(), atol=1e-2, rtol=1) -@pytest.mark.parametrize('backend', ['Vivado', 'Vitis', 'Quartus']) +@pytest.mark.parametrize('backend', ['Vivado', 'Vitis']) def test_jet_tagging(test_case_id, jettagging_model, backend): model = jettagging_model diff --git a/test/pytest/test_recurrent_pytorch.py b/test/pytest/test_recurrent_pytorch.py index 6d1d4a636a..ba2f7ff0bc 100644 --- a/test/pytest/test_recurrent_pytorch.py +++ b/test/pytest/test_recurrent_pytorch.py @@ -31,7 +31,7 @@ def forward(self, x): return output -@pytest.mark.parametrize('backend', ['Vivado', 'Vitis', 'Quartus', 'Altera']) +@pytest.mark.parametrize('backend', ['Vivado', 'Vitis', 'Altera']) @pytest.mark.parametrize('io_type', ['io_parallel']) def test_gru(test_case_id, backend, io_type): model = GRUNet() @@ -62,7 +62,7 @@ def test_gru(test_case_id, backend, io_type): np.testing.assert_allclose(hls_prediction, pytorch_prediction, rtol=0, atol=1e-1) -@pytest.mark.parametrize('backend', ['Vivado', 'Vitis', 'Quartus', 'Altera']) +@pytest.mark.parametrize('backend', ['Vivado', 'Vitis', 'Altera']) @pytest.mark.parametrize('io_type', ['io_stream']) def test_gru_stream(test_case_id, backend, io_type): model = GRUNetStream() @@ -107,7 +107,7 @@ def forward(self, x): return output -@pytest.mark.parametrize('backend', ['Vivado', 'Vitis', 'Quartus', 'Altera']) +@pytest.mark.parametrize('backend', ['Vivado', 'Vitis', 'Altera']) @pytest.mark.parametrize('io_type', ['io_parallel']) def test_lstm(test_case_id, backend, io_type): model = LSTM() @@ -148,10 +148,10 @@ def test_lstm(test_case_id, backend, io_type): np.testing.assert_allclose(hls_prediction, pytorch_prediction, rtol=0, atol=1e-1) -@pytest.mark.parametrize('backend', ['Vivado', 'Vitis', 'Quartus', 'Altera']) +@pytest.mark.parametrize('backend', ['Vivado', 'Vitis', 'Altera']) @pytest.mark.parametrize('io_type', ['io_stream']) def test_lstm_stream(test_case_id, backend, io_type): - if not (backend in ('Quartus', 'Altera') and io_type == 'io_stream'): + if backend != 'Altera': model = LSTMStream() model.eval() @@ -264,38 +264,37 @@ def test_resource_cell(test_case_id, model_cls, n_states, reuse_factor): @pytest.mark.parametrize('backend', ['Vivado', 'Vitis', 'Quartus', 'Altera']) @pytest.mark.parametrize('io_type', ['io_parallel']) def test_rnn(test_case_id, backend, io_type): - if not (backend in ('Quartus', 'Altera') and io_type == 'io_stream'): - model = RNN() - model.eval() + model = RNN() + model.eval() - X_input = torch.randn(1, 1, 10) - X_input = np.round(X_input * 2**16) * 2**-16 # make it exact ap_fixed<32,16> - h0 = torch.zeros(1, 1, 20) + X_input = torch.randn(1, 1, 10) + X_input = np.round(X_input * 2**16) * 2**-16 # make it exact ap_fixed<32,16> + h0 = torch.zeros(1, 1, 20) - pytorch_prediction = model(torch.Tensor(X_input), torch.Tensor(h0)).detach().numpy() + pytorch_prediction = model(torch.Tensor(X_input), torch.Tensor(h0)).detach().numpy() - config = config_from_pytorch_model( - model, - [(1, 10), (1, 20)], - channels_last_conversion='off', - transpose_outputs=False, - default_precision='fixed<32,16>', - ) - output_dir = str(test_root_path / test_case_id) + config = config_from_pytorch_model( + model, + [(1, 10), (1, 20)], + channels_last_conversion='off', + transpose_outputs=False, + default_precision='fixed<32,16>', + ) + output_dir = str(test_root_path / test_case_id) - hls_model = convert_from_pytorch_model( - model, - hls_config=config, - output_dir=output_dir, - backend=backend, - io_type=io_type, - ) + hls_model = convert_from_pytorch_model( + model, + hls_config=config, + output_dir=output_dir, + backend=backend, + io_type=io_type, + ) - hls_model.compile() + hls_model.compile() - hls_prediction = np.reshape(hls_model.predict([X_input.detach().numpy(), h0.detach().numpy()]), (1, 1, 20)) + hls_prediction = np.reshape(hls_model.predict([X_input.detach().numpy(), h0.detach().numpy()]), (1, 1, 20)) - np.testing.assert_allclose(hls_prediction, pytorch_prediction, rtol=0, atol=1e-1) + np.testing.assert_allclose(hls_prediction, pytorch_prediction, rtol=0, atol=1e-1) # ---------------------------------------------------------------------------- # @@ -379,7 +378,7 @@ def _convert(model, test_case_id, backend, input_shape=(None, n_timesteps, n_in) @pytest.mark.parametrize( 'rnn_cls,backend', - [(nn.GRU, 'Vivado'), (nn.LSTM, 'Vivado'), (nn.RNN, 'Vivado'), (nn.RNN, 'Quartus')], + [(nn.GRU, 'Vivado'), (nn.LSTM, 'Vivado'), (nn.RNN, 'Vivado')], ids=['gru', 'lstm', 'rnn', 'rnn-quartus'], ) def test_rnn_sequence_output(test_case_id, rnn_cls, backend): @@ -495,7 +494,7 @@ def forward(self, x): _convert(model, test_case_id, 'Vivado') -@pytest.mark.parametrize('rnn_cls', [nn.RNN, nn.LSTM, nn.GRU], ids=['rnn', 'lstm', 'gru']) +@pytest.mark.parametrize('rnn_cls', [nn.LSTM, nn.GRU], ids=['lstm', 'gru']) def test_rnn_no_bias(test_case_id, rnn_cls): class NoBiasModel(nn.Module): def __init__(self): diff --git a/test/pytest/test_repack_stream.py b/test/pytest/test_repack_stream.py index 37545a884b..628b6630a6 100644 --- a/test/pytest/test_repack_stream.py +++ b/test/pytest/test_repack_stream.py @@ -9,7 +9,7 @@ test_root_path = Path(__file__).parent -@pytest.mark.parametrize('backend', ['Vivado', 'Vitis', 'Quartus', 'Catapult', 'Altera']) +@pytest.mark.parametrize('backend', ['Vivado', 'Vitis', 'Catapult', 'Altera']) def test_repack_precision(test_case_id, backend: str): inp = keras.Input(shape=(3, 3), name='inp') out = keras.layers.Reshape((3, 3), name='reshape')(inp) @@ -46,7 +46,6 @@ def test_repack_precision(test_case_id, backend: str): @pytest.mark.parametrize( 'backend, strategy', [ - ('Quartus', 'Resource'), ('Altera', 'Resource'), ('Vivado', 'Resource'), ('Vitis', 'Resource'), diff --git a/test/pytest/test_reshape.py b/test/pytest/test_reshape.py index bc265fe6bc..31ecee7771 100755 --- a/test/pytest/test_reshape.py +++ b/test/pytest/test_reshape.py @@ -20,7 +20,7 @@ def randX_20_10(): return randX(20, 10) -@pytest.mark.parametrize('backend', ['Vivado', 'Quartus', 'Catapult', 'Altera', 'XLS']) +@pytest.mark.parametrize('backend', ['Vivado', 'Catapult', 'Altera', 'XLS']) @pytest.mark.parametrize('io_type', ['io_parallel', 'io_stream']) def test_reshape_parallel(test_case_id, randX_20_10, backend, io_type): if backend == 'XLS' and io_type != 'io_parallel': diff --git a/test/pytest/test_rnn.py b/test/pytest/test_rnn.py index 74ca874e41..27cf41a9a3 100644 --- a/test/pytest/test_rnn.py +++ b/test/pytest/test_rnn.py @@ -96,7 +96,7 @@ def comparison(hls_weights, keras_weights, class_name): def test_rnn_parsing(test_case_id, rnn_layer, return_sequences): model = create_model_parsing(rnn_layer, return_sequences) - config = hls4ml.utils.config_from_keras_model(model, granularity='name', backend='Vivado') + config = hls4ml.utils.config_from_keras_model(model, granularity='name', backend='Vitis') output_dir = str(test_root_path / test_case_id) hls_model = hls4ml.converters.convert_from_keras_model(model, hls_config=config, output_dir=output_dir) @@ -116,6 +116,7 @@ def create_model_accuracy(rnn_layer, return_sequences): # Subtract 0.5 to include negative values input_shape = (12, 8) X = np.random.rand(50, *input_shape) - 0.5 + X = np.round(X * 2**22) * 2**-22 layer_name = rnn_layer.__name__ model = Sequential() @@ -159,7 +160,6 @@ def create_model_accuracy(rnn_layer, return_sequences): @pytest.mark.parametrize( 'rnn_layer, backend, io_type, strategy', [ - (SimpleRNN, 'Quartus', 'io_parallel', 'resource'), (SimpleRNN, 'Altera', 'io_parallel', 'resource'), (SimpleRNN, 'Vivado', 'io_parallel', 'resource'), (SimpleRNN, 'Vivado', 'io_parallel', 'latency'), @@ -173,7 +173,6 @@ def create_model_accuracy(rnn_layer, return_sequences): (LSTM, 'Vivado', 'io_parallel', 'latency'), (LSTM, 'Vitis', 'io_parallel', 'resource'), (LSTM, 'Vitis', 'io_parallel', 'latency'), - (LSTM, 'Quartus', 'io_parallel', 'resource'), (LSTM, 'Altera', 'io_parallel', 'resource'), (LSTM, 'Vivado', 'io_stream', 'resource'), (LSTM, 'Vivado', 'io_stream', 'latency'), @@ -183,13 +182,11 @@ def create_model_accuracy(rnn_layer, return_sequences): (GRU, 'Vivado', 'io_parallel', 'latency'), (GRU, 'Vitis', 'io_parallel', 'resource'), (GRU, 'Vitis', 'io_parallel', 'latency'), - (GRU, 'Quartus', 'io_parallel', 'resource'), (GRU, 'Altera', 'io_parallel', 'resource'), (GRU, 'Vivado', 'io_stream', 'resource'), (GRU, 'Vivado', 'io_stream', 'latency'), (GRU, 'Vitis', 'io_stream', 'resource'), (GRU, 'Vitis', 'io_stream', 'latency'), - (GRU, 'Quartus', 'io_stream', 'resource'), (GRU, 'Altera', 'io_stream', 'resource'), (Bidirectional, 'Vivado', 'io_parallel', 'resource'), (Bidirectional, 'Vivado', 'io_parallel', 'latency'), @@ -197,7 +194,6 @@ def create_model_accuracy(rnn_layer, return_sequences): (Bidirectional, 'Vitis', 'io_parallel', 'latency'), ], ids=[ - 'SimpleRNN-Quartus-io_parallel-resource', 'SimpleRNN-Altera-io_parallel-resource', 'SimpleRNN-Vivado-io_parallel-resource', 'SimpleRNN-Vivado-io_parallel-latency', @@ -211,7 +207,6 @@ def create_model_accuracy(rnn_layer, return_sequences): 'LSTM-Vivado-io_parallel-latency', 'LSTM-Vitis-io_parallel-resource', 'LSTM-Vitis-io_parallel-latency', - 'LSTM-Quartus-io_parallel-resource', 'LSTM-Altera-io_parallel-resource', 'LSTM-Vivado-io_stream-resource', 'LSTM-Vivado-io_stream-latency', @@ -221,13 +216,11 @@ def create_model_accuracy(rnn_layer, return_sequences): 'GRU-Vivado-io_parallel-latency', 'GRU-Vitis-io_parallel-resource', 'GRU-Vitis-io_parallel-latency', - 'GRU-Quartus-io_parallel-resource', 'GRU-Altera-io_parallel-resource', 'GRU-Vivado-io_stream-resource', 'GRU-Vivado-io_stream-latency', 'GRU-Vitis-io_stream-resource', 'GRU-Vitis-io_stream-latency', - 'GRU-Quartus-io_stream-resource', 'GRU-Altera-io_stream-resource', 'Bidirectional-Vivado-io_parallel-resource', 'Bidirectional-Vivado-io_parallel-latency', @@ -242,7 +235,7 @@ def test_rnn_accuracy(test_case_id, rnn_layer, return_sequences, backend, io_typ model, X = create_model_accuracy(rnn_layer, return_sequences) - default_precision = 'ap_fixed<32, 16>' if backend in ['Vivado', 'Vitis'] else 'ac_fixed<32, 16, true>' + default_precision = 'fixed<32, 10>' hls_config = hls4ml.utils.config_from_keras_model( model, granularity='name', default_precision=default_precision, backend=backend ) @@ -668,11 +661,11 @@ def test_bidirectional_gru_accuracy(test_case_id): model.add(Bidirectional(GRU(4))) hls_config = hls4ml.utils.config_from_keras_model( - model, granularity='name', default_precision='ap_fixed<32, 16>', backend='Vivado' + model, granularity='name', default_precision='fixed<32, 10>', backend='Vitis' ) output_dir = str(test_root_path / test_case_id) hls_model = hls4ml.converters.convert_from_keras_model( - model, hls_config=hls_config, output_dir=output_dir, backend='Vivado' + model, hls_config=hls_config, output_dir=output_dir, backend='Vitis' ) hls_model.compile() @@ -693,11 +686,11 @@ def test_bidirectional_no_bias(test_case_id, cell_type): model.add(Bidirectional(LSTM(4, use_bias=False))) hls_config = hls4ml.utils.config_from_keras_model( - model, granularity='name', default_precision='ap_fixed<32, 16>', backend='Vivado' + model, granularity='name', default_precision='fixed<32, 10>', backend='Vitis' ) output_dir = str(test_root_path / test_case_id) hls_model = hls4ml.converters.convert_from_keras_model( - model, hls_config=hls_config, output_dir=output_dir, backend='Vivado' + model, hls_config=hls_config, output_dir=output_dir, backend='Vitis' ) hls_model.compile() diff --git a/test/pytest/test_softmax.py b/test/pytest/test_softmax.py index 49034918b9..54037ade83 100644 --- a/test/pytest/test_softmax.py +++ b/test/pytest/test_softmax.py @@ -20,7 +20,7 @@ def generate_data(input_shape): @pytest.mark.parametrize('softmax_impl', ['activation', 'standalone']) -@pytest.mark.parametrize('backend', ['Vivado', 'Vitis', 'Quartus', 'Catapult', 'XLS']) +@pytest.mark.parametrize('backend', ['Vivado', 'Vitis', 'Catapult', 'XLS']) @pytest.mark.parametrize('strategy', ['stable', 'latency', 'argmax']) @pytest.mark.parametrize( 'input_bits,input_shape,table_bits,io_type,custom_accum', @@ -86,7 +86,7 @@ def test_softmax( @pytest.mark.parametrize('softmax_impl', ['activation', 'standalone']) -@pytest.mark.parametrize('backend', ['Vivado', 'Vitis', 'Quartus', 'Catapult', 'XLS']) +@pytest.mark.parametrize('backend', ['Vivado', 'Vitis', 'Catapult', 'XLS']) @pytest.mark.parametrize('io_type', ['io_parallel', 'io_stream']) def test_softmax_skipped(test_case_id, softmax_impl, backend, io_type): if backend == 'XLS' and io_type != 'io_parallel': diff --git a/test/pytest/test_softsign.py b/test/pytest/test_softsign.py index 8904fcb8bb..9dd75ebde9 100644 --- a/test/pytest/test_softsign.py +++ b/test/pytest/test_softsign.py @@ -10,7 +10,7 @@ test_root_path = Path(__file__).parent -@pytest.mark.parametrize('backend', ['Vivado', 'Vitis', 'Quartus', 'Catapult']) +@pytest.mark.parametrize('backend', ['Vivado', 'Vitis', 'Catapult']) @pytest.mark.parametrize('input_shape, io_type', [((8,), 'io_parallel'), ((8,), 'io_stream'), ((8, 8, 3), 'io_stream')]) def test_softsign(test_case_id, backend, input_shape, io_type): X = np.random.rand(1000, *input_shape) diff --git a/test/pytest/test_stream_clone.py b/test/pytest/test_stream_clone.py index d86e10acc8..312a33ea15 100644 --- a/test/pytest/test_stream_clone.py +++ b/test/pytest/test_stream_clone.py @@ -70,7 +70,7 @@ def data(): return X -@pytest.mark.parametrize('backend', ['Vivado', 'Quartus', 'Vitis', 'Altera']) +@pytest.mark.parametrize('backend', ['Vivado', 'Vitis', 'Altera']) def test_multi_clone(test_case_id, model_multi_clone, data, backend: str): output_dir = str(test_root_path / test_case_id) hls_config = hls4ml.utils.config_from_keras_model( @@ -90,7 +90,7 @@ def test_multi_clone(test_case_id, model_multi_clone, data, backend: str): np.testing.assert_allclose(r_hls, r_keras, atol=1e-4, rtol=0) -@pytest.mark.parametrize('backend', ['Vivado', 'Quartus', 'Vitis', 'Altera']) +@pytest.mark.parametrize('backend', ['Vivado', 'Vitis', 'Altera']) def test_clone_precision_inheritance(test_case_id, model_clone_precision_inheritance, data, backend: str): output_dir = str(test_root_path / test_case_id) layer_config = { diff --git a/test/pytest/test_trace.py b/test/pytest/test_trace.py index 3babafecbd..2e1f7ef883 100644 --- a/test/pytest/test_trace.py +++ b/test/pytest/test_trace.py @@ -11,7 +11,7 @@ test_root_path = Path(__file__).parent -@pytest.mark.parametrize('backend', ['Vivado', 'Vitis', 'Quartus']) +@pytest.mark.parametrize('backend', ['Vivado', 'Vitis']) @pytest.mark.parametrize('activation', ['relu', None]) def test_trace(test_case_id, backend, activation): """Test the tracing feature with a simple Keras model.""" diff --git a/test/pytest/test_transpose_concat.py b/test/pytest/test_transpose_concat.py index b52b68ff75..319a056753 100644 --- a/test/pytest/test_transpose_concat.py +++ b/test/pytest/test_transpose_concat.py @@ -48,22 +48,18 @@ def hls_model(keras_model, request, test_case_id): [ ('io_stream', 'Vivado'), ('io_stream', 'Vitis'), - ('io_stream', 'Quartus'), ('io_stream', 'Altera'), ('io_parallel', 'Vivado'), ('io_parallel', 'Vitis'), - ('io_parallel', 'Quartus'), ('io_parallel', 'Altera'), ], indirect=True, ids=[ 'vivado_stream', - 'vitis_streamq', - 'quartus_stream', + 'vitis_stream', 'altera_stream', 'vivado_parallel', 'vitis_parallel', - 'quartus_parallel', 'altera_parallel', ], ) diff --git a/test/pytest/test_upsampling.py b/test/pytest/test_upsampling.py index 497b115c6f..ed1fe5da6f 100644 --- a/test/pytest/test_upsampling.py +++ b/test/pytest/test_upsampling.py @@ -46,7 +46,7 @@ def keras_model_2d(): @pytest.mark.parametrize('io_type', ['io_stream', 'io_parallel']) -@pytest.mark.parametrize('backend', ['Vivado', 'Vitis', 'Quartus', 'Catapult', 'Altera']) +@pytest.mark.parametrize('backend', ['Vivado', 'Vitis', 'Catapult', 'Altera']) @pytest.mark.parametrize('model_type', ['1d', '2d']) def test_upsampling(test_case_id, keras_model_1d, keras_model_2d, data_1d, data_2d, model_type, io_type, backend): if model_type == '1d': diff --git a/test/pytest/test_upsampling_pytorch.py b/test/pytest/test_upsampling_pytorch.py index 113ed4cb3f..bbbc0b970c 100644 --- a/test/pytest/test_upsampling_pytorch.py +++ b/test/pytest/test_upsampling_pytorch.py @@ -51,7 +51,7 @@ def forward(self, x): @pytest.mark.parametrize('io_type', ['io_stream', 'io_parallel']) -@pytest.mark.parametrize('backend', ['Vivado', 'Vitis', 'Quartus']) +@pytest.mark.parametrize('backend', ['Vivado', 'Vitis']) def test_pytorch_upsampling1d(test_case_id, data_1d, io_type, backend): model = Upsample1DModel() @@ -81,7 +81,7 @@ def test_pytorch_upsampling1d(test_case_id, data_1d, io_type, backend): @pytest.mark.parametrize('io_type', ['io_parallel']) # Fractional scaling doesn't work with io_stream -@pytest.mark.parametrize('backend', ['Vivado', 'Vitis', 'Quartus']) +@pytest.mark.parametrize('backend', ['Vivado', 'Vitis']) def test_pytorch_upsampling2d(test_case_id, data_2d, io_type, backend): model = Upsample2DModel() diff --git a/test/pytest/test_writer_config.py b/test/pytest/test_writer_config.py index 286aa87cf4..57b9a124c7 100644 --- a/test/pytest/test_writer_config.py +++ b/test/pytest/test_writer_config.py @@ -21,7 +21,7 @@ def keras_model(): @pytest.mark.parametrize('io_type', ['io_stream', 'io_parallel']) -@pytest.mark.parametrize('backend', ['Vivado', 'Vitis', 'Altera']) # No Quartus for now +@pytest.mark.parametrize('backend', ['Vivado', 'Vitis', 'Altera']) @pytest.mark.parametrize('namespace', [None, 'test_namespace']) def test_namespace(test_case_id, keras_model, namespace, io_type, backend): config = hls4ml.utils.config_from_keras_model(keras_model, granularity='name') @@ -84,7 +84,7 @@ def test_emulator(test_case_id, keras_model, io_type, backend): hls_model.compile() # It's enough that the model compiles -@pytest.mark.parametrize('backend', ['Vivado', 'Vitis']) # No Quartus for now +@pytest.mark.parametrize('backend', ['Vivado', 'Vitis']) @pytest.mark.parametrize('write_tar', [True, False]) def test_write_tar(test_case_id, keras_model, write_tar, backend): config = hls4ml.utils.config_from_keras_model(keras_model, granularity='name') @@ -102,7 +102,7 @@ def test_write_tar(test_case_id, keras_model, write_tar, backend): assert tar_written == write_tar -@pytest.mark.parametrize('backend', ['Vivado', 'Vitis']) # No Quartus for now +@pytest.mark.parametrize('backend', ['Vivado', 'Vitis']) @pytest.mark.parametrize('write_weights_txt', [True, False]) def test_write_weights_txt(test_case_id, keras_model, write_weights_txt, backend): config = hls4ml.utils.config_from_keras_model(keras_model, granularity='name') diff --git a/test/pytest/test_zeropadding.py b/test/pytest/test_zeropadding.py index 63d7cf8bdb..92e39e4d02 100644 --- a/test/pytest/test_zeropadding.py +++ b/test/pytest/test_zeropadding.py @@ -50,7 +50,7 @@ def keras_model_2d(): @pytest.mark.parametrize('io_type', ['io_stream', 'io_parallel']) -@pytest.mark.parametrize('backend', ['Vivado', 'Vitis', 'Quartus', 'Catapult', 'Altera']) +@pytest.mark.parametrize('backend', ['Vivado', 'Vitis', 'Catapult', 'Altera']) @pytest.mark.parametrize('model_type', ['1d', '2d']) def test_zeropadding(test_case_id, keras_model_1d, keras_model_2d, data_1d, data_2d, model_type, io_type, backend): if model_type == '1d':