From 3c2b27fa8801e792d94d666c265e836795d750e6 Mon Sep 17 00:00:00 2001 From: guocheng Date: Tue, 28 Jul 2026 12:34:12 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E5=AF=84=E5=AD=98=E5=99=A8?= =?UTF-8?q?=E5=AE=9A=E4=B9=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CLAUDE.md | 1 + chapters/03-overview.md | 4 +- chapters/80-appendix-ids-table.md | 61 +++++++++++++++++++ doc_builder/build.py | 67 +++++++++++++++------ doc_builder/renderers/render_ids_acq_map.py | 8 +++ doc_builder/renderers/render_ids_awg_reg.py | 8 +++ doc_builder/renderers/render_ids_daq_reg.py | 8 +++ doc_builder/renderers/render_ids_exc_map.py | 8 +++ project.yaml | 3 +- 9 files changed, 147 insertions(+), 21 deletions(-) create mode 100644 chapters/80-appendix-ids-table.md create mode 100644 doc_builder/renderers/render_ids_acq_map.py create mode 100644 doc_builder/renderers/render_ids_awg_reg.py create mode 100644 doc_builder/renderers/render_ids_daq_reg.py create mode 100644 doc_builder/renderers/render_ids_exc_map.py diff --git a/CLAUDE.md b/CLAUDE.md index 31350cb..4c73dee 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -85,6 +85,7 @@ python doc_builder/build.py | §5.5 | `chapters/05-05-exc-upconversion.md` | EXC 上变频电路配置 | | §5.6 | `chapters/05-06-pump-config.md` | Pump 模拟电路配置 | | §5.7 | `chapters/05-07-exc-pipeline-demo.md` | EXC-Pump 全流程控制示例 | +| 附录A | `chapters/80-appendix-ids-table.md` | IDS 寄存器索引表(DAQ_REG / AWG_REG / 地址映射) | ## 编辑工作流 diff --git a/chapters/03-overview.md b/chapters/03-overview.md index b6fba1c..b6c127d 100644 --- a/chapters/03-overview.md +++ b/chapters/03-overview.md @@ -26,7 +26,7 @@ ez-Q 2.5测控系统包含5种物理通道,对应5类硬件接口,分别是A - 测控系统的寄存器、配置数据和模拟电路配置返回数据统一采用**大端字节序**。 通道寄存器配置数据支持微控制器实时修改,从而让通道的输入/输出控制具备动态控制能力。 -ACQ和EXC-Pump通道配置寄存器定义参考[读出子系统IDS表.xls](TODO)的`DAQ_REG`和`AWG_REG`页。 +ACQ和EXC-Pump通道配置寄存器的地址索引定义详见[附录A. IDS寄存器索引表](#a-ids)的 [A.2 DAQ_REG](#a2-daq_reg-acq) 和 [A.3 AWG_REG](#a3-awg_reg-exc-pump) 节。 本文档通过对不同编程模型下码字指令、通道寄存器配置数据、通道SRAM配置数据和通道模拟电路配置进行介绍, 旨在让用户掌握对读出激励信号的产生和采集信号处理的编程方法。 @@ -61,7 +61,7 @@ ez-Q 2.5 ASIC平台读出子系统由读出基带板、读出混频板和读出 通过扩展地址指定同一个板卡内的多个通道、通过地址指定一个通道内的不同配置项。 ``` -索引基地址由《读出子系统IDS表.xls》的mapping页定义,本文不对地址翻译进行赘述。 +索引基地址定义详见[附录A. IDS寄存器索引表](#a-ids)的 [A.1 地址空间映射](#a1) 节。 ``` 本文所述的三类编程通道具体定义如下: diff --git a/chapters/80-appendix-ids-table.md b/chapters/80-appendix-ids-table.md new file mode 100644 index 0000000..d182a65 --- /dev/null +++ b/chapters/80-appendix-ids-table.md @@ -0,0 +1,61 @@ +# 附录A. IDS寄存器索引表 + +本附录提供读出子系统通道配置寄存器的地址索引(IDS, Index Data Sheet), +供软件编程时通过基地址 + 偏移地址访问具体寄存器。 + +以下所有表格数据来源于 IDS JSON 数据文件(对应2509批次读出芯片), +由构建时自动渲染生成,确保与硬件设计数据库保持同步。 + +## A.1. 地址空间映射 + +读出子系统的配置访问采用四级寻址层级,从网络到寄存器的完整链路如下: + +1. **网络地址(IP)** — 定位机箱。软件通过指定目标机箱的 IP 地址来确定与哪个机箱内的板卡通信。一个机箱内可安装多块 PXIe 板卡,IP 地址是整个通信链路的起点。 + +2. **扩展地址(Exaddr)** — 定位槽位和总线。[7:3]对应槽位号,exaddr[2:0]对应总线号。读出基带板允许安装在4或者15槽,泵浦板和混频板分别在左右相邻位置,因此软件仅需指定基带板槽位号即可定位全部三个板卡。 + +3. **片选地址(Cid)** — 定位总线上芯片ID。采用5比特编码最大支持32颗芯片挂载到同一个spi总线上,在当前的实现中,直接使用总线编址通道号,每个芯片独占一个总线,因此Cid固定为0。 + +4. **配置基地址(Base)** — 定位具体配置项。采用25比特编码,其中高5比特用来定位不同模块,配合20比特的偏移地址用来具体指定配置项目。 + + +芯片控制存在MCU视角和配置视角,区别是基地址不同。从MCU操控视角来看,AWG和DAQ模块寄存器统一映射在对应 MCU 的 **`0x200000`** 地址空间。从上位机配置视角看,基地址和偏移地址定义如下表所示: + +### A.1.1. ACQ 通道模块基地址 + +对应 `msmt_acq` 通道类型(采集通道),共 4 个通道(ch0~ch3), +每个通道包含 5 个功能模块: + +@import "../../rbpu_datasheet/data/ids.json" using render_ids_acq_map + +### A.1.2. EXC 通道模块基地址 + +对应 `msmt_exc` 通道类型(激励通道),共 4 个通道(ch0~ch3), +每个通道包含 5 个功能模块: + +@import "../../rbpu_datasheet/data/ids.json" using render_ids_exc_map + +## A.2. DAQ_REG — ACQ 寄存器 + +ACQ 通道配置寄存器,对应采集通道的 DAQ 模块。寄存器基地址参见 +[A.1.1 节](#a11-acq) 中 `DAQ_REG` 行。 +各寄存器通过偏移地址在模块内部索引,如下表所示。 + +@import "../../rbpu_datasheet/data/ids.json" using render_ids_daq_reg + +## A.3. AWG_REG — EXC-Pump 寄存器 + +EXC-Pump 通道配置寄存器,对应激励通道的 AWG 模块。寄存器基地址参见 +[A.1.2 节](#a12-exc) 中 `AWG_REG` 行。 +各寄存器通过偏移地址在模块内部索引,如下表所示。 + +@import "../../rbpu_datasheet/data/ids.json" using render_ids_awg_reg + +## A.4. 寄存器访问说明 + +- 所有寄存器支持 **SPI** 和 **MCU** 双端口访问 +- 静态参数建议通过 SPI 配置后保持不变,动态参数通过 MCU 实时更新 +- 寄存器位宽均为 **32 位**(不足 32 位的高位保留) +- 返回数据统一采用 **大端字节序** +- MCU 以固定 **3 个时钟周期/指令** 的速率执行 +- 权限缩写:`RW` = 读写,`RO` = 只读,`WC` = 只写(写清除) diff --git a/doc_builder/build.py b/doc_builder/build.py index 4ec5c26..f2ce49d 100644 --- a/doc_builder/build.py +++ b/doc_builder/build.py @@ -72,11 +72,15 @@ def load_plugin_modules(directory): if spec is None or spec.loader is None: continue mod = importlib.util.module_from_spec(spec) + # 注册到 sys.modules 以支持插件间交叉导入 + sys.modules[py_file.stem] = mod spec.loader.exec_module(mod) modules[py_file.stem] = mod print(f" 已加载: {py_file.stem}") except Exception as e: print(f" 警告: 加载 {py_file.name} 失败: {e}") + # 加载失败时清理 sys.modules + sys.modules.pop(py_file.stem, None) return modules @@ -424,15 +428,19 @@ def postprocess_table_rowspan(html): - 同一行左右连续相同 → colspan 与 preprocess_mpe_tables 配合:MPE 标记展平后产生重复内容,此处恢复为视觉合并。 + 带有 class="no-rowspan" 的 会被跳过,不进行合并处理。 注意:仅处理 内的 (Python markdown tables 扩展的输出格式)。 """ + # 匹配整个
元素,检查其 class 属性 + TABLE_RE = re.compile(r'(]*>)(.*?)(
)', re.DOTALL) + def merge_tbody(match): tbody = match.group(1) rows = TR_RE.findall(tbody) if len(rows) < 2: return tbody - # 解析所有单元格 + # 解析所有单元格(统一补齐到最大列数,处理 rowspan 导致的列数不一致) row_cells = [] for row_html in rows: cells = [] @@ -448,6 +456,11 @@ def postprocess_table_rowspan(html): if num_cols == 0: return tbody + # 统一补齐:不足 num_cols 的行用空单元格补齐 + for row in row_cells: + while len(row) < num_cols: + row.append({"attrs": "", "text": ""}) + # covered[r][c]:该单元格已被 rowspan 或 colspan 覆盖,渲染时跳过 covered = [[False] * num_cols for _ in range(num_rows)] rowspan = [[1] * num_cols for _ in range(num_rows)] @@ -457,14 +470,10 @@ def postprocess_table_rowspan(html): for col in range(num_cols): row = 0 while row < num_rows: - if col >= len(row_cells[row]): - row += 1 - continue count = 1 r = row + 1 while r < num_rows: - if (col < len(row_cells[r]) - and row_cells[r][col]["text"] == row_cells[row][col]["text"] + if (row_cells[r][col]["text"] == row_cells[row][col]["text"] and row_cells[row][col]["text"] != ""): count += 1 covered[r][col] = True @@ -473,18 +482,18 @@ def postprocess_table_rowspan(html): break if count > 1: rowspan[row][col] = count - row = r # 跳过已被当前 rowspan 覆盖的行 + row = r # ---- 计算 colspan(逐行扫描,跳过已覆盖单元格) ---- for row in range(num_rows): col = 0 - while col < len(row_cells[row]): + while col < num_cols: if covered[row][col]: col += 1 continue count = 1 c = col + 1 - while c < num_cols and c < len(row_cells[row]): + while c < num_cols: if (not covered[row][c] and row_cells[row][c]["text"] == row_cells[row][col]["text"] and row_cells[row][col]["text"] != ""): @@ -495,14 +504,14 @@ def postprocess_table_rowspan(html): break if count > 1: colspan[row][col] = count - col = c # 跳过已被当前 colspan 覆盖的列 + col = c # ---- 生成带 rowspan / colspan 的 HTML ---- new_rows = [] for row in range(num_rows): new_cells = [] for col in range(num_cols): - if covered[row][col] or col >= len(row_cells[row]): + if covered[row][col]: continue cell = row_cells[row][col] rs = rowspan[row][col] @@ -512,12 +521,27 @@ def postprocess_table_rowspan(html): attrs += f' rowspan="{rs}"' if cs > 1: attrs += f' colspan="{cs}"' - new_cells.append(f'{cell["text"]}') + # 修复: attrs 非空时加前导空格,避免 畸形标签 + if attrs: + new_cells.append(f'{cell["text"]}') + else: + new_cells.append(f'{cell["text"]}') new_rows.append("" + "".join(new_cells) + "") return "" + "".join(new_rows) + "" - return TBODY_RE.sub(merge_tbody, html) + def process_table(match): + table_open = match.group(1) + body = match.group(2) + table_close = match.group(3) + # 跳过带有 no-rowspan class 的表格 + if 'no-rowspan' in table_open: + return match.group(0) + # 处理表格内的 + body = TBODY_RE.sub(merge_tbody, body) + return table_open + body + table_close + + return TABLE_RE.sub(process_table, html) # ---------- Markdown → HTML ---------- @@ -597,21 +621,28 @@ def slugify(text): def generate_toc(html): - """从 HTML 中提取 H1-H4 标题,生成目录。""" + """从 HTML 中提取 H1-H4 标题,生成目录。 + 锚点直接从标题已有的 id 属性读取,确保与 HTML 中的实际 ID 一致。""" toc = [] - for m in re.finditer(r']*>(.*?)', html, re.DOTALL): + # 匹配带 id 属性的标题标签 + for m in re.finditer(r']*\bid="([^"]*)"[^>]*>(.*?)', html, re.DOTALL): level = int(m.group(1)) - text = re.sub(r'<.*?>', '', m.group(2)).strip() - toc.append({"level": level, "text": text, "anchor": slugify(text)}) + anchor = m.group(2) + text = re.sub(r'<.*?>', '', m.group(3)).strip() + toc.append({"level": level, "text": text, "anchor": anchor}) return toc def inject_anchors(html): - """为所有 H1-H4 标签注入 id 属性,使侧边栏目录可跳转。""" + """为所有 H1-H4 标签注入 id 属性,使侧边栏目录可跳转。 + 如果标题已有 id 属性(如自定义渲染器预设的锚点),则保留原 id。""" def repl(match): level = match.group(1) attrs = match.group(2) inner = match.group(3) + # 保留已有的 id 属性(例如渲染器手动设置的锚点) + if re.search(r'\bid\s*=', attrs): + return f'{inner}' anchor = slugify(re.sub(r'<.*?>', '', inner).strip()) return f'{inner}' return re.sub( diff --git a/doc_builder/renderers/render_ids_acq_map.py b/doc_builder/renderers/render_ids_acq_map.py new file mode 100644 index 0000000..271c568 --- /dev/null +++ b/doc_builder/renderers/render_ids_acq_map.py @@ -0,0 +1,8 @@ +"""ACQ 通道模块基地址表渲染器。""" +from render_ids import _load_ids, render_mapping_acq_table + + +def render(filepath): + """返回 ACQ 通道模块基地址 HTML 表格(不含标题和描述)。""" + data = _load_ids(filepath) + return render_mapping_acq_table(data) diff --git a/doc_builder/renderers/render_ids_awg_reg.py b/doc_builder/renderers/render_ids_awg_reg.py new file mode 100644 index 0000000..1b36176 --- /dev/null +++ b/doc_builder/renderers/render_ids_awg_reg.py @@ -0,0 +1,8 @@ +"""AWG_REG 寄存器表渲染器。""" +from render_ids import _load_ids, render_awg_reg_table + + +def render(filepath): + """返回 AWG_REG 寄存器汇总表 + 位域明细 HTML(不含标题和描述)。""" + data = _load_ids(filepath) + return render_awg_reg_table(data) diff --git a/doc_builder/renderers/render_ids_daq_reg.py b/doc_builder/renderers/render_ids_daq_reg.py new file mode 100644 index 0000000..f8c65e3 --- /dev/null +++ b/doc_builder/renderers/render_ids_daq_reg.py @@ -0,0 +1,8 @@ +"""DAQ_REG 寄存器表渲染器。""" +from render_ids import _load_ids, render_daq_reg_table + + +def render(filepath): + """返回 DAQ_REG 寄存器汇总表 + 位域明细 HTML(不含标题和描述)。""" + data = _load_ids(filepath) + return render_daq_reg_table(data) diff --git a/doc_builder/renderers/render_ids_exc_map.py b/doc_builder/renderers/render_ids_exc_map.py new file mode 100644 index 0000000..c0466ff --- /dev/null +++ b/doc_builder/renderers/render_ids_exc_map.py @@ -0,0 +1,8 @@ +"""EXC 通道模块基地址表渲染器。""" +from render_ids import _load_ids, render_mapping_exc_table + + +def render(filepath): + """返回 EXC 通道模块基地址 HTML 表格(不含标题和描述)。""" + data = _load_ids(filepath) + return render_mapping_exc_table(data) diff --git a/project.yaml b/project.yaml index d06cddc..b5587d6 100644 --- a/project.yaml +++ b/project.yaml @@ -25,4 +25,5 @@ chapters: - 05-04-exc-waveform-store.md - 05-05-exc-upconversion.md - 05-06-pump-config.md - - 05-07-exc-pipeline-demo.md \ No newline at end of file + - 05-07-exc-pipeline-demo.md + - 80-appendix-ids-table.md \ No newline at end of file