Search code examples
dml-lang

DML 1.4 registers imported in DML 1.2 faulty


I have discovered a v1.2 device which imports registers declared in v1.4. This worried me at first sight, but the device compiles to my surprise.

The register methods in the device turned out to be unused by the model for outside register writes/reads which makes the model useless.

The register declaration looks as follows:

register REGISTER {
        method write_register(uint64 value, uint64 enabled_bits, void *aux) {
                ...
        }
        method read_register(uint64 enabled_bits, void* aux) -> (uint64) {
                ...
        }
}

Following the DML 1.2 compatibility reference I have come up with this version:

register REGISTER {
        is (read, write, register)
        method write(uint64 value) {
                ...
        }
        method read() -> (uint64) {
                ...
        }
}

Which resulted in the same outcome with a following compilation warning: warning: unused: write methods are not called automatically for register objects in bank.REGISTER.write

An update to v1.4 will take too much time. A downgrade to v1.2 might be possible.

Is there a solution and is the v1.4 import in v1.2 even officially supported?


Solution

  • Mixing 1.2 and 1.4 within one device happens in a phase while migrating from 1.2 to 1.4. The idea is that you can migrate common code to 1.4 first, one file at a time, and when all common code is 1.4 you can also move devices to 1.4.

    A DML 1.2 device uses the DML 1.2 standard library, where register methods have names like read_access, write_access, before_read and after_read, whereas a DML 1.4 device uses the DML 1.4 standard library where register methods have names like read_register or write_register. Indeed, in your case the read_register implementation in common 1.4 code will have no effect when imported from a 1.2 device.

    However, we provide a library of compatibility magic that allows idiomatic 1.4 code to work better when imported from a 1.2 device. The magic is defined in dml12-compatibility.dml, and in this case you want the dml12_compat_read_register and dml12_compat_write_register templates:

    import "dml12-compatibility.dml";
    register REGISTER is (dml12_compat_read_register, dml12_compat_write_register) {
        method write_register(uint64 value, uint64 enabled_bits, void *aux) {
            ...
        }
        method read_register(uint64 enabled_bits, void* aux) -> (uint64) {
            ...
        }
    }
    

    These templates override expand to nothing when used from DML 1.4, and override some appropriate standard method from DML 1.2 when used from DML 1.2.

    An alternative solution is to do the same manually with conditional code, using #if (dml_1_2) to detect whether we use the 1.2 or 1.4 standard library, and use that to choose which method to override:

    register REGISTER {
      #if (dml_1_2) {
        method write_access(memop, msb1, lsb, value) { ... }
      } #else {
        method write_register(uint64 value, uint64 enabled_bits, void *aux) {
           ...
        }
      }
    }
    

    The predefined magic templates are usually the better alternative for common methods like write_register and read_register, but the above technique is generally applicable for some other less methods where we don't provide compatibility magic.