hybridflow_convert_cfg.py 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263
  1. #!/usr/bin/env python3
  2. """Convert a PPC2 TAS57xx .cfg file (I2C write commands) to a C header or raw
  3. binary with a packed byte stream: each command is [reg, len, data[0..len-1]]
  4. with no padding. The stream is terminated by 0xFF, 0xFF.
  5. Usage:
  6. python3 convert_cfg.py tt_hf1.cfg # C header to stdout
  7. python3 convert_cfg.py --bin tt_hf1.cfg # raw .bin file
  8. """
  9. import sys
  10. # TAS575x/578x register names by page
  11. # Page 0x00: Device Control
  12. PAGE0_REGS = {
  13. 0x00: "Page select",
  14. 0x01: "Reset",
  15. 0x02: "Standby/Shutdown ctrl",
  16. 0x03: "Mute",
  17. 0x04: "PLL/DSP source",
  18. 0x05: "De-emphasis / SDOUT / Auto-detect",
  19. 0x06: "SDOUT select",
  20. 0x07: "GPIO/SDOUT",
  21. 0x08: "GPIO enable",
  22. 0x09: "BCK/LRCLK config",
  23. 0x0C: "Master mode BCK divider",
  24. 0x0D: "PLL reference clock",
  25. 0x0E: "DAC reference clock",
  26. 0x0F: "PLL divider P",
  27. 0x10: "PLL multiplier J",
  28. 0x11: "PLL divider D (MSB)",
  29. 0x12: "PLL divider D (LSB)",
  30. 0x13: "PLL divider R",
  31. 0x14: "DSP clock divider",
  32. 0x15: "DAC clock divider",
  33. 0x16: "NCP clock divider",
  34. 0x17: "OSR clock divider",
  35. 0x1B: "Master BCK divider",
  36. 0x1C: "Master LRCLK divider (MSB)",
  37. 0x1D: "Master LRCLK divider (LSB)",
  38. 0x20: "IDAC (MSB)",
  39. 0x21: "IDAC (LSB)",
  40. 0x22: "FS speed mode",
  41. 0x25: "Ignore errors",
  42. 0x28: "I2S config",
  43. 0x2A: "DAC data path",
  44. 0x2B: "DSP program select",
  45. 0x3C: "Clock detect",
  46. 0x3D: "Digital volume fine (Ch B)",
  47. 0x3E: "Digital volume fine (Ch A)",
  48. 0x3F: "Auto mute time",
  49. 0x40: "Digital volume ctrl",
  50. 0x41: "Volume ramp config",
  51. 0x44: "Digital volume L",
  52. 0x45: "Digital volume R",
  53. 0x48: "Auto mute ctrl",
  54. 0x54: "Mute output select",
  55. 0x55: "GPIO output select",
  56. 0x56: "Analog output enable",
  57. 0x59: "GPIO output ctrl",
  58. 0x5C: "FS speed detect",
  59. 0x6A: "Output amplitude ctrl",
  60. 0x6B: "Output amplitude ctrl 2",
  61. 0x6C: "Undervoltage protection",
  62. 0x6E: "Power stage ctrl 1",
  63. 0x6F: "Power stage ctrl 2",
  64. 0x70: "Power stage ctrl 3",
  65. 0x71: "Power stage ctrl 4",
  66. 0x76: "VCOM ramp rate",
  67. 0x77: "VCOM power ctrl",
  68. 0x78: "VCOM power ctrl 2",
  69. }
  70. # Page 0x01: Output Control
  71. PAGE1_REGS = {
  72. 0x00: "Page select",
  73. 0x01: "Output amplitude ctrl",
  74. 0x02: "Analog gain ctrl",
  75. 0x06: "Analog output ctrl",
  76. 0x07: "Analog boost ctrl",
  77. 0x08: "Analog boost ctrl 2",
  78. }
  79. # DSP coefficient page descriptions
  80. DSP_PAGE_NAMES = {
  81. # miniDSP A (Channel A) coefficient pages
  82. 0x2C: "DSP-A: Process block coefficients",
  83. 0x2D: "DSP-A: Process block coefficients (cont.)",
  84. 0x2E: "DSP-A: Biquad filters",
  85. 0x2F: "DSP-A: Biquad filters (cont.)",
  86. 0x30: "DSP-A: Biquad/DRC",
  87. 0x31: "DSP-A: DRC/filters",
  88. 0x32: "DSP-A: Mixer/crossover",
  89. 0x33: "DSP-A: Interpolation/misc",
  90. 0x34: "DSP-A: Adaptation",
  91. # miniDSP B (Channel B) coefficient pages (mirror of A)
  92. 0x3E: "DSP-B: Process block coefficients",
  93. 0x3F: "DSP-B: Process block coefficients (cont.)",
  94. 0x40: "DSP-B: Biquad filters",
  95. 0x41: "DSP-B: Biquad filters (cont.)",
  96. 0x42: "DSP-B: Biquad/DRC",
  97. 0x43: "DSP-B: DRC/filters",
  98. 0x44: "DSP-B: Mixer/crossover",
  99. 0x45: "DSP-B: Interpolation/misc",
  100. 0x46: "DSP-B: Adaptation",
  101. }
  102. # These pages contain miniDSP instruction RAM
  103. INSTRUCTION_PAGES = set(range(0x98, 0xAA))
  104. def get_reg_comment(page, reg, data_len):
  105. """Return a register annotation comment, or empty string."""
  106. if page == 0x00:
  107. name = PAGE0_REGS.get(reg)
  108. if name:
  109. return f" // {name}"
  110. # Multi-byte writes might span known regs
  111. for offset in range(data_len):
  112. if (reg + offset) in PAGE0_REGS:
  113. names = []
  114. for o in range(data_len):
  115. n = PAGE0_REGS.get(reg + o, f"0x{reg+o:02X}")
  116. names.append(n)
  117. return f" // {' / '.join(names)}"
  118. elif page == 0x01:
  119. name = PAGE1_REGS.get(reg)
  120. if name:
  121. return f" // {name}"
  122. elif page == 0x7F:
  123. if reg == 0x00:
  124. return " // Book select"
  125. return ""
  126. def get_page_comment(page):
  127. """Return extra context for a page select comment."""
  128. if page in DSP_PAGE_NAMES:
  129. return f" — {DSP_PAGE_NAMES[page]}"
  130. if page in INSTRUCTION_PAGES:
  131. return " — miniDSP instruction RAM"
  132. if page == 0x00:
  133. return " — Device Control"
  134. if page == 0x01:
  135. return " — Output Control"
  136. return ""
  137. def convert(cfg_path):
  138. with open(cfg_path) as f:
  139. lines = f.readlines()
  140. name = cfg_path.rsplit('/', 1)[-1].rsplit('.', 1)[0]
  141. out = []
  142. out.append(f"// Auto-generated from {cfg_path}")
  143. out.append(f"// Packed byte stream: each command is [reg, len, data...]")
  144. out.append(f"// Terminated by 0xFF, 0xFF")
  145. out.append("")
  146. out.append("#pragma once")
  147. out.append("")
  148. out.append("#include <stdint.h>")
  149. out.append("")
  150. out.append(f"static const uint8_t {name}_seq[] = {{")
  151. current_page = 0x00
  152. pending_comments = []
  153. total_bytes = 0
  154. for line in lines:
  155. stripped = line.strip()
  156. if not stripped:
  157. continue
  158. if stripped.startswith('#'):
  159. pending_comments.append(stripped)
  160. continue
  161. if stripped.startswith('w '):
  162. parts = stripped.split()
  163. reg = int(parts[2], 16)
  164. data_bytes = parts[3:]
  165. data_len = len(data_bytes)
  166. data_hex = ', '.join(f'0x{b.upper()}' for b in data_bytes)
  167. # Track page changes
  168. is_page_select = (reg == 0x00 and data_len == 1)
  169. if is_page_select:
  170. new_page = int(data_bytes[0], 16)
  171. # Emit pending comments
  172. for c in pending_comments:
  173. comment_text = c.lstrip('# ').strip()
  174. if comment_text:
  175. if comment_text.startswith("Setting Page"):
  176. page_num = int(comment_text.split("0x")[1], 16)
  177. extra = get_page_comment(page_num)
  178. out.append(f' // {comment_text}{extra}')
  179. else:
  180. out.append(f' // {comment_text}')
  181. pending_comments = []
  182. # Build register annotation
  183. reg_comment = ""
  184. if is_page_select:
  185. current_page = new_page
  186. reg_comment = " // Page select"
  187. elif reg == 0x7F:
  188. reg_comment = " // Book select"
  189. else:
  190. reg_comment = get_reg_comment(current_page, reg, data_len)
  191. out.append(
  192. f' 0x{reg:02X}, {data_len}, {data_hex},{reg_comment}'
  193. )
  194. total_bytes += 2 + data_len # reg + len + data
  195. out.append(' 0xFF, 0xFF // end of stream')
  196. total_bytes += 2
  197. out.append('};')
  198. out.append(f'// Total: {total_bytes} bytes')
  199. out.append('')
  200. return '\n'.join(out)
  201. def convert_bin(cfg_path):
  202. """Convert a .cfg file to a raw binary packed byte stream."""
  203. with open(cfg_path) as f:
  204. lines = f.readlines()
  205. data = bytearray()
  206. for line in lines:
  207. stripped = line.strip()
  208. if not stripped or stripped.startswith('#'):
  209. continue
  210. if stripped.startswith('w '):
  211. parts = stripped.split()
  212. reg = int(parts[2], 16)
  213. data_bytes = [int(b, 16) for b in parts[3:]]
  214. data.append(reg)
  215. data.append(len(data_bytes))
  216. data.extend(data_bytes)
  217. data.extend([0xFF, 0xFF])
  218. return bytes(data)
  219. if __name__ == '__main__':
  220. if len(sys.argv) < 2:
  221. print(f"Usage: {sys.argv[0]} [--bin] <cfg_file>", file=sys.stderr)
  222. sys.exit(1)
  223. bin_mode = '--bin' in sys.argv
  224. cfg_file = [a for a in sys.argv[1:] if a != '--bin'][0]
  225. if bin_mode:
  226. out_path = cfg_file.rsplit('.', 1)[0] + '.bin'
  227. data = convert_bin(cfg_file)
  228. with open(out_path, 'wb') as f:
  229. f.write(data)
  230. print(f"Wrote {len(data)} bytes to {out_path}", file=sys.stderr)
  231. else:
  232. print(convert(cfg_file))