/*************************************************************************
 *
 *  This file is part of ACT dataflow neuro library
 *
 *  Copyright (c) 2020-2021 Rajit Manohar
 *  Copyright (c) 2022 University of Groningen - Ole Richter  
 *  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.
 *
 **************************************************************************
 */

namespace tmpl {
  namespace dataflow_neuro {

    export defproc TIELO_X1(bool! y; bool vdd, vss)
    {
      y = vss;
    }

    export defproc TIEHI_X1(bool! y; bool vdd, vss)
    {
      y = vdd;
    }

    /*-- inverters --*/

    defproc inv (bool! y; bool? a, vdd, vss)
    {
      prs {
        a => y-
      }
    }
      
    template<pint nf>
    defproc szinv <: inv()
    {
      [nf = 0 -> sizing { y {-1} }
      [] else -> sizing { y {-2*nf,svt,nf} }
      ]
    }

    export defcell INV_X1<: szinv<0>() { } 
    export defcell INV_X2<: szinv<1>() { } 
    export defcell INV_X4<: szinv<2>() { }
    export defcell INV_X8<: szinv<4>() { }


    /*--  signal buffers --*/

    defproc buf (bool! y; bool? a, vdd, vss)
    {
      bool _y;
      prs {
        a => _y-
      _y => y-
      }
    }
    export defcell BUF_X1<: buf()
    {
      sizing { _y {-1}; y {-1} }
    }
    export defcell BUF_X2<: buf()
    {
      sizing { _y {-1}; y {-2} }
    }
    export defcell BUF_X3<: buf()
    {
      sizing { _y {-1.5}; y {-3} }
    }
    export defcell BUF_X4<: buf()
    {
      sizing { _y {-1.5}; y {-4,2} }
    }
        export defcell BUF_X6<: buf()
    {
      sizing { _y {-3}; y {-6,2} }
    }
        export defcell BUF_X8<: buf()
    {
      sizing { _y {-4,2}; y {-8,4} }
    }
        export defcell BUF_X12<: buf()
    {
      sizing { _y {-6,2}; y {-12,4} }
    }

    /*-- delay cells --*/
    // TODO properly

    // export defcell DLY4_X1(bool! y; bool? a, vdd, vss)
    // {
    //   bool _y, __y, ___y;
    //   prs {
    //     a => _y-
    //     _y => __y-
    //     __y => ___y-
    //     ___y => y-
    //   }
    // }

    export defcell DLY4_X1(bool! y; bool? a, vdd, vss)
    {
        BUF_X1 bufchain[16];
        (i:0..14: bufchain[i].y = bufchain[i+1].a;)
        bufchain[0].a = a;
        bufchain[15].y = y;
    }

      
    /*-- simple gates --*/
      
    export defcell NOR2_X1(bool! y; bool? a, b, vdd, vss)
    {
      prs {
        a | b => y-
      }
      sizing { y {-1} }
    }
      
    export defcell NOR3_X1(bool! y; bool? a, b, c, vdd, vss)
    {
      prs {
        a | b | c => y-
      }
      sizing { y {-1} }
    } 

    export defcell NOR4_X1(bool! y; bool? a, b, c, d, vdd, vss)
    {
      prs {
        a | b | c | d => y-
      }
      sizing { y {-1} }
    }  


    export defcell OR2_X1(bool! y; bool? a, b, vdd, vss)
    {
      bool _y;
      prs {
        a | b => _y-
        _y => y-
      }
      sizing { _y{-1}; y{-1} }
    }
      
    export defcell OR2_X2(bool! y; bool? a, b, vdd, vss)
    {
      bool _y;
      prs {
        a | b => _y-
        _y => y-
      }
      sizing { _y{-1}; y{-2} }
    }

    export defcell OR3_X1(bool! y; bool? a, b, c, vdd, vss)
    {
      bool _y;
      prs {
        a | b | c => _y-
        _y => y-
      }
      sizing { _y{-1}; y{-1} }
    }

    export defcell OR4_X1(bool! y; bool? a, b, c, d, vdd, vss)
    {
      bool _y;
      prs {
        a | b | c | d => _y-
        _y => y-
      }
      sizing { _y{-1}; y{-1} }
    }


    export defcell NAND2_X1(bool! y; bool? a, b, vdd, vss)
    {
      prs {  
        a & b => y-
      }
      sizing { y{-1} }
    }
      
    export defcell NAND3_X1(bool! y; bool? a, b, c, vdd, vss)
    {
      prs {  
        a & b & c => y-
      }
      sizing { y{-1} }
    }

    export defcell NAND4_X1(bool! y; bool? a, b, c, d, vdd, vss)
    {
      prs {  
        a & b & c & d => y-
      }
      sizing { y{-1} }
    }

    export defcell AND2_X1(bool! y; bool? a, b, vdd, vss) 
    {
      bool _y;
      prs {  
        a & b => _y- 
      _y => y-
      }
      sizing { _y{-1}; y{-1} }
    } 

