You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
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`.
11
9
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.
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.
18
14
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.
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 one or more modules, and then running a
18
+
conversion phase by applying `port-dml` on the DML files that were compiled.
25
19
20
+
During the analysis phase, if some code is shared between multiple modules,
21
+
then it often happens that some unused code is discarded by the compiler before
22
+
it has been fully analysed. This happens in particular for unused templates and
23
+
top-level `if` statements. Such unused code will only get basic syntactic
24
+
conversions, like transforming `parameter` to `param`, and miss conversions
25
+
that depend on semantic analysis, like transforming `read_access`
26
+
to `read_register`. This
27
+
can be solved by including multiple modules in the analysis step; the
28
+
conversion phase for common code will then combine the analyses of all modules,
29
+
and utilize semantic analysis from any code paths that is active in *any* of the
30
+
included modules.
26
31
32
+
The `port-dml-module` script relies on
33
+
rather crude heuristics which often may be
34
+
incorrect; for this reason, the script also prints exactly how it invokes the
35
+
lower level `make` and `port-dml` commands. This allows each step to be
36
+
individually rerun manually with tweaked settings.
37
+
38
+
The `port-dml` script can also be used directly without `port-dml-module`; this
39
+
mode of operation has a steeper learning curve but provides greater control
40
+
which can be advantageous when porting a large code base.
27
41
28
42
## Using the port-dml script
29
43
In order to port a DML 1.2 file to DML 1.4, first pass the <code>-P
@@ -54,7 +68,7 @@ If you build your device from a Simics project, you can use the variable
54
68
be set to the absolute path of a file; `make` will pass that in the
55
69
-P flag to `dmlc`. Note that if you want to re-run an analysis,
56
70
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.
71
+
DMLC to re-run on all devices in the Simics module.
58
72
59
73
If parts of the device source code is unused, e.g. if a template is
60
74
never instantiated within the device, then DMLC can not perform a full
@@ -75,3 +89,99 @@ tags](changes-auto.html) and apply the change manually.
75
89
76
90
</div>
77
91
92
+
## Porting common code still used from DML 1.2 code
93
+
94
+
When porting a large code base to DML 1.4, you likely want to work
95
+
incrementally, porting some devices at a time. It can then happen that some of
96
+
your newly ported 1.4 files share common code with devices that are still in DML 1.2.
97
+
98
+
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.
99
+
There are two possible strategies for this: Either convert the common file in place,
100
+
or duplicate it into separate 1.2 and 1.4 versions.
101
+
102
+
### Keep a separate DML 1.4 copy
103
+
104
+
After letting conversion tools convert `foo.dml` to DML 1.4, you can rename the
105
+
converted file into `foo-dml14.dml`, and restore the original 1.2 version as
106
+
`foo-dml12.dml`, and finally create a trampoline file `foo.dml` containing:
107
+
108
+
```
109
+
dml 1.4;
110
+
#if (dml_1_2) {
111
+
import "foo-dml12.dml";
112
+
} #else {
113
+
import "foo-dml14.dml";
114
+
}
115
+
```
116
+
117
+
This way, existing `import "foo.dml";` statements from both DML 1.2 and 1.4 devices
118
+
will continue to work.
119
+
120
+
The apparent downside of this approach is that the logic of the common code is
121
+
duplicated across two files, which is a problem if the DML 1.2 variant is expected
122
+
to be maintained over a longer period of time. However, if all uses from DML 1.2 of
123
+
the common code are expected to be ported within a short migration period,
124
+
then this is likely the preferred approach.
125
+
126
+
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.
127
+
128
+
Note that the `#if` trick in the `foo.dml` trampoline above utilizes an
A common file can be ported to DML 1.4 and still be useful from DML 1.2, with a
136
+
number of caveats. For instance, devices often implement functionality by
137
+
overriding standard methods, and some methods have been renamed between DML 1.2
138
+
and 1.4. For instance, an override of the `read_access` register method in a
139
+
DML 1.2 device roughly corresponds to a `read_register` override in a DML 1.4
140
+
device, and an attribute with `parameter allocate_type = "uint64"` in DML 1.2
141
+
corresponds to an event with `is uint64_attr` in DML 1.4. Much of this can be
142
+
taken care of by the `dml12-compatibility.dml` layer: A shared DML 1.4 file can
143
+
say `import "dml12-compatibility.dml";`. This does nothing when imported from a
144
+
DML 1.4, but when imported from DML 1.2, it provides some glue that ties DML
145
+
1.4 constructs to the DML 1.2 API. For instance, it defines templates such that
146
+
`is uint64_attr` in the DML 1.4 file will expand to define `allocate_type` when
147
+
imported from DML 1.2. This file also provides some templates for explicit
148
+
instantiation. In particular, the `dml12_compat_read_register` template can be
149
+
instantiated on a DML 1.4 register that overrides the `read_register` method;
150
+
this has no effect in a DML 1.4 device, but in a DML 1.2 device it overrides
151
+
the DML 1.2 method `read_access` to call the provided override. Similarly,
152
+
the `dml12_compat_write_register` template can be used on registers that override
153
+
`write_register`; `dml12_compat_read_field` and
154
+
`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.
155
+
156
+
Sometimes, the facilities in `dml12-compatibility.dml` are not sufficient for
157
+
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:
158
+
```
159
+
dml 1.4;
160
+
161
+
#if (dml_1_2) {
162
+
template read_twelve {
163
+
method read() -> (uint64) {
164
+
log info: "read";
165
+
return 12;
166
+
}
167
+
}
168
+
} #else {
169
+
template read_twelve is read {
170
+
shared method read() -> (uint64) {
171
+
log info: "read";
172
+
return 12;
173
+
}
174
+
}
175
+
}
176
+
```
177
+
This is somewhat similar to the `foo-dml14.dml` trampoline approach discussed above, with
178
+
the difference that it can be applied selectively only on problematic parts of the
179
+
file.
180
+
181
+
If the flag `--compat` is passed to the `port-dml` script, then the script will
182
+
automatically detect some cases where similar `#if` clauses are needed for
183
+
compatibility, and insert them automatically. The script will also add an
184
+
`dml12-compatibility.dml` import. The `--compat` flag can also be passed to the
185
+
`port-dml-module` script; in this case, the script will pass on `--compat` to
186
+
`port-dml` when converting DML files that don't reside in the directory of any
0 commit comments