toffe
toffe

Reputation: 63

Register array with a field in only one of the registers in DML 1.4

I want to make a register array, where one of the registers should include a field in bit 0 with a value of 1.

I have tried using a conditional without any success.

register feature_bits[i < N_FEATURE_SELECT_REGS] size 4 is unmapped {
    #if (i == 1) {
        field virtio_f_version_1 @ [0] "VIRTIO_F_VERSION_1" {
            param init_val = 1;
        }
    }
}

I have also tried indexing the register element and set the field accordingly

register feature_bits[i < N_FEATURE_SELECT_REGS] size 4 is unmapped;
register feature_bits[1] {
    field VIRTIO_F_VERSION_1 @ [0] {
        param init_val = 1;
    }
}

None of these approaches worked.

Upvotes: 1

Views: 90

Answers (2)

SvenAron
SvenAron

Reputation: 121

If we take a step back and look at what you're trying to accomplish here, it's possible that a third option can be usable. If it's only the init-value of some fields that differs, you can create a template type and iterate over it like this:

template feature_bit {
    param lsb : uint64;
}

bank regs is (init, hard_reset) {
    register features[i < 4] size 4 @ unmapped;

    method init() {
        foreach f in (each feature_bit in (this)) {
            features[f.lsb / 32].val[f.lsb % 32] = 1;
        }
    }

    method hard_reset() {
        init();
    }

    group featA is feature_bit { param lsb = 42; }
    group featB is feature_bit { param lsb = 3; }
}

The init-template is depth-first recursive, so the feature registers will be initialized first, and then the init() method of the bank will run and set the value of the feature-registers by iterating over all instances of the feature_bit template. We also call init from hard_reset, which is also depth-first recursive, otherwise the register will be 0 after reset.

Upvotes: 1

Erik Carstensen
Erik Carstensen

Reputation: 898

Arrays in DML are homogeneous; every subobject must exist for all indices. This is because when you write a method inside an array, each array index translates to an implicit method argument, so in your case, if a method in your register calls this.virtio_f_version.read(), this translates to something like regs__feature_bits__virtio_f_version_1__read(_dev, _i). This function exists for all values of i, therefore the field must exist for all values of i.

There are two approaches to solve your problem. The first is to let the field exist in all indices, and add code to make it pretend to not exist in indices other than 1:

register feature_bits[...]; {
  field VIRTIO_F_VERSION_1 @ [0] {
    param init_val = i == 1 ? 1 : 0;
    method write(uint64 value) {
      if (i == 1) {
        return set_f_version_1(value);
      } else if (value != this.val) {
        log spec_viol: "write outside fields";
      }
    }
  }
}

The second approach is to accept that your set of registers is heterogeneous, and use a template to share code between them instead:

template feature_bit {
  param i;
  #if (i == 1) {
    field virtio_f_version ... { ... }
  }
  ... lots of common fields here ...
}
register feature_bits_0 is feature_bit { param index = 0; }
register feature_bits_1 is feature_bit { param index = 1; }
register feature_bits_2 is feature_bit { param index = 2; }
...

Which approach to choose is a trade-off; the first solution is more economic in terms of memory use and compile speed (because DML doesn't need to create almost-identical copies of methods across register indices), whereas the second solution gives a model that more accurately reflects the specification upon inspection (because the model will declare to the simulator that virtio_f_version_1 is a field only for one of the registers).

If you read the spec and you get the feeling that this field is a singular exception to an otherwise homogeneous array, then the first approach is probably better, but if registers vary wildly across indices then the second approach probably makes more sense.

Upvotes: 1

Related Questions