;Example 1: special memmodule with descriptors
; - feels not firrtly
; - if you are representing the semantics this way, why not just represent them via FIRRTL? you already have a mechanism to do this
; - the type of m is not anywhere in the code, and requires looking at documentation
circuit top :
   memmodule CMEM_2R_2W_1RW :
      read-ports = 2
      write-ports = 2
      readwrite-ports = 1
      read-latency = 0
      write-latency = 1
   module Top :
      mem m of CMEM_2R_2W_1RW : UInt<32>[4],128
      ;...

;Example 2: special memmodule with typed parameters
; - must specify read/write latency at instantiation
; - can't easily parameterize type of mask...
; - introduces new concept of type parameterization which is NOT what we want to do
circuit top :
   memmodule MEM_2R_2W_1RW (T, size, rlatency, wlatency):
      output read : {data : T, flip en : UInt<1>, flip clk : Clock}[2]
      input write : {data : T, en : UInt<1>, clk : Clock}[2]
      input rdwr : {rdata : T, ren : UInt<1>, wdata : T, wen : UInt<1>, clk : Clock}[1]
      input mask : T ;WRONG, needs to be
   module Top :
      mem m of MEM_2R_2W_1RW (UInt<32>[4],0,1)
      ;...

;Example 3: black box/normal module memories
; - need new cmem for every type and width
; - either annotation gives semantic information, which is bad, or...
; - behavioral FIRRTL also gives semantic information, which is bad and brittle to infer
circuit
   module cmem : @cmem_2r_2w_1rw
      output read : {data : UInt<32>[4], flip en : UInt<1>, flip clk : Clock}[2]
      input write : {data : UInt<32>[4], en : UInt<1>, clk : Clock}[2]
      input rdwr : {rdata : UInt<32>[4], ren : UInt<1>, wdata : UInt<32>[4], wen : UInt<1>, clk : Clock}[1]
      input mask : UInt<1>[4]
   module Top :
      mem m of cmem : UInt<32>[4],128
      ;...

;Example 4: special memstmt with descriptors
; - feels not firrtly
; - the type of m is not anywhere in the code, and requires looking at documentation
circuit top :
   module Top :
      mem m : UInt<32>[4],128,2,2,1,0,1 ;datatype,size,#rdports,#wrports,#rwports,rlat,wlat
      ;...


; All m's have type: 
   {
      read  : {flip data : UInt<32>[4], en : UInt<1>, index : UInt<7>, clk : Clock}[2]
      write : {data : UInt<32>[4], en : UInt<1>, index : UInt<7>, clk : Clock, mask : UInt<1>[4]}[2]
      rdwr  : {flip rdata : UInt<32>[4], ren : UInt<1>, wdata : UInt<32>[4], wen : UInt<1>, index : UInt<7>, clk : Clock, mask : UInt<1>[4]}[1]
   }


Changes to compiler:
Resolve genders: is much easier because (1) don't need to infer ports, and (2) is no longer fixed-point algorithm
Expand accessors: now makes an expression indexer
Lowering: Need to special case memories
Need to do cse early on, definitely before inline indexers
Expand whens: need to special case memories and their subfields.. ugh.






