6 Building Well-Behaved Models 8 Simics Model Development Checklist
Model Builder User's Guide  /  II Device Modeling  / 

7 DML Tips and Tricks

This chapter contains some tips and tricks which are useful when developing with DML. It also warns about some common errors which are easy to do, but which can be hard to track down if you are not aware of them.

7.1 Register instead of attributes

It can sometimes be convenient to use registers instead of attributes. Each register is represented as a Simics attribute and an unmapped register works the same way as an integer attribute. For example, a register can be divided into fields which is not possible for an attribute. A register is unmapped if its offset is undefined, this can be done by using the template unmapped for the register. Note also that the default reset behavior differ between attributes and registers: A register will by default be restored to its hard_reset_value upon a hard reset, while an integer attribute won't.

7.2 Misspelled identifiers

Misspelling an identifier in DML can be very frustrating. The DML compiler removes all unused code when generating C code. A misspelled identifier can be considered as unused code by the DML compiler. Assume you want to override the default read method but you misspell read. Your new read method will be removed as it is considered as unused code. Be careful when overriding methods and parameters.

register a is (unmapped, read) {
    method read() -> (value) {
        value = 0x10101;
    }
}

7.3 Alias registers

An alias register is a register which contains another register's value. Below is an example how this can be handled using an alias template:

bank regs {
    register X size 4 @ 0x00 "the X register";
    register Y size 4 @ 0x04 is alias { param alias_reg = X; }
}

template alias is (register, desc) {
    param alias_reg;
    param desc = "alias of " + alias_reg.name;

    method read_register(uint64 enabled_bytes, void *aux)-> (uint64) {
        log info, 4: "Redirecting read access to %s", alias_reg.qname;
        return alias_reg.read_register(enabled_bytes, aux);
    }

    method write_register(uint64 value, uint64 enabled_bytes, void *aux) {
        log info, 4: "Redirecting write access to %s", alias_reg.qname;
        alias_reg.write_register(value, enabled_bytes, aux);
    }
}
This causes the Y register to become an alias register to X. The Y register does not hold any state of its own; instead it redirects any accesses to the X register.

7.4 Field set order

Fields in register are accessed least significant field first. Here is an example of a little endian device:

bank regs {
    method triggered() {
        log info: "pow!!!";
    }
    register r size 4 @ 0x0000 {
        field Trigger @ [0] is write {
            method write(uint64 value) {
                log info: "Writing Trigger";
                if (Enabled.val)
                    triggered();
            }
        }
        field Enabled @ [1] is write {
            method write(uint64 value) {
                this.val = value;
                log info: "Writing Enabled";
            }
        }
    }
}
Writing to the register r will result in this output:

simics> phys_mem.write 0x0 3 -l
[tst info] Writing Trigger
[tst info] Writing Enabled

The Trigger field is accessed first and then the Enabled field, writing 1 to both. The intention was to trigger the pow!!! output, but Enabled is set after Trigger and is therefore still 0. In this situation it is better to use the write method and hook in our side-effect after the write is executed:

bank regs {
    method triggered() {
        log info: "pow!!!";
    }
    register r size 4 @ 0x0000 is write {
        field Trigger @ [0] is write {
            method write(uint64 value) {
                this.val = value;
                log info: "Writing Trigger";
            }
        }
        field Enabled @  [1] is write {
            method write(uint64 value) {
                this.val = value;
                log info: "Writing Enabled";
            }
        }
        method write(uint64 mop) {
            default(mop);
            if (Enabled.val && Trigger.val) {
                Trigger.val = 0;
                triggered();
            }
        }
    }
}

Now it behaves as expected:

simics> phys_mem.write 0x0 3 -l
[tst info] Writing Trigger
[tst info] Writing Enabled
[tst info] pow!!!
6 Building Well-Behaved Models 8 Simics Model Development Checklist