    export defcell AND2_X2(bool! y; bool? a, b, vdd, vss) 
    { 
      bool _y;
      prs {
        a & b => _y-
      _y => y-
      }
      sizing { _y{-1}; y{-2} }
    }

    export defcell AND3_X1(bool! y; bool? a, b, c, vdd, vss) 
    {
      bool _y;
      prs {  
        a & b & c => _y- 
      _y => y-
      }
      sizing { _y{-1}; y{-1} }
    } 

    export defcell AND4_X1(bool! y; bool? a, b, c, d, vdd, vss) 
    {
      bool _y;
      prs {  
        a & b & c & d => _y- 
      _y => y-
      }
      sizing { _y{-1}; y{-1} }
    }

    export defcell XOR2_X1(bool! y; bool? a, b, vdd, vss)
    {
      bool _a, _b;
      prs {
        a => _a-
        b => _b-

        [keeper=0] ~b & ~_a | ~_b & ~a -> y+
        _b & _a | b & a -> y-
      }
      sizing { _a{-1}; _b{-1}; y{-1} }
    }

    export defcell XNOR2_X1(bool! y; bool? a, b, vdd, vss)
    {
      bool _a, _b;
      prs {
        a => _a-
        b => _b-

        [keeper=0] ~b & ~a | ~_b & ~_a -> y+
        b & _a | _b & a -> y-
      }
      sizing { _a{-1}; _b{-1}; y{-1} }
    }

    export defcell MUX2_X1(bool! y; bool? a, b, s, vdd, vss)
    {
      // y = !( S ? b : a )
      // Adjusted to fit the XFAB Muxes
      bool _s;
      bool _y;
      prs {
        s => _s-

        [keeper=0] ~a & ~s | ~b & ~_s -> _y+
        a & _s | b & s -> _y-
        _y => y-
      }
      sizing { _s{-1}; y{-1}; _y{-1}}
    }

    export defcell MUX4_X1(bool! y; bool? a, b, c, d, s0, s1, vdd, vss)
    {
      // y = !( S ? a : b )
      bool _s0;
      bool _s1;
      bool _yab;
      bool _ycd;

      prs {
        s0 => _s0-
        s1 => _s1-

        [keeper=0] a & _s0 | b & s0 -> _yab-
        ~a & ~s0 | ~b & ~_s0 -> _yab+

        [keeper=0] c & _s0 | d & s0 -> _ycd-
        ~c & ~s0 | ~d & ~_s0 -> _ycd+

        [keeper=0]_yab & _s1 | _ycd & s1 -> y-
        ~_yab & ~s1 | ~_ycd & ~_s1 -> y+

      }
      sizing {_s0{-1}; _s1{-1}; y{-1}; _yab{-1}; _ycd{-1}}
    }


    export defcell OAI21_X1(bool! y; bool? a, b, c, vdd, vss)
    {
      prs {
        (a | b) & c => y-  
      }
      sizing { y{-1} }
    }

    export defcell AOI21_X1(bool! y; bool? a, b, c, vdd, vss) 
    { 
      prs {
        a & b | c => y-
      }
      sizing { y{-1} }
    }

    export defcell OAI22_X1(bool! y; bool? a, b, c, d, vdd, vss)
    {
      // y = !((a|b) & (c|d))
      prs {
        (a | b) & (c | d) => y-  
      }
      sizing { y{-1} }
    }

    export defcell AOI22_X1(bool! y; bool? a, b, c, d, vdd, vss) 
    { 
      prs {
        a & b | c & d => y-
      }
      sizing { y{-1} }
    }


    /*--- buffered transmission gates ---*/
      
    export defcell TBUF1_X1 (bool! y; bool? a, en, vdd, vss)
    {
      bool _en;
      prs {
        en => _en-

      ~a & ~_en -> y+
        a & en -> y-
      }
      sizing { _en{-1}; y{-1} }
    }
    
    export defcell TBUF_X2 (bool! y; bool? a, en, vdd, vss)
    {
      bool _en;
      prs {
        en => _en-

      ~a & ~_en -> y+
        a & en -> y-
      }
      sizing { _en{-2}; y{-2,2} }
    }
    export defproc DFFQ_R_X1 (bool? clk_B, reset_B, d; bool! q,q_B; bool? vdd,vss)
    {
      bool _clk_B, __clk_B, _mqi,_mqib,_sqi,_sqib;
      prs {

        // Creating delayed versions of the clock
        clk_B => _clk_B-
        _clk_B => __clk_B-

        (~d & ~_clk_B)|(~reset_B)|(~__clk_B&~_mqi) -> _mqib+
        ((d & __clk_B)|(_mqi & _clk_B))&reset_B -> _mqib-

        _mqib => _mqi-

        (~_mqi &~__clk_B)|(~reset_B)|(~_sqi&~_clk_B) -> _sqib+
        ((_mqi &_clk_B)|(_sqi&__clk_B))&reset_B -> _sqib-

        _sqib => _sqi-
        _sqib => q-

        q => q_B-


      }
    } 
  }
}


