Repository: AlexMontgomerie/fpgaconvnet-hls (language: python)

Issue:
Update network.py, resolve unsupported attributes issues
Update network.py, resolve unsupported attributes issues. The onnxruntime inferencesession doesn't support `weight_width`, `data_width`, `acc_width`, and `block_floating_point`, hence a new onnx file _new.onnx is created to get rid of these four attributes. 


Discussion / hints:

Fix patch:
diff --git a/.gitignore b/.gitignore
index d7ae700..c10c056 100644
--- a/.gitignore
+++ b/.gitignore
@@ -56,3 +56,6 @@ sd_card/
 partition_*/
 
 fpgaconvnet/hls/hardware/system.hpp
+**/build/**
+/test/**/*.json
+/test/**/*.toml
diff --git a/fpgaconvnet/hls/generate/layers/avg_pooling.py b/fpgaconvnet/hls/generate/layers/avg_pooling.py
new file mode 100644
index 0000000..f7a7bc1
--- /dev/null
+++ b/fpgaconvnet/hls/generate/layers/avg_pooling.py
@@ -0,0 +1,194 @@
+import fpgaconvnet.hls.generate.modules.sliding_window as generate_sliding_window
+import fpgaconvnet.hls.generate.modules.avg_pool as generate_avg_pool
+
+avg_pooling_layer_template_header = """#ifndef {NAME}_HPP_
+#define {NAME}_HPP_
+
+#include "sliding_window.hpp"
+#include "avg_pool.hpp"
+
+#define name        {name}
+#define NAME        {NAME}
+#define {NAME}_ID   {id}
+
+#define {NAME}_BATCH_SIZE    {batch_size}
+#define {NAME}_ROWS          {rows}
+#define {NAME}_COLS          {cols}
+#define {NAME}_CHANNELS      {channels}
+#define {NAME}_COARSE        {coarse}
+#define {NAME}_KERNEL_SIZE_X {kernel_size_x}
+#define {NAME}_KERNEL_SIZE_Y {kernel_size_y}
+#define {NAME}_STRIDE_X      {stride_x}
+#define {NAME}_STRIDE_Y      {stride_y}
+#define {NAME}_FINE          {fine}
+
+#define {NAME}_COARSE_IN    {NAME}_COARSE
+#define {NAME}_COARSE_OUT   {NAME}_COARSE
+
+#define {NAME}_ROWS_OUT     {rows_out}
+#define {NAME}_COLS_OUT     {cols_out}
+#define {NAME}_CHANNELS_OUT {channels_out}
+
+// define the data type
+typedef ap_fixed<{data_width},{data_int_width},AP_RND> {name}_data_t;
+typedef {name}_data_t {name}_input_t;
+typedef {name}_data_t {name}_output_t;
+
+// SLIDING WINDOW
+#define {NAME}_SLIDING_WINDOW_BATCH_SIZE    {batch_size}
+#define {NAME}_SLIDING_WINDOW_ROWS          {rows}
+#define {NAME}_SLIDING_WINDOW_COLS          {cols}
+#define {NAME}_SLIDING_WINDOW_CHANNELS      {channels_per_module}
+#define {NAME}_SLIDING_WINDOW_KERNEL_SIZE_X {kernel_size_x}
+#define {NAME}_SLIDING_WINDOW_KERNEL_SIZE_Y {kernel_size_y}
+#define {NAME}_SLIDING_WINDOW_STRIDE_X      {stride_x}
+#define {NAME}_SLIDING_WINDOW_STRIDE_Y      {stride_y}
+#define {NAME}_SLIDING_WINDOW_PAD_LEFT      {pad_left}
+#define {NAME}_SLIDING_WINDOW_PAD_RIGHT     {pad_right}
+#define {NAME}_SLIDING_WINDOW_PAD_TOP       {pad_top}
+#define {NAME}_SLIDING_WINDOW_PAD_BOTTOM    {pad_bottom}
+
+// AVG_POOL
+#define {NAME}_AVG_POOL_BATCH_SIZE   {batch_size}
+#define {NAME}_AVG_POOL_ROWS         {rows_out}
+#define {NAME}_AVG_POOL_COLS         {cols_out}
+#define {NAME}_AVG_POOL_CHANNELS     {channels_per_module}
+#define {NAME}_AVG_POOL_KERNEL_SIZE_X {kernel_size_x}
+#define {NAME}_AVG_POOL_KERNEL_SIZE_Y {kernel_size_y}
+#define {NAME}_AVG_POOL_FINE         {fine}
+
+/**
+ * FUNCTION DEFINITION
+ */
+ 
+void {name}(
+    stream_t({name}_data_t) in[{NAME}_COARSE],
+    stream_t({name}_data_t) out[{NAME}_COARSE], 
+    int mode
+);
+
+#undef name
+#undef NAME
+#endif
+"""
+
+avg_pooling_layer_template_src = """#include "{name}.hpp"
+
+void {name}_sliding_window(
+    stream_t({name}_data_t) &in,
+    stream_t({name}_data_t) out[{NAME}_KERNEL_SIZE_X][{NAME}_KERNEL_SIZE_Y]
+) {{
+#pragma HLS INLINE OFF
+{sliding_window}
+}}
+
+void {name}_avg_pool(
+    stream_t({name}_data_t) in[{NAME}_KERNEL_SIZE_X][{NAME}_KERNEL_SIZE_Y],
+    stream_t({name}_data_t) &out
+) {{
+#pragma HLS INLINE OFF
+{avg_pool}
+}}
+
+void {name}(
+    stream_t({name}_data_t) in[{NAME}_COARSE],
+    stream_t({name}_data_t) out[{NAME}_COARSE],
+    int mode
+)
+{{
+    
+#pragma HLS INLINE OFF
+#pragma HLS DATAFLOW
+
+#pragma HLS STREAM variable=in depth={buffer_depth}
+#pragma HLS STREAM variable=out 
+
+#pragma HLS ARRAY_PARTITION variable=in complete dim=0
+#pragma HLS ARRAY_PARTITION variable=out complete dim=0
+
+    stream_t({name}_data_t) sw_out[{NAME}_COARSE][{NAME}_KERNEL_SIZE_X][{NAME}_KERNEL_SIZE_Y]; //sliding window output
+    
+#pragma HLS STREAM variable=sw_out
+#pragma HLS ARRAY_PARTITION variable=sw_out complete dim=0
+
+    for(unsigned int coarse_index=0; coarse_index<{NAME}_COARSE; coarse_index++)
+    {{
+#pragma HLS UNROLL
+        {name}_sliding_window(in[coarse_index],sw_out[coarse_index]);
+        {name}_avg_pool(sw_out[coarse_index],out[coarse_index]);
+    }}
+}}
+
+"""
+
+def gen_avg_pooling_layer(name,param,src_path,header_path):
+    
+    # get sliding window type
+    single_channel = True if param['channels_in'] == 1 else False
+    
+    # SLIDING WINDOW MODULE INIT
+    sliding_window = generate_sliding_window.gen_sliding_window_module(
+        name+"_sliding_window", 
+        "in", "out",
+        sliding_window_t=f"{name}_data_t",
+        indent=4
+    )
+    
+    # AVG_POOL MODULE INIT
+    avg_pool = generate_avg_pool.gen_avg_pool_module(
+        name+"_avg_pool",
+        "in", 
+        "out",
+        avg_pool_t=f"{name}_data_t",
+        indent=4
+    )
+    
+    # src
+    avg_pooling_layer_src = avg_pooling_layer_template_src.format(
+        name            =name,
+        NAME            =name.upper(),
+        # buffer_depth=max(param['buffer_depth'],2),
+        buffer_depth    =2,
+        sliding_window  =sliding_window,
+        avg_pool            =avg_pool
+    )
+    
+    # header
+    avg_pooling_layer_header = avg_pooling_layer_template_header.format(
+        name                =name,
+        NAME                =name.upper(),
+        id                  =0, # param['id'],
+        batch_size          =param['batch_size'],
+        rows                =param['rows_in'],
+        cols                =param['cols_in'],
+        channels            =param['channels_in'],
+        channels_per_module =param['channels_in']//param['coarse'],
+        coarse              =param['coarse'],
+        kernel_size_x       =param['kernel_size'][0],
+        kernel_size_y       =param['kernel_size'][1],
+        stride_x            =param['stride'][0],
+        stride_y            =param['stride'][1],
+        pad_left            =param['pad_left'],
+        pad_right           =param['pad_right'],
+        pad_top             =param['pad_top'],
+        pad_bottom          =param['pad_bottom'],
+        fine                =param['fine'] if 'fine' in param else "1",
+        rows_out            =param['rows_out'],
+        cols_out            =param['cols_out'],
+        channels_out        =param['channels_out'],
+        data_width          =param['data_t']['width'],
+        data_int_width      =(param['data_t']['width']-param['data_t']['binary_point'])
+    )
+    
+    # write source file
+    with open(src_path,'w') as src_file:
+        src_file.write(avg_pooling_layer_src)
+        
+    # write header file
+    with open(header_path,'w') as header_file:
+        header_file.write(avg_pooling_layer_header)
+        
+    return 
+
+
+
diff --git a/fpgaconvnet/hls/generate/layers/convolution.py b/fpgaconvnet/hls/generate/layers/convolution.py
index 2a7ce01..91ff60a 100644
--- a/fpgaconvnet/hls/generate/layers/convolution.py
+++ b/fpgaconvnet/hls/generate/layers/convolution.py
@@ -147,7 +147,7 @@
 }}
 
 void {name}_fork(
-#if {NAME}_KERNEL_SIZE_X == 1 && {NAME}_KERNEL_SIZE_Y == 1
+#if {NAME}_KERNEL_SIZE_X == 1 && {NAME}_KERNEL_SIZE_Y == 1 && {NAME}_STRIDE_X == 1 && {NAME}_STRIDE_Y == 1
     stream_t({name}_input_t)  &in,
     stream_t({name}_output_t) out[{NAME}_COARSE_OUT]
 #else
@@ -162,7 +162,7 @@
 
 void {name}_conv(
     const {name}_weight_t weights[DIVIDE({NAME}_WEIGHTS,{NAME}_COARSE_IN*{NAME}_COARSE_GROUP*{NAME}_COARSE_OUT*{NAME}_KERNEL_SIZE_X*{NAME}_KERNEL_SIZE_Y)][{NAME}_KERNEL_SIZE_X][{NAME}_KERNEL_SIZE_Y],
-#if {NAME}_KERNEL_SIZE_X == 1 && {NAME}_KERNEL_SIZE_Y == 1
+#if {NAME}_KERNEL_SIZE_X == 1 && {NAME}_KERNEL_SIZE_Y == 1 && {NAME}_STRIDE_X == 1 && {NAME}_STRIDE_Y == 1
     stream_t({name}_input_t) &in,
 #else
     stream_t({name}_input_t)  in[{NAME}_KERNEL_SIZE_X][{NAME}_KERNEL_SIZE_Y],
@@ -230,7 +230,7 @@
     #pragma HLS ARRAY_PARTITION variable=sw_out complete dim=0
 #endif
 
-#if {NAME}_KERNEL_SIZE_X == 1 && {NAME}_KERNEL_SIZE_Y == 1
+#if {NAME}_KERNEL_SIZE_X == 1 && {NAME}_KERNEL_SIZE_Y == 1 && {NAME}_STRIDE_X == 1 && {NAME}_STRIDE_Y == 1
     stream_t({name}_input_t) fork_out[{NAME}_COARSE_IN*{NAME}_COARSE_GROUP][{NAME}_COARSE_OUT];
 #else
     stream_t({name}_input_t) fork_out[{NAME}_COARSE_IN*{NAME}_COARSE_GROUP][{NAME}_COARSE_OUT][{NAME}_KERNEL_SIZE_X][{NAME}_KERNEL_SIZE_Y];
@@ -256,7 +256,7 @@
 
     {name}_coarse_in_loop: for(unsigned int i=0;i<{NAME}_COARSE_IN*{NAME}_COARSE_GROUP;i++) {{
         #pragma HLS unroll
-#if {NAME}_KERNEL_SIZE_X == 1 && {NAME}_KERNEL_SIZE_Y == 1
+#if {NAME}_KERNEL_SIZE_X == 1 && {NAME}_KERNEL_SIZE_Y == 1 && {NAME}_STRIDE_X == 1 && {NAME}_STRIDE_Y == 1
         {name}_fork(in[i], fork_out[i]);
 #else
         {name}_sliding_window(in[i], sw_out[i]);
@@ -318,7 +318,8 @@ def gen_convolution_layer(name, param, src_path, header_path):
     )
 
     # FORK MODULE INIT
-    fork = generate_fork.gen_fork_module(
+    fork = generate_fork.gen_conv_fork_module(
+        name, 
         name+"_fork",
         "in", "out",
         fork_t=f"{name}_input_t",
@@ -326,7 +327,8 @@ def gen_convolution_layer(name, param, src_path, header_path):
     )
 
     # CONV MODULE INIT
-    conv = generate_conv.gen_conv_module(
+    conv = generate_conv.gen_convolution_conv_module(
+        name, 
         name+"_conv",
         "in", "weights", "out",
         data_t=f"{name}_input_t",
@@ -406,16 +408,16 @@ def gen_convolution_layer(name, param, src_path, header_path):
         rows_out            =param['rows_out'],
         cols_out            =param['cols_out'],
         channels_out        =param['channels_out'],
-        input_width         =param['input_width'],
-        input_int_width     =param['input_width']//2,
-        output_width        =param['output_width'],
-        output_int_width    =param['output_width']//2,
-        acc_width           =param['acc_width'],
-        acc_int_width       =param['acc_width']//2,
-        weight_width        =param['weight_width'],
-        weight_int_width    =param['weight_width']//2,
-        biases_width        =param['biases_width'],
-        biases_int_width    =param['biases_width']//2,
+        input_width         =param['input_t']['width'],
+        input_int_width     =(param['input_t']['width'] - param['input_t']['binary_point']),
+        output_width        =param['output_t']['width'],
+        output_int_width    =(param['output_t']['width'] - param['output_t']['binary_point']),
+        acc_width           =param['acc_t']['width'],
+        acc_int_width       =(param['acc_t']['width'] - param['acc_t']['binary_point']),
+        weight_width        =param['weight_t']['width'],
+        weight_int_width    =(param['weight_t']['width'] - param['weight_t']['binary_point']),
+        biases_width        =param['acc_t']['width'],
+        biases_int_width    =(param['acc_t']['width'] - param['acc_t']['binary_point']),
         has_bias            =param['has_bias']
     )
 
diff --git a/fpgaconvnet/hls/generate/layers/elementwise_add.py b/fpgaconvnet/hls/generate/layers/elementwise_add.py
new file mode 100644
index 0000000..453ab6a
--- /dev/null
+++ b/fpgaconvnet/hls/generate/layers/elementwise_add.py
@@ -0,0 +1,143 @@
+# import modules 
+import fpgaconvnet.hls.generate.modules.elementwise_add as generate_elementwise_add
+
+elementwise_add_layer_template_header = """#ifndef {NAME}_HPP_
+#define {NAME}_HPP_
+
+#include "elementwise_add.hpp"
+
+#define name        {name}
+#define NAME        {NAME}
+#define {NAME}_ID   {id}
+
+#define {NAME}_BATCH_SIZE   {batch_size}
+#define {NAME}_ROWS         {rows}
+#define {NAME}_COLS         {cols}
+#define {NAME}_CHANNELS     {channels}
+#define {NAME}_COARSE       {coarse}
+
+#define {NAME}_COARSE_IN    {NAME}_COARSE
+#define {NAME}_COARSE_OUT   {NAME}_COARSE
+
+#define {NAME}_ROWS_OUT     {rows_out}
+#define {NAME}_COLS_OUT     {cols_out}
+#define {NAME}_CHANNELS_OUT {channels_out}
+
+#define {NAME}_ELEMENTWISE_ADD_BATCH_SIZE   {batch_size}
+#define {NAME}_ELEMENTWISE_ADD_ROWS         {rows}
+#define {NAME}_ELEMENTWISE_ADD_COLS         {cols}
+#define {NAME}_ELEMENTWISE_ADD_CHANNELS     {channels_per_module}
+
+typedef ap_fixed<{data_width},{data_int_width},AP_RND> {name}_data_t;
+typedef {name}_data_t {name}_input_t;
+typedef {name}_data_t {name}_output_t;
+
+/**
+  * FUNCTION DEFINITION
+  */
+  
+void {name}(
+    stream_t({name}_data_t) in_1[{NAME}_COARSE],
+    stream_t({name}_data_t) in_2[{NAME}_COARSE],
+    stream_t({name}_data_t) out[{NAME}_COARSE],
+    int mode
+);
+
+#undef name
+#undef NAME
+#endif
+"""
+
+elementwise_add_layer_template_src = """#include "{name}.hpp"
+
+void {name}_elementwise_add(
+    stream_t({name}_data_t) &in_1,
+    stream_t({name}_data_t) &in_2,
+    stream_t({name}_data_t) &out
+) {{
+
+#pragma HLS INLINE OFF
+{elementwise_add}
+}}
+
+void {name}(
+    stream_t({name}_data_t) in_1[{NAME}_COARSE],
+    stream_t({name}_data_t) in_2[{NAME}_COARSE],
+    stream_t({name}_data_t) out[{NAME}_COARSE],
+    int mode
+) 
+{{
+    
+#pragma HLS INLINE OFF
+
+// depth may need to be adjusted to avoid deadlocks
+#pragma HLS STREAM variable=in_1 depth=16284
+#pragma HLS STREAM variable=in_2 depth=16284
+#pragma HLS STREAM variable=out 
+
+#pragma HLS ARRAY_PARTITION variable=in_1 complete dim=0
+#pragma HLS ARRAY_PARTITION variable=in_2 complete dim=0
+#pragma HLS ARRAY_PARTITION variable=out complete dim=0
+
+#pragma HLS DATAFLOW
+
+    for (unsigned int coarse_index=0; coarse_index<{NAME}_COARSE; coarse_index++)
+    {{
+#pragma HLS unroll
+        {name}_elementwise_add(in_1[coarse_index], in_2[coarse_index], out[coarse_index]);
+    }}
+}}
+
+"""
+
+def gen_elementwise_add_layer(name,param,src_path,header_path):
+    
+    # ELEMENTWISE_ADD MODULE INIT
+    elementwise_add = generate_elementwise_add.gen_elementwise_add_module(
+        name+"_elementwise_add",
+        "in_1",
+        "in_2",
+        "out",
+        elementwise_add_t="data_t",
+        indent=4
+    )
+    
+    # src
+    elementwise_add_layer_src = elementwise_add_layer_template_src.format(
+        name = name, 
+        NAME = name.upper(),
+        # buffer_depth=max(param['buffer_depth'],2),
+        elementwise_add = elementwise_add
+    )
+    
+    # header
+    elementwise_add_layer_header = elementwise_add_layer_template_header.format(
+        name = name,
+        NAME = name.upper(),
+        id = 0, #param['id'],
+        batch_size = param['batch_size'],
+        rows = param['rows_in'],
+        cols = param['cols_in'],
+        channels = param['channels_in'],
+        channels_per_module = param['channels_in']//param['coarse_in'],
+        coarse = param['coarse_in'],
+        rows_out = param['rows_out'],
+        cols_out = param['cols_out'],
+        channels_out = param['channels_out'],
+        data_width = param['data_t']['width'],
+        data_int_width = param['data_t']['width']-param['data_t']['binary_point']
+        # data_width = 16,
+        # data_int_width = 8
+    )
+    
+    # write source file 
+    with open(src_path,'w') as src_file:
+        src_file.write(elementwise_add_layer_src)
+        
+    # write header file
+    with open(header_path,'w') as header_file:
+        header_file.write(elementwise_add_layer_header)
+        
+    return
+        
+        
\ No newline at end of file
diff --git a/fpgaconvnet/hls/generate/layers/elementwise_mul.py b/fpgaconvnet/hls/generate/layers/elementwise_mul.py
new file mode 100644
index 0000000..3509d82
--- /dev/null
+++ b/fpgaconvnet/hls/generate/layers/elementwise_mul.py
@@ -0,0 +1,139 @@
+# import modules 
+import fpgaconvnet.hls.generate.modules.elementwise_mul as generate_elementwise_mul
+
+elementwise_mul_layer_template_header = """#ifndef {NAME}_HPP_
+#define {NAME}_HPP_
+
+#include "elementwise_mul.hpp"
+
+#define name        {name}
+#define NAME        {NAME}
+#define {NAME}_ID   {id}
+
+#define {NAME}_BATCH_SIZE   {batch_size}
+#define {NAME}_ROWS         {rows}
+#define {NAME}_COLS         {cols}
+#define {NAME}_CHANNELS     {channels}
+#define {NAME}_COARSE       {coarse}
+
+#define {NAME}_COARSE_IN    {NAME}_COARSE
+#define {NAME}_COARSE_OUT   {NAME}_COARSE
+
+#define {NAME}_ROWS_OUT     {rows_out}
+#define {NAME}_COLS_OUT     {cols_out}
+#define {NAME}_CHANNELS_OUT {channels_out}
+
+#define {NAME}_ELEMENTWISE_MUL_BATCH_SIZE   {batch_size}
+#define {NAME}_ELEMENTWISE_MUL_ROWS         {rows}
+#define {NAME}_ELEMENTWISE_MUL_COLS         {cols}
+#define {NAME}_ELEMENTWISE_MUL_CHANNELS     {channels_per_module}
+
+typedef ap_fixed<{data_width},{data_int_width},AP_RND> {name}_data_t;
+typedef {name}_data_t {name}_input_t;
+typedef {name}_data_t {name}_output_t;
+
+/**
+    * FUNCTION DEFINITION
+    */
+    
+void {name}(
+    stream_t({name}_data_t) in_1[{NAME}_COARSE],
+    stream_t({name}_data_t) in_2[{NAME}_COARSE],
+    stream_t({name}_data_t) out[{NAME}_COARSE],
+    int mode
+);
+
+#undef name
+#undef NAME
+#endif
+"""
+
+elementwise_mul_layer_template_src = """#include "{name}.hpp"
+
+void {name}_elementwise_mul(
+    stream_t({name}_data_t) &in_1,
+    stream_t({name}_data_t) &in_2,
+    stream_t({name}_data_t) &out
+) {{
+    
+#pragma HLS INLINE OFF
+{elementwise_mul}
+}}
+
+void {name}(
+    stream_t({name}_data_t) in_1[{NAME}_COARSE],
+    stream_t({name}_data_t) in_2[{NAME}_COARSE],
+    stream_t({name}_data_t) out[{NAME}_COARSE],
+    int mode
+) {{
+
+#pragma HLS INLINE OFF
+
+#pragma HLS STREAM variable=in_1 depth=64
+#pragma HLS STREAM variable=in_2 depth=64
+#pragma HLS STREAM variable=out 
+
+#pragma HLS_ARRAY_PARTITION variable=in_1 complete dim=0
+#pragma HLS_ARRAY_PARTITION variable=in_2 complete dim=0
+#pragma HLS_ARRAY_PARTITION variable=out complete dim=0
+
+#pragma HLS DATAFLOW
+
+    for (unsigned int coarse_index=0; coarse_index<{NAME}_COARSE; coarse_index++)
+    {{
+        #pragma HLS UNROLL
+        {name}_elementwise_mul(in_1[coarse_index], in_2[coarse_index], out[coarse_index]);
+    }}
+}}
+
+"""
+
+def gen_elementwise_mul_layer(name, param, src_path, header_path): 
+    
+    # ELEMENTWISE_MUL MODULE INIT
+    elementwise_mul = generate_elementwise_mul.gen_elementwise_mul_module(
+        name+"_elementwise_mul",
+        "in_1",
+        "in_2",
+        "out",
+        elementwise_mul_t="data_t",
+        indent=4
+    )
+    
+    # src 
+    elementwise_mul_layer_src = elementwise_mul_layer_template_src.format(
+        name = name, 
+        NAME = name.upper(),
+        elementwise_mul = elementwise_mul
+    )
+    
+    # header
+    elementwise_mul_layer_header = elementwise_mul_layer_template_header.format(
+        name = name, 
+        NAME = name.upper(),
+        id = 0, #param['id'],
+        batch_size = param['batch_size'],
+        rows = param['rows_in'],
+        cols = param['cols_in'],
+        channels = param['channels_in'],
+        channels_per_module = param['channels_in']//param['coarse_in'],
+        coarse = param['coarse_in'],
+        rows_out = param['rows_out'],
+        cols_out = param['cols_out'],
+        channels_out = param['channels_out'],
+        data_width = param['data_t']['width'],
+        data_int_width = param['data_t']['width']-param['data_t']['binary_point']
+        # data_width = 16,
+        # data_int_width = 8
+    )
+    
+    # write source file
+    with open(src_path, 'w') as src_file:
+        src_file.write(elementwise_mul_layer_src)
+        
+    # write header file 
+    with open(header_path, 'w') as header_file:
+        header_file.write(elementwise_mul_layer_header)
+        
+    return 
+        
\ No newline at end of file
diff --git a/fpgaconvnet/hls/generate/layers/global_pooling.py b/fpgaconvnet/hls/generate/layers/global_pooling.py
new file mode 100644
index 0000000..fb792d0
--- /dev/null
+++ b/fpgaconvnet/hls/generate/layers/global_pooling.py
@@ -0,0 +1,126 @@
+import fpgaconvnet.hls.generate.modules.global_pool as generate_global_pool
+
+global_pooling_layer_template_header = """#ifndef {NAME}_HPP_
+#define {NAME}_HPP_
+
+#include "global_pool.hpp"
+
+#define name        {name}
+#define NAME        {NAME}
+#define {NAME}_ID   {id}
+
+#define {NAME}_BATCH_SIZE    {batch_size}
+#define {NAME}_ROWS          {rows}
+#define {NAME}_COLS          {cols}
+#define {NAME}_CHANNELS      {channels}
+#define {NAME}_COARSE        {coarse}
+
+#define {NAME}_COARSE_IN    {NAME}_COARSE
+#define {NAME}_COARSE_OUT   {NAME}_COARSE
+
+#define {NAME}_ROWS_OUT     {rows_out}
+#define {NAME}_COLS_OUT     {cols_out}
+#define {NAME}_CHANNELS_OUT {channels_out}
+
+#define {NAME}_GLOBAL_POOL_BATCH_SIZE    {batch_size}
+#define {NAME}_GLOBAL_POOL_ROWS          {rows}
+#define {NAME}_GLOBAL_POOL_COLS          {cols}
+#define {NAME}_GLOBAL_POOL_CHANNELS      {channels_per_module}
+
+typedef ap_fixed<{data_width},{data_int_width},AP_RND> {name}_data_t;
+typedef {name}_data_t {name}_input_t;
+typedef {name}_data_t {name}_output_t;
+
+/**
+  * FUNCTION DEFINITION
+  */
+  
+void {name}(
+    stream_t({name}_data_t) in[{NAME}_COARSE],
+    stream_t({name}_data_t) out[{NAME}_COARSE], 
+    int mode
+);
+
+#undef name
+#undef NAME
+#endif
+"""
+
+global_pppppooling_layer_template_src = """#include "{name}.hpp"
+
+void {name}_global_pool(
+    stream_t({name}_data_t) &in,
+    stream_t({name}_data_t) &out
+) {{
+#pragma HLS INLINE OFF
+{global_pool}
+}}
+
+void {name}(
+    stream_t({name}_data_t) in[{NAME}_COARSE],
+    stream_t({name}_data_t) out[{NAME}_COARSE], 
+    int mode
+) {{
+    #pragma HLS INLINE OFF
+    
+    #pragma HLS STREAM variable=in depth={buffer_depth}
+    #pragma HLS STREAM variable=out
+    
+    #pragma HLS ARRAY_PARTITION variable=in complete dim=0
+    #pragma HLS ARRAY_PARTITION variable=out complete dim=0
+    
+    #pragma HLS DATAFLOW
+    
+    for (unsigned int coarse_index=0; coarse_index<{NAME}_COARSE; coarse_index++) 
+    {{
+#pragma HLS UNROLL
+        {name}_global_pool(in[coarse_index], out[coarse_index]);
+    }}
+}}
+"""
+
+def gen_global_pooling_layer(name,param,src_path,header_path): 
+    
+    # GLOBAL POOL MODULE INIT
+    global_pool = generate_global_pool.gen_global_pool_module(
+        name+"_global_pool",
+        "in",
+        "out",
+        global_pool_t=f"{name}_data_t",
+        indent=4
+    )
+    
+    # src
+    global_pooling_layer_src = global_pppppooling_layer_template_src.format(
+        name=name,
+        NAME=name.upper(),
+        buffer_depth=max(param['buffer_depth'],2),
+        global_pool=global_pool
+    )
+    
+    global_pooling_layer_header = global_pooling_layer_template_header.format(
+        name                =name,
+        NAME                =name.upper(),
+        id                  =0, # param['id'],
+        batch_size          =param['batch_size'],
+        rows                =param['rows_in'],
+        cols                =param['cols_in'],
+        channels            =param['channels_in'],
+        channels_per_module =param['channels_in']//param['coarse'],
+        coarse              =param['coarse'],
+        rows_out            =param['rows_out'],
+        cols_out            =param['cols_out'],
+        channels_out        =param['channels_out'],
+        data_width          =param['data_t']['width'],
+        data_int_width      =param['data_t']['width']-param['data_t']['binary_point']
+    )
+    
+    # write source file 
+    with open(src_path, 'w') as src_file: 
+        src_file.write(global_pooling_layer_src)
+        
+    # write header file
+    with open(header_path, 'w') as header_file: 
+        header_file.write(global_pooling_layer_header)
+        
+    return 
\ No newline at end of file
diff --git a/fpgaconvnet/hls/generate/layers/inner_product.py b/fpgaconvnet/hls/generate/layers/inner_product.py
index d8beeca..553365d 100644
--- a/fpgaconvnet/hls/generate/layers/inner_product.py
+++ b/fpgaconvnet/hls/generate/layers/inner_product.py
@@ -324,16 +324,16 @@ def gen_inner_product_layer(name,param,src_path,header_path):
         filters_per_module  =param['filters']//param['coarse_out'],
         coarse_in           =param['coarse_in'],
         coarse_out          =param['coarse_out'],
-        input_width         =param['input_width'],
-        input_int_width     =param['input_width']//2,
-        output_width        =param['output_width'],
-        output_int_width    =param['output_width']//2,
-        acc_width           =param['acc_width'],
-        acc_int_width       =param['acc_width']//2,
-        weight_width        =param['weight_width'],
-        weight_int_width    =param['weight_width']//2,
-        biases_width        =param['biases_width'],
-        biases_int_width    =param['biases_width']//2,
+        input_width         =param['input_t']['width'],
+        input_int_width     =(param['input_t']['width'] - param['input_t']['binary_point']),
+        output_width        =param['output_t']['width'],
+        output_int_width    =(param['output_t']['width'] - param['output_t']['binary_point']),
+        acc_width           =param['acc_t']['width'],
+        acc_int_width       =(param['acc_t']['width'] - param['acc_t']['binary_point']),
+        weight_width        =param['weight_t']['width'],
+        weight_int_width    =(param['weight_t']['width'] - param['weight_t']['binary_point']),
+        biases_width        =param['acc_t']['width'],
+        biases_int_width    =(param['acc_t']['width'] - param['acc_t']['binary_point']),
         has_bias            =param['has_bias'],
     )
 
diff --git a/fpgaconvnet/hls/generate/layers/pooling.py b/fpgaconvnet/hls/generate/layers/pooling.py
index d9735f9..0a23db0 100644
--- a/fpgaconvnet/hls/generate/layers/pooling.py
+++ b/fpgaconvnet/hls/generate/layers/pooling.py
@@ -147,7 +147,7 @@ def gen_pooling_layer(name,param,src_path,header_path):
     pooling_layer_src = pooling_layer_template_src.format(
         name            =name,
         NAME            =name.upper(),
-        buffer_depth=max(param['buffer_depth'],2),
+        buffer_depth    =max(param['buffer_depth'],2),
         sliding_window  =sliding_window,
         pool            =pool
     )
@@ -175,8 +175,8 @@ def gen_pooling_layer(name,param,src_path,header_path):
         rows_out            =param['rows_out'],
         cols_out            =param['cols_out'],
         channels_out        =param['channels_out'],
-        data_width          =param['data_width'],
-        data_int_width      =param['data_width']//2,
+        data_width          =param['data_t']['width'],
+        data_int_width      =(param['data_t']['width']-param['data_t']['binary_point']),
     )
 
     # write source file
diff --git a/fpgaconvnet/hls/generate/layers/relu.py b/fpgaconvnet/hls/generate/layers/relu.py
index f03b7be..b313674 100644
--- a/fpgaconvnet/hls/generate/layers/relu.py
+++ b/fpgaconvnet/hls/generate/layers/relu.py
@@ -120,8 +120,8 @@ def gen_relu_layer(name,param,src_path,header_path):
         rows_out            =param['rows_out'],
         cols_out            =param['cols_out'],
         channels_out        =param['channels_out'],
-        data_width          =param['data_width'],
-        data_int_width      =param['data_width']//2
+        data_width          =param['data_t']['width'],
+        data_int_width      =(param['data_t']['width']-param['data_t']['binary_point']),
     )
 
     # write source file
diff --git a/fpgaconvnet/hls/generate/layers/split.py b/fpgaconvnet/hls/generate/layers/split.py
new file mode 100644
index 0000000..e7164e8
--- /dev/null
+++ b/fpgaconvnet/hls/generate/layers/split.py
@@ -0,0 +1,166 @@
+import fpgaconvnet.hls.generate.modules.fork as generate_fork
+
+split_layer_template_header = """#ifndef {NAME}_HPP_
+#define {NAME}_HPP_
+
+#include "fork.hpp"
+
+#define name        {name}
+#define NAME        {NAME}
+#define {NAME}_ID   {id}
+
+#define {NAME}_BATCH_SIZE   {batch_size}
+#define {NAME}_ROWS         {rows}
+#define {NAME}_COLS         {cols}
+#define {NAME}_CHANNELS     {channels}
+#define {NAME}_COARSE       {coarse}
+#define {NAME}_KERNEL_SIZE_X 1
+#define {NAME}_KERNEL_SIZE_Y 1
+
+#define {NAME}_COARSE_IN    {NAME}_COARSE
+#define {NAME}_COARSE_OUT   {NAME}_COARSE
+
+#define {NAME}_ROWS_OUT     {rows_out}
+#define {NAME}_COLS_OUT     {cols_out}
+#define {NAME}_CHANNELS_OUT {channels_out}
+
+#define {NAME}_FORK_BATCH_SIZE    {batch_size}
+#define {NAME}_FORK_ROWS          {rows}
+#define {NAME}_FORK_COLS          {cols}
+#define {NAME}_FORK_CHANNELS      {channels_per_module}
+#define {NAME}_FORK_COARSE        2
+#define {NAME}_FORK_KERNEL_SIZE_X 1
+#define {NAME}_FORK_KERNEL_SIZE_Y 1
+
+typedef ap_fixed<{data_width},{data_int_width},AP_RND> {name}_data_t;
+typedef {name}_data_t {name}_input_t;
+typedef {name}_data_t {name}_output_t;
+
+/**
+ * FUNCTION DEFINITION
+ */
+
+void {name}(
+    stream_t({name}_data_t) in[{NAME}_COARSE],
+    stream_t({name}_data_t) out_1[{NAME}_COARSE],
+    stream_t({name}_data_t) out_2[{NAME}_COARSE],
+    int mode
+);
+
+#undef name
+#undef NAME
+#endif
+"""
+    
+    
+split_layer_template_src = """#include "{name}.hpp"
+
+void {name}_fork(
+#if {NAME}_KERNEL_SIZE_X == 1 && {NAME}_KERNEL_SIZE_Y == 1
+    stream_t({name}_input_t)  &in,
+    stream_t({name}_output_t) out[{NAME}_COARSE_OUT]
+#else
+    stream_t({name}_input_t)  in[{NAME}_KERNEL_SIZE_X][{NAME}_KERNEL_SIZE_Y],
+    stream_t({name}_output_t) out[{NAME}_COARSE_OUT][{NAME}_KERNEL_SIZE_X][{NAME}_KERNEL_SIZE_Y]
+#endif
+) {{
+
+#pragma HLS INLINE OFF
+{fork}
+}}
+
+void {name}(
+    stream_t({name}_data_t) in[{NAME}_COARSE],
+    stream_t({name}_data_t) out_1[{NAME}_COARSE],
+    stream_t({name}_data_t) out_2[{NAME}_COARSE],
+    int mode
+) {{
+    
+#pragma HLS INLINE OFF
+
+#pragma HLS STREAM variable=in depth={buffer_depth}
+#pragma HLS STREAM variable=out_1 
+#pragma HLS STREAM variable=out_2
+
+#pragma HLS ARRAY_PARTITION variable=in complete dim=0
+#pragma HLS ARRAY_PARTITION variable=out_1 complete dim=0
+#pragma HLS ARRAY_PARTITION variable=out_2 complete dim=0
+
+#pragma HLS DATAFLOW
+
+    stream_t({name}_input_t) fork_out[{NAME}_COARSE_IN][{NAME}_FORK_COARSE];
+    #pragma HLS STREAM variable=fork_out
+    #pragma HLS ARRAY_PARTITION variable=fork_out complete dim=0
+
+    for(unsigned int coarse_index=0; coarse_index<{NAME}_COARSE; coarse_index++)
+    {{
+#pragma HLS unroll
+        {name}_fork(in[coarse_index], fork_out[coarse_index]);
+    }}
+    
+    for (unsigned long pixel_index=0; pixel_index< DIVIDE({NAME}_BATCH_SIZE*{NAME}_ROWS*{NAME}_COLS*{NAME}_CHANNELS,{NAME}_COARSE); pixel_index++)
+    {{
+        #pragma HLS PIPELINE II=1
+        for(unsigned int coarse_index=0; coarse_index<{NAME}_COARSE; coarse_index++)
+        {{
+            #pragma HLS unroll
+            {name}_data_t tmp_1 = fork_out[coarse_index][0].read();
+            {name}_data_t tmp_2 = fork_out[coarse_index][1].read();
+            out_1[coarse_index].write(tmp_1);
+            out_2[coarse_index].write(tmp_2);
+        }}  
+    }}
+}}
+
+"""
+    
+    
+def gen_split_layer(name,param,src_path,header_path):
+    
+    # FORK MODULE INIT
+    fork = generate_fork.gen_fork_module(
+        name+"_fork",
+        "in",
+        "out",
+        fork_t="data_t",
+        indent=4
+    )
+    
+    # src 
+    split_layer_src = split_layer_template_src.format(
+        name=name,
+        NAME=name.upper(),
+        buffer_depth=max(param['buffer_depth'],2),
+        # buffer_depth=2,
+        fork=fork
+    )
+    
+    # header
+    split_layer_header = split_layer_template_header.format(
+        name                =name,
+        NAME                =name.upper(),
+        id                  =0, # param['id'],
+        batch_size          =param['batch_size'],
+        rows                =param['rows_in'],
+        cols                =param['cols_in'],
+        channels            =param['channels_in'],
+        channels_per_module =param['channels_in']//param['coarse_in'],
+        coarse              =param['coarse_in'],
+        rows_out            =param['rows_out'],
+        cols_out            =param['cols_out'],
+        channels_out        =param['channels_out'],
+        data_width          =param['data_t']['width'],
+        data_int_width      =(param['data_t']['width']-param['data_t']['binary_point'])
+        # data_width          =16,
+        # data_int_width      =8
+    )
+    
+    # write source file
+    with open(src_path,'w') as src_file:
+        src_file.write(split_layer_src)
+        
+    # write header file
+    with open(header_path,'w') as header_file:
+        header_file.write(split_layer_header)
+        
+    return 
diff --git a/fpgaconvnet/hls/generate/layers/squeeze.py b/fpgaconvnet/hls/generate/layers/squeeze.py
index 3b5c647..0d476b9 100644
--- a/fpgaconvnet/hls/generate/layers/squeeze.py
+++ b/fpgaconvnet/hls/generate/layers/squeeze.py
@@ -111,8 +111,8 @@ def gen_squeeze_layer(name,param,src_path,header_path):
         rows_out            =param['rows_out'],
         cols_out            =param['cols_out'],
         channels_out        =param['channels_out'],
-        data_width          =param['data_width'],
-        data_int_width      =param['data_width']//2,
+        data_width          =param['data_t']['width'],
+        data_int_width      =(param['data_t']['width']-param['data_t']['binary_point']),
         buffer_size         =lcm(param['coarse_in'],param['coarse_out']),
     )
 
diff --git a/fpgaconvnet/hls/generate/modules/avg_pool.py b/fpgaconvnet/hls/generate/modules/avg_pool.py
new file mode 100644
index 0000000..524c4df
--- /dev/null
+++ b/fpgaconvnet/hls/generate/modules/avg_pool.py
@@ -0,0 +1,22 @@
+avg_pool_template="""
+{indent}avg_pool<
+{indent}    {NAME}_BATCH_SIZE,
+{indent}    {NAME}_ROWS,
+{indent}    {NAME}_COLS,
+{indent}    {NAME}_CHANNELS,
+{indent}    {NAME}_KERNEL_SIZE_X,
+{indent}    {NAME}_KERNEL_SIZE_Y, 
+{indent}    {avg_pool_t}
+{indent}>({input_stream},{output_stream});
+"""
+
+def gen_avg_pool_module(name,input_stream,output_stream,
+        avg_pool_t="data_t",indent=0):
+    return avg_pool_template.format(
+        NAME            =name.upper(),
+        input_stream    =input_stream,
+        output_stream   =output_stream,
+        avg_pool_t      =avg_pool_t,
+        indent          =" "*indent
+    )
+    
\ No newline at end of file
diff --git a/fpgaconvnet/hls/generate/modules/conv.py b/fpgaconvnet/hls/generate/modules/conv.py
index 3ec8eb8..7d4dfe0 100644
--- a/fpgaconvnet/hls/generate/modules/conv.py
+++ b/fpgaconvnet/hls/generate/modules/conv.py
@@ -6,7 +6,8 @@
 {indent}    {NAME}_CHANNELS,
 {indent}    {NAME}_FILTERS,
 {indent}    {NAME}_GROUPS,
-#if ({NAME}_KERNEL_SIZE_X > 1) || ({NAME}_KERNEL_SIZE_Y > 1)
+{_if} ({NAME}_KERNEL_SIZE_X > 1) || ({NAME}_KERNEL_SIZE_Y > 1)
+{_if_conv} ({NAME}_KERNEL_SIZE_X > 1) || ({NAME}_KERNEL_SIZE_Y > 1) || ({LAYER_NAME}_STRIDE_X > 1) || ({LAYER_NAME}_STRIDE_Y > 1)
 {indent}    {NAME}_FINE,
 {indent}    {NAME}_KERNEL_SIZE_X,
 {indent}    {NAME}_KERNEL_SIZE_Y,
@@ -20,6 +21,7 @@
 def gen_conv_module(name,input_stream,weights_stream,output_stream,
         data_t="data_t",weight_t="weight_t",acc_t="acc_t",indent=0):
     return conv_template.format(
+        LAYER_NAME      =name.upper(),
         NAME            =name.upper(),
         input_stream    =input_stream,
         weights_stream  =weights_stream,
@@ -27,5 +29,24 @@ def gen_conv_module(name,input_stream,weights_stream,output_stream,
         data_t          =data_t,
         weight_t        =weight_t,
         acc_t           =acc_t,
+        _if             ="#if",
+        _if_conv        ="//",
         indent          =" "*indent
     )
+
+def gen_convolution_conv_module(layer_name,name,input_stream,weights_stream,output_stream,
+        data_t="data_t",weight_t="weight_t",acc_t="acc_t",indent=0):
+    return conv_template.format(
+        LAYER_NAME      =layer_name.upper(),
+        NAME            =name.upper(),
+        input_stream    =input_stream,
+        weights_stream  =weights_stream,
+        output_stream   =output_stream,
+        data_t          =data_t,
+        weight_t        =weight_t,
+        acc_t           =acc_t,
+        _if             ="//",
+        _if_conv        ="#if",
+        indent          =" "*indent
+    )
+
diff --git a/fpgaconvnet/hls/generate/modules/elementwise_add.py b/fpgaconvnet/hls/generate/modules/elementwise_add.py
new file mode 100644
index 0000000..4dfe155
--- /dev/null
+++ b/fpgaconvnet/hls/generate/modules/elementwise_add.py
@@ -0,0 +1,20 @@
+elementwise_add_template = """
+{indent}elementwise_add<
+{indent}    {NAME}_BATCH_SIZE,
+{indent}    {NAME}_ROWS,
+{indent}    {NAME}_COLS,
+{indent}    {NAME}_CHANNELS,
+{indent}    {elementwise_add_t}
+{indent}>({input_stream_1},{input_stream_2},{output_stream});
+"""
+
+def gen_elementwise_add_module(name,input_stream_1,input_stream_2,output_stream,
+        elementwise_add_t="data_t",indent=0):
+    return elementwise_add_template.format(
+        NAME                =name.upper(),
+        input_stream_1      =input_stream_1,
+        input_stream_2      =input_stream_2,
+        output_stream       =output_stream,
+        elementwise_add_t   =elementwise_add_t,
+        indent              =" "*indent
+    )
\ No newline at end of file
diff --git a/fpgaconvnet/hls/generate/modules/elementwise_mul.py b/fpgaconvnet/hls/generate/modules/elementwise_mul.py
new file mode 100644
index 0000000..fdf2b41
--- /dev/null
+++ b/fpgaconvnet/hls/generate/modules/elementwise_mul.py
@@ -0,0 +1,20 @@
+elementwise_mul_template = """
+{indent}elementwise_mul<
+{indent}    {NAME}_BATCH_SIZE,
+{indent}    {NAME}_ROWS,
+{indent}    {NAME}_COLS,
+{indent}    {NAME}_CHANNELS,
+{indent}    {elementwise_mul_t}
+{indent}>({input_stream_1},{input_stream_2},{output_stream});
+"""
+
+def gen_elementwise_mul_module(name,input_stream_1,input_stream_2,output_stream,
+        elementwise_mul_t="data_t",indent=0):
+    return elementwise_mul_template.format(
+        NAME                =name.upper(),
+        input_stream_1      =input_stream_1,
+        input_stream_2      =input_stream_2,
+        output_stream       =output_stream,
+        elementwise_mul_t   =elementwise_mul_t,
+        indent              =" "*indent
+    )
\ No newline at end of file
diff --git a/fpgaconvnet/hls/generate/modules/fork.py b/fpgaconvnet/hls/generate/modules/fork.py
index 8dee39d..6623c9b 100644
--- a/fpgaconvnet/hls/generate/modules/fork.py
+++ b/fpgaconvnet/hls/generate/modules/fork.py
@@ -5,7 +5,8 @@
 {indent}    {NAME}_COLS,
 {indent}    {NAME}_CHANNELS,
 {indent}    {NAME}_COARSE,
-#if {NAME}_KERNEL_SIZE_X > 1 || {NAME}_KERNEL_SIZE_Y > 1
+{_if}       {NAME}_KERNEL_SIZE_X > 1 || {NAME}_KERNEL_SIZE_Y > 1
+{_if_conv}  {NAME}_KERNEL_SIZE_X > 1 || {LAYER_NAME}_KERNEL_SIZE_Y > 1 || {LAYER_NAME}_STRIDE_X > 1 || {NAME}_STRIDE_Y > 1
 {indent}    {NAME}_KERNEL_SIZE_X,
 {indent}    {NAME}_KERNEL_SIZE_Y,
 #endif
@@ -16,10 +17,26 @@
 def gen_fork_module(name,input_stream,output_stream,
         fork_t="data_t",indent=0):
     return fork_template.format(
+        LAYER_NAME      =name.upper(),
         NAME            =name.upper(),
         input_stream    =input_stream,
         output_stream   =output_stream,
         fork_t          =fork_t,
+        _if             ="#if",
+        _if_conv        ="//",
+        indent          =" "*indent
+    )
+    
+def gen_conv_fork_module(layer_name, name,input_stream,output_stream,
+        fork_t="data_t",indent=0):
+    return fork_template.format(
+        LAYER_NAME      =layer_name.upper(),
+        NAME            =name.upper(),
+        input_stream    =input_stream,
+        output_stream   =output_stream,
+        fork_t          =fork_t,
+        _if             ="//",
+        _if_conv        ="#if",
         indent          =" "*indent
     )
 
diff --git a/fpgaconvnet/hls/generate/modules/global_pool.py b/fpgaconvnet/hls/generate/modules/global_pool.py
new file mode 100644
index 0000000..bd06f6e
--- /dev/null
+++ b/fpgaconvnet/hls/generate/modules/global_pool.py
@@ -0,0 +1,19 @@
+global_pool_template="""
+{indent}global_pool<
+{indent}    {NAME}_BATCH_SIZE,
+{indent}    {NAME}_ROWS,
+{indent}    {NAME}_COLS,
+{indent}    {NAME}_CHANNELS,
+{indent}    {global_pool_t}
+{indent}>({input_stream},{output_stream});
+"""
+
+def gen_global_pool_module(name,input_stream,output_stream,
+        global_pool_t="data_t",indent=0):
+    return global_pool_template.format(
+        NAME            =name.upper(),
+        input_stream    =input_stream,
+        output_stream   =output_stream,
+        global_pool_t   =global_pool_t,
+        indent          =" "*indent
+    )
\ No newline at end of file
diff --git a/fpgaconvnet/hls/generate/network.py b/fpgaconvnet/hls/generate/network.py
index 516e66e..cbc07f5 100644
--- a/fpgaconvnet/hls/generate/network.py
+++ b/fpgaconvnet/hls/generate/network.py
@@ -4,9 +4,12 @@
 import numpy as np
 
 import fpgaconvnet.proto.fpgaconvnet_pb2
-import fpgaconvnet.tools.onnx_helper as onnx_helper
+from fpgaconvnet.parser.Parser import Parser
+import fpgaconvnet.parser.onnx.helper as onnx_helper
 
 from fpgaconvnet.hls.generate.partition import GeneratePartition
+from fpgaconvnet.hls.generate.network_template import *
+from pathlib import Path
 
 class GenerateNetwork:
     """
@@ -23,7 +26,7 @@ class GenerateNetwork:
         from the `partition_path`
     """
 
-    def __init__(self, name, partition_path, model_path, fpga_part="xc7z045ffg900-2", clk=5):
+    def __init__(self, name, partition_path, model_path, fpga_part="xc7z045ffg900-2", clk=5, port_width=64):
 
         # save name
         self.name = name
@@ -31,56 +34,102 @@ def __init__(self, name, partition_path, model_path, fpga_part="xc7z045ffg900-2"
         # save platform information
         self.fpga_part = fpga_part
         self.clk = clk
+        
+        # save port_width
+        self.port_width = port_width
+        
+        # configure data types
+        if self.port_width == 8: 
+            write_data_type = "ap_uint<8>"
+            write_ap_axis_param = "8,1,1,1"
+        elif self.port_width == 16:
+            write_data_type = "ap_uint<16>"
+            write_ap_axis_param = "16,1,1,1"
+        elif self.port_width == 32:
+            write_data_type = "ap_uint<32>"
+            write_ap_axis_param = "32,1,1,1"
+        elif self.port_width == 64:
+            write_data_type = "ap_uint<64>"
+            write_ap_axis_param = "64,1,1,1"
+        elif self.port_width == 128:
+            write_data_type = "ap_uint<128>"
+            write_ap_axis_param = "128,1,1,1"
+        else:
+            raise ValueError("port_width must be 8, 16, 32, 64, or 128")
+        
+        # write common.hpp 
+        write_common = config_common.format(
+            DATA_TYPE = write_data_type, 
+            AP_AXIS_PARAM = write_ap_axis_param
+        )
+        with open (Path(__file__).parent.parent /"hardware"/"common.hpp", "w") as f:
+            f.write(write_common)
+        
+        # write common_tb.hpp
+        write_common_tb = config_common_tb.format(
+            DMA_WIDTH = self.port_width
+        )
+        with open (Path(__file__).parent.parent /"hardware"/"common_tb.hpp", "w") as f:
+            f.write(write_common_tb)
 
         # load partition information
         self.partitions = fpgaconvnet.proto.fpgaconvnet_pb2.partitions()
         with open(partition_path,'r') as f:
            json_format.Parse(f.read(), self.partitions)
 
-        # load onnx model
-        self.model = onnx_helper.load(model_path)
-        self.model = onnx_helper.update_batch_size(self.model, 1) # TODO
-        # self.model = onnx_helper.update_batch_size(self.model,self.partition.batch_size)
-
-        # remove biases
-        for partition in self.partitions.partition:
-            for layer in partition.layers:
-                if layer.bias_path:
-                    initializer = onnx_helper.get_model_initializer(self.model, layer.bias_path, to_tensor=False)
-                    # TODO: seems like theres no bias initializer for inner product layer
-                    if not initializer:
-                        continue
-                    zeroes = np.zeros(onnx.numpy_helper.to_array(initializer).shape).astype(np.float32)
-                    initializer_new = onnx.numpy_helper.from_array(zeroes,name=initializer.name)
-                    self.model.graph.initializer.remove(initializer)
-                    self.model.graph.initializer.extend([initializer_new])
+        # set up parser
+        print("FIXME: use HLS backend in Parser")
+        self.parser = Parser(backend="chisel", batch_size=self.partitions.partition[0].batch_size) # FIXME for hls backend, resource model is missing
+        # modify network to be supported by onnx
+        model = onnx.load(model_path)
+        nodes = model.graph.node
+        for node in nodes:
+            attrs = node.attribute
+            for attr in attrs[:]:
+                if attr.name == 'weight_width': 
+                    assert attr.i == 16, "weight_width must be 16"
+                    attrs.remove(attr)
+                elif attr.name == 'data_width':
+                    assert attr.i == 16, "data_width must be 16"
+                    attrs.remove(attr)
+                elif attr.name == 'acc_width':
+                    attrs.remove(attr)
+                elif attr.name == 'block_floating_point':
+                    assert attr.s == (b''), "block_floating_point must be false"
+                    attrs.remove(attr)
+        new_model_path = model_path.replace('.onnx','_new.onnx')
+        onnx.save(model, new_model_path)
+        # load network (parser onnx model)
+        self.net = self.parser.onnx_to_fpgaconvnet(new_model_path)
+        # load the existing partition information into the net object
+        self.net = self.parser.prototxt_to_fpgaconvnet(self.net,partition_path)
 
         # add intermediate layers to outputs
-        for node in self.model.graph.node:
+        for node in self.net.model.graph.node:
             layer_info = onnx.helper.ValueInfoProto()
             layer_info.name = node.output[0]
-            self.model.graph.output.append(layer_info)
+            self.net.model.graph.output.append(layer_info)
 
-        # add input aswell to output
+        # add input aswell to outpuexisting t
         layer_info = onnx.helper.ValueInfoProto()
-        layer_info.name = self.model.graph.input[0].name
-        self.model.graph.output.append(layer_info)
+        layer_info.name = self.net.model.graph.input[0].name
+        self.net.model.graph.output.append(layer_info)
 
         # remove input initializers
         name_to_input = {}
-        inputs = self.model.graph.input
+        inputs = self.net.model.graph.input
         for input in inputs:
             name_to_input[input.name] = input
-        for initializer in self.model.graph.initializer:
+        for initializer in self.net.model.graph.initializer:
             if initializer.name in name_to_input:
                 inputs.remove(name_to_input[initializer.name])
 
         # inference session
-        self.sess = onnxruntime.InferenceSession(self.model.SerializeToString())
+        self.sess = onnxruntime.InferenceSession(self.net.model.SerializeToString())
 
         # create generator for each partition
         self.partitions_generator = [ GeneratePartition(
-            self.name, partition, self.model, self.sess, f"partition_{i}") for \
+            self.name, partition, self.net.model, self.sess, f"partition_{i}", self.port_width) for \
                     i, partition in enumerate(self.partitions.partition) ]
 
         # flags
@@ -193,4 +242,3 @@ def generate_all_partitions(self, num_jobs=1):
         for i in range(len(self.partitions_generator)):
             self.generate_partition(i)
 
-
diff --git a/fpgaconvnet/hls/generate/network_template.py b/fpgaconvnet/hls/generate/network_template.py
new file mode 100644
index 0000000..2096ebd
--- /dev/null
+++ b/fpgaconvnet/hls/generate/network_template.py
@@ -0,0 +1,817 @@
+config_common = """/**
+ * Copyright (C) 2022 Alexander Montgomerie-Corcoran
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef COMMON_HPP_
+#define COMMON_HPP_
+
+#include <ap_int.h>
+#include <ap_fixed.h>
+#include "hls_stream.h"
+#include "hls_math.h"
+#include "ap_axi_sdata.h"
+#include "stdint.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+
+#ifdef SYSTEM_HPP_
+#include "system.hpp"
+#endif
+
+// hlslib imports
+#include "hlslib/xilinx/Flatten.h"
+
+#define ERROR_TOLERANCE 0.2
+
+// macro for template variable pragmas
+#define PRAGMA_SUB(x) _Pragma (#x)
+#define DO_PRAGMA(x) PRAGMA_SUB(x)
+
+// macro for integer division
+#define DIVIDE(a,b) ((const unsigned int) ((a)/(b)))
+
+// macro for evaluating min and max
+#define MAX(a,b) (((a)>(b))?(a):(b))
+#define MIN(a,b) (((a)<(b))?(a):(b))
+
+// macro for stream wrapper
+#define stream_t(x) hls::stream<x>
+/* #define stream_t(x) hlslib::Stream<x> */
+
+// default data types
+typedef {DATA_TYPE} mem_int;
+typedef ap_axis<{AP_AXIS_PARAM}> axi_stream_t;
+typedef hls::stream<axi_stream_t> axi_stream_hw_t;
+
+// <total_width, int_width, rounding type>
+typedef ap_fixed<16, 8,AP_RND> data_t;
+typedef ap_fixed<30,16,AP_RND> acc_t;
+typedef ap_fixed<16, 8,AP_RND> weight_t;
+
+#endif
+"""
+
+config_common_tb = """/**
+ * Copyright (C) 2022 Alexander Montgomerie-Corcoran
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef COMMON_TB_HPP_
+#define COMMON_TB_HPP_
+
+#include "common.hpp"
+#include <string>
+#include <iostream>
+#include <fstream>
+#include <typeinfo>
+#include <stdio.h>
+#include <stdlib.h>
+
+//////////////////////////////////////////////////////////////////////
+/////////////////////////////// LAYERS ///////////////////////////////
+//////////////////////////////////////////////////////////////////////
+
+template<int SIZE, int STREAMS, typename T>
+void load_data(
+    std::string filepath,
+    T data[SIZE][STREAMS]
+) {{
+    // read in file
+    const char *filepath_cstr = filepath.c_str();
+    FILE * fp = fopen(filepath_cstr,"r");
+
+    // check file opened
+    if (fp == NULL) {{
+        perror("Failed: ");
+    }}
+
+    // save to array
+    int index = 0;
+    for(int i=0;i<SIZE;i++) {{
+        for(int j=0;j<STREAMS;j++) {{
+            float val;
+            fscanf(fp,"%f\\n", &val);
+            data[i][j] = T( val );
+        }}
+    }}
+
+    // close file
+    fclose(fp);
+}}
+
+template<int SIZE, int STREAMS, typename T>
+void to_stream(
+    T in[SIZE][STREAMS],
+    stream_t(T) out[STREAMS]
+) {{
+    for(int i=0;i<SIZE;i++) {{
+        for(int j=0;j<STREAMS;j++) {{
+ 	    out[j].write(in[i][j]);
+	}}
+    }}
+}}
+
+///////////////////////////////////////////////////////////////////////
+/////////////////////////////// MODULES ///////////////////////////////
+///////////////////////////////////////////////////////////////////////
+
+/* (ACCUM) */
+template<int SIZE, typename T>
+void load_data(
+    std::string filepath,
+    T data[SIZE]
+) {{
+    // read in file
+    const char *filepath_cstr = filepath.c_str();
+    FILE * fp = fopen(filepath_cstr,"r");
+
+    // check file opened
+    if (fp == NULL) {{
+        perror("Failed: ");
+    }}
+
+    // save to array
+    for(int i=0;i<SIZE;i++) {{
+        float val;
+        fscanf(fp,"%f\\n", &val);
+        data[i] = T( val );
+    }}
+
+    // close file
+    fclose(fp);
+}}
+
+template<int SIZE, typename T>
+void to_stream(
+    T in[SIZE],
+    stream_t(T) &out
+)
+{{
+    for(int i=0;i<SIZE;i++) {{
+ 	out.write(in[i]);
+    }}
+}}
+
+/* (CONV,POOL) */
+template<int SIZE, int KERNEL_SIZE_X, int KERNEL_SIZE_Y, typename T>
+void load_data(
+    std::string filepath,
+    T data[SIZE][KERNEL_SIZE_X][KERNEL_SIZE_Y]
+) {{
+
+    // read in file
+    const char *filepath_cstr = filepath.c_str();
+    FILE * fp = fopen(filepath_cstr,"r");
+
+    // check file opened
+    if (fp == NULL) {{
+        perror("Failed: ");
+    }}
+     // save to array
+    for(int i=0;i<SIZE;i++) {{
+        for(int k1=0;k1<KERNEL_SIZE_X;k1++) {{
+            for(int k2=0;k2<KERNEL_SIZE_Y;k2++) {{
+                float val;
+                fscanf(fp,"%f\\n", &val);
+                data[i][k1][k2] = T(val);
+	        }}
+	    }}
+    }}
+
+    // close file
+    fclose(fp);
+}}
+
+template<int SIZE, int KERNEL_SIZE_X, int KERNEL_SIZE_Y, typename T>
+void to_stream(
+    T in[SIZE][KERNEL_SIZE_X][KERNEL_SIZE_Y],
+    stream_t(T) out[KERNEL_SIZE_X][KERNEL_SIZE_Y]
+) {{
+    for(int i=0;i<SIZE;i++) {{
+        for(int k1=0;k1<KERNEL_SIZE_X;k1++) {{
+            for(int k2=0;k2<KERNEL_SIZE_Y;k2++) {{
+                out[k1][k2].write(in[i][k1][k2]);
+            }}
+	    }}
+    }}
+}}
+
+/* (FORK) */
+template<int SIZE, int COARSE, int KERNEL_SIZE_X, int KERNEL_SIZE_Y, typename T>
+void load_data(
+    std::string filepath,
+    T data[SIZE][COARSE][KERNEL_SIZE_X][KERNEL_SIZE_Y]
+) {{
+    // read in file
+    const char *filepath_cstr = filepath.c_str();
+    FILE * fp = fopen(filepath_cstr,"r");
+
+    // check file opened
+    if (fp == NULL) {{
+        perror("Failed: ");
+    }}
+
+    // save to array
+    for(int i=0;i<SIZE;i++) {{
+        for(int c=0;c<COARSE;c++) {{
+            for(int k1=0;k1<KERNEL_SIZE_X;k1++) {{
+                for(int k2=0;k2<KERNEL_SIZE_Y;k2++) {{
+                    float val;
+                    fscanf(fp,"%f\\n", &val);
+                    data[i][c][k1][k2] = T(val);
+                }}
+            }}
+        }}
+    }}
+
+    // close file
+    fclose(fp);
+}}
+
+template<int SIZE, int COARSE, int KERNEL_SIZE_X, int KERNEL_SIZE_Y, typename T>
+void to_stream(
+    T in[SIZE][COARSE][KERNEL_SIZE_X][KERNEL_SIZE_Y],
+    stream_t(T) out[COARSE][KERNEL_SIZE_X][KERNEL_SIZE_Y]
+) {{
+    for(int i=0;i<SIZE;i++) {{
+        for(int c=0;c<COARSE;c++) {{
+            for(int k1=0;k1<KERNEL_SIZE_X;k1++) {{
+	            for(int k2=0;k2<KERNEL_SIZE_Y;k2++) {{
+	                out[c][k1][k2].write(in[i][c][k1][k2]);
+                }}
+	        }}
+        }}
+    }}
+}}
+
+/* (WEIGHTS_INTR) */
+template<int COARSE_IN, int COARSE_OUT, int CHANNELS, int FILTERS, int KERNEL_SIZE_X, int KERNEL_SIZE_Y, typename T>
+void load_data(
+    std::string filepath,
+    T data[COARSE_IN][COARSE_OUT][DIVIDE(CHANNELS,COARSE_IN)*DIVIDE(FILTERS,COARSE_OUT)][KERNEL_SIZE_X][KERNEL_SIZE_Y]
+) {{
+    // read in file
+    const char *filepath_cstr = filepath.c_str();
+    FILE * fp = fopen(filepath_cstr,"r");
+
+    // check file opened
+    if (fp == NULL) {{
+        perror("Failed: ");
+    }}
+
+    const int channels_per_coarse_in = DIVIDE(CHANNELS,COARSE_IN);
+    const int filters_per_coarse_out = DIVIDE(FILTERS,COARSE_OUT);
+
+    // save to array
+    for(int i=0;i<channels_per_coarse_in;i++) {{
+        for(int cin=0;cin<COARSE_IN;cin++) {{
+            for(int j=0;j<filters_per_coarse_out;j++) {{
+                for(int cout=0;cout<COARSE_OUT;cout++) {{
+                    for(int k1=0;k1<KERNEL_SIZE_X;k1++) {{
+                        for(int k2=0;k2<KERNEL_SIZE_Y;k2++) {{
+                            float val;
+                            fscanf(fp,"%f\\n", &val);
+                            //int index = (i*COARSE_IN+cin)*filters_per_coarse_out+j*COARSE_OUT+cout;
+                            int index = i*filters_per_coarse_out+j;
+                            data[cin][cout][index][k1][k2] = T(val);
+                        }}
+                    }}
+                }}
+            }}
+        }}
+    }}
+
+    // close file
+    fclose(fp);
+}}
+
+/* (WEIGHTS_INTR) */
+template<int COARSE_IN, int COARSE_OUT, int CHANNELS, int FILTERS, typename T>
+void load_data(
+    std::string filepath,
+    T data[COARSE_IN][COARSE_OUT][DIVIDE(CHANNELS,COARSE_IN)*DIVIDE(FILTERS,COARSE_OUT)]
+) {{
+    // read in file
+    const char *filepath_cstr = filepath.c_str();
+    FILE * fp = fopen(filepath_cstr,"r");
+
+    // check file opened
+    if (fp == NULL) {{
+        perror("Failed: ");
+    }}
+
+    const int channels_per_coarse_in = DIVIDE(CHANNELS,COARSE_IN);
+    const int filters_per_coarse_out = DIVIDE(FILTERS,COARSE_OUT);
+
+    // save to array
+    for(int i=0;i<channels_per_coarse_in;i++) {{
+        for(int cin=0;cin<COARSE_IN;cin++) {{
+            for(int j=0;j<filters_per_coarse_out;j++) {{
+                for(int cout=0;cout<COARSE_OUT;cout++) {{
+                    float val;
+                    fscanf(fp,"%f\\n", &val);
+                    int index = i*filters_per_coarse_out+j;
+                    data[cin][cout][index] = T(val);
+                }}
+            }}
+        }}
+    }}
+
+    // close file
+    fclose(fp);
+}}
+
+
+template<int SIZE, int COARSE_IN, int COARSE_OUT, int KERNEL_SIZE_X, int KERNEL_SIZE_Y, typename T>
+void to_stream(
+    T in[COARSE_IN][COARSE_OUT][SIZE][KERNEL_SIZE_X][KERNEL_SIZE_Y],
+    stream_t(T) out[COARSE_IN][COARSE_OUT][KERNEL_SIZE_X][KERNEL_SIZE_Y]
+) {{
+    for(int cin=0;cin<COARSE_IN;cin++) {{
+        for(int cout=0;cout<COARSE_OUT;cout++) {{
+            for(int i=0;i<SIZE;i++) {{
+                for(int k1=0;k1<KERNEL_SIZE_X;k1++) {{
+                    for(int k2=0;k2<KERNEL_SIZE_Y;k2++) {{
+                        out[cin][cout][k1][k2].write(in[cin][cout][i][k1][k2]);
+                    }}
+                }}
+	    }}
+        }}
+    }}
+}}
+
+/* /1* (WEIGHTS_INTR) *1/ */
+/* template<int SIZE, int COARSE_IN, int COARSE_OUT, typename T> */
+/* void load_data( */
+/*     std::string filepath, */
+/*     T data[COARSE_IN][COARSE_OUT][SIZE] */
+/* ) {{ */
+/*     // read in file */
+/*     const char *filepath_cstr = filepath.c_str(); */
+/*     FILE * fp = fopen(filepath_cstr,"r"); */
+
+/*     // check file opened */
+/*     if (fp == NULL) {{ */
+/*         perror("Failed: "); */
+/*     }} */
+
+/*     // save to array */
+/*     for(int cin=0;cin<COARSE_IN;cin++) {{ */
+/*         for(int cout=0;cout<COARSE_OUT;cout++) {{ */
+/*             for(int i=0;i<SIZE;i++) {{ */
+/*                 float val; */
+/*                 fscanf(fp,"%f\\n", &val); */
+/*                 data[cin][cout][i] = T(val); */
+/*             }} */
+/*         }} */
+/*     }} */
+
+/*     // close file */
+/*     fclose(fp); */
+/* }} */
+
+template<int SIZE, int COARSE_IN, int COARSE_OUT, typename T>
+void to_stream(
+    T in[COARSE_IN][COARSE_OUT][SIZE],
+    stream_t(T) out[COARSE_IN][COARSE_OUT]
+) {{
+    for(int cin=0;cin<COARSE_IN;cin++) {{
+        for(int cout=0;cout<COARSE_OUT;cout++) {{
+            for(int i=0;i<SIZE;i++) {{
+                out[cin][cout].write(in[cin][cout][i]);
+	    }}
+        }}
+    }}
+}}
+
+/* (WEIGHTS_INTR) */
+template<int SIZE, int COARSE_IN, int COARSE_OUT, int FINE, typename T>
+void load_data(
+    std::string filepath,
+    T data[COARSE_IN][COARSE_OUT][SIZE][FINE]
+) {{
+    // read in file
+    const char *filepath_cstr = filepath.c_str();
+    FILE * fp = fopen(filepath_cstr,"r");
+
+    // check file opened
+    if (fp == NULL) {{
+        perror("Failed: ");
+    }}
+
+    // save to array
+    for(int cin=0;cin<COARSE_IN;cin++) {{
+        for(int cout=0;cout<COARSE_OUT;cout++) {{
+            for(int i=0;i<SIZE;i++) {{
+                for(int f=0;f<FINE;f++) {{
+                    float val;
+                    fscanf(fp,"%f\\n", &val);
+                    data[cin][cout][i][f] = T(val);
+                }}
+            }}
+        }}
+    }}
+
+    // close file
+    fclose(fp);
+}}
+
+template<int SIZE, int COARSE_IN, int COARSE_OUT, int FINE, typename T>
+void to_stream(
+    T in[COARSE_IN][COARSE_OUT][SIZE][FINE],
+    stream_t(T) out[COARSE_IN][COARSE_OUT][FINE]
+) {{
+    for(int cin=0;cin<COARSE_IN;cin++) {{
+        for(int cout=0;cout<COARSE_OUT;cout++) {{
+            for(int i=0;i<SIZE;i++) {{
+                for(int f=0;f<FINE;f++) {{
+                    out[cin][cout][f].write(in[cin][cout][i][f]);
+                }}
+	    }}
+        }}
+    }}
+}}
+
+
+/* (GLUE) */
+/* same as CONV/POOL */
+/*template<int SIZE, int COARSE_IN, int COARSE_OUT, typename T>
+void load_data(
+    std::string filepath,
+    T data[SIZE][COARSE_IN][COARSE_OUT]
+) {{
+
+    // read in file
+    const char *filepath_cstr = filepath.c_str();
+    FILE * fp = fopen(filepath_cstr,"r");
+
+    // check file opened
+    if (fp == NULL) {{
+        perror("Failed: ");
+    }}
+
+    // save to array
+    for(int i=0;i<SIZE;i++) {{
+        for(int a=0;a<COARSE_IN;a++) {{
+            for(int b=0;b<COARSE_OUT;b++) {{
+                float val;
+                fscanf(fp,"%f\\n", &val);
+                data[i][a][b] = T(val);
+	    }}
+        }}
+    }}
+
+    // close file
+    fclose(fp);
+}}
+
+template<int SIZE, int COARSE_IN, int COARSE_OUT, typename T>
+void to_stream(
+    T in[SIZE][COARSE_IN][COARSE_OUT],
+    stream_t(T) out[COARSE_IN][COARSE_OUT]
+) {{
+    for(int i=0;i<SIZE;i++) {{
+        for(int a=0;a<COARSE_IN;a++) {{
+            for(int b=0;b<COARSE_OUT;b++) {{
+	        out[a][b].write(in[i][a][b]);
+	    }}
+        }}
+    }}
+}}*/
+
+
+////////////////////////////////////////////////
+////////// CHECK STREAM TO VALID OUTPUT ////////
+////////////////////////////////////////////////
+
+
+template<typename T>
+int checkStreamEqual(
+		stream_t(T) &test,
+		stream_t(T) &valid,
+		bool print_out=false
+)
+{{
+    int err = 0;
+    int index = 0;
+    while(!valid.empty()) {{
+		if(test.empty()) {{
+			printf("ERROR: empty early\\n");
+			return 1;
+		}}
+		T tmp = test.read();
+		T tmp_valid = valid.read();
+
+		// if(print_out) printf("%x,%x\\n",tmp.range(),tmp_valid.range());
+		if(print_out) printf("%x,%x,%f,%f\\n",tmp.range(),tmp_valid.range(),
+                tmp.to_float(), tmp_valid.to_float());
+
+		if(
+				(tmp.to_float() > tmp_valid.to_float()+ERROR_TOLERANCE) ||
+				(tmp.to_float() < tmp_valid.to_float()-ERROR_TOLERANCE)
+		)
+		{{
+			//printf("ERROR: wrong value\\n");
+			printf("(%d) ERROR: wrong value %f != %f (err=%f)\\n", index, tmp.to_float(),
+                    tmp_valid.to_float(), tmp.to_float()-tmp_valid.to_float());
+			return 1;
+			err++;
+		}}
+        // increment index
+        index++;
+	}}
+
+	if(!test.empty())
+	{{
+		printf("ERROR: still data in stream\\n");
+		return 1;
+		err++;
+	}}
+	return err;
+}}
+
+template<
+    unsigned int SIZE,
+    unsigned int STREAMS,
+    unsigned int DMA_WIDTH = {DMA_WIDTH},
+    unsigned int DATA_WIDTH = 16
+>
+int check_array_equal(
+    volatile mem_int test[SIZE],
+    volatile mem_int valid[SIZE]
+)
+{{
+
+    const unsigned dma_width = DMA_WIDTH;
+    const unsigned data_width = DATA_WIDTH;
+    const unsigned bit_mask = (1 << data_width) - 1;
+
+    int err = 0;
+    for(int i=0;i<SIZE;i++) {{
+        for(int j=0;j<STREAMS;j++) {{
+        	mem_int temp = test[i];
+        	mem_int temp_valid = valid[i];
+            // std::cout<< "at i = " << i << ", j = " << j << ", 64 bit temp = " << temp << ", temp_valid = " << temp_valid << std::endl;
+            data_t tmp, tmp_valid;
+            tmp.range()         = (temp >> j*data_width) & bit_mask;
+            tmp_valid.range()   = (temp_valid >> j*data_width) & bit_mask;
+            // std::cout << "at i = " << i << ", j = " << j << ", tmp = " << tmp << ", tmp_valid = " << tmp_valid << std::endl;
+            // std::cout << "at i = " << i << ", j = " << j << ", bitwise tmp = " << tmp.range() << ", bitwise tmp_valid = " << tmp_valid.range() << std::endl;
+            if(
+                (tmp.to_float() > tmp_valid.to_float()+ERROR_TOLERANCE) ||
+                (tmp.to_float() < tmp_valid.to_float()-ERROR_TOLERANCE)
+            ){{
+                err += 1;
+                printf("ERROR (%d,%d): %f != %f\\n",i,j,tmp.to_float(),tmp_valid.to_float());
+            }}
+        }}
+    }}
+    return err;
+}}
+
+////////////////////////////////////////////////////
+////////// LOAD DATA FOR PARTITION TESTS ///////////
+////////////////////////////////////////////////////
+
+template<
+    int INPUTS,
+    int SIZE,
+    int WR_FACTOR=1
+>
+// void load_net_weights(
+//     std::string filepath,
+//     volatile mem_int data[INPUTS][DIVIDE(SIZE,INPUTS)],
+//     int wr_index = 0
+// )
+// {{
+//     // read in file
+//     const char *filepath_cstr = filepath.c_str();
+//     FILE * fp = fopen(filepath_cstr,"r");
+
+//     // check file opened
+//     if (fp == NULL) {{
+//         perror("Failed to load weights at");
+//     }}
+
+//     for(int w=0;w<WR_FACTOR;w++) {{
+//         for(int i=0;i<INPUTS;i++) {{
+//             for(int j=0;j<DIVIDE(SIZE,INPUTS);j++) {{
+//                 // read in the value from the file
+//                 mem_int val;
+//                 fscanf(fp,"%l\\n", &val);
+//                 // add to the input if correct weights reloading index
+//                 if ( w == wr_index ) {{
+//                     data[i][j] = val;
+//                 }}
+//            }}
+//         }}
+//     }}
+//     // close file
+//     fclose(fp);
+// }}
+void load_net_weights(
+    std::string filepath,
+    volatile mem_int data[INPUTS][DIVIDE(SIZE, INPUTS)],
+    int wr_index = 0
+) {{
+    // read in file
+    const char *filepath_cstr = filepath.c_str();
+    std::ifstream fp(filepath_cstr);
+
+    // check file opened
+    if (!fp.is_open()) {{
+        perror("Failed to load weights at");
+        return;
+    }}
+
+    for (int w = 0; w < WR_FACTOR; w++) {{
+        for (int i = 0; i < INPUTS; i++) {{
+            for (int j = 0; j < DIVIDE(SIZE, INPUTS); j++) {{
+                // read in the value from the file
+                std::string line;
+                if (std::getline(fp, line)) {{
+                    mem_int val(line.c_str(), 10);
+                    // add to the input if correct weights reloading index
+                    if (w == wr_index) {{
+                        data[i][j] = val;
+                        mem_int non_volatile_val = data[i][j];
+                        // std::cout << "data[" << i << "][" << j << "] = " << non_volatile_val.to_string(10) << std::endl;
+                    }}
+                }} 
+            }}
+        }}
+    }}
+    // close file
+    fp.close();
+}}
+
+template<
+    unsigned int INPUTS,
+    unsigned int BATCH_SIZE,
+    unsigned int ROWS,
+    unsigned int COLS,
+    unsigned int CHANNELS,
+    unsigned int STREAMS,
+    unsigned int WR_FACTOR=1,
+    unsigned int DMA_WIDTH = {DMA_WIDTH},
+    unsigned int DATA_WIDTH = 16,
+    typename mem_t = mem_int
+>
+// void load_net_data(
+//     std::string filepath,
+//     mem_t data[INPUTS][BATCH_SIZE*ROWS*COLS*DIVIDE(CHANNELS,STREAMS)*WR_FACTOR],
+//     int wr_index = 0
+// )
+// {{
+
+//     // read in file
+//     const char *filepath_cstr = filepath.c_str();
+//     FILE * fp = fopen(filepath_cstr,"r");
+
+//     // check file opened
+//     if (fp == NULL) {{
+//         perror("Failed to load data at");
+//     }}
+
+//     // get variables
+//     int channels_per_stream = DIVIDE(CHANNELS,STREAMS);
+//     int dma_channels = DIVIDE(DMA_WIDTH,DATA_WIDTH);
+
+//     // save to array
+//     for(int i=0;i<BATCH_SIZE*ROWS*COLS;i++) {{
+//         for(int j=0;j<WR_FACTOR;j++) {{
+//             for(int k=0;k<channels_per_stream;k++) {{
+//                 // read in the value from the file
+//                 mem_int val;
+//                 fscanf(fp,"%lu\\n", &val);
+//                 // specific weights reloading index
+//                 if (j == wr_index) {{
+//                     int out_index = i*channels_per_stream*WR_FACTOR + j*channels_per_stream + k;
+//                     data[0][out_index] = val;
+//                     // std::cout << "data[0][" << out_index << "] = " << data[0][out_index] << std::endl;
+//                 }}
+//             }}
+//         }}
+//     }}
+//     // close file
+//     fclose(fp);
+// }}
+void load_net_data(
+    std::string filepath,
+    mem_t data[INPUTS][BATCH_SIZE*ROWS*COLS*DIVIDE(CHANNELS,STREAMS)*WR_FACTOR],
+    int wr_index = 0
+)
+{{
+
+    // read in file
+    std::ifstream fp(filepath);
+
+    // check file opened
+    if (!fp.is_open()) {{
+        perror("Failed to load data at");
+        return;
+    }}
+
+    // get variables
+    int channels_per_stream = DIVIDE(CHANNELS,STREAMS);
+    int dma_channels = DIVIDE(DMA_WIDTH,DATA_WIDTH);
+
+    // save to array
+    for(int i=0;i<BATCH_SIZE*ROWS*COLS;i++) {{
+        for(int j=0;j<WR_FACTOR;j++) {{
+            for(int k=0;k<channels_per_stream;k++) {{
+                // read in the value from the file
+                std::string line;
+                if (std::getline(fp, line)) {{
+                    mem_int val(line.c_str(), 10);
+                    // specific weights reloading index
+                    if (j == wr_index) {{
+                        int out_index = i*channels_per_stream*WR_FACTOR + j*channels_per_stream + k;
+                        data[0][out_index] = val;
+                        // std::cout << "data[0][" << out_index << "] = " << data[0][out_index].to_string(10) << std::endl;
+                    }}
+                }}
+            }}
+        }}
+    }}
+    // close file
+    fp.close();
+}}
+
+template<
+    unsigned int BATCH_SIZE,
+    unsigned int ROWS,
+    unsigned int COLS,
+    unsigned int CHANNELS,
+    unsigned int STREAMS,
+    unsigned int WR_FACTOR,
+    typename T
+>
+void load_wr_data(
+    std::string filepath,
+    T data[BATCH_SIZE*ROWS*COLS*DIVIDE(CHANNELS,STREAMS)][STREAMS],
+    int wr_index = 0
+)
+{{
+
+    // read in file
+    const char *filepath_cstr = filepath.c_str();
+    FILE * fp = fopen(filepath_cstr,"r");
+
+    // check file opened
+    if (fp == NULL) {{
+        perror("Failed to load data at");
+    }}
+
+    // get variables
+    int channels_per_stream = DIVIDE(CHANNELS,STREAMS);
+
+    // save to array
+    for(int i=0;i<BATCH_SIZE*ROWS*COLS;i++) {{
+        for(int j=0;j<WR_FACTOR;j++) {{
+            for(int k=0;k<channels_per_stream;k++) {{
+                for(int l=0;l<STREAMS;l++) {{
+                    // read in the value from the file
+                    float val;
+                    fscanf(fp,"%f\\n", &val);
+                    // specific weights reloading index
+                    if (j == wr_index) {{
+                        data[i*channels_per_stream+k][l] = T( val );
+                    }}
+                }}
+            }}
+        }}
+    }}
+
+    // close file
+    fclose(fp);
+}}
+
+#endif
+"""
\ No newline at end of file
diff --git a/fpgaconvnet/hls/generate/partition.py b/fpgaconvnet/hls/generate/partition.py
index abdd59d..5325bf5 100644
--- a/fpgaconvnet/hls/generate/partition.py
+++ b/fpgaconvnet/hls/generate/partition.py
@@ -9,29 +9,36 @@
 
 from fpgaconvnet.hls.generate.partition_template import *
 
-from fpgaconvnet.hls.generate.layers.convolution    import gen_convolution_layer
-from fpgaconvnet.hls.generate.layers.pooling        import gen_pooling_layer
-from fpgaconvnet.hls.generate.layers.relu           import gen_relu_layer
-from fpgaconvnet.hls.generate.layers.inner_product  import gen_inner_product_layer
-from fpgaconvnet.hls.generate.layers.squeeze        import gen_squeeze_layer
+from fpgaconvnet.hls.generate.layers.convolution        import gen_convolution_layer
+from fpgaconvnet.hls.generate.layers.pooling            import gen_pooling_layer
+from fpgaconvnet.hls.generate.layers.avg_pooling        import gen_avg_pooling_layer
+from fpgaconvnet.hls.generate.layers.global_pooling     import gen_global_pooling_layer
+from fpgaconvnet.hls.generate.layers.relu               import gen_relu_layer
+from fpgaconvnet.hls.generate.layers.inner_product      import gen_inner_product_layer
+from fpgaconvnet.hls.generate.layers.squeeze            import gen_squeeze_layer
+from fpgaconvnet.hls.generate.layers.split              import gen_split_layer
+from fpgaconvnet.hls.generate.layers.elementwise_add    import gen_elementwise_add_layer
+from fpgaconvnet.hls.generate.layers.elementwise_mul    import gen_elementwise_mul_layer
 from fpgaconvnet.hls.generate.util import *
 
 import fpgaconvnet.hls.tools.onnx_data as onnx_data
 from fpgaconvnet.hls.tools.array_init import array_init
 
 import fpgaconvnet.proto.fpgaconvnet_pb2 as fpgaconvnet_pb2
-import fpgaconvnet.tools.onnx_helper as onnx_helper
+from fpgaconvnet.parser import Parser
+import fpgaconvnet.parser.onnx.helper as onnx_helper
 import fpgaconvnet.tools.layer_enum as layer_enum
 
 class GeneratePartition:
 
-    def __init__(self, name, partition, model, sess, output_path):
+    def __init__(self, name, partition, model, sess, output_path, port_width=64):
 
         self.name = name
         self.partition = partition
         self.output_path = output_path
         self.model = model
         self.sess = sess
+        self.port_width = port_width
 
         # make output path directory
         self.mkdir(self.output_path)
@@ -81,6 +88,8 @@ def generate_layers(self):
         for layer in self.partition.layers:
             # get parameters of layer
             parameters = MessageToDict(layer.parameters, preserving_proto_field_name=True)
+            # FIXME adding buffer depth to params in a hacky way
+            parameters['buffer_depth'] = layer.streams_in[0].buffer_depth
             # init function arguments for this layer
             fn_args = []
             # init hardware generation args
@@ -91,13 +100,19 @@ def generate_layers(self):
                 os.path.join(self.output_path, "include", f"{layer.name}.hpp")
             ]
             # create hardware for each layer
+            print(f"--------- Generating layer {layer.name} ---------")
             if layer.type == fpgaconvnet_pb2.layer.layer_type.CONVOLUTION:
                 fn_args.append(f"{layer.name}_weights")
                 if layer.parameters.has_bias == 1:
                     fn_args.append(f"{layer.name}_biases")
                 gen_convolution_layer(*args)
             if layer.type == fpgaconvnet_pb2.layer.layer_type.POOLING:
-                gen_pooling_layer(*args)
+                if layer.name.startswith("Max"):
+                    gen_pooling_layer(*args)
+                elif layer.name.startswith("Average"):
+                    gen_avg_pooling_layer(*args)
+            if layer.type == fpgaconvnet_pb2.layer.layer_type.AVERAGE_POOLING:
+                gen_global_pooling_layer(*args)
             if layer.type == fpgaconvnet_pb2.layer.layer_type.CONCAT:
                 gen_concat_layer(*args)
             if layer.type == fpgaconvnet_pb2.layer.layer_type.RELU:
@@ -111,6 +126,12 @@ def generate_layers(self):
                 gen_inner_product_layer(*args)
             if layer.type == fpgaconvnet_pb2.layer.layer_type.SQUEEZE:
                 gen_squeeze_layer(*args)
+            if layer.type == fpgaconvnet_pb2.layer.layer_type.ELTWISE:
+                if layer.name.startswith("Add"):
+                    gen_elementwise_add_layer(*args)
+                elif layer.name.startswith("Mul"):
+                    gen_elementwise_mul_layer(*args)
+                else: raise ValueError("Operation type can only be Add or Mul")
             # create layer call
             for stream_in in layer.streams_in:
                 fn_args.append(stream_in.name)
@@ -148,6 +169,8 @@ def generate_parameters(self):
                 if layer_enum.from_proto_layer_type(layer.type) == layer_enum.LAYER_TYPE.Convolution:
                     transformed_weights = onnx_data.get_weights_convolution(weights_raw, layer, wr_factor=wr_factor)
                 elif layer_enum.from_proto_layer_type(layer.type) == layer_enum.LAYER_TYPE.InnerProduct:
+                    #TODO: Support transA and transB parameters
+                    weights_raw = weights_raw.T
                     transformed_weights = onnx_data.get_weights_inner_product(weights_raw, layer, wr_factor=wr_factor)
                 ## get the output path for the weights
                 output_path = os.path.join(self.output_path, "data", f"{layer.name}_weights")
@@ -156,13 +179,16 @@ def generate_parameters(self):
                     with open(f'{output_path}_{weights_reloading_index}.csv', 'w') as f:
                         f.write(array_init(transformed_weights[weights_reloading_index]))
                 ## flatten weights into a stream
+                weight_int_width = layer.parameters.weight_t.width - \
+                    layer.parameters.weight_t.binary_point
                 weights_stream =  onnx_data._convert_fixed_port_stream(
                         transformed_weights.reshape(-1),
-                        total_width=layer.parameters.weight_width,
-                        int_width=layer.parameters.weight_width//2)
+                        total_width=layer.parameters.weight_t.width,
+                        int_width=weight_int_width)
                 ## save to .dat format
+                print(f"Writing weights stream to .dat file")
                 onnx_data._fixed_point_stream_to_dat(weights_stream, output_path=output_path,
-                        streams=1, port_width=64, ports=1)
+                        streams=1, port_width=self.port_width, ports=1)
                 # add weight generators (if bias present)
                 if layer.parameters.has_bias == 1:
                     ## add a biases generator
