; VL Verilog Toolkit
; Copyright (C) 2008-2011 Centaur Technology
;
; Contact:
;   Centaur Technology Formal Verification Group
;   7600-C N. Capital of Texas Highway, Suite 300, Austin, TX 78731, USA.
;   http://www.centtech.com/
;
; 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, write to the Free Software
; Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA.
;
; Original author: Jared Davis <jared@centtech.com>

(in-package "VL")
(include-book "toe-wirealist")
(include-book "toe-verilogify")
(include-book "../mlib/find-module")
(include-book "centaur/esim/esim-sexpr-support" :dir :system)
(local (include-book "../util/arithmetic"))
(local (include-book "../util/osets"))
(local (include-book "../util/esim-lemmas"))


(defxdoc modinsts-to-eoccs
  :parents (e-conversion)
  :short "How we convert Verilog modules instances into (preliminary) E module
occurrences."

  :long "<p>This documentation assumes you have already read @(see
e-conversion).  Note that the E occurrences we generate in this initial pass
are only preliminary and might include multiple drivers of certain wires.  See
also @(see exploding-vectors), since we are going to be doing a lot of
that.</p>

<p>To convert each Verilog module instance into preliminary E occurrences, we
need to create E language @(see acl2::patterns) that represent (1) the inputs
and outputs of each module, i.e., the @(':i') and @(':o') patterns for each
module, and (2) the corresponding \"actuals\" of each module instance, i.e.,
the @(':i') and @(':o') patterns for each occurrence.</p>


<h4>Basic Idea</h4>

<p>Suppose we have a module with port declarations like:</p>

@({
input [3:0] a;
input b;
input [5:3] c;

...
})

<p>Then we are going to generate an input pattern like:</p>

@({
:i ( (a[0] a[1] a[2] a[3])
     (b)
     (c[3] c[4] c[5])
     ...)
})

<p>Here, individual bits like @('a[2]') and @('b') are @(see vl-emodwire-p)s.
The bits for each vector form a @(see vl-emodwirelist-p) like @('(a[0] a[1]
a[2] a[3])') or @('(b)').  Finally, our full @(':i') and @(':o') patterns are
lists of such vectors, and are recognized with @(see
vl-emodwirelistlist-p).</p>


<h4>Module I/O Patterns</h4>

<p>Recall the difference between port declarations (see @(see vl-portdecl-p))
and ports (see @(see vl-port-p)).  For instance:</p>

@({
module mymod (.low(vec[3:0]), .high(vec[5:4]), foo)   <-- ports
  input foo;
  input [5:0] vec;   <-- port declarations
endmodule
})

<p>We generate the @(':i') and @(':o') patterns for each module from their port
<i>declarations</i>, not from thir ports; see @(see vl-portdecls-to-i/o) for
details.  Because of this, the actual input pattern for @('mymod') would look
like this:</p>

@({
:i ((foo)
    (vec[0] vec[1] vec[2] vec[3] vec[4] vec[5]))
})


<h4>Port Patterns</h4>

<p>An instance of this mymod might look something like this:</p>

@({
mymod myinstance (a[3:0], b[1:0], c[7]);
})

<p>Unfortunately, the I/O patterns we have generated for @('mymod') are not
very useful when we want to translate @('myinstance'), because its entries are
not at all in the same shape or order as the ports.</p>

<p>To correct for this, we build a <b>port pattern</b> for each module.  For
instance, the port pattern for @('mymod') would be:</p>

@({
 ((vec[3] vec[2] vec[1] vec[0])
  (vec[5] vec[4])
  (foo))
})

<p>The port pattern matches the shape of the module's port expressions, and
lists the wires each port is connected to, in MSB-first order.</p>

<p>Port patterns are generated by @(see vl-portlist-msb-bit-pattern).  We can
carry out certain checking to ensure that the port pattern mentions every input
and output wire without duplication; see @(see port-bit-checking).</p>


<h4>Preliminary E Occurrences</h4>

<p>Port patterns make it pretty easy to create the E occurrence for a module
instance.  In particular, for any valid module instance, we can explode the
\"actuals\" into wires that line up perfectly with the port pattern.  In the
case of @('myinstance'), we sort of intuitively might imagine generating the
following \"actual pattern\":</p>

@({
 ((a[3] a[2] a[1] a[0])
  (b[1] b[0])
  (c[7]))
})

<p>We don't actually build this pattern.  Instead, we directly construct an
alist that binds each formal to its actual; see @(see
vl-modinst-eocc-bindings).</p>

<p>The @(':i') and @(':o') patterns for the module may then be instantiated
with this pattern to form the @(':i') and @(':o') fields for the occurrence.
The main function that does all of this is @(see vl-modinst-to-eocc).</p>")




; ----------------------------------------------------------------------------
;
;                      MAKING THE MODULE I/O PATTERNS
;
;           (just for the port decls -- NOT the actual "port pattern")
;
; ----------------------------------------------------------------------------

(defsection vl-portdecls-to-i/o
  :parents (modinsts-to-eoccs)
  :short "Compute the @(':i') and @(':o') fields for a module."

  :long "<p><b>Signature:</b> @(call vl-portdecls-to-i/o) returns @('(mv
successp warnings in-wires out-wires)').</p>

<p>We don't take a warnings accumulator because we memoize this function.</p>

<p>See @(see vl-emodwirelistlist-p) for some discussion about the kinds of
patterns we generate.</p>

