Skip to content

Commit 25d7ff0

Browse files
committed
Extend port-dml documentation
1 parent 8cd62db commit 25d7ff0

File tree

1 file changed

+113
-17
lines changed

1 file changed

+113
-17
lines changed

doc/1.4/port-dml.md

Lines changed: 113 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -5,25 +5,25 @@
55

66
# Porting DML 1.2 to DML 1.4
77
When porting a DML 1.2 file to DML 1.4, most differences can be taken
8-
care of by the automatic conversion script `port-dml`. The
9-
script relies on the `dmlc` compiler to produce information on
10-
what changes need to be applied.
8+
care of by the automatic conversion script `port-dml`.
119

12-
The easiest way to invoke `port-dml` is through a wrapper
13-
script `port-dml-module`. The script ports all devices in one
14-
SIMICS module, and all imported files it depends on. The scripts works by
15-
invoking `make` and `port-dml`, and prints how they are invoked, which
16-
is instructive for understanding how to use `port-dml`
17-
standalone.
18-
19-
The `port-dml-module` script works well for converting most DML
20-
devices, but has some limitations when it comes to common code that is
21-
shared between many devices. In particular, if some parts of common
22-
code is unused, e.g. if a provided template is never instantiated,
23-
then it may skip some conversions. For this reason, it can be better
24-
to use `port-dml` directly for common code.
10+
The porting process consists of two phases: An *analysis phase*, where the
11+
`dmlc` compiler is run to produce a *tag file*, which describes the required
12+
changes; this is followed by a *conversion phase* where the `port-dml`
13+
script reads the tag file and applies changes.
2514

15+
The easiest way to perform conversion is through the wrapper script
16+
`port-dml-module`. This script automatizes the porting process by first running an
17+
analysis step using `make` to build a module, and then running a conversion phase by
18+
applying `port-dml` on the DML files that were compiled. The script relies on
19+
rather crude heuristics which often may be
20+
incorrect; for this reason, the script also prints exactly what lower level
21+
commands it invokes. This allows each step to be individually rerun manually
22+
with tweaked settings.
2623

24+
The `port-dml` script can also be used directly without `port-dml-module`; this
25+
mode of operation has a steeper learning curve but provides greater control
26+
which can be advantageous when porting a large code base.
2727

2828
## Using the port-dml script
2929
In order to port a DML 1.2 file to DML 1.4, first pass the <code>-P
@@ -54,7 +54,7 @@ If you build your device from a Simics project, you can use the variable
5454
be set to the absolute path of a file; `make` will pass that in the
5555
-P flag to `dmlc`. Note that if you want to re-run an analysis,
5656
then you need to first run <code>make clean-<em>module</em></code> to force
57-
DMLC to re-run on all devices in the SIMICS module.
57+
DMLC to re-run on all devices in the Simics module.
5858

5959
If parts of the device source code is unused, e.g. if a template is
6060
never instantiated within the device, then DMLC can not perform a full
@@ -75,3 +75,99 @@ tags](changes-auto.html) and apply the change manually.
7575

7676
</div>
7777