@@ -178,13 +204,19 @@ def generate_parameters(self):
                     with open(f'{output_path}.csv', 'w') as f:
                         f.write(array_init(transformed_biases[0]))
                     ## flatten biases into a stream
+                    # FIXME check if bias width should be accum width or smth else
+                    acc_int_width = layer.parameters.acc_t.width - \
+                        layer.parameters.acc_t.binary_point
                     biases_stream = onnx_data._convert_fixed_port_stream(
                             transformed_biases.reshape(-1),
-                            total_width=layer.parameters.biases_width,
-                            int_width=layer.parameters.biases_width//2)
+                            total_width=layer.parameters.acc_t.width,
+                            int_width=acc_int_width)
                     ## save to .dat format
+                    # onnx_data._fixed_point_stream_to_dat(biases_stream, output_path=output_path,
+                    #         streams=1, port_width=64, ports=1)
+                    print("Writing biases stream to .dat file")
                     onnx_data._fixed_point_stream_to_dat(biases_stream, output_path=output_path,
-                            streams=1, port_width=64, ports=1)
+                            streams=1, port_width=self.port_width, ports=1)
 
         # get weights definitions and intialisation
         self.weights_def = "\n\n".join([w.generate_def() for w in weights])
@@ -219,6 +251,31 @@ def generate_include(self):
         for layer in self.partition.layers:
             include +=f"#include \"{layer.name}.hpp\"\n"
 
