Ports in groups - intel/device-modeling-language GitHub Wiki

We want to permit group g { port p { port q; } }; here's some design sketches for how to do this.

Initial attempts

We have some different options, which I have parameterized by how you expect the port prefix to appear in the port object name.

dev.port.g.p.q

The idea is that all ports appear under the dev.port namespace, so the g prefix appears after that.

A sibling bank b would appear as a second cousin, in dev.bank.g.b. For this reason, we need two params to define prefix, param port_portname and bank_portname, defaulting to parent.objkind == "device" #? "port." + name : name (s/port/bank/ for bank_portname). Both params are available on groups, banks and ports, and the full name of a port is the sum of obj.port_portname for all objects in the hierarchy, so in:

group g {
  port_portname = "x.g";
  bank b {
    param port_portname = "y.b";
    port p {
      param port_portname = "z.p";
}}}

... you will get dev.x.g.y.b.z.p and dev.bank.g.b.

We have largely dismissed this idea, since it gets too complicated in practice (tried a few different approaches and the above sketch is the simplest we could find). Including it here for completeness.

dev.g.p.q

The idea here is that the port prefix only appears directly under dev, as a common special case for top-level port objects. No prefix is added for any other objects; you generally create group indirections to control the namespace.

A single param portname is available, in port, bank and group objects (not strictly necessary in groups). Its primary purpose is to suppress port. on top level, and defaults to parent.objkind == "device" #? "port." + name #: name in ports, the same with s/port/bank/ in banks, and name in groups.

group g {
  param portname = "x.g";
  bank b {
    param portname = "y.b";
    port p {
      param portname = "z.p";
}}}

then the bank's name is dev.x.g.y.b and the port's name is dev.x.g.y.b.z.p.

A problem with this approach is that we stop encouraging the .port naming convention in subobjects; it's up to each modeler how to apply .port naming conventions which leads to inconsistency.

dev.g.port.p.port.q

The idea here is to have an easy-to-explain rule that "all ports are prefixed with 'port.' by default". I.e., the portname param works as in the previous design, but the default value is "port." + name in ports, "bank." + name in banks, and name in groups. This is a simple rule that arguably is a natural generalization of current behaviour, but it's probably seldom what you want so it will be common to override the param with name.

dev.g.port.p.q

The idea here is that any port should generally be marked as such using a port prefix, but you can put a port in a group to group it with other ports as a subsystem of a device, and you can also create a hierarchy of groups by putting a port in a port. The default value of portname is _nongroup_parent.objtype == "device" #? "port." + name #: name, where _nongroup_parent is an existing internal param.

This approach is rather nice, but the g subsystem is represented in Simics as a naked namespace object, so if it contains an attribute g.a for configuration or state, then that will be available only as dev.g_a; if you want the attribute to be exposed as dev.g.a, then you need to write port g { param portname = name; port p { param portname = "port.p"; { port q;, i.e. override portname both in g and g.p.

We could help this use case by instead defining a non-overrideable param portname = add_portname_prefix #? "port." + name #: name; and say that add_portname_prefix defaults to true iff it's false for all ancestor port and bank objects. I.e., the port. prefix is added automatically but never more than once in the hierarchy. However, this adds even more complexity to the definition.

Conclusion

The above designs are essentially heuristics to guess whether a port object represents a physical port to connect with other hardware (and deserves a .prefix), or if it primarily represents some sort of subdevice. Perhaps the best is to view subdevice and port as different kinds of object, i.e. subdevice d { port b; } gives a.port.b, and differs from group in that subdevice d { saved int a; } creates an attribute dev.d.a instead of dev.d_a.

With this kind of object, dev.g.port.p.port.q is the clear winner of the above alternatives: port.p.port.q may look strange, but that's because you probably wanted to write something like group g { subdevice p { port q.

Adding a new object type is a rather big change; this hasn't happened since around 2007. So, as a first step, I propose to add a template template subdevice_port is port that, when instantiated, suppresses the .port portion of the portname. Once this is proven commonly useful in real code, we can add a subdevice object type.