463 lines
42 KiB
HTML
463 lines
42 KiB
HTML
<!doctype html>
|
||
<html lang="zh-CN">
|
||
<head>
|
||
<meta charset="utf-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||
<title>CSI040 控制模块详细设计文档</title>
|
||
<style>
|
||
:root {
|
||
--bg: #f6f7f5;
|
||
--paper: #ffffff;
|
||
--ink: #202421;
|
||
--muted: #667069;
|
||
--line: #dfe5df;
|
||
--accent: #167c80;
|
||
--accent-soft: #e6f4f2;
|
||
--warn-soft: #fff7e6;
|
||
--code-bg: #1f2422;
|
||
--code-ink: #edf2ee;
|
||
--shadow: 0 16px 36px rgba(32, 36, 33, 0.08);
|
||
}
|
||
* { box-sizing: border-box; }
|
||
html { scroll-behavior: smooth; }
|
||
body {
|
||
margin: 0;
|
||
color: var(--ink);
|
||
background: var(--bg);
|
||
font: 15px/1.72 "Microsoft YaHei", "PingFang SC", "Segoe UI", Arial, sans-serif;
|
||
}
|
||
.shell {
|
||
display: grid;
|
||
grid-template-columns: 288px minmax(0, 1fr);
|
||
min-height: 100vh;
|
||
}
|
||
aside {
|
||
position: sticky;
|
||
top: 0;
|
||
height: 100vh;
|
||
overflow: auto;
|
||
padding: 28px 22px;
|
||
border-right: 1px solid var(--line);
|
||
background: #fbfcfb;
|
||
}
|
||
.brand {
|
||
padding-bottom: 18px;
|
||
margin-bottom: 18px;
|
||
border-bottom: 1px solid var(--line);
|
||
}
|
||
.brand-title {
|
||
margin: 0 0 8px;
|
||
font-size: 18px;
|
||
line-height: 1.35;
|
||
font-weight: 700;
|
||
}
|
||
.brand-meta {
|
||
margin: 0;
|
||
color: var(--muted);
|
||
font-size: 12px;
|
||
line-height: 1.5;
|
||
}
|
||
nav a {
|
||
display: block;
|
||
color: #35403a;
|
||
text-decoration: none;
|
||
border-radius: 6px;
|
||
padding: 5px 8px;
|
||
margin: 1px 0;
|
||
}
|
||
nav a:hover { background: var(--accent-soft); color: var(--accent); }
|
||
.toc-l1 { font-weight: 700; }
|
||
.toc-l2 { padding-left: 18px; font-size: 14px; }
|
||
.toc-l3 { padding-left: 34px; font-size: 13px; color: var(--muted); }
|
||
main {
|
||
min-width: 0;
|
||
padding: 38px 42px 72px;
|
||
}
|
||
article {
|
||
max-width: 1120px;
|
||
margin: 0 auto;
|
||
padding: 42px 52px;
|
||
background: var(--paper);
|
||
border: 1px solid var(--line);
|
||
border-radius: 10px;
|
||
box-shadow: var(--shadow);
|
||
}
|
||
h1, h2, h3, h4, h5, h6 {
|
||
position: relative;
|
||
line-height: 1.32;
|
||
margin: 1.9em 0 0.72em;
|
||
color: #1d2622;
|
||
}
|
||
h1 { margin-top: 0; font-size: 30px; padding-bottom: 14px; border-bottom: 2px solid var(--accent); }
|
||
h2 { font-size: 23px; border-top: 1px solid var(--line); padding-top: 24px; }
|
||
h3 { font-size: 18px; }
|
||
h4 { font-size: 16px; }
|
||
.anchor {
|
||
position: absolute;
|
||
left: -24px;
|
||
color: var(--accent);
|
||
opacity: 0;
|
||
text-decoration: none;
|
||
font-weight: 500;
|
||
}
|
||
h1:hover .anchor, h2:hover .anchor, h3:hover .anchor, h4:hover .anchor { opacity: 1; }
|
||
p { margin: 0.8em 0; }
|
||
ul { padding-left: 1.35em; }
|
||
li { margin: 0.25em 0; }
|
||
code {
|
||
font-family: Consolas, "Cascadia Mono", "SFMono-Regular", monospace;
|
||
font-size: 0.92em;
|
||
border-radius: 4px;
|
||
background: #eef2ef;
|
||
padding: 0.12em 0.35em;
|
||
}
|
||
pre {
|
||
position: relative;
|
||
overflow: auto;
|
||
margin: 18px 0;
|
||
padding: 18px 20px;
|
||
color: var(--code-ink);
|
||
background: var(--code-bg);
|
||
border-radius: 8px;
|
||
line-height: 1.55;
|
||
}
|
||
pre code { padding: 0; background: transparent; color: inherit; font-size: 13px; }
|
||
.table-wrap { overflow-x: auto; margin: 18px 0 24px; border: 1px solid var(--line); border-radius: 8px; }
|
||
table { width: 100%; border-collapse: collapse; min-width: 640px; }
|
||
th, td { padding: 10px 12px; border-bottom: 1px solid var(--line); vertical-align: top; }
|
||
th { position: sticky; top: 0; z-index: 1; background: #edf5f3; text-align: left; font-weight: 700; }
|
||
tr:nth-child(even) td { background: #fafbfa; }
|
||
tr:last-child td { border-bottom: 0; }
|
||
.doc-note {
|
||
margin: 0 0 28px;
|
||
padding: 14px 16px;
|
||
background: var(--warn-soft);
|
||
border: 1px solid #f1d28a;
|
||
border-radius: 8px;
|
||
color: #654700;
|
||
}
|
||
.top-link {
|
||
position: fixed;
|
||
right: 24px;
|
||
bottom: 22px;
|
||
padding: 8px 12px;
|
||
border-radius: 999px;
|
||
background: var(--accent);
|
||
color: #fff;
|
||
text-decoration: none;
|
||
box-shadow: 0 8px 18px rgba(22,124,128,.22);
|
||
font-size: 13px;
|
||
}
|
||
@media (max-width: 980px) {
|
||
.shell { display: block; }
|
||
aside { position: static; height: auto; max-height: 42vh; border-right: 0; border-bottom: 1px solid var(--line); }
|
||
main { padding: 22px 14px 54px; }
|
||
article { padding: 28px 18px; border-radius: 8px; }
|
||
.anchor { display: none; }
|
||
h1 { font-size: 25px; }
|
||
h2 { font-size: 20px; }
|
||
}
|
||
@media print {
|
||
body { background: #fff; font-size: 12px; }
|
||
.shell { display: block; }
|
||
aside, .top-link { display: none; }
|
||
main { padding: 0; }
|
||
article { max-width: none; padding: 0; border: 0; box-shadow: none; }
|
||
h1, h2, h3 { break-after: avoid; }
|
||
pre, .table-wrap { break-inside: avoid; }
|
||
a { color: inherit; text-decoration: none; }
|
||
}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<div class="shell" id="top">
|
||
<aside>
|
||
<div class="brand">
|
||
<p class="brand-title">CSI040 控制模块详细设计文档</p>
|
||
<p class="brand-meta">源文件:docs/csi040_ctrl_design.md<br>生成时间:2026-07-01 09:47:07<br>离线单文件 HTML</p>
|
||
</div>
|
||
<nav aria-label="文档目录">
|
||
<a class="toc-l1" href="#csi040-控制模块详细设计文档">CSI040 控制模块详细设计文档</a>
|
||
<a class="toc-l2" href="#1-文档范围">1. 文档范围</a>
|
||
<a class="toc-l2" href="#2-总体结构">2. 总体结构</a>
|
||
<a class="toc-l2" href="#3-模块参数">3. 模块参数</a>
|
||
<a class="toc-l3" href="#3-1-csi040-ctrl">3.1 <code>csi040_ctrl</code></a>
|
||
<a class="toc-l3" href="#3-2-csi040-spi-master">3.2 <code>csi040_spi_master</code></a>
|
||
<a class="toc-l2" href="#4-顶层接口">4. 顶层接口</a>
|
||
<a class="toc-l3" href="#4-1-时钟复位">4.1 时钟复位</a>
|
||
<a class="toc-l3" href="#4-2-bram-风格控制口">4.2 BRAM 风格控制口</a>
|
||
<a class="toc-l3" href="#4-3-状态输出">4.3 状态输出</a>
|
||
<a class="toc-l3" href="#4-4-asic-侧引脚">4.4 ASIC 侧引脚</a>
|
||
<a class="toc-l2" href="#5-bram-寄存器映射">5. BRAM 寄存器映射</a>
|
||
<a class="toc-l3" href="#5-1-status-字段">5.1 <code>STATUS</code> 字段</a>
|
||
<a class="toc-l3" href="#5-2-状态清除">5.2 状态清除</a>
|
||
<a class="toc-l2" href="#6-高层命令序列设计">6. 高层命令序列设计</a>
|
||
<a class="toc-l3" href="#6-1-busy-机制">6.1 Busy 机制</a>
|
||
<a class="toc-l3" href="#6-2-写控制寄存器">6.2 写控制寄存器</a>
|
||
<a class="toc-l3" href="#6-3-写通道使能">6.3 写通道使能</a>
|
||
<a class="toc-l3" href="#6-4-写触发命令">6.4 写触发命令</a>
|
||
<a class="toc-l3" href="#6-5-特殊序列-seq-cmd">6.5 特殊序列 <code>SEQ_CMD</code></a>
|
||
<a class="toc-l3" href="#6-6-通道参数写">6.6 通道参数写</a>
|
||
<a class="toc-l3" href="#6-7-direct-spi-访问">6.7 Direct SPI 访问</a>
|
||
<a class="toc-l2" href="#7-spi-master-设计">7. SPI Master 设计</a>
|
||
<a class="toc-l3" href="#7-1-命令接口">7.1 命令接口</a>
|
||
<a class="toc-l3" href="#7-2-spi-帧格式">7.2 SPI 帧格式</a>
|
||
<a class="toc-l3" href="#7-3-spi-状态机">7.3 SPI 状态机</a>
|
||
<a class="toc-l3" href="#7-4-读数据采样">7.4 读数据采样</a>
|
||
<a class="toc-l2" href="#8-复位行为">8. 复位行为</a>
|
||
<a class="toc-l2" href="#9-当前-testbench-覆盖">9. 当前 Testbench 覆盖</a>
|
||
<a class="toc-l2" href="#10-仿真与波形">10. 仿真与波形</a>
|
||
<a class="toc-l2" href="#11-软件使用建议">11. 软件使用建议</a>
|
||
<a class="toc-l2" href="#12-后续可改进点">12. 后续可改进点</a>
|
||
|
||
</nav>
|
||
</aside>
|
||
<main>
|
||
<article>
|
||
<p class="doc-note">优化版 HTML:已添加侧边目录、章节锚点、响应式表格、打印样式和离线 CSS。</p>
|
||
<h1 id="csi040-控制模块详细设计文档"><a class="anchor" href="#csi040-控制模块详细设计文档" aria-label="章节链接">#</a>CSI040 控制模块详细设计文档</h1>
|
||
<h2 id="1-文档范围"><a class="anchor" href="#1-文档范围" aria-label="章节链接">#</a>1. 文档范围</h2>
|
||
<p>本文档描述 CSI040 ASIC 控制模块的 RTL 设计,覆盖以下文件:</p>
|
||
<ul><li><code>c64_pro.srcs/sources_1/rtl/csi040/csi040_ctrl.v</code></li><li><code>c64_pro.srcs/sources_1/rtl/csi040/csi040_spi_master.v</code></li><li><code>c64_pro.srcs/sources_1/rtl/csi040/csi040_ctrl_tb.v</code></li></ul>
|
||
<p>该模块在 FPGA/PL 侧提供一组 BRAM 风格的控制寄存器。上位逻辑或 PS 通过写这些寄存器来触发 CSI040 ASIC 的 SPI 读写、通道参数配置、模式解锁、触发控制等操作。</p>
|
||
<h2 id="2-总体结构"><a class="anchor" href="#2-总体结构" aria-label="章节链接">#</a>2. 总体结构</h2>
|
||
<pre><code class="language-text">PS / 软件
|
||
|
|
||
| BRAM 风格寄存器访问
|
||
v
|
||
+------------------+
|
||
| csi040_ctrl |
|
||
| |
|
||
| 软件寄存器映射 |
|
||
| shadow 寄存器 |
|
||
| seq 序列控制器 |
|
||
| 状态/错误锁存 |
|
||
+--------+---------+
|
||
|
|
||
| cmd_valid / cmd_ready / cmd_addr / cmd_wdata / cmd_len
|
||
v
|
||
+---------------------+
|
||
| csi040_spi_master |
|
||
| |
|
||
| SPI 组帧 |
|
||
| SCK/CSN/SI/SO 时序 |
|
||
+----------+----------+
|
||
|
|
||
| asic_csn / asic_sck / asic_si / asic_so
|
||
v
|
||
CSI040 ASIC</code></pre>
|
||
<p><code>csi040_ctrl</code> 是上层可见的控制模块,负责接收 BRAM 写操作、保存软件配置、发起高层命令序列,并把命令转换为 SPI master 的抽象命令接口。</p>
|
||
<p><code>csi040_spi_master</code> 是底层 SPI 引擎,负责把 opcode、ASIC 地址和数据字节按 SPI 时序移出,并在读操作时采样 <code>asic_so</code>。</p>
|
||
<h2 id="3-模块参数"><a class="anchor" href="#3-模块参数" aria-label="章节链接">#</a>3. 模块参数</h2>
|
||
<h3 id="3-1-csi040-ctrl"><a class="anchor" href="#3-1-csi040-ctrl" aria-label="章节链接">#</a>3.1 <code>csi040_ctrl</code></h3>
|
||
<div class="table-wrap"><table><thead><tr><th>参数</th><th style="text-align:right">默认值</th><th>说明</th></tr></thead><tbody><tr><td><code>VERSION</code></td><td style="text-align:right"><code>32'h04040001</code></td><td>模块版本号,读 BRAM 地址 <code>0x0000</code> 返回。</td></tr><tr><td><code>SPI_CLK_DIV</code></td><td style="text-align:right"><code>50</code></td><td>SPI SCK 半周期分频系数,<code>f_sck = f_clk / (2 * SPI_CLK_DIV)</code>。</td></tr><tr><td><code>REG_ADDR_WIDTH</code></td><td style="text-align:right"><code>16</code></td><td>ASIC 内部寄存器地址宽度。</td></tr><tr><td><code>SPI_ADDR_BYTES</code></td><td style="text-align:right"><code>2</code></td><td>SPI 帧中的地址字节数。</td></tr><tr><td><code>MAX_BURST_BYTES</code></td><td style="text-align:right"><code>4</code></td><td>单次 SPI 最大数据字节数。</td></tr><tr><td><code>SPI_OPCODE_WRITE</code></td><td style="text-align:right"><code>8'h00</code></td><td>默认写 opcode;testbench 中重载为 <code>8'h02</code>。</td></tr><tr><td><code>SPI_OPCODE_READ</code></td><td style="text-align:right"><code>8'h80</code></td><td>默认读 opcode;testbench 中重载为 <code>8'h03</code>。</td></tr><tr><td><code>CSI_REG_*</code></td><td style="text-align:right">多个地址</td><td>CSI040 ASIC 内部寄存器地址参数。</td></tr></tbody></table></div>
|
||
<h3 id="3-2-csi040-spi-master"><a class="anchor" href="#3-2-csi040-spi-master" aria-label="章节链接">#</a>3.2 <code>csi040_spi_master</code></h3>
|
||
<div class="table-wrap"><table><thead><tr><th>参数</th><th style="text-align:right">默认值</th><th>说明</th></tr></thead><tbody><tr><td><code>CLK_DIV</code></td><td style="text-align:right"><code>50</code></td><td>SCK 半周期对应的 <code>clk</code> 周期数。</td></tr><tr><td><code>REG_ADDR_WIDTH</code></td><td style="text-align:right"><code>16</code></td><td><code>cmd_addr</code> 地址宽度。</td></tr><tr><td><code>ADDR_BYTES</code></td><td style="text-align:right"><code>2</code></td><td>SPI 地址字段字节数。</td></tr><tr><td><code>MAX_BURST_BYTES</code></td><td style="text-align:right"><code>4</code></td><td>最大 payload 字节数。</td></tr><tr><td><code>OPCODE_WRITE</code></td><td style="text-align:right"><code>8'h00</code></td><td>SPI 写命令 opcode。</td></tr><tr><td><code>OPCODE_READ</code></td><td style="text-align:right"><code>8'h80</code></td><td>SPI 读命令 opcode。</td></tr></tbody></table></div>
|
||
<h2 id="4-顶层接口"><a class="anchor" href="#4-顶层接口" aria-label="章节链接">#</a>4. 顶层接口</h2>
|
||
<h3 id="4-1-时钟复位"><a class="anchor" href="#4-1-时钟复位" aria-label="章节链接">#</a>4.1 时钟复位</h3>
|
||
<div class="table-wrap"><table><thead><tr><th>信号</th><th>方向</th><th>说明</th></tr></thead><tbody><tr><td><code>clk</code></td><td>input</td><td>主时钟,当前所有寄存器逻辑都使用该时钟。</td></tr><tr><td><code>rst_n</code></td><td>input</td><td>异步低有效复位。</td></tr><tr><td><code>bram_clk</code></td><td>input</td><td>接口保留信号,当前 RTL 未使用。</td></tr><tr><td><code>bram_rst</code></td><td>input</td><td>接口保留信号,当前 RTL 未使用。</td></tr></tbody></table></div>
|
||
<p>注意:虽然接口里有 <code>bram_clk</code>,但当前实现没有使用它。因此 BRAM 控制访问应与 <code>clk</code> 同步。如果后续接真实 AXI BRAM Controller 且时钟不同,需要补 CDC 或改成 <code>bram_clk</code> 域逻辑。</p>
|
||
<h3 id="4-2-bram-风格控制口"><a class="anchor" href="#4-2-bram-风格控制口" aria-label="章节链接">#</a>4.2 BRAM 风格控制口</h3>
|
||
<div class="table-wrap"><table><thead><tr><th>信号</th><th>方向</th><th style="text-align:right">位宽</th><th>说明</th></tr></thead><tbody><tr><td><code>bram_addr</code></td><td>input</td><td style="text-align:right">13</td><td>BRAM 字节地址,当前使用 4 字节对齐地址。</td></tr><tr><td><code>bram_din</code></td><td>input</td><td style="text-align:right">32</td><td>写数据。</td></tr><tr><td><code>bram_dout</code></td><td>output reg</td><td style="text-align:right">32</td><td>读数据,在读访问时更新。</td></tr><tr><td><code>bram_en</code></td><td>input</td><td style="text-align:right">1</td><td>BRAM 访问使能。</td></tr><tr><td><code>bram_we</code></td><td>input</td><td style="text-align:right">4</td><td>写使能。当前仅识别 <code>4'hF</code> 为写、<code>4'h0</code> 为读。</td></tr></tbody></table></div>
|
||
<p>RTL 中访问判定为:</p>
|
||
<pre><code class="language-verilog">assign bram_wr_valid = bram_en && (bram_we == 4'hF);
|
||
assign bram_rd_valid = bram_en && (bram_we == 4'h0);</code></pre>
|
||
<p>因此当前模块不支持 byte enable 局部写。如果软件侧产生 <code>bram_we = 4'h1/4'h3</code> 之类的写操作,该写不会被模块识别。</p>
|
||
<h3 id="4-3-状态输出"><a class="anchor" href="#4-3-状态输出" aria-label="章节链接">#</a>4.3 状态输出</h3>
|
||
<div class="table-wrap"><table><thead><tr><th>信号</th><th>方向</th><th>说明</th></tr></thead><tbody><tr><td><code>busy</code></td><td>output</td><td>模块忙标志。只要高层序列、SPI master 或待发 SPI 命令任一处于忙状态,该信号为高。</td></tr><tr><td><code>spi_done</code></td><td>output</td><td>SPI master 完成脉冲,持续 1 个 <code>clk</code> 周期。</td></tr><tr><td><code>error_busy</code></td><td>output reg</td><td>忙时启动新命令的粘滞错误标志,需要软件写 <code>STATUS[1]</code> 清除。</td></tr></tbody></table></div>
|
||
<h3 id="4-4-asic-侧引脚"><a class="anchor" href="#4-4-asic-侧引脚" aria-label="章节链接">#</a>4.4 ASIC 侧引脚</h3>
|
||
<div class="table-wrap"><table><thead><tr><th>信号</th><th>方向</th><th>说明</th></tr></thead><tbody><tr><td><code>asic_rst_n</code></td><td>output reg</td><td>ASIC 复位控制,软件通过 <code>PIN_CTRL[0]</code> 控制,复位默认值为 1。</td></tr><tr><td><code>asic_trig</code></td><td>output reg</td><td>ASIC 外部触发引脚,软件通过 <code>PIN_CTRL[1]</code> 控制,复位默认值为 0。</td></tr><tr><td><code>asic_csn</code></td><td>output</td><td>SPI 片选,低有效。</td></tr><tr><td><code>asic_sck</code></td><td>output</td><td>SPI 时钟,空闲为低。</td></tr><tr><td><code>asic_si</code></td><td>output</td><td>SPI master 输出到 ASIC 的串行数据。</td></tr><tr><td><code>asic_so</code></td><td>input</td><td>ASIC 输出到 SPI master 的串行数据。</td></tr></tbody></table></div>
|
||
<h2 id="5-bram-寄存器映射"><a class="anchor" href="#5-bram-寄存器映射" aria-label="章节链接">#</a>5. BRAM 寄存器映射</h2>
|
||
<p>所有地址均为字节地址。</p>
|
||
<div class="table-wrap"><table><thead><tr><th style="text-align:right">地址</th><th>名称</th><th>访问</th><th>说明</th></tr></thead><tbody><tr><td style="text-align:right"><code>13'h0000</code></td><td><code>VERSION</code></td><td>R</td><td>读取模块版本号 <code>VERSION</code>。</td></tr><tr><td style="text-align:right"><code>13'h0004</code></td><td><code>STATUS</code></td><td>R/W1C</td><td>读取模块状态;写 bit0 清 <code>done_latched</code>,写 bit1 清 <code>error_busy</code>。</td></tr><tr><td style="text-align:right"><code>13'h0008</code></td><td><code>SPI_ADDR</code></td><td>R/W</td><td>Direct SPI 访问的 ASIC 地址。</td></tr><tr><td style="text-align:right"><code>13'h000C</code></td><td><code>SPI_WDATA</code></td><td>R/W</td><td>Direct SPI 写数据。payload 第 1 字节来自 <code>bram_din[7:0]</code>。</td></tr><tr><td style="text-align:right"><code>13'h0010</code></td><td><code>SPI_CTRL</code></td><td>W</td><td>启动 Direct SPI 访问。<code>bit0=write</code>,<code>bits[15:8]=length</code>。</td></tr><tr><td style="text-align:right"><code>13'h0014</code></td><td><code>SPI_RDATA</code></td><td>R</td><td>最近一次 SPI 读回数据。</td></tr><tr><td style="text-align:right"><code>13'h0018</code></td><td><code>PIN_CTRL</code></td><td>R/W</td><td><code>bit0=asic_rst_n</code>,<code>bit1=asic_trig</code>。</td></tr><tr><td style="text-align:right"><code>13'h001C</code></td><td><code>LAST_ADDR</code></td><td>R</td><td>最近一次完成 SPI 操作的 ASIC 地址。</td></tr><tr><td style="text-align:right"><code>13'h0020</code></td><td><code>CONTROL</code></td><td>R/W</td><td>更新 <code>control_shadow</code>,并写 ASIC 控制寄存器 <code>0x0002</code>。</td></tr><tr><td style="text-align:right"><code>13'h0024</code></td><td><code>CH_ENABLE</code></td><td>R/W</td><td>更新 <code>ch_enable_shadow</code>,并写 ASIC 通道使能寄存器 <code>0x0007/0x0008</code>。</td></tr><tr><td style="text-align:right"><code>13'h0028</code></td><td><code>TRIGGER</code></td><td>W</td><td>写 ASIC 触发寄存器 <code>0x0110 = 8'hAA</code>。</td></tr><tr><td style="text-align:right"><code>13'h002C</code></td><td><code>SEQ_CMD</code></td><td>W</td><td>启动特殊序列:DCS unlock、Test unlock、mode exit、控制寄存器快捷写。</td></tr><tr><td style="text-align:right"><code>13'h0040</code></td><td><code>CH_INDEX</code></td><td>R/W</td><td>选择后续通道参数写入的 channel index。</td></tr><tr><td style="text-align:right"><code>13'h0044</code></td><td><code>CH_MISC</code></td><td>W</td><td>写当前 channel 的 offset <code>0x0</code>。</td></tr><tr><td style="text-align:right"><code>13'h0048</code></td><td><code>CH_CUR_A</code></td><td>W</td><td>写当前 channel 的 offset <code>0x1</code>。</td></tr><tr><td style="text-align:right"><code>13'h004C</code></td><td><code>CH_CUR_B</code></td><td>W</td><td>写当前 channel 的 offset <code>0x2</code>。</td></tr><tr><td style="text-align:right"><code>13'h0050</code></td><td><code>CH_CUR_C</code></td><td>W</td><td>写当前 channel 的 offset <code>0x3</code>。</td></tr><tr><td style="text-align:right"><code>13'h0054</code></td><td><code>CH_CUR_D</code></td><td>W</td><td>写当前 channel 的 offset <code>0x4</code>。</td></tr><tr><td style="text-align:right"><code>13'h0058</code></td><td><code>CH_STIM_PW</code></td><td>W</td><td>写当前 channel 的 offset <code>0x5</code>。</td></tr><tr><td style="text-align:right"><code>13'h005C</code></td><td><code>CH_DZ0</code></td><td>W</td><td>写当前 channel 的 offset <code>0x6</code>。</td></tr><tr><td style="text-align:right"><code>13'h0060</code></td><td><code>CH_DZ1</code></td><td>W</td><td>写当前 channel 的 offset <code>0x7</code>。</td></tr><tr><td style="text-align:right"><code>13'h0064</code></td><td><code>CH_SKIP_AB</code></td><td>W</td><td>写当前 channel 的 offset <code>0x8</code>。</td></tr><tr><td style="text-align:right"><code>13'h0068</code></td><td><code>CH_SKIP_CD</code></td><td>W</td><td>写当前 channel 的 offset <code>0x9</code>。</td></tr><tr><td style="text-align:right"><code>13'h006C</code></td><td><code>CH_STIM_NUM</code></td><td>W</td><td>写当前 channel 的 offset <code>0xA</code>。</td></tr><tr><td style="text-align:right"><code>13'h0070</code></td><td><code>CH_DZ2</code></td><td>W</td><td>写当前 channel 的 offset <code>0xB</code>。</td></tr><tr><td style="text-align:right"><code>13'h0074</code></td><td><code>CH_THERAPY_DELAY</code></td><td>W</td><td>写当前 channel 的 offset <code>0xC</code>。</td></tr><tr><td style="text-align:right"><code>13'h0078</code></td><td><code>CH_THERAPY_NUM</code></td><td>W</td><td>写当前 channel 的 offset <code>0xD</code>。</td></tr><tr><td style="text-align:right"><code>13'h007C</code></td><td><code>CH_DZ3</code></td><td>W</td><td>写当前 channel 的 offset <code>0xE</code>。</td></tr><tr><td style="text-align:right"><code>13'h0080</code></td><td><code>CH_MASTER_NUM</code></td><td>W</td><td>写当前 channel 的 offset <code>0xF</code>。</td></tr></tbody></table></div>
|
||
<h3 id="5-1-status-字段"><a class="anchor" href="#5-1-status-字段" aria-label="章节链接">#</a>5.1 <code>STATUS</code> 字段</h3>
|
||
<p>RTL 返回表达式为:</p>
|
||
<pre><code class="language-verilog">bram_dout <= {16'd0, seq_id, seq_step, error_busy, done_latched, spi_cmd_ready, ctrl_busy};</code></pre>
|
||
<p>该拼接宽度为 26 bit,赋值给 32 bit 时左侧补 0。因此软件侧实际可按低位理解:</p>
|
||
<div class="table-wrap"><table><thead><tr><th style="text-align:right">位</th><th>名称</th><th>说明</th></tr></thead><tbody><tr><td style="text-align:right"><code>[5]</code></td><td><code>ctrl_busy</code></td><td>当前模块忙。</td></tr><tr><td style="text-align:right"><code>[6]</code></td><td><code>spi_cmd_ready</code></td><td>SPI master 是否空闲可接收新命令。</td></tr><tr><td style="text-align:right"><code>[7]</code></td><td><code>done_latched</code></td><td>最近至少完成过一次 SPI 操作,写 <code>STATUS[0]=1</code> 清除。</td></tr><tr><td style="text-align:right"><code>[8]</code></td><td><code>error_busy</code></td><td>忙时写命令产生的错误,写 <code>STATUS[1]=1</code> 清除。</td></tr><tr><td style="text-align:right"><code>[11:9]</code></td><td><code>seq_step</code></td><td>当前高层序列步骤。</td></tr><tr><td style="text-align:right"><code>[15:12]</code></td><td><code>seq_id</code></td><td>当前高层序列编号。</td></tr><tr><td style="text-align:right">其他</td><td>0</td><td>保留。</td></tr></tbody></table></div>
|
||
<h3 id="5-2-状态清除"><a class="anchor" href="#5-2-状态清除" aria-label="章节链接">#</a>5.2 状态清除</h3>
|
||
<p>写 <code>BRAM_ADDR_STATUS</code> 时:</p>
|
||
<div class="table-wrap"><table><thead><tr><th style="text-align:right">写入位</th><th>行为</th></tr></thead><tbody><tr><td style="text-align:right"><code>bram_din[0] = 1</code></td><td>清除 <code>done_latched</code>。</td></tr><tr><td style="text-align:right"><code>bram_din[1] = 1</code></td><td>清除 <code>error_busy</code>。</td></tr></tbody></table></div>
|
||
<h2 id="6-高层命令序列设计"><a class="anchor" href="#6-高层命令序列设计" aria-label="章节链接">#</a>6. 高层命令序列设计</h2>
|
||
<p>模块内部用 <code>seq_id + seq_step + seq_active</code> 控制多拍或多 SPI 帧操作。</p>
|
||
<div class="table-wrap"><table><thead><tr><th style="text-align:right"><code>seq_id</code></th><th>名称</th><th>触发来源</th></tr></thead><tbody><tr><td style="text-align:right"><code>4'h0</code></td><td><code>SEQ_IDLE</code></td><td>空闲。</td></tr><tr><td style="text-align:right"><code>4'h1</code></td><td><code>SEQ_WRITE_CONTROL</code></td><td>写 <code>CONTROL</code>,或 <code>SEQ_CMD[3]/[4]</code>。</td></tr><tr><td style="text-align:right"><code>4'h2</code></td><td><code>SEQ_CH_ENABLE</code></td><td>写 <code>CH_ENABLE</code>。</td></tr><tr><td style="text-align:right"><code>4'h3</code></td><td><code>SEQ_TRIGGER</code></td><td>写 <code>TRIGGER</code>。</td></tr><tr><td style="text-align:right"><code>4'h4</code></td><td><code>SEQ_DCS_UNLOCK</code></td><td>写 <code>SEQ_CMD[0]</code>。</td></tr><tr><td style="text-align:right"><code>4'h5</code></td><td><code>SEQ_TEST_UNLOCK</code></td><td>写 <code>SEQ_CMD[1]</code>。</td></tr><tr><td style="text-align:right"><code>4'h6</code></td><td><code>SEQ_MODE_EXIT</code></td><td>写 <code>SEQ_CMD[2]</code>。</td></tr><tr><td style="text-align:right"><code>4'h7</code></td><td><code>SEQ_CH_WRITE</code></td><td>写任意通道参数寄存器。</td></tr><tr><td style="text-align:right"><code>4'h8</code></td><td><code>SEQ_DIRECT</code></td><td>写 <code>SPI_CTRL</code>。</td></tr></tbody></table></div>
|
||
<h3 id="6-1-busy-机制"><a class="anchor" href="#6-1-busy-机制" aria-label="章节链接">#</a>6.1 Busy 机制</h3>
|
||
<p>模块忙信号定义:</p>
|
||
<pre><code class="language-verilog">ctrl_busy = seq_active || spi_busy || spi_cmd_valid;
|
||
busy = ctrl_busy;</code></pre>
|
||
<p>对所有会启动 SPI 的 BRAM 写操作,RTL 都先检查 <code>!ctrl_busy</code>。若忙时写入命令寄存器:</p>
|
||
<ul><li>不启动新命令;</li><li><code>error_busy <= 1'b1</code>;</li><li>该错误保持到软件写 <code>STATUS[1]</code> 清除。</li></ul>
|
||
<p>推荐软件流程:</p>
|
||
<p>1. 轮询 <code>busy == 0</code> 或 <code>STATUS[5] == 0</code>。 2. 写命令寄存器。 3. 等待 <code>done_latched == 1</code> 或等待固定时间。 4. 如有读操作,读取 <code>SPI_RDATA</code>。 5. 写 <code>STATUS[0] = 1</code> 清除 done 标志。</p>
|
||
<h3 id="6-2-写控制寄存器"><a class="anchor" href="#6-2-写控制寄存器" aria-label="章节链接">#</a>6.2 写控制寄存器</h3>
|
||
<p>写 <code>BRAM_ADDR_CONTROL</code>:</p>
|
||
<p>1. <code>control_shadow <= bram_din[7:0]</code> 2. 启动 <code>SEQ_WRITE_CONTROL</code> 3. 产生 1 个 SPI write:</p>
|
||
<pre><code class="language-text">ASIC addr = CSI_REG_CONTROL = 0x0002
|
||
data = control_shadow
|
||
length = 1 byte</code></pre>
|
||
<p>testbench 中写:</p>
|
||
<pre><code class="language-text">write_bram(13'h0020, 32'h00000010)</code></pre>
|
||
<p>期望 SPI 帧:</p>
|
||
<pre><code class="language-text">0x02_0002_10</code></pre>
|
||
<h3 id="6-3-写通道使能"><a class="anchor" href="#6-3-写通道使能" aria-label="章节链接">#</a>6.3 写通道使能</h3>
|
||
<p>写 <code>BRAM_ADDR_CH_ENABLE</code>:</p>
|
||
<p>1. <code>ch_enable_shadow <= bram_din[15:0]</code> 2. 启动 <code>SEQ_CH_ENABLE</code> 3. 分两步写 ASIC:</p>
|
||
<pre><code class="language-text">seq_step 0: addr 0x0007, data ch_enable_shadow[7:0]
|
||
seq_step 1: addr 0x0008, data ch_enable_shadow[15:8]</code></pre>
|
||
<p>testbench 中写 <code>0xA55A</code>,期望两帧:</p>
|
||
<pre><code class="language-text">0x02_0007_5A
|
||
0x02_0008_A5</code></pre>
|
||
<h3 id="6-4-写触发命令"><a class="anchor" href="#6-4-写触发命令" aria-label="章节链接">#</a>6.4 写触发命令</h3>
|
||
<p>写 <code>BRAM_ADDR_TRIGGER</code> 启动 <code>SEQ_TRIGGER</code>,SPI 写:</p>
|
||
<pre><code class="language-text">ASIC addr = CSI_REG_STIM_TRIGGER = 0x0110
|
||
data = 8'hAA</code></pre>
|
||
<p>注意:该 SPI trigger 与 <code>asic_trig</code> 引脚是两个不同路径。<code>asic_trig</code> 只受 <code>PIN_CTRL[1]</code> 控制。</p>
|
||
<h3 id="6-5-特殊序列-seq-cmd"><a class="anchor" href="#6-5-特殊序列-seq-cmd" aria-label="章节链接">#</a>6.5 特殊序列 <code>SEQ_CMD</code></h3>
|
||
<p>写 <code>BRAM_ADDR_SEQ_CMD</code> 时,RTL 按 bit 优先级解析:</p>
|
||
<div class="table-wrap"><table><thead><tr><th style="text-align:right">bit</th><th>序列</th><th>实际 SPI 操作</th></tr></thead><tbody><tr><td style="text-align:right"><code>0</code></td><td>DCS unlock</td><td>连续向 <code>0x0009</code> 写 <code>55h, B3h, 0Ah</code>。</td></tr><tr><td style="text-align:right"><code>1</code></td><td>Test unlock</td><td>连续向 <code>0x0009</code> 写 <code>AAh, 4Ch, F5h</code>。</td></tr><tr><td style="text-align:right"><code>2</code></td><td>Mode exit</td><td>向 <code>0x0009</code> 写 <code>F9h</code>。</td></tr><tr><td style="text-align:right"><code>3</code></td><td>控制快捷写</td><td><code>control_shadow <= 8'h10</code>,随后写 <code>CONTROL</code>。</td></tr><tr><td style="text-align:right"><code>4</code></td><td>控制快捷写</td><td><code>control_shadow <= 8'h08</code>,随后写 <code>CONTROL</code>。</td></tr></tbody></table></div>
|
||
<p>若多个 bit 同时为 1,优先级从 bit0 到 bit4。</p>
|
||
<h3 id="6-6-通道参数写"><a class="anchor" href="#6-6-通道参数写" aria-label="章节链接">#</a>6.6 通道参数写</h3>
|
||
<p>通道地址由函数 <code>ch_addr</code> 计算:</p>
|
||
<pre><code class="language-verilog">ch_addr = CSI_CH_BASE + (ch * CSI_CH_STRIDE) + offset;</code></pre>
|
||
<p>默认:</p>
|
||
<pre><code class="language-text">CSI_CH_BASE = 0x0010
|
||
CSI_CH_STRIDE = 0x0010</code></pre>
|
||
<p>所以:</p>
|
||
<pre><code class="language-text">ASIC channel register address = 0x0010 + ch_index * 0x0010 + ch_offset</code></pre>
|
||
<p>示例:</p>
|
||
<pre><code class="language-text">CH_INDEX = 3
|
||
写 CH_CUR_A = 0x7F
|
||
offset = 0x1
|
||
ASIC addr = 0x0010 + 3 * 0x0010 + 0x1 = 0x0041
|
||
SPI frame = 0x02_0041_7F</code></pre>
|
||
<p>当前所有通道参数写都只发送 <code>bram_din[7:0]</code>。</p>
|
||
<h3 id="6-7-direct-spi-访问"><a class="anchor" href="#6-7-direct-spi-访问" aria-label="章节链接">#</a>6.7 Direct SPI 访问</h3>
|
||
<p>Direct SPI 用于软件直接访问任意 ASIC 寄存器。</p>
|
||
<p>操作流程:</p>
|
||
<p>1. 写 <code>SPI_ADDR</code>,设置 ASIC 地址。 2. 若是写操作,写 <code>SPI_WDATA</code>。 3. 写 <code>SPI_CTRL</code> 启动 SPI:</p>
|
||
<pre><code class="language-text">SPI_CTRL[0] = direct_write, 1 为写,0 为读
|
||
SPI_CTRL[15:8] = direct_len,单位 byte;0 会被转换为 1</code></pre>
|
||
<p><code>direct_len</code> 在 SPI master 内部被限制到 <code>1..MAX_BURST_BYTES</code>。</p>
|
||
<p>读操作完成后:</p>
|
||
<ul><li><code>last_rdata <= spi_cmd_rdata[31:0]</code></li><li>软件读 <code>SPI_RDATA</code> 获取数据</li><li>软件读 <code>LAST_ADDR</code> 获取最近访问地址</li></ul>
|
||
<p>testbench 中直接读 Part ID:</p>
|
||
<pre><code class="language-text">SPI_ADDR = 0x011D
|
||
SPI_CTRL = 0x00000100 // write=0, len=1
|
||
期望帧 = 0x03_011D_00
|
||
读回 = 0x28</code></pre>
|
||
<h2 id="7-spi-master-设计"><a class="anchor" href="#7-spi-master-设计" aria-label="章节链接">#</a>7. SPI Master 设计</h2>
|
||
<p><code>csi040_spi_master</code> 接收抽象命令,并转换成 SPI bit 流。</p>
|
||
<h3 id="7-1-命令接口"><a class="anchor" href="#7-1-命令接口" aria-label="章节链接">#</a>7.1 命令接口</h3>
|
||
<div class="table-wrap"><table><thead><tr><th>信号</th><th>方向</th><th>说明</th></tr></thead><tbody><tr><td><code>cmd_ready</code></td><td>output</td><td>SPI master 空闲时为 1。</td></tr><tr><td><code>cmd_valid</code></td><td>input</td><td>命令有效。空闲时采样。</td></tr><tr><td><code>cmd_write</code></td><td>input</td><td>1 为写,0 为读。</td></tr><tr><td><code>cmd_addr</code></td><td>input</td><td>ASIC 寄存器地址。</td></tr><tr><td><code>cmd_wdata</code></td><td>input</td><td>写 payload。低字节先作为 payload byte0。</td></tr><tr><td><code>cmd_len</code></td><td>input</td><td>payload 字节数。0 转 1,超过最大值则截断。</td></tr><tr><td><code>done</code></td><td>output reg</td><td>SPI 帧完成脉冲,持续 1 个 <code>clk</code>。</td></tr><tr><td><code>cmd_rdata</code></td><td>output reg</td><td>读回 payload。</td></tr><tr><td><code>busy</code></td><td>output reg</td><td>SPI master 忙。</td></tr></tbody></table></div>
|
||
<h3 id="7-2-spi-帧格式"><a class="anchor" href="#7-2-spi-帧格式" aria-label="章节链接">#</a>7.2 SPI 帧格式</h3>
|
||
<p>默认 <code>ADDR_BYTES = 2</code>,帧格式为:</p>
|
||
<pre><code class="language-text">byte0 opcode
|
||
byte1 address high byte
|
||
byte2 address low byte
|
||
byte3+ payload bytes</code></pre>
|
||
<p>1 字节写示例:</p>
|
||
<pre><code class="language-text">opcode = 0x02
|
||
addr = 0x0002
|
||
data = 0x10
|
||
frame = 0x02_0002_10</code></pre>
|
||
<p>地址字节按 MSB first 输出。payload 来自:</p>
|
||
<pre><code class="language-verilog">cmd_wdata[8*n +: 8]</code></pre>
|
||
<p>即 <code>cmd_wdata[7:0]</code> 是第一个 payload byte。</p>
|
||
<h3 id="7-3-spi-状态机"><a class="anchor" href="#7-3-spi-状态机" aria-label="章节链接">#</a>7.3 SPI 状态机</h3>
|
||
<div class="table-wrap"><table><thead><tr><th>状态</th><th>行为</th></tr></thead><tbody><tr><td><code>IDLE_ST</code></td><td><code>cmd_ready=1</code>,<code>csn=1</code>,<code>sck=0</code>。若 <code>cmd_valid=1</code>,锁存 frame 并进入传输。</td></tr><tr><td><code>TRANS_ST</code></td><td>每 <code>CLK_DIV</code> 个 <code>clk</code> 翻转一次 <code>sck</code>;上升沿采样读数据;下降沿准备下一位输出。</td></tr><tr><td><code>DONE_ST</code></td><td><code>csn</code> 拉高,<code>done</code> 脉冲 1 拍,<code>busy</code> 拉低,返回 idle。</td></tr></tbody></table></div>
|
||
<p>SPI 时钟频率:</p>
|
||
<pre><code class="language-text">f_sck = f_clk / (2 * CLK_DIV)</code></pre>
|
||
<p>testbench 中:</p>
|
||
<pre><code class="language-text">f_clk = 100 MHz
|
||
CLK_DIV = 2
|
||
f_sck = 25 MHz</code></pre>
|
||
<p>该值用于加速仿真。实际硬件中应根据 CSI040 SPI 时序要求重新设置 <code>SPI_CLK_DIV</code>。</p>
|
||
<h3 id="7-4-读数据采样"><a class="anchor" href="#7-4-读数据采样" aria-label="章节链接">#</a>7.4 读数据采样</h3>
|
||
<p>SPI master 在 header 发送完之后开始采样 <code>so</code>:</p>
|
||
<pre><code class="language-text">HEADER_BITS = 8 * (1 + ADDR_BYTES)</code></pre>
|
||
<p>默认地址 2 字节时,header 为 24 bit。读数据从第 25 个 bit 开始采样,采样结果移入 <code>rx_shift</code>,帧结束时赋给 <code>cmd_rdata</code>。</p>
|
||
<h2 id="8-复位行为"><a class="anchor" href="#8-复位行为" aria-label="章节链接">#</a>8. 复位行为</h2>
|
||
<p><code>rst_n = 0</code> 时,<code>csi040_ctrl</code> 主要寄存器复位如下:</p>
|
||
<div class="table-wrap"><table><thead><tr><th>寄存器</th><th style="text-align:right">复位值</th></tr></thead><tbody><tr><td><code>direct_addr</code></td><td style="text-align:right"><code>0</code></td></tr><tr><td><code>direct_wdata</code></td><td style="text-align:right"><code>0</code></td></tr><tr><td><code>direct_len</code></td><td style="text-align:right"><code>1</code></td></tr><tr><td><code>direct_write</code></td><td style="text-align:right"><code>1</code></td></tr><tr><td><code>control_shadow</code></td><td style="text-align:right"><code>0</code></td></tr><tr><td><code>ch_enable_shadow</code></td><td style="text-align:right"><code>0</code></td></tr><tr><td><code>ch_index</code></td><td style="text-align:right"><code>0</code></td></tr><tr><td><code>ch_data</code></td><td style="text-align:right"><code>0</code></td></tr><tr><td><code>ch_offset</code></td><td style="text-align:right"><code>0</code></td></tr><tr><td><code>seq_id</code></td><td style="text-align:right"><code>SEQ_IDLE</code></td></tr><tr><td><code>seq_step</code></td><td style="text-align:right"><code>0</code></td></tr><tr><td><code>seq_active</code></td><td style="text-align:right"><code>0</code></td></tr><tr><td><code>error_busy</code></td><td style="text-align:right"><code>0</code></td></tr><tr><td><code>done_latched</code></td><td style="text-align:right"><code>0</code></td></tr><tr><td><code>last_rdata</code></td><td style="text-align:right"><code>0</code></td></tr><tr><td><code>last_addr</code></td><td style="text-align:right"><code>0</code></td></tr><tr><td><code>asic_rst_n</code></td><td style="text-align:right"><code>1</code></td></tr><tr><td><code>asic_trig</code></td><td style="text-align:right"><code>0</code></td></tr></tbody></table></div>
|
||
<p><code>csi040_spi_master</code> 复位后:</p>
|
||
<pre><code class="language-text">csn = 1
|
||
sck = 0
|
||
si = 0
|
||
done = 0
|
||
busy = 0
|
||
state = IDLE_ST</code></pre>
|
||
<h2 id="9-当前-testbench-覆盖"><a class="anchor" href="#9-当前-testbench-覆盖" aria-label="章节链接">#</a>9. 当前 Testbench 覆盖</h2>
|
||
<p><code>csi040_ctrl_tb</code> 实例化 DUT 时重载:</p>
|
||
<pre><code class="language-verilog">.SPI_CLK_DIV ( 2 ),
|
||
.SPI_OPCODE_WRITE ( 8'h02 ),
|
||
.SPI_OPCODE_READ ( 8'h03 )</code></pre>
|
||
<p>testbench 使用 100 MHz clock,复位 8 个周期后开始测试。</p>
|
||
<p>覆盖场景:</p>
|
||
<div class="table-wrap"><table><thead><tr><th>测试项</th><th>BRAM 操作</th><th>期望 SPI 帧</th></tr></thead><tbody><tr><td>控制寄存器写</td><td><code>write_bram(0x0020, 0x10)</code></td><td><code>0x02000210</code></td></tr><tr><td>通道使能低字节</td><td><code>write_bram(0x0024, 0xA55A)</code></td><td><code>0x0200075A</code></td></tr><tr><td>通道使能高字节</td><td>同上</td><td><code>0x020008A5</code></td></tr><tr><td>通道 3 Current A</td><td><code>CH_INDEX=3</code>,<code>CH_CUR_A=0x7F</code></td><td><code>0x0200417F</code></td></tr><tr><td>DCS unlock step0</td><td><code>SEQ_CMD=1</code></td><td><code>0x02000955</code></td></tr><tr><td>DCS unlock step1</td><td>同上</td><td><code>0x020009B3</code></td></tr><tr><td>DCS unlock step2</td><td>同上</td><td><code>0x0200090A</code></td></tr><tr><td>Direct read Part ID</td><td><code>SPI_ADDR=0x011D</code>,<code>SPI_CTRL=0x0100</code></td><td><code>0x03011D00</code></td></tr><tr><td>读回检查</td><td>ASIC 模型返回 <code>0x28</code></td><td><code>SPI_RDATA[7:0] == 0x28</code></td></tr></tbody></table></div>
|
||
<p>testbench 中 SPI monitor 的行为:</p>
|
||
<ul><li><code>negedge asic_csn</code> 清空 bit 计数和 shift 寄存器;</li><li><code>posedge asic_sck</code> 采样 <code>asic_si</code>;</li><li><code>posedge asic_csn</code> 锁存完整帧;</li><li>在读数据阶段从 <code>asic_so</code> 返回 <code>read_response = 8'h28</code>。</li></ul>
|
||
<h2 id="10-仿真与波形"><a class="anchor" href="#10-仿真与波形" aria-label="章节链接">#</a>10. 仿真与波形</h2>
|
||
<p>已验证的命令:</p>
|
||
<pre><code class="language-powershell">& 'C:\Xilinx\Vivado\2023.1\bin\xvlog.bat' `
|
||
c64_pro.srcs/sources_1/rtl/csi040/csi040_spi_master.v `
|
||
c64_pro.srcs/sources_1/rtl/csi040/csi040_ctrl.v `
|
||
c64_pro.srcs/sources_1/rtl/csi040/csi040_ctrl_tb.v
|
||
|
||
& 'C:\Xilinx\Vivado\2023.1\bin\xelab.bat' csi040_ctrl_tb -debug typical -snapshot csi040_ctrl_tb_sim_dbg
|
||
|
||
& 'C:\Xilinx\Vivado\2023.1\bin\xsim.bat' csi040_ctrl_tb_sim_dbg -runall</code></pre>
|
||
<p>当前仿真结果:</p>
|
||
<pre><code class="language-text">$finish called at time : 10940 ns</code></pre>
|
||
<p>未触发 testbench 中的 <code>ERROR ... $stop</code>。</p>
|
||
<p>打开 GUI:</p>
|
||
<pre><code class="language-powershell">& 'C:\Xilinx\Vivado\2023.1\bin\xsim.bat' csi040_ctrl_tb_sim_dbg -gui</code></pre>
|
||
<p>GUI 中可用:</p>
|
||
<pre><code class="language-tcl">log_wave -r /csi040_ctrl_tb/*
|
||
run all</code></pre>
|
||
<p>建议观察信号:</p>
|
||
<pre><code class="language-text">/csi040_ctrl_tb/bram_addr
|
||
/csi040_ctrl_tb/bram_din
|
||
/csi040_ctrl_tb/bram_dout
|
||
/csi040_ctrl_tb/bram_en
|
||
/csi040_ctrl_tb/bram_we
|
||
/csi040_ctrl_tb/busy
|
||
/csi040_ctrl_tb/spi_done
|
||
/csi040_ctrl_tb/error_busy
|
||
/csi040_ctrl_tb/asic_csn
|
||
/csi040_ctrl_tb/asic_sck
|
||
/csi040_ctrl_tb/asic_si
|
||
/csi040_ctrl_tb/asic_so
|
||
/csi040_ctrl_tb/frame_in
|
||
/csi040_ctrl_tb/frame_bits
|
||
/csi040_ctrl_tb/dut/seq_id
|
||
/csi040_ctrl_tb/dut/seq_step
|
||
/csi040_ctrl_tb/dut/seq_active
|
||
/csi040_ctrl_tb/dut/spi_cmd_valid
|
||
/csi040_ctrl_tb/dut/spi_cmd_ready
|
||
/csi040_ctrl_tb/dut/spi_cmd_addr
|
||
/csi040_ctrl_tb/dut/spi_cmd_wdata
|
||
/csi040_ctrl_tb/dut/spi_cmd_len
|
||
/csi040_ctrl_tb/dut/u_csi040_spi_master/cstate
|
||
/csi040_ctrl_tb/dut/u_csi040_spi_master/tx_shift
|
||
/csi040_ctrl_tb/dut/u_csi040_spi_master/rx_shift
|
||
/csi040_ctrl_tb/dut/u_csi040_spi_master/sampled_bits</code></pre>
|
||
<h2 id="11-软件使用建议"><a class="anchor" href="#11-软件使用建议" aria-label="章节链接">#</a>11. 软件使用建议</h2>
|
||
<p>典型写配置流程:</p>
|
||
<pre><code class="language-text">1. 轮询 STATUS[5] == 0
|
||
2. 写目标 BRAM 命令寄存器
|
||
3. 轮询 STATUS[7] == 1
|
||
4. 如是读命令,读取 SPI_RDATA
|
||
5. 写 STATUS[0] = 1 清除 done_latched
|
||
6. 如 STATUS[8] = 1,记录错误并写 STATUS[1] = 1 清除 error_busy</code></pre>
|
||
<p>注意事项:</p>
|
||
<ul><li>忙时不要写命令寄存器,否则命令会被丢弃,并置位 <code>error_busy</code>。</li><li><code>PIN_CTRL</code> 是直接引脚控制,不会自动生成 SPI。</li><li><code>TRIGGER</code> 是 SPI 写 ASIC trigger register,不等同于 <code>asic_trig</code> 引脚。</li><li><code>SPI_CTRL[15:8]</code> 为直接 SPI payload 长度,最大有效值受 <code>MAX_BURST_BYTES</code> 限制。</li><li>当前 BRAM 接口只支持全字写。</li></ul>
|
||
<h2 id="12-后续可改进点"><a class="anchor" href="#12-后续可改进点" aria-label="章节链接">#</a>12. 后续可改进点</h2>
|
||
<ul><li>明确 <code>bram_clk/bram_rst</code> 的设计意图:要么真正使用它们,要么在接口说明中标记为 unused。</li><li>增加 byte enable 写支持,方便接 AXI BRAM Controller。</li><li>为 <code>STATUS</code> 位定义软件头文件常量,避免软件侧硬编码 bit 位置。</li><li>扩展 testbench:</li><li>忙时写命令,检查 <code>error_busy</code>;</li><li><code>PIN_CTRL</code> 读写;</li><li><code>TRIGGER</code> SPI 序列;</li><li>Test unlock 和 mode exit;</li><li>Direct SPI 多字节 burst;</li><li><code>cmd_len=0</code>、超过 <code>MAX_BURST_BYTES</code> 的边界行为;</li><li>SPI read 多字节返回数据。</li><li>增加 SystemVerilog assertion,用来约束命令重入、SPI 帧长度和 <code>csn/sck</code> 时序。</li></ul>
|
||
</article>
|
||
</main>
|
||
</div>
|
||
<a class="top-link" href="#top">返回顶部</a>
|
||
</body>
|
||
</html>
|
||
|