--  Gates declaration
--  Copyright (C) 2017 Tristan Gingold
--
--  This file is part of GHDL.
--
--  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 2 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 <gnu.org/licenses>.

package Netlists.Gates is
   --  Id 0 is None
   --  Id 1 is Free
   --  Id 2 is top design

   --  Dyadic gates.  Inputs and output have the same width.
   Id_And  : constant Module_Id := 3;
   Id_Or   : constant Module_Id := 4;
   Id_Xor  : constant Module_Id := 5;
   Id_Nand : constant Module_Id := 6;
   Id_Nor  : constant Module_Id := 7;
   Id_Xnor : constant Module_Id := 8;

   Id_Add : constant Module_Id := 9;
   Id_Sub : constant Module_Id := 10;
   Id_Umin : constant Module_Id := 11;
   Id_Smin : constant Module_Id := 12;
   Id_Umax : constant Module_Id := 13;
   Id_Smax : constant Module_Id := 14;
   Id_Umul : constant Module_Id := 15;
   Id_Smul : constant Module_Id := 16;
   Id_Udiv : constant Module_Id := 17;
   Id_Sdiv : constant Module_Id := 18;
   Id_Umod : constant Module_Id := 19;
   Id_Smod : constant Module_Id := 20;
   Id_Srem : constant Module_Id := 21;

   subtype Dyadic_Module_Id is Module_Id range Id_And .. Id_Srem;

   Id_Not : constant Module_Id := 22;
   Id_Neg : constant Module_Id := 23;
   Id_Abs : constant Module_Id := 24;

   subtype Monadic_Module_Id is Module_Id range Id_Not .. Id_Abs;

   --  Logical and arithmetic shifts.
   --  FIXME: clarify right operand: width, large values
   Id_Lsl : constant Module_Id := 25;
   Id_Lsr : constant Module_Id := 26;
   Id_Asr : constant Module_Id := 27;

   subtype Shift_Module_Id is Module_Id range Id_Lsl .. Id_Asr;

   --  Rotations.
   --  FIXME: clarify right operand.
   Id_Rol : constant Module_Id := 28;
   Id_Ror : constant Module_Id := 29;

   subtype Rotate_Module_Id is Module_Id range Id_Lsl .. Id_Asr;

   subtype Shift_Rotate_Module_Id is Module_Id range Id_Lsl .. Id_Ror;

   Id_Eq  : constant Module_Id := 30;
   Id_Ne  : constant Module_Id := 31;
   Id_Ule : constant Module_Id := 32;
   Id_Sle : constant Module_Id := 33;
   Id_Ult : constant Module_Id := 34;
   Id_Slt : constant Module_Id := 35;
   Id_Uge : constant Module_Id := 36;
   Id_Sge : constant Module_Id := 37;
   Id_Ugt : constant Module_Id := 38;
   Id_Sgt : constant Module_Id := 39;

   subtype Compare_Module_Id is Module_Id range Id_Eq .. Id_Sgt;

   Id_Red_And : constant Module_Id := 40;
   Id_Red_Or  : constant Module_Id := 41;
   Id_Red_Xor : constant Module_Id := 42;

   subtype Reduce_Module_Id is Module_Id range Id_Red_And .. Id_Red_Xor;

   --  Inputs: i0, i1, ..., iN
   --  Output: o
   --  o = i0 & i1 & ... & iN
   Id_Concat2 : constant Module_Id := 43;
   Id_Concat3 : constant Module_Id := 44;
   Id_Concat4 : constant Module_Id := 45;

   --  Concatenation with N inputs.
   Id_Concatn : constant Module_Id := 46;

   subtype Concat_Module_Id is Module_Id range Id_Concat2 .. Id_Concatn;

   --  Inputs: s, i0, i1
   --  Output: o
   Id_Mux2 : constant Module_Id := 47;
   --  Inputs: s, i0, i1, s2, s3
   --  Output: o
   Id_Mux4 : constant Module_Id := 48;

   subtype Mux_Module_Id is Module_Id range Id_Mux2 .. Id_Mux4;

   --  Inputs: 0: Selector as one-hot encoding
   --          1: Default value (when the selector is 0)
   --          2..1+W: values (2: MSB of sel, 1+W: LSB of sel)
   --  Output: 0: selected value
   Id_Pmux : constant Module_Id := 49;

   --  Non-overlap dynamic extraction (get a slice of IN0).
   --  The width of the index gives the maximum index value.
   --  The WD = width(IN0) / 2**width(IN1), and the division must be exact.
   --  OUT := IN0[IN1*WD+WD-1:IN1*WD]
   --  Inputs:  0: the memory to be indexed
   --           1: the index
   Id_Bmux : constant Module_Id := 50;

   --  Like a wire: the output is equal to the input, but could be elimited
   --  at any time.  Isignal has an initial value.
   --
   --  Id_Output are inserted at the beginning because a module output cannot
   --  be read.  At the end, this is not an issue because an output is driven
   --  by a gate (and thus the value of the output could be read), but that
   --  driving value may not be available early enough.
   --  Id_Ioutput is an output with an initial value.
   --
   --  Inputs: 0: driver
   --          1: initial value (if any)
   --  Output: 0: value
   Id_Signal  : constant Module_Id := 52;
   Id_Isignal : constant Module_Id := 53;
   Id_Output  : constant Module_Id := 54;
   Id_Ioutput : constant Module_Id := 55;
   Id_Port    : constant Module_Id := 56;

   --  Id_Inout is a virtual gate used to fit inout direction into the netlist
   --  model which has only inputs and outputs.
   --  It is virtual because it doesn't perform any computation.
   --  Its output 1 must always be connected to an inout port of the module.
   --  (more precisely: to an input port marked as inout of the self instance).
   --  If input 0 is connected, it is a driver to the inout port.
   --  The current value of the inout port can be read from output 0.
   --
   --  Inputs:  0: value to be assigned to the port
   --  Outputs: 0: value of the port
   --           1: direct and only connection to the port
   Id_Inout   : constant Module_Id := 57;

   --  Like Id_Inout but with an initial value.
   Id_Iinout  : constant Module_Id := 58;

   --  Behaves like Id_Signal but for enable wires.
   Id_Enable  : constant Module_Id := 59;

   --  Temporary gate, O = I
   Id_Nop : constant Module_Id := 60;

   --  Note: initial values must be constant nets.
   --
   --  A simple D flip-flop.  The D input is stored on a rising edge of CLK.
   --  Q is the output.  For falling edge dff, use a NOT gate on the CLK
   --  input.
   --  Inputs:  0: CLK
   --           1: D
   --  Output:  0: Q
   Id_Dff   : constant Module_Id := 64;

   --  A DFF with an asynchronous reset.  Note that the asynchronous reset
   --  has priority over the clock.  When RST is asserted, the value is
   --  set to RST_VAL.
   --  Inputs:  0: CLK
   --           1: D
   --           2: RST
   --           3: RST_VAL
   --  Output:  0: Q
   Id_Adff  : constant Module_Id := 65;

   --  A simple DFF with an initial value (must be constant).  This is
   --  for FPGAs.
   --  Inputs:  0: CLK
   --           1: D
   --           2: INIT (initial value)
   --  Output:  0: Q
   Id_Idff  : constant Module_Id := 66;

   --  A DFF with an asynchronous reset and an initial value.
   --  Inputs:  0: CLK
   --           1: D
   --           2: RST
   --           3: RST_VAL
   --           4: INIT (initial value)
   --  Output:  0: Q
   Id_Iadff : constant Module_Id := 67;

   subtype Dff_Module_Id is Module_Id range Id_Dff .. Id_Iadff;

   --  Multi clock dff.  ELSE is the output of the next DFF.
   --  Inputs:  0: CLK
   --           1: D
   --           2: ELSE
   --  Output:  0: Q
   Id_Mdff : constant Module_Id := 68;

   --  Multi clock dff with initial value.  ELSE is the output of the next DFF.
   --  Inputs:  0: CLK
   --           1: D
   --           2: ELSE
   --           3: Init
   --  Output:  0: Q
   Id_Midff : constant Module_Id := 69;

   --  Gated D Latch
   --  Inputs:  0: D
   --           1: EN
   --  Outputs: 0: Q
   Id_Dlatch : constant Module_Id := 70;

   --  Tri state buffer.
   --  Inputs:  0: D
   --           1: EN
   --  Outputs: 0: O
   --  O <= EN ? O : 'Z'
   Id_Tri : constant Module_Id := 72;

   --  A resolver for tri-state.  The two inputs (tri or resolver gates) are
   --  connected together and to the output.
   --  I0  I1  O
   --  Z   Z   Z
   --  Z   v1  v1
   --  v0  Z   v0
   --  v0  v1  vo   Ok if v0 = v1, error if v0 /= v1.
   Id_Resolver : constant Module_Id := 73;

   --  Width change: truncate or extend.  Sign is know in order to possibly
   --  detect loss of value.
   Id_Utrunc : constant Module_Id := 82;
   Id_Strunc : constant Module_Id := 83;
   Id_Uextend : constant Module_Id := 84;
   Id_Sextend : constant Module_Id := 85;

   subtype Truncate_Module_Id is Module_Id range Id_Utrunc .. Id_Strunc;
   subtype Extend_Module_Id is Module_Id range Id_Uextend .. Id_Sextend;

   --  Extract a bit or a slice at a constant offset.
   --  OUT := IN0[OFF+WD-1:OFF]
   --  Param:   0: offset
   Id_Extract : constant Module_Id := 86;

   --  OUT := IN0[IN1+OFF+WD-1:IN1+OFF]
   --  Param:   0: offset
   --  Inputs:  0: MEM (the memory)
   --           1: IDX (then index)
   Id_Dyn_Extract : constant Module_Id := 87;

   --  Like Insert but for dynamic values.
   --  Params:  0: offset
   --  Inputs:  0: the memory
   --           1: the value to insert
   --           2: the index.
   --  T := IN0
   --  T [IN2+OFF+WD-1:IN2+OFF] := IN1
   --  OUT := T
   Id_Dyn_Insert : constant Module_Id := 88;

   --  Like Dyn_Insert but with an enable input.
   --  Inputs:  3: enable
   Id_Dyn_Insert_En : constant Module_Id := 89;

   subtype Dyn_Insert_Module_Id is
     Module_Id range Id_Dyn_Insert .. Id_Dyn_Insert_En;

   --  Gate to compute dynamic insert or extract offsets.
   --  Provides the scale (step) factor (needed for insert/extract wider than
   --   1 bit), also provides the maximum index value.
   --  The width of the output must be clog2(STEP*(MAX+1)).
   --  The width of the index (IN0) must be minimum (= clog2(MAX + 1)).
   --  For multi-dimensional insert/extract, memidx needs to be combined with
   --   addidx.
   --  Inputs:  0: index
   --  Params:  0: step
   --           1: max (maximum value for index, so length - 1).
   --  OUT := IN0 * STEP,  IN0 <= MAX
   Id_Memidx : constant Module_Id := 90;

   --  Combine (simply add) indexes for dynamic insert or extract.
   --  Despite the addition being commutative, the inputs are ordered.
   --  Input 0 must be a memidx (the least significant one, so with the smaller
   --   step), and input 1 must be either a memidx or an addidx.
   --  OUT := IN0 + IN1, size extension (max of inputs width).
   --  Inputs:  0: a memidx
   --           1: chain (addidx or memidx).
   Id_Addidx : constant Module_Id := 91;

   --  TODO:
   --  Id_Addidx_Cst : constant Module_Id := XX;

   --  Represent a memory with a fixed size.
   --  This is not really a regular gate, as it must be handled with the
   --  read and write ports.
   --  The width of IPORT and OPORT is the size (in bits) of the memory.
   --  IPORT and OPORT describe how the memory is affect by the ports.  It is
   --  as if the memory is constantly being modified by the ports, under the
   --  write enable condition.
   --  The order of the ports is important as it defines the order of action.
   --  The first port connected to OPORT is the first port to act.
   --  Inputs:  IPORT
   --  Outputs: OPORT
   Id_Memory : constant Module_Id := 92;

   --  Same as Id_Memory but with an initial value.
   --  Inputs:  IPORT
   --           INIT
   --  Outputs: OPORT
   Id_Memory_Init : constant Module_Id := 93;

   --  Asynchronous memory read port.
   --  Inputs:  PPORT  (previous memory port)
   --           ADDR
   --  Outputs: NPORT  (next memory port)
   --           DATA
   Id_Mem_Rd : constant Module_Id := 94;

   --  Synchronous memory read port.
   --  Inputs:  PPORT (previous memory port)
   --           ADDR
   --           CLK
   --           EN
   --  Outputs: NPORT (next memory port)
   --           DATA
   Id_Mem_Rd_Sync : constant Module_Id := 95;

   --  Synchronous memory write port
   --  Inputs:  PPORT (previous memory port)
   --           ADDR
   --           CLK
   --           EN
   --           DATA
   --  Outputs: NPORT (next memory port)
   Id_Mem_Wr_Sync : constant Module_Id := 96;

   --  Virtual gate to gather 2 dffs of a multiport memory.
   Id_Mem_Multiport : constant Module_Id := 97;

   --  Positive/rising edge and negative/falling edge detector.
   --  These are pseudo gates.
   Id_Posedge : constant Module_Id := 100;
   Id_Negedge : constant Module_Id := 101;
   subtype Edge_Module_Id is Module_Id range Id_Posedge .. Id_Negedge;

   --  Input signal must always be true.
   Id_Assert : constant Module_Id := 104;
   Id_Assume : constant Module_Id := 105;

   --  Input is true when a sequence is covered.
   Id_Cover : constant Module_Id := 106;
   --  Use to cover the precedent of an assertion.
   Id_Assert_Cover : constant Module_Id := 107;

   --  Formal gates.
   Id_Allconst : constant Module_Id := 108;
   Id_Anyconst : constant Module_Id := 109;
   Id_Allseq : constant Module_Id := 110;
   Id_Anyseq : constant Module_Id := 111;

   subtype Formal_Module_Id is Module_Id range Id_Allconst .. Id_Anyseq;

   --  Constants are gates with only one constant output.  There are multiple
   --  kind of constant gates: for small width, the value is stored as a
   --  parameter, possibly signed or unsigned extended.
   Id_Const_X : constant Module_Id := 112;
   Id_Const_Z : constant Module_Id := 113;
   Id_Const_0 : constant Module_Id := 114;
   Id_Const_1 : constant Module_Id := 115;
   Id_Const_UB32 : constant Module_Id := 116;
   Id_Const_SB32 : constant Module_Id := 117;
   Id_Const_UL32 : constant Module_Id := 118;

   --  Large width.
   --  For Const_Bit: param N is for bits 32*N .. 32*N+31
   --  For Const_Log: param 2*N   is for 0/1 of bits 32*N .. 32*N+31
   --                 param 2*N+1 is for Z/X of bits 32*N .. 32*N+31
   Id_Const_Bit : constant Module_Id := 119;
   Id_Const_Log : constant Module_Id := 120;

   subtype Constant_Module_Id is Module_Id range Id_Const_X .. Id_Const_Log;

   subtype Constant_Defined_Module_Id is
     Module_Id range Id_Const_0 .. Id_Const_Log;

   --  Id 128 is the first user id.
end Netlists.Gates;
