Skip to content

Commit 67c39b3

Browse files
calad0ijmduarte
andauthored
Fix for #905 (#906)
* fix multi clones w/ diff outs in stream io * fix test --------- Co-authored-by: Javier Duarte <[email protected]>
1 parent a9fc0fc commit 67c39b3

File tree

2 files changed

+65
-8
lines changed

2 files changed

+65
-8
lines changed

hls4ml/backends/fpga/passes/clone.py

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -20,21 +20,19 @@ def initialize(self):
2020
class CloneFunctionTemplate(FunctionCallTemplate):
2121
def __init__(self):
2222
super().__init__(Clone, include_header=clone_include_list)
23-
self.template = None # to be filled once number of clones known
2423

2524
def format(self, node):
2625
params = self._default_function_params(node)
2726
for i, _output in enumerate(node.outputs):
2827
params['output' + str(i + 1)] = node.variables[node.outputs[i]].name
2928

30-
if self.template is None:
31-
self.template = (
32-
'nnet::clone_stream<{input_t}, {output_t}, {size}>({input}, '
33-
+ ', '.join(['{output' + str(i + 1) + '}' for i in range(len(node.outputs))])
34-
+ ');'
35-
)
29+
template = (
30+
'nnet::clone_stream<{input_t}, {output_t}, {size}>({input}, '
31+
+ ', '.join(['{output' + str(i + 1) + '}' for i in range(len(node.outputs))])
32+
+ ');'
33+
)
3634

37-
return self.template.format(**params)
35+
return template.format(**params)
3836

3937

4038
def register_clone(backend):
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import os
2+
import random
3+
from pathlib import Path
4+
5+
import numpy as np
6+
import pytest
7+
import tensorflow as tf
8+
from keras.layers import Add, Dense
9+
from tensorflow import keras
10+
11+
from hls4ml.converters import convert_from_keras_model
12+
13+
test_root_path = Path(__file__).parent
14+
15+
16+
@pytest.fixture(scope='module')
17+
def model():
18+
seed = 42
19+
os.environ['RANDOM_SEED'] = f'{seed}'
20+
np.random.seed(seed)
21+
tf.random.set_seed(seed)
22+
tf.get_logger().setLevel('ERROR')
23+
random.seed(seed)
24+
25+
inp = keras.Input(shape=(10,))
26+
x = Dense(10)(inp)
27+
y = Dense(10)(inp)
28+
z = Dense(10)(inp)
29+
xy = Add()([x, y]) # 5
30+
xy = Add()([xy, y]) # 5
31+
out = Add()([xy, z]) # 5
32+
model = keras.Model(inp, out)
33+
return model
34+
35+
36+
@pytest.fixture(scope='module')
37+
def data():
38+
rng = np.random.RandomState(42)
39+
X = rng.normal(0, 1, (1000, 10))
40+
X = np.clip(X, -16, 15)
41+
return X
42+
43+
44+
@pytest.mark.parametrize('backend', ['Vivado', 'Quartus', 'Vitis'])
45+
def test_multi_clone(model, data, backend: str):
46+
output_dir = str(test_root_path / f'hls4mlprj_stream_multi_clone_{backend}')
47+
hls_config = {'Model': {'Precision': 'fixed<32,5>', 'ReuseFactor': 1}}
48+
model_hls = convert_from_keras_model(
49+
model,
50+
backend=backend,
51+
output_dir=output_dir,
52+
hls_config=hls_config,
53+
io_type='io_stream', # clone only happens with stream io.
54+
)
55+
model_hls.compile()
56+
r_hls = model_hls.predict(data)
57+
r_keras = model(data).numpy()
58+
59+
assert np.allclose(r_hls, r_keras, atol=1e-5, rtol=0)

0 commit comments

Comments
 (0)