<p>Historic note.  We originally tried to base our @(':i') and @(':o') patterns
on the order of a module's ports.  We now instead use the order of the port
declarations.  This is particularly nice for ports whose expressions are
concatenations such as @('{foo, bar, baz}'), since the individual components
might not even have the same direction.</p>"

  (defund vl-portdecls-to-i/o (portdecls walist)
    "Returns (MV SUCCESSP WARNINGS IN-WIRES OUT-WIRES)"
    (declare (xargs :guard (and (vl-portdecllist-p portdecls)
                                (vl-wirealist-p walist))))
    (b* (((when (atom portdecls))
          (mv t nil nil nil))

         (decl1 (car portdecls))
         ((vl-portdecl decl1) decl1)

         ((unless (or (eq decl1.dir :vl-input)
                      (eq decl1.dir :vl-output)))
          (b* ((w (make-vl-warning
                     :type :vl-bad-portdecl
                     :msg "~a0: port declaration has unsupported direction ~x1."
                     :args (list decl1 decl1.dir)
                     :fatalp t
                     :fn 'vl-portdecls-to-i/o)))
            (mv nil (list w) nil nil)))

         (entry (hons-get decl1.name walist))
         ((unless entry)
          (b* ((w (make-vl-warning
                     :type :vl-bad-portdecl
                     :msg "~a0: no wire alist entry for ~w1."
                     :args (list decl1 decl1.name)
                     :fatalp t
                     :fn 'vl-portdecls-to-i/o)))
            (mv nil (list w) nil nil)))

         (msb-wires (mbe :logic (list-fix (cdr entry)) :exec (cdr entry)))
         (lsb-wires (reverse msb-wires))

         ;; Sanity check: make sure that we found the right number of wires.
         ;; This shouldn't happen if the ports and the net/reg decls agree on
         ;; their range, which presumably we should have checked for earlier,
         ;; right?  Well, it seems safest to check it here, "too."
         ((unless (and (vl-maybe-range-resolved-p decl1.range)
                       (= (length lsb-wires)
                          (vl-maybe-range-size decl1.range))))
          (b* ((w (make-vl-warning
                   :type :vl-programming-error
                   :msg "~a0: wire-alist has ~x1 wires for ~w2, but its range ~
                         is ~a3."
                   :args (list decl1 (length lsb-wires) decl1.name decl1.range)
                   :fatalp t
                   :fn 'vl-portdecls-to-i/o)))
            (mv nil (list w) nil nil)))

         ;; Process all the other port declarations.
         ((mv successp warnings in-wires out-wires)
          (vl-portdecls-to-i/o (cdr portdecls) walist))

         ((unless successp)
          (mv nil warnings in-wires out-wires))

         ((mv in-wires out-wires)
          (case decl1.dir
            (:vl-input  (mv (cons lsb-wires in-wires) out-wires))
            (:vl-output (mv in-wires (cons lsb-wires out-wires)))
            (otherwise
             (prog2$ (er hard? 'vl-portdecls-to-i/o "Provably impossible.")
                     (mv in-wires out-wires))))))

      (mv t warnings in-wires out-wires)))

  ;; We want to memoize top-level calls because this will be invoked repeatedly
  ;; from other modules when we're trying to build the E occurrences for module
  ;; instances.
  (memoize 'vl-portdecls-to-i/o :recursive nil)

  (defmvtypes vl-portdecls-to-i/o (booleanp true-listp true-listp true-listp))

  (local (in-theory (enable vl-portdecls-to-i/o)))

  (defthm vl-warninglist-p-of-vl-portdecls-to-i/o
    (vl-warninglist-p (mv-nth 1 (vl-portdecls-to-i/o portdecls walist))))

  (defthm vl-portdecls-to-i/o-basics
    (implies (and (force (vl-portdecllist-p portdecls))
                  (force (vl-wirealist-p walist)))
             (let ((ret (vl-portdecls-to-i/o portdecls walist)))
               (and (vl-emodwirelistlist-p (mv-nth 2 ret))
                    (vl-emodwirelistlist-p (mv-nth 3 ret)))))))




; ----------------------------------------------------------------------------
;
;                         MAKING THE PORT PATTERN
;
;       (the real "port pattern" -- not the module i/o portdecl pattern)
;
; ----------------------------------------------------------------------------

(defsection vl-port-msb-bits
  :parents (modinsts-to-eoccs)
  :short "Compute the port pattern for a single port."

  :long "<p><b>Signature:</b> @(call vl-port-msb-bits) returns @('(mv successp
warnings msb-bits)').</p>"

  (defund vl-port-msb-bits (x walist)
    "Returns (MV SUCCESSP WARNINGS MSB-BITS)"
    (declare (xargs :guard (and (vl-port-p x)
                                (vl-wirealist-p walist))))
    (b* ((expr (vl-port->expr x))
         ((unless expr)
          (b* ((w (make-vl-warning
                   :type :vl-bad-port
                   :msg "~a0: expected no blank ports."
                   :args (list x)
                   :fatalp t
                   :fn 'vl-port-msb-bits)))
            (mv nil (list w) nil)))

         ((mv successp warnings msb-bits)
          (vl-msb-expr-bitlist expr walist nil))

         ((unless successp)
          (b* ((w (make-vl-warning
                   :type :vl-bad-port
                   :msg "~a0: failed to generate wires for this port."
                   :args (list x)
                   :fatalp t
                   :fn 'vl-port-msb-bits)))
            (mv nil (cons w warnings) nil))))

      (mv t warnings msb-bits)))

  (defmvtypes vl-port-msb-bits (booleanp true-listp true-listp))

  (local (in-theory (enable vl-port-msb-bits)))

  (defthm vl-warninglist-p-of-vl-port-msb-bits
    (vl-warninglist-p (mv-nth 1 (vl-port-msb-bits x walist))))

  (defthm vl-emodwirelist-p-of-vl-port-msb-bits
    (implies (and (force (vl-port-p x))
                  (force (vl-wirealist-p walist)))
             (vl-emodwirelist-p (mv-nth 2 (vl-port-msb-bits x walist))))))



(defsection vl-portlist-msb-bit-pattern
  :parents (modinsts-to-eoccs)
  :short "Compute the port pattern for a module."

  :long "<p><b>Signature:</b> @(call vl-portlist-msb-bit-pattern) returns
@('(mv successp warnings pattern)').</p>

<p>We don't take a warnings accumulator because we memoize this function.</p>"

  (defund vl-portlist-msb-bit-pattern (x walist)
    "Returns (MV SUCCESSP WARNINGS PATTERN)"
    (declare (xargs :guard (and (vl-portlist-p x)
                                (vl-wirealist-p walist))))
    (b* (((when (atom x))
          (mv t nil nil))
         ((mv successp1 warnings1 wires1) (vl-port-msb-bits (car x) walist))
         ((mv successp2 warnings2 wires2) (vl-portlist-msb-bit-pattern (cdr x) walist)))
      (mv (and successp1 successp2)
          (append warnings1 warnings2)
          (cons wires1 wires2))))

  (memoize 'vl-portlist-msb-bit-pattern
           :recursive nil)

  (local (in-theory (enable vl-portlist-msb-bit-pattern)))

  (defmvtypes vl-portlist-msb-bit-pattern (booleanp true-listp true-listp))

  (defthm vl-warninglist-p-of-vl-portlist-msb-bit-pattern
    (vl-warninglist-p (mv-nth 1 (vl-portlist-msb-bit-pattern x walist))))

  (defthm true-list-listp-of-vl-portlist-msb-bit-pattern
    (true-list-listp (mv-nth 2 (vl-portlist-msb-bit-pattern x walist))))

  (defthm vl-emodwirelistlist-p-of-vl-portlist-msb-bit-pattern
    (implies (and (force (vl-portlist-p x))
                  (force (vl-wirealist-p walist)))
             (vl-emodwirelistlist-p (mv-nth 2 (vl-portlist-msb-bit-pattern x walist))))))





; ----------------------------------------------------------------------------
;
;                             PORT-BIT CHECKING
;
;               (a separate transform before making occurrences)
;
; ----------------------------------------------------------------------------

(defxdoc port-bit-checking
  :parents (e-conversion)
  :short "A well-formedness check to ensure that ports and port declarations
agree, and are simple enough for E conversion."

  :long "<p>Before generating E modules, we do a global pass over the module
list and make sure that we can generate the port pattern for each module
appropriately.</p>")

(defsection vl-module-check-port-bits
  :parents (port-bit-checking)
  :short "Ensure the port pattern for a module is reasonable."

  :long "<p>@(call vl-module-check-port-bits) separately builds up the bit
patterns for ports and the port declarations of the module @('x'), then makes
sure that there is exactly one port bit for every port declaration bit and vice
versa.  We extend @('X') with a fatal warning if this doesn't hold.</p>"

  (local
   (encapsulate
     ()
     (local (defthm insert-under-iff
              (iff (insert a x)
                   t)
              :hints(("Goal" :in-theory (enable (:ruleset sets::primitive-rules))))))

     (local (defthm demote-in-to-member-equal
              (implies (setp x)
                       (equal (in a x)
                              (if (member-equal a x)
                                  t
                                nil)))
              :hints(("Goal" :in-theory (enable (:ruleset sets::primitive-rules))))))

     (defthm empty-intersect-to-intersectp-equal
       (implies (and (setp x)
                     (setp y))
                (equal (empty (sets::intersect x y))
                       (not (intersectp-equal x y))))
       :hints(("Goal"
               :induct (sets::intersect x y)
               :in-theory (e/d ((:ruleset sets::primitive-rules))
                               ;; speed hint
                               (promote-member-equal-to-membership
                                sets::insert-identity
                                sets::intersect-symmetric
                                sets::double-containment
                                sets::setp)))))))

  ;; Speed hint
  (local (in-theory (disable no-duplicatesp-equal
                             union
                             intersect
                             mergesort
                             difference
                             intersectp-equal
                             hons-assoc-equal
                             acl2::consp-under-iff-when-true-listp
                             subsetp-equal-when-first-two-same-yada-yada
                             sets::double-containment)))

  (defund vl-module-check-port-bits (x)
    (declare (xargs :guard (vl-module-p x)))
    (b* (((vl-module x) x)
         (warnings x.warnings)

         ;; Construct the wire alist
         ((mv successp warnings walist)
          (vl-module-wirealist x warnings))
         ((unless successp)
          (change-vl-module x :warnings warnings))
         ((with-fast walist))

         ;; Get bit lists for ports and port declarations
         ((mv okp1 warnings1 port-bits)
          (vl-portlist-msb-bit-pattern x.ports walist))
         ((mv okp2 warnings2 in-wires out-wires)
          (vl-portdecls-to-i/o x.portdecls walist))
         (warnings (append warnings1 warnings2 x.warnings))
         ((unless (and okp1 okp2))
          (change-vl-module x :warnings warnings))

         ;; Turn everything into sets so we can compare them efficiently
         (flat-ports   (flatten port-bits))
         (flat-ports-s (mergesort flat-ports))

         (flat-ins     (flatten in-wires))
         (flat-outs    (flatten out-wires))
         (flat-ins-s   (mergesort flat-ins))
         (flat-outs-s  (mergesort flat-outs))
         (flat-decls-s (union flat-ins-s flat-outs-s))

         ;; Check: unique bits for all port declarations.
         (warnings
          (if (mbe :logic (uniquep (append flat-ins flat-outs))
                   :exec (and (mbe :logic (uniquep flat-ins)
                                   :exec  (same-lengthp flat-ins-s flat-ins))
                              (mbe :logic (uniquep flat-outs)
                                   :exec  (same-lengthp flat-outs-s flat-outs))
                              (mbe :logic (not (intersectp-equal flat-ins flat-outs))
                                   :exec  (not (sets::intersectp flat-ins-s flat-outs-s)))))
              warnings
            ;; Else, there are duplicated port names!
            (b* ((dupe-names (duplicated-members (vl-portdecllist->names x.portdecls)))
                 (dupe-bits  (duplicated-members (append flat-ins flat-outs)))
                 (w (if dupe-names
                        (make-vl-warning
                         :type :vl-bad-portdecls
                         :msg "The following ports are illegally declared ~
                               more than once: ~&0."
                         :args (list dupe-names)
                         :fatalp t
                         :fn 'vl-module-check-port-bits)
                      (make-vl-warning
                       :type :vl-programming-error
                       :msg "Failed to generate unique port bit names even ~
                             though the port decls have unique names.  Jared ~
                             thinks this should be impossible unless the wire ~
                             alist is invalid. Duplicate bits: ~&0."
                       :args (list (vl-verilogify-emodwirelist dupe-bits))
                       :fatalp t
                       :fn 'vl-module-check-port-bits))))
              (cons w warnings))))

         ;; Check: unique bits for all ports.
         (warnings
          (if (mbe :logic (uniquep flat-ports)
                   :exec (same-lengthp flat-ports-s flat-ports))
              warnings
            (b* ((dupe-bits (duplicated-members flat-ports))
                 (w (make-vl-warning
                     :type :vl-bad-ports
                     :msg "The following wires are directly connected to ~
                           multiple ports: ~&0."
                     :args (list (vl-verilogify-emodwirelist dupe-bits))
                     :fatalp t
                     :fn 'vl-module-check-port-bits)))
              (cons w warnings))))

         ;; Check: every declared bit is in a port, and vice versa.
         (warnings
          (if (equal flat-decls-s flat-ports-s)
              warnings
            (b* ((extra-port-bits (difference flat-ports-s flat-decls-s))
                 (extra-decl-bits (difference flat-decls-s flat-ports-s))
                 (w (make-vl-warning
                     :type :vl-bad-ports
                     :msg "Mismatch between the ports and port declarations:~%  ~
                            - Bits only in ports: ~&0~%  ~
                            - Bits only in port declarations: ~&1"
                     :args (list (vl-verilogify-emodwirelist extra-port-bits)
                                 (vl-verilogify-emodwirelist extra-decl-bits))
                     :fatalp t
                     :fn 'vl-module-check-port-bits)))
              (cons w warnings)))))

      ;; Optimization: don't change the module if the warnings haven't changed.
      ;; This is actually very useful: it lets us reuse the memoized wirealist
      ;; and portpat stuff for modules that don't have any problems.
      (if (equal x.warnings warnings)
          x
        (change-vl-module x :warnings warnings))))

  (local (in-theory (enable vl-module-check-port-bits)))

  (defthm vl-module-p-of-vl-module-check-port-bits
    (implies (force (vl-module-p x))
             (vl-module-p (vl-module-check-port-bits x))))

  (defthm vl-module->name-of-vl-module-check-port-bits
    (equal (vl-module->name (vl-module-check-port-bits x))
           (vl-module->name x))))



(defprojection vl-modulelist-check-port-bits (x)
  (vl-module-check-port-bits x)
  :guard (vl-modulelist-p x)
  :result-type vl-modulelist-p
  :parents (port-bit-checking)

  :long "<p>Performance note.  This will look expensive because it calls @(see
vl-module-wirealist), @(see vl-portdecls-to-i/o) and @(see
vl-portlist-msb-bit-pattern) on all modules.  But since these are memoized, we
get to reuse this work when we generate the eoccs for module instances and need
to look up these patterns.</p>"

  :rest
  ((defthm vl-modulelist->names-of-vl-modulelist-check-port-bits
     (equal (vl-modulelist->names (vl-modulelist-check-port-bits x))
            (vl-modulelist->names x)))))



; ----------------------------------------------------------------------------
;
;             MAKING THE ALIST OF FORMALS->ACTUALS FOR AN INSTANCE
;
; ----------------------------------------------------------------------------

(defsection vl-plainarg-lsb-bits
  :parents (modinsts-to-eoccs)
  :short "Build the list of @(see vl-emodwire-p)s for a @(see vl-plainarg-p),
in <b>LSB-first</b> order."

  :long "<p><b>Signature:</b> @(call vl-plainarg-lsb-bits) returns @('(mv
successp warnings lsb-bits)').</p>

<p>See @(see vl-msb-expr-bitlist).  This function just makes sure a @(see
vl-plainarg-p) isn't blank and then calls @('vl-msb-expr-bitlist') to do the
work.  We return the bits in LSB-first order to match the convention throughout
E.</p>"

  (defund vl-plainarg-lsb-bits (x walist warnings)
    "Returns (MV SUCCESSP WARNINGS LSB-BITS)"
    (declare (xargs :guard (and (vl-plainarg-p x)
                                (vl-wirealist-p walist)
                                (vl-warninglist-p warnings))))
    (b* ((expr (vl-plainarg->expr x))
         ((unless expr)
          (b* ((w (make-vl-warning
                   :type :vl-unsupported
                   :msg "In vl-plainarg-lsb-bits, expected no blank ports."
                   :fatalp t
                   :fn 'vl-plainarg-lsb-bits)))
            (mv nil (cons w warnings) nil)))
         ((mv successp warnings bits)
          (vl-msb-expr-bitlist expr walist warnings)))
        (mv successp warnings (reverse bits))))

  (local (in-theory (enable vl-plainarg-lsb-bits)))

  (defmvtypes vl-plainarg-lsb-bits (nil nil true-listp))

  (defthm true-listp-of-vl-plainarg-lsb-bits-1
    (implies (true-listp warnings)
             (true-listp (mv-nth 1 (vl-plainarg-lsb-bits x walist warnings))))
    :rule-classes :type-prescription)

  (defthm vl-warninglist-p-of-vl-plainarg-lsb-bits
    (implies (force (vl-warninglist-p warnings))
             (vl-warninglist-p
              (mv-nth 1 (vl-plainarg-lsb-bits x walist warnings)))))

  (defthm vl-emodwirelist-p-of-vl-plainarg-lsb-bits
    (implies (and (force (vl-plainarg-p x))
                  (force (vl-wirealist-p walist)))
             (vl-emodwirelist-p (mv-nth 2 (vl-plainarg-lsb-bits x walist warnings))))))


(defsection vl-plainarglist-lsb-pattern
  :parents (modinsts-to-eoccs)
  :short "Build lists of @(see vl-emodwire-p)s for a @(see vl-plainarglist-p)."

  :long "<p><b>Signature:</b> @(call vl-plainarglist-lsb-pattern) returns
@('(mv successp warnings pattern)').</p>

<p>We project @(see vl-plainarg-lsb-bits) across a list of arguments, and cons
together the resulting bits to produce an @(see vl-emodwirelistlist-p) where
each sub-list is in LSB-order.</p>"

  (defund vl-plainarglist-lsb-pattern (x walist warnings)
    "Returns (MV SUCCESSP WARNINGS PATTERN)"
    (declare (xargs :guard (and (vl-plainarglist-p x)
                                (vl-wirealist-p walist)
                                (vl-warninglist-p warnings))))
    (b* (((when (atom x))
          (mv t warnings nil))
         ((mv car-successp warnings car-lsb-bits)
          (vl-plainarg-lsb-bits (car x) walist warnings))
         ((mv cdr-successp warnings cdr-lsb-pattern)
          (vl-plainarglist-lsb-pattern (cdr x) walist warnings)))
      (mv (and car-successp cdr-successp)
          warnings
          (cons car-lsb-bits cdr-lsb-pattern))))

  (defmvtypes vl-plainarglist-lsb-pattern (booleanp nil true-listp))

  (local (in-theory (enable vl-plainarglist-lsb-pattern)))

  (defthm true-listp-of-vl-plainarglist-lsb-pattern-1
    (implies (true-listp warnings)
             (true-listp (mv-nth 1 (vl-plainarglist-lsb-pattern x walist warnings))))
    :rule-classes :type-prescription)

  (defthm vl-warninglist-p-of-vl-plainarglist-lsb-pattern
    (implies (force (vl-warninglist-p warnings))
             (vl-warninglist-p
              (mv-nth 1
               (vl-plainarglist-lsb-pattern x walist warnings)))))

  (defthm true-list-listp-of-vl-plainarglist-lsb-pattern
    (true-list-listp
     (mv-nth 2 (vl-plainarglist-lsb-pattern x walist warnings))))

  (defthm vl-emodwirelistlist-p-of-vl-plainarglist-lsb-pattern
    (implies (and (force (vl-plainarglist-p x))
                  (force (vl-wirealist-p walist)))
             (vl-emodwirelistlist-p
              (mv-nth 2 (vl-plainarglist-lsb-pattern x walist warnings))))))


(defsection vl-modinst-eocc-bindings
  :parents (modinsts-to-eoccs)
  :short "Build a (slow) alist binding the \"formals\" for a module to the
\"actuals\" from an instance."

  :long "<p><b>Signature:</b> @(call vl-modinst-eocc-bindings) returns @('(mv
successp warnings binding-alist)').</p>

<ul>

<li>@('actuals') are the arguments in the module instance.</li>

<li>@('portpat') is the port pattern for the module being instanced; see @(see
modinsts-to-eoccs).  We assume (in the guard) that it is the same length as the
actuals (i.e., the module instance has the proper arity), but we still have to
check the lengths on the sub-lists.</li>

<li>@('walist') is the wire alist for the superior module.  It is used to
generate the E wires for the actuals.</li>

<li>@('warnings') is a @(see warnings) accumulator for the superior
module.</li>

<li>The @('inst') is the module instance we're processing.  It is semantically
irrelevant and is only used as a context for warnings.</li>

</ul>"

  (defund vl-modinst-eocc-bindings (actuals portpat walist warnings inst)
    "Returns (MV SUCCESSP WARNINGS BINDING-ALIST)"
    (declare (xargs :guard (and (vl-plainarglist-p actuals)
                                (true-list-listp portpat)
                                (vl-emodwirelistlist-p portpat)
                                (same-lengthp actuals portpat)
                                (vl-wirealist-p walist)
                                (vl-warninglist-p warnings)
                                (vl-modinst-p inst))))
    (b* (((when (atom actuals))
          (mv t warnings nil))

         ((vl-modinst inst) inst)
         (expr1 (vl-plainarg->expr (car actuals)))

         ((unless expr1)
          ;; Shouldn't happen if we've properly converted blanks to Zs.
          (b* ((w (make-vl-warning
                   :type :vl-programming-error
                   :msg "~a0: expected all arguments to be non-blank."
                   :args (list inst)
                   :fatalp t
                   :fn 'vl-modinst-eocc-bindings)))
            (mv nil (cons w warnings) nil)))

         ((mv successp warnings expr1-msb-bits)
          (vl-msb-expr-bitlist expr1 walist warnings))

         ((unless successp)
          (b* ((w (make-vl-warning
                   :type :vl-bad-instance
                   :msg "~a0: error generating wires for ~a1."
                   :args (list inst.loc expr1)
                   :fatalp t
                   :fn 'vl-modinst-eocc-bindings)))
            (mv nil (cons w warnings) nil)))

         (formal1-msb-bits (car portpat))

         ((unless (same-lengthp expr1-msb-bits formal1-msb-bits))
          (b* ((nactuals (length expr1-msb-bits))
               (nformals (length formal1-msb-bits))
               (w (make-vl-warning
                   :type :vl-bad-instance
                   :msg "~a0: we produced ~x1 wires~s2 for an argument whose ~
                         corresponding port has ~x3 wire~s4.  ~
                          - Argument wires: ~x5;  ~
                          - Port wires: ~x6."
                   :args (list inst
                               nactuals (if (= nactuals 1) "" "s")
                               nformals (if (= nformals 1) "" "s")
                               (symbol-list-names expr1-msb-bits)
                               (symbol-list-names formal1-msb-bits))
                   :fatalp t
                   :fn 'vl-modinst-eocc-bindings)))
            (mv nil (cons w warnings) nil)))

         ((mv successp warnings binding-alist)
          (vl-modinst-eocc-bindings (cdr actuals) (cdr portpat)
                                    walist warnings inst))

         (binding-alist (append (pairlis$ formal1-msb-bits expr1-msb-bits)
                                binding-alist)))

      (mv successp warnings binding-alist)))

  (defmvtypes vl-modinst-eocc-bindings (booleanp nil true-listp))

  (local (in-theory (enable vl-modinst-eocc-bindings)))

  (defthm vl-warninglist-p-of-vl-modinst-eocc-bindings
    (let ((ret (vl-modinst-eocc-bindings actuals portpat walist warnings inst)))
      (implies (vl-warninglist-p warnings)
               (vl-warninglist-p (mv-nth 1 ret)))))

  (defthm alistp-of-vl-modinst-eocc-bindings
    (let ((ret (vl-modinst-eocc-bindings actuals portpat walist warnings inst)))
      (alistp (mv-nth 2 ret))))

  (local (defthm alist-keys-pairlis$
           (equal (alist-keys (pairlis$ a b))
                  (list-fix a))
           :hints(("Goal" :in-theory (enable alist-keys pairlis$)))))

  (defthm vl-emodwirelist-p-alist-keys-eocc-bindings
    (let ((ret (vl-modinst-eocc-bindings actuals portpat walist warnings inst)))
      (implies (vl-emodwirelistlist-p portpat)
               (vl-emodwirelist-p (alist-keys (mv-nth 2 ret))))))

  (defthm vl-emodwirelist-p-alist-vals-eocc-bindings
    (let ((ret (vl-modinst-eocc-bindings actuals portpat walist warnings inst)))
      (implies (vl-wirealist-p walist)
               (vl-emodwirelist-p (alist-vals (mv-nth 2 ret)))))))




; ----------------------------------------------------------------------------
;
;             CONVERTING MODULE INSTANCES INTO E OCCURRENCES
;
; ----------------------------------------------------------------------------

(defalist vl-ealist-p (x)
  :key (stringp x)
  :val (good-esim-modulep x)
  :keyp-of-nil nil
  :valp-of-nil t
  :parents (e-conversion)
  :short "Alist binding module names to E modules."

  :long "<p>Our main E conversion transform proceeds in dependency order, so
that the E modules for all submodules should already be available.</p>

<p>A @('vl-ealist-p') is an alist that binds module names to the E modules we
have generated for them.  We use it to look up the definitions for submodules.
To make lookups fast, we generally expect it to be a fast alist.</p>")


(defsection vl-modinst-to-eocc
  :parents (modinsts-to-eoccs)
  :short "Main function for transforming a Verilog module instance into
an (preliminary) E language occurrence."

  :long "<p><b>Signature:</b> @(call vl-modinst-to-eocc) returns @('(mv
successp warnings eocc).')</p>

<ul>

<li>@('x') is the module instance that we want to convert into an E
occurrence.</li>

<li>@('walist') is the wire alist for the superior module.  We use it to build
the wire lists for the actuals.</li>

<li>@('mods') is the list of all modules; @('modalist') is its corresponding
@(see vl-modalist) for fast module lookups.  We use these to look up the
submodule being instantiated, so we can compute its port pattern.</li>

<li>@('eal') is the already-processed @(see vl-ealist-p) that binds module
names to the E modules we've built for them so far.  We use this to look up the
definiton of the submodule for the @(':op') field of the E occurrence.</li>

<li>@('warnings') is the @(see warnings) accumulator for the superior
module.</li>

</ul>

<p>We do a lot of sanity checking to make sure the module instance is simple
enough to transform, that the submodule exists and was translated into an E
module successfully, etc.  Then, we figure out the bindings to use for the
@(':i') and @(':o') fields of the occurrence, as described in @(see
modinsts-to-eoccs).</p>"

  (defund vl-modinst-to-eocc (x walist mods modalist eal warnings)
    "Returns (MV SUCCESSP WARNINGS EOCC)"
    (declare (xargs :guard (and (vl-modinst-p x)
                                (vl-wirealist-p walist)
                                (vl-modulelist-p mods)
                                (equal modalist (vl-modalist mods))
                                (vl-ealist-p eal)
                                (vl-warninglist-p warnings))))
    (b* (((vl-modinst x) x)

         ;; Preliminary sanity checks to make sure this instance is
         ;; reasonable.

         ((unless x.instname)
          (b* ((w (make-vl-warning
                   :type :vl-bad-instance
                   :msg "~a0: expected all module instances to be named.  Did ~
                         you run the addinstnames transform?"
                   :args (list x)
                   :fatalp t
                   :fn 'vl-modinst-to-eocc)))
            (mv nil (cons w warnings) nil)))

         ((when x.range)
          (b* ((w (make-vl-warning
                   :type :vl-bad-instance
                   :msg "~a0: expected only simple module instances, but ~s1 ~
                         is an array of module instances.  Did you run the ~
                         replicate transform?"
                   :args (list x x.instname)
                   :fatalp t
                   :fn 'vl-modinst-to-eocc)))
            (mv nil (cons w warnings) nil)))

         ((when (consp (vl-arguments->args x.paramargs)))
          (b* ((w (make-vl-warning
                   :type :vl-bad-instance
                   :msg "~a0: expected only simple module instances, but ~s1 ~
                         still has parameters.  Did you run the ~
                         unparameterization transform?"
                   :args (list x x.instname)
                   :fatalp t
                   :fn 'vl-modinst-to-eocc)))
            (mv nil (cons w warnings) nil)))

         ((when (vl-arguments->namedp x.portargs))
          (b* ((w (make-vl-warning
                   :type :vl-bad-instance
                   :msg "~a0: expected only resolved module instances, but ~
                         ~s1 still has named arguments.  Did you run the ~
                         argresolve transform?"
                   :args (list x x.instname)
                   :fatalp t
                   :fn 'vl-modinst-to-eocc)))
            (mv nil (cons w warnings) nil)))


         ;; Look up the submodule, its esim, etc.

         (sub      (vl-fast-find-module x.modname mods modalist))
         ((unless sub)
          (b* ((w (make-vl-warning
                   :type :vl-bad-instance
                   :msg "~a0 refers to undefined module ~m1."
                   :args (list x x.modname)
                   :fatalp t
                   :fn 'vl-modinst-to-eocc)))
            (mv nil (cons w warnings) nil)))

         (sub-esim (cdr (hons-get x.modname eal)))
         ((unless sub-esim)
          (b* ((w (make-vl-warning
                   :type :vl-bad-submodule
                   :msg "~a0 refers to module ~m1, but we failed to build an ~
                         E module for ~m1."
                   :args (list x x.modname)
                   :fatalp t
                   :fn 'vl-modinst-to-eocc)))
            (mv nil (cons w warnings) nil)))

         ((mv okp & sub-walist) (vl-module-wirealist sub nil))
         ((unless okp)
          (b* ((w (make-vl-warning
                   :type :vl-programming-error
                   :msg "~a0: failed to build a wire alist for ~x0?  Jared ~
                         thinks this should never happen since we were able ~
                         to build the ESIM module for it."
                   :args (list x.modname)
                   :fatalp t
                   :fn 'vl-modinst-to-eocc)))
            (mv nil (cons w warnings) nil)))


         ;; Now compute the port pattern for the submodule.

         ((mv successp & portpat)
          ;; We ignore the warnings here, because (1) there shouldn't be any
          ;; and (2) if there are, it's a problem with the submodule, not with
          ;; the superior module.
          (vl-portlist-msb-bit-pattern (vl-module->ports sub) sub-walist))
         ((unless successp)
          (b* ((w (make-vl-warning
                   :type :vl-programming-error
                   :msg "~a0: failed to build a portlist pattern for module ~
                         ~m1. Jared thinks this should never happen because ~
                         we check that these lists can be built before trying ~
                         to make E occurrences from module instances."
                   :args (list x x.modname)
                   :fatalp t
                   :fn 'vl-modinst-to-eocc)))
            (mv nil (cons w warnings) nil)))


         ;; Build the alist binding formals to actuals...

         (actuals (vl-arguments->args x.portargs))
         ((unless (same-lengthp actuals portpat))
          (b* ((w (make-vl-warning
                   :type :vl-bad-instance
                   :msg "~a0: wrong arity.  Expected ~x1 arguments but found ~x2."
                   :args (list x (len portpat) (len actuals))
                   :fatalp t
                   :fn 'vl-modinst-to-eocc)))
            (mv nil (cons w warnings) nil)))
         ((mv successp warnings binding-alist)
          (vl-modinst-eocc-bindings actuals portpat walist warnings x))
         ((unless successp)
          ;; Already explained why.
          (mv nil warnings nil))


         ;; Get the :i and :o patterns to instantiate.  It would probably be
         ;; reasonable to just look these up from sub-esim.  But as an extra
         ;; sanity check, we'll go ahead and recompute them, and make sure
         ;; everything agrees.  Note that vl-portdecls-to-i/o is memoized, so
         ;; this should be pretty cheap.

         ((mv successp & in-pat out-pat)
          ;; We ignore the warnings here because if there's any problem, it's
          ;; a problem with the submodule.
          (vl-portdecls-to-i/o (vl-module->portdecls sub) sub-walist))
         ((unless successp)
          (b* ((w (make-vl-warning
                   :type :vl-programming-error
                   :msg "~a0: failed to build :i and :o patterns for module ~
                         ~m1.  Jared thinks this should never happen because ~
                         we already built its esim and should have checked ~
                         that this was all okay due to port-bit-checking."
                   :args (list x x.modname)
                   :fatalp t
                   :fn 'vl-modinst-to-eocc)))
            (mv nil (cons w warnings) nil)))

         ((unless (and (equal in-pat (gpl :i sub-esim))
                       (equal out-pat (gpl :o sub-esim))))
          (b* ((w (make-vl-warning
                   :type :vl-programming-error
                   :msg "~a0: the :i and :o patterns we built for ~m1 do ~
                         not agree with the :i and :o patterns of its ESIM? ~
                         Jared thinks this should never happen because the ~
                         patterns should be being built in the same way. ~%  ~
                          - in-pat: ~x2~%  ~
                          - found:  ~x3~%  ~
                          - out-pat: ~x4~%  ~
                          - found:   ~x5~%"
                   :args (list x x.modname in-pat (gpl :i sub-esim)
                               out-pat (gpl :o sub-esim))
                   :fatalp t
                   :fn 'vl-modinst-to-eocc)))
            (mv nil (cons w warnings) nil)))

         ;; Instantiate the :i and :o patterns to get the actuals for this
         ;; occurrence.

         ((with-fast binding-alist))

         ;; Extra sanity check, so that we can prove this always builds a good
         ;; esim occ.
         (all-formal-bits (pat-flatten out-pat (pat-flatten1 in-pat)))
         (all-actual-bits (alist-keys binding-alist))
         ((unless (equal (mergesort all-formal-bits)
                         (mergesort all-actual-bits)))
          (b* ((w (make-vl-warning
                   :type :vl-programming-error
                   :msg "~a0: the binding alist we produced doesn't contain ~
                         bindings for exactly the right bits.  Jared thinks ~
                         vl-modinst-to-eocc-bindings should ensure that this ~
                         never happens."
                   :args (list x)
                   :fatalp t
                   :fn 'vl-modinst-to-eocc)))
            (mv nil (cons w warnings) nil)))

         (inputs   (acl2::al->pat in-pat binding-alist nil))
         (outputs  (acl2::al->pat out-pat binding-alist nil))

         ;; Goofy hack to make sure the instance names are unique:

         (instname (vl-plain-wire-name x.instname))

         (eocc (list* :u instname
                      :op sub-esim
                      :o outputs
                      :i inputs
                      (if x.atts
                          (list :a x.atts)
                        nil))))

      (mv t warnings eocc)))

  (local (in-theory (enable vl-modinst-to-eocc)))

  (defthm vl-warninglist-p-of-vl-modinst-to-eocc
    (let ((ret (vl-modinst-to-eocc x walist mods modalist eal warnings)))
      (implies (force (vl-warninglist-p warnings))
               (vl-warninglist-p (mv-nth 1 ret)))))

  (local (defthm l0
           (implies (vl-emodwirelist-p x)
                    (equal (atom-listp x)
                           (true-listp x)))
           :hints(("Goal" :induct (len x)))))

  (local (defthm l1
           ;; follows from similar-patternsp-of-al->pat
           (implies (and (subsetp-equal (pat-flatten1 pat) (alist-keys al))
                         (vl-emodwirelist-p (alist-keys al))
                         (vl-emodwirelist-p (alist-vals al)))
                    (similar-patternsp (al->pat pat al default) pat))))

  (defthm good-esim-occp-of-vl-modinst-to-eocc
    (let ((ret (vl-modinst-to-eocc x walist mods modalist eal warnings)))
      (implies (and (mv-nth 0 ret)
                    (force (vl-modinst-p x))
                    (force (vl-wirealist-p walist))
                    (force (vl-modulelist-p mods))
                    (force (equal modalist (vl-modalist mods)))
                    (force (vl-ealist-p eal)))
               (good-esim-occp (mv-nth 2 ret))))
    :hints(("Goal"
            :in-theory (enable good-esim-occp)
            :expand ((:free (a x) (good-esim-occp (cons a x))))))))





(defsection vl-modinstlist-to-eoccs
  :parents (modinsts-to-eoccs)
  :short "Build the preliminary E-language occurrences for a list of module
instances."

  :long "<p>We just extend @(see vl-modinst-to-eocc) across a list.</p>"

  (defund vl-modinstlist-to-eoccs (x walist mods modalist eal warnings)
    "Returns (MV SUCCESSP WARNINGS EOCCS)"
    (declare (xargs :guard (and (vl-modinstlist-p x)
                                (vl-wirealist-p walist)
                                (vl-modulelist-p mods)
                                (equal modalist (vl-modalist mods))
                                (vl-ealist-p eal)
                                (vl-warninglist-p warnings))))
    (b* (((when (atom x))
          (mv t warnings nil))
         ((mv car-successp warnings car-eocc)
          (vl-modinst-to-eocc (car x) walist mods modalist eal warnings))
         ((mv cdr-successp warnings cdr-eoccs)
          (vl-modinstlist-to-eoccs (cdr x) walist mods modalist eal warnings)))
      (mv (and car-successp cdr-successp)
          warnings
          (cons car-eocc cdr-eoccs))))

  (defmvtypes vl-modinstlist-to-eoccs (booleanp nil true-listp))

  (local (in-theory (enable vl-modinstlist-to-eoccs)))

  (defthm vl-warninglist-p-of-vl-modinstlist-to-eoccs
    (let ((ret (vl-modinstlist-to-eoccs x walist mods modalist all-walists warnings)))
      (implies (force (vl-warninglist-p warnings))
               (vl-warninglist-p (mv-nth 1 ret)))))

  (defthm good-esim-occsp-of-vl-modinstlist-to-eoccs
    (let ((ret (vl-modinstlist-to-eoccs x walist mods modalist eal warnings)))
      (implies (and (mv-nth 0 ret)
                    (force (vl-modinstlist-p x))
                    (force (vl-wirealist-p walist))
                    (force (vl-modulelist-p mods))
                    (force (equal modalist (vl-modalist mods)))
                    (force (vl-ealist-p eal)))
               (good-esim-occsp (mv-nth 2 ret))))
    :hints(("Goal" :in-theory (enable good-esim-occsp)))))





