1 |
|
2 |
|
3 | const pro = require("util").promisify;
|
4 | const fs = require("fs");
|
5 | const xml2js = require("xml2js");
|
6 | const codeGen = require("./code-gen.js");
|
7 | const rmDir = require("./rmdir.js");
|
8 |
|
9 | module.exports = async config => {
|
10 |
|
11 | async function parseSvd(svdFile) {
|
12 | let svdData = await pro(fs.readFile)(svdFile, "utf8");
|
13 | return (await pro(new xml2js.Parser().parseString)(svdData)).device;
|
14 | }
|
15 |
|
16 | return {
|
17 | async init(cli) {
|
18 | return cli.command("svd <svdFile>");
|
19 | },
|
20 |
|
21 | async start(command, svdFile) {
|
22 |
|
23 | console.info("Generating sources from", svdFile);
|
24 |
|
25 | let device = await parseSvd(svdFile);
|
26 | let cpu = (device.cpu || [{ name: [] }])[0].name[0];
|
27 | if (!cpu) {
|
28 | console.info("Warning: Undefined cpu - assuming CM0");
|
29 | cpu = "CM0";
|
30 | }
|
31 |
|
32 | let stdDevice = await parseSvd(`${__dirname}/../arm-std-svd/ARM${cpu}.svd`);
|
33 | let sysTick = stdDevice.peripherals[0].peripheral.find(p => p.name[0] === "SysTick");
|
34 | device.peripherals[0].peripheral.push(sysTick);
|
35 |
|
36 | if (device.width[0] !== "32") {
|
37 | throw "SVD error: device.width is expected to be 32";
|
38 | }
|
39 |
|
40 | let nonDerived = [];
|
41 |
|
42 | device.peripherals[0].peripheral.forEach(peripheral => {
|
43 |
|
44 | if (!(peripheral.$ || {}).derivedFrom) {
|
45 |
|
46 | let derived = device.peripherals[0].peripheral.filter(dp => (dp.$ || {}).derivedFrom === peripheral.name[0]);
|
47 |
|
48 | let groupName = (peripheral.groupName || peripheral.name)[0];
|
49 |
|
50 | let typeName;
|
51 | if (derived.length > 0) {
|
52 | let allNames = derived.map(dp => dp.name[0]).concat(peripheral.name[0]).sort();
|
53 |
|
54 | if (allNames.some(n => !n.startsWith(groupName))) {
|
55 | typeName = allNames[0] + "_" + allNames[allNames.length - 1];
|
56 | } else {
|
57 | typeName = groupName + "_" + allNames[0].slice(groupName.length) + "_" + allNames[allNames.length - 1].slice(groupName.length);
|
58 | }
|
59 | } else {
|
60 | let name = peripheral.name[0];
|
61 | if (name.startsWith(groupName) && name !== groupName) {
|
62 | typeName = groupName + "_" + name.slice(groupName.length);
|
63 | } else {
|
64 | typeName = name;
|
65 | }
|
66 | }
|
67 |
|
68 | nonDerived.push({
|
69 | typeName,
|
70 | groupName,
|
71 | peripheral,
|
72 | derived
|
73 | });
|
74 | }
|
75 | });
|
76 |
|
77 | nonDerived.forEach(p => {
|
78 | if (!nonDerived.some(p2 => p2.groupName === p.groupName && p2 !== p)) {
|
79 | p.typeName = p.groupName;
|
80 | }
|
81 | p.typeName = p.typeName.toLowerCase();
|
82 | });
|
83 |
|
84 | function svdInt(element) {
|
85 | return parseInt(element[0]);
|
86 | }
|
87 |
|
88 | function inlineDescription(element) {
|
89 | return element.description[0].replace(/[ \r\n]+/g, " ");
|
90 | }
|
91 |
|
92 | function fieldOffset(field) {
|
93 | return svdInt(field.bitOffset);
|
94 | }
|
95 |
|
96 | function fieldWidth(field) {
|
97 | return svdInt(field.bitWidth);
|
98 | }
|
99 |
|
100 | await rmDir("generated");
|
101 |
|
102 | await pro(fs.mkdir)("generated");
|
103 |
|
104 | var sources = [];
|
105 | var symbols = {};
|
106 |
|
107 | var writes = nonDerived.map(type => {
|
108 | let fileName = "generated/" + type.typeName + ".cpp";
|
109 | sources.push(fileName);
|
110 |
|
111 | let code = codeGen();
|
112 |
|
113 | code.begin("namespace target {");
|
114 | code.begin("namespace", type.typeName, "{");
|
115 |
|
116 | code.begin("namespace reg {");
|
117 |
|
118 | type.peripheral.registers[0].register.forEach(register => {
|
119 |
|
120 | let registerSize = svdInt(register.size);
|
121 | if (registerSize !== 32) {
|
122 | throw `Register ${type.peripheral.name}.${register.name[0]} has size ${registerSize}`;
|
123 | }
|
124 |
|
125 |
|
126 | code.wl();
|
127 | code.begin("/**");
|
128 | code.wl(inlineDescription(register));
|
129 | code.end("*/");
|
130 | code.begin("class", register.name, "{");
|
131 |
|
132 | code.wl("volatile unsigned long raw;");
|
133 | code.wl("public:");
|
134 |
|
135 | code.begin("__attribute__((always_inline)) void operator= (unsigned long value) volatile {");
|
136 | code.wl("raw = value;");
|
137 | code.end("}");
|
138 | code.begin("__attribute__((always_inline)) operator unsigned long () volatile {");
|
139 | code.wl("return raw;");
|
140 | code.end("}");
|
141 |
|
142 |
|
143 |
|
144 | let vectors = {};
|
145 | register.fields[0].field.forEach(f1 => {
|
146 | let m1 = f1.name[0].match(/([a-zA-Z]+)([0-9]+)([a-zA-Z]*)$/);
|
147 | if (m1) {
|
148 | let prefix = m1[1];
|
149 | let suffix = m1[3];
|
150 | register.fields[0].field.forEach(f2 => {
|
151 | if (f1 !== f2) {
|
152 | let m2 = f2.name[0].match(/([a-zA-Z]+)([0-9]+)([a-zA-Z]*)$/);
|
153 | if (m2 && m2[1] === prefix && m2[3] === suffix) {
|
154 | let i1 = parseInt(m1[2]);
|
155 | let i2 = parseInt(m2[2]);
|
156 | let min = Math.min(i1, i2);
|
157 | let max = Math.max(i1, i2);
|
158 | let key = m1[1] + "#" + m1[3];
|
159 | let v = vectors[key];
|
160 | if (!v) {
|
161 | v = {
|
162 | min,
|
163 | max,
|
164 | prefix,
|
165 | suffix,
|
166 | fields: []
|
167 | };
|
168 | vectors[key] = v;
|
169 | } else {
|
170 | v.min = Math.min(v.min, min);
|
171 | v.max = Math.max(v.max, max);
|
172 | }
|
173 | v.fields[i1] = f1;
|
174 | v.fields[i2] = f2;
|
175 | }
|
176 | }
|
177 | });
|
178 | }
|
179 | });
|
180 |
|
181 | function writeAccessors(fieldName, bitOffset, bitWidth, description, firstIndex, lastIndex) {
|
182 |
|
183 | let indexed = firstIndex !== undefined;
|
184 |
|
185 | let valueRange = "value in range 0.." + (Math.pow(2, bitWidth) - 1);
|
186 | let indexRange = "index in range " + firstIndex + ".." + lastIndex;
|
187 | let mask = "0x" + (Math.pow(2, bitWidth) - 1).toString(16).toUpperCase();
|
188 |
|
189 | code.begin("/**");
|
190 | code.wl("Gets", description);
|
191 | if (indexed) {
|
192 | code.wl("@param", indexRange);
|
193 | }
|
194 | code.wl("@return", valueRange);
|
195 | code.end("*/");
|
196 | if (indexed) {
|
197 | code.begin("__attribute__((always_inline)) unsigned long", "get" + fieldName + "(int index) volatile {");
|
198 | code.wl("return (raw & (" + mask + " << " + bitOffset + ")) >> " + bitOffset + ";");
|
199 | } else {
|
200 | code.begin("__attribute__((always_inline)) unsigned long", "get" + fieldName + "() volatile {");
|
201 | code.wl("return (raw & (" + mask + " << " + bitOffset + ")) >> " + bitOffset + ";");
|
202 | }
|
203 | code.end("}");
|
204 |
|
205 | code.begin("/**");
|
206 | code.wl("Sets", description);
|
207 | if (indexed) {
|
208 | code.wl("@param", indexRange);
|
209 | }
|
210 | code.wl("@param", valueRange);
|
211 | code.end("*/");
|
212 | if (indexed) {
|
213 | code.begin("__attribute__((always_inline)) unsigned long", "set" + fieldName + "(int index, unsigned long value) volatile {");
|
214 | code.wl("raw = (raw & ~(" + mask + " << " + bitOffset + ")) | ((value << " + bitOffset + ") & (" + mask + " << " + bitOffset + "));");
|
215 | } else {
|
216 | code.begin("__attribute__((always_inline)) unsigned long", "set" + fieldName + "(unsigned long value) volatile {");
|
217 | code.wl("raw = (raw & ~(" + mask + " << " + bitOffset + ")) | ((value << " + bitOffset + ") & (" + mask + " << " + bitOffset + "));");
|
218 | }
|
219 | code.end("}");
|
220 | }
|
221 |
|
222 | Object.entries(vectors).forEach(([k, v]) => {
|
223 |
|
224 | let firstIsMarked;
|
225 | let firstIndex;
|
226 | let firstOffset;
|
227 | let firstDistance;
|
228 | let lastIndex;
|
229 |
|
230 | for (let c = 0; c < v.fields.length; c++) {
|
231 | let field = v.fields[c];
|
232 | if (field) {
|
233 | let bitOffset = fieldOffset(field);
|
234 |
|
235 | if (firstIndex === undefined) {
|
236 | firstIndex = c;
|
237 | firstOffset = bitOffset;
|
238 | } else {
|
239 | if (firstDistance === undefined) {
|
240 | firstDistance = bitOffset - firstOffset;
|
241 | }
|
242 | let expectedOffset = firstOffset + firstDistance * (c - firstIndex);
|
243 | if (expectedOffset === bitOffset) {
|
244 | if (!firstIsMarked) {
|
245 | v.fields[firstIndex].inVector = v;
|
246 | firstIsMarked = true;
|
247 | }
|
248 | field.inVector = v;
|
249 | lastIndex = c;
|
250 | } else {
|
251 | v.fields[c] = undefined;
|
252 | }
|
253 | }
|
254 | }
|
255 | }
|
256 |
|
257 |
|
258 | if (firstIsMarked) {
|
259 | let field = v.fields[firstIndex];
|
260 | let fieldName = field.inVector.prefix + (field.inVector.suffix ? "_" + field.inVector.suffix : "");
|
261 | writeAccessors(
|
262 | fieldName,
|
263 | "(" + firstOffset + " + " + firstDistance + " * (index - " + firstIndex + "))",
|
264 | fieldWidth(field),
|
265 | inlineDescription(field),
|
266 | firstIndex,
|
267 | lastIndex
|
268 | );
|
269 | writeAccessors(fieldName, fieldOffset(field), fieldWidth(field) * (lastIndex - firstIndex + 1), inlineDescription(field));
|
270 | }
|
271 |
|
272 | });
|
273 |
|
274 | register.fields[0].field.filter(field => !field.inVector).forEach(field => {
|
275 | writeAccessors(field.name, fieldOffset(field), fieldWidth(field), inlineDescription(field));
|
276 | });
|
277 |
|
278 | code.end("};");
|
279 | });
|
280 | code.end("};");
|
281 |
|
282 | code.begin("class Peripheral {");
|
283 | code.wl("public:");
|
284 | code.begin("union {");
|
285 |
|
286 | type.peripheral.registers[0].register.forEach(register => {
|
287 |
|
288 | let regOffset = svdInt(register.addressOffset);
|
289 |
|
290 | code.begin("struct {");
|
291 | if (regOffset > 0) {
|
292 | code.wl(`volatile char _space_${register.name}[${regOffset}];`);
|
293 | }
|
294 |
|
295 | code.begin("/**");
|
296 | code.wl(inlineDescription(register));
|
297 | code.end("*/");
|
298 | code.wl(`volatile reg::${register.name} ${register.name};`);
|
299 |
|
300 | code.end("};");
|
301 | });
|
302 |
|
303 | code.end("};");
|
304 | code.end("};");
|
305 |
|
306 | code.end("}");
|
307 |
|
308 | code.wl();
|
309 |
|
310 | [type.peripheral].concat(type.derived).forEach(p => {
|
311 | let symbol = p.name[0].toUpperCase();
|
312 | code.wl("extern " + type.typeName + "::Peripheral", symbol + ";");
|
313 | symbols["_ZN6target" + symbol.length + symbol + "E"] = p.baseAddress[0];
|
314 | });
|
315 |
|
316 | code.end("}");
|
317 |
|
318 | return code.toFile(fileName);
|
319 | });
|
320 |
|
321 | await Promise.all(writes);
|
322 |
|
323 | let package = JSON.parse(await pro(fs.readFile)("package.json", "utf8"));
|
324 |
|
325 | let interrupts = {};
|
326 | device.peripherals[0].peripheral.forEach(p => {
|
327 | (p.interrupt || []).forEach(i => {
|
328 | interrupts[i.value[0]] = i.name[0];
|
329 | });
|
330 | });
|
331 |
|
332 | Object.assign(package, {
|
333 | silicon: {
|
334 | target: {
|
335 | name: device.name[0],
|
336 | cpu,
|
337 | },
|
338 | sources,
|
339 | symbols,
|
340 | interrupts
|
341 | }
|
342 | });
|
343 |
|
344 | await pro(fs.writeFile)("package.json", JSON.stringify(package, null, 2));
|
345 | }
|
346 | };
|
347 |
|
348 | }; |
\ | No newline at end of file |