缓存变化

This commit is contained in:
guocheng 2026-07-19 20:56:15 +08:00
parent f04a3dc263
commit 9a33df9c42
22 changed files with 1597 additions and 719 deletions

View File

@ -4,8 +4,8 @@
"cell_type": "markdown", "cell_type": "markdown",
"metadata": {}, "metadata": {},
"source": [ "source": [
"# RBPU16 数据手册\n", "# RBPU16 Data Sheet",
"\nRun All → 生成自包含 HTML可直接发送给他人" "\n\nRun All -> self-contained HTML."
] ]
}, },
{ {
@ -14,19 +14,13 @@
"metadata": {}, "metadata": {},
"outputs": [], "outputs": [],
"source": [ "source": [
"import sys, os, csv, json, re, subprocess, base64, mimetypes\n", "import sys,os,re,base64,mimetypes,subprocess\n",
"from datetime import datetime\n", "from datetime import datetime as dt; from pathlib import Path; from html import escape as eh\n",
"from pathlib import Path\n", "try: import markdown\n",
"from html import escape\n", "except: subprocess.check_call([sys.executable,'-m','pip','install','markdown','-q']); import markdown\n",
"\n", "B=Path(os.getcwd()).resolve(); os.chdir(str(B)); sys.path.insert(0,str(B))\n",
"try:\n", "from doc_builder import import_handler, image_extension, codeblock_extension\n",
" import markdown as md_lib\n", "print(f' {B.name}')\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"
] ]
}, },
{ {
@ -42,266 +36,85 @@
"metadata": {}, "metadata": {},
"outputs": [], "outputs": [],
"source": [ "source": [
"# ============================================================\n", "def pc(md_text):\n",
"# Data loaders\n", " md_text = import_handler.process_imports(md_text,B,pc)\n",
"# ============================================================\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", "\n",
"def csv_dicts(path):\n", "cn=[]\n",
" rows = []\n", "with open(B/'project.yaml',encoding='utf-8') as f:\n",
" with open(path, encoding=\"utf-8\") as f:\n", " ic=False\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 \"<p><em>no data</em></p>\"\n",
" cols = cols or list(rows[0].keys())\n",
" h = [\"<table><thead><tr>\"]\n",
" for c in cols: h.append(f\"<th>{eh(c)}</th>\")\n",
" h.append(\"</tr></thead><tbody>\")\n",
" for r in rows:\n",
" h.append(\"<tr>\")\n",
" for c in cols: h.append(f\"<td>{eh(str(r.get(c,\\\"\\\")))}</td>\")\n",
" h.append(\"</tr>\")\n",
" h.append(\"</tbody></table>\")\n",
" return \"\\n\".join(h)\n",
"\n",
"def render_bga_grid(raw):\n",
" if len(raw) < 2: return \"<p><em>no data</em></p>\"\n",
" ncols = len(raw[0]) - 1\n",
" h = ['<table class=\"compact pin-grid\"><thead><tr><th></th>']\n",
" for c in range(1, ncols+1): h.append(f'<th class=\"text-center\">{c}</th>')\n",
" h.append(\"</tr></thead><tbody>\")\n",
" for r in raw[1:]:\n",
" if not r: continue\n",
" h.append(f'<tr><th class=\"text-center\">{eh(r[0])}</th>')\n",
" for c in range(1, ncols+1):\n",
" val = r[c] if c < len(r) else \"\"\n",
" h.append(f'<td class=\"text-center text-mono\" style=\"font-size:6pt\">{eh(val)}</td>')\n",
" h.append(\"</tr>\")\n",
" h.append(\"</tbody></table>\")\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 \"<em>任意</em>\"\n",
" if v == \"NA\": return \"<em>-</em>\"\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\":'<span class=\"perm-rw\">RW</span>',\"RO\":'<span class=\"perm-ro\">RO</span>',\"WC\":'<span class=\"perm-wc\">WC</span>'}\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'<h3 id=\"reg-{mn.lower()}\">{mn} - {descs.get(mn,\"\")}</h3>')\n",
" h.append(f\"<p>{len(entries)} 个寄存器/存储段</p>\")\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'<h4 id=\"reg-{sn}\">{sn.upper()}</h4>')\n",
" h.append(f'<p class=\"reg-summary\"><strong>地址:</strong><code>{eh(addr)}</code> <strong>权限:</strong>{perm_b(perm)} <strong>描述:</strong>{eh(sd)}</p>')\n",
" if fields:\n",
" h.append('<table class=\"compact\"><thead><tr><th style=\"width:11%\">Bits</th><th style=\"width:18%\">Field</th><th style=\"width:14%\">Reset</th><th style=\"width:12%\">Range</th><th>Description</th></tr></thead><tbody>')\n",
" for f in fields:\n",
" b = f.get(\"Bits\",[])\n",
" h.append(f'<tr><td class=\"bits\">{bits_str(b)}</td><td class=\"text-mono\">{eh(f.get(\"FieldName\",\"\"))}</td><td class=\"reset-val\">{eh(f.get(\"ResetValue\",\"\"))}</td><td>{rng_str(f.get(\"Range\"))}</td><td>{eh(f.get(\"FieldDescription\",\"\"))}</td></tr>')\n",
" h.append(\"</tbody></table>\")\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'<div class=\"warning\">@import not found: {import_path}</div>'\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'<p><em>unsupported: {import_path}</em></p>'\n",
" except Exception as e:\n",
" import traceback\n",
" return f'<div class=\"warning\">@import error: {e}<pre>{traceback.format_exc()}</pre></div>'\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",
" for line in f:\n", " for line in f:\n",
" line = line.rstrip()\n", " line=line.rstrip()\n",
" if line.startswith(\"chapters:\"): in_ch = True; continue\n", " if line.startswith('chapters:'): ic=True; continue\n",
" if in_ch:\n", " if ic:\n",
" m = re.match(r\"^\\s*-\\s+(.+)$\", line)\n", " mx=re.match(r'^\\s*-\\s+(.+)$',line)\n",
" if m: chapter_names.append(m.group(1).strip())\n", " if mx: cn.append(mx.group(1).strip())\n",
" elif line and not line.startswith(\" \"): in_ch = False\n", " elif line and not line.startswith(' '): ic=False\n",
"\n", "\n",
"chapters = []\n", "print('Processing...')\n",
"for i, name in enumerate(chapter_names):\n", "chapters=[]\n",
" fp = BASE / \"chapters\" / name\n", "for i,n in enumerate(cn):\n",
" fp=B/'chapters'/n\n",
" if not fp.exists(): continue\n", " if not fp.exists(): continue\n",
" print(f\" [{i+1}] {name}\")\n", " print(f' [{i+1}] {n}')\n",
" content = process_chapter(fp.read_text(encoding=\"utf-8\"))\n", " c=pc(fp.read_text(encoding='utf-8'))\n",
" m = re.search(r\"<h1[^>]*>(.*?)</h1>\", content)\n", " mx=re.search(r'<h1[^>]*>(.*?)</h1>',c)\n",
" chapters.append({\"id\":f\"ch{i+1}\",\"number\":str(i+1),\"title\":m.group(1) if m else fp.stem,\"content\":content})\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",
"\n", "apd=B/'chapters'/'appendix'\n",
"apd = BASE / \"chapters\" / \"appendix\"\n",
"if apd.exists():\n", "if apd.exists():\n",
" for j, fp in enumerate(sorted(apd.glob(\"*.md\"), key=lambda p: p.name)):\n", " for j,fp in enumerate(sorted(apd.glob('*.md'),key=lambda p:p.name)):\n",
" label = chr(65+j)\n", " lb=chr(65+j)\n",
" print(f\" [appendix/{label}] {fp.name}\")\n", " c=pc(fp.read_text(encoding='utf-8'))\n",
" content = process_chapter(fp.read_text(encoding=\"utf-8\"))\n", " mx=re.search(r'<h1[^>]*>(.*?)</h1>',c)\n",
" m = re.search(r\"<h1[^>]*>(.*?)</h1>\", content)\n", " chapters.append(dict(id=f'app{lb}',number=f'Appendix {lb}',title=mx.group(1) if mx else fp.stem,content=c))\n",
" chapters.append({\"id\":f\"app{label}\",\"number\":f\"附录 {label}\",\"title\":m.group(1) if m else fp.stem,\"content\":content})\n",
"\n", "\n",
"# ============================================================\n", "CSS=(B/'doc_builder'/'themes'/'datasheet.css').read_text(encoding='utf-8')\n",
"# CSS (local)\n", "TITLE='RBPU16'; AU='Guo Cheng'; DATE=dt.now().strftime('%Y-%m-%d')\n",
"# ============================================================\n", "COVER=''.join([\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\"<li>{f}</li>\" for f in FEATURES)\n",
"COVER = \"\".join([\n",
" '<div class=\"cover-page\">',\n", " '<div class=\"cover-page\">',\n",
" '<div class=\"product-family\">Quantum Readout SoC</div>',\n", " '<div class=\"product-family\">READOUT BASEBAND PROCESSOR</div>',\n",
" '<h1>RBPU16<br>读出基带处理芯片</h1>',\n", " '<h1>RBPU16<br>',chr(35835),chr(20986),chr(22522),chr(24102),chr(22788),chr(29702),chr(33455),chr(29255),'</h1>',\n",
" '<div class=\"subtitle\">数据手册 &middot; User Manual</div>',\n", " '<div class=\"subtitle\">Data Sheet &middot; User Manual</div>',\n",
" f'<ul class=\"feature-list\">{feat_li}</ul>',\n", " f'<div class=\"cover-meta\"><p>Rev 1.0 &middot; {DATE}</p><p>{AU}</p></div>',\n",
" f'<div class=\"cover-meta\"><p>版本 1.0 &middot; {DATE}</p><p>{AUTHOR}</p></div>',\n", " '</div>',\n",
" \"</div>\",\n",
"])\n", "])\n",
"\n", "ch_html=chr(10).join(f'<div class=\"chapter\" id=\"{c[\"id\"]}\">{c[\"content\"]}</div>' for c in chapters)\n",
"toc_items = [f'<li class=\"toc-h1\"><a href=\"#{c[\"id\"]}\">{c[\"number\"]} {eh(c[\"title\"])}</a></li>' for c in chapters]\n", "FULL=chr(10).join([\n",
"TOC = '<div class=\"toc\"><h2>目录</h2><ul class=\"toc-list\">' + \"\\n\".join(toc_items) + \"</ul></div>\"\n", " '<!DOCTYPE html>','<html lang=\"zh-CN\">','<head>',\n",
"SIDEBAR = '<nav class=\"screen-toc\"><strong>目录</strong><ul>' + \"\\n\".join(toc_items) + \"</ul></nav>\"\n", " '<meta charset=\"utf-8\">','<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">',\n",
"ch_html = \"\\n\".join(f'<div class=\"chapter\" id=\"{c[\"id\"]}\">{c[\"content\"]}</div>' for c in chapters)\n", " f'<title>{TITLE} Data Sheet</title>',f'<meta name=\"author\" content=\"{AU}\">',\n",
"\n", " '<style>',CSS,'</style>','</head>','<body class=\"auto-numbering\">',\n",
"FULL_HTML = \"\\n\".join([\n", " COVER,ch_html,\n",
" \"<!DOCTYPE html>\",\n", " '<div class=\"page-break\"></div>','<h1>Revision History</h1>',\n",
" '<html lang=\"zh-CN\">',\n",
" \"<head>\",\n",
" '<meta charset=\"utf-8\">',\n",
" '<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">',\n",
" f\"<title>{TITLE} {SUBTITLE}</title>\",\n",
" f'<meta name=\"author\" content=\"{AUTHOR}\">',\n",
" \"<style>\", CSS, \"</style>\",\n",
" \"</head>\",\n",
" '<body class=\"auto-numbering\">',\n",
" COVER, TOC, ch_html,\n",
" '<div class=\"page-break\"></div>',\n",
" \"<h1>修订历史</h1>\",\n",
" '<table class=\"revision compact\">',\n", " '<table class=\"revision compact\">',\n",
" \"<thead><tr><th>版本</th><th>日期</th><th>修订内容</th><th>作者</th></tr></thead>\",\n", " '<thead><tr><th>Rev</th><th>Date</th><th>Changes</th><th>Author</th></tr></thead>',\n",
" f\"<tbody><tr><td>1.0</td><td>{DATE}</td><td>初版发布</td><td>{AUTHOR}</td></tr></tbody>\",\n", " f'<tbody><tr><td>1.0</td><td>{DATE}</td><td>Initial</td><td>{AU}</td></tr></tbody>','</table>',\n",
" \"</table>\",\n", " '</body>','</html>',\n",
" SIDEBAR,\n",
" \"</body>\",\n",
" \"</html>\",\n",
"])\n", "])\n",
"\n", "SR=re.compile(r'src=\"(assets/[^\"]+)\"'); mimetypes.init()\n",
"# ============================================================\n", "def em(m):\n",
"# Embed images as base64\n", " r=m.group(1); ip=(B/r).resolve()\n",
"# ============================================================\n", " if not ip.exists(): return m.group(0)\n",
"\n", " mt,_=mimetypes.guess_type(str(ip))\n",
"print(\"\\n Embedding images...\")\n", " if not mt: mt='image/png'\n",
"SRC_RE = re.compile(r'src=\"(\\.\\./assets/[^\"]+)\"')\n", " with open(ip,'rb') as f: b64=base64.b64encode(f.read()).decode()\n",
"mimetypes.init()\n", " return f'src=\"data:{mt};base64,{b64}\"'\n",
"\n", "FULL=SR.sub(em,FULL)\n",
"def embed_image(match):\n", "out=B/'output'; out.mkdir(exist_ok=True)\n",
" rel_path = match.group(1)\n", "hp=out/f'{TITLE}_Data_Sheet.html'; hp.write_text(FULL,encoding='utf-8')\n",
" img_path = (BASE / \"chapters\" / rel_path).resolve()\n", "print(f'\\n {hp.name} ({hp.stat().st_size/1024:.0f} KB) OK')\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"
] ]
}, },
{ {
"cell_type": "markdown", "cell_type": "markdown",
"metadata": {}, "metadata": {},
"source": [ "source": [
"## Done\n", "## Done",
"\n报告在 `output/` 目录,可直接发送给他人。" "\n\nSelf-contained HTML in `output/`."
] ]
} }
], ],