78+
## Porting common code still used from DML 1.2 code
79+
80+
When porting a large code base to DML 1.4, you likely want to work
81+
incrementally, porting some devices at a time. It can then happen that some of
82+
your newly ported 1.4 files share common code with devices that are still in DML 1.2.
83+
84+
It is not allowed to import a DML 1.2 file from a DML 1.4 device, but a DML 1.2 device may import a DML 1.4 file with some caveats. Thus, any code common between DML 1.2 and 1.4 must be ported to 1.4 before any device can be converted.
85+
There are two possible strategies for this: Either convert the common file in place,
86+
or duplicate it into separate 1.2 and 1.4 versions.
87+
88+
### Keep a separate DML 1.4 copy
89+
90+
After letting conversion tools convert `foo.dml` to DML 1.4, you can rename the
91+
converted file into `foo-dml14.dml`, and restore the original 1.2 version as
92+
`foo-dml12.dml`, and finally create a trampoline file `foo.dml` containing:
93+
94+
```
95+
dml 1.4;
96+
#if (dml_1_2) {
97+
import "foo-dml12.dml";
98+
} #else {
99+
import "foo-dml14.dml";
100+
}
101+
```
102+
103+
This way, existing `import "foo.dml";` statements from both DML 1.2 and 1.4 devices
104+
will continue to work.
105+
106+
The apparent downside of this approach is that the logic of the common code is
107+
duplicated across two files, which is a problem if the DML 1.2 variant is expected
108+
to be maintained over a longer period of time. However, if all uses from DML 1.2 of
109+
the common code are expected to be ported within a short migration period,
110+
then this is likely the preferred approach.
111+
112+
After the last DML 1.2 use of the common code has been ported, `foo-dml12.dml` can be removed, and `foo-dml14.dml` can be moved back to `foo.dml`, overwriting the trampoline.
113+
114+
Note that the `#if` trick in the `foo.dml` trampoline above utilizes an
115+
otherwise undocumented DML feature: DML normally doesn't allow `import`
116+
statements within `#if` blocks, but a special exception was added to permit it
117+
specifically within `#if (dml_1_2)`, in order to support this use case.
118+
119+
### In-place conversion, preserving DML 1.2 compatibility
120+
121+
A common file can be ported to DML 1.4 and still be useful from DML 1.2, with a
122+
number of caveats. For instance, devices often implement functionality by
123+
overriding standard methods, and some methods have been renamed between DML 1.2
124+
and 1.4. For instance, an override of the `read_access` register method in a
125+
DML 1.2 device roughly corresponds to a `read_register` override in a DML 1.4
126+
device, and an attribute with `parameter allocate_type = "uint64"` in DML 1.2
127+
corresponds to an event with `is uint64_attr` in DML 1.4. Much of this can be
128+
taken care of by the `dml12-compatibility.dml` layer: A shared DML 1.4 file can
129+
say `import "dml12-compatibility.dml";`. This does nothing when imported from a
130+
DML 1.4, but when imported from DML 1.2, it provides some glue that ties DML
131+
1.4 constructs to the DML 1.2 API. For instance, it defines templates such that
132+
`is uint64_attr` in the DML 1.4 file will expand to define `allocate_type` when
133+
imported from DML 1.2. This file also provides some templates for explicit
134+
instantiation. In particular, the `dml12_compat_read_register` template can be
135+
instantiated on a DML 1.4 register that overrides the `read_register` method;
136+
this has no effect in a DML 1.4 device, but in a DML 1.2 device it overrides
137+
the DML 1.2 method `read_access` to call the provided override. Similarly,
138+
the `dml12_compat_write_register` template can be used on registers that override
139+
`write_register`; `dml12_compat_read_field` and
140+
`dml12_compat_write_field` can be used on field that override the `read_field` or `write_field` method; and `dml12_compat_io_memory_access` can be used on banks that override the `io_memory_access` method.
141+
142+
Sometimes, the facilities in `dml12-compatibility.dml` are not sufficient for
143+
full DML 1.2 compatibility. For instance, suppose you want to use the `shared` annotation on a `read` method when writing the DML 1.4 version of a template. There are fundamental limitations in DML 1.2 that prevent such overrides. This can be overcome with an `#if (dml_1_2)` block on the top level:
144+
```
145+
dml 1.4;
146+
147+
#if (dml_1_2) {
148+
template read_twelve {
149+
method read() -> (uint64) {
150+
log info: "read";
151+
return 12;
152+
}
153+
}
154+
} #else {
155+
template read_twelve is read {
156+
shared method read() -> (uint64) {
157+
log info: "read";
158+
return 12;
159+
}
160+
}
161+
}
162+
```
163+
This is somewhat similar to the `foo-dml14.dml` trampoline approach discussed above, with
164+
the difference that it can be applied selectively only on problematic parts of the
165+
file.
166+
167+
If the flag `--compat` is passed to the `port-dml` script, then the script will
168+
automatically detect some cases where such `#if` clauses are needed for
169+
compatibility, and insert them automatically. The script will also add an
170+
`dml12-compatibility.dml` import. The `--compat` flag can also be passed to the
171+
`port-dml-module` script; in this case, the script will pass on `--compat` to
172+
`port-dml` when converting DML files that don't reside in the directory of any
173+
of the ported modules.

0 commit comments

Comments
 (0)