Elastic Silicon Interconnect (ESI) modeling

First for those of you who don’t know about ESI, this is the short description I used pre-CIRCT:

The Elastic Silicon Interconnect project is fundamentally an advanced silicon (FPGA/ASIC) interconnect generator which raises the level of abstraction for inter/intra/CPU communication. A hardware-centric type system enables strong type safety. Latency insensitive (elastic) connections by default allows the compiler to make optimization decisions. Essentially, the intent is to separate/abstract the wire signaling layer from the message layer and allow designers to not worry about the wire signaling layer.

In the CIRCT context, I’m planning on modeling this using three mechanisms:

  1. A set of “high-level” types intended to be used in other dialects and hand-coded RTL. This will supplement the std dialect types and the eventual RTL dialect types (enums, structs, unions, arrays). The ESI types will be arbitrary precision floating/fixed points, variable sized lists, strings, and whatever else ends up making sense.
  2. Another set of MLIR types called ESI Ports which wrap other types and used to indicate that an argument or return is an ESI connection and has different semantics. The first ESI connection type I’ll be implementing is channel to model a point-to-point stream of messages (values of the wrapped type). Example: rtl.module @foo(%a: !esi.channel<i4>) {...} would indicate a module foo which has a single input port, an ESI channel which transmits i4. By default, a channel would become a latency-insensitive connection – data paired with a valid signal and some notion of backpressure (e.g. a ready signal).
  3. A set of Ops which operate on ESI connections. These will include things like CDC, gearboxes, perf counters, etc. It will also include a cosim bridge op to connect a simulation to an external piece of software.

Here’s an example system fragment (not checked for syntactic correctness):

module {
  // Sums the inputs. Outputs the running sum on each input.
  rtl.module @sum(%v: !esi.chan<i2>) -> (!esi.chan<i64>) {...}

  // An external piece of software drives a stream of i2s into the system and we feed back i64s.
  %inputChan = esi.cosim(%sum) : !esi.chan<i2>
  // Insert a counter to count the number of tokens on this stream.
  %inputChanCounted = esi.perfctr %inputChan
  // Funnel to @adder
  %sum = rtl.instance "summer" @sum(%inputChanCounted) : !esi.chan<i2> -> !esi.chan<i64>

So my question is: does this seem like a reasonable way to model this? Any suggestions? Does anyone foresee any problems with this model?