View File

@ -2,18 +2,18 @@
## 主要特点 ## 主要特点
* DAC 更新率最高 12 GSPS * DAC 更新率最高 12 GSPS
* ADC 采样率最高 6 GSPS * ADC 采样率最高 6 GSPS
* AWG 支持直接/调制波形输出 * AWG 支持直接/调制波形输出
* AWG 支持标记脉冲输出 * AWG 支持标记脉冲输出
* DAQ 支持多种数据采集模式 * DAQ 支持多种数据采集模式
* 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%}

View File

@ -1,19 +1,73 @@
# 规格参数 # 规格参数
## 数字接口规格 ## 电气特性
| 规格项 | 最小值 | 典型值 | 测试条件DIG_VDD = 1.0 VIO_VDD = 1.8 VAVDD = 1.0 VAVDD18 = 1.8 VTA = 25°C除非另有说明。
|:-|:-|:-|
| SPI 速率 | 50 Mbps | — |
| LVDS 速率 | 750 Mbps | — |
## SPI 时序参数 ### 数字接口
| 标识 | 定义 | 时序 (ns) | | 参数 | 最小值 | 典型值 | 最大值 | 单位 |
|:-|:-|:-| |:-|:-|:-|:-|:-|
| Tc2s | SPI 帧开始时CSN 下降沿到 SCLK 下降沿的时间 | >3 | | SPI 时钟频率 | 50 | — | — | Mbps |
| Tms | MOSI 数据的建立时间 | >3 | | LVDS 数据速率 | 750 | — | — | Mbps |
| Tmh | MOSI 数据的保持时间 | >3 |
| Tss | MISO 数据的建立时间 | >3 | ### 电源
| Tsh | MISO 数据的保持时间 | >3 |
| Tc2h | SPI 帧结束时SCLK 上升沿到 CSN 上升沿的时间 | >3 | | 供电域 | 标称电压 | 最大电流 | 单位 |
|:-|:-|:-|:-|
| 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 |
<div class="note">
以上电流值为各供电域最低过流要求来源pin_name.csv。VREF / PLL_VREF520 / ADC_VR850 / ADC_VR350 等基准电压端口电流可忽略(< 1 mA
</div>
## 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 |
<div class="note">
带 "—" 的参数待补充实测值。
</div>

View File

@ -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 | | DIG_VDD | 0.3 | 1.2 | V |
| DAC 模拟 1.8V 供电电压 | DAC_AVDD18 | 1.8 | V | pin_name.csv | | IO_VDD | 0.3 | 2.0 | V |
| DAC 数字 1.0V 供电电压 | DAC_DVDD | 1.0 | V | pin_name.csv | | DAC_AVDD18 | 0.3 | 2.0 | V |
| PLL 模拟 1.0V 供电电压 | PLL_VDD, VCO_VDD | 1.0 | V | pin_name.csv | | PLL_VDD18 | 0.3 | 2.0 | V |
| PLL 模拟 1.8V 供电电压 | PLL_VDD18 | 1.8 | V | pin_name.csv | | ADC_VDD18 | 0.3 | 2.0 | V |
| PLL 数字 1.0V 供电电压 | PLL_DVDD | 1.0 | V | pin_name.csv | | AVDD_ENCODER, AVDD_P2S | 0.3 | 1.2 | V |
| ADC 模拟 1.0V 供电电压 | ADC_VDD | 1.0 | V | pin_name.csv | | DAC_DVDD, PLL_DVDD, ADC_DVDD | 0.3 | 1.2 | V |
| ADC 模拟 1.8V 供电电压 | ADC_VDD18 | 1.8 | V | pin_name.csv | | PLL_VDD, VCO_VDD, ADC_VDD | 0.3 | 1.2 | V |
| ADC 数字 1.0V 供电电压 | ADC_DVDD | 1.0 | V | pin_name.csv | | 数字 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 |
<div class="note"> <div class="note">
所有电压值均以对应地平面DGND 或 AGND为参考。操作最大值的绝对值最小/最大极限电压、结温和存储温度范围、ESD 防护等级等参数待补充。 以上极限值为设计参考值。实测极限值待芯片表征完成后更新。
</div>
## ESD 防护等级
| 模型 | 等级 | 单位 |
|:-|:-|:-|
| HBM人体模型 | — | V |
| CDM充电器件模型 | — | V |
<div class="note">
ESD 防护等级待补充。
</div>
## 热阻特性
| 参数 | 符号 | 典型值 | 单位 |
|:-|:-|:-|:-|
| 结到环境热阻 | θJA | — | °C/W |
| 结到壳热阻 | θJC | — | °C/W |
| 结到板热阻 | θJB | — | °C/W |
<div class="note">
热阻参数待封装设计完成后补充。
</div>
## 推荐工作条件
| 参数 | 符号 | 标称值 | 单位 |
|:-|:-|:-|:-|
| 数字内核供电 | 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 |
<div class="note">
所有电压以对应地平面DGND 或 AGND为参考。
</div> </div>

View File

@ -1,24 +1,25 @@
# 管脚描述 # 管脚配置与功能描述
## 管脚顶视图 ## 管脚顶视图
芯片配置,顶视图:
![pin_map](../assets/pin_map.png) ![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.0 | mm |
* 基板大小1.4 cm × 1.4 cm | 焊球直径 | 0.5 | mm |
| 焊球数量 | 13 × 13 = 169 | 个 |
| 基板尺寸 | 14 × 14 | mm |
| 封装类型 | CSP-BGA | — |

View File

@ -1,163 +1,173 @@
# 典型性能特 # 典型性能特
除非特殊说明以下性能指标的测试条件为DIG_VDD = 1.0VIO_VDD = 1.8VAVDD = 1.0VAVDD18 = 1.8VT = 25℃ 除非另有说明以下性能指标的测试条件为DIG_VDD = 1.0 VIO_VDD = 1.8 VAVDD = 1.0 VAVDD18 = 1.8 VTA = 25°C。满载条件定义为满占空比、功能全开AWG + DAQ + MIX + 插值 + NCO + 解模)
## 电源特性 ## 电源特性
### 标称电压与最低过流要求 ### 电流消耗
| 供电域 | 标称电压 | 最低过流要求 | 单位 | | 供电域 | 标称电压 (V) | 空闲电流 (mA) | 满载 12G 电流 (mA) | 单位 |
|:-|:-|:-|:-| |:-|:-|:-|:-|:-|
| DIG_VDD | 1.0 | 2000 | mA | | DIG_VDD | 1.0 | 26.0 | 539.0 | mA |
| IO_VDD | 1.8 | 1000 | mA | | IO_VDD | 1.8 | 8.0 | 7.0 | mA |
| AVDD_ENCODER, AVDD_P2S | 1.0 | 400 + 150 | mA | | AVDD_ENCODER | 1.0 | 19.2 | 251.0 | mA |
| DAC_AVDD18 | 1.8 | 120 | mA | | AVDD_P2S | 1.0 | 168.7 | 227.3 | mA |
| DAC_DVDD | 1.0 | 50 | mA | | DAC_AVDD18 | 1.8 | ≅207.8 | ≅206.0 | mA |
| PLL_VDD18 | 1.8 | 50 | mA | | DAC_DVDD | 1.0 | 8.0 | 33.0 | mA |
| PLL_VDD, VCO_VDD | 1.0 | 50 + 50 | mA | | PLL_VDD18 | 1.8 | 包含在 +1.8V 模拟轨 | 包含在 +1.8V 模拟轨 | mA |
| PLL_DVDD | 1.0 | 50 | mA | | PLL_VDD, VCO_VDD | 1.0 | 26.0 | 144.6 | mA |
| ADC_VDD | 1.0 | 400 | mA | | PLL_DVDD | 1.0 | — | — | mA |
| ADC_VDD18 | 1.8 | 200 | mA | | ADC_VDD | 1.0 | 71.0 | 164.7 | mA |
| ADC_DVDD | 1.0 | 100 | mA | | ADC_VDD18 | 1.8 | 包含在 +1.8V 模拟轨 | 包含在 +1.8V 模拟轨 | mA |
| ADC_DVDD | 1.0 | — | — | mA |
| **总计** | — | **531.7** | **1569.6** | mA |
### 实测电流分布 数据来源:电流测试.xlsx。
(来源:电流测试.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 |
<div class="note"> <div class="note">
+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 电流随时钟频率变化显著,是总功耗的主要变量。
</div>
### 功耗
| 工作状态 | 总功耗 (W) | 条件 |
|:-|:-|:-|
| 空闲(复位前) | — | 时钟未配置 |
| 待机(复位后) | — | 时钟使能,无激励/采集 |
| 满载 6G | — | DAC_CLK = 6 GHz |
| 满载 10G | — | DAC_CLK = 10 GHz |
| 满载 12G | — | DAC_CLK = 12 GHz |
<div class="note">
功耗数据待根据实测电流和电压计算后填写。
</div> </div>
## 时钟与 NCO 特性 ## 时钟与 NCO 特性
| 参数 | 条件 | 来源 | | 参数 | 符号 | 最小值 | 典型值 | 最大值 | 单位 |
|:-|:-|:-| |:-|:-|:-|:-|:-|:-|
| DAC 更新时钟 (DAC_CLK) | 内部 PLL 倍频,最高 12 GHz | 宣传页 | | DAC 更新时钟频率 | fDAC | — | — | 12 | GHz |
| ADC 采样时钟 (ADC_CLK) | 内部 PLL 倍频,最高 6 GHz | 宣传页 | | ADC 采样时钟频率 | fADC | — | — | 6 | GHz |
| NCO 频率范围 | 测试覆盖 100 MHz ~ 2400 MHz | tc_awg_nco_freq | | NCO 频率范围 | fNCO | 100 | — | 2400 | MHz |
| NCO 基频 (fs/8) | 750 MHz | ChipConfig.py | | NCO 基频 (fs/8) | — | — | 750 | — | MHz |
| NCO 频率分辨率 | — | — | — | — | Hz |
| 参考时钟频率范围 | fREF | 50 | 100 | 250 | MHz |
| PLL 锁定时间 | tLOCK | — | — | — | μs |
| 时钟抖动RMS | tJITTER | — | — | — | ps |
NCO 频率扫频测试结果4002400 MHz NCO 频率扫频测试结果4002400 MHz
![nco_freq_sweep](../assets/nco_freq_sweep.png) ![nco_freq_sweep](../assets/nco_freq_sweep.png)
<div class="note">
参考时钟频率范围、VCO 输出频率、PLL 锁定时间、时钟抖动等参数待补充。
</div>
## DAC 特性 ## 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 |
| SFDR1.3 GHz 输出) | SFDR | — | — | — | dBc |
| NSD1.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) | | NRZ 直出 | ![dac_1300M](../assets/dac_1300M.jpg) |
| 混频模式 | ![dac_mix](../assets/dac_1300M_mix.jpg) | | 混频 (MIX) | ![dac_mix](../assets/dac_1300M_mix.jpg) |
| 半带混频模式 | ![dac_mix_hb](../assets/dac_1300M_mix_hb.jpg) | | 半带混频 (MIX-HB) | ![dac_mix_hb](../assets/dac_1300M_mix_hb.jpg) |
DAC 基带输出扫频特性(混频+半带模式3002300 MHz ### 扫频特性
DAC 基带输出扫频(混频 + 半带模式3002300 MHz
![dac_freq_sweep](../assets/dac_freq_sweep.png) ![dac_freq_sweep](../assets/dac_freq_sweep.png)
<div class="note">
SFDR、NSD、相位噪声、DNL、INL、输出共模电压等参数待补充。
</div>
## ADC 特性 ## 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 采集波形示例(满量程正弦波输入):
![adc_full](../assets/adc_full.png) ![adc_full](../assets/adc_full.png){w=48%}
![adc_sample](../assets/adc_sample0.png){w=48%}
![adc_sample](../assets/adc_sample0.png)
<div class="note">
分辨率、SNR、SINAD、ENOB、SFDR、输入带宽、输入范围、输入阻抗等参数待补充。
</div>
## 数字接口特性 ## 数字接口特性
| 参数 | 条件 | 值 | 来源 | | 参数 | 符号 | 最小值 | 典型值 | 最大值 | 单位 |
|:-|:-|:-|:-| |:-|:-|:-|:-|:-|:-|
| SPI 速率 | 最低 | 50 Mbps | specification.md | | SPI 时钟频率 | fSCLK | 50 | — | — | Mbps |
| LVDS 速率 | 最低 | 750 Mbps | specification.md | | LVDS 数据速率 | fLVDS | 750 | — | — | Mbps |
| LVDS 发送延迟 | — | 85 ns | tc_lvds_delay | | LVDS 发送延迟 | tTX | — | 85 | — | ns |
| LVDS 接收延迟 | — | 75 ns | tc_lvds_delay | | LVDS 接收延迟 | tRX | — | 75 | — | ns |
| SYNC_IN 最小脉宽 | tSYNC | — | — | — | ns |
SPI 突发传输时钟数量测试50 Mbps | RST_N 最小脉宽 | tRST | — | — | — | ns |
![spi_50mbps](../assets/spi_50mbps.png) ![spi_50mbps](../assets/spi_50mbps.png)
SPI 突发传输时钟数量测试25 Mbps
![spi_25mbps](../assets/spi_25mbps.png) ![spi_25mbps](../assets/spi_25mbps.png)
LVDS 数据传输速率测试:
![lvds_speed](../assets/lvds_speed.png) ![lvds_speed](../assets/lvds_speed.png)
<div class="note"> ## 数字处理能力
SPI 时序参数详见 [规格参数](#规格参数)。SYNC_IN 最小脉宽、RST_N 最小脉宽等参数待补充。
</div>
## 数字性能特性 | 参数 | 规格 | 来源 |
|:-|:-|:-|
| 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 |
<div class="note"> | 参数 | 典型值 | 单位 |
死时间、Pump 脉宽范围、NCO 相位分辨率等参数待补充。 |:-|:-|:-|
</div> | 测温范围 | — | °C |
| 分辨率 | — | °C |
## 温度特性 | 精度 | — | °C |
片上温度传感器监视结果示例:
![temp_monitor](../assets/temp_monitor.png) ![temp_monitor](../assets/temp_monitor.png)
<div class="note"> ## 中断与异常
温度传感器精度、分辨率等参数待补充。
</div>
## 中断与异常处理 ### 中断向量表
### 中断源列表
| 中断源 | 触发条件 | 说明 | | 中断源 | 触发条件 | 说明 |
|:-|:-|:-| |:-|:-|:-|
| EXIT_IR | MCU 执行退出指令 | MCU 程序正常退出时触发表明任务执行完毕 | | EXIT_IR | MCU 执行退出指令 | 正常退出,任务执行完毕 |
| FORCE_AWG | 外部强制 AWG 请求 | 外部控制器通过写寄存器强制 AWG 模块进入特定状态 | | ILLEGAL_ONE | 指令字为 0xFFFFFFFF | 指令内存异常,可能未编程 |
| FORCE_DAQ | 外部强制 DAQ 请求 | 外部控制器通过写寄存器强制 DAQ 模块进入特定状态 | | ILLEGAL_ZERO | 指令字为 0x00000000 | 指令内存异常,常见于空白区域 |
| ILLEGAL_ONE | 非法指令(数据为全 1 | MCU 执行到全 1 指令字 (0xFFFFFFFF) 时触发,表明指令内存异常 | | UNALIGN_SW | 16 位访问地址未对齐 | 检查 load/store 指令对齐 |
| ILLEGAL_ZERO | 非法指令(数据为全 0 | MCU 执行到全 0 指令字时触发,常见于未编程的指令空间 | | UNALIGN_LW | 32 位访问地址未对齐 | 检查 load/store 指令对齐 |
| UNALIGN_SW | 短字非对齐访问 | 16 位数据访问地址未对齐时触发 | | LINK_DOWN | LVDS 链路同步丢失 | 接收端返回未同步状态 |
| UNALIGN_LW | 长字非对齐访问 | 32 位数据访问地址未对齐时触发 | | FORCE_AWG | 外部强制 AWG 请求 | 寄存器触发 |
| LINK_DOWN | LVDS 链路断开 | LVDS 数据接收链路同步丢失时触发,接收端重新进入未同步状态 | | FORCE_DAQ | 外部强制 DAQ 请求 | 寄存器触发 |
### 典型异常波形 ### 典型异常波形

View File

@ -1,35 +1,46 @@
# 专业术语 # 术语与缩写
| 术语 | 全称 | 说明 | | 术语 | 全称 | 说明 |
|:-|:-|:-| |:-|:-|:-|
| RBPU | Readout Baseband Processing Unit | 读出基带处理单元 | | **ADC** | Analog-to-Digital Converter | 模数转换器 |
| SoC | System on Chip | 片上系统 | | **AWG** | Arbitrary Waveform Generator | 任意波形发生器,产生读出激励波形 |
| AWG | Arbitrary Waveform Generator | 任意波形发生器,用于产生读出激励波形 | | **BGA** | Ball Grid Array | 球栅阵列封装 |
| DAQ | Data Acquisition | 数据采集模块,用于采集和处理回波信号 | | **CDM** | Charged Device Model | 充电器件模型ESD 测试) |
| DAC | Digital-to-Analog Converter | 数模转换器 | | **CRC** | Cyclic Redundancy Check | 循环冗余校验 |
| ADC | Analog-to-Digital Converter | 模数转换器 | | **CSP** | Chip Scale Package | 芯片级封装 |
| PLL | Phase-Locked Loop | 锁相环,片上时钟管理 | | **DAC** | Digital-to-Analog Converter | 数模转换器 |
| NCO | Numerically Controlled Oscillator | 数控振荡器,用于频率合成 | | **DAQ** | Data Acquisition | 数据采集模块,采集和处理回波信号 |
| DSP | Digital Signal Processing | 数字信号处理 | | **DNL** | Differential Nonlinearity | 微分非线性 |
| LVDS | Low-Voltage Differential Signaling | 低电压差分信号 | | **DSP** | Digital Signal Processing | 数字信号处理 |
| SPI | Serial Peripheral Interface | 串行外设接口 | | **ENOB** | Effective Number of Bits | 有效位数 |
| MCU | Micro Controller Unit | 微控制器单元AWG/DAQ 内部控制核心 | | **ESD** | Electrostatic Discharge | 静电放电 |
| PlsGen | Pulse Generator | 脉冲产生器,用于产生 Pump 使能脉冲 | | **FIFO** | First In, First Out | 先入先出缓冲器 |
| Pump | — | 外部泵浦通道,用于上变频基带信号 | | **FM** | Frequency Modulation | 频率调制 |
| POR | Power-On Reset | 上电复位 | | **FPGA** | Field-Programmable Gate Array | 现场可编程门阵列 |
| CRC | Cyclic Redundancy Check | 循环冗余校验 | | **HB** | Half-Band | 半带滤波器DAC 插值滤波器 |
| FM | Frequency Modulation | 频率调制 | | **HBM** | Human Body Model | 人体模型ESD 测试) |
| PM | Phase Modulation | 相位调制 | | **INL** | Integral Nonlinearity | 积分非线性 |
| AM | Amplitude Modulation | 幅度调制 | | **LVDS** | Low-Voltage Differential Signaling | 低电压差分信号 |
| HB | Half-Band | 半带滤波器DAC 插值滤波器 | | **MCU** | Micro Controller Unit | 微控制器AWG/DAQ 内部控制核心 |
| SFDR | Spurious-Free Dynamic Range | 无杂散动态范围 | | **MIX** | Mixer / Mix Mode | 混频器 / 混频输出模式 |
| NSD | Noise Spectral Density | 噪声谱密度 | | **MSL** | Moisture Sensitivity Level | 湿度敏感等级 |
| SNR | Signal-to-Noise Ratio | 信噪比 | | **NCO** | Numerically Controlled Oscillator | 数控振荡器,用于频率合成 |
| ENOB | Effective Number of Bits | 有效位数 | | **NRZ** | Non-Return-to-Zero | 不归零编码 / DAC 直出模式 |
| DNL | Differential Nonlinearity | 微分非线性 | | **NSD** | Noise Spectral Density | 噪声谱密度 |
| INL | Integral Nonlinearity | 积分非线性 | | **PCB** | Printed Circuit Board | 印刷电路板 |
| FCW | Frequency Control Word | 频率控制字 | | **PLL** | Phase-Locked Loop | 锁相环,片上时钟管理 |
| PCW | Phase Control Word | 相位控制字 | | **PlsGen** | Pulse Generator | 脉冲产生器,产生 Pump 使能脉冲 |
| Qubit | Quantum Bit | 量子比特 | | **PM** | Phase Modulation | 相位调制 |
| RO | Readout Output | 读出激励输出通道 | | **POR** | Power-On Reset | 上电复位 |
| RI | Readout Input | 读出回波输入通道 | | **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 | 压控振荡器 |

View File

@ -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) ![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 | 管理芯片控制状态,支撑运维和调试 |
<div class="important"> <div class="important">
读出芯片不仅支持以 ASIC 芯片模式工作,也支持部分核心控制在 FPGA 内部工作。这两种工作模式受制于 ASIC 和 FPGA 资源以及外部 ADC、DAC 硬件性能不同,部分功能实现存在差异,后续阅读时需要重点关注。 读出芯片支持 ASIC 模式和 FPGA 模式两种工作方式。两种模式受限于资源及外部 ADC/DAC 硬件性能差异,部分功能实现存在不同,阅读时需注意区分
</div> </div>
## 关键数据路径 ## 关键数据路径
### 读出激励输出通道 ### 读出激励输出通道
<!-- MERGED from ../readout_program/ri_datapath.md --> 读出激励输出通道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` | MCU 指令 (MCU_INS) | `0x700000` | MCU 执行的指令流 |
4. **波形映射表 (AWG_IDX)**:码字 → 波形段的映射查找表,位于地址空间 `0xA00000` | MCU 数据 (MCU_DAT) | `0x800000` | 指令引用的初始数据 |
5. **波形数据存储 (AWG_WVE)**:实际输出的波形采样点,位于地址空间 `0xB00000` | 控制寄存器 (AWG_REG) | `0x900000` | AWG 工作模式与参数 |
6. **模拟配置 (SYS_ANA)**DAC 输出模式NRZ/MIX/HBMIX与偏置校准 | 波形映射表 (AWG_IDX) | `0xA00000` | 码字 → 波形段映射 |
| 波形数据 (AWG_WVE) | `0xB00000` | 波形采样点存储 |
| 模拟配置 (SYS_ANA) | — | DAC 输出模式与偏置校准 |
波形输出流程如下: #### 波形输出流程
1. MCU 每 3 个时钟周期执行一条指令,发出控制码字 1. MCU 每 3 个时钟周期执行一条指令,发出控制码字
2. 码字中的 `WAVE_ID` 字段通过查找表映射为波形段的起始地址和长度 2. `WAVE_ID` 字段经查找表映射为波形段起始地址和长度
3. 波形数据从 WVE 存储中读取,经希尔伯特变换生成 I/Q 两路 3. 波形数据经希尔伯特变换生成 I/Q 两路
4. I/Q 信号与 NCO 混频,实现频率搬移 4. I/Q 信号与 NCO 混频,实现频率搬移
5. 经数字调幅、半带插值滤波 5. 经数字调幅、半带插值滤波
6. 输出至 DAC 转换为模拟信号 6. 输出至 DAC 转换为模拟信号
AWG 支持以下输出模式: #### 输出模式
| 模式 | 描述 | 特点 | | 模式 | 频率范围 | 应用场景 |
|:-|:-|:-| |:-|:-|:-|
| **直出模式 (NRZ)** | 基带波形直接输出 | 输出频率 DC3 GHz适用于直接产生多音基带信号 | | **NRZ 直出** | DC3 GHz | 多音基带信号,多量子比特并行读出 |
| **调制模式 (MIX)** | 基带波形与 NCO 混频 | 输出频率 0.32.4 GHz通过 NCO 频率控制精确频点定位 | | **MIX 混频** | 0.32.4 GHz | NCO 精确频点控制,腔频扫描 |
| **NCO Only 模式** | 仅 NCO 连续波形 | 方便连接外部仪器进行芯片测试 | | **NCO Only** | — | 芯片测试,连续波形输出 |
| **半带调制模式 (HBMIX)** | 混频 + 半带插值 | 提升高频输出信号质量 | | **HBMIX 半带混频** | 高频段 | 启用半带插值,提升高频信号质量 |
<div class="note"> **射频直出**NRZ 模式下利用高阶奈奎斯特区可实现 67 GHz 射频直出,功率平坦度约 2 dB受 sinc 滚降限制)。
一般情况下,直出模式用于量子实验,通过直接输出包含多个读出频率的波形,可以实现对多个量子比特的并行读出;调制模式用于腔频扫描等应用,通过实时修改 NCO 频率实现扫频功能NCO Only 模式可以输出连续波形,方便连接外部仪器上进行测试。
</div>
**射频直出**:在 NRZ 模式下利用高阶奈奎斯特区输出,可以实现 67 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复位后需重新编程。
### 读出回波信号处理 ### 读出回波信号处理
<!-- MERGED from ../readout_program/ro_datapath.md --> 读出回波输入通道RI 通道)由 ADC 和 DAQ 模块构成,采集并处理反射基带信号。
读出回波输入通道RI 通道)由 ADC + DAQ 模块构成,负责采集和处理反射回来的基带信号。 #### 模拟前端
**模拟信号路径**RF 回波信号经外部混频器下变频为基带 → 巴伦差分转换 → ADC 采样FPGA 平台 8 位 @4 GSPSASIC 平台 8 位 @6 GSPS RF 回波信号 → 外部混频器下变频为基带 → 巴伦差分转换 → ADC 采样。
FPGA 平台8 位 @ 4 GSPSASIC 平台8 位 @ 6 GSPS。
DAQ 模块需要编程五类数据: #### 编程资源
1. **控制器指令 (MCU_INS)**DAQ 内部 MCU 的执行指令流,位于地址空间 `0x200000` DAQ 模块需编程五类数据资源:
2. **控制器数据 (MCU_DAT)**MCU 需要引用的初始数据,位于地址空间 `0x300000`
3. **控制寄存器 (DAQ_REG)**:配置 DAQ 工作模式与参数,位于地址空间 `0x400000`
4. **读出参数与滤波器权重 (DAQ_PAR + DAQ_FLT)**:解模参数、匹配滤波器系数,位于地址空间 `0x500000``0x580000`
5. **模拟配置 (SYS_ANA)**ADC 参考电压选择
数据处理流程: | 资源 | 地址空间 | 说明 |
1. ADC 采集原始波形数据
2. 数字解模:原始波形与匹配滤波器进行相关运算,得到 I/Q 复数数据
3. 态判定:根据 I/Q 数据判定量子态0 态/1 态/2 态/未定义)
4. 态统计:对多次测量的态结果进行计数统计
5. 结果输出:通过 `RSLT_PUSH` LVDS 接口发送数据
DAQ 支持多种数据采集模式:
| 模式 | 输出数据量 | 适用场景 |
|:-|:-|:-| |:-|:-|:-|
| 原始波形 | 最大 | 调试、原始数据分析 | | MCU 指令 (MCU_INS) | `0x200000` | DAQ MCU 执行指令流 |
| IQ 数据 | 中等 (64 bit × N) | 解模结果分析 | | MCU 数据 (MCU_DAT) | `0x300000` | 指令引用的初始数据 |
| 态数据 | 小 (2 bit × N) | 高速态读出 | | 控制寄存器 (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 来配置内部寄存器和内存SPI 采用四线模式。时钟极性 CPOL = 1即时钟在空闲时处于高电平状态时钟相位 CPHA = 1即在第一个时钟沿输出数据第二个时钟沿采样数据。SPI 的通信数据格式采用自定义协议,支持流模式发送接收。 SPI 采用四线模式CPOL = 1空闲高电平CPHA = 1首沿输出、次沿采样
![spi_prot](../assets/spi_prot.png) ![spi_prot](../assets/spi_prot.png)
**SPI 帧格式:** **帧格式:**
| 字段 | RW | ADDR | CHIP_ID | RSV | DATA | | 字段 | RW | ADDR | CHIP_ID | RSV | DATA |
|:-:|:-:|:-:|:-:|:-:|:-:| |:-:|:-:|:-:|:-:|:-:|:-:|
| 位宽 | 1 | 25 | 5 | 1 | 32 × N | | 位宽 | 1 | 25 | 5 | 1 | 32 × N |
| 含义 | 0: 读, 1: 写 | 32 MB 寻址空间 | 总线片选 | 保留位 | 数据 | | 说明 | 0: 读, 1: 写 | 32 MB 地址空间 | 片选 ID | 保留 | 数据 (N ≥ 1) |
<div class="note"> 协议以流模式传输:先发送 1 bit 读写标志 + 25 bit 地址 + 5 bit 片选 ID之后发送/接收 N × 32 bit 数据,直至主控端拉低 CSN 结束通信。每完成 32 bit 后地址自动递增 4 字节。
协议首先发送 1 比特读写标识位,然后发送 25 比特地址位,接着再发送 5 比特的芯片选择信号,最后再发送/接收数据。数据发送/接收以 32 比特为单位,以流模式发送,支持发送/接收 N × 32 位的数据,直到主控端将 CSN 拉低后结束通信N ≥ 1。流模式要求从设备在完成 32 比特数据接收/发送后自动对地址进行 4 字节递增。
</div>
### LVDS 通信协议 ### 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 MSB LSB
``` ```
帧头包含 4 比特数据,其中最高位比特用于指示是否启动数据传输,次高位置 0 保留最低两位用于指示本次传输的数据量0: 16 比特1: 32 比特2: 64 比特3: 128 比特)。载荷后面附带 8 比特 CRC 校验码,校验多项式为: HEAD 字段定义:
| Bit | 说明 |
|:-|:-|
| [3] | 启动标志1 = 启动传输) |
| [2] | 保留(置 0 |
| [1:0] | 载荷长度0 = 16 bit1 = 32 bit2 = 64 bit3 = 128 bit |
CRC8 多项式:
$$x^8 + x^2 + x + 1$$ $$x^8 + x^2 + x + 1$$
![lvds_prot](../assets/lvds_prot.png) ![lvds_prot](../assets/lvds_prot.png)
**同步机制**:接收端在接收数据之前必须先和发送端同步。发送端通过发送同步码来同步接收端,当完成设定数量的同步码发送完毕后发送一个结束码结束同步过程。同步码为字符串 "hfnl" 对应的 32 比特数据,结束码为 "exit" 对应的 32 比特数据。 **同步机制**:接收端必须先与发送端同步方可接收数据。发送端发送同步码 `hfnl`32 bit达到设定次数后发送结束码 `exit`32 bit。接收端状态转移
接收端在启动时被初始化到未同步状态,此时必须接收到足够次数的同步码之后才能进入确认状态,直到接收到结束码之后发送端和接收端同时进入就绪状态。接收端后续如果在接收数据过程中发现 CRC8 校验码出错,会再次进入未同步状态。 * 初始 → **未同步**:等待同步码
* **未同步****确认**:收到足够次数的同步码
* **确认****就绪**:收到结束码
* **就绪****未同步**:数据接收中 CRC8 校验错误
**空闲状态**:当处于空闲状态不用传输数据时,发送端将 LVDS 信号电平拉高。当传输数据时,发送端将 LVDS 电平拉低两个数据周期启动数据发送。 **空闲**:无数据传输时 LVDS 电平拉高;发送数据时拉低两个数据周期后开始发送。

View File

@ -1,161 +1,220 @@
# 应用注意 # 应用信息
## 模拟接口 ## 模拟接口
### DAC 输出接口 ### DAC 输出接口
DAC 输出 012 GHz 信号,内部为 50 Ω 电阻网络,外部差分 270 nH 电感到地,然后经巴伦差分转单端,注意传输线阻抗匹配 DAC 输出频率范围 DC12 GHz内部集成 50 Ω 电阻网络。外部需接差分 270 nH 电感到地经巴伦差分转单端输出。PCB 走线注意阻抗匹配50 Ω 差分)
DAC 支持三种输出模式: ```schemdraw
import schemdraw
from schemdraw import elements as e
* **NRZ 直出模式**:基带波形直接输出,输出频率范围 DC3 GHz。适用于直接产生多音基带信号。 with schemdraw.Drawing(show=False) as d:
* **混频模式 (MIX)**:基带波形与 NCO 混频后输出,输出频率范围 0.32.4 GHz。通过 NCO 频率控制可实现精确的频点定位。 d.config(fontsize=11)
* **半带混频模式 (MIX-HB)**:在混频模式基础上启用半带插值滤波器,提升输出信号质量,适用于高频输出场景。 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 直出 | DC3 GHz | 基带波形直接输出,适合多音基带信号 |
| MIX 混频 | 0.32.4 GHz | 基带与 NCO 混频,精确频点定位 |
| MIX-HB 半带混频 | 高频段 | 混频 + 半带插值,提升高频信号质量 |
频谱参考见 [DAC 特性](#dac-特性)。
### ADC 输入接口 ### ADC 输入接口
ADC 采用差分模拟输入包含两组独立输入通道ADC_VINP1/ADC_VINN1, ADC_VINP2/ADC_VINN2支持最高 6 GSPS 采样率。 ADC 采用差分模拟输入,两组独立通道。
* 满量程输入范围:差分 0.7 Vpp | 参数 | 规格 | 单位 |
* 输入共模电压0.5 V |:-|:-|:-|
* 输入阻抗差分100 Ω | 满量程输入范围(差分) | 0.7 | Vpp |
* 输入带宽 (3 dB)3 GHz | 输入共模电压 | 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 耦合。参考时钟频率范围 50250 MHz典型输入为 100 MHz 或 125 MHz 低相噪时钟源。时钟信号经内部 PLL 倍频,产生 DAC 更新时钟612 GHz、ADC 采样时钟36 GHz和数字主时钟250500 MHz 参考时钟通过 CLK_REF_P / CLK_REF_N 差分输入AC 耦合。
PLL 及 NCO 频率特性参考见 [时钟与 NCO 特性](#时钟与-nco-特性)。 | 参数 | 规格 | 单位 |
|:-|:-|:-|
| 频率范围 | 50250 | MHz |
| 典型频率 | 100 或 125 | MHz |
| 要求 | 低相噪时钟源 | — |
内部 PLL 倍频分配:
| 输出 | 频率范围 | 用途 |
|:-|:-|:-|
| DAC_CLK | 612 GHz | DAC 更新时钟 |
| ADC_CLK | 36 GHz | ADC 采样时钟 |
| DIG_CLK | 250500 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 滤波电容 |
<div class="note"> <div class="note">
环路滤波器推荐参数(电荷泵电流、环路带宽、阻容值等)待补充。 环路滤波器参数(电荷泵电流、环路带宽、具体阻容值)待 PLL 设计确认后补充。
</div> </div>
### 基准电压输入 ### 基准电压与偏置
此端口的外接电阻决定了基准电流源的大小。其中ADC_VBIAS_IREF_RES 端口要求外接电阻 5.2 kΩ 到地,并联 10 μF 去耦电容。DAC_VBIAS_IREF_RES 端口要求外接电阻 5.2 kΩ 到地,并联 10 μF 去耦电容。
各基准电压端口要求如下:
| 端口 | 电压/配置 | 说明 | | 端口 | 电压/配置 | 说明 |
|:-|:-|:-| |:-|:-|:-|
| ADC_VR850 | 850 mV | ADC 外部参考电压,需去耦 | | ADC_VR850 | 850 mV | ADC 外部参考,需去耦 |
| ADC_VR350 | 350 mV | ADC 外部参考电压,需去耦 | | ADC_VR350 | 350 mV | ADC 外部参考,需去耦 |
| ADC_REF_SENSE | 1.8V / 0.52V | ADC 带隙基准内外选择 | | ADC_REF_SENSE | 1.8 V / 0.52 V | ADC 带隙基准选择 |
| PLL_VREF520 | 520 mV | PLL 外部参考电压,需去耦 | | PLL_VREF520 | 520 mV | PLL 外部参考,需去耦 |
| VREF500IN | 500 mV | DAC 外部参考电压,需去耦 | | VREF500IN | 500 mV | DAC 外部参考,需去耦 |
| DAC_VBIAS_IREF_RES | 外接 5.2 kΩ + 10 μF | DAC 偏置电流基准 | | DAC_VBIAS_IREF_RES | 5.2 kΩ + 10 μF 到地 | DAC 偏置基准电流 |
| ADC_VBIAS_IREF_RES | 外接 5.2 kΩ + 10 μF | ADC 偏置电流基准 | | ADC_VBIAS_IREF_RES | 5.2 kΩ + 10 μF 到地 | ADC 偏置基准电流 |
| BIAS_CAP | 外接 100 nF | PLL 偏置去耦 | | PLL_CP_OUT | 电荷泵输出 | 经环路滤波器接 VCTRL |
| RES_2K | 外接 2 kΩ | PLL 外接电阻 |
| PORT_BIAS_TB | 外接 10 μF + 1 μF + 0.1 μF × 3 | PLL 滤波电容 |
| 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 以及系统同步。校准过程参考附录 [运维操作手册](#运维操作手册)。 <div class="note">
上电时序波形图待补充。
</div>
在实验中读出芯片的操作遵循**数据配置**、**实验运行**和**数据采集**三个阶段。 ### 复位
| 复位类型 | 触发方式 | 影响范围 |
|:-|:-|:-|
| 上电复位 (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 |
<div class="important"> <div class="important">
若芯片出现异常,在实验配置前应该通过写寄存器来软复位芯片的状态机和寄存器默认值。数据配置用例参考附录 [历史无关配置集](#历史无关配置集)。 若芯片出现异常,实验配置前应通过写寄存器软复位芯片状态机和寄存器默认值。配置用例参考附录 [历史无关配置集](#历史无关配置集)。
</div> </div>
### 实验运行阶段 ### 实验运行阶段
读出芯片的运行通过同步触发信号来启动,从而确保多个芯片间具有确定的时序关系。此外考虑到不同类型通信固有延迟不同,同步信号在芯片内部首先经过同步延迟模块,同步延迟模块能够为 DAQ 和 AWG 分别延迟不同的时间,从而确保读出芯片的 DAQ 和 AWG 模块能够协同运行。 同步触发信号 (SYNC_IN) 启动芯片运行,确保多芯片间时序确定。同步信号经内部延迟模块为 DAQ 和 AWG 分别补偿延迟,保证协同运行。
同步信号到达 DAQ 和 AWG 模块后DAQ 和 AWG 模块内部的 MCU 分别启动运行MCU 从 0 地址加载指令运行其按照每三个时钟一个指令的速度执行MCU 根据编程指令修改控制寄存器与发出控制码字 MCU 从地址 0 启动,每 3 个时钟周期执行一条指令,通过控制码字驱动
* 对于 AWG控制码字驱动波形输出和 Pump 使能输出。 * **AWG**:波形输出 + Pump 使能
* 对于 DAQ控制码字驱动数据采集和反馈上报/下发。 * **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}$$ $$T > \frac{64 \times N}{F}$$
## 异常处理 ## 异常处理
读出芯片内部集成了中断管理和异常检测机制,通过 INT_STATUS 和 INT_MASK 寄存器进行管理。INT_STATUS 寄存器是 INT_MASK 与对应实际输入状态按位相与再与 INT_STATUS 本身按位或运算的结果,需要通过命令清零。 ### 中断管理
### 中断源 芯片通过 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 数据接收链路同步丢失时触发,接收端重新进入未同步状态 |
### 中断处理流程 ### 中断处理流程
异常发生时,建议按以下流程处理: 1. 读取 INT_STATUS确认中断源
2. 记录异常信息(类型、时间、运行状态)
3. 按中断类型处理:
1. **读取 INT_STATUS 寄存器**,确认中断源 | 中断源 | 处理措施 |
2. **记录异常信息**(中断类型、发生时间、当前运行状态) |:-|:-|
3. **根据中断类型采取对应措施** | EXIT_IR | 正常退出,可启动下一轮实验 |
- EXIT_IR正常退出无需处理可启动下一轮实验 | ILLEGAL_ONE / ILLEGAL_ZERO | 检查并重新下载 MCU 程序,软复位 MCU |
- ILLEGAL_ONE / ILLEGAL_ZERO检查 MCU 程序是否正确写入指令内存,必要时重新下载程序并软复位 MCU | UNALIGN_SW / UNALIGN_LW | 检查 load/store 指令地址对齐 |
- UNALIGN_SW / UNALIGN_LW检查 MCU 程序中的内存访问指令,确保 load/store 指令地址对齐 | LINK_DOWN | 检查 LVDS 物理连接和信号完整性,重新同步 |
- LINK_DOWN检查 LVDS 物理连接和信号完整性,重新执行 LVDS 同步流程 | FORCE_AWG / FORCE_DAQ | 按外部控制器逻辑处理 |
- FORCE_AWG / FORCE_DAQ根据外部控制器逻辑处理
4. **写 INT_STATUS 寄存器清零中断标志位** 4. 写 INT_STATUS 清零中断标志
5. **若需恢复运行**:通过软复位(写 SYNC_SOFT 寄存器)复位 MCU 状态机,重新配置后启动 5. 如需恢复运行:软复位 MCU 状态机并重新配置
### 异常预防 ### 异常预防
* MCU 程序写入后应通过回读比对验证写入正确性 * MCU 程序写入后通过回读比对验证正确性
* LVDS 数据传输前确保收发双方已完成同步 * LVDS 传输前确认收发双方已完成同步
* 避免在实验运行期间访问运维寄存器 * 实验运行期间禁止访问运维寄存器(系统/模拟/PLL 配置)
* 建议启用 INT_MASK 中需要关注的中断源,屏蔽不需要的中断源以简化处理逻辑 * 通过 INT_MASK 启用关注的中断源,屏蔽无关中断
* MCU 程序末尾务必包含退出指令 (`exit x0, x0, 0`),避免 MCU 从非法内存区域取指 * MCU 程序末尾必须包含退出指令 `exit x0, x0, 0`
### 典型异常波形
异常波形截图参考见 [中断与异常处理](#中断与异常处理)。

View File

@ -4,7 +4,7 @@
读出芯片偏移地址空间分配如下所示用户可以通过读写相应地址来控制读出芯片功能和访问芯片状态。读出芯片地址空间包含系统配置状态、模拟配置状态、DAQ 配置状态、AWG 配置状态以及 PLL 配置状态五个部分。 读出芯片偏移地址空间分配如下所示用户可以通过读写相应地址来控制读出芯片功能和访问芯片状态。读出芯片地址空间包含系统配置状态、模拟配置状态、DAQ 配置状态、AWG 配置状态以及 PLL 配置状态五个部分。
@import "data/seg_define.csv" @import "data/seg_define.csv" using render_address.py
<div class="note"> <div class="note">
@ -62,4 +62,4 @@ DAQ MCU 发出的控制码字格式32 位):
读出芯片的详细寄存器/存储定义参考 [读出芯片 IDS 表](../script/读出子系统IDS表.xls)。相应的驱动软件配置文件参考 [读出芯片 IDS 表 JSON](../data/ids.json)。 读出芯片的详细寄存器/存储定义参考 [读出芯片 IDS 表](../script/读出子系统IDS表.xls)。相应的驱动软件配置文件参考 [读出芯片 IDS 表 JSON](../data/ids.json)。
@import "data/ids.json" @import "data/ids.json" using render_registers.py

View File

@ -1,13 +1,51 @@
# 芯片尺寸 # 封装与机械尺寸
## 封装外形
![chip_size](../assets/chip_size.png) ![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 |
<div class="note">
推荐回流曲线参数待封装厂家提供后补充。
</div>
## 标识信息
| 位置 | 内容 |
|:-|:-| |:-|:-|
| 焊球中心间距 | 1 mm | | 顶面 | 芯片型号、批号、生产日期 |
| 焊球大小 | 0.5 mm | | 底面 | — |
| 焊球数量 | 13 × 13 = 169 个 |
| 基板大小 | 1.4 cm × 1.4 cm | <div class="note">
| 封装类型 | CSP-BGA | 芯片标识格式待确认后补充。
</div>
## 存储与搬运
| 参数 | 规格 | 单位 |
|:-|:-|:-|
| 存储温度范围 | 65 ~ +150 | °C |
| MSL 等级 | — | — |
| 防潮包装要求 | — | — |
<div class="note">
MSL 等级和防潮要求待封装设计确认后补充。
</div>

View File

@ -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/<tag>.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<div class="warning">'
f'{lang} render error: {e}'
f'</div>\n'
)
last_end = match.end()
result.append(text[last_end:])
return ''.join(result)

View File

@ -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<alt>.*?)\]\((?P<src>[^)]+)\)'
r'(?:\{'
r'(?:s=(?P<s>\d+%))?'
r'(?:,\s*)?'
r'(?:w=(?P<w>\d+%))?'
r'(?:,\s*h=(?P<h>\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'<img src="{src}" alt="{alt}" style="{style}">'
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)

View File

@ -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 <name> importlib loads doc_builder/render_<name>.py
calls render(filepath) returns HTML string
No 'as' default code viewer with line numbers
Each project provides its own render_<name>.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'<div class="warning">'
f'@import file not found: {import_path}'
f'</div>'
)
# .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 <name> → 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'<div class="note">'
f'Renderer script <code>doc_builder/{renderer_name}</code> not found. '
f'Showing default code view. '
f'Create this file with a <code>render(filepath)</code> function '
f'to customize rendering.'
f'</div>'
)
return note + _render_default(fp)
except Exception as e:
return (
f'<div class="warning">'
f'@import renderer error ({import_path} as {renderer_name}): {e}'
f'<pre>{traceback.format_exc()}</pre>'
f'</div>'
)
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_<name>.py script does not exist."""
pass
def _load_renderer(name, base_dir):
"""Load a renderer script from doc_builder/<name>.
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 '<div class="warning">Cannot display binary file.</div>'
from html import escape
ext = filepath.suffix.lstrip('.').upper()
html = [
f'<div class="code-block">',
f'<div class="code-header">{filepath.name} ({len(lines)} lines)</div>',
f'<pre class="code-lines">',
]
for i, line in enumerate(lines, 1):
# escape HTML but keep the line content
escaped = escape(line.rstrip('\n\r'))
html.append(
f'<span class="ln">{i:4d}</span> '
f'<span class="lc">{escaped}</span>'
)
html.append('</pre></div>')
return '\n'.join(html)

View File

@ -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 '<p><em>empty file</em></p>'
# 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 = ['<table><thead><tr>']
for c in cols:
h.append(f'<th>{escape(c)}</th>')
h.append('</tr></thead><tbody>')
for s in segs:
h.append('<tr>')
for c in cols:
h.append(f'<td>{escape(str(s.get(c, "")))}</td>')
h.append('</tr>')
h.append('</tbody></table>')
return '\n'.join(h)

36
doc_builder/render_bga.py Normal file
View File

@ -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 '<p><em>empty or malformed file</em></p>'
ncols = len(rows[0]) - 1
h = ['<table class="compact pin-grid"><thead><tr><th></th>']
for c in range(1, ncols + 1):
h.append(f'<th class="text-center">{c}</th>')
h.append('</tr></thead><tbody>')
for r in rows[1:]:
if not r:
continue
h.append(
f'<tr><th class="text-center">{escape(r[0])}</th>'
)
for c in range(1, ncols + 1):
val = r[c] if c < len(r) else ''
h.append(
f'<td class="text-center text-mono"'
f' style="font-size:6pt">{escape(val)}</td>'
)
h.append('</tr>')
h.append('</tbody></table>')
return '\n'.join(h)

View File

@ -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 '<em>任意</em>'
if v == 'NA':
return '<em>—</em>'
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': '<span class="perm-rw">RW</span>',
'RO': '<span class="perm-ro">RO</span>',
'WC': '<span class="perm-wc">WC</span>',
}
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'<h3 id="reg-{mod_name.lower()}">{mod_name}{desc}</h3>'
)
html.append(f'<p>共 {len(entries)} 个寄存器/存储段。</p>')
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'<h4 id="reg-{seg_name}">{seg_name.upper()}</h4>'
)
html.append(
f'<p class="reg-summary">'
f'<strong>偏移地址:</strong><code>{eh(addr)}</code> '
f'<strong>权限:</strong>{_perm_badge(perm)} '
f'<strong>描述:</strong>{eh(sd)}</p>'
)
if fields:
html.append(
'<table class="compact"><thead><tr>'
'<th style="width:11%">Bits</th>'
'<th style="width:18%">Field Name</th>'
'<th style="width:14%">Reset Value</th>'
'<th style="width:12%">Range</th>'
'<th>Description</th>'
'</tr></thead><tbody>'
)
for f in fields:
b = f.get('Bits', [])
html.append(
f'<tr>'
f'<td class="bits">{_bits_str(b)}</td>'
f'<td class="text-mono">{eh(f.get("FieldName", ""))}</td>'
f'<td class="reset-val">{eh(f.get("ResetValue", ""))}</td>'
f'<td>{_range_str(f.get("Range"))}</td>'
f'<td>{eh(f.get("FieldDescription", ""))}</td>'
f'</tr>'
)
html.append('</tbody></table>')
html.append('')
return '\n'.join(html)