+        # get input, weight and output data width
+        if (self.partition.layers[0].parameters.input_t.width != 0):
+            input_data_width = self.partition.layers[0].parameters.input_t.width
+        elif (self.partition.layers[0].parameters.data_t.width != 0):
+            input_data_width = self.partition.layers[0].parameters.data_t.width
+        else:
+            raise ValueError("Input data width not found")
+        
+        if (self.partition.weights_reloading_layer != "None"):
+            for layer in self.partition.layers:
+                if (layer.name == self.partition.weights_reloading_layer):
+                    if (layer.parameters.weight_t.width != 0):
+                        weight_data_width = layer.parameters.weight_t.width
+                    else:
+                        raise ValueError("Weight data width not found")
+        else: 
+            weight_data_width = 0
+        
+        if (self.partition.layers[-1].parameters.output_t.width != 0):
+            output_data_width = self.partition.layers[-1].parameters.output_t.width
+        elif (self.partition.layers[-1].parameters.data_t.width != 0):
+            output_data_width = self.partition.layers[-1].parameters.data_t.width
+        else:
+            raise ValueError("Output data width not found")
+
         # HEADER
         network_header = network_header_template.format(
             name        =self.name,
@@ -239,6 +296,10 @@ def generate_include(self):
             WR_LAYER    =self.partition.weights_reloading_layer.upper(),
             wr_factor   =self.partition.weights_reloading_factor,
             wr_flag     =int(self.partition.weights_reloading_layer != "None"),
+            DMA_WIDTH   =self.port_width,
+            input_data_width=input_data_width,
+            weight_data_width=weight_data_width,
+            output_data_width=output_data_width,
             include     =include
         )
 
