1""" 2BICR Generation Tool 3-------------------- 4 5This tool is used to generate a BICR (Board Information Configuration Register) 6file from a JSON file that contains the BICR configuration. It can also be used 7to do the reverse operation, i.e., to extract the BICR configuration from a BICR 8hex file. 9 10:: 11 12 JSON ┌────────────┐ JSON 13 │ │ │ ▲ 14 └─────►│ ├───────┘ 15 │ bicrgen.py │ 16 ┌─────►│ ├───────┐ 17 │ │ │ ▼ 18 HEX └────────────┘ HEX 19 20Usage:: 21 22 python genbicr.py \ 23 --input <input_file.{hex,json}> \ 24 [--svd <svd_file>] \ 25 [--output <output_file.{hex,json}>] \ 26 [--list] 27 28Copyright (c) 2024 Nordic Semiconductor ASA 29SPDX-License-Identifier: Apache-2.0 30""" 31 32import argparse 33import json 34import struct 35import sys 36import xml.etree.ElementTree as ET 37from dataclasses import dataclass 38from enum import Enum 39from pathlib import Path 40from pprint import pprint 41 42from intelhex import IntelHex 43 44 45class Register: 46 def __init__(self, regs: ET.Element, name: str, data: bytes | bytearray | None = None) -> None: 47 cluster_name, reg_name = name.split(".") 48 49 cluster = regs.find(f".//registers/cluster[name='{cluster_name}']") 50 self._offset = int(cluster.find("addressOffset").text, 0) 51 52 self._reg = cluster.find(f".//register[name='{reg_name}']") 53 self._offset += int(self._reg.find("addressOffset").text, 0) 54 self._size = int(self._reg.find("size").text, 0) // 8 55 56 self._data = data 57 58 @property 59 def offset(self) -> int: 60 return self._offset 61 62 @property 63 def size(self) -> int: 64 return self._size 65 66 def _msk_pos(self, name: str) -> tuple[int, int]: 67 field = self._reg.find(f".//fields/field[name='{name}']") 68 field_lsb = int(field.find("lsb").text, 0) 69 field_msb = int(field.find("msb").text, 0) 70 71 mask = (0xFFFFFFFF - (1 << field_lsb) + 1) & (0xFFFFFFFF >> (31 - field_msb)) 72 73 return mask, field_lsb 74 75 def _enums(self, field: str) -> list[ET.Element]: 76 return self._reg.findall( 77 f".//fields/field[name='{field}']/enumeratedValues/enumeratedValue" 78 ) 79 80 def __getitem__(self, field: str) -> int: 81 if not self._data: 82 raise TypeError("Empty register") 83 84 msk, pos = self._msk_pos(field) 85 raw = struct.unpack("<I", self._data[self._offset : self._offset + 4])[0] 86 return (raw & msk) >> pos 87 88 def __setitem__(self, field: str, value: int) -> None: 89 if not isinstance(self._data, bytearray): 90 raise TypeError("Register is read-only") 91 92 msk, pos = self._msk_pos(field) 93 raw = raw = struct.unpack("<I", self._data[self._offset : self._offset + 4])[0] 94 raw &= ~msk 95 raw |= (value << pos) & msk 96 self._data[self._offset : self._offset + 4] = struct.pack("<I", raw) 97 98 def enum_get(self, field: str) -> str: 99 value = self[field] 100 for enum in self._enums(field): 101 if value == int(enum.find("value").text, 0): 102 return enum.find("name").text 103 104 raise ValueError(f"Invalid enum value for {field}: {value}") 105 106 def enum_set(self, field: str, value: str) -> None: 107 for enum in self._enums(field): 108 if value == enum.find("name").text: 109 self[field] = int(enum.find("value").text, 0) 110 return 111 112 raise ValueError(f"Invalid enum value for {field}: {value}") 113 114 115class PowerSupplyScheme(Enum): 116 UNCONFIGURED = "Unconfigured" 117 VDD_VDDH_1V8 = "VDD_VDDH_1V8" 118 VDDH_2V1_5V5 = "VDDH_2V1_5V5" 119 120 121@dataclass 122class PowerConfig: 123 scheme: PowerSupplyScheme 124 125 @classmethod 126 def from_raw(cls: "PowerConfig", bicr_spec: ET.Element, data: bytes) -> "PowerConfig": 127 power_config = Register(bicr_spec, "POWER.CONFIG", data) 128 129 if ( 130 power_config.enum_get("VDDAO5V0") == "Shorted" 131 and power_config.enum_get("VDDAO1V8") == "External" 132 ): 133 scheme = PowerSupplyScheme.VDD_VDDH_1V8 134 elif ( 135 power_config.enum_get("VDDAO5V0") == "External" 136 and power_config.enum_get("VDDAO1V8") == "Internal" 137 ): 138 scheme = PowerSupplyScheme.VDDH_2V1_5V5 139 else: 140 scheme = PowerSupplyScheme.UNCONFIGURED 141 142 return cls(scheme=scheme) 143 144 @classmethod 145 def from_json(cls: "PowerConfig", data: dict) -> "PowerConfig": 146 power = data["power"] 147 148 return cls(scheme=PowerSupplyScheme[power["scheme"]]) 149 150 def to_raw(self, bicr_spec: ET.Element, buf: bytearray): 151 power_config = Register(bicr_spec, "POWER.CONFIG", buf) 152 153 if self.scheme == PowerSupplyScheme.VDD_VDDH_1V8: 154 power_config.enum_set("VDDAO5V0", "Shorted") 155 power_config.enum_set("VDDAO1V8", "External") 156 power_config.enum_set("VDD1V0", "Internal") 157 power_config.enum_set("VDDRF1V0", "Shorted") 158 power_config.enum_set("VDDAO0V8", "Internal") 159 power_config.enum_set("VDDVS0V8", "Internal") 160 power_config.enum_set("INDUCTOR", "Present") 161 elif self.scheme == PowerSupplyScheme.VDDH_2V1_5V5: 162 power_config.enum_set("VDDAO5V0", "External") 163 power_config.enum_set("VDDAO1V8", "Internal") 164 power_config.enum_set("VDD1V0", "Internal") 165 power_config.enum_set("VDDRF1V0", "Shorted") 166 power_config.enum_set("VDDAO0V8", "Internal") 167 power_config.enum_set("VDDVS0V8", "Internal") 168 power_config.enum_set("INDUCTOR", "Present") 169 else: 170 power_config.enum_set("VDDAO5V0", "Unconfigured") 171 power_config.enum_set("VDDAO1V8", "Unconfigured") 172 power_config.enum_set("VDD1V0", "Unconfigured") 173 power_config.enum_set("VDDRF1V0", "Unconfigured") 174 power_config.enum_set("VDDAO0V8", "Unconfigured") 175 power_config.enum_set("VDDVS0V8", "Unconfigured") 176 power_config.enum_set("INDUCTOR", "Unconfigured") 177 178 def to_json(self, buf: dict): 179 buf["power"] = {"scheme": self.scheme.name} 180 181 182class IoPortPower(Enum): 183 DISCONNECTED = "Disconnected" 184 SHORTED = "Shorted" 185 EXTERNAL_1V8 = "External1V8" 186 187 188class IoPortPowerExtended(Enum): 189 DISCONNECTED = "Disconnected" 190 SHORTED = "Shorted" 191 EXTERNAL_1V8 = "External1V8" 192 EXTERNAL_FULL = "ExternalFull" 193 194 195@dataclass 196class IoPortPowerConfig: 197 p1_supply: IoPortPower 198 p2_supply: IoPortPower 199 p6_supply: IoPortPower 200 p7_supply: IoPortPower 201 p9_supply: IoPortPowerExtended 202 203 @classmethod 204 def from_raw( 205 cls: "IoPortPowerConfig", bicr_spec: ET.Element, data: bytes 206 ) -> "IoPortPowerConfig": 207 ioport_power0 = Register(bicr_spec, "IOPORT.POWER0", data) 208 ioport_power1 = Register(bicr_spec, "IOPORT.POWER1", data) 209 210 return cls( 211 p1_supply=IoPortPower(ioport_power0.enum_get("P1")), 212 p2_supply=IoPortPower(ioport_power0.enum_get("P2")), 213 p6_supply=IoPortPower(ioport_power0.enum_get("P6")), 214 p7_supply=IoPortPower(ioport_power0.enum_get("P7")), 215 p9_supply=IoPortPowerExtended(ioport_power1.enum_get("P9")), 216 ) 217 218 @classmethod 219 def from_json(cls: "IoPortPowerConfig", data: dict) -> "IoPortPowerConfig": 220 ioport_power = data["ioPortPower"] 221 222 return cls( 223 p1_supply=IoPortPower[ioport_power["p1Supply"]], 224 p2_supply=IoPortPower[ioport_power["p2Supply"]], 225 p6_supply=IoPortPower[ioport_power["p6Supply"]], 226 p7_supply=IoPortPower[ioport_power["p7Supply"]], 227 p9_supply=IoPortPowerExtended[ioport_power["p9Supply"]], 228 ) 229 230 def to_raw(self, bicr_spec: ET.Element, buf: bytearray): 231 ioport_power0 = Register(bicr_spec, "IOPORT.POWER0", buf) 232 ioport_power1 = Register(bicr_spec, "IOPORT.POWER1", buf) 233 234 ioport_power0.enum_set("P1", self.p1_supply.value) 235 ioport_power0.enum_set("P2", self.p2_supply.value) 236 ioport_power0.enum_set("P6", self.p6_supply.value) 237 ioport_power0.enum_set("P7", self.p7_supply.value) 238 ioport_power1.enum_set("P9", self.p9_supply.value) 239 240 def to_json(self, buf: dict): 241 buf["ioPortPower"] = { 242 "p1Supply": self.p1_supply.name, 243 "p2Supply": self.p2_supply.name, 244 "p6Supply": self.p6_supply.name, 245 "p7Supply": self.p7_supply.name, 246 "p9Supply": self.p9_supply.name, 247 } 248 249 250@dataclass 251class IoPortImpedanceConfig: 252 p6_impedance_ohms: int 253 p7_impedance_ohms: int 254 255 @classmethod 256 def from_raw( 257 cls: "IoPortImpedanceConfig", bicr_spec: ET.Element, data: bytes 258 ) -> "IoPortImpedanceConfig": 259 drivectl0 = Register(bicr_spec, "IOPORT.DRIVECTRL0", data) 260 261 return cls( 262 p6_impedance_ohms=int(drivectl0.enum_get("P6")[4:]), 263 p7_impedance_ohms=int(drivectl0.enum_get("P7")[4:]), 264 ) 265 266 @classmethod 267 def from_json(cls: "IoPortImpedanceConfig", data: dict) -> "IoPortImpedanceConfig": 268 ioport_impedance = data["ioPortImpedance"] 269 270 return cls( 271 p6_impedance_ohms=ioport_impedance["p6ImpedanceOhms"], 272 p7_impedance_ohms=ioport_impedance["p7ImpedanceOhms"], 273 ) 274 275 def to_raw(self, bicr_spec: ET.Element, buf: bytearray): 276 drivectl0 = Register(bicr_spec, "IOPORT.DRIVECTRL0", buf) 277 278 drivectl0.enum_set("P6", f"Ohms{self.p6_impedance_ohms}") 279 drivectl0.enum_set("P7", f"Ohms{self.p7_impedance_ohms}") 280 281 def to_json(self, buf: dict): 282 buf["ioPortImpedance"] = { 283 "p6ImpedanceOhms": self.p6_impedance_ohms, 284 "p7ImpedanceOhms": self.p7_impedance_ohms, 285 } 286 287 288class LFXOMode(Enum): 289 CRYSTAL = "Crystal" 290 EXT_SINE = "ExtSine" 291 EXT_SQUARE = "ExtSquare" 292 293 294@dataclass 295class LFXOConfig: 296 accuracy_ppm: int 297 mode: LFXOMode 298 builtin_load_capacitors: bool 299 builtin_load_capacitance_pf: int | None 300 startup_time_ms: int 301 302 @classmethod 303 def from_raw(cls: "LFXOConfig", bicr_spec: ET.Element, data: bytes) -> "LFXOConfig": 304 lfosc_lfxoconfig = Register(bicr_spec, "LFOSC.LFXOCONFIG", data) 305 306 try: 307 loadcap = lfosc_lfxoconfig.enum_get("LOADCAP") 308 except ValueError: 309 builtin_load_capacitors = True 310 builtin_load_capacitance_pf = lfosc_lfxoconfig["LOADCAP"] 311 else: 312 if loadcap == "Unconfigured": 313 raise ValueError("Invalid LFXO load capacitors configuration") 314 315 builtin_load_capacitors = False 316 builtin_load_capacitance_pf = None 317 318 startup_time_ms = 0 319 try: 320 lfosc_lfxoconfig.enum_get("TIME") 321 except ValueError: 322 startup_time_ms = lfosc_lfxoconfig["TIME"] 323 else: 324 raise ValueError("Invalid LFXO startup time (not configured)") 325 326 return cls( 327 accuracy_ppm=int(lfosc_lfxoconfig.enum_get("ACCURACY")[:3]), 328 mode=LFXOMode(lfosc_lfxoconfig.enum_get("MODE")), 329 builtin_load_capacitors=builtin_load_capacitors, 330 builtin_load_capacitance_pf=builtin_load_capacitance_pf, 331 startup_time_ms=startup_time_ms, 332 ) 333 334 @classmethod 335 def from_json(cls: "LFXOConfig", data: dict) -> "LFXOConfig": 336 lfxo = data["lfosc"]["lfxo"] 337 338 builtin_load_capacitors = lfxo["builtInLoadCapacitors"] 339 if builtin_load_capacitors: 340 builtin_load_capacitance_pf = lfxo["builtInLoadCapacitancePf"] 341 else: 342 builtin_load_capacitance_pf = None 343 344 return cls( 345 accuracy_ppm=lfxo["accuracyPPM"], 346 mode=LFXOMode[lfxo["mode"]], 347 builtin_load_capacitors=builtin_load_capacitors, 348 builtin_load_capacitance_pf=builtin_load_capacitance_pf, 349 startup_time_ms=lfxo["startupTimeMs"], 350 ) 351 352 def to_raw(self, bicr_spec: ET.Element, buf: bytearray): 353 lfosc_lfxoconfig = Register(bicr_spec, "LFOSC.LFXOCONFIG", buf) 354 355 lfosc_lfxoconfig.enum_set("ACCURACY", f"{self.accuracy_ppm}ppm") 356 lfosc_lfxoconfig.enum_set("MODE", self.mode.value) 357 lfosc_lfxoconfig["TIME"] = self.startup_time_ms 358 359 if self.builtin_load_capacitors: 360 lfosc_lfxoconfig["LOADCAP"] = self.builtin_load_capacitance_pf 361 else: 362 lfosc_lfxoconfig.enum_set("LOADCAP", "External") 363 364 def to_json(self, buf: dict): 365 lfosc = buf["lfosc"] 366 lfosc["lfxo"] = { 367 "accuracyPPM": self.accuracy_ppm, 368 "mode": self.mode.name, 369 "builtInLoadCapacitors": self.builtin_load_capacitors, 370 "startupTimeMs": self.startup_time_ms, 371 } 372 373 if self.builtin_load_capacitors: 374 lfosc["lfxo"]["builtInLoadCapacitancePf"] = self.builtin_load_capacitance_pf 375 376 377@dataclass 378class LFRCCalibrationConfig: 379 calibration_enabled: bool 380 temp_meas_interval_seconds: float | None 381 temp_delta_calibration_trigger_celsius: float | None 382 max_meas_interval_between_calibrations: int | None 383 384 @classmethod 385 def from_raw( 386 cls: "LFRCCalibrationConfig", bicr_spec: ET.Element, data: bytes 387 ) -> "LFRCCalibrationConfig": 388 lfosc_lfrcautocalconfig = Register(bicr_spec, "LFOSC.LFRCAUTOCALCONFIG", data) 389 390 calibration_enabled = lfosc_lfrcautocalconfig.enum_get("ENABLE") == "Enabled" 391 if calibration_enabled: 392 return cls( 393 calibration_enabled=calibration_enabled, 394 temp_meas_interval_seconds=lfosc_lfrcautocalconfig["TEMPINTERVAL"], 395 temp_delta_calibration_trigger_celsius=lfosc_lfrcautocalconfig["TEMPDELTA"], 396 max_meas_interval_between_calibrations=lfosc_lfrcautocalconfig["INTERVALMAXNO"], 397 ) 398 else: 399 return cls( 400 calibration_enabled=calibration_enabled, 401 temp_meas_interval_seconds=None, 402 temp_delta_calibration_trigger_celsius=None, 403 max_meas_interval_between_calibrations=None, 404 ) 405 406 @classmethod 407 def from_json(cls: "LFRCCalibrationConfig", data: dict) -> "LFRCCalibrationConfig": 408 lfrccal = data["lfosc"]["lfrccal"] 409 410 calibration_enabled = lfrccal["calibrationEnabled"] 411 if calibration_enabled: 412 temp_meas_interval_seconds = lfrccal["tempMeasIntervalSeconds"] 413 temp_delta_calibration_trigger_celsius = lfrccal["tempDeltaCalibrationTriggerCelsius"] 414 max_meas_interval_between_calibrations = lfrccal["maxMeasIntervalBetweenCalibrations"] 415 else: 416 temp_meas_interval_seconds = None 417 temp_delta_calibration_trigger_celsius = None 418 max_meas_interval_between_calibrations = None 419 420 return cls( 421 calibration_enabled=calibration_enabled, 422 temp_meas_interval_seconds=temp_meas_interval_seconds, 423 temp_delta_calibration_trigger_celsius=temp_delta_calibration_trigger_celsius, 424 max_meas_interval_between_calibrations=max_meas_interval_between_calibrations, 425 ) 426 427 def to_raw(self, bicr_spec: ET.Element, buf: bytearray): 428 lfosc_lfrcautocalconfig = Register(bicr_spec, "LFOSC.LFRCAUTOCALCONFIG", buf) 429 430 lfosc_lfrcautocalconfig.enum_set( 431 "ENABLE", "Enabled" if self.calibration_enabled else "Disabled" 432 ) 433 if self.calibration_enabled: 434 lfosc_lfrcautocalconfig["TEMPINTERVAL"] = self.temp_meas_interval_seconds 435 lfosc_lfrcautocalconfig["TEMPDELTA"] = self.temp_delta_calibration_trigger_celsius 436 lfosc_lfrcautocalconfig["INTERVALMAXNO"] = self.max_meas_interval_between_calibrations 437 438 def to_json(self, buf: dict): 439 lfosc = buf["lfosc"] 440 lfosc["lfrccal"] = { 441 "calibrationEnabled": self.calibration_enabled, 442 } 443 444 if self.calibration_enabled: 445 lfosc["lfrccal"]["tempMeasIntervalSeconds"] = self.temp_meas_interval_seconds 446 lfosc["lfrccal"]["tempDeltaCalibrationTriggerCelsius"] = ( 447 self.temp_delta_calibration_trigger_celsius 448 ) 449 lfosc["lfrccal"]["maxMeasIntervalBetweenCalibrations"] = ( 450 self.max_meas_interval_between_calibrations 451 ) 452 453 454class LFOSCSource(Enum): 455 LFXO = "LFXO" 456 LFRC = "LFRC" 457 458 459@dataclass 460class LFOSCConfig: 461 source: LFOSCSource 462 lfxo: LFXOConfig | None 463 lfrccal: LFRCCalibrationConfig | None 464 465 @classmethod 466 def from_raw(cls: "LFOSCConfig", bicr_spec: ET.Element, data: bytes) -> "LFOSCConfig": 467 lfosc_lfxoconfig = Register(bicr_spec, "LFOSC.LFXOCONFIG", data) 468 469 mode = lfosc_lfxoconfig.enum_get("MODE") 470 if mode == "Disabled": 471 source = LFOSCSource.LFRC 472 lfxo = None 473 lfrccal = LFRCCalibrationConfig.from_raw(bicr_spec, data) 474 elif mode == "Unconfigured": 475 raise ValueError("Invalid LFOSC configuration") 476 else: 477 source = LFOSCSource.LFXO 478 lfxo = LFXOConfig.from_raw(bicr_spec, data) 479 lfrccal = None 480 481 return cls( 482 source=source, 483 lfxo=lfxo, 484 lfrccal=lfrccal, 485 ) 486 487 @classmethod 488 def from_json(cls: "LFOSCConfig", data: dict) -> "LFOSCConfig": 489 lfosc = data["lfosc"] 490 491 source = LFOSCSource[lfosc["source"]] 492 if source == LFOSCSource.LFXO: 493 source = source 494 lfxo = LFXOConfig.from_json(data) 495 lfrccal = None 496 else: 497 source = source 498 lfxo = None 499 lfrccal = LFRCCalibrationConfig.from_json(data) 500 501 return cls( 502 source=source, 503 lfxo=lfxo, 504 lfrccal=lfrccal, 505 ) 506 507 def to_raw(self, bicr_spec: ET.Element, buf: bytearray): 508 lfosc_lfxoconfig = Register(bicr_spec, "LFOSC.LFXOCONFIG", buf) 509 510 if self.source == LFOSCSource.LFRC: 511 lfosc_lfxoconfig.enum_set("MODE", "Disabled") 512 self.lfrccal.to_raw(bicr_spec, buf) 513 elif self.source == LFOSCSource.LFXO: 514 self.lfxo.to_raw(bicr_spec, buf) 515 516 def to_json(self, buf: dict): 517 buf["lfosc"] = { 518 "source": self.source.name, 519 } 520 521 if self.source == LFOSCSource.LFXO: 522 self.lfxo.to_json(buf) 523 else: 524 self.lfrccal.to_json(buf) 525 526 527class HFXOMode(Enum): 528 CRYSTAL = "Crystal" 529 EXT_SQUARE = "ExtSquare" 530 531 532@dataclass 533class HFXOConfig: 534 mode: HFXOMode 535 builtin_load_capacitors: bool 536 builtin_load_capacitance_pf: float | None 537 startup_time_us: int 538 539 @classmethod 540 def from_raw(cls: "HFXOConfig", bicr_spec: ET.Element, data: bytes) -> "HFXOConfig": 541 hfxo_config = Register(bicr_spec, "HFXO.CONFIG", data) 542 hfxo_startuptime = Register(bicr_spec, "HFXO.STARTUPTIME", data) 543 544 mode = HFXOMode(hfxo_config.enum_get("MODE")) 545 546 try: 547 loadcap = hfxo_config.enum_get("LOADCAP") 548 except ValueError: 549 builtin_load_capacitors = True 550 builtin_load_capacitance_pf = hfxo_config["LOADCAP"] * 0.25 551 else: 552 if loadcap == "Unconfigured": 553 raise ValueError("Invalid HFXO load capacitors configuration") 554 555 builtin_load_capacitors = False 556 builtin_load_capacitance_pf = None 557 558 startup_time_us = 0 559 try: 560 hfxo_startuptime.enum_get("TIME") 561 except ValueError: 562 startup_time_us = hfxo_startuptime["TIME"] 563 else: 564 raise ValueError("Invalid LFXO startup time (not configured)") 565 566 return cls( 567 mode=mode, 568 builtin_load_capacitors=builtin_load_capacitors, 569 builtin_load_capacitance_pf=builtin_load_capacitance_pf, 570 startup_time_us=startup_time_us, 571 ) 572 573 @classmethod 574 def from_json(cls: "HFXOConfig", data: dict) -> "HFXOConfig": 575 hfxo = data["hfxo"] 576 577 builtin_load_capacitors = hfxo["builtInLoadCapacitors"] 578 if builtin_load_capacitors: 579 builtin_load_capacitance_pf = hfxo["builtInLoadCapacitancePf"] 580 else: 581 builtin_load_capacitance_pf = None 582 583 return cls( 584 mode=HFXOMode[hfxo["mode"]], 585 builtin_load_capacitors=builtin_load_capacitors, 586 builtin_load_capacitance_pf=builtin_load_capacitance_pf, 587 startup_time_us=hfxo["startupTimeUs"], 588 ) 589 590 def to_raw(self, bicr_spec: ET.Element, buf: bytearray): 591 hfxo_config = Register(bicr_spec, "HFXO.CONFIG", buf) 592 hfxo_startuptime = Register(bicr_spec, "HFXO.STARTUPTIME", buf) 593 594 hfxo_config.enum_set("MODE", self.mode.value) 595 hfxo_startuptime["TIME"] = self.startup_time_us 596 597 if self.builtin_load_capacitors: 598 hfxo_config["LOADCAP"] = int(self.builtin_load_capacitance_pf / 0.25) 599 else: 600 hfxo_config.enum_set("LOADCAP", "External") 601 602 def to_json(self, buf: dict): 603 buf["hfxo"] = { 604 "mode": self.mode.name, 605 "builtInLoadCapacitors": self.builtin_load_capacitors, 606 "startupTimeUs": self.startup_time_us, 607 } 608 609 if self.builtin_load_capacitors: 610 buf["hfxo"]["builtInLoadCapacitancePf"] = self.builtin_load_capacitance_pf 611 612 613@dataclass 614class BICR: 615 power: PowerConfig 616 ioport_power: IoPortPowerConfig 617 ioport_impedance: IoPortImpedanceConfig 618 lfosc: LFOSCConfig 619 hfxo: HFXOConfig 620 621 @classmethod 622 def from_raw(cls: "BICR", bicr_spec: ET.Element, data: bytes) -> "BICR": 623 return cls( 624 power=PowerConfig.from_raw(bicr_spec, data), 625 ioport_power=IoPortPowerConfig.from_raw(bicr_spec, data), 626 ioport_impedance=IoPortImpedanceConfig.from_raw(bicr_spec, data), 627 lfosc=LFOSCConfig.from_raw(bicr_spec, data), 628 hfxo=HFXOConfig.from_raw(bicr_spec, data), 629 ) 630 631 @classmethod 632 def from_json(cls: "BICR", data: dict) -> "BICR": 633 return cls( 634 power=PowerConfig.from_json(data), 635 ioport_power=IoPortPowerConfig.from_json(data), 636 ioport_impedance=IoPortImpedanceConfig.from_json(data), 637 lfosc=LFOSCConfig.from_json(data), 638 hfxo=HFXOConfig.from_json(data), 639 ) 640 641 def to_raw(self, bicr_spec: ET.Element, buf: bytearray): 642 self.power.to_raw(bicr_spec, buf) 643 self.ioport_power.to_raw(bicr_spec, buf) 644 self.ioport_impedance.to_raw(bicr_spec, buf) 645 self.lfosc.to_raw(bicr_spec, buf) 646 self.hfxo.to_raw(bicr_spec, buf) 647 648 def to_json(self, buf: dict): 649 self.power.to_json(buf) 650 self.ioport_power.to_json(buf) 651 self.ioport_impedance.to_json(buf) 652 self.lfosc.to_json(buf) 653 self.hfxo.to_json(buf) 654 655 656if __name__ == "__main__": 657 parser = argparse.ArgumentParser(allow_abbrev=False) 658 parser.add_argument("-i", "--input", type=Path, required=True, help="Input file") 659 parser.add_argument("-s", "--svd", type=Path, help="SVD file") 660 group = parser.add_mutually_exclusive_group(required=True) 661 group.add_argument("-o", "--output", type=Path, help="Output file") 662 group.add_argument("-l", "--list", action="store_true", help="List BICR options") 663 args = parser.parse_args() 664 665 if args.input.suffix == ".hex" or (args.output and args.output.suffix == ".hex"): 666 if not args.svd: 667 sys.exit("SVD file is required for hex files") 668 669 bicr_spec = ET.parse(args.svd).getroot().find(".//peripheral[name='BICR_NS']") 670 671 if args.input.suffix == ".hex": 672 ih = IntelHex() 673 ih.loadhex(args.input) 674 bicr = BICR.from_raw(bicr_spec, ih.tobinstr()) 675 elif args.input.suffix == ".json": 676 with open(args.input) as f: 677 data = json.load(f) 678 bicr = BICR.from_json(data) 679 else: 680 sys.exit("Unsupported input file format") 681 682 if args.output: 683 if args.output.suffix == ".hex": 684 bicr_address = int(bicr_spec.find("baseAddress").text, 0) 685 last_reg = Register(bicr_spec, "TAMPC.ACTIVESHIELD") 686 bicr_size = last_reg.offset + last_reg.size 687 688 buf = bytearray([0xFF] * bicr_size) 689 bicr.to_raw(bicr_spec, buf) 690 691 ih = IntelHex() 692 ih.frombytes(buf, offset=bicr_address) 693 ih.tofile(args.output, format="hex") 694 elif args.output.suffix == ".json": 695 buf = dict() 696 bicr.to_json(buf) 697 698 with open(args.output, "w") as f: 699 json.dump(buf, f, indent=4) 700 else: 701 sys.exit("Unsupported output file format") 702 elif args.list: 703 pprint(bicr) 704