View File

@ -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 '<div class="warning">pyyaml not installed. Run: pip install pyyaml</div>'
with open(str(filepath), encoding='utf-8') as f:
data = yaml.safe_load(f)
if not isinstance(data, dict):
return f'<pre><code>{escape(str(data))}</code></pre>'
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'<h3>{label}{len(items)} 项)</h3>')
html.append(
'<table><thead><tr>'
'<th style="width:16%">ID</th>'
'<th style="width:16%">名称</th>'
'<th>描述</th>'
'<th style="width:20%">指标</th>'
'</tr></thead><tbody>'
)
for e in items:
html.append(
f'<tr>'
f'<td class="text-mono">{escape(e["id"])}</td>'
f'<td>{escape(e["name"])}</td>'
f'<td>{escape(e["desc"])}</td>'
f'<td>{escape(e["limit"])}</td>'
f'</tr>'
)
html.append('</tbody></table>')
return '\n'.join(html)

View File

@ -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 '<p><em>empty file</em></p>'
cols = list(rows[0].keys())
h = ['<table><thead><tr>']
for c in cols:
h.append(f'<th>{escape(c)}</th>')
h.append('</tr></thead><tbody>')
for r in rows:
h.append('<tr>')
for c in cols:
h.append(f'<td>{escape(str(r.get(c, "")))}</td>')
h.append('</tr>')
h.append('</tbody></table>')
return '\n'.join(h)

