/*************************************************************************
 *
 *  This file is part of ACT dataflow neuro library
 *
 *  Copyright (c) 2022 University of Groningen - Ole Richter
 *  Copyright (c) 2022 University of Groningen - Michele Mastella
 *  Copyright (c) 2022 University of Groningen - Hugh Greatorex
 *  Copyright (c) 2022 University of Groningen - Madison Cotteret
 * 
 *
 *  This source describes Open Hardware and is licensed under the CERN-OHL-W v2 or later
 *
 *  You may redistribute and modify this documentation and make products
 *  using it under the terms of the CERN-OHL-W v2 (https:/cern.ch/cern-ohl).
 *  This documentation is distributed WITHOUT ANY EXPRESS OR IMPLIED
 *  WARRANTY, INCLUDING OF MERCHANTABILITY, SATISFACTORY QUALITY
 *  AND FITNESS FOR A PARTICULAR PURPOSE. Please see the CERN-OHL-W v2
 *  for applicable conditions.
 *
 *  Source location: https://git.web.rug.nl/bics/actlib_dataflow_neuro
 *
 *  As per CERN-OHL-W v2 section 4.1, should You produce hardware based on
 *  these sources, You must maintain the Source Location visible in its
 *  documentation.
 *
 **************************************************************************
 */
import "../../dataflow_neuro/cell_lib_async.act";
import "../../dataflow_neuro/cell_lib_std.act";
import "../../dataflow_neuro/treegates.act";
import "../../dataflow_neuro/primitives.act";
import std::channel;
open std::channel;

// import std::func;
open std;

import std::data;
open std::data;


namespace tmpl {
	namespace dataflow_neuro {

		/**
		 * Bundled data (non dual rail, with req)
		 * 2
		 * quasi delay insensitive channel (dual rail).
		 * Basically a buffer with a bitwise conversion in front of it.
		 */
		export template<pint N, N_dly_cfg>
		defproc bd2qdi(bd<N> in; avMx1of2<N> out; bool? dly_cfg[N_dly_cfg]; power supply; bool? reset_B) {
			// Delay on req_in
			bool _req;
			delayprog<N_dly_cfg> dly(.in = in.r, .out = _req, .s = dly_cfg, .supply = supply);

			// sig buff the reset signal
			bool _reset_BX, _reset_BXX[N];
			BUF_X4 reset_buf(.a=reset_B, .y=_reset_BX,.vdd=supply.vdd,.vss=supply.vss);
            sigbuf<N> reset_bufarray(.in=_reset_BX, .out=_reset_BXX, .supply=supply);

            // sig buff the req
			bool _reqX, _reqXX[N+1];
			BUF_X4 req_buf(.a=_req, .y=_reqX,.vdd=supply.vdd,.vss=supply.vss);
            sigbuf<N+1> req_bufarray(.in=_reqX, .out=_reqXX, .supply=supply);

            // bd2qdi conversion
            // Each line goes to a t pin, its not to a f.
            bool _inB[N];
            INV_X1 input_invs[N];
            (i:N:
            	input_invs[i].a = in.d[i];
            	input_invs[i].y = _inB[i];
            	input_invs[i].vss = supply.vss;
            	input_invs[i].vdd = supply.vdd;
            )

			// BUFFER
			// Basically the buffer_s but with the validity tree ripped out
			// and just connected to in_req instead.

			// And probably need a delay on the in_ack to ensure en has time to disable
			// before the inputs go to another state.
			// Actually apparently no: there is a fixed, huge delay, already incurred
			// by communicating with pads-> uC -> windows 95 and back again.

            // Since the input is never invalid, also need a mechanism
            // for the output to become invalid, when an out_ack is received. 

            //control
            bool _en;
            A_3C_RB_X4 inack_ctl(.c1=_en,.c2=_reqXX[N],.c3=out.v,.y=in.a,.pr_B=_reset_BX,.sr_B=_reset_BX,.vdd=supply.vdd,.vss=supply.vss);
            A_1C1P_X1 en_ctl(.c1=in.a,.p1=out.v,.y=_en,.vdd=supply.vdd,.vss=supply.vss);

            //function
            bool _out_a_BX_t[N],_out_a_BX_f[N],_out_a_B,_en_X_t[N],_en_X_f[N];
            A_2C2N_RB_X4 f_buf_func[N];
            A_2C2N_RB_X4 t_buf_func[N];
            sigbuf<N> en_buf_t(.in=_en, .out=_en_X_t, .supply=supply);
            sigbuf<N> en_buf_f(.in=_en, .out=_en_X_f, .supply=supply);
            INV_X1 out_a_inv(.a=out.a,.y=_out_a_B, .vss = supply.vss, .vdd = supply.vdd);
            sigbuf<N> out_a_B_buf_f(.in=_out_a_B,.out=_out_a_BX_t, .supply=supply);
            sigbuf<N> out_a_B_buf_t(.in=_out_a_B,.out=_out_a_BX_f, .supply=supply);
            // check if you can also do single var to array connect a=b[N]
            // and remove them from the loop
            (i:N: 
                f_buf_func[i].y=out.d.d[i].f;
                t_buf_func[i].y=out.d.d[i].t;
                f_buf_func[i].c1=_en_X_f[i];
                t_buf_func[i].c1=_en_X_t[i];
                f_buf_func[i].c2=_out_a_BX_f[i];
                t_buf_func[i].c2=_out_a_BX_t[i];
                f_buf_func[i].n1=_inB[i];
                t_buf_func[i].n1=in.d[i];
                f_buf_func[i].n2=_reqXX[i];
                t_buf_func[i].n2=_reqXX[i];
                f_buf_func[i].vdd=supply.vdd;
                t_buf_func[i].vdd=supply.vdd;
                f_buf_func[i].vss=supply.vss;
                t_buf_func[i].vss=supply.vss;
                t_buf_func[i].pr_B = _reset_BXX[i];
                t_buf_func[i].sr_B = _reset_BXX[i];
                f_buf_func[i].pr_B = _reset_BXX[i];
                f_buf_func[i].sr_B = _reset_BXX[i];
            )


		}


		
		/**
		 * quasi delay insensitive channel (dual rail).
		 * 2
		 * Bundled data (non dual rail, with req)
		 */
		export template<pint N, N_dly_cfg>
		defproc qdi2bd(avMx1of2<N> in; bd<N> out; bool? dly_cfg[N_dly_cfg]; power supply; bool? reset_B) {

            // Buffer
            buffer<N> buf(.in = in, .supply = supply, .reset_B = reset_B);
            buf.out.a = out.a;
            
            // Vtree
            vtree<N> out_vtree(.supply = supply);
            (i:N:
            	out_vtree.in.d[i].t = buf.out.d.d[i].t;
            	out_vtree.in.d[i].f = buf.out.d.d[i].f;
            )
            buf.out.v = out_vtree.out;

            // Delay
            delayprog<N_dly_cfg> dly(.in = out_vtree.out, .out = out.r, .s = dly_cfg, .supply = supply);
            out_vtree.out = dly.in;

            // Wire output data bits to buffer True lines
            (i:N:
            	buf.out.d.d[i].t = out.d[i];
            )

		}



	}

}