修复渲染问题
This commit is contained in:
parent
37ba99c570
commit
bbe972e192
|
|
@ -38,6 +38,6 @@ jobs:
|
||||||
for f in output/*.html; do
|
for f in output/*.html; do
|
||||||
echo "上传: $(basename "$f")"
|
echo "上传: $(basename "$f")"
|
||||||
curl -sS --retries 3 -u "${WEBDAV_USER}:${WEBDAV_PASS}" \
|
curl -sS --retries 3 -u "${WEBDAV_USER}:${WEBDAV_PASS}" \
|
||||||
-T "$f" "${WEBDAV_URL}/$(basename "$f")"
|
-T "$f" "${WEBDAV_URL}/docs/$(basename "$f")"
|
||||||
done
|
done
|
||||||
echo "部署完成"
|
echo "部署完成"
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
<!-- CI/CD 状态徽章 — 请将下面的 URL 中的 gitea.example.com 和仓库路径替换为你的实际地址 -->
|
<!-- CI/CD 状态徽章 — 请将下面的 URL 中的 gitea.example.com 和仓库路径替换为你的实际地址 -->
|
||||||
[](http://114.214.202.87:9030/readout/readout_program/actions)
|
[](http://114.214.202.87:9030/readout/readout_program/actions)
|
||||||
|
|
||||||
> 📖 **在线文档**: [https://gc-home.top/manual-doc-readout/](https://gc-home.top/manual-doc-readout/)
|
> 📖 **在线文档**: [https://gc-home.top/docs/manual-doc-readout/](https://gc-home.top/docs/manual-doc-readout/)
|
||||||
|
|
||||||
本项目为 ez-Q 2.5 读出子系统的编程控制模型文档。采用 **"文档即代码" (Docs as Code)** 工作方式,
|
本项目为 ez-Q 2.5 读出子系统的编程控制模型文档。采用 **"文档即代码" (Docs as Code)** 工作方式,
|
||||||
通过 Markdown 纯文本写作、Git 版本控制和 Python 构建管道生成自包含 HTML 报告。
|
通过 Markdown 纯文本写作、Git 版本控制和 Python 构建管道生成自包含 HTML 报告。
|
||||||
|
|
|
||||||
|
|
@ -60,9 +60,7 @@ ez-Q 2.5 ASIC平台读出子系统由读出基带板、读出混频板和读出
|
||||||
当软件需要对不同通道编程时,其通过ip地址指定机箱、通过槽位号指定板卡、
|
当软件需要对不同通道编程时,其通过ip地址指定机箱、通过槽位号指定板卡、
|
||||||
通过扩展地址指定同一个板卡内的多个通道、通过地址指定一个通道内的不同配置项。
|
通过扩展地址指定同一个板卡内的多个通道、通过地址指定一个通道内的不同配置项。
|
||||||
|
|
||||||
```
|
|
||||||
索引基地址定义详见[附录A. IDS寄存器索引表](#a-ids)的 [A.1 地址空间映射](#a1) 节。
|
索引基地址定义详见[附录A. IDS寄存器索引表](#a-ids)的 [A.1 地址空间映射](#a1) 节。
|
||||||
```
|
|
||||||
|
|
||||||
本文所述的三类编程通道具体定义如下:
|
本文所述的三类编程通道具体定义如下:
|
||||||
* ACQ 编程通道定义从混频板`rf_in`输入端口到基带板卡内部DAQ模块;
|
* ACQ 编程通道定义从混频板`rf_in`输入端口到基带板卡内部DAQ模块;
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ ACQ通道利用MCU产生32位的码字来控制读出行为,32位的操控码
|
||||||
|比特位|名字|功能描述|
|
|比特位|名字|功能描述|
|
||||||
|:-|:-|:-|
|
|:-|:-|:-|
|
||||||
|[31:16]|QUBIT_EN| 16个量子比特使能,高电平使能对应位量子比特|
|
|[31:16]|QUBIT_EN| 16个量子比特使能,高电平使能对应位量子比特|
|
||||||
|[15]|COUNT_SAVE_EN| 态计数据存储使能, 高电平使能态计数存储|
|
|[15]|COUNT_SAVE_EN| 态计数数据存储使能, 高电平使能态计数存储|
|
||||||
|[14]|STATE_SAVE_EN| 态数据存储使能, 高电平使能态数据存储|
|
|[14]|STATE_SAVE_EN| 态数据存储使能, 高电平使能态数据存储|
|
||||||
|[13]|IQ_SAVE_EN| 解模数据存储使能, 高电平使能解模数据存储|
|
|[13]|IQ_SAVE_EN| 解模数据存储使能, 高电平使能解模数据存储|
|
||||||
|[12]|WAVE_SAVE_EN| 波形数据存储使能, 高电平使能波形数据存储|
|
|[12]|WAVE_SAVE_EN| 波形数据存储使能, 高电平使能波形数据存储|
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,7 @@ $$ \text{Qubit }N\text{ DAQ\_PAR 基地址} = \mathtt{0x00500000} + N \times \ma
|
||||||
|:-|:-|:-|:-|
|
|:-|:-|:-|:-|
|
||||||
|`0x00`|dds_fpw|[31:12] fcw|解模载波频率控制字:$F_c = fcw / 2^{20} \times F_s$|
|
|`0x00`|dds_fpw|[31:12] fcw|解模载波频率控制字:$F_c = fcw / 2^{20} \times F_s$|
|
||||||
|^|^|[11:0] pcw|解模载波相位控制字:$\phi_c = pcw / 2^{12} \times 2\pi$|
|
|^|^|[11:0] pcw|解模载波相位控制字:$\phi_c = pcw / 2^{12} \times 2\pi$|
|
||||||
|`0x04`|mtf_idx|[31:16] addr|权重数据起始地址(单位:时钟周期|
|
|`0x04`|mtf_idx|[31:16] addr|权重数据起始地址(单位:时钟周期)|
|
||||||
|^|^|[15:0] len|权重数据样本长度(单位:时钟周期)|
|
|^|^|[15:0] len|权重数据样本长度(单位:时钟周期)|
|
||||||
|`0x08`|line0_ab|[31:16] a0|直线0 斜率系数 a(16位有符号数)|
|
|`0x08`|line0_ab|[31:16] a0|直线0 斜率系数 a(16位有符号数)|
|
||||||
|^|^|[15:0] b0|直线0 斜率系数 b(16位有符号数)|
|
|^|^|[15:0] b0|直线0 斜率系数 b(16位有符号数)|
|
||||||
|
|
|
||||||
|
|
@ -104,8 +104,8 @@ mtf_idx 的 addr 和 len 单位为时钟周期(ASIC 平台每周期 8 采样
|
||||||
|地址|寄存器|字段|值|说明|
|
|地址|寄存器|字段|值|说明|
|
||||||
|:-|:-|:-|:-|:-|
|
|:-|:-|:-|:-|:-|
|
||||||
|`0x00400044`|func_ctrl|iq_scale [3]|`0`|IQ 结果截位 [27:8]|
|
|`0x00400044`|func_ctrl|iq_scale [3]|`0`|IQ 结果截位 [27:8]|
|
||||||
|`0x00400044`|func_ctrl|two_sta_en [2]|`0`|三态读出模式|
|
|^|^|two_sta_en [2]|`0`|三态读出模式|
|
||||||
|`0x00400044`|func_ctrl|const_en [7]|`0`|使用查找表权重(非常数权重)|
|
|^|^|const_en [7]|`0`|使用查找表权重(非常数权重)|
|
||||||
|`0x00400080`|mtf_idx_q0|addr [31:16] / len [15:0]|fallback 值|q0 匹配滤波器索引(查找表模式的 fallback)|
|
|`0x00400080`|mtf_idx_q0|addr [31:16] / len [15:0]|fallback 值|q0 匹配滤波器索引(查找表模式的 fallback)|
|
||||||
|`0x004000C0`|dds_fpw_q0|fcw [31:12] / pcw [11:0]|fallback 值|q0 DDS参数(查找表模式的 fallback)|
|
|`0x004000C0`|dds_fpw_q0|fcw [31:12] / pcw [11:0]|fallback 值|q0 DDS参数(查找表模式的 fallback)|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ AWG模块仅使用码字指令的低13位,其余位保留,
|
||||||
|[10]|NCO_CLR_EN| NCO清零使能, 高有效|
|
|[10]|NCO_CLR_EN| NCO清零使能, 高有效|
|
||||||
|[9]|MARK_EN| 标记脉冲输出使能, 高有效|
|
|[9]|MARK_EN| 标记脉冲输出使能, 高有效|
|
||||||
|[8]|PUMP_EN| 泵浦脉冲输出使能, 高有效|
|
|[8]|PUMP_EN| 泵浦脉冲输出使能, 高有效|
|
||||||
|[7:0]|WAVE_ID| 波形编号, 最大索256个波形输出|
|
|[7:0]|WAVE_ID| 波形编号, 最大索引256个波形输出|
|
||||||
|
|
||||||
- 码字比特[12]用于禁用波形输出,通过使能该比特可以仅输出Pump或者Marker脉冲而不输出该指令码字[7:0]索引的波形。
|
- 码字比特[12]用于禁用波形输出,通过使能该比特可以仅输出Pump或者Marker脉冲而不输出该指令码字[7:0]索引的波形。
|
||||||
- 码字比特[11]用于使能动态波形输出功能,该功能直接使用wave_ctrl寄存器中来索引波形,暂不使用该功能;
|
- 码字比特[11]用于使能动态波形输出功能,该功能直接使用wave_ctrl寄存器中来索引波形,暂不使用该功能;
|
||||||
|
|
|
||||||
|
|
@ -15,9 +15,9 @@ EXC-Pump通道的全部寄存器可以被SPI和MCU同时访问,
|
||||||
|
|
||||||
- `wave_ctrl`波形输出直接控制,可直接从波形仓库取采样点输出。
|
- `wave_ctrl`波形输出直接控制,可直接从波形仓库取采样点输出。
|
||||||
- 高16位为索引地址,单位是时钟周期
|
- 高16位为索引地址,单位是时钟周期
|
||||||
- 低16为为索引长度,单位是时钟周期
|
- 低16位为索引长度,单位是时钟周期
|
||||||
- `amplitude`调制幅度控制字
|
- `amplitude`调制幅度控制字
|
||||||
- 高16为幅度控制字acw,范围-32768~32767,归一化幅度$Amp = acw/2^{14}$
|
- 高16位为幅度控制字acw,范围-32768~32767,归一化幅度$Amp = acw/2^{14}$
|
||||||
- `frequency`调制频率控制
|
- `frequency`调制频率控制
|
||||||
- 32比特频率控制字fcw,$F_{nco}= fcw/2^{32}*F_s$,其中$F_s$是输出采样率。
|
- 32比特频率控制字fcw,$F_{nco}= fcw/2^{32}*F_s$,其中$F_s$是输出采样率。
|
||||||
- `phase`调制相位控制
|
- `phase`调制相位控制
|
||||||
|
|
|
||||||
|
|
@ -6,4 +6,4 @@
|
||||||
* 波形仓库的容量为128 KB,在FPGA平台和ASIC平台下最大分别支持16 us和10 us波形输出。
|
* 波形仓库的容量为128 KB,在FPGA平台和ASIC平台下最大分别支持16 us和10 us波形输出。
|
||||||
|
|
||||||
对于EXC输出频率$F_{out}$(例如6.7 GHz),在本振为$F_{LO}$(例如5.5GHz)本振频率下,
|
对于EXC输出频率$F_{out}$(例如6.7 GHz),在本振为$F_{LO}$(例如5.5GHz)本振频率下,
|
||||||
则存储区描绘的基带波形频率$F_{s}$为1.2 GHz ,$F_s = F_{out} - F_{LO}$。
|
则存储区描绘的基带波形频率$F_{IF}$为1.2 GHz ,$F_{IF} = F_{out} - F_{LO}$。
|
||||||
|
|
|
||||||
|
|
@ -150,7 +150,7 @@ MCU程序加载到 MCU_INS(`0x00700000`),触发后MCU执行程序并生成
|
||||||
|:-|:-|:-|:-|:-|
|
|:-|:-|:-|:-|:-|
|
||||||
|1|`0x00B00000`|AWG_WVE|波形采样点|256 × 16 bit|
|
|1|`0x00B00000`|AWG_WVE|波形采样点|256 × 16 bit|
|
||||||
|2|`0x00A00000`|AWG_IDX[0]|addr / len|`0x0000` / `0x0020`|
|
|2|`0x00A00000`|AWG_IDX[0]|addr / len|`0x0000` / `0x0020`|
|
||||||
|3|`0x00900034`|amplitude|acw|`8192`|
|
|3|`0x00900034`|amplitude|acw|`16384`|
|
||||||
|3|`0x00900038`|frequency|fcw|`0`|
|
|3|`0x00900038`|frequency|fcw|`0`|
|
||||||
|3|`0x0090003C`|phase|pcw|`0`|
|
|3|`0x0090003C`|phase|pcw|`0`|
|
||||||
|3|`0x00900044`|func_ctrl|awg_mode, mix_mode, intp_sel|`0x0`|
|
|3|`0x00900044`|func_ctrl|awg_mode, mix_mode, intp_sel|`0x0`|
|
||||||
|
|
|
||||||
|
|
@ -213,26 +213,49 @@ def process_image_attrs(text):
|
||||||
|
|
||||||
# ---------- KaTeX 预处理 ----------
|
# ---------- KaTeX 预处理 ----------
|
||||||
|
|
||||||
|
# 公式占位符存储(preprocess → postprocess 传递)
|
||||||
|
_MATH_PLACEHOLDERS = {}
|
||||||
|
|
||||||
|
|
||||||
def preprocess_katex(text):
|
def preprocess_katex(text):
|
||||||
"""
|
"""
|
||||||
将 Markdown 中的 LaTeX 公式包装为原始 HTML,
|
将 Markdown 中的 LaTeX 公式替换为唯一占位符,
|
||||||
防止 Markdown 解析器错误解释公式中的 _ * 等字符。
|
防止 Markdown 解析器错误解释公式中的 _ * 等字符。
|
||||||
|
占位符在 postprocess_katex 中还原为 HTML 包装的公式。
|
||||||
"""
|
"""
|
||||||
|
_MATH_PLACEHOLDERS.clear()
|
||||||
|
|
||||||
# 块级公式 $$...$$
|
# 块级公式 $$...$$
|
||||||
def protect_display(match):
|
def protect_display(match):
|
||||||
latex = match.group(1)
|
latex = match.group(1).strip()
|
||||||
return f'<div class="math-display">$${latex}$$</div>'
|
idx = len(_MATH_PLACEHOLDERS)
|
||||||
|
key = f"\x00MATH_DISPLAY_{idx}\x00"
|
||||||
|
_MATH_PLACEHOLDERS[key] = f'<div class="math-display">$${latex}$$</div>'
|
||||||
|
return key
|
||||||
|
|
||||||
text = re.sub(r'\$\$\s*(.+?)\s*\$\$', protect_display, text, flags=re.DOTALL)
|
text = re.sub(r'\$\$\s*(.+?)\s*\$\$', protect_display, text, flags=re.DOTALL)
|
||||||
|
|
||||||
# 行内公式 $...$
|
# 行内公式 $...$
|
||||||
def protect_inline(match):
|
def protect_inline(match):
|
||||||
latex = match.group(1)
|
latex = match.group(1)
|
||||||
return f'<span class="math-inline">${latex}$</span>'
|
idx = len(_MATH_PLACEHOLDERS)
|
||||||
|
key = f"\x00MATH_INLINE_{idx}\x00"
|
||||||
|
_MATH_PLACEHOLDERS[key] = f'<span class="math-inline">${latex}$</span>'
|
||||||
|
return key
|
||||||
|
|
||||||
text = re.sub(r'(?<!\d)\$([^$\s].*?[^$\s])\$(?!\d)', protect_inline, text)
|
text = re.sub(r'(?<!\d)\$([^$\s].*?[^$\s])\$(?!\d)', protect_inline, text)
|
||||||
|
|
||||||
return text
|
return text
|
||||||
|
|
||||||
|
|
||||||
|
def postprocess_katex(html):
|
||||||
|
"""将占位符还原为 HTML 包装的 LaTeX 公式。"""
|
||||||
|
result = html
|
||||||
|
for key, replacement in _MATH_PLACEHOLDERS.items():
|
||||||
|
result = result.replace(key, replacement)
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
# ---------- 自定义代码块渲染 ----------
|
# ---------- 自定义代码块渲染 ----------
|
||||||
|
|
||||||
FENCED_CODEBLOCK_OPEN_RE = re.compile(r'^```(\w+)(?:\s+[^\n]*)?$')
|
FENCED_CODEBLOCK_OPEN_RE = re.compile(r'^```(\w+)(?:\s+[^\n]*)?$')
|
||||||
|
|
@ -292,8 +315,11 @@ def render_custom_codeblocks_md(text, renderers):
|
||||||
# ^ : 与上方单元格合并(rowspan)
|
# ^ : 与上方单元格合并(rowspan)
|
||||||
# < : 与左侧单元格合并(colspan,本单元格被吸收)
|
# < : 与左侧单元格合并(colspan,本单元格被吸收)
|
||||||
# > : 与右侧单元格合并(colspan,右侧单元格被吸收)
|
# > : 与右侧单元格合并(colspan,右侧单元格被吸收)
|
||||||
# 策略:展平时将合并标记替换为被合并单元格的内容,
|
# 策略:展平时将合并标记替换为被合并单元格的内容并附加哨兵标记,
|
||||||
# 再由 postprocess_table_rowspan 检测重复内容生成 rowspan/colspan。
|
# 再由 postprocess_table_rowspan 根据哨兵标记生成 rowspan/colspan。
|
||||||
|
# 仅显式标记了 ^ / < / > 的单元格才会参与合并,避免跨逻辑组的过度合并。
|
||||||
|
|
||||||
|
MERGE_MARKER = "<!--MPEMERGE-->"
|
||||||
|
|
||||||
# 表格行: | cell | cell | ... |
|
# 表格行: | cell | cell | ... |
|
||||||
TABLE_ROW_RE = re.compile(r'^\|.+\|$')
|
TABLE_ROW_RE = re.compile(r'^\|.+\|$')
|
||||||
|
|
@ -387,22 +413,26 @@ def _flatten_mpe_table(table_lines):
|
||||||
for row in parsed:
|
for row in parsed:
|
||||||
for col in range(num_cols - 2, -1, -1):
|
for col in range(num_cols - 2, -1, -1):
|
||||||
if row[col].strip() == ">":
|
if row[col].strip() == ">":
|
||||||
row[col] = row[col + 1]
|
row[col] = MERGE_MARKER + row[col + 1]
|
||||||
|
|
||||||
# 第 2 遍:处理 <(左→右,复制左侧单元格内容)
|
# 第 2 遍:处理 <(左→右,复制左侧单元格内容)
|
||||||
for row in parsed:
|
for row in parsed:
|
||||||
for col in range(1, num_cols):
|
for col in range(1, num_cols):
|
||||||
if row[col].strip() == "<":
|
if row[col].strip() == "<":
|
||||||
row[col] = row[col - 1]
|
row[col] = MERGE_MARKER + row[col - 1]
|
||||||
|
|
||||||
# 第 3 遍:处理 ^(上→下,复制上方单元格内容)
|
# 第 3 遍:处理 ^(上→下,复制上方单元格内容,清除旧哨兵避免重复)
|
||||||
prev_cells = _split_table_cells(header)
|
prev_cells = _split_table_cells(header)
|
||||||
while len(prev_cells) < num_cols:
|
while len(prev_cells) < num_cols:
|
||||||
prev_cells.append("")
|
prev_cells.append("")
|
||||||
for row in parsed:
|
for row in parsed:
|
||||||
for col in range(num_cols):
|
for col in range(num_cols):
|
||||||
if row[col].strip() == "^":
|
if row[col].strip() == "^":
|
||||||
row[col] = prev_cells[col] if col < len(prev_cells) else row[col]
|
content = prev_cells[col] if col < len(prev_cells) else row[col]
|
||||||
|
# 剥离 prev_cells 中已有的哨兵标记,避免链式累积
|
||||||
|
if content.startswith(MERGE_MARKER):
|
||||||
|
content = content[len(MERGE_MARKER):]
|
||||||
|
row[col] = MERGE_MARKER + content
|
||||||
prev_cells = list(row)
|
prev_cells = list(row)
|
||||||
|
|
||||||
# 重建表格行
|
# 重建表格行
|
||||||
|
|
@ -423,10 +453,11 @@ TBODY_RE = re.compile(r'(<tbody>.*?</tbody>)', re.DOTALL)
|
||||||
|
|
||||||
def postprocess_table_rowspan(html):
|
def postprocess_table_rowspan(html):
|
||||||
"""
|
"""
|
||||||
扫描 HTML 表格的 <tbody>,将连续内容相同的单元格合并:
|
扫描 HTML 表格的 <tbody>,将带有 MERGE_MARKER 哨兵的单元格合并:
|
||||||
- 同一列上下连续相同 → rowspan
|
- 同一列上下连续相同(且下方含哨兵) → rowspan
|
||||||
- 同一行左右连续相同 → colspan
|
- 同一行左右连续相同(且右侧含哨兵) → colspan
|
||||||
与 preprocess_mpe_tables 配合:MPE 标记展平后产生重复内容,此处恢复为视觉合并。
|
与 preprocess_mpe_tables 配合:仅显式标记了 ^ / < / > 的单元格生成哨兵,
|
||||||
|
避免跨逻辑组的过度合并。
|
||||||
|
|
||||||
带有 class="no-rowspan" 的 <table> 会被跳过,不进行合并处理。
|
带有 class="no-rowspan" 的 <table> 会被跳过,不进行合并处理。
|
||||||
注意:仅处理 <tbody> 内的 <tr>(Python markdown tables 扩展的输出格式)。
|
注意:仅处理 <tbody> 内的 <tr>(Python markdown tables 扩展的输出格式)。
|
||||||
|
|
@ -467,16 +498,23 @@ def postprocess_table_rowspan(html):
|
||||||
colspan = [[1] * num_cols for _ in range(num_rows)]
|
colspan = [[1] * num_cols for _ in range(num_rows)]
|
||||||
|
|
||||||
# ---- 计算 rowspan(逐列扫描) ----
|
# ---- 计算 rowspan(逐列扫描) ----
|
||||||
|
# 仅当下方单元格带 MERGE_MARKER 哨兵且内容匹配时才合并
|
||||||
for col in range(num_cols):
|
for col in range(num_cols):
|
||||||
row = 0
|
row = 0
|
||||||
while row < num_rows:
|
while row < num_rows:
|
||||||
count = 1
|
count = 1
|
||||||
r = row + 1
|
r = row + 1
|
||||||
while r < num_rows:
|
while r < num_rows:
|
||||||
if (row_cells[r][col]["text"] == row_cells[row][col]["text"]
|
below_text = row_cells[r][col]["text"]
|
||||||
and row_cells[row][col]["text"] != ""):
|
above_text = row_cells[row][col]["text"]
|
||||||
|
# 下方单元格必须带哨兵标记,且去除哨兵后与上方内容一致
|
||||||
|
if (below_text.startswith(MERGE_MARKER)
|
||||||
|
and below_text[len(MERGE_MARKER):] == above_text
|
||||||
|
and above_text != ""):
|
||||||
count += 1
|
count += 1
|
||||||
covered[r][col] = True
|
covered[r][col] = True
|
||||||
|
# 将合并单元格的内容统一为去除哨兵的版本
|
||||||
|
row_cells[r][col]["text"] = above_text
|
||||||
r += 1
|
r += 1
|
||||||
else:
|
else:
|
||||||
break
|
break
|
||||||
|
|
@ -494,11 +532,16 @@ def postprocess_table_rowspan(html):
|
||||||
count = 1
|
count = 1
|
||||||
c = col + 1
|
c = col + 1
|
||||||
while c < num_cols:
|
while c < num_cols:
|
||||||
|
right_text = row_cells[row][c]["text"]
|
||||||
|
left_text = row_cells[row][col]["text"]
|
||||||
|
# 右侧单元格必须带哨兵标记,且去除哨兵后与左侧内容一致
|
||||||
if (not covered[row][c]
|
if (not covered[row][c]
|
||||||
and row_cells[row][c]["text"] == row_cells[row][col]["text"]
|
and right_text.startswith(MERGE_MARKER)
|
||||||
and row_cells[row][col]["text"] != ""):
|
and right_text[len(MERGE_MARKER):] == left_text
|
||||||
|
and left_text != ""):
|
||||||
count += 1
|
count += 1
|
||||||
covered[row][c] = True
|
covered[row][c] = True
|
||||||
|
row_cells[row][c]["text"] = left_text
|
||||||
c += 1
|
c += 1
|
||||||
else:
|
else:
|
||||||
break
|
break
|
||||||
|
|
@ -808,6 +851,9 @@ def main():
|
||||||
# Markdown → HTML
|
# Markdown → HTML
|
||||||
chapter_html = markdown_to_html(text)
|
chapter_html = markdown_to_html(text)
|
||||||
|
|
||||||
|
# KaTeX 还原(占位符 → HTML 包装的公式)
|
||||||
|
chapter_html = postprocess_katex(chapter_html)
|
||||||
|
|
||||||
# 图片 base64 内嵌
|
# 图片 base64 内嵌
|
||||||
base_dirs = [ch_path.parent, ASSETS_DIR, PROJECT_ROOT]
|
base_dirs = [ch_path.parent, ASSETS_DIR, PROJECT_ROOT]
|
||||||
chapter_html = embed_images(chapter_html, base_dirs)
|
chapter_html = embed_images(chapter_html, base_dirs)
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,185 @@
|
||||||
|
"""IDS 数据共享模块。
|
||||||
|
|
||||||
|
提供 JSON 加载和 HTML 表格渲染函数,
|
||||||
|
供 render_ids_acq_map / render_ids_exc_map / render_ids_daq_reg / render_ids_awg_reg 复用。
|
||||||
|
"""
|
||||||
|
|
||||||
|
import json
|
||||||
|
from pathlib import Path
|
||||||
|
from collections import OrderedDict
|
||||||
|
|
||||||
|
# 输出顺序(先列出的模块排在前面)
|
||||||
|
ACQ_MODULE_ORDER = ["MCU_INS", "MCU_DAT", "DAQ_REG", "DAQ_PAR", "DAQ_FLT"]
|
||||||
|
EXC_MODULE_ORDER = ["MCU_INS", "MCU_DAT", "AWG_REG", "AWG_IDX", "AWG_WVE"]
|
||||||
|
|
||||||
|
|
||||||
|
# ---------- 数据加载 ----------
|
||||||
|
|
||||||
|
def _load_ids(filepath: str) -> dict:
|
||||||
|
"""加载 IDS JSON 文件并返回解析后的 dict。"""
|
||||||
|
path = Path(filepath)
|
||||||
|
if not path.exists():
|
||||||
|
raise FileNotFoundError(f"IDS 文件不存在: {path}")
|
||||||
|
with open(path, "r", encoding="utf-8") as f:
|
||||||
|
return json.load(f)
|
||||||
|
|
||||||
|
|
||||||
|
# ---------- HTML 工具 ----------
|
||||||
|
|
||||||
|
def _td(text: str, **kwargs) -> str:
|
||||||
|
"""生成 <td> 单元格,支持 rowspan / style 等属性。"""
|
||||||
|
attrs = " ".join(f'{k}="{v}"' for k, v in kwargs.items() if v is not None)
|
||||||
|
return f"<td {attrs}>{text}</td>" if attrs else f"<td>{text}</td>"
|
||||||
|
|
||||||
|
|
||||||
|
def _th(text: str) -> str:
|
||||||
|
return f"<th>{text}</th>"
|
||||||
|
|
||||||
|
|
||||||
|
def _bit_range_str(bits: list) -> str:
|
||||||
|
"""将 [15, 8] 或 [7] 转为位段字符串。"""
|
||||||
|
if len(bits) == 1:
|
||||||
|
return str(bits[0])
|
||||||
|
return f"{bits[0]}:{bits[-1]}"
|
||||||
|
|
||||||
|
|
||||||
|
# ---------- 映射表渲染 ----------
|
||||||
|
|
||||||
|
def render_mapping_acq_table(data: dict) -> str:
|
||||||
|
"""渲染 msmt_acq 通道模块基地址表。"""
|
||||||
|
mi = data.get("MappingInfo", {}).get("msmt_acq", {})
|
||||||
|
return _render_mapping_table(mi, ACQ_MODULE_ORDER, "ACQ")
|
||||||
|
|
||||||
|
|
||||||
|
def render_mapping_exc_table(data: dict) -> str:
|
||||||
|
"""渲染 msmt_exc 通道模块基地址表。"""
|
||||||
|
mi = data.get("MappingInfo", {}).get("msmt_exc", {})
|
||||||
|
return _render_mapping_table(mi, EXC_MODULE_ORDER, "EXC")
|
||||||
|
|
||||||
|
|
||||||
|
def _render_mapping_table(mapping: dict, module_order: list, label: str) -> str:
|
||||||
|
"""通用的通道模块基地址渲染。"""
|
||||||
|
rows = []
|
||||||
|
# 收集所有模块名
|
||||||
|
all_modules = set()
|
||||||
|
for ch_info in mapping.values():
|
||||||
|
all_modules.update(ch_info.get("Modules", {}).keys())
|
||||||
|
|
||||||
|
# 按 module_order 排序,未在列表中的放最后
|
||||||
|
ordered = [m for m in module_order if m in all_modules]
|
||||||
|
ordered += sorted(all_modules - set(module_order))
|
||||||
|
|
||||||
|
for mod_name in ordered:
|
||||||
|
for ch in sorted(mapping.keys(), key=int):
|
||||||
|
info = mapping[ch]
|
||||||
|
modules = info.get("Modules", {})
|
||||||
|
if mod_name not in modules:
|
||||||
|
continue
|
||||||
|
m = modules[mod_name]
|
||||||
|
exaddr = info.get("Exaddr", "")
|
||||||
|
cid = info.get("Cid", "")
|
||||||
|
rows.append(
|
||||||
|
"<tr>"
|
||||||
|
f"<td>ch{ch}</td>"
|
||||||
|
f"<td>{exaddr}</td>"
|
||||||
|
f"<td>{cid}</td>"
|
||||||
|
f"<td><code>{mod_name}</code></td>"
|
||||||
|
f"<td><code>{m.get('StartAddress', '')}</code></td>"
|
||||||
|
f"<td>{m.get('Size', '')}</td>"
|
||||||
|
f"<td>{m.get('Info', '')}</td>"
|
||||||
|
"</tr>"
|
||||||
|
)
|
||||||
|
|
||||||
|
if not rows:
|
||||||
|
return "<p>(无映射数据)</p>"
|
||||||
|
|
||||||
|
header = (
|
||||||
|
"<tr>"
|
||||||
|
"<th>通道</th><th>Exaddr</th><th>Cid</th>"
|
||||||
|
"<th>模块</th><th>起始地址</th><th>大小</th><th>说明</th>"
|
||||||
|
"</tr>"
|
||||||
|
)
|
||||||
|
return f"<table>{header}{''.join(rows)}</table>"
|
||||||
|
|
||||||
|
|
||||||
|
# ---------- 寄存器表渲染 ----------
|
||||||
|
|
||||||
|
def render_daq_reg_table(data: dict) -> str:
|
||||||
|
"""渲染 DAQ_REG 寄存器汇总表 + 位域明细。"""
|
||||||
|
regs = data.get("Modules", {}).get("DAQ_REG", {})
|
||||||
|
return _render_reg_table(regs, "DAQ_REG")
|
||||||
|
|
||||||
|
|
||||||
|
def render_awg_reg_table(data: dict) -> str:
|
||||||
|
"""渲染 AWG_REG 寄存器汇总表 + 位域明细。"""
|
||||||
|
regs = data.get("Modules", {}).get("AWG_REG", {})
|
||||||
|
return _render_reg_table(regs, "AWG_REG")
|
||||||
|
|
||||||
|
|
||||||
|
def _render_reg_table(regs: dict, label: str) -> str:
|
||||||
|
"""通用寄存器表渲染(汇总表 + 每位域明细)。"""
|
||||||
|
if not regs:
|
||||||
|
return "<p>(无寄存器数据)</p>"
|
||||||
|
|
||||||
|
# 按偏移地址排序
|
||||||
|
sorted_regs = sorted(regs.values(), key=lambda r: int(r.get("OffsetAddress", "0x0"), 16))
|
||||||
|
|
||||||
|
# ---- 汇总表 ----
|
||||||
|
summary_rows = []
|
||||||
|
for r in sorted_regs:
|
||||||
|
name = list(regs.keys())[list(regs.values()).index(r)]
|
||||||
|
offset = r.get("OffsetAddress", "")
|
||||||
|
perm = r.get("Permission", "")
|
||||||
|
desc = r.get("SegDescription", "")
|
||||||
|
summary_rows.append(
|
||||||
|
"<tr>"
|
||||||
|
f"<td><strong>{name}</strong></td>"
|
||||||
|
f"<td><code>{offset}</code></td>"
|
||||||
|
f"<td>{perm}</td>"
|
||||||
|
f"<td>{desc}</td>"
|
||||||
|
"</tr>"
|
||||||
|
)
|
||||||
|
|
||||||
|
summary = (
|
||||||
|
"<table>"
|
||||||
|
"<tr><th>寄存器名</th><th>偏移地址</th><th>权限</th><th>描述</th></tr>"
|
||||||
|
f"{''.join(summary_rows)}"
|
||||||
|
"</table>"
|
||||||
|
)
|
||||||
|
|
||||||
|
# ---- 位域明细表(仅含字段数 > 0 的寄存器) ----
|
||||||
|
detail_parts = []
|
||||||
|
for r in sorted_regs:
|
||||||
|
fields = r.get("Fields", [])
|
||||||
|
if not fields:
|
||||||
|
continue
|
||||||
|
name = list(regs.keys())[list(regs.values()).index(r)]
|
||||||
|
offset = r.get("OffsetAddress", "")
|
||||||
|
|
||||||
|
field_rows = []
|
||||||
|
for f in fields:
|
||||||
|
bits = f.get("Bits", [])
|
||||||
|
field_name = f.get("FieldName", "")
|
||||||
|
fdesc = (f.get("FieldDescription", "") or "").replace("\n", "<br>")
|
||||||
|
reset = f.get("ResetValue", "")
|
||||||
|
field_rows.append(
|
||||||
|
"<tr>"
|
||||||
|
f"<td><code>[{_bit_range_str(bits)}]</code></td>"
|
||||||
|
f"<td><strong>{field_name}</strong></td>"
|
||||||
|
f"<td>{reset}</td>"
|
||||||
|
f"<td>{fdesc}</td>"
|
||||||
|
"</tr>"
|
||||||
|
)
|
||||||
|
|
||||||
|
if field_rows:
|
||||||
|
detail_parts.append(
|
||||||
|
f"<h4>{name}({offset})</h4>"
|
||||||
|
"<table>"
|
||||||
|
"<tr><th>位段</th><th>字段名</th><th>复位值</th><th>描述</th></tr>"
|
||||||
|
f"{''.join(field_rows)}"
|
||||||
|
"</table>"
|
||||||
|
)
|
||||||
|
|
||||||
|
if detail_parts:
|
||||||
|
return f"{summary}<br>{''.join(detail_parts)}"
|
||||||
|
return summary
|
||||||
Loading…
Reference in New Issue