# CSI040 控制模块 Datasheet ## 1. 概述 `csi040_ctrl` 是用于控制 CSI040 ASIC 的 PL 侧控制模块。模块对上层软件暴露一组 32 bit BRAM 风格寄存器,并把软件写寄存器的动作转换为 CSI040 SPI 总线访问。 该模块主要完成: - CSI040 ASIC 控制寄存器写入 - 16 通道 enable 配置 - 单通道参数寄存器写入 - DCS/Test 模式 key 序列写入 - Direct SPI 读写 - ASIC reset/trigger 引脚直接控制 - SPI busy/done/error 状态反馈 相关 RTL 文件: - `c64_pro.srcs/sources_1/rtl/csi040/csi040_ctrl.v` - `c64_pro.srcs/sources_1/rtl/csi040/csi040_spi_master.v` - `c64_pro.srcs/sources_1/rtl/csi040/csi040_ctrl_tb.v` ## 2. 模块框图 ```text +------------------------------+ BRAM register | | SPI command access -------->| csi040_ctrl |----------------+ | | | | register map / shadow regs | v | sequence engine | +--------------------+ | busy/done/error status | | csi040_spi_master | +------------------------------+ +---------+----------+ | | | | | direct pins | SPI pins v v v asic_rst_n asic_trig asic_csn/asic_sck/asic_si/asic_so ``` ## 3. 主要特性 | 项目 | 规格 | | --- | --- | | 软件接口 | 32 bit BRAM 风格寄存器接口 | | BRAM 地址宽度 | 13 bit | | 支持写类型 | 仅支持 `bram_we == 4'hF` 的整字写 | | 支持读类型 | `bram_en == 1` 且 `bram_we == 4'h0` | | ASIC 地址宽度 | 默认 16 bit | | SPI 地址字节数 | 默认 2 byte | | Direct SPI 最大 payload | 默认 4 byte,由 `MAX_BURST_BYTES` 决定 | | SPI SCK 极性 | idle low | | SPI CSN 极性 | active low | | SPI 输出位序 | opcode/address/data 均 MSB first 按帧发送;payload byte0 来自低 8 bit | | 忙保护 | 忙时写命令寄存器不会启动新命令,并置位 `error_busy` | | 复位 | `rst_n` 异步低有效 | ## 4. 参数 ### 4.1 `csi040_ctrl` 参数 | 参数 | 默认值 | 说明 | | --- | ---: | --- | | `VERSION` | `32'h04040001` | 模块版本号,读 `0x0000` 返回 | | `SPI_CLK_DIV` | `50` | SPI SCK 半周期分频,`f_sck = f_clk / (2 * SPI_CLK_DIV)` | | `REG_ADDR_WIDTH` | `16` | ASIC 寄存器地址宽度 | | `SPI_ADDR_BYTES` | `2` | SPI 帧中地址字段字节数 | | `MAX_BURST_BYTES` | `4` | Direct SPI 最大 payload 字节数 | | `SPI_OPCODE_WRITE` | `8'h00` | SPI 写 opcode | | `SPI_OPCODE_READ` | `8'h80` | SPI 读 opcode | | `CSI_REG_CONTROL` | `16'h0002` | CSI040 control register 地址 | | `CSI_REG_CH_ENABLE1` | `16'h0007` | channel 0-7 enable 地址 | | `CSI_REG_CH_ENABLE2` | `16'h0008` | channel 8-15 enable 地址 | | `CSI_REG_TEST_DCS_KEY` | `16'h0009` | DCS/Test key register 地址 | | `CSI_CH_BASE` | `16'h0010` | 通道寄存器基地址 | | `CSI_CH_STRIDE` | `16'h0010` | 通道寄存器地址步进 | | `CSI_REG_STIM_TRIGGER` | `16'h0110` | stimulation trigger register 地址 | | `CSI_REG_PART_ID` | `16'h011D` | part ID register 地址 | | `CSI_REG_REVISION` | `16'h011E` | revision register 地址 | ### 4.2 `MAX_BURST_BYTES` 说明 `MAX_BURST_BYTES` 同时影响以下信号宽度和行为: | 位置 | 影响 | | --- | --- | | `spi_cmd_wdata` | 宽度为 `8 * MAX_BURST_BYTES` | | `spi_cmd_rdata` | 宽度为 `8 * MAX_BURST_BYTES` | | SPI master 内部 frame | 最大帧长为 `8 * (1 + SPI_ADDR_BYTES + MAX_BURST_BYTES)` | | Direct SPI 长度 | `SPI_CTRL[15:8]` 超过 `MAX_BURST_BYTES` 时会被截断 | 高层命令序列目前都只发 1 byte payload;`MAX_BURST_BYTES` 主要服务于 Direct SPI burst 访问。 ## 5. 端口说明 ### 5.1 时钟复位 | 信号 | 方向 | 位宽 | 说明 | | --- | --- | ---: | --- | | `clk` | input | 1 | 主时钟,模块内部逻辑均使用此时钟 | | `rst_n` | input | 1 | 异步低有效复位 | | `bram_clk` | input | 1 | 当前 RTL 未使用,保留接口 | | `bram_rst` | input | 1 | 当前 RTL 未使用,保留接口 | 注意:当前实现中 `bram_clk/bram_rst` 未参与逻辑,BRAM 访问必须与 `clk` 同步。 ### 5.2 BRAM 寄存器接口 | 信号 | 方向 | 位宽 | 说明 | | --- | --- | ---: | --- | | `bram_addr` | input | 13 | BRAM 字节地址 | | `bram_din` | input | 32 | 写数据 | | `bram_dout` | output reg | 32 | 读数据 | | `bram_en` | input | 1 | BRAM 访问使能 | | `bram_we` | input | 4 | 写 strobe;仅 `4'hF` 识别为写,`4'h0` 识别为读 | ### 5.3 状态输出 | 信号 | 方向 | 位宽 | 说明 | | --- | --- | ---: | --- | | `busy` | output | 1 | 模块忙。为 1 时不要写命令寄存器 | | `spi_done` | output | 1 | SPI master 完成脉冲,持续 1 个 `clk` | | `error_busy` | output reg | 1 | 忙时启动命令产生的粘滞错误标志 | ### 5.4 ASIC 引脚 | 信号 | 方向 | 位宽 | 说明 | | --- | --- | ---: | --- | | `asic_rst_n` | output reg | 1 | ASIC reset 引脚,软件通过 `PIN_CTRL[0]` 控制 | | `asic_trig` | output reg | 1 | ASIC trig 引脚,软件通过 `PIN_CTRL[1]` 控制 | | `asic_csn` | output | 1 | SPI CSN,低有效 | | `asic_sck` | output | 1 | SPI SCK,空闲低 | | `asic_si` | output | 1 | SPI master 输出到 ASIC | | `asic_so` | input | 1 | ASIC 输出到 SPI master | ## 6. BRAM 寄存器映射 | 地址 | 名称 | 访问 | 说明 | | ---: | --- | --- | --- | | `13'h0000` | `VERSION` | R | 返回 `VERSION` | | `13'h0004` | `STATUS` | R/W1C | 状态寄存器;写 bit0 清 done,写 bit1 清 error | | `13'h0008` | `SPI_ADDR` | R/W | Direct SPI 目标 ASIC 地址 | | `13'h000C` | `SPI_WDATA` | R/W | Direct SPI 写数据 | | `13'h0010` | `SPI_CTRL` | W | Direct SPI 启动控制 | | `13'h0014` | `SPI_RDATA` | R | 最近一次 SPI 读回数据 | | `13'h0018` | `PIN_CTRL` | R/W | 直接控制 `asic_rst_n/asic_trig` | | `13'h001C` | `LAST_ADDR` | R | 最近一次完成 SPI 操作的 ASIC 地址 | | `13'h0020` | `CONTROL` | R/W | 写 CSI040 control register | | `13'h0024` | `CH_ENABLE` | R/W | 写 channel enable1/2 | | `13'h0028` | `TRIGGER` | W | 向 ASIC trigger register 写 `8'hAA` | | `13'h002C` | `SEQ_CMD` | W | 特殊序列命令 | | `13'h0040` | `CH_INDEX` | R/W | 选择通道编号 | | `13'h0044` | `CH_MISC` | W | 当前通道 offset `0x0` | | `13'h0048` | `CH_CUR_A` | W | 当前通道 offset `0x1` | | `13'h004C` | `CH_CUR_B` | W | 当前通道 offset `0x2` | | `13'h0050` | `CH_CUR_C` | W | 当前通道 offset `0x3` | | `13'h0054` | `CH_CUR_D` | W | 当前通道 offset `0x4` | | `13'h0058` | `CH_STIM_PW` | W | 当前通道 offset `0x5` | | `13'h005C` | `CH_DZ0` | W | 当前通道 offset `0x6` | | `13'h0060` | `CH_DZ1` | W | 当前通道 offset `0x7` | | `13'h0064` | `CH_SKIP_AB` | W | 当前通道 offset `0x8` | | `13'h0068` | `CH_SKIP_CD` | W | 当前通道 offset `0x9` | | `13'h006C` | `CH_STIM_NUM` | W | 当前通道 offset `0xA` | | `13'h0070` | `CH_DZ2` | W | 当前通道 offset `0xB` | | `13'h0074` | `CH_THERAPY_DELAY` | W | 当前通道 offset `0xC` | | `13'h0078` | `CH_THERAPY_NUM` | W | 当前通道 offset `0xD` | | `13'h007C` | `CH_DZ3` | W | 当前通道 offset `0xE` | | `13'h0080` | `CH_MASTER_NUM` | W | 当前通道 offset `0xF` | ## 7. 寄存器位定义 ### 7.1 `STATUS` 寄存器 `STATUS` 读回值由 RTL 拼接得到: ```verilog {16'd0, seq_id, seq_step, error_busy, done_latched, spi_cmd_ready, ctrl_busy} ``` 赋值给 32 bit `bram_dout` 时左侧补 0。软件可按以下位使用: | 位 | 名称 | 说明 | | ---: | --- | --- | | `[5]` | `ctrl_busy` | 当前模块忙 | | `[6]` | `spi_cmd_ready` | SPI master 空闲可接收命令 | | `[7]` | `done_latched` | 至少完成过一次 SPI 操作 | | `[8]` | `error_busy` | 忙时写命令错误 | | `[11:9]` | `seq_step` | 当前序列步骤 | | `[15:12]` | `seq_id` | 当前序列编号 | 写 `STATUS` 清除粘滞位: | 写 bit | 行为 | | ---: | --- | | `[0] = 1` | 清除 `done_latched` | | `[1] = 1` | 清除 `error_busy` | ### 7.2 `SPI_CTRL` 寄存器 | 位 | 名称 | 说明 | | ---: | --- | --- | | `[0]` | `direct_write` | 1:Direct SPI 写;0:Direct SPI 读 | | `[15:8]` | `direct_len` | payload 字节数;0 会被转换为 1 | | 其他 | 保留 | 当前未使用 | `direct_len` 最终会在 SPI master 内限制到 `1..MAX_BURST_BYTES`。 ### 7.3 `PIN_CTRL` 寄存器 | 位 | 名称 | 说明 | | ---: | --- | --- | | `[0]` | `asic_rst_n` | 直接驱动 ASIC reset_n | | `[1]` | `asic_trig` | 直接驱动 ASIC trig | | 其他 | 保留 | 当前读回为 0 | ### 7.4 `SEQ_CMD` 寄存器 | 位 | 命令 | SPI 行为 | | ---: | --- | --- | | `[0]` | DCS unlock | 向 `0x0009` 依次写 `55h, B3h, 0Ah` | | `[1]` | Test unlock | 向 `0x0009` 依次写 `AAh, 4Ch, F5h` | | `[2]` | Mode exit | 向 `0x0009` 写 `F9h` | | `[3]` | Control quick write | 设置 `control_shadow = 8'h10` 并写 `0x0002` | | `[4]` | Control quick write | 设置 `control_shadow = 8'h08` 并写 `0x0002` | 若多个 bit 同时为 1,优先级从 bit0 到 bit4。 ## 8. 命令行为 ### 8.1 `CONTROL` 写 `BRAM_ADDR_CONTROL`: ```text control_shadow <= bram_din[7:0] SPI write addr 0x0002, data control_shadow, len 1 ``` ### 8.2 `CH_ENABLE` 写 `BRAM_ADDR_CH_ENABLE`: ```text ch_enable_shadow <= bram_din[15:0] step0: SPI write addr 0x0007, data ch_enable_shadow[7:0] step1: SPI write addr 0x0008, data ch_enable_shadow[15:8] ``` ### 8.3 通道参数写 通道参数地址计算: ```text asic_addr = CSI_CH_BASE + CH_INDEX * CSI_CH_STRIDE + channel_offset ``` 默认: ```text asic_addr = 0x0010 + CH_INDEX * 0x0010 + channel_offset ``` 示例: ```text CH_INDEX = 3 写 CH_CUR_A = 0x7F channel_offset = 0x1 asic_addr = 0x0041 ``` ### 8.4 Direct SPI Direct SPI 使用 `SPI_ADDR/SPI_WDATA/SPI_CTRL`: ```text 1. 写 SPI_ADDR 设置 ASIC 地址 2. 写 SPI_WDATA 设置写数据 3. 写 SPI_CTRL 启动 direct transfer 4. 读操作完成后,从 SPI_RDATA 取数据 ``` ## 9. SPI 时序规格 ### 9.1 SCK 频率 ```text f_sck = f_clk / (2 * SPI_CLK_DIV) ``` 示例: | `f_clk` | `SPI_CLK_DIV` | `f_sck` | | ---: | ---: | ---: | | 100 MHz | 50 | 1 MHz | | 100 MHz | 25 | 2 MHz | | 100 MHz | 10 | 5 MHz | | 100 MHz | 5 | 10 MHz | ### 9.2 SPI 帧格式 默认 `SPI_ADDR_BYTES = 2`: ```text byte0 opcode byte1 address high byte byte2 address low byte byte3.. payload byte0..N ``` 写 1 byte 示例: ```text opcode = 0x02 addr = 0x0002 data = 0x10 frame = 0x02_0002_10 ``` ### 9.3 SPI bit 数 ```text frame_bits = 8 * (1 + SPI_ADDR_BYTES + payload_len) ``` 默认地址 2 byte 时: | payload_len | frame_bits | | ---: | ---: | | 1 | 32 | | 2 | 40 | | 3 | 48 | | 4 | 56 | ### 9.4 传输时间估算 不计状态机少量额外周期时: ```text transfer_time ≈ frame_bits * 2 * SPI_CLK_DIV * clk_period ``` 100 MHz、`SPI_CLK_DIV = 50`、1 byte payload: ```text frame_bits = 32 transfer_time ≈ 32 * 2 * 50 * 10 ns = 32 us ``` ## 10. 软件推荐流程 ### 10.1 写普通命令 ```text 1. 轮询 STATUS[5] == 0,确认不忙 2. 写目标命令寄存器 3. 轮询 STATUS[7] == 1,确认完成 4. 写 STATUS[0] = 1,清除 done_latched 5. 若 STATUS[8] == 1,记录错误并写 STATUS[1] = 1 清除 ``` ### 10.2 Direct read 示例 读取 CSI040 Part ID: ```text write SPI_ADDR = 0x0000011D write SPI_CTRL = 0x00000100 // bit0=0 read, len=1 wait done read SPI_RDATA[7:0] ``` testbench 中 ASIC 模型返回 `8'h28`。 ### 10.3 Direct write 示例 向 ASIC `0x0002` 写 `0x10`: ```text write SPI_ADDR = 0x00000002 write SPI_WDATA = 0x00000010 write SPI_CTRL = 0x00000101 // bit0=1 write, len=1 wait done ``` ## 11. 复位默认值 | 项目 | 复位值 | | --- | ---: | | `direct_addr` | `0` | | `direct_wdata` | `0` | | `direct_len` | `1` | | `direct_write` | `1` | | `control_shadow` | `0` | | `ch_enable_shadow` | `0` | | `ch_index` | `0` | | `seq_id` | `SEQ_IDLE` | | `seq_step` | `0` | | `seq_active` | `0` | | `error_busy` | `0` | | `done_latched` | `0` | | `last_rdata` | `0` | | `last_addr` | `0` | | `asic_rst_n` | `1` | | `asic_trig` | `0` | | `asic_csn` | `1` | | `asic_sck` | `0` | | `asic_si` | `0` | ## 12. 集成注意事项 - `bram_clk` 和 `bram_rst` 当前未使用,BRAM 访问必须同步到 `clk`。 - 只支持整字写,软件侧需要保证 `bram_we == 4'hF`。 - `busy = 1` 时不要写命令寄存器,否则命令被丢弃并置位 `error_busy`。 - `TRIGGER` 寄存器是通过 SPI 写 ASIC trigger register;`PIN_CTRL[1]` 是直接驱动 `asic_trig` 引脚,两者不是同一个功能。 - `SPI_OPCODE_WRITE/READ` 需要与最终 CSI040 SPI 协议匹配。当前 testbench 使用 `8'h02/8'h03`。 - 若系统时钟不是 100 MHz,需要重新计算 `SPI_CLK_DIV`,保证 CSI040 SPI 时序满足要求。 - Direct SPI 多字节读写最大长度由 `MAX_BURST_BYTES` 控制,默认最多 4 byte。 ## 13. 验证状态 当前 testbench 覆盖: | 测试项 | 期望 | | --- | --- | | CONTROL 写 | SPI frame `0x02000210` | | CH_ENABLE 写 | SPI frame `0x0200075A` 和 `0x020008A5` | | CH3 CUR_A 写 | SPI frame `0x0200417F` | | DCS unlock | SPI frame `0x02000955/0x020009B3/0x0200090A` | | Direct read Part ID | SPI frame `0x03011D00`,读回 `0x28` | 已运行: ```text xvlog: pass xelab: pass xsim : $finish at 10940 ns ```