Skip to content

Commit 67234ce

Browse files
bo3zvloncar
authored andcommitted
Quartus multiple model inputs
1 parent ae31793 commit 67234ce

File tree

1 file changed

+81
-57
lines changed

1 file changed

+81
-57
lines changed

hls4ml/writer/quartus_writer.py

Lines changed: 81 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -111,8 +111,10 @@ def write_project_cpp(self, model):
111111
newline = line
112112
if io_type == 'io_stream':
113113
newline += 'void myproject(\n'
114-
newline += indent+'stream_in<{}> &input_stream,\n'.format(model_inputs[0].type.name)
115-
newline += indent+'stream_out<{}> &output_stream\n'.format(model_outputs[0].type.name)
114+
for inp in model_inputs:
115+
newline += indent+'stream_in<{}> &{}_stream,\n'.format(inp.type.name, inp.name)
116+
for out in model_outputs:
117+
newline += indent+'stream_out<{}> &{}_stream\n'.format(out.type.name, out.name)
116118
newline += ') {\n'
117119
if io_type == 'io_parallel':
118120
newline = 'output_data myproject(\n'
@@ -124,8 +126,10 @@ def write_project_cpp(self, model):
124126
newline = line
125127
if io_type == 'io_stream':
126128
newline += 'component void myproject(\n'
127-
newline += indent+'stream_in<{}> &input_stream,\n'.format(model_inputs[0].type.name)
128-
newline += indent+'stream_out<{}> &output_stream\n'.format(model_outputs[0].type.name)
129+
for inp in model_inputs:
130+
newline += indent+'stream_in<{}> &{}_stream,\n'.format(inp.type.name, inp.name)
131+
for out in model_outputs:
132+
newline += indent+'stream_out<{}> &{}_stream\n'.format(out.type.name, out.name)
129133
newline += ') {\n'
130134
if io_type == 'io_parallel':
131135
newline += 'component output_data myproject(\n'
@@ -148,10 +152,11 @@ def write_project_cpp(self, model):
148152
elif '//hls-fpga-machine-learning initialize input/output' in line:
149153
if io_type == 'io_stream':
150154
newline = line
151-
newline += indent + f'for (size_t i = 0; i < {model_inputs[0].size_cpp()} / {model_inputs[0].type.name}::size; i++) {{\n'
152-
newline += indent + f' {model_inputs[0].type.name} tmp = input_stream.read();\n'
153-
newline += indent + f' {model_inputs[0].name}.write(tmp);\n'
154-
newline += indent + f'}}\n'
155+
for inp in model_inputs:
156+
newline += indent + f'for (size_t i = 0; i < {inp.size_cpp()} / {inp.type.name}::size; i++) {{\n'
157+
newline += indent + f' {inp.type.name} tmp = {inp.name}_stream.read();\n'
158+
newline += indent + f' {inp.name}.write(tmp);\n'
159+
newline += indent + f'}}\n'
155160
else:
156161
newline = line
157162
newline += indent+'hls_register output_data outputs;\n'
@@ -195,11 +200,12 @@ def write_project_cpp(self, model):
195200
elif '//hls-fpga-machine-learning return' in line:
196201
if io_type == 'io_stream':
197202
newline = line
198-
newline += indent + f'for (size_t i = 0; i < {model_outputs[0].size_cpp()} / {model_outputs[0].type.name}::size; i++) {{\n'
199-
newline += indent + f' {model_outputs[0].type.name} tmp = {model_outputs[0].name}.read();\n'
200-
newline += indent + f' output_stream.write(tmp);\n'
201-
newline += indent + f'}}\n'
202-
newline += '}\n'
203+
for out in model_outputs:
204+
newline += indent + f'for (size_t i = 0; i < {out.size_cpp()} / {out.type.name}::size; i++) {{\n'
205+
newline += indent + f' {out.type.name} tmp = {out.name}.read();\n'
206+
newline += indent + f' {out.name}_stream.write(tmp);\n'
207+
newline += indent + f'}}\n'
208+
newline += '}\n'
203209
else:
204210
newline = line
205211
newline += indent+'return outputs;\n'
@@ -242,8 +248,10 @@ def write_project_header(self, model):
242248
# For io_stream, input and output are passed by reference; see myproject.h & myproject.cpp for more details
243249
if io_type == 'io_stream':
244250
newline += 'void myproject(\n'
245-
newline += indent+'stream_in<{}> &input_stream,\n'.format(model_inputs[0].type.name)
246-
newline += indent+'stream_out<{}> &output_stream\n'.format(model_outputs[0].type.name)
251+
for inp in model_inputs:
252+
newline += indent+'stream_in<{}> &{}_stream,\n'.format(inp.type.name, inp.name)
253+
for out in model_outputs:
254+
newline += indent+'stream_out<{}> &{}_stream\n'.format(out.type.name, out.name)
247255
newline += ');\n'
248256
# In io_parallel, a struct is returned; see myproject.h & myproject.cpp for more details
249257
else:
@@ -256,8 +264,10 @@ def write_project_header(self, model):
256264
newline = line
257265
if io_type == 'io_stream':
258266
newline += 'component void myproject(\n'
259-
newline += indent+'stream_in<{}> &input_stream,\n'.format(model_inputs[0].type.name)
260-
newline += indent+'stream_out<{}> &output_stream\n'.format(model_outputs[0].type.name)
267+
for inp in model_inputs:
268+
newline += indent+'stream_in<{}> &{}_stream,\n'.format(inp.type.name, inp.name)
269+
for out in model_outputs:
270+
newline += indent+'stream_out<{}> &{}_stream\n'.format(out.type.name, out.name)
261271
newline += ');\n'
262272
else:
263273
newline += 'component output_data myproject(\n'
@@ -452,7 +462,9 @@ def write_testbench_stream(self, model):
452462
return
453463