View File

@ -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 <img> 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: <svg filename>' 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'<div class="warning">'
f'Schemdraw error:<pre>{result.stderr[:500] or result.stdout[:500]}</pre>'
f'</div>'
)
# 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'<figure class="img-sm">'
f'<img src="data:image/svg+xml;base64,{b64}"'
f' alt="schemdraw diagram">'
f'</figure>'
)
else:
return (
f'<div class="warning">'
f'Schemdraw ran but produced no SVG output.'
f'</div>'
)
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

View File

@ -37,72 +37,78 @@ body {
background: var(--bg); background: var(--bg);
max-width: 900px; max-width: 900px;
margin: 0 auto; margin: 0 auto;
padding: 2em 2.5em; padding: 0;
} }
/* ---------- Cover ---------- */ /* ---------- Cover ---------- */
.cover-page { .cover-page {
background: linear-gradient(160deg, var(--cover-bg) 0%, #002244 100%); width: 100%;
color: var(--cover-text); 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; text-align: center;
padding: 80px 40px; width: 100%;
margin: -2em -2.5em 3em -2.5em; padding: 0 2.5em;
border-radius: 0 0 8px 8px; box-sizing: border-box;
display: flex; }
flex-direction: column; .cover-page .cover-meta {
justify-content: center; position: absolute;
align-items: center; bottom: 40px;
min-height: 95vh; 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 { .cover-page .product-family {
font-family: var(--font-heading); font-family: var(--font-heading);
font-size: 0.9rem; font-size: 0.75rem;
letter-spacing: 0.3em; letter-spacing: 0.5em;
text-transform: uppercase; text-transform: uppercase;
opacity: 0.7; color: var(--text-muted);
margin-bottom: 40px; margin-bottom: 64px;
} }
.cover-page h1 { .cover-page h1 {
font-family: var(--font-heading); font-family: var(--font-heading);
font-size: 2.4rem; font-size: 2.4rem;
font-weight: 700; font-weight: 700;
line-height: 1.35; line-height: 1.4;
margin-bottom: 20px; margin-bottom: 20px;
color: var(--cover-text); color: var(--heading);
border: none; border: none;
padding: 0; padding: 0;
} }
.cover-page .subtitle { .cover-page .subtitle {
font-family: var(--font-heading); font-family: var(--font-heading);
font-size: 1.1rem; font-size: 1rem;
font-weight: 300; font-weight: 400;
opacity: 0.8; color: var(--text-muted);
margin-bottom: 48px; margin-bottom: 64px;
}
.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;
} }
.cover-page .feature-list { display: none; }
.cover-page .cover-meta { .cover-page .cover-meta {
margin-top: auto; position: absolute;
font-size: 0.85rem; bottom: 40px;
opacity: 0.55; font-size: 0.8rem;
padding-top: 40px; 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 ---------- */ /* ---------- Headings ---------- */
h1 { h1 {
@ -202,16 +208,96 @@ pre {
} }
pre code { background: none; padding: 0; border: none; font-size: inherit; } 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 ---------- */ /* ---------- Figures ---------- */
figure { margin: 1.5em auto; text-align: center; max-width: 100%; } figure {
figure img { max-width: 100%; height: auto; } 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 { figure figcaption {
font-family: var(--font-heading); font-family: var(--font-heading);
font-size: 0.88rem; font-size: 0.85rem;
color: var(--text-muted); 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 / Notes ---------- */
blockquote { blockquote {
@ -283,9 +369,9 @@ blockquote {
/* ---------- Auto-numbering ---------- */ /* ---------- Auto-numbering ---------- */
body.auto-numbering { counter-reset: section; } body.auto-numbering { counter-reset: section; }
body.auto-numbering h1 { counter-increment: section; counter-reset: subsection; } body.auto-numbering .chapter h1 { counter-increment: section; counter-reset: subsection; }
body.auto-numbering h1::before { content: counter(section) ". "; } body.auto-numbering .chapter h1::before { content: counter(section) ". "; }
body.auto-numbering h2 { counter-increment: subsection; counter-reset: subsubsection; } body.auto-numbering .chapter h2 { counter-increment: subsection; counter-reset: subsubsection; }
body.auto-numbering h2::before { content: counter(section) "." counter(subsection) " "; } body.auto-numbering .chapter h2::before { content: counter(section) "." counter(subsection) " "; }
body.auto-numbering h3 { counter-increment: subsubsection; } body.auto-numbering .chapter h3 { counter-increment: subsubsection; }
body.auto-numbering h3::before { content: counter(section) "." counter(subsection) "." counter(subsubsection) " "; } body.auto-numbering .chapter h3::before { content: counter(section) "." counter(subsection) "." counter(subsubsection) " "; }

145
readme.md
View File

@ -1,64 +1,115 @@
# RBPU16 读出基带处理芯片 · 数据手册 # RBPU16 读出基带处理芯片 · 数据手册
RBPU16 是一款用于超导量子比特态信息读出的 SoC 芯片,最大支持 16 个量子比特并行读出,内部集成 PLL、ADC、DAC、DSP 等模块。 纯文本管理Python 脚本构建,生成自包含 HTML 报告。
本手册帮助相关工程师开展芯片性能评估、硬件设计、驱动开发及芯片使用。
## 项目结构 ## 项目结构
``` ```
├── build.ipynb # 构建入口VS Code 打开 → Run All → 生成报告 ├── build.ipynb # 构建入口VS Code 打开 → Run All
├── project.yaml # 项目配置(标题、章节列表、作者) ├── project.yaml # 项目配置(标题、作者、章节列表)
├── chapters/ # 章节源文件Markdown纯文本管理 ├── chapters/ # 章节源文件Markdown
│ ├── 00_cover.md ├── data/ # 结构化数据CSV / JSON
│ ├── 01_specifications.md ├── assets/ # 图片PNG / JPG
│ ├── ... ├── doc_builder/ # 构建工具(可跨项目复用)
│ └── appendix/ │ ├── themes/datasheet.css
├── data/ # 结构化数据 │ ├── renderers/ # 代码块渲染器
│ ├── pin_name.csv # 管脚定义(用户直接编辑) │ │ └── schemdraw.py
│ ├── pin_loc.csv # 管脚 BGA 位置网格 │ ├── render_table.py # @import 渲染器
│ ├── seg_define.csv # 寄存器地址段定义 │ ├── render_bga.py
│ └── ids.json # 寄存器详细定义(由 XLS 生成,供脚本读取) │ ├── render_address.py
├── assets/ # 图片PNG/JPG │ ├── render_registers.py
├── doc_builder/ # 构建工具 │ ├── render_requirements.py
│ └── themes/ │ ├── import_handler.py # @import 调度器
│ └── datasheet.css │ ├── image_extension.py # 图片尺寸扩展
└── output/ # 构建产物gitignore │ └── codeblock_extension.py# 代码块渲染pre-markdown
└── RBPU16_数据手册.html # 自包含单文件,可直接发送 └── output/ # 构建产物
└── RBPU16_Data_Sheet.html
``` ```
## 快速开始 ## project.yaml
### 生成报告 每个项目根目录的配置文件,`build.ipynb` 读取它来驱动构建。
1. VS Code 打开 `build.ipynb` ```yaml
2. 点击 **Run All** title: RBPU16 读出基带处理芯片
3. 报告生成到 `output/` 目录 subtitle: 数据手册
author: 郭成
theme: datasheet
chapters:
- 00_cover.md
- 01_specifications.md
- ...
```
> 依赖 `markdown` 库,首次运行会自动安装。 ## 语法参考
### 编辑内容 ### 1. 图片
| 修改什么 | 编辑哪个文件 | 如何重新生成 |
|---------|------------|------------|
| 正文内容 | `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` 引用数据文件,构建时自动渲染为表格:
```markdown ```markdown
@import "data/pin_name.csv" → 管脚定义表 ![alt](../assets/x.png){s=75%} # 等比例缩放,默认 75%
@import "data/pin_loc.csv" → BGA 焊球网格 ![alt](../assets/x.png){s=100%} # 原始尺寸
@import "data/seg_define.csv" → 地址映射总表 ![alt](../assets/x.png){w=50%} # 宽度 50%,高度自适应
@import "data/ids.json" → 寄存器详细定义 ![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 <script.py>` 加载项目根目录下的 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 |