@@ -299,24 +360,34 @@ def create_testbench_data(self, input_data):
         # get the input name and shape
         input_name  = self.sess.get_inputs()[0].name
         input_shape = self.sess.get_inputs()[0].shape
-        # TODO: check data is right shape
+        # check data is right shape
+        if input_shape != list(input_data.shape):
+            raise ValueError(f"expected input shape: {input_shape}, data shape: {list(input_data.shape)}")
         # save input layer
         # TODO add bitwidth
-        input_node = self.partition.input_node
+        if len(self.partition.input_nodes) > 1:
+            # check if multiple input nodes
+            raise NotImplementedError("Multiple input nodes not currently supported.")
+        input_node = self.partition.input_nodes[0]
         input_stream = np.array( self.sess.run([input_node], { input_name : input_data } )[0] )
         input_stream = np.moveaxis(input_stream, 1, -1)
         input_stream = onnx_data._convert_fixed_port_stream(input_stream.reshape(-1))
+        print("Writing input stream to .dat file")
         onnx_data._fixed_point_stream_to_dat(input_stream,
                 os.path.join(self.output_path, f"data/{self.partition.layers[0].name}_in"),
-                streams=int(self.partition.layers[0].parameters.coarse_in))
+                streams=int(self.partition.layers[0].parameters.coarse_in), port_width=self.port_width)
         # save output layer
-        output_node = self.partition.output_node
+        if len(self.partition.output_nodes) > 1:
+            # check if multiple output nodes
+            raise NotImplementedError("Multiple output nodes not currently supported.")
+        output_node = self.partition.output_nodes[0]
         output_stream = np.array( self.sess.run([output_node], { input_name : input_data } )[0] )
         output_stream = np.moveaxis(output_stream, 1, -1)
         output_stream = onnx_data._convert_fixed_port_stream(output_stream.reshape(-1))
+        print("Writing valid output stream to .dat file")
         onnx_data._fixed_point_stream_to_dat(output_stream,
                 os.path.join(self.output_path, f"data/{self.partition.layers[-1].name}_out"),
-                streams=int(self.partition.layers[-1].parameters.coarse_out))
+                streams=int(self.partition.layers[-1].parameters.coarse_out), port_width=self.port_width)
 
     """
     Vivado HLS functions
diff --git a/fpgaconvnet/hls/generate/partition_template.py b/fpgaconvnet/hls/generate/partition_template.py
index 9709f33..85393f3 100644
--- a/fpgaconvnet/hls/generate/partition_template.py
+++ b/fpgaconvnet/hls/generate/partition_template.py
@@ -64,6 +64,11 @@
 );
 #endif
 
+#define {NAME}_DMA_WIDTH            {DMA_WIDTH}
+#define {NAME}_IN_DATA_WIDTH        {input_data_width}
+#define {NAME}_WEIGHTS_DATA_WIDTH   {weight_data_width}
+#define {NAME}_OUT_DATA_WIDTH       {output_data_width}
+
 void process(
     int weights_reloading_index,
     volatile mem_int in_hw[{NAME}_PORTS_IN][{NAME}_SIZE_IN],
@@ -114,7 +119,9 @@
         {NAME}_WR_CHANNELS_IN,
         {NAME}_WR_PORTS_IN,
         {NAME}_WR_STREAMS_IN,
-        {wr_layer}_weight_t
+        {wr_layer}_weight_t,
+        {NAME}_DMA_WIDTH,
+        {NAME}_WEIGHTS_DATA_WIDTH
     >(wr_hw,wr);
 
     weights_reloading<
@@ -150,7 +157,9 @@
         {NAME}_CHANNELS_IN,
         {NAME}_PORTS_IN,
         {NAME}_STREAMS_IN,
-        {name}_input_t
+        {name}_input_t,
+        {NAME}_DMA_WIDTH,
+        {NAME}_IN_DATA_WIDTH
     >(in_hw,in);
 
     int mode = 0;
@@ -165,7 +174,9 @@
         {NAME}_PORTS_OUT,
         {NAME}_STREAMS_OUT,
         {NAME}_WEIGHTS_RELOADING_FACTOR,
-        {name}_output_t
+        {name}_output_t,
+        {NAME}_DMA_WIDTH,
+        {NAME}_OUT_DATA_WIDTH
     >(weights_reloading_index,out,out_hw);
 
 }}
@@ -244,16 +255,18 @@
 
 #if {NAME}_WEIGHTS_RELOADING_FLAG
         static mem_int weights[{NAME}_PORTS_WR][{NAME}_SIZE_WR] = {{0}};
-#endif
 
         // load weights
+        printf("LOADING WEIGHTS \\n");
         load_net_weights<
             {NAME}_PORTS_WR,
             {NAME}_SIZE_WR,
             {NAME}_WEIGHTS_RELOADING_FACTOR
         >("{weights_reloading_path}", weights, wr_index);
+#endif
 
         // load valid output
+        printf("LOADING VALID OUTPUT DATA \\n");
         load_net_data<
             {NAME}_PORTS_OUT,
             {NAME}_BATCH_SIZE,
@@ -267,12 +280,18 @@
         printf("RUNNING NETWORK \\n");
 
         // perform weights reloading
+#if {NAME}_WEIGHTS_RELOADING_FLAG
         if( wr_index > 0 ) {{
             fpgaconvnet_ip(1,wr_index,weights,test_in,test_out);
         }}
+#endif   
 
         // run the network
+#if {NAME}_WEIGHTS_RELOADING_FLAG
         fpgaconvnet_ip(0,wr_index,weights,test_in,test_out);
+#else
+        fpgaconvnet_ip(0,wr_index,test_in,test_out);
+#endif
 
         // check array is correct
         for(int i=0; i<{NAME}_PORTS_OUT;i++) {{
@@ -286,5 +305,3 @@
     return err;
 }}
 """
-
-
diff --git a/fpgaconvnet/hls/generate/util.py b/fpgaconvnet/hls/generate/util.py
index 6883b21..514d33c 100644
--- a/fpgaconvnet/hls/generate/util.py
+++ b/fpgaconvnet/hls/generate/util.py
@@ -60,7 +60,7 @@ def generate_def(self):
     def generate_init(self):
         return f"""
 #pragma HLS ARRAY_PARTITION variable={self.name}_biases complete dim=1
-#pragma HLS RESOURCE variable={self.name}_biases core=ROM
+#pragma HLS RESOURCE variable={self.name}_biases core=ROM_nP
 #pragma HLS STABLE variable={self.name}_biases
         """
     def __repr__(self):
diff --git a/fpgaconvnet/hls/hardware/avg_pool.hpp b/fpgaconvnet/hls/hardware/avg_pool.hpp
new file mode 100644
index 0000000..20581d0
--- /dev/null
+++ b/fpgaconvnet/hls/hardware/avg_pool.hpp
@@ -0,0 +1,56 @@
+#ifndef AVG_POOL_HPP_
+#define AVG_POOL_HPP_
+
+#include "common.hpp"
+
+/**
+  * AVG_POOL
+  */
+
+ template <
+     unsigned int BATCH_SIZE,
+     unsigned int ROWS,
+     unsigned int COLS,
+     unsigned int CHANNELS,
+     unsigned int KERNEL_SIZE_X,
+     unsigned int KERNEL_SIZE_Y,
+     typename avg_pool_t
+>
+void avg_pool(
+    stream_t(avg_pool_t) in[KERNEL_SIZE_X][KERNEL_SIZE_Y],
+    stream_t(avg_pool_t) &out
+)
+{
+
+#pragma HLS INLINE OFF
+
+    const unsigned int batch_size   = BATCH_SIZE;
+    const unsigned int rows         = ROWS;
+    const unsigned int cols         = COLS;
+    const unsigned int channels     = CHANNELS;
+    const unsigned int kernel_size_x = KERNEL_SIZE_X;
+    const unsigned int kernel_size_y = KERNEL_SIZE_Y;
+
+#pragma HLS STREAM variable=in
+#pragma HLS STREAM variable=out
+#pragma HLS ARRAY_PARTITION variable=in complete dim=0
+
+    avg_pool_t accum_value;
+    unsigned long pool_area = kernel_size_x * kernel_size_y;
+    #pragma HLS DEPENDENCE variable=accum_value RAW intra true
+
+    pixel_loop: for (unsigned long pixel_index = 0; pixel_index < batch_size*rows*cols*channels; pixel_index++) {
+        accum_value = 0;
+        #pragma HLS PIPELINE II=1 rewind
+
+        pool_loop_1: for (unsigned char k1 = 0; k1 < kernel_size_x; k1++) {
+            pool_loop_2: for (unsigned char k2 = 0; k2 < kernel_size_y; k2++) {
+                accum_value += in[k1][k2].read();
+            }
+        }
+    
+        out.write(accum_value/pool_area);
+    }
+}
+
+#endif
\ No newline at end of file
diff --git a/fpgaconvnet/hls/hardware/common.hpp b/fpgaconvnet/hls/hardware/common.hpp
index 51c6e94..f86c72f 100644
--- a/fpgaconvnet/hls/hardware/common.hpp
+++ b/fpgaconvnet/hls/hardware/common.hpp
@@ -18,6 +18,7 @@
 #ifndef COMMON_HPP_
 #define COMMON_HPP_
 
+#include <ap_int.h>
 #include <ap_fixed.h>
 #include "hls_stream.h"
 #include "hls_math.h"
@@ -52,10 +53,11 @@
 /* #define stream_t(x) hlslib::Stream<x> */
 
 // default data types
-typedef unsigned long mem_int;
+typedef ap_uint<64> mem_int;
 typedef ap_axis<64,1,1,1> axi_stream_t;
 typedef hls::stream<axi_stream_t> axi_stream_hw_t;
 
+// <total_width, int_width, rounding type>
 typedef ap_fixed<16, 8,AP_RND> data_t;
 typedef ap_fixed<30,16,AP_RND> acc_t;
 typedef ap_fixed<16, 8,AP_RND> weight_t;