454464
outvar = model.get_output_variables()[0]
455-
invar = model.get_input_variables()[0]
465+
466+
model_inputs = model.get_input_variables()
467+
model_outputs = model.get_output_variables()
456468

457469
filedir = os.path.dirname(os.path.abspath(__file__))
458470

@@ -479,10 +491,7 @@ def write_testbench_stream(self, model):
479491

480492
f = open(os.path.join(filedir, '../templates/quartus/myproject_test_stream.cpp'), 'r')
481493
fout = open('{}/{}_test.cpp'.format(model.config.get_output_dir(), model.config.get_project_name()), 'w')
482-
483-
if len(model.get_input_variables()) > 1 or len(model.get_output_variables()) > 1:
484-
raise Exception('Quartus io_stream supports exactly one input/output per model')
485-
494+
486495
for line in f.readlines():
487496
indent = ' ' * (len(line) - len(line.lstrip(' ')))
488497

@@ -491,29 +500,39 @@ def write_testbench_stream(self, model):
491500

492501
elif '//hls-fpga-machine learning instantiate inputs and outputs' in line:
493502
newline = line
494-
newline += indent + 'stream_in<{}> inputs;\n'.format(invar.type.name)
495-
newline += indent + 'stream_out<{}> outputs;\n'.format(outvar.type.name)
496-
503+
for inp in model_inputs:
504+
newline += indent+'stream_in<{}> {}_input;\n'.format(inp.type.name, inp.name)
505+
for out in model_outputs:
506+
newline += indent+'stream_out<{}> {}_output;\n'.format(out.type.name, out.name)
507+
497508
# TODO - This is one-input specific (are multiple model inputs needed at all?)
498509
elif '//hls-fpga-machine-learning insert data' in line:
499510
newline = line
500-
newline += indent + f'float vals[{invar.size_cpp()}]; \n'
501-
newline += indent + f'for (int j = 0 ; j < {invar.size_cpp()} ; j++) {{\n'
502-
newline += indent + f' vals[j] = in[j]; \n'
503-
newline += indent + f'}}'
504-
newline += indent + f'nnet::convert_data<float, {invar.type.name}, {invar.size_cpp()}>(vals, inputs);\n'
505-
511+
c = 0
512+
for inp in model_inputs:
513+
newline += indent + f'float vals_{c}[{inp.size_cpp()}]; \n'
514+
newline += indent + f'for (int j = 0 ; j < {inp.size_cpp()} ; j++) {{\n'
515+
newline += indent + indent + f'vals_{c}[j] = in[j]; \n'
516+
newline += indent + f'}}\n'
517+
newline += indent + f'nnet::convert_data<float, {inp.type.name}, {inp.size_cpp()}>(vals_{c}, {inp.name}_input);\n'
518+
c += 1
519+
506520
elif '//hls-fpga-machine-learning insert zero' in line:
507521
newline = line
508-
newline += indent + f'float vals[{invar.size_cpp()}]; \n'
509-
newline += indent + f'for (int j = 0 ; j < {invar.size_cpp()} ; j++) {{'
510-
newline += indent + f' vals[j] = 0.0; \n'
511-
newline += indent + f'}}'
512-
newline += indent + f'nnet::convert_data<float, {invar.type.name}, {invar.size_cpp()}>(vals, inputs);\n'
522+
c = 0
523+
for inp in model_inputs:
524+
newline += indent + f'float vals_{c}[{inp.size_cpp()}]; \n'
525+
newline += indent + f'for (int j = 0 ; j < {inp.size_cpp()} ; j++) {{\n'
526+
newline += indent + indent + f'vals_{c}[j] = 0.0; \n'
527+
newline += indent + f'}}\n'
528+
newline += indent + f'nnet::convert_data<float, {inp.type.name}, {inp.size_cpp()}>(vals_{c}, {inp.name}_input);\n'
529+
c += 1
513530

