xactflow-component 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,476 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Callable, Iterable, Optional, Sequence, TypeVar
4
+
5
+ from lxml import etree
6
+
7
+ from ipxact.schema.businterface import FieldReference
8
+ from ipxact.schema.common import (
9
+ ArrayBound,
10
+ Assertion,
11
+ Choice,
12
+ File,
13
+ FileSet,
14
+ ModeRef,
15
+ ModuleParameter,
16
+ Parameter,
17
+ PartSelect,
18
+ SubPortReference,
19
+ Vector,
20
+ )
21
+ from ipxact.schema.ports import (
22
+ CellSpecification,
23
+ DriveConstraint,
24
+ LevelFlag,
25
+ LoadConstraint,
26
+ Payload,
27
+ Protocol,
28
+ Qualifier,
29
+ TimingConstraint,
30
+ )
31
+ from ipxact.schema.vlnv import VLNVRef
32
+
33
+ NAMESPACE = "http://www.accellera.org/XMLSchema/IPXACT/1685-2022"
34
+ _NSMAP = {"ipxact": NAMESPACE}
35
+
36
+ T = TypeVar("T")
37
+
38
+
39
+ def qn(tag: str) -> str:
40
+ """Build a namespace-qualified IP-XACT tag, e.g. qn("component") -> "{ns}component"."""
41
+ return f"{{{NAMESPACE}}}{tag}"
42
+
43
+
44
+ def element(tag: str, text: object = None, /, **attrs: object) -> etree._Element:
45
+ """Create an ipxact-namespaced element; attributes whose value is None are dropped.
46
+
47
+ tag and text are positional-only so that **attrs stays free for XML attributes that
48
+ happen to be named "text", "help" or "tag" (ipxact:enumeration has two of them).
49
+ """
50
+ elem = etree.Element(qn(tag), nsmap=_NSMAP)
51
+ for name, value in attrs.items():
52
+ if value is not None:
53
+ elem.set(name, str(value))
54
+ if text is not None:
55
+ elem.text = str(text)
56
+ return elem
57
+
58
+
59
+ def sub(parent: etree._Element, tag: str, text: object = None, /, **attrs: object) -> etree._Element:
60
+ child = element(tag, text, **attrs)
61
+ parent.append(child)
62
+ return child
63
+
64
+
65
+ def add_text(parent: etree._Element, tag: str, value: object, **attrs: object) -> None:
66
+ """Append a simple text child, or nothing when the value is unset."""
67
+ if value is not None:
68
+ sub(parent, tag, value, **attrs)
69
+
70
+
71
+ def bool_str(value: bool) -> str:
72
+ return "true" if value else "false"
73
+
74
+
75
+ def add_bool(parent: etree._Element, tag: str, value: Optional[bool], **attrs: object) -> None:
76
+ if value is not None:
77
+ sub(parent, tag, bool_str(value), **attrs)
78
+
79
+
80
+ def add_texts(parent: etree._Element, tag: str, values: Iterable[object]) -> None:
81
+ for value in values:
82
+ sub(parent, tag, value)
83
+
84
+
85
+ def add_items(parent: etree._Element, container_tag: str, items: Sequence[T], writer: Callable[[T], etree._Element]) -> None:
86
+ """Write container_tag holding one writer(item) per item, or nothing when items is empty.
87
+
88
+ Mirrors ipxact.parser.common_parser.parse_children, the same shape in reverse.
89
+ """
90
+ if not items:
91
+ return
92
+ container = sub(parent, container_tag)
93
+ for item in items:
94
+ container.append(writer(item))
95
+
96
+
97
+ def add_name_group(
98
+ parent: etree._Element,
99
+ name: Optional[str],
100
+ display_name: Optional[str] = None,
101
+ short_description: Optional[str] = None,
102
+ description: Optional[str] = None,
103
+ ) -> None:
104
+ """Write ipxact:nameGroup (name, displayName, shortDescription, description).
105
+
106
+ Nearly every IP-XACT type opens with this group, but the matching ipxact-compiler
107
+ dataclasses declare display_name/description as trailing optional fields. Element order
108
+ therefore cannot be read off dataclass field order anywhere in this package; it comes
109
+ from the XSD.
110
+ """
111
+ if name is not None:
112
+ sub(parent, "name", name)
113
+ add_text(parent, "displayName", display_name)
114
+ add_text(parent, "shortDescription", short_description)
115
+ add_text(parent, "description", description)
116
+
117
+
118
+ def write_vendor_extensions(parent: etree._Element, extensions: Sequence[str]) -> None:
119
+ """Re-insert opaque vendor extension fragments verbatim.
120
+
121
+ Vendor extension content is not IP-XACT's to define, so it is kept byte-identical rather
122
+ than re-derived. Giving every element that has children an empty text node switches
123
+ libxml2's pretty printer off for the whole fragment, so serializing does not inject
124
+ indentation into somebody else's markup.
125
+ """
126
+ if not extensions:
127
+ return
128
+ container = sub(parent, "vendorExtensions")
129
+ for extension in extensions:
130
+ container.append(etree.fromstring(extension.encode("utf-8")))
131
+ for node in container.iter():
132
+ if len(node) and node.text is None:
133
+ node.text = ""
134
+
135
+
136
+ def add_vlnv(parent: etree._Element, vlnv: object) -> None:
137
+ """Write ipxact:versionedIdentifier (vendor/library/name/version as child elements)."""
138
+ sub(parent, "vendor", vlnv.vendor)
139
+ sub(parent, "library", vlnv.library)
140
+ sub(parent, "name", vlnv.name)
141
+ sub(parent, "version", vlnv.version)
142
+
143
+
144
+ def write_vlnv_ref(tag: str, ref: VLNVRef) -> etree._Element:
145
+ """Write a configurableLibraryRefType element (VLNV as attributes)."""
146
+ elem = element(tag, vendor=ref.vendor, library=ref.library, name=ref.name, version=ref.version)
147
+ if ref.config_element_values:
148
+ values = sub(elem, "configurableElementValues")
149
+ for reference_id, value in ref.config_element_values.items():
150
+ sub(values, "configurableElementValue", value, referenceId=reference_id)
151
+ return elem
152
+
153
+
154
+ def add_vectors(parent: etree._Element, vectors: Sequence[Vector], with_ids: bool = True) -> None:
155
+ if not vectors:
156
+ return
157
+ container = sub(parent, "vectors")
158
+ for vector in vectors:
159
+ vector_elem = sub(container, "vector", vectorId=vector.vector_id if with_ids else None)
160
+ sub(vector_elem, "left", vector.left)
161
+ sub(vector_elem, "right", vector.right)
162
+
163
+
164
+ def add_array_bounds(parent: etree._Element, arrays: Sequence[ArrayBound], with_ids: bool = True) -> None:
165
+ if not arrays:
166
+ return
167
+ container = sub(parent, "arrays")
168
+ for array in arrays:
169
+ array_elem = sub(container, "array", arrayId=array.array_id if with_ids else None)
170
+ sub(array_elem, "left", array.left)
171
+ sub(array_elem, "right", array.right)
172
+
173
+
174
+ def add_mode_refs(parent: etree._Element, mode_refs: Sequence[ModeRef], with_priority: bool = True) -> None:
175
+ """Write ipxact:modeRef elements.
176
+
177
+ Most places reuse the global modeRef element, whose priority attribute is required. The
178
+ copies declared inline in busInterface.xsd (an initiator's addressSpaceRef, a target's
179
+ memoryMapRef) have no priority attribute at all, hence the flag.
180
+ """
181
+ for mode_ref in mode_refs:
182
+ sub(parent, "modeRef", mode_ref.name, priority=mode_ref.priority if with_priority else None)
183
+
184
+
185
+ def add_range(parent: etree._Element, left: Optional[str], right: Optional[str]) -> None:
186
+ if left is None and right is None:
187
+ return
188
+ range_elem = sub(parent, "range")
189
+ add_text(range_elem, "left", left)
190
+ add_text(range_elem, "right", right)
191
+
192
+
193
+ def add_part_select(parent: etree._Element, part_select: Optional[PartSelect]) -> None:
194
+ if part_select is None:
195
+ return
196
+ elem = sub(parent, "partSelect")
197
+ if part_select.indices:
198
+ indices = sub(elem, "indices")
199
+ add_texts(indices, "index", part_select.indices)
200
+ add_range(elem, part_select.range_left, part_select.range_right)
201
+
202
+
203
+ def add_sub_port_references(parent: etree._Element, refs: Sequence[SubPortReference]) -> None:
204
+ for ref in refs:
205
+ elem = sub(parent, "subPortReference", subPortRef=ref.sub_port_ref)
206
+ add_part_select(elem, ref.part_select)
207
+
208
+
209
+ def add_field_reference_group(parent: etree._Element, reference: FieldReference, with_range: bool = False) -> None:
210
+ """Write fieldReferenceGroup / fieldSliceReferenceGroup content directly into parent."""
211
+ if reference.address_space_ref is not None:
212
+ sub(parent, "addressSpaceRef", addressSpaceRef=reference.address_space_ref)
213
+ elif reference.memory_map_ref is not None:
214
+ sub(parent, "memoryMapRef", memoryMapRef=reference.memory_map_ref)
215
+ if reference.memory_remap_ref is not None:
216
+ sub(parent, "memoryRemapRef", memoryRemapRef=reference.memory_remap_ref)
217
+ elif with_range:
218
+ raise ValueError("a field slice reference requires address_space_ref or memory_map_ref")
219
+ for bank_ref in reference.bank_refs:
220
+ sub(parent, "bankRef", bankRef=bank_ref)
221
+ if reference.address_block_ref is not None:
222
+ sub(parent, "addressBlockRef", addressBlockRef=reference.address_block_ref)
223
+ elif with_range:
224
+ raise ValueError("a field slice reference requires address_block_ref")
225
+ for register_file_ref in reference.register_file_refs:
226
+ sub(parent, "registerFileRef", registerFileRef=register_file_ref)
227
+ if reference.register_ref is not None:
228
+ sub(parent, "registerRef", registerRef=reference.register_ref)
229
+ elif with_range:
230
+ raise ValueError("a field slice reference requires register_ref")
231
+ if reference.alternate_register_ref is not None:
232
+ sub(parent, "alternateRegisterRef", alternateRegisterRef=reference.alternate_register_ref)
233
+ sub(parent, "fieldRef", fieldRef=reference.field_ref)
234
+ if with_range and reference.range is not None:
235
+ add_range(parent, reference.range.range_left, reference.range.range_right)
236
+
237
+
238
+ def _add_parameter_body(parent: etree._Element, parameter: Parameter, with_ids: bool) -> None:
239
+ # A plain parameter's vectors/arrays carry no vectorId/arrayId: only moduleParameterType
240
+ # declares those attributes, so they are dropped rather than written out invalidly.
241
+ add_name_group(parent, parameter.name, parameter.display_name, parameter.short_description, parameter.description)
242
+ add_vectors(parent, parameter.vectors, with_ids=with_ids)
243
+ add_array_bounds(parent, parameter.arrays, with_ids=with_ids)
244
+ sub(parent, "value", parameter.value)
245
+ write_vendor_extensions(parent, parameter.vendor_extensions)
246
+
247
+
248
+ def _parameter_attributes(parameter: Parameter) -> dict:
249
+ return {
250
+ "parameterId": parameter.parameter_id,
251
+ "prompt": parameter.prompt,
252
+ "choiceRef": parameter.choice_ref,
253
+ "order": parameter.order,
254
+ "configGroups": " ".join(parameter.config_groups) if parameter.config_groups else None,
255
+ "minimum": parameter.minimum,
256
+ "maximum": parameter.maximum,
257
+ "type": parameter.type if parameter.type != "string" else None,
258
+ "sign": parameter.sign,
259
+ "prefix": parameter.prefix,
260
+ "unit": parameter.unit,
261
+ "resolve": parameter.resolve if parameter.resolve != "immediate" else None,
262
+ }
263
+
264
+
265
+ def write_parameter(parameter: Parameter) -> etree._Element:
266
+ elem = element("parameter", None, **_parameter_attributes(parameter))
267
+ _add_parameter_body(elem, parameter, with_ids=False)
268
+ return elem
269
+
270
+
271
+ def write_module_parameter(parameter: ModuleParameter) -> etree._Element:
272
+ attrs = _parameter_attributes(parameter)
273
+ attrs.update(
274
+ dataType=parameter.data_type,
275
+ usageType=parameter.usage_type if parameter.usage_type != "typed" else None,
276
+ dataTypeDefinition=parameter.data_type_definition,
277
+ constrained=" ".join(parameter.constrained) if parameter.constrained else None,
278
+ )
279
+ elem = element("moduleParameter", None, **attrs)
280
+ _add_parameter_body(elem, parameter, with_ids=True)
281
+ return elem
282
+
283
+
284
+ def add_parameters(parent: etree._Element, parameters: Sequence[Parameter]) -> None:
285
+ add_items(parent, "parameters", parameters, write_parameter)
286
+
287
+
288
+ def add_module_parameters(parent: etree._Element, parameters: Sequence[ModuleParameter]) -> None:
289
+ add_items(parent, "moduleParameters", parameters, write_module_parameter)
290
+
291
+
292
+ def add_choices(parent: etree._Element, choices: Sequence[Choice]) -> None:
293
+ if not choices:
294
+ return
295
+ container = sub(parent, "choices")
296
+ for choice in choices:
297
+ choice_elem = sub(container, "choice")
298
+ sub(choice_elem, "name", choice.name)
299
+ for enumeration in choice.enumerations:
300
+ sub(choice_elem, "enumeration", enumeration.value, text=enumeration.text, help=enumeration.help)
301
+
302
+
303
+ def add_assertions(parent: etree._Element, assertions: Sequence[Assertion]) -> None:
304
+ if not assertions:
305
+ return
306
+ container = sub(parent, "assertions")
307
+ for assertion in assertions:
308
+ assertion_elem = sub(container, "assertion")
309
+ add_name_group(assertion_elem, assertion.name, assertion.display_name, description=assertion.description)
310
+ sub(assertion_elem, "assert", assertion.assert_expression)
311
+
312
+
313
+ def add_file_builder(parent: etree._Element, builder: object) -> None:
314
+ """Write ipxact:defaultFileBuilder (fileBuilderType)."""
315
+ elem = sub(parent, "defaultFileBuilder")
316
+ sub(elem, "fileType", builder.file_type)
317
+ add_text(elem, "command", builder.command)
318
+ add_text(elem, "flags", builder.flags)
319
+ add_text(elem, "replaceDefaultFlags", builder.replace_default_flags)
320
+
321
+
322
+ def _add_file(parent: etree._Element, file: File) -> None:
323
+ elem = sub(parent, "file")
324
+ sub(elem, "name", file.name)
325
+ add_texts(elem, "fileType", file.file_types)
326
+ if file.is_structural:
327
+ sub(elem, "isStructural", bool_str(True))
328
+ if file.is_include_file or file.include_has_external_declarations:
329
+ sub(
330
+ elem,
331
+ "isIncludeFile",
332
+ bool_str(file.is_include_file),
333
+ externalDeclarations=bool_str(True) if file.include_has_external_declarations else None,
334
+ )
335
+ add_text(elem, "logicalName", file.logical_name)
336
+ add_texts(elem, "exportedName", file.exported_names)
337
+ if file.build_command is not None or file.build_flags is not None:
338
+ build_command = sub(elem, "buildCommand")
339
+ add_text(build_command, "command", file.build_command)
340
+ add_text(build_command, "flags", file.build_flags)
341
+ add_texts(elem, "dependency", file.dependencies)
342
+ write_vendor_extensions(elem, file.vendor_extensions)
343
+
344
+
345
+ def write_file_set(file_set: FileSet) -> etree._Element:
346
+ elem = element("fileSet")
347
+ add_name_group(elem, file_set.name, file_set.display_name, description=file_set.description)
348
+ add_texts(elem, "group", file_set.groups)
349
+ for file in file_set.files:
350
+ _add_file(elem, file)
351
+ for builder in file_set.default_file_builders:
352
+ add_file_builder(elem, builder)
353
+ add_texts(elem, "dependency", file_set.dependencies)
354
+ write_vendor_extensions(elem, file_set.vendor_extensions)
355
+ return elem
356
+
357
+
358
+ def add_file_sets(parent: etree._Element, file_sets: Sequence[FileSet]) -> None:
359
+ add_items(parent, "fileSets", file_sets, write_file_set)
360
+
361
+
362
+ def add_local_name_refs(parent: etree._Element, tag: str, names: Sequence[str]) -> None:
363
+ """Write fileSetRef / constraintSetRef / busInterfaceRef style wrappers around a localName."""
364
+ for name in names:
365
+ ref_elem = sub(parent, tag)
366
+ sub(ref_elem, "localName", name)
367
+
368
+
369
+ def _add_level_flag(parent: etree._Element, tag: str, flag: Optional[LevelFlag], with_power_domain: bool) -> None:
370
+ if flag is None:
371
+ return
372
+ sub(
373
+ parent,
374
+ tag,
375
+ bool_str(flag.value),
376
+ level=flag.level,
377
+ powerDomainRef=flag.power_domain_ref if with_power_domain else None,
378
+ )
379
+
380
+
381
+ def add_qualifier(parent: etree._Element, qualifier: Optional[Qualifier]) -> None:
382
+ if qualifier is None:
383
+ return
384
+ elem = sub(parent, "qualifier")
385
+ add_bool(elem, "isAddress", qualifier.is_address)
386
+ add_bool(elem, "isData", qualifier.is_data)
387
+ add_bool(elem, "isClock", qualifier.is_clock)
388
+ _add_level_flag(elem, "isReset", qualifier.is_reset, with_power_domain=False)
389
+ add_bool(elem, "isValid", qualifier.is_valid)
390
+ add_bool(elem, "isInterrupt", qualifier.is_interrupt)
391
+ _add_level_flag(elem, "isClockEn", qualifier.is_clock_en, with_power_domain=True)
392
+ _add_level_flag(elem, "isPowerEn", qualifier.is_power_en, with_power_domain=True)
393
+ add_bool(elem, "isOpcode", qualifier.is_opcode)
394
+ add_bool(elem, "isProtection", qualifier.is_protection)
395
+ if qualifier.is_flow_control is not None:
396
+ sub(
397
+ elem,
398
+ "isFlowControl",
399
+ bool_str(qualifier.is_flow_control.value),
400
+ flowType=qualifier.is_flow_control.flow_type,
401
+ user=qualifier.is_flow_control.user,
402
+ )
403
+ if qualifier.is_user is not None:
404
+ sub(elem, "isUser", bool_str(qualifier.is_user.value), user=qualifier.is_user.user)
405
+ add_bool(elem, "isRequest", qualifier.is_request)
406
+ add_bool(elem, "isResponse", qualifier.is_response)
407
+
408
+
409
+ def _add_cell_specification(parent: etree._Element, cell: CellSpecification) -> None:
410
+ elem = sub(parent, "cellSpecification", cellStrength=cell.cell_strength)
411
+ # cellSpecification is a required choice: exactly one of cellFunction or cellClass.
412
+ if cell.cell_function is not None:
413
+ sub(elem, "cellFunction", cell.cell_function)
414
+ elif cell.cell_class is not None:
415
+ sub(elem, "cellClass", cell.cell_class)
416
+ else:
417
+ raise ValueError("a cell specification requires cell_function or cell_class")
418
+
419
+
420
+ def add_drive_constraint(parent: etree._Element, constraint: Optional[DriveConstraint]) -> None:
421
+ if constraint is None:
422
+ return
423
+ _add_cell_specification(sub(parent, "driveConstraint"), constraint.cell)
424
+
425
+
426
+ def add_load_constraint(parent: etree._Element, constraint: Optional[LoadConstraint]) -> None:
427
+ if constraint is None:
428
+ return
429
+ elem = sub(parent, "loadConstraint")
430
+ _add_cell_specification(elem, constraint.cell)
431
+ add_text(elem, "count", constraint.count)
432
+
433
+
434
+ def add_timing_constraints(parent: etree._Element, constraints: Sequence[TimingConstraint]) -> None:
435
+ for constraint in constraints:
436
+ sub(
437
+ parent,
438
+ "timingConstraint",
439
+ constraint.value,
440
+ clockEdge=constraint.clock_edge,
441
+ delayType=constraint.delay_type,
442
+ clockName=constraint.clock_name,
443
+ )
444
+
445
+
446
+ def _add_payload(parent: etree._Element, payload: Optional[Payload]) -> None:
447
+ if payload is None:
448
+ return
449
+ elem = sub(parent, "payload")
450
+ add_text(elem, "name", payload.name)
451
+ sub(elem, "type", payload.type)
452
+ if payload.extension is not None:
453
+ sub(
454
+ elem,
455
+ "extension",
456
+ payload.extension,
457
+ mandatory=bool_str(True) if payload.extension_mandatory else None,
458
+ )
459
+
460
+
461
+ def add_protocol(parent: etree._Element, protocol: Optional[Protocol]) -> None:
462
+ if protocol is None:
463
+ return
464
+ elem = sub(parent, "protocol")
465
+ sub(elem, "protocolType", protocol.protocol_type, custom=protocol.custom_type_name)
466
+ _add_payload(elem, protocol.payload)
467
+
468
+
469
+ def add_clock_driver_body(parent: etree._Element, driver: object) -> None:
470
+ """Write clockDriverType content, shared by ipxact:clockDriver and ipxact:otherClockDriver."""
471
+ sub(parent, "clockPeriod", driver.clock_period, units=driver.period_units)
472
+ sub(parent, "clockPulseOffset", driver.clock_pulse_offset, units=driver.offset_units)
473
+ sub(parent, "clockPulseValue", driver.clock_pulse_value)
474
+ sub(parent, "clockPulseDuration", driver.clock_pulse_duration, units=driver.duration_units)
475
+
476
+
@@ -0,0 +1,164 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Sequence
4
+
5
+ from lxml import etree
6
+
7
+ from ipxact.schema.component_sections import (
8
+ ClearboxElement,
9
+ ComponentGenerator,
10
+ Cpu,
11
+ ExternalTypeDefinitionsRef,
12
+ Mode,
13
+ OtherClockDriver,
14
+ PowerDomain,
15
+ ResetType,
16
+ )
17
+
18
+ from .common_writer import (
19
+ add_clock_driver_body,
20
+ add_field_reference_group,
21
+ add_name_group,
22
+ add_parameters,
23
+ add_part_select,
24
+ add_sub_port_references,
25
+ add_text,
26
+ add_texts,
27
+ bool_str,
28
+ sub,
29
+ write_vendor_extensions,
30
+ write_vlnv_ref,
31
+ )
32
+
33
+
34
+ def add_external_type_definitions(
35
+ parent: etree._Element, external_type_definitions: Sequence[ExternalTypeDefinitionsRef]
36
+ ) -> None:
37
+ if not external_type_definitions:
38
+ return
39
+ container = sub(parent, "typeDefinitions")
40
+ for reference in external_type_definitions:
41
+ elem = sub(container, "externalTypeDefinitions")
42
+ add_name_group(elem, reference.name)
43
+ elem.append(write_vlnv_ref("typeDefinitionsRef", reference.type_definitions))
44
+
45
+
46
+ def add_power_domains(parent: etree._Element, power_domains: Sequence[PowerDomain]) -> None:
47
+ if not power_domains:
48
+ return
49
+ container = sub(parent, "powerDomains")
50
+ for power_domain in power_domains:
51
+ elem = sub(container, "powerDomain")
52
+ add_name_group(elem, power_domain.name)
53
+ add_text(elem, "alwaysOn", power_domain.always_on)
54
+ add_text(elem, "subDomainOf", power_domain.sub_domain_of)
55
+ add_parameters(elem, power_domain.parameters)
56
+ write_vendor_extensions(elem, power_domain.vendor_extensions)
57
+
58
+
59
+ def add_modes(parent: etree._Element, modes: Sequence[Mode]) -> None:
60
+ if not modes:
61
+ return
62
+ container = sub(parent, "modes")
63
+ for mode in modes:
64
+ elem = sub(container, "mode")
65
+ add_name_group(elem, mode.name)
66
+ for port_slice in mode.port_slices:
67
+ slice_elem = sub(elem, "portSlice")
68
+ add_name_group(slice_elem, port_slice.name, port_slice.display_name, description=port_slice.description)
69
+ sub(slice_elem, "portRef", portRef=port_slice.port_ref)
70
+ add_sub_port_references(slice_elem, port_slice.sub_port_refs)
71
+ add_part_select(slice_elem, port_slice.part_select)
72
+ for field_slice in mode.field_slices:
73
+ slice_elem = sub(elem, "fieldSlice")
74
+ add_name_group(slice_elem, field_slice.name, field_slice.display_name, description=field_slice.description)
75
+ add_field_reference_group(slice_elem, field_slice.field_ref, with_range=True)
76
+ add_text(elem, "condition", mode.condition)
77
+ write_vendor_extensions(elem, mode.vendor_extensions)
78
+
79
+
80
+ def add_cpus(parent: etree._Element, cpus: Sequence[Cpu]) -> None:
81
+ if not cpus:
82
+ return
83
+ container = sub(parent, "cpus")
84
+ for cpu in cpus:
85
+ elem = sub(container, "cpu")
86
+ add_name_group(elem, cpu.name)
87
+ sub(elem, "range", cpu.range)
88
+ sub(elem, "width", cpu.width)
89
+ if cpu.regions:
90
+ regions = sub(elem, "regions")
91
+ for region in cpu.regions:
92
+ region_elem = sub(regions, "region")
93
+ add_name_group(region_elem, region.name)
94
+ sub(region_elem, "addressOffset", region.address_offset)
95
+ sub(region_elem, "range", region.range)
96
+ write_vendor_extensions(region_elem, region.vendor_extensions)
97
+ add_text(elem, "addressUnitBits", cpu.address_unit_bits)
98
+ sub(elem, "memoryMapRef", cpu.memory_map_ref)
99
+ add_parameters(elem, cpu.parameters)
100
+ write_vendor_extensions(elem, cpu.vendor_extensions)
101
+
102
+
103
+ def add_clearbox_elements(parent: etree._Element, clearbox_elements: Sequence[ClearboxElement]) -> None:
104
+ if not clearbox_elements:
105
+ return
106
+ container = sub(parent, "clearboxElements")
107
+ for clearbox_element in clearbox_elements:
108
+ elem = sub(container, "clearboxElement")
109
+ add_name_group(elem, clearbox_element.name)
110
+ sub(elem, "clearboxType", clearbox_element.clearbox_type)
111
+ if clearbox_element.driveable:
112
+ sub(elem, "driveable", bool_str(True))
113
+ add_parameters(elem, clearbox_element.parameters)
114
+ write_vendor_extensions(elem, clearbox_element.vendor_extensions)
115
+
116
+
117
+ def add_component_generators(parent: etree._Element, generators: Sequence[ComponentGenerator]) -> None:
118
+ if not generators:
119
+ return
120
+ container = sub(parent, "componentGenerators")
121
+ for generator in generators:
122
+ elem = sub(
123
+ container,
124
+ "componentGenerator",
125
+ hidden=bool_str(True) if generator.hidden else None,
126
+ scope=generator.scope if generator.scope != "instance" else None,
127
+ )
128
+ add_name_group(elem, generator.name)
129
+ add_text(elem, "phase", generator.phase)
130
+ add_parameters(elem, generator.parameters)
131
+ add_text(elem, "apiType", generator.api_type)
132
+ sub(elem, "apiService", generator.api_service)
133
+ if generator.transport_methods:
134
+ transport_methods = sub(elem, "transportMethods")
135
+ add_texts(transport_methods, "transportMethod", generator.transport_methods)
136
+ sub(elem, "generatorExe", generator.generator_exe)
137
+ write_vendor_extensions(elem, generator.vendor_extensions)
138
+ # instanceGeneratorType extends generatorType, so its group elements follow the
139
+ # base type's content rather than sitting with the other name-like children.
140
+ add_texts(elem, "group", generator.groups)
141
+
142
+
143
+ def add_reset_types(parent: etree._Element, reset_types: Sequence[ResetType]) -> None:
144
+ if not reset_types:
145
+ return
146
+ container = sub(parent, "resetTypes")
147
+ for reset_type in reset_types:
148
+ elem = sub(container, "resetType")
149
+ add_name_group(elem, reset_type.name, reset_type.display_name, description=reset_type.description)
150
+ write_vendor_extensions(elem, reset_type.vendor_extensions)
151
+
152
+
153
+ def add_other_clock_drivers(parent: etree._Element, clock_drivers: Sequence[OtherClockDriver]) -> None:
154
+ if not clock_drivers:
155
+ return
156
+ container = sub(parent, "otherClockDrivers")
157
+ for clock_driver in clock_drivers:
158
+ elem = sub(
159
+ container,
160
+ "otherClockDriver",
161
+ clockName=clock_driver.clock_name,
162
+ clockSource=clock_driver.clock_source,
163
+ )
164
+ add_clock_driver_body(elem, clock_driver)