diff --git a/fpgaconvnet/hls/hardware/common_tb.hpp b/fpgaconvnet/hls/hardware/common_tb.hpp
index da2c88b..7d9743f 100644
--- a/fpgaconvnet/hls/hardware/common_tb.hpp
+++ b/fpgaconvnet/hls/hardware/common_tb.hpp
@@ -508,9 +508,14 @@ int check_array_equal(
     int err = 0;
     for(int i=0;i<SIZE;i++) {
         for(int j=0;j<STREAMS;j++) {
+        	mem_int temp = test[i];
+        	mem_int temp_valid = valid[i];
+            // std::cout<< "at i = " << i << ", j = " << j << ", 64 bit temp = " << temp << ", temp_valid = " << temp_valid << std::endl;
             data_t tmp, tmp_valid;
-            tmp.range()         = (test[i] >> j*data_width) & bit_mask;
-            tmp_valid.range()   = (valid[i]>> j*data_width) & bit_mask;
+            tmp.range()         = (temp >> j*data_width) & bit_mask;
+            tmp_valid.range()   = (temp_valid >> j*data_width) & bit_mask;
+            // std::cout << "at i = " << i << ", j = " << j << ", tmp = " << tmp << ", tmp_valid = " << tmp_valid << std::endl;
+            // std::cout << "at i = " << i << ", j = " << j << ", bitwise tmp = " << tmp.range() << ", bitwise tmp_valid = " << tmp_valid.range() << std::endl;
             if(
                 (tmp.to_float() > tmp_valid.to_float()+ERROR_TOLERANCE) ||
                 (tmp.to_float() < tmp_valid.to_float()-ERROR_TOLERANCE)
@@ -532,36 +537,71 @@ template<
     int SIZE,
     int WR_FACTOR=1
 >
+// void load_net_weights(
+//     std::string filepath,
+//     volatile mem_int data[INPUTS][DIVIDE(SIZE,INPUTS)],
+//     int wr_index = 0
+// )
+// {
+//     // read in file
+//     const char *filepath_cstr = filepath.c_str();
+//     FILE * fp = fopen(filepath_cstr,"r");
+
+//     // check file opened
+//     if (fp == NULL) {
+//         perror("Failed to load weights at");
+//     }
+
+//     for(int w=0;w<WR_FACTOR;w++) {
+//         for(int i=0;i<INPUTS;i++) {
+//             for(int j=0;j<DIVIDE(SIZE,INPUTS);j++) {
+//                 // read in the value from the file
+//                 mem_int val;
+//                 fscanf(fp,"%l\n", &val);
+//                 // add to the input if correct weights reloading index
+//                 if ( w == wr_index ) {
+//                     data[i][j] = val;
+//                 }
+//            }
+//         }
+//     }
+//     // close file
+//     fclose(fp);
+// }
 void load_net_weights(
     std::string filepath,
-    volatile mem_int data[INPUTS][DIVIDE(SIZE,INPUTS)],
+    volatile mem_int data[INPUTS][DIVIDE(SIZE, INPUTS)],
     int wr_index = 0
-)
-{
+) {
     // read in file
     const char *filepath_cstr = filepath.c_str();
-    FILE * fp = fopen(filepath_cstr,"r");
+    std::ifstream fp(filepath_cstr);
 
     // check file opened
-    if (fp == NULL) {
+    if (!fp.is_open()) {
         perror("Failed to load weights at");
+        return;
     }
 
-    for(int w=0;w<WR_FACTOR;w++) {
-        for(int i=0;i<INPUTS;i++) {
-            for(int j=0;j<DIVIDE(SIZE,INPUTS);j++) {
+    for (int w = 0; w < WR_FACTOR; w++) {
+        for (int i = 0; i < INPUTS; i++) {
+            for (int j = 0; j < DIVIDE(SIZE, INPUTS); j++) {
                 // read in the value from the file
-                mem_int val;
-                fscanf(fp,"%l\n", &val);
-                // add to the input if correct weights reloading index
-                if ( w == wr_index ) {
-                    data[i][j] = val;
-                }
-           }
+                std::string line;
+                if (std::getline(fp, line)) {
+                    mem_int val(line.c_str(), 10);
+                    // add to the input if correct weights reloading index
+                    if (w == wr_index) {
+                        data[i][j] = val;
+                        mem_int non_volatile_val = data[i][j];
+                        // std::cout << "data[" << i << "][" << j << "] = " << non_volatile_val.to_string(10) << std::endl;
+                    }
+                } 
+            }
         }
     }
     // close file
-    fclose(fp);
+    fp.close();
 }
 
 template<
@@ -576,6 +616,45 @@ template<
     unsigned int DATA_WIDTH = 16,
     typename mem_t = mem_int
 >
+// void load_net_data(
+//     std::string filepath,
+//     mem_t data[INPUTS][BATCH_SIZE*ROWS*COLS*DIVIDE(CHANNELS,STREAMS)*WR_FACTOR],
+//     int wr_index = 0
+// )
+// {
+
+//     // read in file
+//     const char *filepath_cstr = filepath.c_str();
+//     FILE * fp = fopen(filepath_cstr,"r");
+
+//     // check file opened
+//     if (fp == NULL) {
+//         perror("Failed to load data at");
+//     }
+
+//     // get variables
+//     int channels_per_stream = DIVIDE(CHANNELS,STREAMS);
+//     int dma_channels = DIVIDE(DMA_WIDTH,DATA_WIDTH);
+
+//     // save to array
+//     for(int i=0;i<BATCH_SIZE*ROWS*COLS;i++) {
+//         for(int j=0;j<WR_FACTOR;j++) {
+//             for(int k=0;k<channels_per_stream;k++) {
+//                 // read in the value from the file
+//                 mem_int val;
+//                 fscanf(fp,"%lu\n", &val);
+//                 // specific weights reloading index
+//                 if (j == wr_index) {
+//                     int out_index = i*channels_per_stream*WR_FACTOR + j*channels_per_stream + k;
+//                     data[0][out_index] = val;
+//                     // std::cout << "data[0][" << out_index << "] = " << data[0][out_index] << std::endl;
+//                 }
+//             }
+//         }
+//     }
+//     // close file
+//     fclose(fp);
+// }
 void load_net_data(
     std::string filepath,
     mem_t data[INPUTS][BATCH_SIZE*ROWS*COLS*DIVIDE(CHANNELS,STREAMS)*WR_FACTOR],
@@ -584,12 +663,12 @@ void load_net_data(
 {
 
     // read in file
-    const char *filepath_cstr = filepath.c_str();
-    FILE * fp = fopen(filepath_cstr,"r");
+    std::ifstream fp(filepath);
 
     // check file opened
-    if (fp == NULL) {
+    if (!fp.is_open()) {
         perror("Failed to load data at");
+        return;
     }
 
     // get variables
@@ -601,18 +680,21 @@ void load_net_data(
         for(int j=0;j<WR_FACTOR;j++) {
             for(int k=0;k<channels_per_stream;k++) {
                 // read in the value from the file
-                mem_int val;
-                fscanf(fp,"%ld\n", &val);
-                // specific weights reloading index
-                if (j == wr_index) {
-                    int out_index = i*channels_per_stream*WR_FACTOR + j*channels_per_stream + k;
-                    data[0][out_index] = val;
+                std::string line;
+                if (std::getline(fp, line)) {
+                    mem_int val(line.c_str(), 10);
+                    // specific weights reloading index
+                    if (j == wr_index) {
+                        int out_index = i*channels_per_stream*WR_FACTOR + j*channels_per_stream + k;
+                        data[0][out_index] = val;
+                        // std::cout << "data[0][" << out_index << "] = " << data[0][out_index].to_string(10) << std::endl;
+                    }
                 }
             }
         }
     }
     // close file
-    fclose(fp);
+    fp.close();
 }
 
 template<
diff --git a/fpgaconvnet/hls/hardware/elementwise_add.hpp b/fpgaconvnet/hls/hardware/elementwise_add.hpp
new file mode 100644
index 0000000..44db58a
--- /dev/null
+++ b/fpgaconvnet/hls/hardware/elementwise_add.hpp
@@ -0,0 +1,42 @@
+#ifndef ELEMENTWISE_ADD_HPP_
+#define ELEMENTWISE_ADD_HPP_
+
+#include "common.hpp"
+
+/**
+ * ELEMENTWISE ADD FUNCTION
+ */
+template<
+    unsigned int BATCH_SIZE,
+    unsigned int ROWS,
+    unsigned int COLS,
+    unsigned int CHANNELS,
+    typename elementwise_add_t
+>
+void elementwise_add(
+    stream_t(elementwise_add_t)  &in1,
+    stream_t(elementwise_add_t)  &in2,
+    stream_t(elementwise_add_t)  &out
+)
+{
+
+#pragma HLS INLINE OFF
+
+    const unsigned int batch_size   = BATCH_SIZE;
+    const unsigned int rows         = ROWS;
+    const unsigned int cols         = COLS;
+    const unsigned int channels     = CHANNELS;
+
+#pragma HLS STREAM variable=in1
+#pragma HLS STREAM variable=in2
+#pragma HLS STREAM variable=out
+
+    for(unsigned long pixel_index=0 ; pixel_index < batch_size*rows*cols*channels ; pixel_index++) {
+        #pragma HLS PIPELINE II=1 rewind
+        elementwise_add_t tmp1 = in1.read();
+        elementwise_add_t tmp2 = in2.read();
+        out.write(tmp1 + tmp2);
+    }
+}
+
+#endif
diff --git a/fpgaconvnet/hls/hardware/elementwise_mul.hpp b/fpgaconvnet/hls/hardware/elementwise_mul.hpp
new file mode 100644
index 0000000..ee732a7
--- /dev/null
+++ b/fpgaconvnet/hls/hardware/elementwise_mul.hpp
@@ -0,0 +1,41 @@
+#ifndef ELEMENTWISE_MUL_HPP_
+#define ELEMENTWISE_MUL_HPP_
+
+#include "common.hpp"
+
+/**
+ * ELEMENTWISE MUL FUNCTION
+ */
+template<
+    unsigned int BATCH_SIZE,
+    unsigned int ROWS,
+    unsigned int COLS,
+    unsigned int CHANNELS,
+    typename elementwise_mul_t
+>
+void elementwise_mul(
+    stream_t(elementwise_mul_t)  &in1,
+    stream_t(elementwise_mul_t)  &in2,
+    stream_t(elementwise_mul_t)  &out
+)
+{
+#pragma HLS INLINE OFF
+
+    const unsigned int batch_size   = BATCH_SIZE;
+    const unsigned int rows         = ROWS;
+    const unsigned int cols         = COLS;
+    const unsigned int channels     = CHANNELS;
+
+#pragma HLS STREAM variable=in1
+#pragma HLS STREAM variable=in2
+#pragma HLS STREAM variable=out
+
+    for(unsigned long pixel_index=0 ; pixel_index < batch_size*rows*cols*channels ; pixel_index++) {
+        #pragma HLS PIPELINE II=1 rewind
+        elementwise_mul_t tmp1 = in1.read();
+        elementwise_mul_t tmp2 = in2.read();
+        out.write(tmp1 * tmp2);
+    }
+}
+
+#endif
\ No newline at end of file
diff --git a/fpgaconvnet/hls/hardware/global_pool.hpp b/fpgaconvnet/hls/hardware/global_pool.hpp
new file mode 100644
index 0000000..99aac2c
--- /dev/null
+++ b/fpgaconvnet/hls/hardware/global_pool.hpp
@@ -0,0 +1,62 @@
+#ifndef GLOBAL_POOL_HPP_
+#define GLOBAL_POOL_HPP_
+
+#include "common.hpp"
+/**
+  * GLOBAL_POOL
+  */
+
+ template <
+     unsigned int BATCH_SIZE,
+     unsigned int ROWS,
+     unsigned int COLS,
+     unsigned int CHANNELS,
+     typename global_pool_t
+>
+
+void global_pool(
+    stream_t(global_pool_t) &in, 
+    stream_t(global_pool_t) &out
+)
+{
+#pragma HLS INLINE OFF
+
+    const unsigned int batch_size   = BATCH_SIZE;
+    const unsigned int rows         = ROWS;
+    const unsigned int cols         = COLS;
+    const unsigned int channels     = CHANNELS; 
+
+#pragma HLS STREAM variable=in
+#pragma HLS STREAM variable=out
+
+    global_pool_t pool_cache[rows*cols][batch_size*channels];
+    #pragma HLS ARRAY_PARTITION variable=pool_cache complete dim=0
+    #pragma HLS DEPENDENCE variable=pool_cache RAW intra true
+
+    ap_fixed<64, 56, AP_RND,AP_SAT> accum_value; 
+    global_pool_t accum_cache; 
+
+    read_loop: for (unsigned long i = 0; i < rows*cols; i++) {
+        read_loop2: for (unsigned long j = 0; j < batch_size*channels; j++) {
+            pool_cache[i][j] = in.read();
+            // std::cout << "pool_cache[" << i << "][" << j << "] = " << pool_cache[i][j] << std::endl;
+        }
+    }
+
+    average_loop: for (unsigned long i = 0; i < batch_size*channels; i++) {
+        #pragma HLS PIPELINE II=1 rewind
+        accum_value = 0;        
+
+        average_loop2: for (unsigned long j = 0; j < rows*cols; j++) {
+            accum_value += pool_cache[j][i];
+            // std::cout << "adding pool_cache[" << j << "][" << i << "] = " << pool_cache[j][i] << std::endl;
+        }
+        // std::cout << "rows*cols = " << rows*cols << std::endl;
+        accum_value = accum_value / (rows*cols);
+        accum_cache.range() = (accum_value.range(15,8) << 8) + (accum_value.range(7,0));
+        // std::cout << "accum_value_range = " << accum_value.range() << ", accum_cache_range = " << accum_cache.range() << std::endl;
+        out.write(accum_cache);
+    }
+}
+
+#endif
\ No newline at end of file
diff --git a/fpgaconvnet/hls/hardware/mem_write.hpp b/fpgaconvnet/hls/hardware/mem_write.hpp
index 5233d2d..9f7aa39 100644
--- a/fpgaconvnet/hls/hardware/mem_write.hpp
+++ b/fpgaconvnet/hls/hardware/mem_write.hpp
@@ -79,8 +79,9 @@ void mem_write(
         streams_loop: for(unsigned stream_index=0; stream_index<streams; stream_index++) {
             type_t stream_cache = out[stream_index].read();
             unsigned int port_index = (int) (stream_index/dma_channels);
-            port_cache[port_index] |= ( ( stream_cache.range() & bit_mask ) <<
-                    ( ( stream_index%dma_channels ) * data_width ) );
+            type_t write_value; 
+            write_value.range() = stream_cache.range() & bit_mask;
+            port_cache[port_index] |= (stream_cache.range() & bit_mask) * mem_t(1) << (stream_index%dma_channels)*data_width;
         }
 
         port_write_loop: for (unsigned port_index=0; port_index < ports; port_index++) {
diff --git a/fpgaconvnet/hls/hardware/sliding_window.hpp b/fpgaconvnet/hls/hardware/sliding_window.hpp
index 023bee0..e4432da 100644
--- a/fpgaconvnet/hls/hardware/sliding_window.hpp
+++ b/fpgaconvnet/hls/hardware/sliding_window.hpp
@@ -95,10 +95,10 @@ void sliding_window_line_shift(
         sliding_window_t pixel;
 
         // pixel padding
-        if( row_index < pad_bottom ) {
+        if( row_index < pad_top ) {
             pixel = 0;
         }
-        else if ( row_index > rows+pad_bottom-1 ) {
+        else if ( row_index > rows+pad_top-1 ) {
             pixel = 0;
         }
         else if ( col_index < pad_left ) {
diff --git a/fpgaconvnet/hls/tools/onnx_data.py b/fpgaconvnet/hls/tools/onnx_data.py
index 5c0ce5d..0a34a78 100644
--- a/fpgaconvnet/hls/tools/onnx_data.py
+++ b/fpgaconvnet/hls/tools/onnx_data.py
@@ -16,7 +16,8 @@
 
 import fpgaconvnet.tools.graphs as graphs
 import fpgaconvnet.tools.layer_enum as layer_enum
-import fpgaconvnet.tools.onnx_helper as onnx_helper
+from fpgaconvnet.parser import Parser
+import fpgaconvnet.parser.onnx.helper as onnx_helper
 import fpgaconvnet.proto.fpgaconvnet_pb2 as fpgaconvnet_pb2
 
 from fpgaconvnet.hls.tools.array_init import array_init
@@ -104,8 +105,9 @@ def _fixed_point_stream_format(stream, streams=1, port_width=64, ports=1):
     # get width of fixed point data
     data_width = sum(stream[0].format)
     # check theres enough ports for the streams
+    print(f"Streams:{streams}, Ports:{ports}, PortWidth:{port_width}, DataWidth:{data_width}, Maximum stream number: {ports*(port_width/data_width)}, Number of elements = {stream.shape[0]}")
     if streams > ports*(port_width/data_width):
-        raise ValueError
+        raise ValueError(f"Streams:{streams}, Ports:{ports}, PortWidth:{port_width}, DataWidth:{data_width}. Too many streams for p*(pw/dw): {ports*(port_width/data_width)}")
     # get port data type
     if   port_width == 8:
         port_type = np.uint8
@@ -155,9 +157,55 @@ def _fixed_point_stream_to_bin(stream, output_path, streams=1, port_width=64, po
         bin_out[i].astype(port_type).tofile(f"{output_path}_{i}.bin".format(i=i))
 
 def _fixed_point_stream_to_dat(stream, output_path, streams=1, port_width=64, ports=1):
-    # get the formatted_stream
-    bin_out = _fixed_point_stream_format(stream, streams=streams, port_width=port_width, ports=ports)
-    # save to binary file
+    # check it's only a 1D array
+    assert len(stream.shape) == 1
+    # check the stream is fixed-point
+    # TODO
+    # get width of fixed point data
+    data_width = sum(stream[0].format)
+    # check theres enough ports for the streams
+    print(f"Streams:{streams}, Ports:{ports}, PortWidth:{port_width}, DataWidth:{data_width}, Maximum stream number: {ports*(port_width/data_width)}, Number of elements = {stream.shape[0]}")
+    if streams > ports*(port_width/data_width):
+        raise ValueError(f"Streams:{streams}, Ports:{ports}, PortWidth:{port_width}, DataWidth:{data_width}. Insufficient port_width, too many streams for p*(pw/dw): {ports*(port_width/data_width)}")
+    # get port data type
+    # if   port_width == 8:
+    #     port_type = np.uint8
+    # elif port_width == 16:
+    #     port_type = np.uint16
+    # elif port_width == 32:
+    #     port_type = np.uint32
+    # elif port_width == 64:
+    #     port_type = np.uint64
+    # else:
+    #     raise TypeError
+    if port_width not in [8, 16, 32, 64, 128, 256, 512]:
+        raise TypeError ("port_width must be 8, 16, 32, 64, 128, 256, or 512")
+    # get stream data type
+    if   0  < data_width <= 8:
+        data_type = np.uint8
+    elif 8  < data_width <= 16:
+        data_type = np.uint16
+    elif 16 < data_width <= 32:
+        data_type = np.uint32
+    elif 32 < data_width <= 64:
+        data_type = np.uint64
+    else:
+        raise TypeError ("data_width must be 8, 16, 32, or 64")
+    # check streams are a factor of the stream shape
+    if not stream.shape[0]%streams == 0:
+        raise ValueError
+    # get the size of the binary streas out, size is number of lines in .dat file
+    size = int(stream.shape[0]/streams)
+    # binary stream init
+    bin_out = np.zeros([ports,size], dtype=object)
+    # binary stream packing
+    for i in range(size):
+        for j in range(streams):
+            port_index = math.floor((j*data_width)/port_width) # streams*data_width/port_width has to be < 1
+            stream_val = int(data_type( stream[i*streams+j].bits_to_signed() & ((2**data_width)-1) ))
+            # bin_out[port_index][i] |= port_type( stream_val  << (data_width*j)%port_width )
+            bin_out[port_index][i] += int( stream_val  << (data_width*j)%port_width ) # replace |= for undetermined port width
+    # return the formatted stream
     for i in range(ports):
         with open(f"{output_path}_{i}.dat", 'w') as f:
             f.write("\n".join([str(j) for j in bin_out[i]]))
diff --git a/setup.py b/setup.py
deleted file mode 100644
index 47a1072..0000000
--- a/setup.py
+++ /dev/null
@@ -1,28 +0,0 @@
-import setuptools
-
-with open("README.md", "r", encoding="utf-8") as fh:
-    long_description = fh.read()
-
-setuptools.setup(
-    name="fpgaconvnet-hls",
-    version="0.1.2",
-    author="Alex Montgomerie",
-    author_email="am9215@ic.ac.uk",
-    description="Optimiser for mapping convolutional neural network models to FPGA platforms.",
-    long_description=long_description,
-    long_description_content_type="text/markdown",
-    url="https://github.com/AlexMontgomerie/fpgaconvnet-hls",
-    include_package_data=True,
-    packages=setuptools.find_namespace_packages(
-        include=['fpgaconvnet.*']),
-    classifiers=[
-        "Programming Language :: Python :: 3",
-        "Operating System :: OS Independent",
-    ],
-    python_requires='>=3.8',
-    install_requires=[
-        "fpbinary",
-        "untangle",
-        "numpy"
-    ]
-)


Test patch:
diff --git a/test/layers/avg_pooling/config/config_0.json b/test/layers/avg_pooling/config/config_0.json
new file mode 100644
index 0000000..a6fa605
--- /dev/null
+++ b/test/layers/avg_pooling/config/config_0.json
@@ -0,0 +1,17 @@
+{
+    "type": "AVG_POOLING",
+    "rows_in": 56,
+    "cols_in": 56,
+    "channels_in": 32,
+    "kernel_size": 2,
+    "stride": 2,
+    "pad": 0,
+    "coarse": 1,
+    "coarse_in": 1,
+    "coarse_out": 1,
+    "fine": 1,
+    "pool_type": "avg",
+    "batch_size": 1,
+    "data_width": 16,
+    "description": "layer name: pool1, net name: resnet.prototxt"
+}
diff --git a/test/layers/avg_pooling/gen_layer.py b/test/layers/avg_pooling/gen_layer.py
new file mode 100644
index 0000000..2bf31e9
--- /dev/null
+++ b/test/layers/avg_pooling/gen_layer.py
@@ -0,0 +1,81 @@
+import sys
+import os
+import numpy as np
+import csv
+import copy
+
+sys.path.append('..')
+
+from fpgaconvnet.models.layers.PoolingLayer import PoolingLayer
+import fpgaconvnet.hls.generate.layers.avg_pooling as avg_pooling
+from Layer import Layer
+
+class AvgPoolingLayerTB(Layer):
+    def __init__(self):
+        self.name = 'avg_pooling_layer'
+        Layer.__init__(self,self.name)
+
+    # update stimulus generation
+    def gen_stimulus(self):
+
+        # Init Layer
+        layer = PoolingLayer(
+            self.param['rows_in'],
+            self.param['cols_in'],
+            self.param['channels_in'],
+            coarse=self.param['coarse'],
+            pool_type=self.param['pool_type'],
+            kernel_rows=self.param['kernel_size'],
+            kernel_cols=self.param['kernel_size'],
+            stride_rows=self.param['stride'],
+            stride_cols=self.param['stride'],
+        )
+
+        # update parameters
+        self.param["kernel_size"] = layer.kernel_size
+        self.param["stride"] = layer.stride
+        self.param["pad_top"] = layer.pad_top
+        self.param["pad_right"] = layer.pad_right
+        self.param["pad_bottom"] = layer.pad_bottom
+        self.param["pad_left"] = layer.pad_left
+
+        # data in
+        data_in = self.gen_data([
+            self.param['rows_in'],
+            self.param['cols_in'],
+            self.param['channels_in']
+        ])
+
+        # data out
+        data_out = layer.functional_model(copy.copy(data_in))[0]
+        data_out = np.moveaxis(data_out,0,-1)
+
+        # add output dimensions
+        self.param['rows_out']      = layer.rows_out()
+        self.param['cols_out']      = layer.cols_out()
+        self.param['channels_out']  = layer.channels_out()
+
+        # return data
+        data = {
+            'input'  : data_in.reshape(-1).tolist(),
+            'output' : data_out.reshape(-1).tolist()
+        }
+        # resource and latency model
+        model = {
+            'latency'   : layer.latency(),
+            'resources' : layer.resource()
+        }
+        return data, model
+
+    # update layer generation
+    def gen_layer(self,src_path,header_path):
+        avg_pooling.gen_avg_pooling_layer(
+            self.name,
+            self.param,
+            os.path.join(src_path,'{}.cpp'.format(self.name)),
+            os.path.join(header_path,'{}.hpp'.format(self.name))
+        )
+
+if __name__ == '__main__':
+    avg_pooling_layer_tb = AvgPoolingLayerTB()
+    avg_pooling_layer_tb.main(sys.argv[1:])
diff --git a/test/layers/avg_pooling/src/avg_pooling_layer_top.cpp b/test/layers/avg_pooling/src/avg_pooling_layer_top.cpp
new file mode 100644
index 0000000..39f6ea7
--- /dev/null
+++ b/test/layers/avg_pooling/src/avg_pooling_layer_top.cpp
@@ -0,0 +1,18 @@
+#include "avg_pooling_layer_tb.hpp"
+#include "avg_pooling_layer.hpp"
+
+void avg_pooling_layer_top(
+  stream_t(avg_pooling_layer_data_t) in[AVG_POOLING_LAYER_COARSE],
+  stream_t(avg_pooling_layer_data_t) out[AVG_POOLING_LAYER_COARSE],
+  int mode
+)
+{
+#pragma HLS DATAFLOW
+#pragma HLS INTERFACE ap_ctrl_chain port=return
+
+#pragma HLS STREAM variable=in
+#pragma HLS STREAM variable=out
+
+    avg_pooling_layer(in,out,mode);
+
+}
\ No newline at end of file
diff --git a/test/layers/avg_pooling/tb/avg_pooling_layer_tb.cpp b/test/layers/avg_pooling/tb/avg_pooling_layer_tb.cpp
new file mode 100644
index 0000000..f47128a
--- /dev/null
+++ b/test/layers/avg_pooling/tb/avg_pooling_layer_tb.cpp
@@ -0,0 +1,54 @@
+#include "common_tb.hpp"
+#include "avg_pooling_layer_tb.hpp"
+
+int main() {
+    int err = 0;
+
+    std::string input_path  = std::string(DATA_DIR)+"/input.dat";
+    std::string output_path = std::string(DATA_DIR)+"/output.dat";
+
+    stream_t(avg_pooling_layer_data_t) in[AVG_POOLING_LAYER_COARSE];
+    stream_t(avg_pooling_layer_data_t) out[AVG_POOLING_LAYER_COARSE];
+    stream_t(avg_pooling_layer_data_t) out_correct[AVG_POOLING_LAYER_COARSE];
+
+    // test images
+    static avg_pooling_layer_data_t test_in[DIVIDE(AVG_POOLING_LAYER_CHANNELS,AVG_POOLING_LAYER_COARSE)*AVG_POOLING_LAYER_ROWS*AVG_POOLING_LAYER_COLS][AVG_POOLING_LAYER_COARSE];
+    static avg_pooling_layer_data_t test_out[DIVIDE(AVG_POOLING_LAYER_CHANNELS,AVG_POOLING_LAYER_COARSE)*AVG_POOLING_LAYER_ROWS_OUT*AVG_POOLING_LAYER_COLS_OUT][AVG_POOLING_LAYER_COARSE];
+
+    // load input
+    load_data<
+        DIVIDE(AVG_POOLING_LAYER_CHANNELS,AVG_POOLING_LAYER_COARSE)*AVG_POOLING_LAYER_ROWS*AVG_POOLING_LAYER_COLS,
+        AVG_POOLING_LAYER_COARSE,
+        avg_pooling_layer_data_t
+    >(input_path,test_in);
+
+    // load output
+    load_data<
+        DIVIDE(AVG_POOLING_LAYER_CHANNELS,AVG_POOLING_LAYER_COARSE)*AVG_POOLING_LAYER_ROWS_OUT*AVG_POOLING_LAYER_COLS_OUT,
+        AVG_POOLING_LAYER_COARSE,
+        avg_pooling_layer_data_t
+    >(output_path,test_out);
+
+    // convert to streams
+    to_stream<
+        DIVIDE(AVG_POOLING_LAYER_CHANNELS,AVG_POOLING_LAYER_COARSE)*AVG_POOLING_LAYER_ROWS*AVG_POOLING_LAYER_COLS,
+        AVG_POOLING_LAYER_COARSE,
+        avg_pooling_layer_data_t
+    >(test_in,in);
+
+    to_stream<
+        DIVIDE(AVG_POOLING_LAYER_CHANNELS,AVG_POOLING_LAYER_COARSE)*AVG_POOLING_LAYER_ROWS_OUT*AVG_POOLING_LAYER_COLS_OUT,
+        AVG_POOLING_LAYER_COARSE,
+        avg_pooling_layer_data_t
+    >(test_out,out_correct);
+
+    avg_pooling_layer_top(in,out,0);
+
+    for(int i=0;i<AVG_POOLING_LAYER_COARSE;i++) {
+        printf("TESTING OUTPUT %d: ",i);
+        err += checkStreamEqual<avg_pooling_layer_data_t>(out[i],out_correct[i],false);
+        printf("%s\n",(err==0) ? "passed" : "failed");
+    }
+
+    return err; 
+}
\ No newline at end of file
diff --git a/test/layers/avg_pooling/tb/avg_pooling_layer_tb.hpp b/test/layers/avg_pooling/tb/avg_pooling_layer_tb.hpp
new file mode 100644
index 0000000..b9fb1b3
--- /dev/null
+++ b/test/layers/avg_pooling/tb/avg_pooling_layer_tb.hpp
@@ -0,0 +1,14 @@
+#ifndef AVG_POOLING_LAYER_TB_HPP
+#define AVG_POOLING_LAYER_TB_HPP
+
+#include "common.hpp"
+#include "avg_pooling_layer.hpp"
+#include "avg_pooling_layer_param.hpp"
+
+void avg_pooling_layer_top(
+  stream_t(avg_pooling_layer_data_t) in[AVG_POOLING_LAYER_COARSE],
+  stream_t(avg_pooling_layer_data_t) out[AVG_POOLING_LAYER_COARSE],
+  int mode
+);
+
+#endif
\ No newline at end of file
diff --git a/test/layers/convolution/config/config_0.json b/test/layers/convolution/config/config_0.json
index d08659e..e3fc6f1 100644
--- a/test/layers/convolution/config/config_0.json
+++ b/test/layers/convolution/config/config_0.json
@@ -1,24 +1,74 @@
 {
-    "buffer_depth": 0,
-    "rows_in": 1,
-    "cols_in": 1,
-    "channels_in": 10,
-    "filters": 10,
-    "kernel_size": 1,
-    "stride": 1,
-    "groups": 1,
-    "pad": 0,
-    "coarse_in": 1,
-    "coarse_out": 1,
+    "batch_size": 1,
+    "rows_in": 32,
+    "cols_in": 32,
+    "channels_in": 16,
+    "rows_out": 16,
+    "cols_out": 16,
+    "channels_out": 32,
+    "coarse_in": 16,
+    "coarse_out": 8,
     "coarse_group": 1,
+    "groups": 1,
     "fine": 1,
-    "wr_factor": 1,
-    "batch_size": 1,
-    "input_width": 16,
-    "acc_width": 30,
-    "weight_width": 16,
-    "biases_width": 16,
-    "output_width": 16,
+    "filters": 32,
+    "pad_top": 0,
+    "pad_right": 0,
+    "pad_bottom": 0,
+    "pad_left": 0,
+    "kernel_rows": 1,
+    "kernel_cols": 1,
+    "kernel_size": [
+      1,
+      1
+    ],
+    "stride_rows": 2,
+    "stride_cols": 2,
+    "stride": [
+      2,
+      2
+    ],
+    "mem_bw_in": 100.0,
+    "mem_bw_out": 100.0,
+    "data_t": {},
+    "weight_t": {
+      "width": 16,
+      "binary_point": 15
+    },
+    "acc_t": {
+      "width": 30,
+      "binary_point": 23
+    },
+    "input_t": {
+      "width": 16,
+      "binary_point": 8
+    },
+    "output_t": {
+      "width": 16,
+      "binary_point": 8
+    },
     "has_bias": 1,
-    "description": "no spatial dim, no channels, no filters. No parallelism. Kernel Size =1"
-}
+    "use_uram": false,
+    "block_floating_point": false,
+    "on_chip_addr_range": 4,
+    "off_chip_buffer_size": 0,
+    "off_chip_interval": -1,
+    "stream_weights": 0,
+    "stream_inputs": [
+      false
+    ],
+    "stream_outputs": [
+      false
+    ],
+    "input_compression_ratio": [
+      1.0,
+      1.0,
+      1.0
+    ],
+    "output_compression_ratio": [
+      1.0
+    ],
+    "weight_compression_ratio": [
+      1.0
+    ]
+  }
\ No newline at end of file
diff --git a/test/layers/elementwise_add/config/config_0.json b/test/layers/elementwise_add/config/config_0.json
new file mode 100644
index 0000000..568d9ff
--- /dev/null
+++ b/test/layers/elementwise_add/config/config_0.json
@@ -0,0 +1,12 @@
+{
+    "type": "ELTWISE_ADD",
+    "rows_in": 28,
+    "cols_in": 28,
+    "channels_in": 64,
+    "coarse": 4,
+    "coarse_in": 4,
+    "coarse_out": 4,
+    "batch_size": 1,
+    "data_width": 16,
+    "description": "layer name: inception_3b/relu_pool_proj, net name: googlenet.prototxt"
+}
diff --git a/test/layers/elementwise_add/gen_layer.py b/test/layers/elementwise_add/gen_layer.py
new file mode 100644
index 0000000..e0e969b
--- /dev/null
+++ b/test/layers/elementwise_add/gen_layer.py
@@ -0,0 +1,77 @@
+import sys 
+import os
+import numpy as np
+
+sys.path.append("..")
+from Layer import Layer
+
+from fpgaconvnet.models.layers.EltWiseLayer import EltWiseLayer
+import fpgaconvnet.hls.generate.layers.elementwise_add as elementwise_add
+
+class EltWiseAddLayerTB(Layer): 
+    def __init__(self):
+        self.name = 'elementwise_add_layer'
+        Layer.__init__(self,self.name)
+        
+    # update stimulus generation
+    def gen_stimulus(self): 
+        # Init Module
+        layer = EltWiseLayer(
+            self.param['rows_in'],
+            self.param['cols_in'],
+            self.param['channels_in'],
+            ports_in=2, 
+            coarse=self.param['coarse'],
+            op_type='add'
+        )
+        
+        # data in
+        data_in = np.zeros([
+            2, 
+            self.param['rows_in'],
+            self.param['cols_in'],
+            self.param['channels_in']
+        ])
+        
+        for ports_in in range (2): 
+            data_in[ports_in] = self.gen_data([
+                self.param['rows_in'],
+                self.param['cols_in'],
+                self.param['channels_in']
+            ],data_range=[-1,1])
+            
+        # data out
+        assert layer.op_type == 'add', f"Operation type cannot be {layer.op_type}, must be add"
+        data_out = layer.functional_model(data_in)[0]
+        data_out = np.moveaxis(data_out,0,-1)
+        
+        # add output dimensions
+        self.param['rows_out']      = layer.rows_out()
+        self.param['cols_out']      = layer.cols_out()
+        self.param['channels_out']  = layer.channels_out()
+        # return data
+        data = {
+            'input_1'  : data_in[0].reshape(-1).tolist(),
+            'input_2'  : data_in[1].reshape(-1).tolist(),
+            'output' : data_out.reshape(-1).tolist()
+        }
+        # resource and latency model
+        model = {
+            'latency'   : layer.latency(),
+            'resources' : layer.resource()
+        }
+        return data, model
+    
+    # update layer generation
+    def gen_layer(self,src_path,header_path): 
+        elementwise_add.gen_elementwise_add_layer(
+            self.name,
+            self.param,
+            os.path.join(src_path,'{}.cpp'.format(self.name)),
+            os.path.join(header_path,'{}.hpp'.format(self.name))
+        )
+        
+if __name__ == '__main__':
+    elementwise_add_layer = EltWiseAddLayerTB()
+    elementwise_add_layer.main(sys.argv[1:])
+        
\ No newline at end of file
diff --git a/test/layers/elementwise_add/src/elementwise_add_layer_top.cpp b/test/layers/elementwise_add/src/elementwise_add_layer_top.cpp
new file mode 100644
index 0000000..410ca37
--- /dev/null
+++ b/test/layers/elementwise_add/src/elementwise_add_layer_top.cpp
@@ -0,0 +1,21 @@
+#include "elementwise_add_layer_tb.hpp"
+#include "elementwise_add_layer.hpp"
+
+void elementwise_add_layer_top(
+  stream_t(elementwise_add_layer_data_t) in1[ELEMENTWISE_ADD_LAYER_COARSE],
+  stream_t(elementwise_add_layer_data_t) in2[ELEMENTWISE_ADD_LAYER_COARSE],
+  stream_t(elementwise_add_layer_data_t) out[ELEMENTWISE_ADD_LAYER_COARSE],
+  int mode
+)
+{
+
+#pragma HLS DATAFLOW
+#pragma HLS INTERFACE ap_ctrl_chain port=return
+
+#pragma HLS STREAM variable=in1 
+#pragma HLS STREAM variable=in2
+#pragma HLS STREAM variable=out
+
+    elementwise_add_layer(in1,in2,out,mode);
+
+}
\ No newline at end of file
diff --git a/test/layers/elementwise_add/tb/elementwise_add_layer_tb.cpp b/test/layers/elementwise_add/tb/elementwise_add_layer_tb.cpp
new file mode 100644
index 0000000..3d94fe6
--- /dev/null
+++ b/test/layers/elementwise_add/tb/elementwise_add_layer_tb.cpp
@@ -0,0 +1,68 @@
+#include "common_tb.hpp"
+#include "elementwise_add_layer_tb.hpp"
+
+int main()
+{
+    int err = 0;
+    std::string input_1_path = std::string(DATA_DIR)+"/input_1.dat";
+    std::string input_2_path = std::string(DATA_DIR)+"/input_2.dat";
+    std::string output_path  = std::string(DATA_DIR)+"/output.dat";
+
+    stream_t(elementwise_add_layer_data_t) in1[ELEMENTWISE_ADD_LAYER_COARSE];
+    stream_t(elementwise_add_layer_data_t) in2[ELEMENTWISE_ADD_LAYER_COARSE];
+    stream_t(elementwise_add_layer_data_t) out[ELEMENTWISE_ADD_LAYER_COARSE];
+    stream_t(elementwise_add_layer_data_t) out_correct[ELEMENTWISE_ADD_LAYER_COARSE];
+
+    // test images
+    static elementwise_add_layer_data_t test_in1[DIVIDE(ELEMENTWISE_ADD_LAYER_CHANNELS,ELEMENTWISE_ADD_LAYER_COARSE)*ELEMENTWISE_ADD_LAYER_ROWS*ELEMENTWISE_ADD_LAYER_COLS][ELEMENTWISE_ADD_LAYER_COARSE];
+    static elementwise_add_layer_data_t test_in2[DIVIDE(ELEMENTWISE_ADD_LAYER_CHANNELS,ELEMENTWISE_ADD_LAYER_COARSE)*ELEMENTWISE_ADD_LAYER_ROWS*ELEMENTWISE_ADD_LAYER_COLS][ELEMENTWISE_ADD_LAYER_COARSE];
+    static elementwise_add_layer_data_t test_out[DIVIDE(ELEMENTWISE_ADD_LAYER_CHANNELS,ELEMENTWISE_ADD_LAYER_COARSE)*ELEMENTWISE_ADD_LAYER_ROWS*ELEMENTWISE_ADD_LAYER_COLS][ELEMENTWISE_ADD_LAYER_COARSE];
+
+    // load input
+    load_data<
+        DIVIDE(ELEMENTWISE_ADD_LAYER_CHANNELS,ELEMENTWISE_ADD_LAYER_COARSE)*ELEMENTWISE_ADD_LAYER_ROWS*ELEMENTWISE_ADD_LAYER_COLS,
+        ELEMENTWISE_ADD_LAYER_COARSE,
+        elementwise_add_layer_data_t
+    >(input_1_path,test_in1);
+
+    load_data<
+        DIVIDE(ELEMENTWISE_ADD_LAYER_CHANNELS,ELEMENTWISE_ADD_LAYER_COARSE)*ELEMENTWISE_ADD_LAYER_ROWS*ELEMENTWISE_ADD_LAYER_COLS,
+        ELEMENTWISE_ADD_LAYER_COARSE,
+        elementwise_add_layer_data_t
+    >(input_2_path,test_in2);
+
+    // load output
+    load_data<
+        DIVIDE(ELEMENTWISE_ADD_LAYER_CHANNELS,ELEMENTWISE_ADD_LAYER_COARSE)*ELEMENTWISE_ADD_LAYER_ROWS*ELEMENTWISE_ADD_LAYER_COLS,
+        ELEMENTWISE_ADD_LAYER_COARSE,
+        elementwise_add_layer_data_t
+    >(output_path,test_out);
+
+    // convert to streams
+    to_stream<
+        DIVIDE(ELEMENTWISE_ADD_LAYER_CHANNELS,ELEMENTWISE_ADD_LAYER_COARSE)*ELEMENTWISE_ADD_LAYER_ROWS*ELEMENTWISE_ADD_LAYER_COLS,
+        ELEMENTWISE_ADD_LAYER_COARSE,
+        elementwise_add_layer_data_t
+    >(test_in1,in1);
+
+    to_stream<
+        DIVIDE(ELEMENTWISE_ADD_LAYER_CHANNELS,ELEMENTWISE_ADD_LAYER_COARSE)*ELEMENTWISE_ADD_LAYER_ROWS*ELEMENTWISE_ADD_LAYER_COLS,
+        ELEMENTWISE_ADD_LAYER_COARSE,
+        elementwise_add_layer_data_t
+    >(test_in2,in2);
+
+    to_stream<
+        DIVIDE(ELEMENTWISE_ADD_LAYER_CHANNELS,ELEMENTWISE_ADD_LAYER_COARSE)*ELEMENTWISE_ADD_LAYER_ROWS*ELEMENTWISE_ADD_LAYER_COLS,
+        ELEMENTWISE_ADD_LAYER_COARSE,
+        elementwise_add_layer_data_t
+    >(test_out,out_correct);
+
+    elementwise_add_layer_top(in1,in2,out,0);
+
+    for (int i = 0; i < ELEMENTWISE_ADD_LAYER_COARSE; i++)
+    {
+        printf("TESTING OUTPUT %d: ", i);
+        err += checkStreamEqual<elementwise_add_layer_data_t>(out[i], out_correct[i]);
+        printf("%s\n", (err == 0) ? "passed" : "failed");
+    }
+}
\ No newline at end of file
diff --git a/test/layers/elementwise_add/tb/elementwise_add_layer_tb.hpp b/test/layers/elementwise_add/tb/elementwise_add_layer_tb.hpp
new file mode 100644
index 0000000..ccd5a7b
--- /dev/null
+++ b/test/layers/elementwise_add/tb/elementwise_add_layer_tb.hpp
@@ -0,0 +1,15 @@
+#ifndef ELEMENTWISE_ADD_LAYER_TB_HPP_
+#define ELEMENTWISE_ADD_LAYER_TB_HPP_
+
+#include "common.hpp"
+#include "elementwise_add_layer.hpp"
+#include "elementwise_add_layer_param.hpp"
+
+void elementwise_add_layer_top(
+  stream_t(elementwise_add_layer_data_t) in1[ELEMENTWISE_ADD_LAYER_COARSE],
+  stream_t(elementwise_add_layer_data_t) in2[ELEMENTWISE_ADD_LAYER_COARSE],
+  stream_t(elementwise_add_layer_data_t) out[ELEMENTWISE_ADD_LAYER_COARSE],
+  int mode
+);
+
+#endif 
\ No newline at end of file
diff --git a/test/layers/elementwise_mul/config/config_0.json b/test/layers/elementwise_mul/config/config_0.json
new file mode 100644
index 0000000..af9c500
--- /dev/null
+++ b/test/layers/elementwise_mul/config/config_0.json
@@ -0,0 +1,12 @@
+{
+    "type": "ELTWISE_MUL",
+    "rows_in": 28,
+    "cols_in": 28,
+    "channels_in": 64,
+    "coarse": 4,
+    "coarse_in": 4,
+    "coarse_out": 4,
+    "batch_size": 1,
+    "data_width": 16,
+    "description": "layer name: inception_3b/relu_pool_proj, net name: googlenet.prototxt"
+}
diff --git a/test/layers/elementwise_mul/gen_layer.py b/test/layers/elementwise_mul/gen_layer.py
new file mode 100644
index 0000000..e4dd107
--- /dev/null
+++ b/test/layers/elementwise_mul/gen_layer.py
@@ -0,0 +1,77 @@
+import sys 
+import os 
+import numpy as np
+
+sys.path.append("..")
+from Layer import Layer
+
+from fpgaconvnet.models.layers.EltWiseLayer import EltWiseLayer
+import fpgaconvnet.hls.generate.layers.elementwise_mul as elementwise_mul
+
+class EltWiseMulLayerTB(Layer):
+    def __init__(self):
+        self.name = 'elementwise_mul_layer'
+        Layer.__init__(self,self.name)
+        
+    # update stimulus generation
+    def gen_stimulus(self): 
+        # Init Module
+        layer = EltWiseLayer(
+            self.param['rows_in'],
+            self.param['cols_in'],
+            self.param['channels_in'],
+            ports_in=2, 
+            coarse=self.param['coarse'],
+            op_type='mul'
+        )
+        
+        # data in
+        data_in = np.zeros([
+            2, 
+            self.param['rows_in'],
+            self.param['cols_in'],
+            self.param['channels_in']
+        ])
+        
+        for ports_in in range (2): 
+            data_in[ports_in] = self.gen_data([
+                self.param['rows_in'],
+                self.param['cols_in'],
+                self.param['channels_in']
+            ],data_range=[-1,1])
+            
+        # data out
+        assert layer.op_type == 'mul', f"Operation type cannot be {layer.op_type}, must be mul"
+        data_out = layer.functional_model(data_in)[0]
+        data_out = np.moveaxis(data_out,0,-1)
+        
+        # add output dimensions
+        self.param['rows_out']      = layer.rows_out()
+        self.param['cols_out']      = layer.cols_out()
+        self.param['channels_out']  = layer.channels_out()
+        # return data
+        data = {
+            'input_1'  : data_in[0].reshape(-1).tolist(),
+            'input_2'  : data_in[1].reshape(-1).tolist(),
+            'output' : data_out.reshape(-1).tolist()
+        }
+        
+        # resource and latency model
+        model = {
+            'latency'   : layer.latency(),
+            'resources' : layer.resource()
+        }
+        return data, model
+    
+    # update layer generation
+    def gen_layer(self,src_path,header_path): 
+        elementwise_mul.gen_elementwise_mul_layer(
+            self.name,
+            self.param,
+            os.path.join(src_path,'{}.cpp'.format(self.name)),
+            os.path.join(header_path,'{}.hpp'.format(self.name))
+        )
+        
+if __name__ == '__main__':
+    elementwise_add_layer = EltWiseMulLayerTB()
+    elementwise_add_layer.main(sys.argv[1:])
\ No newline at end of file
diff --git a/test/layers/elementwise_mul/src/elementwise_mul_layer_top.cpp b/test/layers/elementwise_mul/src/elementwise_mul_layer_top.cpp
new file mode 100644
index 0000000..2f64f56
--- /dev/null
+++ b/test/layers/elementwise_mul/src/elementwise_mul_layer_top.cpp
@@ -0,0 +1,21 @@
+#include "elementwise_mul_layer_tb.hpp"
+#include "elementwise_mul_layer.hpp"
+
+void elementwise_mul_layer_top(
+  stream_t(elementwise_mul_layer_data_t) in1[ELEMENTWISE_MUL_LAYER_COARSE],
+  stream_t(elementwise_mul_layer_data_t) in2[ELEMENTWISE_MUL_LAYER_COARSE],
+  stream_t(elementwise_mul_layer_data_t) out[ELEMENTWISE_MUL_LAYER_COARSE],
+  int mode
+)
+{
+
+#pragma HLS DATAFLOW
+#pragma HLS INTERFACE ap_ctrl_chain port=return
+
+#pragma HLS STREAM variable=in1
+#pragma HLS STREAM variable=in2
+#pragma HLS STREAM variable=out
+
+    elementwise_mul_layer(in1,in2,out,mode);
+
+}
\ No newline at end of file
diff --git a/test/layers/elementwise_mul/tb/elementwise_mul_layer_tb.cpp b/test/layers/elementwise_mul/tb/elementwise_mul_layer_tb.cpp
new file mode 100644
index 0000000..449d8be
--- /dev/null
+++ b/test/layers/elementwise_mul/tb/elementwise_mul_layer_tb.cpp
@@ -0,0 +1,68 @@
+#include "common_tb.hpp"
+#include "elementwise_mul_layer_tb.hpp"
+
+int main()
+{
+    int err = 0; 
+    std::string input_1_path = std::string(DATA_DIR)+"/input_1.dat";
+    std::string input_2_path = std::string(DATA_DIR)+"/input_2.dat";
+    std::string output_path  = std::string(DATA_DIR)+"/output.dat";
+
+    stream_t(elementwise_mul_layer_data_t) in1[ELEMENTWISE_MUL_LAYER_COARSE];
+    stream_t(elementwise_mul_layer_data_t) in2[ELEMENTWISE_MUL_LAYER_COARSE];
+    stream_t(elementwise_mul_layer_data_t) out[ELEMENTWISE_MUL_LAYER_COARSE];
+    stream_t(elementwise_mul_layer_data_t) out_correct[ELEMENTWISE_MUL_LAYER_COARSE];
+
+    // test images
+    static elementwise_mul_layer_data_t test_in1[DIVIDE(ELEMENTWISE_MUL_LAYER_CHANNELS,ELEMENTWISE_MUL_LAYER_COARSE)*ELEMENTWISE_MUL_LAYER_ROWS*ELEMENTWISE_MUL_LAYER_COLS][ELEMENTWISE_MUL_LAYER_COARSE];
+    static elementwise_mul_layer_data_t test_in2[DIVIDE(ELEMENTWISE_MUL_LAYER_CHANNELS,ELEMENTWISE_MUL_LAYER_COARSE)*ELEMENTWISE_MUL_LAYER_ROWS*ELEMENTWISE_MUL_LAYER_COLS][ELEMENTWISE_MUL_LAYER_COARSE];
+    static elementwise_mul_layer_data_t test_out[DIVIDE(ELEMENTWISE_MUL_LAYER_CHANNELS,ELEMENTWISE_MUL_LAYER_COARSE)*ELEMENTWISE_MUL_LAYER_ROWS*ELEMENTWISE_MUL_LAYER_COLS][ELEMENTWISE_MUL_LAYER_COARSE];
+
+    // load input 
+    load_data<
+        DIVIDE(ELEMENTWISE_MUL_LAYER_CHANNELS,ELEMENTWISE_MUL_LAYER_COARSE)*ELEMENTWISE_MUL_LAYER_ROWS*ELEMENTWISE_MUL_LAYER_COLS,
+        ELEMENTWISE_MUL_LAYER_COARSE,
+        elementwise_mul_layer_data_t
+    >(input_1_path,test_in1);
+
+    load_data<
+        DIVIDE(ELEMENTWISE_MUL_LAYER_CHANNELS,ELEMENTWISE_MUL_LAYER_COARSE)*ELEMENTWISE_MUL_LAYER_ROWS*ELEMENTWISE_MUL_LAYER_COLS,
+        ELEMENTWISE_MUL_LAYER_COARSE,
+        elementwise_mul_layer_data_t
+    >(input_2_path,test_in2);
+
+    // load output
+    load_data<
+        DIVIDE(ELEMENTWISE_MUL_LAYER_CHANNELS,ELEMENTWISE_MUL_LAYER_COARSE)*ELEMENTWISE_MUL_LAYER_ROWS*ELEMENTWISE_MUL_LAYER_COLS,
+        ELEMENTWISE_MUL_LAYER_COARSE,
+        elementwise_mul_layer_data_t
+    >(output_path,test_out);
+
+    // convert to streams
+    to_stream<
+        DIVIDE(ELEMENTWISE_MUL_LAYER_CHANNELS,ELEMENTWISE_MUL_LAYER_COARSE)*ELEMENTWISE_MUL_LAYER_ROWS*ELEMENTWISE_MUL_LAYER_COLS,
+        ELEMENTWISE_MUL_LAYER_COARSE,
+        elementwise_mul_layer_data_t
+    >(test_in1,in1);
+
+    to_stream<
+        DIVIDE(ELEMENTWISE_MUL_LAYER_CHANNELS,ELEMENTWISE_MUL_LAYER_COARSE)*ELEMENTWISE_MUL_LAYER_ROWS*ELEMENTWISE_MUL_LAYER_COLS,
+        ELEMENTWISE_MUL_LAYER_COARSE,
+        elementwise_mul_layer_data_t
+    >(test_in2,in2);
+
+    to_stream<
+        DIVIDE(ELEMENTWISE_MUL_LAYER_CHANNELS,ELEMENTWISE_MUL_LAYER_COARSE)*ELEMENTWISE_MUL_LAYER_ROWS*ELEMENTWISE_MUL_LAYER_COLS,
+        ELEMENTWISE_MUL_LAYER_COARSE,
+        elementwise_mul_layer_data_t
+    >(test_out,out_correct);
+
+    elementwise_mul_layer_top(in1,in2,out,0);
+
+    for (int i = 0; i < ELEMENTWISE_MUL_LAYER_COARSE; i++)
+    {
+        printf("TESTING OUTPUT %d: ", i);
+        err += checkStreamEqual<elementwise_mul_layer_data_t>(out[i], out_correct[i]);
+        printf("%s\n", (err == 0) ? "passed" : "failed");
+    }
+}
\ No newline at end of file
diff --git a/test/layers/elementwise_mul/tb/elementwise_mul_layer_tb.hpp b/test/layers/elementwise_mul/tb/elementwise_mul_layer_tb.hpp
new file mode 100644
index 0000000..a892efe
--- /dev/null
+++ b/test/layers/elementwise_mul/tb/elementwise_mul_layer_tb.hpp
@@ -0,0 +1,15 @@
+#ifndef ELEMENTWISE_MUL_LAYER_TB_HPP_
+#define ELEMENTWISE_MUL_LAYER_TB_HPP_
+
+#include "common.hpp"
+#include "elementwise_mul_layer.hpp"
+#include "elementwise_mul_layer_param.hpp"
+
+void elementwise_mul_layer_top(
+  stream_t(elementwise_mul_layer_data_t) in1[ELEMENTWISE_MUL_LAYER_COARSE],
+  stream_t(elementwise_mul_layer_data_t) in2[ELEMENTWISE_MUL_LAYER_COARSE],
+  stream_t(elementwise_mul_layer_data_t) out[ELEMENTWISE_MUL_LAYER_COARSE],
+  int mode
+);
+
+#endif
\ No newline at end of file
diff --git a/test/layers/gen_config.py b/test/layers/gen_config.py
index 58aa355..76561df 100644
--- a/test/layers/gen_config.py
+++ b/test/layers/gen_config.py
@@ -1,3 +1,4 @@
+# This script is out of date and yet to be updated
 import os
 import sys
 import json
diff --git a/test/layers/global_pooling/config/config_0.json b/test/layers/global_pooling/config/config_0.json
new file mode 100644
index 0000000..7c0149a
--- /dev/null
+++ b/test/layers/global_pooling/config/config_0.json
@@ -0,0 +1,12 @@
+{
+    "type": "GLOBAL_POOLING",
+    "rows_in": 8,
+    "cols_in": 8,
+    "channels_in": 32,
+    "coarse": 1,
+    "coarse_in": 1,
+    "coarse_out": 1,
+    "batch_size": 1,
+    "data_width": 16,
+    "description": "layer name: pool1, net name: resnet.prototxt"
+}
diff --git a/test/layers/global_pooling/gen_layer.py b/test/layers/global_pooling/gen_layer.py
new file mode 100644
index 0000000..25622e0
--- /dev/null
+++ b/test/layers/global_pooling/gen_layer.py
@@ -0,0 +1,69 @@
+import sys
+import os
+import numpy as np
+import copy
+
+sys.path.append('..')
+
+from fpgaconvnet.models.layers.GlobalPoolingLayer import GlobalPoolingLayer
+import fpgaconvnet.hls.generate.layers.global_pooling as global_pooling
+from Layer import Layer
+
+class GlobalPoolingLayerTB(Layer):
+    def __init__(self):
+        self.name = 'global_pooling_layer'
+        Layer.__init__(self,self.name)
+        
+    # update stimulus generation
+    def gen_stimulus(self): 
+        
+        # Init Layer
+        layer = GlobalPoolingLayer(
+            rows=self.param['rows_in'],
+            cols=self.param['cols_in'],
+            channels=self.param['channels_in'],
+            coarse=self.param['coarse'],
+        )
+        
+        # data in
+        data_in = self.gen_data([
+            self.param['rows_in'],
+            self.param['cols_in'],
+            self.param['channels_in']
+        ])
+        
+        # data out
+        data_out = layer.functional_model(copy.copy(data_in))[0]
+        data_out = np.moveaxis(data_out,0,-1)
+        
+        # add output dimensions
+        self.param['rows_out']      = layer.rows_out()
+        self.param['cols_out']      = layer.cols_out()
+        self.param['channels_out']  = layer.channels_out()
+        
+        # return data
+        data = {
+            'input'  : data_in.reshape(-1).tolist(),
+            'output' : data_out.reshape(-1).tolist()
+        }
+        
+        # resource and latency model
+        model = {
+            'latency'   : layer.latency(),
+            'resources' : layer.resource()
+        }
+        return data, model
+    
+    # update layer generation
+    def gen_layer(self,src_path,header_path):
+        global_pooling.gen_global_pooling_layer(
+            self.name,
+            self.param,
+            os.path.join(src_path,'{}.cpp'.format(self.name)),
+            os.path.join(header_path,'{}.hpp'.format(self.name))
+        )
+        
+if __name__ == "__main__":
+    global_pooling_layer_tb = GlobalPoolingLayerTB()
+    global_pooling_layer_tb.main(sys.argv[1:])
+        
\ No newline at end of file
diff --git a/test/layers/global_pooling/src/global_pooling_layer_top.cpp b/test/layers/global_pooling/src/global_pooling_layer_top.cpp
new file mode 100644
index 0000000..316b8da
--- /dev/null
+++ b/test/layers/global_pooling/src/global_pooling_layer_top.cpp
@@ -0,0 +1,18 @@
+#include "global_pooling_layer_tb.hpp"
+#include "global_pooling_layer.hpp"
+
+void global_pooling_layer_top(
+  stream_t(global_pooling_layer_data_t) in[GLOBAL_POOLING_LAYER_COARSE],
+  stream_t(global_pooling_layer_data_t) out[GLOBAL_POOLING_LAYER_COARSE],
+  int mode
+)
+{
+#pragma HLS DATAFLOW
+#pragma HLS INTERFACE ap_ctrl_chain port=return
+
+#pragma HLS STREAM variable=in
+#pragma HLS STREAM variable=out
+
+    global_pooling_layer(in,out,mode);
+
+}
\ No newline at end of file
diff --git a/test/layers/global_pooling/tb/global_pooling_layer_tb.cpp b/test/layers/global_pooling/tb/global_pooling_layer_tb.cpp
new file mode 100644
index 0000000..1e916a4
--- /dev/null
+++ b/test/layers/global_pooling/tb/global_pooling_layer_tb.cpp
@@ -0,0 +1,54 @@
+#include "common_tb.hpp"
+#include "global_pooling_layer_tb.hpp"
+
+int main() {
+    int err = 0; 
+
+    std::string input_path  = std::string(DATA_DIR)+"/input.dat";
+    std::string output_path = std::string(DATA_DIR)+"/output.dat";
+
+    stream_t(global_pooling_layer_data_t) in[GLOBAL_POOLING_LAYER_COARSE];
+    stream_t(global_pooling_layer_data_t) out[GLOBAL_POOLING_LAYER_COARSE];
+    stream_t(global_pooling_layer_data_t) out_correct[GLOBAL_POOLING_LAYER_COARSE];
+
+    // test images
+    static global_pooling_layer_data_t test_in[DIVIDE(GLOBAL_POOLING_LAYER_CHANNELS,GLOBAL_POOLING_LAYER_COARSE)*GLOBAL_POOLING_LAYER_ROWS*GLOBAL_POOLING_LAYER_COLS][GLOBAL_POOLING_LAYER_COARSE];
+    static global_pooling_layer_data_t test_out[DIVIDE(GLOBAL_POOLING_LAYER_CHANNELS,GLOBAL_POOLING_LAYER_COARSE)*GLOBAL_POOLING_LAYER_ROWS_OUT*GLOBAL_POOLING_LAYER_COLS_OUT][GLOBAL_POOLING_LAYER_COARSE];
+
+    // load input
+    load_data<
+        DIVIDE(GLOBAL_POOLING_LAYER_CHANNELS,GLOBAL_POOLING_LAYER_COARSE)*GLOBAL_POOLING_LAYER_ROWS*GLOBAL_POOLING_LAYER_COLS,
+        GLOBAL_POOLING_LAYER_COARSE,
+        global_pooling_layer_data_t
+    >(input_path,test_in);
+
+    // load output
+    load_data<
+        DIVIDE(GLOBAL_POOLING_LAYER_CHANNELS,GLOBAL_POOLING_LAYER_COARSE)*GLOBAL_POOLING_LAYER_ROWS_OUT*GLOBAL_POOLING_LAYER_COLS_OUT,
+        GLOBAL_POOLING_LAYER_COARSE,
+        global_pooling_layer_data_t
+    >(output_path,test_out);
+
+    // convert to streams
+    to_stream<
+        DIVIDE(GLOBAL_POOLING_LAYER_CHANNELS,GLOBAL_POOLING_LAYER_COARSE)*GLOBAL_POOLING_LAYER_ROWS*GLOBAL_POOLING_LAYER_COLS,
+        GLOBAL_POOLING_LAYER_COARSE,
+        global_pooling_layer_data_t
+    >(test_in,in);
+
+    to_stream<
+        DIVIDE(GLOBAL_POOLING_LAYER_CHANNELS,GLOBAL_POOLING_LAYER_COARSE)*GLOBAL_POOLING_LAYER_ROWS_OUT*GLOBAL_POOLING_LAYER_COLS_OUT,
+        GLOBAL_POOLING_LAYER_COARSE,
+        global_pooling_layer_data_t
+    >(test_out,out_correct);
+
+    global_pooling_layer_top(in,out,0);
+
+    for(int i=0;i<GLOBAL_POOLING_LAYER_COARSE;i++) {
+        printf("TESTING OUTPUT %d: ",i);
+        err += checkStreamEqual<global_pooling_layer_data_t>(out[i],out_correct[i],false);
+        printf("%s\n",(err==0) ? "passed" : "failed");
+    }
+
+    return err;
+}
\ No newline at end of file
diff --git a/test/layers/global_pooling/tb/global_pooling_layer_tb.hpp b/test/layers/global_pooling/tb/global_pooling_layer_tb.hpp
new file mode 100644
index 0000000..64bce9a
--- /dev/null
+++ b/test/layers/global_pooling/tb/global_pooling_layer_tb.hpp
@@ -0,0 +1,14 @@
+#ifndef GLOBAL_POOLING_LAYER_TB_HPP
+#define GLOBAL_POOLING_LAYER_TB_HPP
+
+#include "common.hpp"
+#include "global_pooling_layer.hpp"
+#include "global_pooling_layer_param.hpp"
+
+void global_pooling_layer_top(
+  stream_t(global_pooling_layer_data_t) in[GLOBAL_POOLING_LAYER_COARSE],
+  stream_t(global_pooling_layer_data_t) out[GLOBAL_POOLING_LAYER_COARSE],
+  int mode
+);
+
+#endif 
\ No newline at end of file
diff --git a/test/layers/inner_product/config/config_0.json b/test/layers/inner_product/config/config_0.json
index 9302d5b..6b53c74 100644
--- a/test/layers/inner_product/config/config_0.json
+++ b/test/layers/inner_product/config/config_0.json
@@ -3,10 +3,10 @@
     "buffer_depth" : 0,
     "rows_in": 1,
     "cols_in": 1,
-    "channels_in": 10,
-    "filters": 100,
-    "coarse_in": 1,
-    "coarse_out": 1,
+    "channels_in": 64,
+    "filters": 10,
+    "coarse_in": 2,
+    "coarse_out": 5,
     "fine": 1,
     "batch_size": 1,
     "wr_factor" : 1,
@@ -16,5 +16,5 @@
     "biases_width": 8,
     "output_width": 16,
     "has_bias": 1,
-    "description": "layer name: ip1, net name: lenet.prototxt"
+    "description": " "
 }
diff --git a/test/layers/pooling/gen_layer.py b/test/layers/pooling/gen_layer.py
index dbe9376..b83db4a 100644
--- a/test/layers/pooling/gen_layer.py
+++ b/test/layers/pooling/gen_layer.py
@@ -23,12 +23,12 @@ def gen_stimulus(self):
             self.param['rows_in'],
             self.param['cols_in'],
             self.param['channels_in'],
-            pool_type=self.param['pool_type'],
-            kernel_size=self.param['kernel_size'],
-            stride=self.param['stride'],
-            pad=self.param['pad'],
             coarse=self.param['coarse'],
-            data_width=self.param["data_width"]
+            pool_type=self.param['pool_type'],
+            kernel_rows=self.param['kernel_size'],
+            kernel_cols=self.param['kernel_size'],
+            stride_rows=self.param['stride'],
+            stride_cols=self.param['stride']
         )
 
         # update parameters
diff --git a/test/layers/run_tests.sh b/test/layers/run_tests.sh
index 31e5cb4..a0d3d42 100755
--- a/test/layers/run_tests.sh
+++ b/test/layers/run_tests.sh
@@ -48,6 +48,7 @@ function run_test {
 
     echo "RUNNING TEST ${1}"
     # GENERATE INPUTS
+    mkdir -p include
     mkdir -p data/test_${1}
     python gen_layer.py -c config/config_${1}.json -o $PWD/data/test_${1} -s src/ -h include/ -t tb/
     # RUN TEST
diff --git a/test/layers/split/config/config_0.json b/test/layers/split/config/config_0.json
new file mode 100644
index 0000000..55983e2
--- /dev/null
+++ b/test/layers/split/config/config_0.json
@@ -0,0 +1,12 @@
+{
+    "type": "RELU",
+    "rows_in": 28,
+    "cols_in": 28,
+    "channels_in": 64,
+    "coarse": 4,
+    "coarse_in": 4,
+    "coarse_out": 4,
+    "batch_size": 1,
+    "data_width": 16,
+    "description": "layer name: inception_3b/relu_pool_proj, net name: googlenet.prototxt"
+}
diff --git a/test/layers/split/gen_layer.py b/test/layers/split/gen_layer.py
new file mode 100644
index 0000000..4df4122
--- /dev/null
+++ b/test/layers/split/gen_layer.py
@@ -0,0 +1,62 @@
+import sys
+import os
+import numpy as np
+
+sys.path.append("..")
+from Layer import Layer
+
+from fpgaconvnet.models.layers.SplitLayer import SplitLayer
+import fpgaconvnet.hls.generate.layers.split as split
+
+class SplitLayerTB(Layer):
+    def __init__(self):
+        self.name = 'split_layer'
+        Layer.__init__(self,self.name)
+        
+    # update stimulus generation
+    def gen_stimulus(self): 
+        # Init Module 
+        layer = SplitLayer(
+            self.param['rows_in'],
+            self.param['cols_in'],
+            self.param['channels_in'],
+            coarse=self.param['coarse']
+        )
+        
+        # data in
+        data_in = self.gen_data([
+            self.param['rows_in'],
+            self.param['cols_in'],
+            self.param['channels_in']
+        ],data_range=[-1,1])
+        # data out
+        data_out = layer.functional_model(data_in)[0]
+        
+        # add output dimensions
+        self.param['rows_out']      = layer.rows_out()
+        self.param['cols_out']      = layer.cols_out()
+        self.param['channels_out']  = layer.channels_out()
+        # return data
+        data = {
+            'input'  : data_in.reshape(-1).tolist(),
+            'output' : data_out.reshape(-1).tolist()
+        }
+        # resource and latency model
+        model = {
+            'latency'   : layer.latency(),
+            'resources' : layer.resource()
+        }
+        return data, model
+        
+    # update layer generation
+    def gen_layer(self,src_path,header_path):
+        split.gen_split_layer(
+            self.name,
+            self.param,
+            os.path.join(src_path,'{}.cpp'.format(self.name)),
+            os.path.join(header_path,'{}.hpp'.format(self.name))
+        )
+        
+if __name__ == '__main__':
+    split_layer_tb = SplitLayerTB()
+    split_layer_tb.main(sys.argv[1:])
\ No newline at end of file
diff --git a/test/layers/split/src/split_layer_top.cpp b/test/layers/split/src/split_layer_top.cpp
new file mode 100644
index 0000000..eb2a1f1
--- /dev/null
+++ b/test/layers/split/src/split_layer_top.cpp
@@ -0,0 +1,21 @@
+#include "split_layer_tb.hpp"
+#include "split_layer.hpp"
+
+void split_layer_top(
+  stream_t(split_layer_data_t) in[SPLIT_LAYER_COARSE],
+  stream_t(split_layer_data_t) out_1[SPLIT_LAYER_COARSE],
+  stream_t(split_layer_data_t) out_2[SPLIT_LAYER_COARSE], 
+  int mode
+)
+{
+
+#pragma HLS DATAFLOW
+#pragma HLS INTERFACE ap_ctrl_chain port=return
+
+#pragma HLS STREAM variable=in
+#pragma HLS STREAM variable=out_1
+#pragma HLS STREAM variable=out_2
+
+    split_layer(in,out_1,out_2,mode);
+
+}
\ No newline at end of file
diff --git a/test/layers/split/tb/split_layer_tb.cpp b/test/layers/split/tb/split_layer_tb.cpp
new file mode 100644
index 0000000..9f02a1a
--- /dev/null
+++ b/test/layers/split/tb/split_layer_tb.cpp
@@ -0,0 +1,64 @@
+#include "common_tb.hpp"
+#include "split_layer_tb.hpp"
+
+int main()
+{
+    int err = 0;
+    std::string input_path  = std::string(DATA_DIR)+"/input.dat";
+    std::string output_path = std::string(DATA_DIR)+"/output.dat";
+
+    stream_t(split_layer_data_t) in[SPLIT_LAYER_COARSE];
+    stream_t(split_layer_data_t) out_1[SPLIT_LAYER_COARSE];
+    stream_t(split_layer_data_t) out_2[SPLIT_LAYER_COARSE];
+    stream_t(split_layer_data_t) out_correct_1[SPLIT_LAYER_COARSE];
+    stream_t(split_layer_data_t) out_correct_2[SPLIT_LAYER_COARSE];
+
+    // test images
+    static split_layer_data_t test_in[DIVIDE(SPLIT_LAYER_CHANNELS,SPLIT_LAYER_COARSE)*SPLIT_LAYER_ROWS*SPLIT_LAYER_COLS][SPLIT_LAYER_COARSE];
+    static split_layer_data_t test_out[DIVIDE(SPLIT_LAYER_CHANNELS,SPLIT_LAYER_COARSE)*SPLIT_LAYER_ROWS_OUT*SPLIT_LAYER_COLS_OUT][SPLIT_LAYER_COARSE];
+
+    // load input
+    load_data<
+        DIVIDE(SPLIT_LAYER_CHANNELS,SPLIT_LAYER_COARSE)*SPLIT_LAYER_ROWS*SPLIT_LAYER_COLS,
+        SPLIT_LAYER_COARSE,
+        split_layer_data_t
+    >(input_path,test_in);
+
+    // load output
+    load_data<
+        DIVIDE(SPLIT_LAYER_CHANNELS,SPLIT_LAYER_COARSE)*SPLIT_LAYER_ROWS_OUT*SPLIT_LAYER_COLS_OUT,
+        SPLIT_LAYER_COARSE,
+        split_layer_data_t
+    >(output_path,test_out);
+
+    // convert to streams
+    to_stream<
+        DIVIDE(SPLIT_LAYER_CHANNELS,SPLIT_LAYER_COARSE)*SPLIT_LAYER_ROWS*SPLIT_LAYER_COLS,
+        SPLIT_LAYER_COARSE,
+        split_layer_data_t
+    >(test_in,in);
+
+    to_stream<
+        DIVIDE(SPLIT_LAYER_CHANNELS,SPLIT_LAYER_COARSE)*SPLIT_LAYER_ROWS_OUT*SPLIT_LAYER_COLS_OUT,
+        SPLIT_LAYER_COARSE,
+        split_layer_data_t
+    >(test_out,out_correct_1);
+
+    to_stream<
+        DIVIDE(SPLIT_LAYER_CHANNELS,SPLIT_LAYER_COARSE)*SPLIT_LAYER_ROWS_OUT*SPLIT_LAYER_COLS_OUT,
+        SPLIT_LAYER_COARSE,
+        split_layer_data_t
+    >(test_out,out_correct_2);
+
+    split_layer_top(in,out_1,out_2,0);
+
+    for(int i=0;i<SPLIT_LAYER_COARSE;i++)
+    {
+        printf("TESTING OUTPUT %d: ",i);
+        err += checkStreamEqual<split_layer_data_t>(out_1[i],out_correct_1[i]);
+        err += checkStreamEqual<split_layer_data_t>(out_2[i],out_correct_2[i]);
+        printf("%s\n",(err==0) ? "passed" : "failed");
+    }
+
+    return err;
+}
\ No newline at end of file
diff --git a/test/layers/split/tb/split_layer_tb.hpp b/test/layers/split/tb/split_layer_tb.hpp
new file mode 100644
index 0000000..1ab7761
--- /dev/null
+++ b/test/layers/split/tb/split_layer_tb.hpp
@@ -0,0 +1,15 @@
+#ifndef SPLIT_LAYER_TB_HPP_
+#define SPLIT_LAYER_TB_HPP_
+
+#include "common.hpp"
+#include "split_layer.hpp"
+#include "split_layer_param.hpp"
+
+void split_layer_top(
+  stream_t(split_layer_data_t) in[SPLIT_LAYER_COARSE],
+  stream_t(split_layer_data_t) out_1[SPLIT_LAYER_COARSE],
+  stream_t(split_layer_data_t) out_2[SPLIT_LAYER_COARSE], 
+  int mode
+);
+
+#endif 
\ No newline at end of file
diff --git a/test/modules/avg_pool/config/config_0.json b/test/modules/avg_pool/config/config_0.json
new file mode 100644
index 0000000..90ebcf3
--- /dev/null
+++ b/test/modules/avg_pool/config/config_0.json
@@ -0,0 +1,10 @@
+{
+    "type": "POOL",
+    "rows": 1,
+    "cols": 1,
+    "channels": 4,
+    "pool_type": 1,
+    "kernel_size": [3,3],
+    "batch_size": 1,
+    "description": "module name: avg_pool"
+}
diff --git a/test/modules/avg_pool/gen_data.py b/test/modules/avg_pool/gen_data.py
new file mode 100644
index 0000000..c8ad456
--- /dev/null
+++ b/test/modules/avg_pool/gen_data.py
@@ -0,0 +1,62 @@
+import os
+import sys
+
+sys.path.append('..')
+
+from fpgaconvnet.models.modules.Pool import Pool
+from Data import Data
+
+class AvgPoolTB(Data):
+    def __init__(self):
+        Data.__init__(self,'avg_pool')
+
+    # update stimulus generation
+    def gen_stimulus(self):
+
+        # Init Module
+        if self.param['pool_type'] == 0:
+            pool_type = 'max'
+        if self.param['pool_type'] == 1:
+            pool_type = 'avg'
+        avg_pool = Pool(
+            self.param['rows'],
+            self.param['cols'],
+            self.param['channels'],
+            self.param['kernel_size'],
+            pool_type
+        )
+
+        # add parameters
+        self.param['data_width'] = avg_pool.data_width
+        self.param['data_int_width'] = avg_pool.data_width//2
+
+        # data in
+        data_in = self.gen_data([
+            self.param['rows'],
+            self.param['cols'],
+            self.param['channels'],
+            self.param['kernel_size'][0],
+            self.param['kernel_size'][1]
+        ])
+
+        # data out
+        data_out = avg_pool.functional_model(data_in)
+
+        # return data
+        data = {
+            'input'     : data_in.reshape(-1).tolist(),
+            'output'    : data_out.reshape(-1).tolist()
+        }
+
+        # resource and latency model
+        model = {
+            'latency'   : avg_pool.latency(),
+            'resources' : avg_pool.rsc()
+        }
+
+        return data, model
+
+if __name__ == '__main__':
+    pool_tb = AvgPoolTB()
+    pool_tb.main(sys.argv[1:])
+
diff --git a/test/modules/avg_pool/src/avg_pool.cpp b/test/modules/avg_pool/src/avg_pool.cpp
new file mode 100644
index 0000000..f01a08c
--- /dev/null
+++ b/test/modules/avg_pool/src/avg_pool.cpp
@@ -0,0 +1,22 @@
+#include "avg_pool_tb.hpp"
+#include "avg_pool.hpp"
+
+void avg_pool_top(
+    stream_t(avg_pool_t) in[AVG_POOL_KERNEL_SIZE_0][AVG_POOL_KERNEL_SIZE_1],
+    stream_t(avg_pool_t) &out
+)
+{
+
+#pragma HLS DATAFLOW
+
+    avg_pool<
+        AVG_POOL_BATCH_SIZE,
+        AVG_POOL_ROWS,
+        AVG_POOL_COLS,
+        AVG_POOL_CHANNELS,
+        AVG_POOL_KERNEL_SIZE_0,
+        AVG_POOL_KERNEL_SIZE_1,
+        avg_pool_t
+    >(in,out);
+
+}
\ No newline at end of file
diff --git a/test/modules/avg_pool/tb/avg_pool_tb.cpp b/test/modules/avg_pool/tb/avg_pool_tb.cpp
new file mode 100644
index 0000000..2889bae
--- /dev/null
+++ b/test/modules/avg_pool/tb/avg_pool_tb.cpp
@@ -0,0 +1,56 @@
+#include "common_tb.hpp"
+#include "avg_pool_tb.hpp"
+
+int main()
+{
+    int err = 0;
+
+    // file paths
+    std::string input_path  = std::string(DATA_DIR)+"/input.dat";
+    std::string output_path = std::string(DATA_DIR)+"/output.dat";
+
+    // in/out streams
+    stream_t(avg_pool_t) in[AVG_POOL_KERNEL_SIZE_0][AVG_POOL_KERNEL_SIZE_1];
+    stream_t(avg_pool_t) out;
+    stream_t(avg_pool_t) out_valid;
+
+    static avg_pool_t test_in[AVG_POOL_ROWS*AVG_POOL_COLS*AVG_POOL_CHANNELS][AVG_POOL_KERNEL_SIZE_0][AVG_POOL_KERNEL_SIZE_1];
+    static avg_pool_t test_out[AVG_POOL_ROWS*AVG_POOL_COLS*AVG_POOL_CHANNELS];
+
+    // load data_in
+    load_data<
+        AVG_POOL_ROWS*AVG_POOL_COLS*AVG_POOL_CHANNELS,
+        AVG_POOL_KERNEL_SIZE_0,
+        AVG_POOL_KERNEL_SIZE_1,
+        avg_pool_t
+    >(input_path,test_in);
+
+    // load data_out
+    load_data<
+        AVG_POOL_ROWS*AVG_POOL_COLS*AVG_POOL_CHANNELS,
+        avg_pool_t
+    >(output_path,test_out);
+
+    // convert input stream
+    to_stream<
+        AVG_POOL_ROWS*AVG_POOL_COLS*AVG_POOL_CHANNELS,
+        AVG_POOL_KERNEL_SIZE_0,
+        AVG_POOL_KERNEL_SIZE_1,
+        avg_pool_t
+    >(test_in,in);
+
+    // convert to out valid stream
+    to_stream<
+        AVG_POOL_ROWS*AVG_POOL_COLS*AVG_POOL_CHANNELS,
+        avg_pool_t
+    >(test_out,out_valid);
+
+    // run avg_pool
+    avg_pool_top(in,out);
+
+    // check the stream is correct
+    err += checkStreamEqual<avg_pool_t>(out,out_valid,false);
+
+    return err;
+
+}
\ No newline at end of file
diff --git a/test/modules/avg_pool/tb/avg_pool_tb.hpp b/test/modules/avg_pool/tb/avg_pool_tb.hpp
new file mode 100644
index 0000000..d86d089
--- /dev/null
+++ b/test/modules/avg_pool/tb/avg_pool_tb.hpp
@@ -0,0 +1,15 @@
+#ifndef AVG_POOL_TB_HPP_
+#define AVG_POOL_TB_HPP_
+
+#include "common.hpp"
+#include "avg_pool_param.hpp"
+
+// define the type based on the test configuration
+typedef ap_fixed<AVG_POOL_DATA_WIDTH, AVG_POOL_DATA_INT_WIDTH, AP_RND,AP_SAT> avg_pool_t;
+
+void avg_pool_top(
+    stream_t(avg_pool_t) in[AVG_POOL_KERNEL_SIZE_0][AVG_POOL_KERNEL_SIZE_1],
+    stream_t(avg_pool_t) &out
+);
+
+#endif
\ No newline at end of file
diff --git a/test/modules/elementwise_add/config/config_0.json b/test/modules/elementwise_add/config/config_0.json
new file mode 100644
index 0000000..fb22037
--- /dev/null
+++ b/test/modules/elementwise_add/config/config_0.json
@@ -0,0 +1,8 @@
+{
+    "type": "EltWiseADD",
+    "rows": 14,
+    "cols": 14,
+    "channels": 64,
+    "batch_size": 1,
+    "description": "module name: elementwise_add, layer name: , net name: "
+}
diff --git a/test/modules/elementwise_add/config/config_1.json b/test/modules/elementwise_add/config/config_1.json
new file mode 100644
index 0000000..1f1ec5b
--- /dev/null
+++ b/test/modules/elementwise_add/config/config_1.json
@@ -0,0 +1,12 @@
+{
+    "type": "EltWiseADD",
+    "rows": 7,
+    "cols": 7,
+    "channels": 64,
+    "rows_out": 28,
+    "cols_out": 28,
+    "channels_out": 64,
+    "batch_size": 1,
+    "wordlength":16, 
+    "description": "module name: elementwise_add, layer name: , net name: "
+}
\ No newline at end of file
diff --git a/test/modules/elementwise_add/config/config_2.json b/test/modules/elementwise_add/config/config_2.json
new file mode 100644
index 0000000..e6af8c7
--- /dev/null
+++ b/test/modules/elementwise_add/config/config_2.json
@@ -0,0 +1,11 @@
+{
+    "type": "EltWiseADD",
+    "rows": 14,
+    "cols": 14,
+    "channels": 64,
+    "rows_out": 14,
+    "cols_out": 14,
+    "channels_out": 64,
+    "batch_size": 1,
+    "description": "module name: elementwise_add, layer name: , net name: "
+}
\ No newline at end of file
diff --git a/test/modules/elementwise_add/config/config_3.json b/test/modules/elementwise_add/config/config_3.json
new file mode 100644
index 0000000..2f0472f
--- /dev/null
+++ b/test/modules/elementwise_add/config/config_3.json
@@ -0,0 +1,11 @@
+{
+    "type": "EltWiseADD",
+    "rows": 28,
+    "cols": 28,
+    "channels": 8,
+    "rows_out": 28,
+    "cols_out": 28,
+    "channels_out": 8,
+    "batch_size": 1,
+    "description": "module name: elementwise_add, layer name: , net name: "
+}
\ No newline at end of file
diff --git a/test/modules/elementwise_add/config/config_4.json b/test/modules/elementwise_add/config/config_4.json
new file mode 100644
index 0000000..c3f6985
--- /dev/null
+++ b/test/modules/elementwise_add/config/config_4.json
@@ -0,0 +1,11 @@
+{
+    "type": "EltWiseADD",
+    "rows": 56,
+    "cols": 56,
+    "channels": 1,
+    "rows_out": 56,
+    "cols_out": 56,
+    "channels_out": 1,
+    "batch_size": 1,
+    "description": "module name: elementwise_add, layer name: , net name: "
+}
\ No newline at end of file
diff --git a/test/modules/elementwise_add/config/config_5.json b/test/modules/elementwise_add/config/config_5.json
new file mode 100644
index 0000000..cb47e52
--- /dev/null
+++ b/test/modules/elementwise_add/config/config_5.json
@@ -0,0 +1,11 @@
+{
+    "type": "EltWiseADD",
+    "rows": 56,
+    "cols": 56,
+    "channels": 16,
+    "rows_out": 56,
+    "cols_out": 56,
+    "channels_out": 16,
+    "batch_size": 1,
+    "description": "module name: elementwise_add, layer name: , net name: "
+}
\ No newline at end of file
diff --git a/test/modules/elementwise_add/config/config_6.json b/test/modules/elementwise_add/config/config_6.json
new file mode 100644
index 0000000..66d496e
--- /dev/null
+++ b/test/modules/elementwise_add/config/config_6.json
@@ -0,0 +1,11 @@
+{
+    "type": "EltWiseADD",
+    "rows": 14,
+    "cols": 14,
+    "channels": 8,
+    "rows_out": 14,
+    "cols_out": 14,
+    "channels_out": 8,
+    "batch_size": 1,
+    "description": "module name: elementwise_add, layer name: , net name: "
+}
\ No newline at end of file
diff --git a/test/modules/elementwise_add/config/config_7.json b/test/modules/elementwise_add/config/config_7.json
new file mode 100644
index 0000000..0b96cd6
--- /dev/null
+++ b/test/modules/elementwise_add/config/config_7.json
@@ -0,0 +1,11 @@
+{
+    "type": "EltWiseADD",
+    "rows": 28,
+    "cols": 28,
+    "channels": 1,
+    "rows_out": 28,
+    "cols_out": 28,
+    "channels_out": 1,
+    "batch_size": 1,
+    "description": "module name: elementwise_add, layer name: , net name: "
+}
\ No newline at end of file
diff --git a/test/modules/elementwise_add/config/config_8.json b/test/modules/elementwise_add/config/config_8.json
new file mode 100644
index 0000000..1b18908
--- /dev/null
+++ b/test/modules/elementwise_add/config/config_8.json
@@ -0,0 +1,11 @@
+{
+    "type": "EltWiseADD",
+    "rows": 28,
+    "cols": 28,
+    "channels": 512,
+    "rows_out": 28,
+    "cols_out": 28,
+    "channels_out": 512,
+    "batch_size": 1,
+    "description": "module name: elementwise_add, layer name: , net name: "
+}
\ No newline at end of file
diff --git a/test/modules/elementwise_add/config/config_9.json b/test/modules/elementwise_add/config/config_9.json
new file mode 100644
index 0000000..3648c3c
--- /dev/null
+++ b/test/modules/elementwise_add/config/config_9.json
@@ -0,0 +1,11 @@
+{
+    "type": "EltWiseADD",
+    "rows": 28,
+    "cols": 28,
+    "channels": 32,
+    "rows_out": 28,
+    "cols_out": 28,
+    "channels_out": 32,
+    "batch_size": 1,
+    "description": "module name: elementwise_add, layer name: , net name: "
+}
\ No newline at end of file
diff --git a/test/modules/elementwise_add/gen_data.py b/test/modules/elementwise_add/gen_data.py
new file mode 100644
index 0000000..813ee9b
--- /dev/null
+++ b/test/modules/elementwise_add/gen_data.py
@@ -0,0 +1,73 @@
+import os
+import sys
+import numpy as np
+
+sys.path.append('..')
+
+from fpgaconvnet.models.modules.EltWise import EltWise
+from Data import Data
+
+class EltWiseADDTB(Data):
+    def __init__(self):
+        Data.__init__(self,'elementwise_add')
+
+    # update stimulus generation
+    def gen_stimulus(self):
+
+        # Init Module
+        elementwise_add = EltWise(
+            self.param['rows'],
+            self.param['cols'],
+            self.param['channels'], 
+            2, 
+            'add'
+        )
+        
+        elementwise_add.eltwise_type = 'add'
+
+        # add parameters
+        self.param['data_width'] = elementwise_add.data_width
+        self.param['data_int_width'] = elementwise_add.data_width//2
+
+        # data in
+        data_in = np.ndarray(shape=(
+            2, 
+            self.param['rows'],
+            self.param['cols'],
+            self.param['channels']
+        ), dtype=float)
+        
+        data_in[0] = self.gen_data([
+            self.param['rows'],
+            self.param['cols'],
+            self.param['channels']
+        ])
+        
+        data_in[1] = self.gen_data([
+            self.param['rows'],
+            self.param['cols'],
+            self.param['channels']
+        ])
+
+        # data out
+        data_out = elementwise_add.functional_model(data_in)
+
+        # return data
+        data = {
+            'input_1'     : data_in[0].reshape(-1).tolist(),
+            'input_2'     : data_in[1].reshape(-1).tolist(),
+            'output'    : data_out.reshape(-1).tolist()
+        }
+
+        # resource and latency model
+        model = {
+            'latency'   : elementwise_add.latency(),
+            'resources' : elementwise_add.rsc()
+        }
+
+        return data, model
+
+if __name__ == '__main__':
+    elementwise_add_tb = EltWiseADDTB()
+    elementwise_add_tb.main(sys.argv[1:])
+
diff --git a/test/modules/elementwise_add/src/elementwise_add.cpp b/test/modules/elementwise_add/src/elementwise_add.cpp
new file mode 100644
index 0000000..fbbb40a
--- /dev/null
+++ b/test/modules/elementwise_add/src/elementwise_add.cpp
@@ -0,0 +1,22 @@
+#include "elementwise_add_tb.hpp"
+#include "elementwise_add.hpp"
+
+void elementwise_add_top(
+    stream_t(elementwise_add_t) &in1,
+    stream_t(elementwise_add_t) &in2,
+    stream_t(elementwise_add_t) &out
+)
+{
+
+#pragma HLS DATAFLOW
+
+    // DUT
+    elementwise_add<
+        ELEMENTWISE_ADD_BATCH_SIZE,
+        ELEMENTWISE_ADD_ROWS,
+        ELEMENTWISE_ADD_COLS,
+        ELEMENTWISE_ADD_CHANNELS,
+        elementwise_add_t
+    >(in1,in2,out);
+
+}
diff --git a/test/modules/elementwise_add/tb/elementwise_add_tb.cpp b/test/modules/elementwise_add/tb/elementwise_add_tb.cpp
new file mode 100644
index 0000000..b08fe19
--- /dev/null
+++ b/test/modules/elementwise_add/tb/elementwise_add_tb.cpp
@@ -0,0 +1,63 @@
+#include "common_tb.hpp"
+#include "elementwise_add_tb.hpp"
+
+int main()
+{
+    int err = 0;
+    std::string input_path_1  = std::string(DATA_DIR)+"/input_1.dat";
+    std::string input_path_2  = std::string(DATA_DIR)+"/input_2.dat";
+    std::string output_path = std::string(DATA_DIR)+"/output.dat";
+
+    // in/out streams
+    stream_t(elementwise_add_t) in1;
+    stream_t(elementwise_add_t) in2;
+    stream_t(elementwise_add_t) out;
+    stream_t(elementwise_add_t) out_valid;
+
+    // test inputs data
+    static elementwise_add_t test_in1[ELEMENTWISE_ADD_ROWS*ELEMENTWISE_ADD_COLS*ELEMENTWISE_ADD_CHANNELS];
+    static elementwise_add_t test_in2[ELEMENTWISE_ADD_ROWS*ELEMENTWISE_ADD_COLS*ELEMENTWISE_ADD_CHANNELS];
+    static elementwise_add_t test_out[ELEMENTWISE_ADD_ROWS*ELEMENTWISE_ADD_COLS*ELEMENTWISE_ADD_CHANNELS];
+
+    // load data_in
+    load_data<
+        ELEMENTWISE_ADD_ROWS*ELEMENTWISE_ADD_COLS*ELEMENTWISE_ADD_CHANNELS,
+        elementwise_add_t
+    >(input_path_1,test_in1);
+
+    load_data<
+        ELEMENTWISE_ADD_ROWS*ELEMENTWISE_ADD_COLS*ELEMENTWISE_ADD_CHANNELS,
+        elementwise_add_t
+    >(input_path_2,test_in2);
+
+    // load data_out
+    load_data<
+        ELEMENTWISE_ADD_ROWS*ELEMENTWISE_ADD_COLS*ELEMENTWISE_ADD_CHANNELS,
+        elementwise_add_t
+    >(output_path,test_out);
+
+    // convert input stream
+    to_stream<
+        ELEMENTWISE_ADD_ROWS*ELEMENTWISE_ADD_COLS*ELEMENTWISE_ADD_CHANNELS,
+        elementwise_add_t
+    >(test_in1,in1);
+
+    to_stream<
+        ELEMENTWISE_ADD_ROWS*ELEMENTWISE_ADD_COLS*ELEMENTWISE_ADD_CHANNELS,
+        elementwise_add_t
+    >(test_in2,in2);
+
+    // convert to out valid stream
+    to_stream<
+        ELEMENTWISE_ADD_ROWS*ELEMENTWISE_ADD_COLS*ELEMENTWISE_ADD_CHANNELS,
+        elementwise_add_t
+    >(test_out,out_valid);
+
+    // run elementwise_add
+    elementwise_add_top(in1,in2,out);
+
+    err += checkStreamEqual<elementwise_add_t>(out,out_valid,false);
+
+    return err;
+
+}
diff --git a/test/modules/elementwise_add/tb/elementwise_add_tb.hpp b/test/modules/elementwise_add/tb/elementwise_add_tb.hpp
new file mode 100644
index 0000000..89df297
--- /dev/null
+++ b/test/modules/elementwise_add/tb/elementwise_add_tb.hpp
@@ -0,0 +1,16 @@
+#ifndef ELEMENTWISE_ADD_TB_HPP_
+#define ELEMENTWISE_ADD_TB_HPP_
+
+#include "common.hpp"
+#include "elementwise_add_param.hpp"
+
+// define the type based on the test configuration
+typedef ap_fixed<ELEMENTWISE_ADD_DATA_WIDTH,ELEMENTWISE_ADD_DATA_INT_WIDTH,AP_RND,AP_SAT> elementwise_add_t;
+
+void elementwise_add_top(
+    stream_t(elementwise_add_t) &in1,
+    stream_t(elementwise_add_t) &in2,
+    stream_t(elementwise_add_t) &out
+);
+
+#endif
\ No newline at end of file
diff --git a/test/modules/elementwise_mul/config/config_0.json b/test/modules/elementwise_mul/config/config_0.json
new file mode 100644
index 0000000..a805e1b
--- /dev/null
+++ b/test/modules/elementwise_mul/config/config_0.json
@@ -0,0 +1,8 @@
+{
+    "type": "EltWiseMUL",
+    "rows": 14,
+    "cols": 14,
+    "channels": 64,
+    "batch_size": 1,
+    "description": "module name: elementwise_add, layer name: , net name: "
+}
diff --git a/test/modules/elementwise_mul/config/config_1.json b/test/modules/elementwise_mul/config/config_1.json
new file mode 100644
index 0000000..bc2091d
--- /dev/null
+++ b/test/modules/elementwise_mul/config/config_1.json
@@ -0,0 +1,12 @@
+{
+    "type": "EltWiseMUL",
+    "rows": 7,
+    "cols": 7,
+    "channels": 64,
+    "rows_out": 28,
+    "cols_out": 28,
+    "channels_out": 64,
+    "batch_size": 1,
+    "wordlength":16, 
+    "description": "module name: elementwise_add, layer name: , net name: "
+}
\ No newline at end of file
diff --git a/test/modules/elementwise_mul/config/config_2.json b/test/modules/elementwise_mul/config/config_2.json
new file mode 100644
index 0000000..c7ce91d
--- /dev/null
+++ b/test/modules/elementwise_mul/config/config_2.json
@@ -0,0 +1,11 @@
+{
+    "type": "EltWiseMUL",
+    "rows": 14,
+    "cols": 14,
+    "channels": 64,
+    "rows_out": 14,
+    "cols_out": 14,
+    "channels_out": 64,
+    "batch_size": 1,
+    "description": "module name: elementwise_add, layer name: , net name: "
+}
\ No newline at end of file
diff --git a/test/modules/elementwise_mul/config/config_3.json b/test/modules/elementwise_mul/config/config_3.json
new file mode 100644
index 0000000..9b1c328
--- /dev/null
+++ b/test/modules/elementwise_mul/config/config_3.json
@@ -0,0 +1,11 @@
+{
+    "type": "EltWiseMUL",
+    "rows": 28,
+    "cols": 28,
+    "channels": 8,
+    "rows_out": 28,
+    "cols_out": 28,
+    "channels_out": 8,
+    "batch_size": 1,
+    "description": "module name: elementwise_add, layer name: , net name: "
+}
\ No newline at end of file
diff --git a/test/modules/elementwise_mul/config/config_4.json b/test/modules/elementwise_mul/config/config_4.json
new file mode 100644
index 0000000..6946ca1
--- /dev/null
+++ b/test/modules/elementwise_mul/config/config_4.json
@@ -0,0 +1,11 @@
+{
+    "type": "EltWiseMUL",
+    "rows": 56,
+    "cols": 56,
+    "channels": 1,
+    "rows_out": 56,
+    "cols_out": 56,
+    "channels_out": 1,
+    "batch_size": 1,
+    "description": "module name: elementwise_add, layer name: , net name: "
+}
\ No newline at end of file
diff --git a/test/modules/elementwise_mul/config/config_5.json b/test/modules/elementwise_mul/config/config_5.json
new file mode 100644
index 0000000..645d45f
--- /dev/null
+++ b/test/modules/elementwise_mul/config/config_5.json
@@ -0,0 +1,11 @@
+{
+    "type": "EltWiseMUL",
+    "rows": 56,
+    "cols": 56,
+    "channels": 16,
+    "rows_out": 56,
+    "cols_out": 56,
+    "channels_out": 16,
+    "batch_size": 1,
+    "description": "module name: elementwise_add, layer name: , net name: "
+}
\ No newline at end of file
diff --git a/test/modules/elementwise_mul/config/config_6.json b/test/modules/elementwise_mul/config/config_6.json
new file mode 100644
index 0000000..6d1f100
--- /dev/null
+++ b/test/modules/elementwise_mul/config/config_6.json
@@ -0,0 +1,11 @@
+{
+    "type": "EltWiseMUL",
+    "rows": 14,
+    "cols": 14,
+    "channels": 8,
+    "rows_out": 14,
+    "cols_out": 14,
+    "channels_out": 8,
+    "batch_size": 1,
+    "description": "module name: elementwise_add, layer name: , net name: "
+}
\ No newline at end of file
diff --git a/test/modules/elementwise_mul/config/config_7.json b/test/modules/elementwise_mul/config/config_7.json
new file mode 100644
index 0000000..70d31ad
--- /dev/null
+++ b/test/modules/elementwise_mul/config/config_7.json
@@ -0,0 +1,11 @@
+{
+    "type": "EltWiseMUL",
+    "rows": 28,
+    "cols": 28,
+    "channels": 1,
+    "rows_out": 28,
+    "cols_out": 28,
+    "channels_out": 1,
+    "batch_size": 1,
+    "description": "module name: elementwise_add, layer name: , net name: "
+}
\ No newline at end of file
diff --git a/test/modules/elementwise_mul/config/config_8.json b/test/modules/elementwise_mul/config/config_8.json
new file mode 100644
index 0000000..b648ab1
--- /dev/null
+++ b/test/modules/elementwise_mul/config/config_8.json
@@ -0,0 +1,11 @@
+{
+    "type": "EltWiseMUL",
+    "rows": 28,
+    "cols": 28,
+    "channels": 512,
+    "rows_out": 28,
+    "cols_out": 28,
+    "channels_out": 512,
+    "batch_size": 1,
+    "description": "module name: elementwise_add, layer name: , net name: "
+}
\ No newline at end of file
diff --git a/test/modules/elementwise_mul/config/config_9.json b/test/modules/elementwise_mul/config/config_9.json
new file mode 100644
index 0000000..80b34a0
--- /dev/null
+++ b/test/modules/elementwise_mul/config/config_9.json
@@ -0,0 +1,11 @@
+{
+    "type": "EltWiseMUL",
+    "rows": 28,
+    "cols": 28,
+    "channels": 32,
+    "rows_out": 28,
+    "cols_out": 28,
+    "channels_out": 32,
+    "batch_size": 1,
+    "description": "module name: elementwise_add, layer name: , net name: "
+}
\ No newline at end of file
diff --git a/test/modules/elementwise_mul/gen_data.py b/test/modules/elementwise_mul/gen_data.py
new file mode 100644
index 0000000..6248261
--- /dev/null
+++ b/test/modules/elementwise_mul/gen_data.py
@@ -0,0 +1,73 @@
+import os
+import sys
+import numpy as np
+
+sys.path.append('..')
+
+from fpgaconvnet.models.modules.EltWise import EltWise
+from Data import Data
+
+class EltWiseMULTB(Data):
+    def __init__(self):
+        Data.__init__(self,'elementwise_mul')
+
+    # update stimulus generation
+    def gen_stimulus(self):
+
+        # Init Module
+        elementwise_mul = EltWise(
+            self.param['rows'],
+            self.param['cols'],
+            self.param['channels'], 
+            2, 
+            'mul'
+        )
+        
+        elementwise_mul.eltwise_type = 'mul'
+
+        # add parameters
+        self.param['data_width'] = elementwise_mul.data_width
+        self.param['data_int_width'] = elementwise_mul.data_width//2
+
+        # data in
+        data_in = np.ndarray(shape=(
+            2, 
+            self.param['rows'],
+            self.param['cols'],
+            self.param['channels']
+        ), dtype=float)
+        
+        data_in[0] = self.gen_data([
+            self.param['rows'],
+            self.param['cols'],
+            self.param['channels']
+        ])
+        
+        data_in[1] = self.gen_data([
+            self.param['rows'],
+            self.param['cols'],
+            self.param['channels']
+        ])
+
+        # data out
+        data_out = elementwise_mul.functional_model(data_in)
+
+        # return data
+        data = {
+            'input_1'     : data_in[0].reshape(-1).tolist(),
+            'input_2'     : data_in[1].reshape(-1).tolist(),
+            'output'    : data_out.reshape(-1).tolist()
+        }
+
+        # resource and latency model
+        model = {
+            'latency'   : elementwise_mul.latency(),
+            'resources' : elementwise_mul.rsc()
+        }
+
+        return data, model
+
+if __name__ == '__main__':
+    elementwise_mul_tb = EltWiseMULTB()
+    elementwise_mul_tb.main(sys.argv[1:])
+
diff --git a/test/modules/elementwise_mul/src/elementwise_mul.cpp b/test/modules/elementwise_mul/src/elementwise_mul.cpp
new file mode 100644
index 0000000..192944a
--- /dev/null
+++ b/test/modules/elementwise_mul/src/elementwise_mul.cpp
@@ -0,0 +1,22 @@
+#include "elementwise_mul_tb.hpp"
+#include "elementwise_mul.hpp"
+
+void elementwise_mul_top(
+    stream_t(elementwise_mul_t) &in1,
+    stream_t(elementwise_mul_t) &in2,
+    stream_t(elementwise_mul_t) &out
+)
+{
+
+#pragma HLS DATAFLOW
+
+    // DUT
+    elementwise_mul<
+        ELEMENTWISE_MUL_BATCH_SIZE,
+        ELEMENTWISE_MUL_ROWS,
+        ELEMENTWISE_MUL_COLS,
+        ELEMENTWISE_MUL_CHANNELS,
+        elementwise_mul_t
+    >(in1,in2,out);
+
+}
\ No newline at end of file
diff --git a/test/modules/elementwise_mul/tb/elementwise_mul_tb.cpp b/test/modules/elementwise_mul/tb/elementwise_mul_tb.cpp
new file mode 100644
index 0000000..31d2c05
--- /dev/null
+++ b/test/modules/elementwise_mul/tb/elementwise_mul_tb.cpp
@@ -0,0 +1,63 @@
+#include "common_tb.hpp"
+#include "elementwise_mul_tb.hpp"
+
+int main()
+{
+    int err = 0; 
+    std::string input_path_1  = std::string(DATA_DIR)+"/input_1.dat";
+    std::string input_path_2  = std::string(DATA_DIR)+"/input_2.dat";
+    std::string output_path = std::string(DATA_DIR)+"/output.dat";
+
+    // in/out streams
+    stream_t(elementwise_mul_t) in1;
+    stream_t(elementwise_mul_t) in2;
+    stream_t(elementwise_mul_t) out;
+    stream_t(elementwise_mul_t) out_valid;
+
+    // test inputs data
+    static elementwise_mul_t test_in1[ELEMENTWISE_MUL_ROWS*ELEMENTWISE_MUL_COLS*ELEMENTWISE_MUL_CHANNELS];
+    static elementwise_mul_t test_in2[ELEMENTWISE_MUL_ROWS*ELEMENTWISE_MUL_COLS*ELEMENTWISE_MUL_CHANNELS];
+    static elementwise_mul_t test_out[ELEMENTWISE_MUL_ROWS*ELEMENTWISE_MUL_COLS*ELEMENTWISE_MUL_CHANNELS];
+
+    // load data_in
+    load_data<
+        ELEMENTWISE_MUL_ROWS*ELEMENTWISE_MUL_COLS*ELEMENTWISE_MUL_CHANNELS,
+        elementwise_mul_t
+    >(input_path_1,test_in1);
+
+    load_data<
+        ELEMENTWISE_MUL_ROWS*ELEMENTWISE_MUL_COLS*ELEMENTWISE_MUL_CHANNELS,
+        elementwise_mul_t
+    >(input_path_2,test_in2);
+
+    // load data_out
+    load_data<
+        ELEMENTWISE_MUL_ROWS*ELEMENTWISE_MUL_COLS*ELEMENTWISE_MUL_CHANNELS,
+        elementwise_mul_t
+    >(output_path,test_out);
+
+    // convert input stream
+    to_stream<
+        ELEMENTWISE_MUL_ROWS*ELEMENTWISE_MUL_COLS*ELEMENTWISE_MUL_CHANNELS,
+        elementwise_mul_t
+    >(test_in1,in1);
+
+    to_stream<
+        ELEMENTWISE_MUL_ROWS*ELEMENTWISE_MUL_COLS*ELEMENTWISE_MUL_CHANNELS,
+        elementwise_mul_t
+    >(test_in2,in2);
+
+    // convert to out valid stream
+    to_stream<
+        ELEMENTWISE_MUL_ROWS*ELEMENTWISE_MUL_COLS*ELEMENTWISE_MUL_CHANNELS,
+        elementwise_mul_t
+    >(test_out,out_valid);
+
+    // run elementwise_mul
+    elementwise_mul_top(in1,in2,out);
+
+    err += checkStreamEqual<elementwise_mul_t>(out,out_valid,false);
+
+    return err;
+
+}
\ No newline at end of file
diff --git a/test/modules/elementwise_mul/tb/elementwise_mul_tb.hpp b/test/modules/elementwise_mul/tb/elementwise_mul_tb.hpp
new file mode 100644
index 0000000..d0da7db
--- /dev/null
+++ b/test/modules/elementwise_mul/tb/elementwise_mul_tb.hpp
@@ -0,0 +1,16 @@
+#ifndef ELEMENTWISE_MUL_TB_HPP_
+#define ELEMENTWISE_MUL_TB_HPP_
+
+#include "common.hpp"
+#include "elementwise_mul_param.hpp"
+
+// define the type based on the test configuration
+typedef ap_fixed<ELEMENTWISE_MUL_DATA_WIDTH,ELEMENTWISE_MUL_DATA_INT_WIDTH,AP_RND,AP_SAT> elementwise_mul_t;
+
+void elementwise_mul_top(
+    stream_t(elementwise_mul_t) &in1,
+    stream_t(elementwise_mul_t) &in2,
+    stream_t(elementwise_mul_t) &out
+);
+
+#endif
\ No newline at end of file
diff --git a/test/modules/global_pool/config/config_0.json b/test/modules/global_pool/config/config_0.json
new file mode 100644
index 0000000..a0240cd
--- /dev/null
+++ b/test/modules/global_pool/config/config_0.json
@@ -0,0 +1,8 @@
+{
+    "type": "GLOBAL_POOL",
+    "rows": 40,
+    "cols": 40,
+    "channels": 4,
+    "batch_size": 1,
+    "description": "module name: global_pool"
+}
diff --git a/test/modules/global_pool/gen_data.py b/test/modules/global_pool/gen_data.py
new file mode 100644
index 0000000..97c8503
--- /dev/null
+++ b/test/modules/global_pool/gen_data.py
@@ -0,0 +1,51 @@
+import sys
+
+sys.path.append('..')
+
+from fpgaconvnet.models.modules.GlobalPool import GlobalPool
+from Data import Data
+
+class GlobalPoolTB(Data):
+    def __init__(self): 
+        Data.__init__(self,'global_pool')
+        
+    # update stimulus generation
+    def gen_stimulus(self): 
+        
+        # Init Module
+        global_pool = GlobalPool(
+            self.param['rows'],
+            self.param['cols'],
+            self.param['channels']
+        )
+        
+        # add parameters
+        self.param['data_width'] = global_pool.data_width
+        self.param['data_int_width'] = global_pool.data_width//2
+        
+        # data in
+        data_in = self.gen_data([
+            self.param['rows'],
+            self.param['cols'],
+            self.param['channels']
+        ])
+        
+        data_out = global_pool.functional_model(data_in)
+        
+        # return data
+        data = {
+            'input'     : data_in.reshape(-1).tolist(),
+            'output'    : data_out.reshape(-1).tolist()
+        }
+        
+        # resource and latency model
+        model = {
+            'latency'   : global_pool.latency(),
+            'resources' : global_pool.rsc()
+        }
+        
+        return data, model
+    
+if __name__ == '__main__':
+    global_pool_tb = GlobalPoolTB()
+    global_pool_tb.main(sys.argv[1:])
\ No newline at end of file
diff --git a/test/modules/global_pool/src/global_pool.cpp b/test/modules/global_pool/src/global_pool.cpp
new file mode 100644
index 0000000..bdc6153
--- /dev/null
+++ b/test/modules/global_pool/src/global_pool.cpp
@@ -0,0 +1,19 @@
+#include "global_pool_tb.hpp"
+#include "global_pool.hpp"
+
+void global_pool_top(
+    stream_t(global_pool_t) &in, 
+    stream_t(global_pool_t) &out
+)
+{
+#pragma HLS DATAFLOW
+
+    global_pool<
+        GLOBAL_POOL_BATCH_SIZE,
+        GLOBAL_POOL_ROWS,
+        GLOBAL_POOL_COLS,
+        GLOBAL_POOL_CHANNELS,
+        global_pool_t
+    >(in,out);
+
+}
\ No newline at end of file
diff --git a/test/modules/global_pool/tb/global_pool_tb.cpp b/test/modules/global_pool/tb/global_pool_tb.cpp
new file mode 100644
index 0000000..d5913fb
--- /dev/null
+++ b/test/modules/global_pool/tb/global_pool_tb.cpp
@@ -0,0 +1,51 @@
+#include "common_tb.hpp"
+#include "global_pool_tb.hpp"
+
+int main()
+{
+    int err = 0;
+
+    // file paths
+    std::string input_path  = std::string(DATA_DIR)+"/input.dat";
+    std::string output_path = std::string(DATA_DIR)+"/output.dat";
+
+    // in/out streams
+    stream_t(global_pool_t) in;
+    stream_t(global_pool_t) out;
+    stream_t(global_pool_t) out_valid;
+
+    static global_pool_t test_in[GLOBAL_POOL_ROWS*GLOBAL_POOL_COLS*GLOBAL_POOL_CHANNELS];
+    static global_pool_t test_out[GLOBAL_POOL_CHANNELS];
+
+    // load data_in
+    load_data<
+        GLOBAL_POOL_ROWS*GLOBAL_POOL_COLS*GLOBAL_POOL_CHANNELS,
+        global_pool_t
+    >(input_path,test_in);
+
+    // load data_out
+    load_data<
+        GLOBAL_POOL_CHANNELS,
+        global_pool_t
+    >(output_path,test_out);
+
+    // convert input stream
+    to_stream<
+        GLOBAL_POOL_ROWS*GLOBAL_POOL_COLS*GLOBAL_POOL_CHANNELS,
+        global_pool_t
+    >(test_in,in);
+
+    // convert to out valid stream
+    to_stream<
+        GLOBAL_POOL_CHANNELS,
+        global_pool_t
+    >(test_out,out_valid);
+
+    // run global_pool
+    global_pool_top(in,out);
+
+    // check the stream is correct
+    err += checkStreamEqual<global_pool_t>(out,out_valid,false);
+
+    return err;
+}
\ No newline at end of file
diff --git a/test/modules/global_pool/tb/global_pool_tb.hpp b/test/modules/global_pool/tb/global_pool_tb.hpp
new file mode 100644
index 0000000..1b2b31d
--- /dev/null
+++ b/test/modules/global_pool/tb/global_pool_tb.hpp
@@ -0,0 +1,15 @@
+#ifndef GLOBAL_POOL_TB_HPP_
+#define GLOBAL_POOL_TB_HPP_
+
+#include "common.hpp"
+#include "global_pool_param.hpp"
+
+// define the type based on the test configuration
+typedef ap_fixed<GLOBAL_POOL_DATA_WIDTH, GLOBAL_POOL_DATA_INT_WIDTH, AP_RND,AP_SAT> global_pool_t;
+
+void global_pool_top(
+    stream_t(global_pool_t) &in, 
+    stream_t(global_pool_t) &out
+);
+
+#endif 
\ No newline at end of file
diff --git a/test/modules/pool/tb/pool_tb.cpp b/test/modules/pool/tb/pool_tb.cpp
index 1cb22f8..9dc98a7 100644
--- a/test/modules/pool/tb/pool_tb.cpp
+++ b/test/modules/pool/tb/pool_tb.cpp
@@ -51,7 +51,7 @@ int main()
     pool_top(in,out);
 
     // check the stream is correct
-    checkStreamEqual<pool_t>(out,out_valid);
+    err += checkStreamEqual<pool_t>(out,out_valid,false);
 
     return err;
 }
diff --git a/test/networks/single_layer/run_test.py b/test/networks/single_layer/run_test.py
index 5f93493..19abc78 100644
--- a/test/networks/single_layer/run_test.py
+++ b/test/networks/single_layer/run_test.py
@@ -4,7 +4,9 @@
 
 from fpgaconvnet.hls.generate.network import GenerateNetwork
 
-import fpgaconvnet.tools.onnx_helper as onnx_helper
+from fpgaconvnet.parser import Parser
+import fpgaconvnet.parser.onnx.helper as onnx_helper
+
 
 # create instance of the network
 net = GenerateNetwork("single_layer", "single_layer.json", "single_layer.onnx")
diff --git a/test/networks/single_layer/single_layer.json b/test/networks/single_layer/single_layer.json
index 01ead1c..eea42ce 100644
--- a/test/networks/single_layer/single_layer.json
+++ b/test/networks/single_layer/single_layer.json
@@ -3,29 +3,38 @@
     {
       "id": 0,
       "ports": 1,
-      "batch_size": 1,
-      "input_node": "data",
-      "output_node": "pool1",
+      "batch_size": 1024,
+      "input_nodes": [
+        "data"
+      ],
+      "output_nodes": [
+        "pool1"
+      ],
       "weights_reloading_factor": 1,
-      "weights_reloading_layer": "conv1",
+      "weights_reloading_layer": "Conv_0",
       "layers": [
         {
-          "name": "conv1",
+          "name": "Conv_0",
+          "onnx_node": "Conv_0",
+          "type": "CONVOLUTION",
+          "op_type": "dense",
           "streams_in": [
             {
               "name": "in",
-              "coarse": 1
+              "coarse": 1,
+              "buffer_depth": 2,
+              "node": "Conv_0"
             }
           ],
           "streams_out": [
             {
-              "name": "conv1_conv1_squeeze_pool1",
-              "coarse": 5
+              "name": "Conv_0_MaxPool_1",
+              "coarse": 20,
+              "node": "MaxPool_1"
             }
           ],
           "parameters": {
-            "buffer_depth": 0,
-            "batch_size": 1,
+            "batch_size": 1024,
             "rows_in": 28,
             "cols_in": 28,
             "channels_in": 1,
@@ -33,85 +42,85 @@
             "cols_out": 24,
             "channels_out": 20,
             "coarse_in": 1,
-            "coarse_out": 5,
+            "coarse_out": 20,
             "coarse_group": 1,
             "groups": 1,
             "fine": 25,
             "filters": 20,
             "pad_top": 0,
             "pad_right": 0,
-            "pad_left": 0,
             "pad_bottom": 0,
-            "has_bias": 1,
+            "pad_left": 0,
+            "kernel_rows": 5,
+            "kernel_cols": 5,
             "kernel_size": [
               5,
               5
             ],
+            "stride_rows": 1,
+            "stride_cols": 1,
             "stride": [
               1,
               1
             ],
-            "data_width": 16,
-            "weight_width": 16,
-            "biases_width": 16,
-            "acc_width": 32,
-            "input_width": 16,
-            "output_width": 16
+            "mem_bw_in": 100.0,
+            "mem_bw_out": 100.0,
+            "data_t": {},
+            "weight_t": {
+              "width": 16,
+              "binary_point": 0
+            },
+            "acc_t": {
+              "width": 32,
+              "binary_point": 8
+            },
+            "input_t": {
+              "width": 16,
+              "binary_point": 8
+            },
+            "output_t": {
+              "width": 16,
+              "binary_point": 8
+            },
+            "has_bias": 1,
+            "use_uram": false,
+            "block_floating_point": false,
+            "on_chip_addr_range": 1,
+            "off_chip_buffer_size": 0,
+            "off_chip_interval": -1,
+            "stream_weights": 0,
+            "stream_inputs": [
+              false
+            ],
+            "stream_outputs": [
+              false
+            ]
           },
-          "node_in": "conv1",
-          "node_out": "conv1_squeeze_pool1",
-          "type": "CONVOLUTION",
           "weights_path": "convolution_W",
           "bias_path": "convolution_B"
         },
         {
-          "name": "conv1_squeeze_pool1",
+          "name": "MaxPool_1",
+          "onnx_node": "MaxPool_1",
+          "type": "POOLING",
+          "op_type": "max",
           "streams_in": [
             {
-              "name": "conv1_conv1_squeeze_pool1",
-              "coarse": 5
+              "name": "Conv_0_MaxPool_1",
+              "coarse": 20,
+              "buffer_depth": 2,
+              "node": "Conv_0"
             }
           ],
           "streams_out": [
             {
-              "name": "conv1_squeeze_pool1_pool1",
-              "coarse": 20
+              "name": "MaxPool_1_squeeze_MaxPool_1",
+              "coarse": 20,
+              "node": "squeeze_MaxPool_1"
             }
           ],
           "parameters": {
-            "buffer_depth": 0,
-            "batch_size": 1,
-            "rows_in": 24,
-            "cols_in": 24,
-            "channels_in": 20,
-            "rows_out": 24,
-            "cols_out": 24,
-            "channels_out": 20,
-            "coarse_in": 5,
-            "coarse_out": 20,
-            "data_width": 16
-          },
-          "node_in": "conv1",
-          "node_out": "pool1",
-          "type": "SQUEEZE"
-        },
-        {
-          "name": "pool1",
-          "streams_in": [
-            {
-              "name": "conv1_squeeze_pool1_pool1",
-              "coarse": 20
-            }
-          ],
-          "streams_out": [
-            {
-              "name": "pool1_squeeze_pool1",
-              "coarse": 20
-            }
-          ],
-          "parameters": {
-            "buffer_depth": 0,
-            "batch_size": 1,
+            "batch_size": 1024,
             "rows_in": 24,
             "cols_in": 24,
             "channels_in": 20,
@@ -123,39 +132,55 @@
             "coarse": 20,
             "pad_top": 0,
             "pad_right": 0,
-            "pad_left": 0,
             "pad_bottom": 0,
+            "pad_left": 0,
+            "kernel_rows": 2,
+            "kernel_cols": 2,
             "kernel_size": [
               2,
               2
             ],
+            "stride_rows": 2,
+            "stride_cols": 2,
             "stride": [
               2,
               2
             ],
-            "data_width": 16
-          },
-          "node_in": "conv1_squeeze_pool1",
-          "node_out": "squeeze_pool1",
-          "type": "POOLING"
+            "mem_bw_in": 100.0,
+            "mem_bw_out": 100.0,
+            "data_t": {
+              "width": 16,
+              "binary_point": 8
+            },
+            "stream_inputs": [
+              false
+            ],
+            "stream_outputs": [
+              false
+            ]
+          }
         },
         {
-          "name": "squeeze_pool1",
+          "name": "squeeze_MaxPool_1",
+          "onnx_node": "MaxPool_1",
+          "type": "SQUEEZE",
           "streams_in": [
             {
-              "name": "pool1_squeeze_pool1",
-              "coarse": 20
+              "name": "MaxPool_1_squeeze_MaxPool_1",
+              "coarse": 20,
+              "buffer_depth": 2,
+              "node": "MaxPool_1"
             }
           ],
           "streams_out": [
             {
               "name": "out",
-              "coarse": 1
+              "coarse": 4,
+              "node": "squeeze_MaxPool_1"
             }
           ],
           "parameters": {
-            "buffer_depth": 0,
-            "batch_size": 1,
+            "batch_size": 1024,
             "rows_in": 12,
             "cols_in": 12,
             "channels_in": 20,
@@ -163,14 +188,23 @@
             "cols_out": 12,
             "channels_out": 20,
             "coarse_in": 20,
-            "coarse_out": 1,
-            "data_width": 16
-          },
-          "node_in": "pool1",
-          "node_out": "squeeze_pool1",
-          "type": "SQUEEZE"
+            "coarse_out": 4,
+            "mem_bw_in": 100.0,
+            "mem_bw_out": 100.0,
+            "data_t": {
+              "width": 16,
+              "binary_point": 8
+            },
+            "stream_inputs": [
+              false
+            ],
+            "stream_outputs": [
+              false
+            ]
+          }
         }
-      ]
+      ],
+      "gen_last_width": 16
     }
   ]
 }