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.
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.
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;
}
}
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.
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!!!