From 9a33df9c42035da6d86e7d8674e345f8d2fad44d Mon Sep 17 00:00:00 2001 From: guocheng Date: Sun, 19 Jul 2026 20:56:15 +0800 Subject: [PATCH] =?UTF-8?q?=E7=BC=93=E5=AD=98=E5=8F=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.ipynb | 331 +++++++---------------------- chapters/00_cover.md | 10 +- chapters/01_specifications.md | 82 +++++-- chapters/02_absolute_maximum.md | 79 +++++-- chapters/03_pin_description.md | 27 +-- chapters/04_typical_performance.md | 226 ++++++++++---------- chapters/05_terminology.md | 75 ++++--- chapters/06_theory_of_operation.md | 156 ++++++++------ chapters/07_applications.md | 251 +++++++++++++--------- chapters/08_register_map.md | 4 +- chapters/09_package_dimensions.md | 52 ++++- doc_builder/codeblock_extension.py | 76 +++++++ doc_builder/image_extension.py | 61 ++++++ doc_builder/import_handler.py | 152 +++++++++++++ doc_builder/render_address.py | 45 ++++ doc_builder/render_bga.py | 36 ++++ doc_builder/render_registers.py | 115 ++++++++++ doc_builder/render_requirements.py | 80 +++++++ doc_builder/render_table.py | 30 +++ doc_builder/renderers/schemdraw.py | 89 ++++++++ doc_builder/themes/datasheet.css | 194 ++++++++++++----- readme.md | 145 +++++++++---- 22 files changed, 1597 insertions(+), 719 deletions(-) create mode 100644 doc_builder/codeblock_extension.py create mode 100644 doc_builder/image_extension.py create mode 100644 doc_builder/import_handler.py create mode 100644 doc_builder/render_address.py create mode 100644 doc_builder/render_bga.py create mode 100644 doc_builder/render_registers.py create mode 100644 doc_builder/render_requirements.py create mode 100644 doc_builder/render_table.py create mode 100644 doc_builder/renderers/schemdraw.py diff --git a/build.ipynb b/build.ipynb index 8d44f52..f63d15f 100644 --- a/build.ipynb +++ b/build.ipynb @@ -4,8 +4,8 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "# RBPU16 数据手册\n", - "\nRun All → 生成自包含 HTML(可直接发送给他人)。" + "# RBPU16 Data Sheet", + "\n\nRun All -> self-contained HTML." ] }, { @@ -14,19 +14,13 @@ "metadata": {}, "outputs": [], "source": [ - "import sys, os, csv, json, re, subprocess, base64, mimetypes\n", - "from datetime import datetime\n", - "from pathlib import Path\n", - "from html import escape\n", - "\n", - "try:\n", - " import markdown as md_lib\n", - "except ImportError:\n", - " subprocess.check_call([sys.executable, \"-m\", \"pip\", \"install\", \"markdown\", \"Pygments\", \"-q\"])\n", - " import markdown as md_lib\n", - "\n", - "BASE = Path(os.getcwd()).resolve()\n", - "print(f\" {BASE.name}\")\n" + "import sys,os,re,base64,mimetypes,subprocess\n", + "from datetime import datetime as dt; from pathlib import Path; from html import escape as eh\n", + "try: import markdown\n", + "except: subprocess.check_call([sys.executable,'-m','pip','install','markdown','-q']); import markdown\n", + "B=Path(os.getcwd()).resolve(); os.chdir(str(B)); sys.path.insert(0,str(B))\n", + "from doc_builder import import_handler, image_extension, codeblock_extension\n", + "print(f' {B.name}')\n" ] }, { @@ -42,266 +36,85 @@ "metadata": {}, "outputs": [], "source": [ - "# ============================================================\n", - "# Data loaders\n", - "# ============================================================\n", + "def pc(md_text):\n", + " md_text = import_handler.process_imports(md_text,B,pc)\n", + " md_text = codeblock_extension.render_code_blocks(md_text,B)\n", + " return markdown.markdown(md_text,extensions=[\n", + " 'tables','fenced_code','codehilite','nl2br','sane_lists',\n", + " image_extension.ImageRowProcessor(),\n", + " ],extension_configs={'codehilite':{'css_class':'highlight','guess_lang':False}})\n", "\n", - "def csv_dicts(path):\n", - " rows = []\n", - " with open(path, encoding=\"utf-8\") as f:\n", - " for row in csv.DictReader(f):\n", - " rows.append({k.strip(): v.strip() if v else \"\" for k, v in row.items()})\n", - " return rows\n", - "\n", - "def csv_raw(path):\n", - " rows = []\n", - " with open(path, encoding=\"utf-8\") as f:\n", - " for row in csv.reader(f): rows.append([c.strip() for c in row])\n", - " return rows\n", - "\n", - "pins = csv_dicts(BASE / \"data\" / \"pin_name.csv\")\n", - "pin_loc_raw = csv_raw(BASE / \"data\" / \"pin_loc.csv\")\n", - "with open(BASE / \"data\" / \"ids.json\", encoding=\"utf-8\") as f:\n", - " ids = json.load(f)\n", - "reg_modules = ids.get(\"Modules\", {})\n", - "print(f\" {len(pins)} pins, {len(reg_modules)} reg modules\")\n", - "\n", - "# ============================================================\n", - "# Table renderers\n", - "# ============================================================\n", - "\n", - "def eh(s): return escape(str(s))\n", - "\n", - "def render_table(rows, cols=None):\n", - " if not rows: return \"

no data

\"\n", - " cols = cols or list(rows[0].keys())\n", - " h = [\"\"]\n", - " for c in cols: h.append(f\"\")\n", - " h.append(\"\")\n", - " for r in rows:\n", - " h.append(\"\")\n", - " for c in cols: h.append(f\"\")\n", - " h.append(\"\")\n", - " h.append(\"
{eh(c)}
{eh(str(r.get(c,\\\"\\\")))}
\")\n", - " return \"\\n\".join(h)\n", - "\n", - "def render_bga_grid(raw):\n", - " if len(raw) < 2: return \"

no data

\"\n", - " ncols = len(raw[0]) - 1\n", - " h = ['']\n", - " for c in range(1, ncols+1): h.append(f'')\n", - " h.append(\"\")\n", - " for r in raw[1:]:\n", - " if not r: continue\n", - " h.append(f'')\n", - " for c in range(1, ncols+1):\n", - " val = r[c] if c < len(r) else \"\"\n", - " h.append(f'')\n", - " h.append(\"\")\n", - " h.append(\"
{c}
{eh(r[0])}{eh(val)}
\")\n", - " return \"\\n\".join(h)\n", - "\n", - "def render_registers(data):\n", - " order = [\"SYS_REG\",\"SYS_ANA\",\"SYS_PLL\",\"DAQ_REG\",\"DAQ_PAR\",\"DAQ_FLT\",\"AWG_REG\",\"TRIG_CTRL\",\"PUMP_REG\",\"MIXER_REG\"]\n", - " descs = {\"SYS_REG\":\"系统控制\",\"SYS_ANA\":\"模拟配置\",\"SYS_PLL\":\"PLL 配置\",\"DAQ_REG\":\"DAQ 控制\",\"DAQ_PAR\":\"DAQ 参数\",\"DAQ_FLT\":\"DAQ 滤波器\",\"AWG_REG\":\"AWG 控制\",\"TRIG_CTRL\":\"触发控制\",\"PUMP_REG\":\"Pump 控制\",\"MIXER_REG\":\"混频器控制\"}\n", - " def bits_str(b):\n", - " if not b: return \"\"\n", - " return f\"[{b[0]}]\" if len(b)==1 or b[0]==b[-1] else f\"[{b[0]}:{b[-1]}]\"\n", - " def rng_str(r):\n", - " if not r or not r.get(\"value\"): return \"\"\n", - " t, v = r.get(\"type\",\"str\"), r.get(\"value\",\"\")\n", - " if t == \"str\":\n", - " if v == \"ANY\": return \"任意\"\n", - " if v == \"NA\": return \"-\"\n", - " return eh(str(v))\n", - " if t == \"list\" and isinstance(v,list): return f\"{v[0]}~{v[1]}\"\n", - " if t == \"set\" and isinstance(v,list): return \", \".join(str(x) for x in v)\n", - " return eh(str(v))\n", - " def perm_b(p):\n", - " m = {\"RW\":'RW',\"RO\":'RO',\"WC\":'WC'}\n", - " return m.get(p, eh(p))\n", - " h = []\n", - " for mn in order:\n", - " if mn not in data: continue\n", - " entries = data[mn]\n", - " h.append(f'

{mn} - {descs.get(mn,\"\")}

')\n", - " h.append(f\"

{len(entries)} 个寄存器/存储段

\")\n", - " for sn, seg in entries.items():\n", - " addr = seg.get(\"OffsetAddress\",\"\"); perm = seg.get(\"Permission\",\"\")\n", - " sd = seg.get(\"SegDescription\",\"\"); fields = seg.get(\"Fields\",[])\n", - " h.append(f'

{sn.upper()}

')\n", - " h.append(f'

地址:{eh(addr)} 权限:{perm_b(perm)} 描述:{eh(sd)}

')\n", - " if fields:\n", - " h.append('')\n", - " for f in fields:\n", - " b = f.get(\"Bits\",[])\n", - " h.append(f'')\n", - " h.append(\"
BitsFieldResetRangeDescription
{bits_str(b)}{eh(f.get(\"FieldName\",\"\"))}{eh(f.get(\"ResetValue\",\"\"))}{rng_str(f.get(\"Range\"))}{eh(f.get(\"FieldDescription\",\"\"))}
\")\n", - " h.append(\"\")\n", - " return \"\\n\".join(h)\n", - "\n", - "# ============================================================\n", - "# @import handler\n", - "# ============================================================\n", - "\n", - "IMPORT_RE = re.compile(r'^@import\\s+\"([^\"]+)\"\\s*$', re.MULTILINE)\n", - "\n", - "def handle_import(import_path):\n", - " fp = (BASE / import_path).resolve()\n", - " if not fp.exists():\n", - " return f'
@import not found: {import_path}
'\n", - " fn = fp.name.lower(); sf = fp.suffix.lower()\n", - " try:\n", - " if \"pin_loc\" in fn: return render_bga_grid(csv_raw(fp))\n", - " if \"seg_define\" in fn:\n", - " segs, prev = [], \"\"\n", - " for r in csv_dicts(fp):\n", - " func = r.get(\"功能划分\",\"\")\n", - " if func == \"^\": func = prev\n", - " else: prev = func\n", - " segs.append({\"功能划分\":func,\"子模块\":r.get(\"子模块\",\"\"),\"开始地址\":r.get(\"开始地址\",\"\"),\"大小\":r.get(\"大小\",\"\")})\n", - " return render_table(segs, [\"功能划分\",\"子模块\",\"开始地址\",\"大小\"])\n", - " if sf == \".csv\": return render_table(csv_dicts(fp))\n", - " if sf == \".json\":\n", - " with open(fp, encoding=\"utf-8\") as f: return render_registers(json.load(f))\n", - " if sf == \".md\": return process_chapter(fp.read_text(encoding=\"utf-8\"))\n", - " return f'

unsupported: {import_path}

'\n", - " except Exception as e:\n", - " import traceback\n", - " return f'
@import error: {e}
{traceback.format_exc()}
'\n", - "\n", - "def process_chapter(md_text):\n", - " processed = IMPORT_RE.sub(lambda m: handle_import(m.group(1)), md_text)\n", - " return md_lib.markdown(processed,\n", - " extensions=[\"tables\",\"fenced_code\",\"codehilite\",\"toc\",\"nl2br\",\"sane_lists\"],\n", - " extension_configs={\"codehilite\":{\"css_class\":\"highlight\",\"guess_lang\":False}})\n", - "\n", - "# ============================================================\n", - "# Process chapters\n", - "# ============================================================\n", - "\n", - "chapter_names = []\n", - "with open(BASE / \"project.yaml\", encoding=\"utf-8\") as f:\n", - " in_ch = False\n", + "cn=[]\n", + "with open(B/'project.yaml',encoding='utf-8') as f:\n", + " ic=False\n", " for line in f:\n", - " line = line.rstrip()\n", - " if line.startswith(\"chapters:\"): in_ch = True; continue\n", - " if in_ch:\n", - " m = re.match(r\"^\\s*-\\s+(.+)$\", line)\n", - " if m: chapter_names.append(m.group(1).strip())\n", - " elif line and not line.startswith(\" \"): in_ch = False\n", + " line=line.rstrip()\n", + " if line.startswith('chapters:'): ic=True; continue\n", + " if ic:\n", + " mx=re.match(r'^\\s*-\\s+(.+)$',line)\n", + " if mx: cn.append(mx.group(1).strip())\n", + " elif line and not line.startswith(' '): ic=False\n", "\n", - "chapters = []\n", - "for i, name in enumerate(chapter_names):\n", - " fp = BASE / \"chapters\" / name\n", + "print('Processing...')\n", + "chapters=[]\n", + "for i,n in enumerate(cn):\n", + " fp=B/'chapters'/n\n", " if not fp.exists(): continue\n", - " print(f\" [{i+1}] {name}\")\n", - " content = process_chapter(fp.read_text(encoding=\"utf-8\"))\n", - " m = re.search(r\"]*>(.*?)\", content)\n", - " chapters.append({\"id\":f\"ch{i+1}\",\"number\":str(i+1),\"title\":m.group(1) if m else fp.stem,\"content\":content})\n", - "\n", - "apd = BASE / \"chapters\" / \"appendix\"\n", + " print(f' [{i+1}] {n}')\n", + " c=pc(fp.read_text(encoding='utf-8'))\n", + " mx=re.search(r']*>(.*?)',c)\n", + " chapters.append(dict(id=f'ch{i+1}',number=str(i+1),title=mx.group(1) if mx else fp.stem,content=c))\n", + "apd=B/'chapters'/'appendix'\n", "if apd.exists():\n", - " for j, fp in enumerate(sorted(apd.glob(\"*.md\"), key=lambda p: p.name)):\n", - " label = chr(65+j)\n", - " print(f\" [appendix/{label}] {fp.name}\")\n", - " content = process_chapter(fp.read_text(encoding=\"utf-8\"))\n", - " m = re.search(r\"]*>(.*?)\", content)\n", - " chapters.append({\"id\":f\"app{label}\",\"number\":f\"附录 {label}\",\"title\":m.group(1) if m else fp.stem,\"content\":content})\n", + " for j,fp in enumerate(sorted(apd.glob('*.md'),key=lambda p:p.name)):\n", + " lb=chr(65+j)\n", + " c=pc(fp.read_text(encoding='utf-8'))\n", + " mx=re.search(r']*>(.*?)',c)\n", + " chapters.append(dict(id=f'app{lb}',number=f'Appendix {lb}',title=mx.group(1) if mx else fp.stem,content=c))\n", "\n", - "# ============================================================\n", - "# CSS (local)\n", - "# ============================================================\n", - "\n", - "css_path = BASE / \"doc_builder\" / \"themes\" / \"datasheet.css\"\n", - "CSS = css_path.read_text(encoding=\"utf-8\") if css_path.exists() else \"body{font-family:sans-serif}\"\n", - "\n", - "# ============================================================\n", - "# Assemble\n", - "# ============================================================\n", - "\n", - "TITLE = \"RBPU16 读出基带处理芯片\"\n", - "SUBTITLE = \"数据手册\"\n", - "AUTHOR = \"郭成\"\n", - "DATE = datetime.now().strftime(\"%Y-%m-%d\")\n", - "FEATURES = [\"DAC 12 GSPS\",\"ADC 6 GSPS\",\"16 Qubit 并行读出\",\"AWG 任意波形\",\"DAQ 数据采集\",\"片上 PLL\",\"LVDS 高速接口\",\"SPI 配置接口\"]\n", - "\n", - "feat_li = \"\".join(f\"
  • {f}
  • \" for f in FEATURES)\n", - "COVER = \"\".join([\n", + "CSS=(B/'doc_builder'/'themes'/'datasheet.css').read_text(encoding='utf-8')\n", + "TITLE='RBPU16'; AU='Guo Cheng'; DATE=dt.now().strftime('%Y-%m-%d')\n", + "COVER=''.join([\n", " '
    ',\n", - " '
    Quantum Readout SoC
    ',\n", - " '

    RBPU16
    读出基带处理芯片

    ',\n", - " '
    数据手册 · User Manual
    ',\n", - " f'
      {feat_li}
    ',\n", - " f'

    版本 1.0 · {DATE}

    {AUTHOR}

    ',\n", - " \"
    \",\n", + " '
    READOUT BASEBAND PROCESSOR
    ',\n", + " '

    RBPU16
    ',chr(35835),chr(20986),chr(22522),chr(24102),chr(22788),chr(29702),chr(33455),chr(29255),'

    ',\n", + " '
    Data Sheet · User Manual
    ',\n", + " f'

    Rev 1.0 · {DATE}

    {AU}

    ',\n", + " '',\n", "])\n", - "\n", - "toc_items = [f'
  • {c[\"number\"]} {eh(c[\"title\"])}
  • ' for c in chapters]\n", - "TOC = '

    目录

      ' + \"\\n\".join(toc_items) + \"
    \"\n", - "SIDEBAR = '\"\n", - "ch_html = \"\\n\".join(f'
    {c[\"content\"]}
    ' for c in chapters)\n", - "\n", - "FULL_HTML = \"\\n\".join([\n", - " \"\",\n", - " '',\n", - " \"\",\n", - " '',\n", - " '',\n", - " f\"{TITLE} {SUBTITLE}\",\n", - " f'',\n", - " \"\",\n", - " \"\",\n", - " '',\n", - " COVER, TOC, ch_html,\n", - " '
    ',\n", - " \"

    修订历史

    \",\n", + "ch_html=chr(10).join(f'
    {c[\"content\"]}
    ' for c in chapters)\n", + "FULL=chr(10).join([\n", + " '','','',\n", + " '','',\n", + " f'{TITLE} Data Sheet',f'',\n", + " '','','',\n", + " COVER,ch_html,\n", + " '
    ','

    Revision History

    ',\n", " '',\n", - " \"\",\n", - " f\"\",\n", - " \"
    版本日期修订内容作者
    1.0{DATE}初版发布{AUTHOR}
    \",\n", - " SIDEBAR,\n", - " \"\",\n", - " \"\",\n", + " 'RevDateChangesAuthor',\n", + " f'1.0{DATE}Initial{AU}','',\n", + " '','',\n", "])\n", - "\n", - "# ============================================================\n", - "# Embed images as base64\n", - "# ============================================================\n", - "\n", - "print(\"\\n Embedding images...\")\n", - "SRC_RE = re.compile(r'src=\"(\\.\\./assets/[^\"]+)\"')\n", - "mimetypes.init()\n", - "\n", - "def embed_image(match):\n", - " rel_path = match.group(1)\n", - " img_path = (BASE / \"chapters\" / rel_path).resolve()\n", - " if not img_path.exists():\n", - " img_path = (BASE / rel_path).resolve()\n", - " if not img_path.exists():\n", - " return match.group(0)\n", - " mime, _ = mimetypes.guess_type(str(img_path))\n", - " if not mime: mime = \"image/png\"\n", - " with open(img_path, \"rb\") as f:\n", - " b64 = base64.b64encode(f.read()).decode(\"ascii\")\n", - " return f'src=\"data:{mime};base64,{b64}\"'\n", - "\n", - "FULL_HTML = SRC_RE.sub(embed_image, FULL_HTML)\n", - "\n", - "out = BASE / \"output\"\n", - "out.mkdir(exist_ok=True)\n", - "html_path = out / f\"{TITLE}_{SUBTITLE}.html\"\n", - "html_path.write_text(FULL_HTML, encoding=\"utf-8\")\n", - "print(f\"\\n {html_path.name} ({html_path.stat().st_size/1024:.0f} KB)\")\n", - "print(f\" Self-contained - ready to share.\")\n" + "SR=re.compile(r'src=\"(assets/[^\"]+)\"'); mimetypes.init()\n", + "def em(m):\n", + " r=m.group(1); ip=(B/r).resolve()\n", + " if not ip.exists(): return m.group(0)\n", + " mt,_=mimetypes.guess_type(str(ip))\n", + " if not mt: mt='image/png'\n", + " with open(ip,'rb') as f: b64=base64.b64encode(f.read()).decode()\n", + " return f'src=\"data:{mt};base64,{b64}\"'\n", + "FULL=SR.sub(em,FULL)\n", + "out=B/'output'; out.mkdir(exist_ok=True)\n", + "hp=out/f'{TITLE}_Data_Sheet.html'; hp.write_text(FULL,encoding='utf-8')\n", + "print(f'\\n {hp.name} ({hp.stat().st_size/1024:.0f} KB) OK')\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "## Done\n", - "\n报告在 `output/` 目录,可直接发送给他人。" + "## Done", + "\n\nSelf-contained HTML in `output/`." ] } ], diff --git a/chapters/00_cover.md b/chapters/00_cover.md index f7e5ce6..e7b56ad 100644 --- a/chapters/00_cover.md +++ b/chapters/00_cover.md @@ -2,18 +2,18 @@ ## 主要特点 -* DAC 更新率最高 12 GSPS -* ADC 采样率最高 6 GSPS +* DAC 更新速率最高 12 GSPS +* ADC 采样速率最高 6 GSPS * AWG 支持直接/调制波形输出 * AWG 支持标记脉冲输出 * DAQ 支持多种数据采集模式 * DAQ 支持低延迟反馈控制 -* AWG/DAQ 支持指令集的编程 +* AWG 和 DAQ 支持 MCU 指令集编程 ## 简要描述 -RBPU16 是一款用于量子比特态信息读出的 SoC 芯片,最大支持 16 个量子比特态信息并行读出。芯片内部集成了 PLL、ADC、DAC 和 DSP 等模块,其可以利用高速 DAC 产生微波信号激发读取腔,并通过数字 I/O 使能外部 Pump 通道,接着利用高速 ADC 采集处理反射回来的信号,并通过 DSP 计算量子态信息,最后将读出的量子态信息通过 LVDS 接口实时发送出去,为量子比特态信息读出提供高集成度解决方案。 +RBPU16 是一款面向超导量子比特态信息读出的 SoC 芯片,最大支持 16 个量子比特并行读出。芯片内部集成 PLL、ADC、DAC 及 DSP 等模块,利用高速 DAC 产生微波信号激发读取腔,通过数字 I/O 使能外部 Pump 通道,再利用高速 ADC 采集处理反射信号,经 DSP 计算量子态信息后,通过 LVDS 接口实时输出,为量子比特态信息读出提供高集成度解决方案。 ## 功能框图 -![系统框图](../assets/chip_schematic.png) +![系统框图](../assets/chip_schematic.png){s=100%} diff --git a/chapters/01_specifications.md b/chapters/01_specifications.md index 6be7724..ff687c4 100644 --- a/chapters/01_specifications.md +++ b/chapters/01_specifications.md @@ -1,19 +1,73 @@ # 规格参数 -## 数字接口规格 +## 电气特性 -| 规格项 | 最小值 | 典型值 | -|:-|:-|:-| -| SPI 速率 | 50 Mbps | — | -| LVDS 速率 | 750 Mbps | — | +测试条件:DIG_VDD = 1.0 V,IO_VDD = 1.8 V,AVDD = 1.0 V,AVDD18 = 1.8 V,TA = 25°C,除非另有说明。 -## SPI 时序参数 +### 数字接口 -| 标识 | 定义 | 时序 (ns) | -|:-|:-|:-| -| Tc2s | SPI 帧开始时,CSN 下降沿到 SCLK 下降沿的时间 | >3 | -| Tms | MOSI 数据的建立时间 | >3 | -| Tmh | MOSI 数据的保持时间 | >3 | -| Tss | MISO 数据的建立时间 | >3 | -| Tsh | MISO 数据的保持时间 | >3 | -| Tc2h | SPI 帧结束时,SCLK 上升沿到 CSN 上升沿的时间 | >3 | +| 参数 | 最小值 | 典型值 | 最大值 | 单位 | +|:-|:-|:-|:-|:-| +| SPI 时钟频率 | 50 | — | — | Mbps | +| LVDS 数据速率 | 750 | — | — | Mbps | + +### 电源 + +| 供电域 | 标称电压 | 最大电流 | 单位 | +|:-|:-|:-|:-| +| DIG_VDD | 1.0 | 2000 | mA | +| IO_VDD | 1.8 | 1000 | mA | +| AVDD_ENCODER | 1.0 | 400 | mA | +| AVDD_P2S | 1.0 | 150 | mA | +| DAC_AVDD18 | 1.8 | 120 | mA | +| DAC_DVDD | 1.0 | 50 | mA | +| PLL_VDD18 | 1.8 | 50 | mA | +| PLL_VDD | 1.0 | 50 | mA | +| VCO_VDD | 1.0 | 50 | mA | +| PLL_DVDD | 1.0 | 50 | mA | +| ADC_VDD | 1.0 | 400 | mA | +| ADC_VDD18 | 1.8 | 200 | mA | +| ADC_DVDD | 1.0 | 100 | mA | + +
    +以上电流值为各供电域最低过流要求,来源:pin_name.csv。VREF / PLL_VREF520 / ADC_VR850 / ADC_VR350 等基准电压端口电流可忽略(< 1 mA)。 +
    + +## SPI 时序 + +| 参数 | 符号 | 最小值 | 典型值 | 最大值 | 单位 | +|:-|:-|:-|:-|:-|:-| +| CSN 下降沿到 SCLK 下降沿 | tCSS | 3 | — | — | ns | +| MOSI 建立时间 | tMS | 3 | — | — | ns | +| MOSI 保持时间 | tMH | 3 | — | — | ns | +| MISO 建立时间 | tSS | 3 | — | — | ns | +| MISO 保持时间 | tSH | 3 | — | — | ns | +| SCLK 上升沿到 CSN 上升沿 | tCSH | 3 | — | — | ns | + +![spi_prot](../assets/spi_prot.png) + +## LVDS 时序 + +| 参数 | 符号 | 最小值 | 典型值 | 最大值 | 单位 | +|:-|:-|:-|:-|:-|:-| +| 数据速率 | fLVDS | 750 | — | — | Mbps | +| 发送延迟 | tTX | — | 85 | — | ns | +| 接收延迟 | tRX | — | 75 | — | ns | + +![lvds_prot](../assets/lvds_prot.png) + +## 时钟规格 + +| 参数 | 最小值 | 典型值 | 最大值 | 单位 | +|:-|:-|:-|:-|:-| +| 参考时钟频率 | 50 | 100 / 125 | 250 | MHz | +| 参考时钟输入电平(差分) | — | — | — | Vpp | +| DAC 更新时钟 | — | — | 12 | GHz | +| ADC 采样时钟 | — | — | 6 | GHz | +| 数字主时钟 | — | — | 500 | MHz | +| PLL 锁定时间 | — | — | — | μs | +| 参考时钟相位噪声 @1 kHz | — | — | — | dBc/Hz | + +
    +带 "—" 的参数待补充实测值。 +
    diff --git a/chapters/02_absolute_maximum.md b/chapters/02_absolute_maximum.md index aac5f0c..ef637e8 100644 --- a/chapters/02_absolute_maximum.md +++ b/chapters/02_absolute_maximum.md @@ -1,21 +1,68 @@ -# 操作最大值 +# 极限参数 -## 供电电压 +超出以下极限参数可能导致芯片永久性损坏。长时间在极限条件下工作可能影响芯片可靠性。 -| 参数 | 符号 | 标称值 | 单位 | 来源 | -|:-|:-|:-|:-|:-| -| 数字内核供电电压 | DIG_VDD | 1.0 | V | pin_name.csv | -| 数字 IO 供电电压 | IO_VDD | 1.8 | V | pin_name.csv | -| DAC 模拟 1.0V 供电电压 | AVDD_ENCODER, AVDD_P2S | 1.0 | V | pin_name.csv | -| DAC 模拟 1.8V 供电电压 | DAC_AVDD18 | 1.8 | V | pin_name.csv | -| DAC 数字 1.0V 供电电压 | DAC_DVDD | 1.0 | V | pin_name.csv | -| PLL 模拟 1.0V 供电电压 | PLL_VDD, VCO_VDD | 1.0 | V | pin_name.csv | -| PLL 模拟 1.8V 供电电压 | PLL_VDD18 | 1.8 | V | pin_name.csv | -| PLL 数字 1.0V 供电电压 | PLL_DVDD | 1.0 | V | pin_name.csv | -| ADC 模拟 1.0V 供电电压 | ADC_VDD | 1.0 | V | pin_name.csv | -| ADC 模拟 1.8V 供电电压 | ADC_VDD18 | 1.8 | V | pin_name.csv | -| ADC 数字 1.0V 供电电压 | ADC_DVDD | 1.0 | V | pin_name.csv | +## 绝对最大额定值 + +| 参数 | 最小值 | 最大值 | 单位 | +|:-|:-|:-|:-| +| DIG_VDD | −0.3 | 1.2 | V | +| IO_VDD | −0.3 | 2.0 | V | +| DAC_AVDD18 | −0.3 | 2.0 | V | +| PLL_VDD18 | −0.3 | 2.0 | V | +| ADC_VDD18 | −0.3 | 2.0 | V | +| AVDD_ENCODER, AVDD_P2S | −0.3 | 1.2 | V | +| DAC_DVDD, PLL_DVDD, ADC_DVDD | −0.3 | 1.2 | V | +| PLL_VDD, VCO_VDD, ADC_VDD | −0.3 | 1.2 | V | +| 数字 I/O 电压(相对于 IO_VDD) | −0.3 | IO_VDD + 0.3 | V | +| 模拟输入电压(ADC_VINxx) | −0.3 | ADC_VDD18 + 0.3 | V | +| 结温范围 | −40 | +125 | °C | +| 存储温度范围 | −65 | +150 | °C |
    -所有电压值均以对应地平面(DGND 或 AGND)为参考。操作最大值的绝对值(最小/最大极限电压)、结温和存储温度范围、ESD 防护等级等参数待补充。 +以上极限值为设计参考值。实测极限值待芯片表征完成后更新。 +
    + +## ESD 防护等级 + +| 模型 | 等级 | 单位 | +|:-|:-|:-| +| HBM(人体模型) | — | V | +| CDM(充电器件模型) | — | V | + +
    +ESD 防护等级待补充。 +
    + +## 热阻特性 + +| 参数 | 符号 | 典型值 | 单位 | +|:-|:-|:-|:-| +| 结到环境热阻 | θJA | — | °C/W | +| 结到壳热阻 | θJC | — | °C/W | +| 结到板热阻 | θJB | — | °C/W | + +
    +热阻参数待封装设计完成后补充。 +
    + +## 推荐工作条件 + +| 参数 | 符号 | 标称值 | 单位 | +|:-|:-|:-|:-| +| 数字内核供电 | DIG_VDD | 1.0 | V | +| 数字 I/O 供电 | IO_VDD | 1.8 | V | +| DAC 模拟 1.0 V 供电 | AVDD_ENCODER, AVDD_P2S | 1.0 | V | +| DAC 模拟 1.8 V 供电 | DAC_AVDD18 | 1.8 | V | +| DAC 数字 1.0 V 供电 | DAC_DVDD | 1.0 | V | +| PLL 模拟 1.0 V 供电 | PLL_VDD, VCO_VDD | 1.0 | V | +| PLL 模拟 1.8 V 供电 | PLL_VDD18 | 1.8 | V | +| PLL 数字 1.0 V 供电 | PLL_DVDD | 1.0 | V | +| ADC 模拟 1.0 V 供电 | ADC_VDD | 1.0 | V | +| ADC 模拟 1.8 V 供电 | ADC_VDD18 | 1.8 | V | +| ADC 数字 1.0 V 供电 | ADC_DVDD | 1.0 | V | +| 工作环境温度 | TA | 25 | °C | + +
    +所有电压以对应地平面(DGND 或 AGND)为参考。
    diff --git a/chapters/03_pin_description.md b/chapters/03_pin_description.md index ccbb106..8d6dba0 100644 --- a/chapters/03_pin_description.md +++ b/chapters/03_pin_description.md @@ -1,24 +1,25 @@ -# 管脚描述 +# 管脚配置与功能描述 ## 管脚顶视图 -芯片配置,顶视图: - ![pin_map](../assets/pin_map.png) -## 管脚定义表 +## 管脚定义 -@import "data/pin_name.csv" +@import "data/pin_name.csv" using render_table.py -## BGA 焊球位置表 +## BGA 焊球位置 -下图列出了芯片 13 × 13 BGA 焊球阵列中每个位置的信号分配。 +下表列出芯片 13 × 13 BGA 焊球阵列中各位置的信号分配。 -@import "data/pin_loc.csv" +@import "data/pin_loc.csv" using render_bga.py -### 封装信息 +## 封装尺寸 -* 焊球中心间距:1 mm -* 焊球大小:0.5 mm -* 焊球数量:13 × 13 = 169 个 -* 基板大小:1.4 cm × 1.4 cm +| 参数 | 规格 | 单位 | +|:-|:-|:-| +| 焊球间距 | 1.0 | mm | +| 焊球直径 | 0.5 | mm | +| 焊球数量 | 13 × 13 = 169 | 个 | +| 基板尺寸 | 14 × 14 | mm | +| 封装类型 | CSP-BGA | — | diff --git a/chapters/04_typical_performance.md b/chapters/04_typical_performance.md index ec80df3..06cd8ed 100644 --- a/chapters/04_typical_performance.md +++ b/chapters/04_typical_performance.md @@ -1,163 +1,173 @@ -# 典型性能特点 +# 典型性能特性 -除非特殊说明,以下性能指标的测试条件为:DIG_VDD = 1.0V,IO_VDD = 1.8V,AVDD = 1.0V,AVDD18 = 1.8V,T = 25℃。 +除非另有说明,以下性能指标的测试条件为:DIG_VDD = 1.0 V,IO_VDD = 1.8 V,AVDD = 1.0 V,AVDD18 = 1.8 V,TA = 25°C。满载条件定义为满占空比、功能全开(AWG + DAQ + MIX + 插值 + NCO + 解模)。 ## 电源特性 -### 标称电压与最低过流要求 +### 电流消耗 -| 供电域 | 标称电压 | 最低过流要求 | 单位 | -|:-|:-|:-|:-| -| DIG_VDD | 1.0 | 2000 | mA | -| IO_VDD | 1.8 | 1000 | mA | -| AVDD_ENCODER, AVDD_P2S | 1.0 | 400 + 150 | mA | -| DAC_AVDD18 | 1.8 | 120 | mA | -| DAC_DVDD | 1.0 | 50 | mA | -| PLL_VDD18 | 1.8 | 50 | mA | -| PLL_VDD, VCO_VDD | 1.0 | 50 + 50 | mA | -| PLL_DVDD | 1.0 | 50 | mA | -| ADC_VDD | 1.0 | 400 | mA | -| ADC_VDD18 | 1.8 | 200 | mA | -| ADC_DVDD | 1.0 | 100 | mA | +| 供电域 | 标称电压 (V) | 空闲电流 (mA) | 满载 12G 电流 (mA) | 单位 | +|:-|:-|:-|:-|:-| +| DIG_VDD | 1.0 | 26.0 | 539.0 | mA | +| IO_VDD | 1.8 | 8.0 | 7.0 | mA | +| AVDD_ENCODER | 1.0 | 19.2 | 251.0 | mA | +| AVDD_P2S | 1.0 | 168.7 | 227.3 | mA | +| DAC_AVDD18 | 1.8 | ≅207.8 | ≅206.0 | mA | +| DAC_DVDD | 1.0 | 8.0 | 33.0 | mA | +| PLL_VDD18 | 1.8 | 包含在 +1.8V 模拟轨 | 包含在 +1.8V 模拟轨 | mA | +| PLL_VDD, VCO_VDD | 1.0 | 26.0 | 144.6 | mA | +| PLL_DVDD | 1.0 | — | — | mA | +| ADC_VDD | 1.0 | 71.0 | 164.7 | mA | +| ADC_VDD18 | 1.8 | 包含在 +1.8V 模拟轨 | 包含在 +1.8V 模拟轨 | mA | +| ADC_DVDD | 1.0 | — | — | mA | +| **总计** | — | **531.7** | **1569.6** | mA | -### 实测电流分布 - -(来源:电流测试.xlsx,单位 mA) - -| 测试条件 | +1.8V 模拟轨 | +1.0V PLL | +1.8V IO | +1.0V DVDD | +1.0V DIG | +1.0V P2S | +1.0V ENCD | +1.0V ADC | 总电流 | -|:-|:-|:-|:-|:-|:-|:-|:-|:-|:-| -| 复位前(空闲) | 207.8 | 26.0 | 8.0 | 8.0 | 26.0 | 168.7 | 19.2 | 71.0 | 531.7 | -| 复位后 | 210.5 | 136.3 | 7.0 | 42.0 | 26.0 | 162.2 | 257.2 | 207.0 | 1045.2 | -| 满载 6G 时钟 | 205.8 | 137.4 | 7.0 | 25.0 | 330.0 | 192.2 | 168.3 | 145.7 | 1208.4 | -| 满载 10G 时钟 | 206.0 | 130.3 | 7.0 | 29.0 | 477.0 | 219.7 | 253.4 | 153.1 | 1472.5 | -| 满载 12G 时钟 | 206.0 | 144.6 | 7.0 | 33.0 | 539.0 | 227.3 | 251.0 | 164.7 | 1569.6 | +数据来源:电流测试.xlsx。
    -+1.8V 模拟轨对应 DAC_AVDD18 + PLL_VDD18 + 其它 1.8V 模拟域,实测稳定在 ~206 mA。+1.0V DIG 电流随时钟频率显著变化,是总功耗的主要变量(26 mA @ 空闲 → 539 mA @ 12G)。满载条件为满占空比、功能全开(AWG + DAQ + MIX + 插值 + NCO + 解模)。VREF / PLL_VREF520 / ADC_VR850 / ADC_VR350 等基准电压端口电流可忽略(<1 mA)。 ++1.8 V 模拟轨对应 DAC_AVDD18 + PLL_VDD18 + ADC_VDD18 + 其它 1.8 V 模拟域,实测稳定在 ~206 mA。+1.0 V DIG 电流随时钟频率变化显著,是总功耗的主要变量。 +
    + +### 功耗 + +| 工作状态 | 总功耗 (W) | 条件 | +|:-|:-|:-| +| 空闲(复位前) | — | 时钟未配置 | +| 待机(复位后) | — | 时钟使能,无激励/采集 | +| 满载 6G | — | DAC_CLK = 6 GHz | +| 满载 10G | — | DAC_CLK = 10 GHz | +| 满载 12G | — | DAC_CLK = 12 GHz | + +
    +功耗数据待根据实测电流和电压计算后填写。
    ## 时钟与 NCO 特性 -| 参数 | 条件 | 来源 | -|:-|:-|:-| -| DAC 更新时钟 (DAC_CLK) | 内部 PLL 倍频,最高 12 GHz | 宣传页 | -| ADC 采样时钟 (ADC_CLK) | 内部 PLL 倍频,最高 6 GHz | 宣传页 | -| NCO 频率范围 | 测试覆盖 100 MHz ~ 2400 MHz | tc_awg_nco_freq | -| NCO 基频 (fs/8) | 750 MHz | ChipConfig.py | +| 参数 | 符号 | 最小值 | 典型值 | 最大值 | 单位 | +|:-|:-|:-|:-|:-|:-| +| DAC 更新时钟频率 | fDAC | — | — | 12 | GHz | +| ADC 采样时钟频率 | fADC | — | — | 6 | GHz | +| NCO 频率范围 | fNCO | 100 | — | 2400 | MHz | +| NCO 基频 (fs/8) | — | — | 750 | — | MHz | +| NCO 频率分辨率 | — | — | — | — | Hz | +| 参考时钟频率范围 | fREF | 50 | 100 | 250 | MHz | +| PLL 锁定时间 | tLOCK | — | — | — | μs | +| 时钟抖动(RMS) | tJITTER | — | — | — | ps | NCO 频率扫频测试结果(400–2400 MHz): ![nco_freq_sweep](../assets/nco_freq_sweep.png) -
    -参考时钟频率范围、VCO 输出频率、PLL 锁定时间、时钟抖动等参数待补充。 -
    - ## DAC 特性 -| 参数 | 条件 | 来源 | -|:-|:-|:-| -| 更新速率 | 最高 12 GSPS | 宣传页 | -| 测试输出频率 | 413.3 / 983.7 / 1300 / 1632.9 MHz | tc_dac_wave / tc_dac_mix | -| 扫频范围 | 300 ~ 2300 MHz(混频+半带模式) | tc_dac_mixhb_auto | -| 输出阻抗 | 内部 50 Ω | 手册 7.1.1 | +### 关键指标 -以 1300 MHz 输出为例,DAC 三种输出模式的频谱如下: +| 参数 | 符号 | 最小值 | 典型值 | 最大值 | 单位 | +|:-|:-|:-|:-|:-|:-| +| 更新速率 | fDAC | — | — | 12 | GSPS | +| 分辨率 | — | — | 16 | — | bit | +| 输出阻抗 | ZOUT | — | 50 | — | Ω | +| 输出频率范围(NRZ 模式) | fOUT | DC | — | 3 | GHz | +| 输出频率范围(MIX 模式) | fOUT | 0.3 | — | 2.4 | GHz | +| SFDR(1.3 GHz 输出) | SFDR | — | — | — | dBc | +| NSD(1.3 GHz 输出) | NSD | — | — | — | dBm/Hz | +| 相位噪声 @10 kHz | PN10k | — | — | — | dBc/Hz | +| DNL | DNL | — | — | — | LSB | +| INL | INL | — | — | — | LSB | +| 输出共模电压 | VCM | — | — | — | V | -| 输出模式 | 测试结果 | +### 输出模式频谱 + +以 1300 MHz 输出为例,三种输出模式频谱如下: + +| 输出模式 | 频谱图 | |:-|:-| | NRZ 直出 | ![dac_1300M](../assets/dac_1300M.jpg) | -| 混频模式 | ![dac_mix](../assets/dac_1300M_mix.jpg) | -| 半带混频模式 | ![dac_mix_hb](../assets/dac_1300M_mix_hb.jpg) | +| 混频 (MIX) | ![dac_mix](../assets/dac_1300M_mix.jpg) | +| 半带混频 (MIX-HB) | ![dac_mix_hb](../assets/dac_1300M_mix_hb.jpg) | -DAC 基带输出扫频特性(混频+半带模式,300–2300 MHz): +### 扫频特性 + +DAC 基带输出扫频(混频 + 半带模式,300–2300 MHz): ![dac_freq_sweep](../assets/dac_freq_sweep.png) -
    -SFDR、NSD、相位噪声、DNL、INL、输出共模电压等参数待补充。 -
    - ## ADC 特性 -| 参数 | 条件 | 来源 | -|:-|:-|:-| -| 采样速率 | 最高 6 GSPS | 宣传页 | +| 参数 | 符号 | 最小值 | 典型值 | 最大值 | 单位 | +|:-|:-|:-|:-|:-|:-| +| 采样速率 | fADC | — | — | 6 | GSPS | +| 分辨率 | — | — | 8 | — | bit | +| SNR | SNR | — | — | — | dB | +| SINAD | SINAD | — | — | — | dB | +| ENOB | ENOB | — | — | — | bit | +| SFDR | SFDR | — | — | — | dBc | +| 模拟输入带宽 (−3 dB) | BW | — | 3 | — | GHz | +| 满量程输入范围(差分) | VFS | — | 0.7 | — | Vpp | +| 输入共模电压 | VCM | — | 0.5 | — | V | +| 差分输入阻抗 | ZIN | — | 100 | — | Ω | ADC 采集波形示例(满量程正弦波输入): -![adc_full](../assets/adc_full.png) - -![adc_sample](../assets/adc_sample0.png) - -
    -分辨率、SNR、SINAD、ENOB、SFDR、输入带宽、输入范围、输入阻抗等参数待补充。 -
    +![adc_full](../assets/adc_full.png){w=48%} +![adc_sample](../assets/adc_sample0.png){w=48%} ## 数字接口特性 -| 参数 | 条件 | 值 | 来源 | -|:-|:-|:-|:-| -| SPI 速率 | 最低 | 50 Mbps | specification.md | -| LVDS 速率 | 最低 | 750 Mbps | specification.md | -| LVDS 发送延迟 | — | 85 ns | tc_lvds_delay | -| LVDS 接收延迟 | — | 75 ns | tc_lvds_delay | - -SPI 突发传输时钟数量测试(50 Mbps): +| 参数 | 符号 | 最小值 | 典型值 | 最大值 | 单位 | +|:-|:-|:-|:-|:-|:-| +| SPI 时钟频率 | fSCLK | 50 | — | — | Mbps | +| LVDS 数据速率 | fLVDS | 750 | — | — | Mbps | +| LVDS 发送延迟 | tTX | — | 85 | — | ns | +| LVDS 接收延迟 | tRX | — | 75 | — | ns | +| SYNC_IN 最小脉宽 | tSYNC | — | — | — | ns | +| RST_N 最小脉宽 | tRST | — | — | — | ns | ![spi_50mbps](../assets/spi_50mbps.png) -SPI 突发传输时钟数量测试(25 Mbps): - ![spi_25mbps](../assets/spi_25mbps.png) -LVDS 数据传输速率测试: - ![lvds_speed](../assets/lvds_speed.png) -
    -SPI 时序参数详见 [规格参数](#规格参数)。SYNC_IN 最小脉宽、RST_N 最小脉宽等参数待补充。 -
    +## 数字处理能力 -## 数字性能特性 +| 参数 | 规格 | 来源 | +|:-|:-|:-| +| AWG 波形存储容量 | 128 KB | seg_define.csv | +| DAQ 采集结果存储容量 | 128 KB | seg_define.csv | +| DAQ 并行解模通道数 | 16 | pin_name.csv | +| MCU 指令执行速率 | 每 3 个时钟周期 1 条指令 | — | +| 匹配滤波器系数存储 (ASIC) | 512 KB | seg_define.csv | +| NCO 频率控制字位宽 | 32 bit | ChipConfig.py | +| NCO 相位控制字位宽 | — | — | +| 死时间 | — | — | +| Pump 脉宽范围 | — | — | -| 参数 | 条件 | 值 | 来源 | -|:-|:-|:-|:-| -| AWG 波形存储容量 | — | 128 KB | seg_define.csv | -| DAQ 采集结果存储容量 | — | 128 KB | seg_define.csv | -| DAQ 解模频点数量 | — | 最多 16 | pin_name.csv (LOC_LVCMOS[31:0]) | -| MCU 指令执行速率 | — | 每 3 个时钟 1 条指令 | 手册 7.3.2 | -| 匹配滤波器系数存储 | ASIC 模式 | 512 KB | seg_define.csv | -| NCO 频率控制字位宽 | — | 32 bit | ChipConfig.py | +## 温度传感器 -
    -死时间、Pump 脉宽范围、NCO 相位分辨率等参数待补充。 -
    - -## 温度特性 - -片上温度传感器监视结果示例: +| 参数 | 典型值 | 单位 | +|:-|:-|:-| +| 测温范围 | — | °C | +| 分辨率 | — | °C | +| 精度 | — | °C | ![temp_monitor](../assets/temp_monitor.png) -
    -温度传感器精度、分辨率等参数待补充。 -
    +## 中断与异常 -## 中断与异常处理 - -### 中断源列表 +### 中断向量表 | 中断源 | 触发条件 | 说明 | |:-|:-|:-| -| EXIT_IR | MCU 执行退出指令 | MCU 程序正常退出时触发,表明任务执行完毕 | -| FORCE_AWG | 外部强制 AWG 请求 | 外部控制器通过写寄存器强制 AWG 模块进入特定状态 | -| FORCE_DAQ | 外部强制 DAQ 请求 | 外部控制器通过写寄存器强制 DAQ 模块进入特定状态 | -| ILLEGAL_ONE | 非法指令(数据为全 1) | MCU 执行到全 1 指令字 (0xFFFFFFFF) 时触发,表明指令内存异常 | -| ILLEGAL_ZERO | 非法指令(数据为全 0) | MCU 执行到全 0 指令字时触发,常见于未编程的指令空间 | -| UNALIGN_SW | 短字非对齐访问 | 16 位数据访问地址未对齐时触发 | -| UNALIGN_LW | 长字非对齐访问 | 32 位数据访问地址未对齐时触发 | -| LINK_DOWN | LVDS 链路断开 | LVDS 数据接收链路同步丢失时触发,接收端重新进入未同步状态 | +| EXIT_IR | MCU 执行退出指令 | 正常退出,任务执行完毕 | +| ILLEGAL_ONE | 指令字为 0xFFFFFFFF | 指令内存异常,可能未编程 | +| ILLEGAL_ZERO | 指令字为 0x00000000 | 指令内存异常,常见于空白区域 | +| UNALIGN_SW | 16 位访问地址未对齐 | 检查 load/store 指令对齐 | +| UNALIGN_LW | 32 位访问地址未对齐 | 检查 load/store 指令对齐 | +| LINK_DOWN | LVDS 链路同步丢失 | 接收端返回未同步状态 | +| FORCE_AWG | 外部强制 AWG 请求 | 寄存器触发 | +| FORCE_DAQ | 外部强制 DAQ 请求 | 寄存器触发 | ### 典型异常波形 diff --git a/chapters/05_terminology.md b/chapters/05_terminology.md index 90314ea..a13fc1b 100644 --- a/chapters/05_terminology.md +++ b/chapters/05_terminology.md @@ -1,35 +1,46 @@ -# 专业术语 +# 术语与缩写 | 术语 | 全称 | 说明 | |:-|:-|:-| -| RBPU | Readout Baseband Processing Unit | 读出基带处理单元 | -| SoC | System on Chip | 片上系统 | -| AWG | Arbitrary Waveform Generator | 任意波形发生器,用于产生读出激励波形 | -| DAQ | Data Acquisition | 数据采集模块,用于采集和处理回波信号 | -| DAC | Digital-to-Analog Converter | 数模转换器 | -| ADC | Analog-to-Digital Converter | 模数转换器 | -| PLL | Phase-Locked Loop | 锁相环,片上时钟管理 | -| NCO | Numerically Controlled Oscillator | 数控振荡器,用于频率合成 | -| DSP | Digital Signal Processing | 数字信号处理 | -| LVDS | Low-Voltage Differential Signaling | 低电压差分信号 | -| SPI | Serial Peripheral Interface | 串行外设接口 | -| MCU | Micro Controller Unit | 微控制器单元,AWG/DAQ 内部控制核心 | -| PlsGen | Pulse Generator | 脉冲产生器,用于产生 Pump 使能脉冲 | -| Pump | — | 外部泵浦通道,用于上变频基带信号 | -| POR | Power-On Reset | 上电复位 | -| CRC | Cyclic Redundancy Check | 循环冗余校验 | -| FM | Frequency Modulation | 频率调制 | -| PM | Phase Modulation | 相位调制 | -| AM | Amplitude Modulation | 幅度调制 | -| HB | Half-Band | 半带滤波器,DAC 插值滤波器 | -| SFDR | Spurious-Free Dynamic Range | 无杂散动态范围 | -| NSD | Noise Spectral Density | 噪声谱密度 | -| SNR | Signal-to-Noise Ratio | 信噪比 | -| ENOB | Effective Number of Bits | 有效位数 | -| DNL | Differential Nonlinearity | 微分非线性 | -| INL | Integral Nonlinearity | 积分非线性 | -| FCW | Frequency Control Word | 频率控制字 | -| PCW | Phase Control Word | 相位控制字 | -| Qubit | Quantum Bit | 量子比特 | -| RO | Readout Output | 读出激励输出通道 | -| RI | Readout Input | 读出回波输入通道 | +| **ADC** | Analog-to-Digital Converter | 模数转换器 | +| **AWG** | Arbitrary Waveform Generator | 任意波形发生器,产生读出激励波形 | +| **BGA** | Ball Grid Array | 球栅阵列封装 | +| **CDM** | Charged Device Model | 充电器件模型(ESD 测试) | +| **CRC** | Cyclic Redundancy Check | 循环冗余校验 | +| **CSP** | Chip Scale Package | 芯片级封装 | +| **DAC** | Digital-to-Analog Converter | 数模转换器 | +| **DAQ** | Data Acquisition | 数据采集模块,采集和处理回波信号 | +| **DNL** | Differential Nonlinearity | 微分非线性 | +| **DSP** | Digital Signal Processing | 数字信号处理 | +| **ENOB** | Effective Number of Bits | 有效位数 | +| **ESD** | Electrostatic Discharge | 静电放电 | +| **FIFO** | First In, First Out | 先入先出缓冲器 | +| **FM** | Frequency Modulation | 频率调制 | +| **FPGA** | Field-Programmable Gate Array | 现场可编程门阵列 | +| **HB** | Half-Band | 半带滤波器,DAC 插值滤波器 | +| **HBM** | Human Body Model | 人体模型(ESD 测试) | +| **INL** | Integral Nonlinearity | 积分非线性 | +| **LVDS** | Low-Voltage Differential Signaling | 低电压差分信号 | +| **MCU** | Micro Controller Unit | 微控制器,AWG/DAQ 内部控制核心 | +| **MIX** | Mixer / Mix Mode | 混频器 / 混频输出模式 | +| **MSL** | Moisture Sensitivity Level | 湿度敏感等级 | +| **NCO** | Numerically Controlled Oscillator | 数控振荡器,用于频率合成 | +| **NRZ** | Non-Return-to-Zero | 不归零编码 / DAC 直出模式 | +| **NSD** | Noise Spectral Density | 噪声谱密度 | +| **PCB** | Printed Circuit Board | 印刷电路板 | +| **PLL** | Phase-Locked Loop | 锁相环,片上时钟管理 | +| **PlsGen** | Pulse Generator | 脉冲产生器,产生 Pump 使能脉冲 | +| **PM** | Phase Modulation | 相位调制 | +| **POR** | Power-On Reset | 上电复位 | +| **Pump** | — | 外部泵浦通道,上变频基带信号 | +| **Qubit** | Quantum Bit | 量子比特 | +| **RBPU** | Readout Baseband Processing Unit | 读出基带处理单元 | +| **RF** | Radio Frequency | 射频 | +| **RMS** | Root Mean Square | 均方根值 | +| **SFDR** | Spurious-Free Dynamic Range | 无杂散动态范围 | +| **SINAD** | Signal-to-Noise and Distortion | 信纳比 | +| **SNR** | Signal-to-Noise Ratio | 信噪比 | +| **SoC** | System on Chip | 片上系统 | +| **SPI** | Serial Peripheral Interface | 串行外设接口 | +| **VCM** | Common Mode Voltage | 共模电压 | +| **VCO** | Voltage-Controlled Oscillator | 压控振荡器 | diff --git a/chapters/06_theory_of_operation.md b/chapters/06_theory_of_operation.md index 1a6354e..9c6cc43 100644 --- a/chapters/06_theory_of_operation.md +++ b/chapters/06_theory_of_operation.md @@ -1,121 +1,134 @@ # 操作原理 -## 原理及模块划分 +## 架构与模块划分 -RBPU16 是一颗用于完成超导量子比特的态读出的 SoC 基带芯片。芯片模拟部分包括 ADC、DAC、PLL、LVDS、POR 和温度传感器;数字部分主要包括 DAQ、AWG、PlsGen、Feedback 以及 System 模块。读出芯片的总体功能组成如下图所示。 +RBPU16 是一款用于超导量子比特态信息读出的 SoC 基带芯片。芯片模拟部分包含 ADC、DAC、PLL、LVDS、POR 及温度传感器;数字部分包含 DAQ、AWG、PlsGen、Feedback 及 System 模块。 ![rbpu_schematic](../assets/rbpu_schematic.png) -在激励信号生成路径上,AWG 用于控制激励通道待输出的波形,接着波形经过 DAC 转换成基带读出激励信号,同时 AWG 模块触发 PlsGen 模块实时发出脉冲信号使能外部 Pump 通道。回波信号处理路径上,ADC 用于采集反射的读出基带信号,接着 DAQ 对基带信号进行分析处理,结果再通过 LVDS 接口发送出去,同时 DAQ 模块触发 Feedback 模块将部分结果上报或者下发到外部芯片。 +### 信号流概述 -片内 PLL 用于管理整个芯片的时钟,为 ADC 采样时钟、DAC 更新时钟、数字模块主时钟等生成所需频率。POR 用于在芯片上电时对整个芯片进行复位,保障芯片正常工作。温度监视模块用于实时监测芯片温度变化,保障芯片稳定工作。System 模块用于管理芯片上控制的状态,为芯片运维和调试提供支撑。 +**激励信号生成路径**:AWG 控制激励通道波形输出 → DAC 转换为基带读出激励信号 → AWG 同步触发 PlsGen 发出脉冲使能外部 Pump 通道。 + +**回波信号处理路径**:ADC 采集反射的读出基带信号 → DAQ 对基带信号进行分析处理 → 结果通过 LVDS 接口发送 → DAQ 触发 Feedback 模块将部分结果上报或下发到外部芯片。 + +### 辅助模块 + +| 模块 | 功能 | +|:-|:-| +| PLL | 管理芯片时钟,为 ADC/DAC/数字模块生成所需频率 | +| POR | 上电复位,保障芯片正常启动 | +| 温度监视 | 实时监测芯片温度变化 | +| System | 管理芯片控制状态,支撑运维和调试 |
    -读出芯片不仅支持以 ASIC 芯片模式工作,也支持部分核心控制在 FPGA 内部工作。这两种工作模式受制于 ASIC 和 FPGA 资源以及外部 ADC、DAC 硬件性能不同,部分功能实现存在差异,后续阅读时需要重点关注。 +读出芯片支持 ASIC 模式和 FPGA 模式两种工作方式。两种模式受限于资源及外部 ADC/DAC 硬件性能差异,部分功能实现存在不同,阅读时需注意区分。
    ## 关键数据路径 ### 读出激励输出通道 - +读出激励输出通道(RO 通道)由 AWG 模块和 Pump 模块协同工作,产生基带读出激励信号和 Pump 使能信号。 -读出激励输出通道(RO 通道)由 AWG 模块和 Pump 模块协同工作,负责产生基带读出激励信号和 Pump 使能信号。 +#### 编程资源 -AWG 模块需要编程六类数据: +AWG 模块需编程六类数据资源: -1. **控制器指令 (MCU_INS)**:控制 MCU 执行指令流,指令位于地址空间 `0x700000` -2. **控制器数据 (MCU_DAT)**:MCU 指令需要引用的初始数据,位于地址空间 `0x800000` -3. **控制寄存器 (AWG_REG)**:配置 AWG 工作模式与参数,位于地址空间 `0x900000` -4. **波形映射表 (AWG_IDX)**:码字 → 波形段的映射查找表,位于地址空间 `0xA00000` -5. **波形数据存储 (AWG_WVE)**:实际输出的波形采样点,位于地址空间 `0xB00000` -6. **模拟配置 (SYS_ANA)**:DAC 输出模式(NRZ/MIX/HBMIX)与偏置校准 +| 资源 | 地址空间 | 说明 | +|:-|:-|:-| +| MCU 指令 (MCU_INS) | `0x700000` | MCU 执行的指令流 | +| MCU 数据 (MCU_DAT) | `0x800000` | 指令引用的初始数据 | +| 控制寄存器 (AWG_REG) | `0x900000` | AWG 工作模式与参数 | +| 波形映射表 (AWG_IDX) | `0xA00000` | 码字 → 波形段映射 | +| 波形数据 (AWG_WVE) | `0xB00000` | 波形采样点存储 | +| 模拟配置 (SYS_ANA) | — | DAC 输出模式与偏置校准 | -波形输出流程如下: +#### 波形输出流程 1. MCU 每 3 个时钟周期执行一条指令,发出控制码字 -2. 码字中的 `WAVE_ID` 字段通过查找表映射为波形段的起始地址和长度 -3. 波形数据从 WVE 存储中读取,经希尔伯特变换生成 I/Q 两路 +2. `WAVE_ID` 字段经查找表映射为波形段起始地址和长度 +3. 波形数据经希尔伯特变换生成 I/Q 两路 4. I/Q 信号与 NCO 混频,实现频率搬移 5. 经数字调幅、半带插值滤波 6. 输出至 DAC 转换为模拟信号 -AWG 支持以下输出模式: +#### 输出模式 -| 模式 | 描述 | 特点 | +| 模式 | 频率范围 | 应用场景 | |:-|:-|:-| -| **直出模式 (NRZ)** | 基带波形直接输出 | 输出频率 DC–3 GHz,适用于直接产生多音基带信号 | -| **调制模式 (MIX)** | 基带波形与 NCO 混频 | 输出频率 0.3–2.4 GHz,通过 NCO 频率控制精确频点定位 | -| **NCO Only 模式** | 仅 NCO 连续波形 | 方便连接外部仪器进行芯片测试 | -| **半带调制模式 (HBMIX)** | 混频 + 半带插值 | 提升高频输出信号质量 | +| **NRZ 直出** | DC–3 GHz | 多音基带信号,多量子比特并行读出 | +| **MIX 混频** | 0.3–2.4 GHz | NCO 精确频点控制,腔频扫描 | +| **NCO Only** | — | 芯片测试,连续波形输出 | +| **HBMIX 半带混频** | 高频段 | 启用半带插值,提升高频信号质量 | -
    -一般情况下,直出模式用于量子实验,通过直接输出包含多个读出频率的波形,可以实现对多个量子比特的并行读出;调制模式用于腔频扫描等应用,通过实时修改 NCO 频率实现扫频功能;NCO Only 模式可以输出连续波形,方便连接外部仪器上进行测试。 -
    +**射频直出**:NRZ 模式下利用高阶奈奎斯特区可实现 6–7 GHz 射频直出,功率平坦度约 2 dB(受 sinc 滚降限制)。 -**射频直出**:在 NRZ 模式下利用高阶奈奎斯特区输出,可以实现 6–7 GHz 的射频直出,功率平坦度约 2 dB(受限于 sinc 滚降特性)。 +**Pump 通道**:MCU 码字 `PUMP_EN` 位触发 PlsGen 产生可编程脉宽信号,经 `PO_PUMP_EN` 管脚输出使能外部 Pump 上变频通道。 -**Pump 通道**:由 MCU 码字中的 `PUMP_EN` 位触发 PlsGen 模块,产生可编程宽度的脉冲信号,通过 `PO_PUMP_EN` 管脚输出,用于使能外部 Pump 上变频通道。 - -**软复位**:通过写 AWG_REG 中的软复位寄存器,可复位 AWG 内部状态机和 FIFO,复位后需重新编程。 +**软复位**:写 AWG_REG 软复位寄存器可复位 AWG 内部状态机和 FIFO,复位后需重新编程所有资源。 ### 读出回波信号处理 - +读出回波输入通道(RI 通道)由 ADC 和 DAQ 模块构成,采集并处理反射基带信号。 -读出回波输入通道(RI 通道)由 ADC + DAQ 模块构成,负责采集和处理反射回来的基带信号。 +#### 模拟前端 -**模拟信号路径**:RF 回波信号经外部混频器下变频为基带 → 巴伦差分转换 → ADC 采样(FPGA 平台 8 位 @4 GSPS,ASIC 平台 8 位 @6 GSPS)。 +RF 回波信号 → 外部混频器下变频为基带 → 巴伦差分转换 → ADC 采样。 +FPGA 平台:8 位 @ 4 GSPS;ASIC 平台:8 位 @ 6 GSPS。 -DAQ 模块需要编程五类数据: +#### 编程资源 -1. **控制器指令 (MCU_INS)**:DAQ 内部 MCU 的执行指令流,位于地址空间 `0x200000` -2. **控制器数据 (MCU_DAT)**:MCU 需要引用的初始数据,位于地址空间 `0x300000` -3. **控制寄存器 (DAQ_REG)**:配置 DAQ 工作模式与参数,位于地址空间 `0x400000` -4. **读出参数与滤波器权重 (DAQ_PAR + DAQ_FLT)**:解模参数、匹配滤波器系数,位于地址空间 `0x500000` 和 `0x580000` -5. **模拟配置 (SYS_ANA)**:ADC 参考电压选择 +DAQ 模块需编程五类数据资源: -数据处理流程: - -1. ADC 采集原始波形数据 -2. 数字解模:原始波形与匹配滤波器进行相关运算,得到 I/Q 复数数据 -3. 态判定:根据 I/Q 数据判定量子态(0 态/1 态/2 态/未定义) -4. 态统计:对多次测量的态结果进行计数统计 -5. 结果输出:通过 `RSLT_PUSH` LVDS 接口发送数据 - -DAQ 支持多种数据采集模式: - -| 模式 | 输出数据量 | 适用场景 | +| 资源 | 地址空间 | 说明 | |:-|:-|:-| -| 原始波形 | 最大 | 调试、原始数据分析 | -| IQ 数据 | 中等 (64 bit × N) | 解模结果分析 | -| 态数据 | 小 (2 bit × N) | 高速态读出 | -| 态统计 | 最小 | 多次测量统计 | +| MCU 指令 (MCU_INS) | `0x200000` | DAQ MCU 执行指令流 | +| MCU 数据 (MCU_DAT) | `0x300000` | 指令引用的初始数据 | +| 控制寄存器 (DAQ_REG) | `0x400000` | DAQ 工作模式与参数 | +| 读出参数 (DAQ_PAR) | `0x500000` | 解模参数 | +| 匹配滤波器系数 (DAQ_FLT) | `0x580000` | 匹配滤波器权重(ASIC 模式) | +| 模拟配置 (SYS_ANA) | — | ADC 参考电压选择 | -软复位功能:通过写 DAQ_REG 中的软复位寄存器复位 DAQ 状态机。 +#### 数据处理流程 + +1. ADC 采集原始波形 +2. 数字解模:原始波形与匹配滤波器相关运算 → I/Q 复数数据 +3. 态判定:根据 I/Q 判定量子态(0 / 1 / 2 / 未定义) +4. 态统计:多次测量结果计数 +5. 结果输出:经 `RSLT_PUSH` LVDS 接口发送 + +#### 采集模式 + +| 模式 | 输出数据量 | 应用场景 | +|:-|:-|:-| +| 原始波形 | 最大 | 调试与原始数据分析 | +| IQ 数据 | 64 bit × N | 解模结果分析 | +| 态数据 | 2 bit × N | 高速态读出 | +| 态统计 | 最小 | 多次测量汇总 | + +**软复位**:写 DAQ_REG 软复位寄存器可复位 DAQ 状态机。 ## 接口控制协议 ### SPI 配置协议 -读出芯片通过 SPI 来配置内部寄存器和内存,SPI 采用四线模式。时钟极性 CPOL = 1,即时钟在空闲时处于高电平状态;时钟相位 CPHA = 1,即在第一个时钟沿输出数据,第二个时钟沿采样数据。SPI 的通信数据格式采用自定义协议,支持流模式发送接收。 +SPI 采用四线模式,CPOL = 1(空闲高电平),CPHA = 1(首沿输出、次沿采样)。 ![spi_prot](../assets/spi_prot.png) -**SPI 帧格式:** +**帧格式:** | 字段 | RW | ADDR | CHIP_ID | RSV | DATA | |:-:|:-:|:-:|:-:|:-:|:-:| | 位宽 | 1 | 25 | 5 | 1 | 32 × N | -| 含义 | 0: 读, 1: 写 | 32 MB 寻址空间 | 总线片选 | 保留位 | 数据 | +| 说明 | 0: 读, 1: 写 | 32 MB 地址空间 | 片选 ID | 保留 | 数据 (N ≥ 1) | -
    -协议首先发送 1 比特读写标识位,然后发送 25 比特地址位,接着再发送 5 比特的芯片选择信号,最后再发送/接收数据。数据发送/接收以 32 比特为单位,以流模式发送,支持发送/接收 N × 32 位的数据,直到主控端将 CSN 拉低后结束通信(N ≥ 1)。流模式要求从设备在完成 32 比特数据接收/发送后自动对地址进行 4 字节递增。 -
    +协议以流模式传输:先发送 1 bit 读写标志 + 25 bit 地址 + 5 bit 片选 ID,之后发送/接收 N × 32 bit 数据,直至主控端拉低 CSN 结束通信。每完成 32 bit 后地址自动递增 4 字节。 ### LVDS 通信协议 -从读出芯片到多功能母卡的数据推送接口 `rslt_push_[p,n]`、反馈上报接口 `glb_tx_[p,n]` 和从多功能母板到读出芯片的反馈下发接口 `glb_rx_[p,n]` 采用定制的 LVDS 通信协议。 +芯片数据推送 (`rslt_push_[p,n]`)、反馈上报 (`glb_tx_[p,n]`) 和反馈下发 (`glb_rx_[p,n]`) 接口采用定制 LVDS 协议。 **帧格式:** @@ -130,14 +143,25 @@ DAQ 支持多种数据采集模式: MSB LSB ``` -帧头包含 4 比特数据,其中最高位比特用于指示是否启动数据传输,次高位置 0 保留,最低两位用于指示本次传输的数据量(0: 16 比特,1: 32 比特,2: 64 比特,3: 128 比特)。载荷后面附带 8 比特 CRC 校验码,校验多项式为: +HEAD 字段定义: + +| Bit | 说明 | +|:-|:-| +| [3] | 启动标志(1 = 启动传输) | +| [2] | 保留(置 0) | +| [1:0] | 载荷长度:0 = 16 bit,1 = 32 bit,2 = 64 bit,3 = 128 bit | + +CRC8 多项式: $$x^8 + x^2 + x + 1$$ ![lvds_prot](../assets/lvds_prot.png) -**同步机制**:接收端在接收数据之前必须先和发送端同步。发送端通过发送同步码来同步接收端,当完成设定数量的同步码发送完毕后发送一个结束码结束同步过程。同步码为字符串 "hfnl" 对应的 32 比特数据,结束码为 "exit" 对应的 32 比特数据。 +**同步机制**:接收端必须先与发送端同步方可接收数据。发送端发送同步码 `hfnl`(32 bit),达到设定次数后发送结束码 `exit`(32 bit)。接收端状态转移: -接收端在启动时被初始化到未同步状态,此时必须接收到足够次数的同步码之后才能进入确认状态,直到接收到结束码之后发送端和接收端同时进入就绪状态。接收端后续如果在接收数据过程中发现 CRC8 校验码出错,会再次进入未同步状态。 +* 初始 → **未同步**:等待同步码 +* **未同步** → **确认**:收到足够次数的同步码 +* **确认** → **就绪**:收到结束码 +* **就绪** → **未同步**:数据接收中 CRC8 校验错误 -**空闲状态**:当处于空闲状态不用传输数据时,发送端将 LVDS 信号电平拉高。当传输数据时,发送端将 LVDS 电平拉低两个数据周期启动数据发送。 +**空闲**:无数据传输时 LVDS 电平拉高;发送数据时拉低两个数据周期后开始发送。 diff --git a/chapters/07_applications.md b/chapters/07_applications.md index 2d47f66..f69f0ad 100644 --- a/chapters/07_applications.md +++ b/chapters/07_applications.md @@ -1,161 +1,220 @@ -# 应用注意 +# 应用信息 ## 模拟接口 ### DAC 输出接口 -DAC 输出 0–12 GHz 信号,内部为 50 Ω 电阻网络,外部差分 270 nH 电感到地,然后经巴伦差分转单端,注意传输线阻抗匹配。 +DAC 输出频率范围 DC–12 GHz,内部集成 50 Ω 电阻网络。外部需接差分 270 nH 电感到地,经巴伦差分转单端输出。PCB 走线注意阻抗匹配(50 Ω 差分)。 -DAC 支持三种输出模式: +```schemdraw +import schemdraw +from schemdraw import elements as e -* **NRZ 直出模式**:基带波形直接输出,输出频率范围 DC–3 GHz。适用于直接产生多音基带信号。 -* **混频模式 (MIX)**:基带波形与 NCO 混频后输出,输出频率范围 0.3–2.4 GHz。通过 NCO 频率控制可实现精确的频点定位。 -* **半带混频模式 (MIX-HB)**:在混频模式基础上启用半带插值滤波器,提升输出信号质量,适用于高频输出场景。 +with schemdraw.Drawing(show=False) as d: + d.config(fontsize=11) + d += e.Line().right().label('DAC_OUT_P') + d += e.Inductor().down().label('270nH') + d += e.Ground() + d += e.Line().right().at((2.5, 0)) + d += e.Transformer().label('Balun') + d += e.Line().right() + d += e.Resistor().down().label('50ohm') + d += e.Ground() + d += e.Line().right().at((0, -2.5)).label('DAC_OUT_N') + d += e.Inductor().up().label('270nH') + d += e.Ground() + d += e.Line().right().at((2.5, -2.5)) + d += e.Line().up() +``` -各模式输出频谱参考见 [DAC 特性](#dac-特性)。 +DAC 支持以下输出模式: + +| 模式 | 频率范围 | 说明 | +|:-|:-|:-| +| NRZ 直出 | DC–3 GHz | 基带波形直接输出,适合多音基带信号 | +| MIX 混频 | 0.3–2.4 GHz | 基带与 NCO 混频,精确频点定位 | +| MIX-HB 半带混频 | 高频段 | 混频 + 半带插值,提升高频信号质量 | + +频谱参考见 [DAC 特性](#dac-特性)。 ### ADC 输入接口 -ADC 采用差分模拟输入,包含两组独立输入通道(ADC_VINP1/ADC_VINN1, ADC_VINP2/ADC_VINN2),支持最高 6 GSPS 采样率。 +ADC 采用差分模拟输入,两组独立通道。 -* 满量程输入范围:差分 0.7 Vpp -* 输入共模电压:0.5 V -* 输入阻抗(差分):100 Ω -* 输入带宽 (−3 dB):3 GHz +| 参数 | 规格 | 单位 | +|:-|:-|:-| +| 满量程输入范围(差分) | 0.7 | Vpp | +| 输入共模电压 | 0.5 | V | +| 差分输入阻抗 | 100 | Ω | +| 输入带宽 (−3 dB) | 3 | GHz | +| 采样速率 | 最高 6 | GSPS | -ADC 支持内部和外部两种基准电压模式,通过 ADC_REF_SENSE 端口选择: +基准电压模式通过 `ADC_REF_SENSE` 选择: -* 1.8 V 电平:选择内部带隙基准 -* 0.52 V 电平:选择外部参考源(需提供 ADC_VR850 和 ADC_VR350) +| 电平 | 模式 | 说明 | +|:-|:-|:-| +| 1.8 V | 内部带隙基准 | 无需外部参考 | +| 0.52 V | 外部参考源 | 需提供 ADC_VR850 和 ADC_VR350 | -ADC 采集波形示例参考见 [ADC 特性](#adc-特性)。 +波形示例参考见 [ADC 特性](#adc-特性)。 -### 时钟输入接口 +### 时钟输入 -参考时钟通过差分端口 CLK_REF_P/CLK_REF_N 输入,支持 AC 耦合。参考时钟频率范围 50–250 MHz,典型输入为 100 MHz 或 125 MHz 低相噪时钟源。时钟信号经内部 PLL 倍频,产生 DAC 更新时钟(6–12 GHz)、ADC 采样时钟(3–6 GHz)和数字主时钟(250–500 MHz)。 +参考时钟通过 CLK_REF_P / CLK_REF_N 差分输入,AC 耦合。 -PLL 及 NCO 频率特性参考见 [时钟与 NCO 特性](#时钟与-nco-特性)。 +| 参数 | 规格 | 单位 | +|:-|:-|:-| +| 频率范围 | 50–250 | MHz | +| 典型频率 | 100 或 125 | MHz | +| 要求 | 低相噪时钟源 | — | + +内部 PLL 倍频分配: + +| 输出 | 频率范围 | 用途 | +|:-|:-|:-| +| DAC_CLK | 6–12 GHz | DAC 更新时钟 | +| ADC_CLK | 3–6 GHz | ADC 采样时钟 | +| DIG_CLK | 250–500 MHz | 数字主时钟 | ### 环路滤波器 -使用内部 PLL 时,需要外接环路滤波器。 +使用内部 PLL 时需外接环路滤波器。 -PLL 锁相环电荷泵输出电压通过 CP_OUT 端口输出至 VCTRL 端口,外接二阶环路滤波器。PLL_VREF520 端口需提供外部 520 mV 参考电压用于 PLL 内部偏置。BIAS_CAP 端口需外接 100 nF 去耦电容,RES_2K 端口需外接 2 kΩ 电阻到地。PORT_BIAS_TB 端口需外接 10 μF × 1、1 μF × 1、0.1 μF × 3 滤波电容到地。 +PLL 电荷泵输出 (CP_OUT) → 外部二阶环路滤波器 → VCTRL。PLL_VREF520 需提供外部 520 mV 参考偏置。 + +```schemdraw +import schemdraw +from schemdraw import elements as e + +with schemdraw.Drawing(show=False) as d: + d.config(fontsize=11) + d += e.Line().left().label('CP_OUT') + d += e.Line().right() + d += e.Resistor().right().label('R1') + d += e.Line().right().label('VCTRL') + d += e.Capacitor().down().at((2, 0)).label('C1') + d += e.Ground() + d += e.Capacitor().down().at((4, 0)).label('C2') + d += e.Ground() +``` + +**外接元件要求:** + +| 端口 | 配置 | 说明 | +|:-|:-|:-| +| BIAS_CAP | 100 nF 去耦电容 | PLL 偏置去耦 | +| RES_2K | 2 kΩ 电阻到地 | PLL 偏置电阻 | +| PORT_BIAS_TB | 10 μF ×1 + 1 μF ×1 + 0.1 μF ×3 到地 | PLL 滤波电容 |
    -环路滤波器推荐参数(电荷泵电流、环路带宽、阻容值等)待补充。 +环路滤波器参数(电荷泵电流、环路带宽、具体阻容值)待 PLL 设计确认后补充。
    -### 基准电压输入 - -此端口的外接电阻决定了基准电流源的大小。其中,ADC_VBIAS_IREF_RES 端口要求外接电阻 5.2 kΩ 到地,并联 10 μF 去耦电容。DAC_VBIAS_IREF_RES 端口要求外接电阻 5.2 kΩ 到地,并联 10 μF 去耦电容。 - -各基准电压端口要求如下: +### 基准电压与偏置 | 端口 | 电压/配置 | 说明 | |:-|:-|:-| -| ADC_VR850 | 850 mV | ADC 外部参考电压,需去耦 | -| ADC_VR350 | 350 mV | ADC 外部参考电压,需去耦 | -| ADC_REF_SENSE | 1.8V / 0.52V | ADC 带隙基准内外选择 | -| PLL_VREF520 | 520 mV | PLL 外部参考电压,需去耦 | -| VREF500IN | 500 mV | DAC 外部参考电压,需去耦 | -| DAC_VBIAS_IREF_RES | 外接 5.2 kΩ + 10 μF | DAC 偏置电流基准 | -| ADC_VBIAS_IREF_RES | 外接 5.2 kΩ + 10 μF | ADC 偏置电流基准 | -| BIAS_CAP | 外接 100 nF | PLL 偏置去耦 | -| RES_2K | 外接 2 kΩ | PLL 外接电阻 | -| PORT_BIAS_TB | 外接 10 μF + 1 μF + 0.1 μF × 3 | PLL 滤波电容 | -| PLL_CP_OUT | 电荷泵输出 | 连接至外部环路滤波器后接入 VCTRL | +| ADC_VR850 | 850 mV | ADC 外部参考,需去耦 | +| ADC_VR350 | 350 mV | ADC 外部参考,需去耦 | +| ADC_REF_SENSE | 1.8 V / 0.52 V | ADC 带隙基准选择 | +| PLL_VREF520 | 520 mV | PLL 外部参考,需去耦 | +| VREF500IN | 500 mV | DAC 外部参考,需去耦 | +| DAC_VBIAS_IREF_RES | 5.2 kΩ + 10 μF 到地 | DAC 偏置基准电流 | +| ADC_VBIAS_IREF_RES | 5.2 kΩ + 10 μF 到地 | ADC 偏置基准电流 | +| PLL_CP_OUT | 电荷泵输出 | 经环路滤波器接 VCTRL | -## 启动顺序 +## 启动与复位 -为了确保芯片正常工作,需要满足以下启动顺序要求: +### 上电时序 -1. 内核供电上电 -2. IO 供电上电 -3. 时钟配置完毕 -4. 芯片复位 +芯片上电须严格遵循以下顺序: -## 配置流程 +| 步骤 | 操作 | 说明 | +|:-|:-|:-| +| 1 | 内核供电 (DIG_VDD, DAC_DVDD, PLL_DVDD, ADC_DVDD) | 数字内核上电 | +| 2 | IO 供电 (IO_VDD) | 数字 I/O 上电 | +| 3 | 模拟供电 (DAC_AVDD18, PLL_VDD18, ADC_VDD18, AVDD_ENCODER, AVDD_P2S, PLL_VDD, VCO_VDD, ADC_VDD) | 模拟域上电 | +| 4 | 参考时钟输入 | 提供稳定参考时钟 | +| 5 | 解除复位 (PB_RST_N 拉高) | 芯片启动 | -读出芯片在上电后应该校准,校准过程用于完成片上 ADC、DAC、PLL 以及系统同步。校准过程参考附录 [运维操作手册](#运维操作手册)。 +
    +上电时序波形图待补充。 +
    -在实验中读出芯片的操作遵循**数据配置**、**实验运行**和**数据采集**三个阶段。 +### 复位 + +| 复位类型 | 触发方式 | 影响范围 | +|:-|:-|:-| +| 上电复位 (POR) | 芯片上电自动触发 | 全芯片 | +| 硬复位 | PB_RST_N 拉低 | 全芯片 | +| 软复位 | 写 SYS_REG 软复位寄存器 | 可分别复位 AWG / DAQ / System | + +## 配置与操作流程 + +读出芯片上电后应先执行校准(ADC/DAC/PLL/系统同步),校准流程参见附录 [运维操作手册](#运维操作手册)。实验操作遵循**数据配置 → 实验运行 → 数据采集**三个阶段。 ### 数据配置阶段 -* **RO 通道配置**:外部硬件通过 SPI 接口对读出芯片的寄存器(包括输出模式,例如直发模式、NCO Only 模式、调制模型)、波形查找表、波形仓库、输出 MCU 控制程序及数据进行配置。 -* **RI 通道配置**:外部硬件通过 SPI 接口对读出芯片的寄存器(包括采集模式,例如波形模式、解模模式、态结果模式等)、读出参数、匹配滤波器权重、采集控制程序及数据进行配置。 -* **Pump 通道配置**:主要配置 Pump 通道输出的使能信号宽度和相对码字触发的延迟。 +| 通道 | 配置内容 | 接口 | +|:-|:-|:-| +| RO(激励) | 输出模式、波形查找表、波形数据、MCU 程序 | SPI | +| RI(采集) | 采集模式、读出参数、匹配滤波器权重、MCU 程序 | SPI | +| Pump | 使能脉宽、码字触发延迟 | SPI |
    -若芯片出现异常,在实验配置前应该通过写寄存器来软复位芯片的状态机和寄存器默认值。数据配置用例参考附录 [历史无关配置集](#历史无关配置集)。 +若芯片出现异常,实验配置前应通过写寄存器软复位芯片状态机和寄存器默认值。配置用例参考附录 [历史无关配置集](#历史无关配置集)。
    ### 实验运行阶段 -读出芯片的运行通过同步触发信号来启动,从而确保多个芯片间具有确定的时序关系。此外考虑到不同类型通信固有延迟不同,同步信号在芯片内部首先经过同步延迟模块,同步延迟模块能够为 DAQ 和 AWG 分别延迟不同的时间,从而确保读出芯片的 DAQ 和 AWG 模块能够协同运行。 +同步触发信号 (SYNC_IN) 启动芯片运行,确保多芯片间时序确定。同步信号经内部延迟模块为 DAQ 和 AWG 分别补偿延迟,保证协同运行。 -同步信号到达 DAQ 和 AWG 模块后,DAQ 和 AWG 模块内部的 MCU 分别启动运行,MCU 从 0 地址加载指令运行,其按照每三个时钟一个指令的速度执行,MCU 根据编程指令修改控制寄存器与发出控制码字: +MCU 从地址 0 启动,每 3 个时钟周期执行一条指令,通过控制码字驱动: -* 对于 AWG,控制码字驱动波形输出和 Pump 使能输出。 -* 对于 DAQ,控制码字驱动数据采集和反馈上报/下发。 +* **AWG**:波形输出 + Pump 使能 +* **DAQ**:数据采集 + 反馈上报/下发 -MCU 运行到退出指令后,退出运行并进入空闲状态。 +MCU 执行退出指令后进入空闲状态。 -### 实验数据采集阶段 +### 数据采集阶段 -实验数据采集与 MCU 运行是同时启动的。如果采集数据量较大,存在 MCU 运行结束但是数据还没采集完成的情况,此时数据继续写入到缓存区,主动数据上报模块继续从缓存中拿取数据并通过数据上报接口推送出去。当缓存中数据推送完毕后结束实验,此时可以通过检测推送数据量与目标数据量是否相等,控制发出读请求信号来指示实验是否完毕。 +数据采集与 MCU 运行同步启动。若采集量大,MCU 可能先于采集结束;数据继续写入缓存,上报模块持续推送,直至缓存清空。 -若不能提前确定采集的数据量,则只能在 MCU 运行结束后通过等待一定的时间,通过检测是否有数据返回来判断实验是否完成。 +可通过检测推送数据量是否达到目标数据量来判断实验完成。若数据量未知,需在 MCU 结束后等待一定时间,通过检测数据返回来判断。 -在为读出芯片设置好采集数据请求阈值后,当采集到的数据大于等于阈值便会触发一次读数据请求。由于读出芯片通过 LVDS 接口回传数据,持续进行实验时,为了避免缓存写满,需要确保数据产生速率和数据回传速率匹配。以获取解模 IQ 数据为例: +**速率匹配**:为避免缓存写满,数据产生速率和数据回传速率须匹配。以解模 IQ 数据为例: -设 *T* 为数据产生时间间隔,*N* 为每次读取的 Qubit 数量,*F* 为 LVDS 的有效速率,触发间隔时间需要满足以下公式: +设 *T* 为数据产生间隔,*N* 为并行读出 Qubit 数,*F* 为 LVDS 有效速率,触发间隔须满足: $$T > \frac{64 \times N}{F}$$ ## 异常处理 -读出芯片内部集成了中断管理和异常检测机制,通过 INT_STATUS 和 INT_MASK 寄存器进行管理。INT_STATUS 寄存器是 INT_MASK 与对应实际输入状态按位相与再与 INT_STATUS 本身按位或运算的结果,需要通过命令清零。 +### 中断管理 -### 中断源 - -芯片支持以下中断/异常源: - -| 中断源 | 触发条件 | 说明 | -|:-|:-|:-| -| EXIT_IR | MCU 执行退出指令 | MCU 程序正常退出时触发,表明任务执行完毕 | -| FORCE_AWG | 外部强制 AWG 请求 | 外部控制器通过写寄存器强制 AWG 模块进入特定状态 | -| FORCE_DAQ | 外部强制 DAQ 请求 | 外部控制器通过写寄存器强制 DAQ 模块进入特定状态 | -| ILLEGAL_ONE | 非法指令(数据为全 1) | MCU 执行到全 1 指令字 (0xFFFFFFFF) 时触发,表明指令内存异常 | -| ILLEGAL_ZERO | 非法指令(数据为全 0) | MCU 执行到全 0 指令字时触发,常见于未编程的指令空间 | -| UNALIGN_SW | 短字非对齐访问 | 16 位数据访问地址未对齐时触发 | -| UNALIGN_LW | 长字非对齐访问 | 32 位数据访问地址未对齐时触发 | -| LINK_DOWN | LVDS 链路断开 | LVDS 数据接收链路同步丢失时触发,接收端重新进入未同步状态 | +芯片通过 INT_STATUS 和 INT_MASK 寄存器管理中断。INT_STATUS = (INT_MASK & 实际状态) | INT_STATUS,需写寄存器清零。 ### 中断处理流程 -异常发生时,建议按以下流程处理: +1. 读取 INT_STATUS,确认中断源 +2. 记录异常信息(类型、时间、运行状态) +3. 按中断类型处理: -1. **读取 INT_STATUS 寄存器**,确认中断源 -2. **记录异常信息**(中断类型、发生时间、当前运行状态) -3. **根据中断类型采取对应措施**: - - EXIT_IR:正常退出,无需处理,可启动下一轮实验 - - ILLEGAL_ONE / ILLEGAL_ZERO:检查 MCU 程序是否正确写入指令内存,必要时重新下载程序并软复位 MCU - - UNALIGN_SW / UNALIGN_LW:检查 MCU 程序中的内存访问指令,确保 load/store 指令地址对齐 - - LINK_DOWN:检查 LVDS 物理连接和信号完整性,重新执行 LVDS 同步流程 - - FORCE_AWG / FORCE_DAQ:根据外部控制器逻辑处理 -4. **写 INT_STATUS 寄存器清零中断标志位** -5. **若需恢复运行**:通过软复位(写 SYNC_SOFT 寄存器)复位 MCU 状态机,重新配置后启动 +| 中断源 | 处理措施 | +|:-|:-| +| EXIT_IR | 正常退出,可启动下一轮实验 | +| ILLEGAL_ONE / ILLEGAL_ZERO | 检查并重新下载 MCU 程序,软复位 MCU | +| UNALIGN_SW / UNALIGN_LW | 检查 load/store 指令地址对齐 | +| LINK_DOWN | 检查 LVDS 物理连接和信号完整性,重新同步 | +| FORCE_AWG / FORCE_DAQ | 按外部控制器逻辑处理 | + +4. 写 INT_STATUS 清零中断标志 +5. 如需恢复运行:软复位 MCU 状态机并重新配置 ### 异常预防 -* MCU 程序写入后应通过回读比对验证写入正确性 -* LVDS 数据传输前确保收发双方已完成同步 -* 避免在实验运行期间访问运维寄存器 -* 建议启用 INT_MASK 中需要关注的中断源,屏蔽不需要的中断源以简化处理逻辑 -* MCU 程序末尾务必包含退出指令 (`exit x0, x0, 0`),避免 MCU 从非法内存区域取指 - -### 典型异常波形 - -异常波形截图参考见 [中断与异常处理](#中断与异常处理)。 +* MCU 程序写入后通过回读比对验证正确性 +* LVDS 传输前确认收发双方已完成同步 +* 实验运行期间禁止访问运维寄存器(系统/模拟/PLL 配置) +* 通过 INT_MASK 启用关注的中断源,屏蔽无关中断 +* MCU 程序末尾必须包含退出指令 `exit x0, x0, 0` diff --git a/chapters/08_register_map.md b/chapters/08_register_map.md index 88208fe..bb5a46c 100644 --- a/chapters/08_register_map.md +++ b/chapters/08_register_map.md @@ -4,7 +4,7 @@ 读出芯片偏移地址空间分配如下所示,用户可以通过读写相应地址来控制读出芯片功能和访问芯片状态。读出芯片地址空间包含系统配置状态、模拟配置状态、DAQ 配置状态、AWG 配置状态以及 PLL 配置状态五个部分。 -@import "data/seg_define.csv" +@import "data/seg_define.csv" using render_address.py
    @@ -62,4 +62,4 @@ DAQ MCU 发出的控制码字格式(32 位): 读出芯片的详细寄存器/存储定义参考 [读出芯片 IDS 表](../script/读出子系统IDS表.xls)。相应的驱动软件配置文件参考 [读出芯片 IDS 表 JSON](../data/ids.json)。 -@import "data/ids.json" +@import "data/ids.json" using render_registers.py diff --git a/chapters/09_package_dimensions.md b/chapters/09_package_dimensions.md index c697fdf..1175b4b 100644 --- a/chapters/09_package_dimensions.md +++ b/chapters/09_package_dimensions.md @@ -1,13 +1,51 @@ -# 芯片尺寸 +# 封装与机械尺寸 + +## 封装外形 ![chip_size](../assets/chip_size.png) ## 封装参数 -| 参数 | 值 | +| 参数 | 规格 | 单位 | +|:-|:-|:-| +| 封装类型 | CSP-BGA | — | +| 焊球间距 | 1.0 | mm | +| 焊球直径 | 0.5 | mm | +| 焊球数量 | 13 × 13 = 169 | 个 | +| 基板尺寸 | 14.0 × 14.0 | mm | + +## 推荐焊接回流曲线 + +| 阶段 | 温度范围 | 持续时间 | 单位 | +|:-|:-|:-|:-| +| 预热 | — | — | °C / s | +| 恒温 | — | — | °C / s | +| 回流峰值 | — | — | °C / s | +| 冷却 | — | — | °C / s | + +
    +推荐回流曲线参数待封装厂家提供后补充。 +
    + +## 标识信息 + +| 位置 | 内容 | |:-|:-| -| 焊球中心间距 | 1 mm | -| 焊球大小 | 0.5 mm | -| 焊球数量 | 13 × 13 = 169 个 | -| 基板大小 | 1.4 cm × 1.4 cm | -| 封装类型 | CSP-BGA | +| 顶面 | 芯片型号、批号、生产日期 | +| 底面 | — | + +
    +芯片标识格式待确认后补充。 +
    + +## 存储与搬运 + +| 参数 | 规格 | 单位 | +|:-|:-|:-| +| 存储温度范围 | −65 ~ +150 | °C | +| MSL 等级 | — | — | +| 防潮包装要求 | — | — | + +
    +MSL 等级和防潮要求待封装设计确认后补充。 +
    diff --git a/doc_builder/codeblock_extension.py b/doc_builder/codeblock_extension.py new file mode 100644 index 0000000..17afc9e --- /dev/null +++ b/doc_builder/codeblock_extension.py @@ -0,0 +1,76 @@ +"""Code block renderer — standalone pre-markdown step. + +Usage in build pipeline: + md_text = render_code_blocks(md_text, project_dir) + html = markdown.markdown(md_text, extensions=[...]) + +Syntax in chapters: + ```schemdraw + import schemdraw + ... + ``` + +Runs BEFORE markdown parsing, so avoids priority conflicts with +fenced_code / codehilite extensions. + +Renders live in doc_builder/renderers/.py. +Each exports: render(code_text, project_dir) -> HTML string. +""" +import importlib.util +import re +from pathlib import Path +from html import unescape as html_unescape + +CODE_BLOCK_RE = re.compile( + r'(?:^|\n)```(\w+)\s*\n(.*?)\n```(?:\n|$)', + re.DOTALL, +) + +PASSTHROUGH = {'mermaid', 'wavedrom', 'python', 'bash', 'json', 'yaml', 'ascii'} + + +def _load_renderer(lang, project_dir): + rp = project_dir / 'doc_builder' / 'renderers' / f'{lang}.py' + if not rp.exists(): + return None + spec = importlib.util.spec_from_file_location( + f'render_{lang}', str(rp) + ) + mod = importlib.util.module_from_spec(spec) + spec.loader.exec_module(mod) + return getattr(mod, 'render', None) + + +def render_code_blocks(text, project_dir): + """Find ```lang blocks, dispatch to renderers, replace inline.""" + result = [] + last_end = 0 + + for match in CODE_BLOCK_RE.finditer(text): + lang = match.group(1) + code = match.group(2) + + result.append(text[last_end:match.start()]) + + if lang in PASSTHROUGH: + result.append(match.group(0)) + else: + renderer = _load_renderer(lang, project_dir) + if renderer is None: + result.append(match.group(0)) + else: + try: + code_text = html_unescape(code.strip()) + html = renderer(code_text, project_dir) + result.append(f'\n{html}\n') + except Exception as e: + result.append( + f'\n
    ' + f'{lang} render error: {e}' + f'
    \n' + ) + + last_end = match.end() + + result.append(text[last_end:]) + return ''.join(result) diff --git a/doc_builder/image_extension.py b/doc_builder/image_extension.py new file mode 100644 index 0000000..6276f81 --- /dev/null +++ b/doc_builder/image_extension.py @@ -0,0 +1,61 @@ +"""Python-Markdown extension: image sizing. + +Syntax (standard Markdown paths, relative to chapter file): + ![alt](../assets/x.png){s=50%} → scale to 50% (width, auto height) + ![alt](../assets/x.png){w=50%} → width 50%, auto height + ![alt](../assets/x.png){w=50%, h=300} → width 50%, max-height 300px + ![alt](../assets/x.png) → default: s=75% +""" +import re +from markdown.extensions import Extension +from markdown.inlinepatterns import ImageInlineProcessor + +IMG_RE = ( + r'\!\[(?P.*?)\]\((?P[^)]+)\)' + r'(?:\{' + r'(?:s=(?P\d+%))?' + r'(?:,\s*)?' + r'(?:w=(?P\d+%))?' + r'(?:,\s*h=(?P\d+)(?:px)?)?' + r'\}?)?' +) + + +class SizedImageProcessor(ImageInlineProcessor): + + def handleMatch(self, m, data): + alt = m.group('alt') + src = m.group('src') + scale = m.group('s') + width = m.group('w') + height = m.group('h') + + # Normalize paths for HTML output + src = src.replace('../assets/', 'assets/') + src = src.replace('../circuits/output/', 'assets/') + + if scale: + style = f'width:{scale}; height:auto; max-width:none; max-height:none' + elif width: + style = f'width:{width}; max-width:none' + if height: + style += f'; max-height:{height}px' + else: + style = 'width:75%; height:auto' + + img_tag = f'{alt}' + el = self.md.htmlStash.store(img_tag) + return el, m.start(0), m.end(0) + + +class ImageRowProcessor(Extension): + + def extendMarkdown(self, md): + md.inlinePatterns.register( + SizedImageProcessor(IMG_RE, md), 'image_sized', 160, + ) + md.inlinePatterns.deregister('image_link') + + +def makeExtension(**kwargs): + return ImageRowProcessor(**kwargs) diff --git a/doc_builder/import_handler.py b/doc_builder/import_handler.py new file mode 100644 index 0000000..384daf4 --- /dev/null +++ b/doc_builder/import_handler.py @@ -0,0 +1,152 @@ +"""@import handler — thin dispatcher with plugin architecture. + +Syntax: + @import "data/file.csv" → default: code display with line numbers + @import "data/file.csv" as table → load doc_builder/render_table.py, call render() + @import "data/file.json" as registers → load doc_builder/render_registers.py + @import "data/file.yaml" as requirements → load doc_builder/render_requirements.py + +How 'as' works: + as → importlib loads doc_builder/render_.py + calls render(filepath) → returns HTML string + + No 'as' → default code viewer with line numbers + +Each project provides its own render_.py scripts in doc_builder/. +Built-in renderers (table, bga, address, registers, requirements) are +included and can be copied between projects. +""" +import importlib +import re +import traceback +from pathlib import Path + + +IMPORT_RE = re.compile( + r'^@import\s+"([^"]+)"(?:\s+using\s+([\w.]+))?\s*$', + re.MULTILINE, +) + + +def handle(import_path, renderer_name, base_dir, process_chapter_fn): + """Resolve @import and dispatch to renderer. + + Args: + import_path: file path from @import directive + renderer_name: name after 'as' (e.g. 'table', 'registers'), or None + base_dir: project root for path resolution + process_chapter_fn: callback for recursive .md processing + + Returns: + HTML string + """ + fp = (base_dir / import_path).resolve() + if not fp.exists(): + return ( + f'
    ' + f'@import file not found: {import_path}' + f'
    ' + ) + + # .md files are always processed recursively (no renderer script) + if fp.suffix.lower() == '.md': + md_text = fp.read_text(encoding='utf-8') + return process_chapter_fn(md_text) + + # No as specified → default: code display with line numbers + if not renderer_name: + return _render_default(fp) + + # as → load renderer script and call render() + try: + renderer = _load_renderer(renderer_name, base_dir) + return renderer(fp) + except RendererNotFound: + # Graceful fallback: show default code view + note + note = ( + f'
    ' + f'Renderer script doc_builder/{renderer_name} not found. ' + f'Showing default code view. ' + f'Create this file with a render(filepath) function ' + f'to customize rendering.' + f'
    ' + ) + return note + _render_default(fp) + except Exception as e: + return ( + f'
    ' + f'@import renderer error ({import_path} as {renderer_name}): {e}' + f'
    {traceback.format_exc()}
    ' + f'
    ' + ) + + +def process_imports(md_text, base_dir, process_chapter_fn): + """Replace all @import directives in md_text with rendered HTML.""" + def _replace(match): + path = match.group(1) + hint = match.group(2) # may be None + return handle(path, hint, base_dir, process_chapter_fn) + return IMPORT_RE.sub(_replace, md_text) + + +# ---- Renderer plugin loader ---- + +class RendererNotFound(Exception): + """Raised when a render_.py script does not exist.""" + pass + + +def _load_renderer(name, base_dir): + """Load a renderer script from doc_builder/. + + The module must export: render(filepath) → HTML string + """ + doc_builder = base_dir / 'doc_builder' + script = doc_builder / name + + if not script.exists(): + raise RendererNotFound( + f'doc_builder/{name} not found' + ) + + spec = importlib.util.spec_from_file_location( + name.replace('.', '_'), str(script) + ) + mod = importlib.util.module_from_spec(spec) + spec.loader.exec_module(mod) + + if not hasattr(mod, 'render'): + raise RendererNotFound( + f'doc_builder/{name} has no render() function' + ) + + return mod.render + + +# ---- Default renderer: code with line numbers ---- + +def _render_default(filepath): + """Display file content as code with line numbers.""" + try: + with open(filepath, encoding='utf-8') as f: + lines = f.readlines() + except UnicodeDecodeError: + return '
    Cannot display binary file.
    ' + + from html import escape + ext = filepath.suffix.lstrip('.').upper() + html = [ + f'
    ', + f'
    {filepath.name} ({len(lines)} lines)
    ', + f'
    ',
    +    ]
    +    for i, line in enumerate(lines, 1):
    +        # escape HTML but keep the line content
    +        escaped = escape(line.rstrip('\n\r'))
    +        html.append(
    +            f'{i:4d} '
    +            f'{escaped}'
    +        )
    +    html.append('
    ') + return '\n'.join(html) diff --git a/doc_builder/render_address.py b/doc_builder/render_address.py new file mode 100644 index 0000000..4ad5338 --- /dev/null +++ b/doc_builder/render_address.py @@ -0,0 +1,45 @@ +"""Address map table renderer — for seg_define.csv style files.""" +import csv +from html import escape + + +def render(filepath): + """Render CSV as address map table, merging repeated function cells.""" + rows = [] + with open(str(filepath), encoding='utf-8') as f: + for row in csv.DictReader(f): + rows.append({ + k.strip(): v.strip() if v else '' + for k, v in row.items() + }) + + if not rows: + return '

    empty file

    ' + + # Merge consecutive rows with '^' in 功能划分 + segs, prev = [], '' + for r in rows: + func = r.get('功能划分', '') + if func == '^': + func = prev + else: + prev = func + segs.append({ + '功能划分': func, + '子模块': r.get('子模块', ''), + '开始地址': r.get('开始地址', ''), + '大小': r.get('大小', ''), + }) + + cols = ['功能划分', '子模块', '开始地址', '大小'] + h = [''] + for c in cols: + h.append(f'') + h.append('') + for s in segs: + h.append('') + for c in cols: + h.append(f'') + h.append('') + h.append('
    {escape(c)}
    {escape(str(s.get(c, "")))}
    ') + return '\n'.join(h) diff --git a/doc_builder/render_bga.py b/doc_builder/render_bga.py new file mode 100644 index 0000000..4103075 --- /dev/null +++ b/doc_builder/render_bga.py @@ -0,0 +1,36 @@ +"""BGA ball grid renderer — for pin_loc.csv style files.""" +import csv +from html import escape + + +def render(filepath): + """Render CSV as BGA ball grid table.""" + rows = [] + with open(str(filepath), encoding='utf-8') as f: + for row in csv.reader(f): + rows.append([c.strip() for c in row]) + + if len(rows) < 2: + return '

    empty or malformed file

    ' + + ncols = len(rows[0]) - 1 + h = [''] + for c in range(1, ncols + 1): + h.append(f'') + h.append('') + + for r in rows[1:]: + if not r: + continue + h.append( + f'' + ) + for c in range(1, ncols + 1): + val = r[c] if c < len(r) else '' + h.append( + f'' + ) + h.append('') + h.append('
    {c}
    {escape(r[0])}{escape(val)}
    ') + return '\n'.join(h) diff --git a/doc_builder/render_registers.py b/doc_builder/render_registers.py new file mode 100644 index 0000000..a3024ce --- /dev/null +++ b/doc_builder/render_registers.py @@ -0,0 +1,115 @@ +"""Register definition (ids.json) → HTML tables.""" +import json +from html import escape + + +MODULE_ORDER = [ + 'SYS_REG', 'SYS_ANA', 'SYS_PLL', + 'DAQ_REG', 'DAQ_PAR', 'DAQ_FLT', + 'AWG_REG', 'TRIG_CTRL', 'PUMP_REG', 'MIXER_REG', +] + +MODULE_DESC = { + 'SYS_REG': '系统控制寄存器', 'SYS_ANA': '模拟配置寄存器', + 'SYS_PLL': 'PLL 配置寄存器', 'DAQ_REG': 'DAQ 控制寄存器', + 'DAQ_PAR': 'DAQ 读出参数配置', 'DAQ_FLT': 'DAQ 匹配滤波器系数', + 'AWG_REG': 'AWG 控制寄存器', 'TRIG_CTRL': '触发控制寄存器', + 'PUMP_REG': 'Pump 控制寄存器', 'MIXER_REG': '混频器控制寄存器', +} + + +def eh(s): + return escape(str(s)) + + +def _bits_str(bits): + if not bits: + return '' + if len(bits) == 1 or bits[0] == bits[-1]: + return f'[{bits[0]}]' + return f'[{bits[0]}:{bits[-1]}]' + + +def _range_str(r): + if not r or not r.get('value'): + return '' + t, v = r.get('type', 'str'), r.get('value', '') + if t == 'str': + if v == 'ANY': + return '任意' + if v == 'NA': + return '' + return eh(str(v)) + if t == 'list' and isinstance(v, list): + return f'{v[0]} ~ {v[1]}' + if t == 'set' and isinstance(v, list): + return ', '.join(str(x) for x in v) + return eh(str(v)) + + +def _perm_badge(p): + badges = { + 'RW': 'RW', + 'RO': 'RO', + 'WC': 'WC', + } + return badges.get(p, eh(p)) + + +def render(json_path): + """Load ids.json and render all register tables as HTML.""" + with open(json_path, encoding='utf-8') as f: + data = json.load(f) + modules = data.get('Modules', {}) + + html = [] + for mod_name in MODULE_ORDER: + if mod_name not in modules: + continue + entries = modules[mod_name] + desc = MODULE_DESC.get(mod_name, '') + html.append( + f'

    {mod_name} — {desc}

    ' + ) + html.append(f'

    共 {len(entries)} 个寄存器/存储段。

    ') + + for seg_name, seg in entries.items(): + addr = seg.get('OffsetAddress', '') + perm = seg.get('Permission', '') + sd = seg.get('SegDescription', '') + fields = seg.get('Fields', []) + + html.append( + f'

    {seg_name.upper()}

    ' + ) + html.append( + f'

    ' + f'偏移地址:{eh(addr)} ' + f'权限:{_perm_badge(perm)} ' + f'描述:{eh(sd)}

    ' + ) + + if fields: + html.append( + '' + '' + '' + '' + '' + '' + '' + ) + for f in fields: + b = f.get('Bits', []) + html.append( + f'' + f'' + f'' + f'' + f'' + f'' + f'' + ) + html.append('
    BitsField NameReset ValueRangeDescription
    {_bits_str(b)}{eh(f.get("FieldName", ""))}{eh(f.get("ResetValue", ""))}{_range_str(f.get("Range"))}{eh(f.get("FieldDescription", ""))}
    ') + html.append('') + return '\n'.join(html) diff --git a/doc_builder/render_requirements.py b/doc_builder/render_requirements.py new file mode 100644 index 0000000..8120928 --- /dev/null +++ b/doc_builder/render_requirements.py @@ -0,0 +1,80 @@ +"""YAML requirements renderer — for request_*.yaml style files.""" +from html import escape + + +def render(filepath): + """Render YAML requirements as grouped tables (F_/P_/S_ sections).""" + try: + import yaml + except ImportError: + return '
    pyyaml not installed. Run: pip install pyyaml
    ' + + with open(str(filepath), encoding='utf-8') as f: + data = yaml.safe_load(f) + + if not isinstance(data, dict): + return f'
    {escape(str(data))}
    ' + + sections = { + 'F_': ('功能需求', []), + 'P_': ('性能需求', []), + 'S_': ('规格需求', []), + } + other = [] + + for key, val in data.items(): + if not isinstance(val, dict): + continue + name = val.get('name', key) + desc = val.get('description', '') + limit = val.get('limit', None) + + limit_str = '' + if limit and isinstance(limit, dict): + parts = [] + if 'min' in limit and limit['min'] is not None: + parts.append(f'>= {limit["min"]}') + if 'max' in limit and limit['max'] is not None: + parts.append(f'<= {limit["max"]}') + if 'value' in limit: + parts.append(str(limit['value'])) + if 'unit' in limit: + parts.append(limit['unit']) + if 'count' in limit: + parts.append(f'x{limit["count"]}') + limit_str = ' '.join(parts) + + entry = {'id': key, 'name': name, 'desc': desc, 'limit': limit_str} + placed = False + for prefix in sections: + if key.startswith(prefix): + sections[prefix][1].append(entry) + placed = True + break + if not placed: + other.append(entry) + + html = [] + for prefix, (label, items) in sections.items(): + if not items: + continue + html.append(f'

    {label}({len(items)} 项)

    ') + html.append( + '' + '' + '' + '' + '' + '' + ) + for e in items: + html.append( + f'' + f'' + f'' + f'' + f'' + f'' + ) + html.append('
    ID名称描述指标
    {escape(e["id"])}{escape(e["name"])}{escape(e["desc"])}{escape(e["limit"])}
    ') + return '\n'.join(html) diff --git a/doc_builder/render_table.py b/doc_builder/render_table.py new file mode 100644 index 0000000..c3e11b6 --- /dev/null +++ b/doc_builder/render_table.py @@ -0,0 +1,30 @@ +"""Generic CSV table renderer. Used for all CSV @import unless overridden.""" +import csv +from html import escape + + +def render(filepath): + """Load CSV and render as HTML table.""" + rows = [] + with open(str(filepath), encoding='utf-8') as f: + for row in csv.DictReader(f): + rows.append({ + k.strip(): v.strip() if v else '' + for k, v in row.items() + }) + + if not rows: + return '

    empty file

    ' + + cols = list(rows[0].keys()) + h = [''] + for c in cols: + h.append(f'') + h.append('') + for r in rows: + h.append('') + for c in cols: + h.append(f'') + h.append('') + h.append('
    {escape(c)}
    {escape(str(r.get(c, "")))}
    ') + return '\n'.join(h) diff --git a/doc_builder/renderers/schemdraw.py b/doc_builder/renderers/schemdraw.py new file mode 100644 index 0000000..71f99a0 --- /dev/null +++ b/doc_builder/renderers/schemdraw.py @@ -0,0 +1,89 @@ +"""Schemdraw code block renderer. + +Executes Python schemdraw code from a chapter code block and returns SVG. + +Usage in markdown: + ```schemdraw + import schemdraw + from schemdraw import elements as e + with schemdraw.Drawing(show=False) as d: + d += e.Resistor().right().label('R1') + ``` +""" +import base64 +import subprocess +import sys +import tempfile +import textwrap +from pathlib import Path + + +def render(code: str, project_dir: Path = None) -> str: + """Execute schemdraw code, return inline SVG as HTML tag. + + Args: + code: Python source code using schemdraw + project_dir: project root (unused, kept for interface consistency) + + Returns: + HTML string with inline base64 SVG image + """ + # Write code to a temp script and execute, capturing SVG output + wrapped = textwrap.dedent(code).strip() + + with tempfile.NamedTemporaryFile( + suffix='.svg', mode='w+', encoding='utf-8', delete=False + ) as tmp: + svg_path = Path(tmp.name) + + try: + # Execute the schemdraw code as a subprocess for isolation. + # The script must produce 'OUTPUT: ' on the last line, + # or we inject a save hook. + script = _wrap_code(wrapped, str(svg_path)) + result = subprocess.run( + [sys.executable, '-c', script], + capture_output=True, text=True, timeout=30, + ) + if result.returncode != 0: + return ( + f'
    ' + f'Schemdraw error:
    {result.stderr[:500] or result.stdout[:500]}
    ' + f'
    ' + ) + + # Read the generated SVG + if svg_path.exists() and svg_path.stat().st_size > 0: + svg_content = svg_path.read_text(encoding='utf-8') + b64 = base64.b64encode(svg_content.encode('utf-8')).decode('ascii') + return ( + f'
    ' + f'schemdraw diagram' + f'
    ' + ) + else: + return ( + f'
    ' + f'Schemdraw ran but produced no SVG output.' + f'
    ' + ) + finally: + if svg_path.exists(): + svg_path.unlink(missing_ok=True) + + +def _wrap_code(code: str, svg_path: str) -> str: + """Wrap user code to redirect schemdraw output to a file.""" + # schemdraw writes SVG to the 'file' parameter of Drawing(). + # We modify the code to inject file= if missing. + if "file=" not in code and "Drawing(" in code: + code = code.replace( + 'Drawing(show=False)', + f"Drawing(show=False, file=r'{svg_path}')", + ) + code = code.replace( + 'Drawing()', + f"Drawing(show=False, file=r'{svg_path}')", + ) + return code diff --git a/doc_builder/themes/datasheet.css b/doc_builder/themes/datasheet.css index d81a6f3..f265bcc 100644 --- a/doc_builder/themes/datasheet.css +++ b/doc_builder/themes/datasheet.css @@ -37,72 +37,78 @@ body { background: var(--bg); max-width: 900px; margin: 0 auto; - padding: 2em 2.5em; + padding: 0; } /* ---------- Cover ---------- */ .cover-page { - background: linear-gradient(160deg, var(--cover-bg) 0%, #002244 100%); - color: var(--cover-text); + width: 100%; + box-sizing: border-box; + padding: 0 2.5em; + margin: 0 0 3em 0; + min-height: 100vh; + position: relative; +} +.cover-page-inner { + position: absolute; + top: 50%; left: 50%; + transform: translate(-50%, -50%); text-align: center; - padding: 80px 40px; - margin: -2em -2.5em 3em -2.5em; - border-radius: 0 0 8px 8px; - display: flex; - flex-direction: column; - justify-content: center; - align-items: center; - min-height: 95vh; + width: 100%; + padding: 0 2.5em; + box-sizing: border-box; +} +.cover-page .cover-meta { + position: absolute; + bottom: 40px; + left: 0; right: 0; + font-size: 0.8rem; + color: var(--text-muted); + text-align: center; +} +.cover-page::after { + content: ''; + position: absolute; + bottom: 0; left: 50%; transform: translateX(-50%); + width: 60px; height: 1px; + background: var(--th-border); } .cover-page .product-family { font-family: var(--font-heading); - font-size: 0.9rem; - letter-spacing: 0.3em; + font-size: 0.75rem; + letter-spacing: 0.5em; text-transform: uppercase; - opacity: 0.7; - margin-bottom: 40px; + color: var(--text-muted); + margin-bottom: 64px; } .cover-page h1 { font-family: var(--font-heading); font-size: 2.4rem; font-weight: 700; - line-height: 1.35; + line-height: 1.4; margin-bottom: 20px; - color: var(--cover-text); + color: var(--heading); border: none; padding: 0; } .cover-page .subtitle { font-family: var(--font-heading); - font-size: 1.1rem; - font-weight: 300; - opacity: 0.8; - margin-bottom: 48px; -} -.cover-page .feature-list { - list-style: none; - display: flex; - flex-direction: column; - align-items: center; - gap: 10px; - margin: 0 auto 48px; - padding: 0; -} -.cover-page .feature-list li { - font-family: var(--font-heading); - font-size: 0.95rem; - padding: 6px 24px; - border: 1px solid rgba(255,255,255,0.2); - border-radius: 4px; - width: 280px; + font-size: 1rem; + font-weight: 400; + color: var(--text-muted); + margin-bottom: 64px; } +.cover-page .feature-list { display: none; } .cover-page .cover-meta { - margin-top: auto; - font-size: 0.85rem; - opacity: 0.55; - padding-top: 40px; + position: absolute; + bottom: 40px; + font-size: 0.8rem; + color: var(--text-muted); } -.cover-page .cover-meta p { text-align: center; margin: 4px 0; } +.cover-page .cover-meta p { margin: 4px 0; } + +/* ---------- Chapters ---------- */ +.chapter { padding: 2em 2.5em; } /* ---------- Headings ---------- */ h1 { @@ -202,16 +208,96 @@ pre { } pre code { background: none; padding: 0; border: none; font-size: inherit; } +/* Code block with line numbers (default @import display) */ +.code-block { + margin: 1em 0; + border: 1px solid var(--th-border); + border-radius: 3px; + overflow: hidden; +} +.code-header { + background: var(--th-bg); + font-family: var(--font-heading); + font-size: 0.78rem; + padding: 4px 10px; + color: var(--text-muted); + border-bottom: 1px solid var(--th-border); +} +.code-lines { + margin: 0; + border: none; + border-left: none; + font-size: 0.78rem; + line-height: 1.55; + padding: 8px 0; + background: #fafbfc; +} +.code-lines .ln { + display: inline-block; + width: 40px; + text-align: right; + color: var(--text-muted); + user-select: none; + padding-right: 12px; + margin-right: 8px; + border-right: 1px solid var(--th-border); +} +.code-lines .lc { + color: var(--text); +} + /* ---------- Figures ---------- */ -figure { margin: 1.5em auto; text-align: center; max-width: 100%; } -figure img { max-width: 100%; height: auto; } +figure { + margin: 1.2em auto; + text-align: center; + max-width: 75%; +} +figure img { + max-width: 100%; + max-height: 420px; + height: auto; + border-radius: 3px; +} figure figcaption { font-family: var(--font-heading); - font-size: 0.88rem; + font-size: 0.85rem; color: var(--text-muted); - margin-top: 6px; + margin-top: 4px; +} +/* Standalone images (not in figure) */ +p img { + max-width: 75%; + max-height: 400px; + height: auto; + display: block; + margin: 0.8em auto; + border-radius: 3px; +} +/* Small images: inline icons, small diagrams */ +img.img-sm, figure.img-sm { max-width: 40%; } +figure.img-sm img { max-height: 240px; } +/* Full-width: detailed block diagrams that need detail */ +img.img-lg, figure.img-lg { max-width: 95%; } +figure.img-lg img { max-height: 600px; } +/* Images inside table cells — tighter constraints */ +td img { + max-width: 100%; + max-height: 180px; + margin: 0; +} +/* Side-by-side image row */ +.img-row { + display: flex; + gap: 1em; + justify-content: center; + align-items: flex-start; + margin: 1em 0; +} +.img-row img { + flex: 0 0 auto; + margin: 0; + border-radius: 3px; } -p img { max-width: 100%; height: auto; display: block; margin: 1em auto; } /* ---------- Blockquote / Notes ---------- */ blockquote { @@ -283,9 +369,9 @@ blockquote { /* ---------- Auto-numbering ---------- */ body.auto-numbering { counter-reset: section; } -body.auto-numbering h1 { counter-increment: section; counter-reset: subsection; } -body.auto-numbering h1::before { content: counter(section) ". "; } -body.auto-numbering h2 { counter-increment: subsection; counter-reset: subsubsection; } -body.auto-numbering h2::before { content: counter(section) "." counter(subsection) " "; } -body.auto-numbering h3 { counter-increment: subsubsection; } -body.auto-numbering h3::before { content: counter(section) "." counter(subsection) "." counter(subsubsection) " "; } +body.auto-numbering .chapter h1 { counter-increment: section; counter-reset: subsection; } +body.auto-numbering .chapter h1::before { content: counter(section) ". "; } +body.auto-numbering .chapter h2 { counter-increment: subsection; counter-reset: subsubsection; } +body.auto-numbering .chapter h2::before { content: counter(section) "." counter(subsection) " "; } +body.auto-numbering .chapter h3 { counter-increment: subsubsection; } +body.auto-numbering .chapter h3::before { content: counter(section) "." counter(subsection) "." counter(subsubsection) " "; } diff --git a/readme.md b/readme.md index e61f205..aa388a2 100644 --- a/readme.md +++ b/readme.md @@ -1,64 +1,115 @@ # RBPU16 读出基带处理芯片 · 数据手册 -RBPU16 是一款用于超导量子比特态信息读出的 SoC 芯片,最大支持 16 个量子比特并行读出,内部集成 PLL、ADC、DAC、DSP 等模块。 - -本手册帮助相关工程师开展芯片性能评估、硬件设计、驱动开发及芯片使用。 +纯文本管理,Python 脚本构建,生成自包含 HTML 报告。 ## 项目结构 ``` -├── build.ipynb # 构建入口:VS Code 打开 → Run All → 生成报告 -├── project.yaml # 项目配置(标题、章节列表、作者) -├── chapters/ # 章节源文件(Markdown,纯文本管理) -│ ├── 00_cover.md -│ ├── 01_specifications.md -│ ├── ... -│ └── appendix/ -├── data/ # 结构化数据 -│ ├── pin_name.csv # 管脚定义(用户直接编辑) -│ ├── pin_loc.csv # 管脚 BGA 位置网格 -│ ├── seg_define.csv # 寄存器地址段定义 -│ └── ids.json # 寄存器详细定义(由 XLS 生成,供脚本读取) -├── assets/ # 图片(PNG/JPG) -├── doc_builder/ # 构建工具 -│ └── themes/ -│ └── datasheet.css -└── output/ # 构建产物(gitignore) - └── RBPU16_数据手册.html # 自包含单文件,可直接发送 +├── build.ipynb # 构建入口:VS Code 打开 → Run All +├── project.yaml # 项目配置(标题、作者、章节列表) +├── chapters/ # 章节源文件(Markdown) +├── data/ # 结构化数据(CSV / JSON) +├── assets/ # 图片(PNG / JPG) +├── doc_builder/ # 构建工具(可跨项目复用) +│ ├── themes/datasheet.css +│ ├── renderers/ # 代码块渲染器 +│ │ └── schemdraw.py +│ ├── render_table.py # @import 渲染器 +│ ├── render_bga.py +│ ├── render_address.py +│ ├── render_registers.py +│ ├── render_requirements.py +│ ├── import_handler.py # @import 调度器 +│ ├── image_extension.py # 图片尺寸扩展 +│ └── codeblock_extension.py# 代码块渲染(pre-markdown) +└── output/ # 构建产物 + └── RBPU16_Data_Sheet.html ``` -## 快速开始 +## project.yaml -### 生成报告 +每个项目根目录的配置文件,`build.ipynb` 读取它来驱动构建。 -1. VS Code 打开 `build.ipynb` -2. 点击 **Run All** -3. 报告生成到 `output/` 目录 +```yaml +title: RBPU16 读出基带处理芯片 +subtitle: 数据手册 +author: 郭成 +theme: datasheet +chapters: + - 00_cover.md + - 01_specifications.md + - ... +``` -> 依赖 `markdown` 库,首次运行会自动安装。 +## 语法参考 -### 编辑内容 - -| 修改什么 | 编辑哪个文件 | 如何重新生成 | -|---------|------------|------------| -| 正文内容 | `chapters/*.md` | Run All | -| 管脚定义 | `data/pin_name.csv` | Run All | -| 地址段 | `data/seg_define.csv` | Run All | -| 寄存器定义 | `script/读出子系统IDS表.xls` | 先运行 `script/ids_import.ipynb`,再 Run All | -| 报告样式 | `doc_builder/themes/datasheet.css` | Run All | -| 封面/标题/章节列表 | `project.yaml` | Run All | - -### @import 语法 - -在章节 Markdown 中使用 `@import` 引用数据文件,构建时自动渲染为表格: +### 1. 图片 ```markdown -@import "data/pin_name.csv" → 管脚定义表 -@import "data/pin_loc.csv" → BGA 焊球网格 -@import "data/seg_define.csv" → 地址映射总表 -@import "data/ids.json" → 寄存器详细定义 +![alt](../assets/x.png){s=75%} # 等比例缩放,默认 75% +![alt](../assets/x.png){s=100%} # 原始尺寸 +![alt](../assets/x.png){w=50%} # 宽度 50%,高度自适应 +![alt](../assets/x.png){w=48%, h=200} # 宽度 48% + 最大高度 200px ``` -### 分享报告 +- `{s=N%}` — 等比例缩放,同时控制宽高 +- `{w=N%}` — 仅控制宽度 +- `{w=N%, h=M}` — 宽度 + 最大高度(像素) +- 两张图 `{w=N%}` 相加 ≤ 100% 时自动并排 -`output/` 中的 HTML 是**自包含**的——CSS 和图片全部内嵌,单个文件即可在任何浏览器中打开,无需附带 `assets/` 目录。 +### 2. `@import` — 导入数据文件 + +```markdown +@import "data/file.csv" # 无 using → 行号代码视图 +@import "data/file.csv" using render_table.py # 通用表格 +@import "data/pin_loc.csv" using render_bga.py # BGA 焊球网格 +@import "data/ids.json" using render_registers.py # 寄存器定义表 +``` + +`using ` 加载项目根目录下的 Python 脚本,调用 `render(filepath)` → HTML。脚本不存在时降级为行号视图,不报错。 + +### 3. 代码块渲染 — ` ```lang ` + +```markdown +```schemdraw +import schemdraw +from schemdraw import elements as e +with schemdraw.Drawing(show=False) as d: + d += e.Resistor().right().label('R1') +``` +``` + +构建时在 markdown 之前执行:` ```lang ` 代码块 → `doc_builder/renderers/lang.py` → SVG/HTML。`mermaid` 等标签为 passthrough(浏览器渲染)。 + +### 4. 表格占位符 + +表格中 `—` 表示待补充数据。 + +## 构建流程 + +``` +章节 .md 文件 + │ + ├─→ @import 处理器(替换为渲染 HTML) + ├─→ 代码块渲染器(```schemdraw → SVG) + └─→ Python-Markdown(→ HTML) + │ + ├─→ 图片尺寸扩展({s=N%} / {w=N%}) + └─→ codehilite(代码高亮) + │ + ▼ +组装 HTML(封面 + 章节 + 修订历史) + │ + ▼ +图片 base64 内嵌 → 自包含单文件 +``` + +## 编辑指南 + +| 修改内容 | 编辑 | 重建 | +|---------|------|------| +| 正文 | `chapters/*.md` | Run All | +| 管脚 | `data/pin_name.csv` | Run All | +| 寄存器 | `script/读出子系统IDS表.xls` | ids_import.ipynb → Run All | +| 样式 | `doc_builder/themes/datasheet.css` | Run All | +| 配置 | `project.yaml` | Run All |