514531
elif '//hls-fpga-machine-learning insert top-level-function' in line:
515532
newline = line
516-
newline += indent + f'ihc_hls_enqueue_noret(&{model.config.get_project_name()}, inputs, outputs); \n'
533+
input_params = ', '.join([f'{i.name}_input' for i in model_inputs])
534+
output_params = ', '.join([f'{o.name}_output' for o in model_outputs])
535+
newline += indent + f'ihc_hls_enqueue_noret(&{model.config.get_project_name()}, {input_params}, {output_params}); \n'
517536

518537
elif 'hls-fpga-machine-learning insert run' in line:
519538
newline = line
@@ -522,8 +541,9 @@ def write_testbench_stream(self, model):
522541
elif '//hls-fpga-machine-learning convert output' in line:
523542
newline = line
524543
newline += indent + 'float res[{}];\n'.format(outvar.size_cpp())
525-
newline += indent + 'nnet::convert_data_back<{}, float, {}>(outputs, res);\n'.format(outvar.type.name,
526-
outvar.size_cpp())
544+
newline += indent + 'nnet::convert_data_back<{}, float, {}>({}_output, res);\n'.format(outvar.type.name,
545+
outvar.size_cpp(),
546+
outvar.name)
527547

528548
elif '//hls-fpga-machine-learning insert tb-output' in line:
529549
newline += indent + 'for(int i = 0; i < {}; i++) {{\n'.format(outvar.size_cpp())
@@ -617,32 +637,36 @@ def write_bridge(self, model):
617637
elif '//hls-fpga-machine-learning insert wrapper' in line:
618638
dtype = line.split('#', 1)[1].strip()
619639
if io_type == 'io_stream':
620-
if len(model_inputs) > 1 or len(model_outputs) > 1:
621-
raise Exception('io_stream Quartus supports exactly one input/output')
622-
i = model_inputs[0]
623-
o = model_outputs[0]
624-
625-
# Initialise stream object and store input data (C-array) to a 'stream' object
626-
newline = indent + 'stream_in<{}> inputs;\n'.format(model_inputs[0].type.name)
627-
newline += indent + 'nnet::convert_data<{}, {}, {}>({}, inputs);\n'.format(dtype,
628-
i.type.name,
629-
i.size_cpp(),
630-
i.name,
631-
)
632-
640+
newline = ''
641+
for i in model_inputs:
642+
# Initialise stream object and store input data (C-array) to a 'stream' object
643+
newline += indent + 'stream_in<{}> {}_input;\n'.format(i.type.name, i.name)
644+
newline += indent + 'nnet::convert_data<{}, {}, {}>({}, {}_input);\n'.format(dtype,
645+
i.type.name,
646+
i.size_cpp(),
647+
i.name,
648+
i.name
649+
)
650+
633651
# Initialise stream output
634-
newline += '\n'
635-
newline += indent + 'stream_out<{}> outputs;\n'.format(model_outputs[0].type.name)
636-
652+
for o in model_outputs:
653+
newline += '\n'
654+
newline += indent + 'stream_out<{}> {}_output;\n'.format(o.type.name, o.name)
655+
637656
# Execute top-level function
638-
top_level = indent + '{}(inputs, outputs);\n'.format(model.config.get_project_name())
657+
input_params = ', '.join([f'{i.name}_input' for i in model_inputs])
658+
output_params = ', '.join([f'{o.name}_output' for o in model_outputs])
659+
660+
top_level = indent + '{}({}, {});\n'.format(model.config.get_project_name(), input_params, output_params)
639661
newline += top_level
640662
newline += '\n'
641663

642664
# Store data from 'stream' output to C-array, to be then returned and handled in Python
643-
newline += indent + 'nnet::convert_data_back<{}, {}, {}>(outputs, {});\n'.format(o.type.name,
665+
for o in model_outputs:
666+
newline += indent + 'nnet::convert_data_back<{}, {}, {}>({}_output, {});\n'.format(o.type.name,
644667
dtype,
645668
o.size_cpp(),
669+
o.name,
646670
o.name
647671
)
648672

0 commit comments

Comments
 (0)