573 lines
27 KiB
Verilog
573 lines
27 KiB
Verilog
`timescale 1 ns / 1 ps
|
||
|
||
//------------------------------------------------------------------------------
|
||
// CSI040 ASIC 控制模块。
|
||
//
|
||
// 这个模块对软件暴露一组 BRAM 风格寄存器。软件写寄存器时,
|
||
// 要么更新本地 shadow 寄存器,要么启动一个高层命令序列;
|
||
// 高层命令序列最终会被转换成一次或多次 CSI040 SPI 传输。
|
||
//
|
||
// 软件侧:
|
||
// bram_* - 简单 32 bit 寄存器访问口
|
||
// busy - 命令引擎忙,暂时不能接收新命令
|
||
// error_busy - 忙时写命令产生的粘滞错误标志
|
||
//
|
||
// ASIC 侧:
|
||
// asic_csn/sck/si/so - 由 csi040_spi_master 驱动的 SPI 总线
|
||
// asic_rst_n/trig - 由 BRAM PIN_CTRL 直接控制的引脚
|
||
//------------------------------------------------------------------------------
|
||
module csi040_ctrl#
|
||
(
|
||
// 通用 SPI/ASIC 参数配置。
|
||
parameter VERSION = 32'h04040001,
|
||
parameter SPI_CLK_DIV = 50,
|
||
parameter REG_ADDR_WIDTH = 16,
|
||
parameter SPI_ADDR_BYTES = 2,
|
||
parameter MAX_BURST_BYTES = 4,
|
||
parameter [7:0] SPI_OPCODE_WRITE = 8'h00,
|
||
parameter [7:0] SPI_OPCODE_READ = 8'h80,
|
||
|
||
// 命令引擎会用到的 CSI040 ASIC 内部寄存器地址。
|
||
parameter [REG_ADDR_WIDTH-1:0] CSI_REG_STATUS1 = 16'h0000,
|
||
parameter [REG_ADDR_WIDTH-1:0] CSI_REG_STATUS2 = 16'h0001,
|
||
parameter [REG_ADDR_WIDTH-1:0] CSI_REG_CONTROL = 16'h0002,
|
||
parameter [REG_ADDR_WIDTH-1:0] CSI_REG_INTERRUPT = 16'h0003,
|
||
parameter [REG_ADDR_WIDTH-1:0] CSI_REG_Z_MEAS_SAMP = 16'h0004,
|
||
parameter [REG_ADDR_WIDTH-1:0] CSI_REG_Z_CURRENT_CAL = 16'h0005,
|
||
parameter [REG_ADDR_WIDTH-1:0] CSI_REG_Z_DELAY_RANGE = 16'h0006,
|
||
parameter [REG_ADDR_WIDTH-1:0] CSI_REG_CH_ENABLE1 = 16'h0007,
|
||
parameter [REG_ADDR_WIDTH-1:0] CSI_REG_CH_ENABLE2 = 16'h0008,
|
||
parameter [REG_ADDR_WIDTH-1:0] CSI_REG_TEST_DCS_KEY = 16'h0009,
|
||
parameter [REG_ADDR_WIDTH-1:0] CSI_REG_TEST_MODE = 16'h000A,
|
||
parameter [REG_ADDR_WIDTH-1:0] CSI_REG_CUR_REF_TRIM = 16'h000B,
|
||
parameter [REG_ADDR_WIDTH-1:0] CSI_CH_BASE = 16'h0010,
|
||
parameter [REG_ADDR_WIDTH-1:0] CSI_CH_STRIDE = 16'h0010,
|
||
parameter [REG_ADDR_WIDTH-1:0] CSI_REG_STIM_TRIGGER = 16'h0110,
|
||
parameter [REG_ADDR_WIDTH-1:0] CSI_REG_PART_ID = 16'h011D,
|
||
parameter [REG_ADDR_WIDTH-1:0] CSI_REG_REVISION = 16'h011E
|
||
)
|
||
(
|
||
input clk,
|
||
input rst_n,
|
||
|
||
// BRAM 风格的软件寄存器接口。bram_clk/bram_rst 当前只是保留接口,
|
||
// 实际访问逻辑用 clk 采样。
|
||
input [12:0] bram_addr,
|
||
input bram_clk,
|
||
input [31:0] bram_din,
|
||
output reg [31:0] bram_dout,
|
||
input bram_en,
|
||
input bram_rst,
|
||
input [3:0] bram_we,
|
||
|
||
output busy,
|
||
output spi_done,
|
||
output reg error_busy,
|
||
|
||
// ASIC 直接控制引脚和 SPI 引脚。
|
||
output reg asic_rst_n,
|
||
output reg asic_trig,
|
||
output asic_csn,
|
||
output asic_sck,
|
||
output asic_si,
|
||
input asic_so
|
||
);
|
||
|
||
//-------------------------------------------------------------------------
|
||
// 软件可见的 BRAM 寄存器地址映射。
|
||
//-------------------------------------------------------------------------
|
||
localparam BRAM_ADDR_VERSION = 13'h0000;
|
||
localparam BRAM_ADDR_STATUS = 13'h0004;
|
||
localparam BRAM_ADDR_SPI_ADDR = 13'h0008;
|
||
localparam BRAM_ADDR_SPI_WDATA = 13'h000C;
|
||
localparam BRAM_ADDR_SPI_CTRL = 13'h0010;
|
||
localparam BRAM_ADDR_SPI_RDATA = 13'h0014;
|
||
localparam BRAM_ADDR_PIN_CTRL = 13'h0018;
|
||
localparam BRAM_ADDR_LAST_ADDR = 13'h001C;
|
||
|
||
localparam BRAM_ADDR_CONTROL = 13'h0020;
|
||
localparam BRAM_ADDR_CH_ENABLE = 13'h0024;
|
||
localparam BRAM_ADDR_TRIGGER = 13'h0028;
|
||
localparam BRAM_ADDR_SEQ_CMD = 13'h002C;
|
||
|
||
localparam BRAM_ADDR_CH_INDEX = 13'h0040;
|
||
localparam BRAM_ADDR_CH_MISC = 13'h0044;
|
||
localparam BRAM_ADDR_CH_CUR_A = 13'h0048;
|
||
localparam BRAM_ADDR_CH_CUR_B = 13'h004C;
|
||
localparam BRAM_ADDR_CH_CUR_C = 13'h0050;
|
||
localparam BRAM_ADDR_CH_CUR_D = 13'h0054;
|
||
localparam BRAM_ADDR_CH_STIM_PW = 13'h0058;
|
||
localparam BRAM_ADDR_CH_DZ0 = 13'h005C;
|
||
localparam BRAM_ADDR_CH_DZ1 = 13'h0060;
|
||
localparam BRAM_ADDR_CH_SKIP_AB = 13'h0064;
|
||
localparam BRAM_ADDR_CH_SKIP_CD = 13'h0068;
|
||
localparam BRAM_ADDR_CH_STIM_NUM = 13'h006C;
|
||
localparam BRAM_ADDR_CH_DZ2 = 13'h0070;
|
||
localparam BRAM_ADDR_CH_THERAPY_DELAY = 13'h0074;
|
||
localparam BRAM_ADDR_CH_THERAPY_NUM = 13'h0078;
|
||
localparam BRAM_ADDR_CH_DZ3 = 13'h007C;
|
||
localparam BRAM_ADDR_CH_MASTER_NUM = 13'h0080;
|
||
|
||
// CSI040 每个通道寄存器窗口内的偏移。
|
||
// ASIC 地址 = CSI_CH_BASE + ch_index * CSI_CH_STRIDE + CH_OFF_*。
|
||
localparam CH_OFF_MISC = 4'h0;
|
||
localparam CH_OFF_CUR_A = 4'h1;
|
||
localparam CH_OFF_CUR_B = 4'h2;
|
||
localparam CH_OFF_CUR_C = 4'h3;
|
||
localparam CH_OFF_CUR_D = 4'h4;
|
||
localparam CH_OFF_STIM_PW = 4'h5;
|
||
localparam CH_OFF_DZ0 = 4'h6;
|
||
localparam CH_OFF_DZ1 = 4'h7;
|
||
localparam CH_OFF_SKIP_AB = 4'h8;
|
||
localparam CH_OFF_SKIP_CD = 4'h9;
|
||
localparam CH_OFF_STIM_NUM = 4'hA;
|
||
localparam CH_OFF_DZ2 = 4'hB;
|
||
localparam CH_OFF_THERAPY_DELAY = 4'hC;
|
||
localparam CH_OFF_THERAPY_NUM = 4'hD;
|
||
localparam CH_OFF_DZ3 = 4'hE;
|
||
localparam CH_OFF_MASTER_NUM = 4'hF;
|
||
|
||
// 高层命令序列编号。一个序列可能只发一帧 SPI,也可能发多帧,
|
||
// 例如通道使能、DCS 解锁等。
|
||
localparam SEQ_IDLE = 4'h0;
|
||
localparam SEQ_WRITE_CONTROL = 4'h1;
|
||
localparam SEQ_CH_ENABLE = 4'h2;
|
||
localparam SEQ_TRIGGER = 4'h3;
|
||
localparam SEQ_DCS_UNLOCK = 4'h4;
|
||
localparam SEQ_TEST_UNLOCK = 4'h5;
|
||
localparam SEQ_MODE_EXIT = 4'h6;
|
||
localparam SEQ_CH_WRITE = 4'h7;
|
||
localparam SEQ_DIRECT = 4'h8;
|
||
|
||
// 发给 SPI master 的抽象命令接口。
|
||
reg spi_cmd_valid;
|
||
reg spi_cmd_write;
|
||
reg [REG_ADDR_WIDTH-1:0] spi_cmd_addr;
|
||
reg [8*MAX_BURST_BYTES-1:0] spi_cmd_wdata;
|
||
reg [7:0] spi_cmd_len;
|
||
wire spi_cmd_ready;
|
||
wire [8*MAX_BURST_BYTES-1:0] spi_cmd_rdata;
|
||
wire spi_busy;
|
||
wire spi_master_done;
|
||
|
||
// Direct SPI 访问寄存器。软件想直接访问 ASIC 任意地址时,
|
||
// 通过 SPI_ADDR、SPI_WDATA、SPI_CTRL 配置这些寄存器。
|
||
reg [REG_ADDR_WIDTH-1:0] direct_addr;
|
||
reg [31:0] direct_wdata;
|
||
reg [7:0] direct_len;
|
||
reg direct_write;
|
||
reg [31:0] last_rdata;
|
||
reg [REG_ADDR_WIDTH-1:0] last_addr;
|
||
|
||
// shadow 配置寄存器和序列状态。
|
||
reg [7:0] control_shadow;
|
||
reg [15:0] ch_enable_shadow;
|
||
reg [3:0] ch_index;
|
||
reg [7:0] ch_data;
|
||
reg [3:0] ch_offset;
|
||
reg [3:0] seq_id;
|
||
reg [2:0] seq_step;
|
||
reg seq_active;
|
||
reg done_latched;
|
||
|
||
wire bram_wr_valid;
|
||
wire bram_rd_valid;
|
||
wire ctrl_busy;
|
||
|
||
// 当前只接受整字写。部分 byte enable 写会被忽略。
|
||
assign bram_wr_valid = bram_en && (bram_we == 4'hF);
|
||
assign bram_rd_valid = bram_en && (bram_we == 4'h0);
|
||
|
||
// 从序列启动开始,到 SPI master 和待发送命令脉冲都回到空闲前,
|
||
// busy 一直为 1。
|
||
assign ctrl_busy = seq_active || spi_busy || spi_cmd_valid;
|
||
assign busy = ctrl_busy;
|
||
assign spi_done = spi_master_done;
|
||
|
||
// 把选中的通道号和通道内偏移转换成 CSI040 ASIC 寄存器地址。
|
||
function [REG_ADDR_WIDTH-1:0] ch_addr;
|
||
input [3:0] ch;
|
||
input [3:0] offset;
|
||
begin
|
||
ch_addr = CSI_CH_BASE + (ch * CSI_CH_STRIDE) + offset;
|
||
end
|
||
endfunction
|
||
|
||
// 返回 CSI040 特殊模式序列需要写入的 key 字节。
|
||
// DCS 解锁: 55, B3, 0A
|
||
// Test 解锁: AA, 4C, F5
|
||
// 退出模式: F9
|
||
function [7:0] seq_key_data;
|
||
input [3:0] id;
|
||
input [2:0] step;
|
||
begin
|
||
case (id)
|
||
SEQ_DCS_UNLOCK: begin
|
||
case (step)
|
||
3'd0: seq_key_data = 8'h55;
|
||
3'd1: seq_key_data = 8'hB3;
|
||
default: seq_key_data = 8'h0A;
|
||
endcase
|
||
end
|
||
SEQ_TEST_UNLOCK: begin
|
||
case (step)
|
||
3'd0: seq_key_data = 8'hAA;
|
||
3'd1: seq_key_data = 8'h4C;
|
||
default: seq_key_data = 8'hF5;
|
||
endcase
|
||
end
|
||
default: seq_key_data = 8'hF9;
|
||
endcase
|
||
end
|
||
endfunction
|
||
|
||
// 底层 SPI 移位器。csi040_ctrl 只决定发什么命令;
|
||
// 具体串行时序都由该子模块完成。
|
||
csi040_spi_master#
|
||
(
|
||
.CLK_DIV ( SPI_CLK_DIV ),
|
||
.REG_ADDR_WIDTH ( REG_ADDR_WIDTH ),
|
||
.ADDR_BYTES ( SPI_ADDR_BYTES ),
|
||
.MAX_BURST_BYTES ( MAX_BURST_BYTES ),
|
||
.OPCODE_WRITE ( SPI_OPCODE_WRITE ),
|
||
.OPCODE_READ ( SPI_OPCODE_READ )
|
||
)
|
||
u_csi040_spi_master
|
||
(
|
||
.clk ( clk ),
|
||
.rst_n ( rst_n ),
|
||
|
||
.cmd_ready ( spi_cmd_ready ),
|
||
.cmd_valid ( spi_cmd_valid ),
|
||
.cmd_write ( spi_cmd_write ),
|
||
.cmd_addr ( spi_cmd_addr ),
|
||
.cmd_wdata ( spi_cmd_wdata ),
|
||
.cmd_len ( spi_cmd_len ),
|
||
|
||
.done ( spi_master_done ),
|
||
.cmd_rdata ( spi_cmd_rdata ),
|
||
.busy ( spi_busy ),
|
||
|
||
.csn ( asic_csn ),
|
||
.sck ( asic_sck ),
|
||
.si ( asic_si ),
|
||
.so ( asic_so )
|
||
);
|
||
|
||
//-------------------------------------------------------------------------
|
||
// 软件写解码、shadow 寄存器更新、序列状态维护。
|
||
//
|
||
// 这个 always 块做三件事:
|
||
// 1. 复位和维护本地 shadow 寄存器。
|
||
// 2. SPI 完成时,推进或结束当前高层序列。
|
||
// 3. 解码 BRAM 写操作,并在空闲时启动新命令序列。
|
||
//-------------------------------------------------------------------------
|
||
always @(posedge clk or negedge rst_n) begin
|
||
if (!rst_n) begin
|
||
direct_addr <= 0;
|
||
direct_wdata <= 0;
|
||
direct_len <= 8'd1;
|
||
direct_write <= 1'b1;
|
||
control_shadow <= 8'h00;
|
||
ch_enable_shadow <= 16'h0000;
|
||
ch_index <= 4'h0;
|
||
ch_data <= 8'h00;
|
||
ch_offset <= 4'h0;
|
||
seq_id <= SEQ_IDLE;
|
||
seq_step <= 0;
|
||
seq_active <= 1'b0;
|
||
error_busy <= 1'b0;
|
||
done_latched <= 1'b0;
|
||
last_rdata <= 0;
|
||
last_addr <= 0;
|
||
asic_rst_n <= 1'b1;
|
||
asic_trig <= 1'b0;
|
||
end else begin
|
||
// done_latched 是粘滞标志,直到软件写 STATUS[0] 才清除。
|
||
done_latched <= done_latched;
|
||
|
||
// 每完成一帧 SPI,要么结束单帧序列,要么推进多帧序列的 seq_step。
|
||
if (spi_master_done) begin
|
||
done_latched <= 1'b1;
|
||
last_rdata <= spi_cmd_rdata[31:0];
|
||
last_addr <= spi_cmd_addr;
|
||
|
||
case (seq_id)
|
||
SEQ_CH_ENABLE: begin
|
||
if (seq_step == 3'd1) begin
|
||
seq_active <= 1'b0;
|
||
seq_id <= SEQ_IDLE;
|
||
end else begin
|
||
seq_step <= seq_step + 1'b1;
|
||
end
|
||
end
|
||
SEQ_DCS_UNLOCK,
|
||
SEQ_TEST_UNLOCK: begin
|
||
if (seq_step == 3'd2) begin
|
||
seq_active <= 1'b0;
|
||
seq_id <= SEQ_IDLE;
|
||
end else begin
|
||
seq_step <= seq_step + 1'b1;
|
||
end
|
||
end
|
||
default: begin
|
||
seq_active <= 1'b0;
|
||
seq_id <= SEQ_IDLE;
|
||
end
|
||
endcase
|
||
end
|
||
|
||
// STATUS 的 done/error 位采用写 1 清除。
|
||
if (bram_wr_valid && (bram_addr == BRAM_ADDR_STATUS)) begin
|
||
done_latched <= done_latched & (~bram_din[0]);
|
||
error_busy <= error_busy & (~bram_din[1]);
|
||
end
|
||
|
||
if (bram_wr_valid) begin
|
||
case (bram_addr)
|
||
// Direct SPI 配置寄存器。写这些寄存器不会立刻发 SPI,
|
||
// 只有写 BRAM_ADDR_SPI_CTRL 才会启动传输。
|
||
BRAM_ADDR_SPI_ADDR: direct_addr <= bram_din[REG_ADDR_WIDTH-1:0];
|
||
BRAM_ADDR_SPI_WDATA: direct_wdata <= bram_din;
|
||
|
||
// 直接引脚控制,和 SPI trigger 命令是两条独立路径。
|
||
BRAM_ADDR_PIN_CTRL: begin
|
||
asic_rst_n <= bram_din[0];
|
||
asic_trig <= bram_din[1];
|
||
end
|
||
|
||
// 高层 CONTROL 写:先保存低 8 bit 到本地 shadow,
|
||
// 再发 SPI 写入 ASIC 的 CSI_REG_CONTROL。
|
||
BRAM_ADDR_CONTROL: begin
|
||
control_shadow <= bram_din[7:0];
|
||
if (!ctrl_busy) begin
|
||
seq_id <= SEQ_WRITE_CONTROL;
|
||
seq_step <= 0;
|
||
seq_active <= 1'b1;
|
||
end else begin
|
||
error_busy <= 1'b1;
|
||
end
|
||
end
|
||
|
||
// 通道使能被拆成 ASIC 寄存器 0x0007 和 0x0008,
|
||
// 所以这里启动一个两步序列。
|
||
BRAM_ADDR_CH_ENABLE: begin
|
||
ch_enable_shadow <= bram_din[15:0];
|
||
if (!ctrl_busy) begin
|
||
seq_id <= SEQ_CH_ENABLE;
|
||
seq_step <= 0;
|
||
seq_active <= 1'b1;
|
||
end else begin
|
||
error_busy <= 1'b1;
|
||
end
|
||
end
|
||
|
||
// SPI trigger 命令:向 CSI_REG_STIM_TRIGGER 写 0xAA。
|
||
BRAM_ADDR_TRIGGER: begin
|
||
if (!ctrl_busy) begin
|
||
seq_id <= SEQ_TRIGGER;
|
||
seq_step <= 0;
|
||
seq_active <= 1'b1;
|
||
end else begin
|
||
error_busy <= 1'b1;
|
||
end
|
||
end
|
||
|
||
// 特殊脚本由低 bit 选择:
|
||
// bit0 DCS 解锁,bit1 test 解锁,bit2 退出模式,
|
||
// bit3/bit4 快捷写控制寄存器。
|
||
BRAM_ADDR_SEQ_CMD: begin
|
||
if (!ctrl_busy) begin
|
||
seq_step <= 0;
|
||
seq_active <= 1'b1;
|
||
if (bram_din[0]) seq_id <= SEQ_DCS_UNLOCK;
|
||
else if (bram_din[1]) seq_id <= SEQ_TEST_UNLOCK;
|
||
else if (bram_din[2]) seq_id <= SEQ_MODE_EXIT;
|
||
else if (bram_din[3]) begin
|
||
seq_id <= SEQ_WRITE_CONTROL;
|
||
control_shadow <= 8'h10;
|
||
end else if (bram_din[4]) begin
|
||
seq_id <= SEQ_WRITE_CONTROL;
|
||
control_shadow <= 8'h08;
|
||
end else begin
|
||
seq_active <= 1'b0;
|
||
end
|
||
end else begin
|
||
error_busy <= 1'b1;
|
||
end
|
||
end
|
||
|
||
// 选择后续通道参数写使用的通道号。这里只更新本地寄存器。
|
||
BRAM_ADDR_CH_INDEX: ch_index <= bram_din[3:0];
|
||
|
||
// 任意通道参数写都会捕获低 8 bit,并把 BRAM 地址映射成
|
||
// CSI040 通道窗口内的 offset。
|
||
BRAM_ADDR_CH_MISC,
|
||
BRAM_ADDR_CH_CUR_A,
|
||
BRAM_ADDR_CH_CUR_B,
|
||
BRAM_ADDR_CH_CUR_C,
|
||
BRAM_ADDR_CH_CUR_D,
|
||
BRAM_ADDR_CH_STIM_PW,
|
||
BRAM_ADDR_CH_DZ0,
|
||
BRAM_ADDR_CH_DZ1,
|
||
BRAM_ADDR_CH_SKIP_AB,
|
||
BRAM_ADDR_CH_SKIP_CD,
|
||
BRAM_ADDR_CH_STIM_NUM,
|
||
BRAM_ADDR_CH_DZ2,
|
||
BRAM_ADDR_CH_THERAPY_DELAY,
|
||
BRAM_ADDR_CH_THERAPY_NUM,
|
||
BRAM_ADDR_CH_DZ3,
|
||
BRAM_ADDR_CH_MASTER_NUM: begin
|
||
ch_data <= bram_din[7:0];
|
||
case (bram_addr)
|
||
BRAM_ADDR_CH_MISC: ch_offset <= CH_OFF_MISC;
|
||
BRAM_ADDR_CH_CUR_A: ch_offset <= CH_OFF_CUR_A;
|
||
BRAM_ADDR_CH_CUR_B: ch_offset <= CH_OFF_CUR_B;
|
||
BRAM_ADDR_CH_CUR_C: ch_offset <= CH_OFF_CUR_C;
|
||
BRAM_ADDR_CH_CUR_D: ch_offset <= CH_OFF_CUR_D;
|
||
BRAM_ADDR_CH_STIM_PW: ch_offset <= CH_OFF_STIM_PW;
|
||
BRAM_ADDR_CH_DZ0: ch_offset <= CH_OFF_DZ0;
|
||
BRAM_ADDR_CH_DZ1: ch_offset <= CH_OFF_DZ1;
|
||
BRAM_ADDR_CH_SKIP_AB: ch_offset <= CH_OFF_SKIP_AB;
|
||
BRAM_ADDR_CH_SKIP_CD: ch_offset <= CH_OFF_SKIP_CD;
|
||
BRAM_ADDR_CH_STIM_NUM: ch_offset <= CH_OFF_STIM_NUM;
|
||
BRAM_ADDR_CH_DZ2: ch_offset <= CH_OFF_DZ2;
|
||
BRAM_ADDR_CH_THERAPY_DELAY: ch_offset <= CH_OFF_THERAPY_DELAY;
|
||
BRAM_ADDR_CH_THERAPY_NUM: ch_offset <= CH_OFF_THERAPY_NUM;
|
||
BRAM_ADDR_CH_DZ3: ch_offset <= CH_OFF_DZ3;
|
||
default: ch_offset <= CH_OFF_MASTER_NUM;
|
||
endcase
|
||
|
||
if (!ctrl_busy) begin
|
||
seq_id <= SEQ_CH_WRITE;
|
||
seq_step <= 0;
|
||
seq_active <= 1'b1;
|
||
end else begin
|
||
error_busy <= 1'b1;
|
||
end
|
||
end
|
||
|
||
// Direct SPI 命令启动寄存器。bit0 选择写/读,
|
||
// bits[15:8] 选择字节长度;0 表示 1 字节。
|
||
BRAM_ADDR_SPI_CTRL: begin
|
||
if (!ctrl_busy) begin
|
||
direct_write <= bram_din[0];
|
||
direct_len <= (bram_din[15:8] == 0) ? 8'd1 : bram_din[15:8];
|
||
seq_id <= SEQ_DIRECT;
|
||
seq_step <= 0;
|
||
seq_active <= 1'b1;
|
||
end else begin
|
||
error_busy <= 1'b1;
|
||
end
|
||
end
|
||
endcase
|
||
end
|
||
end
|
||
end
|
||
|
||
//-------------------------------------------------------------------------
|
||
// SPI 命令生成器。
|
||
//
|
||
// 当高层序列有效且 SPI master 空闲时,根据当前 seq_id/seq_step
|
||
// 生成一拍 cmd_valid,并给出本次 SPI 需要的地址、数据和长度。
|
||
//-------------------------------------------------------------------------
|
||
always @(posedge clk or negedge rst_n) begin
|
||
if (!rst_n) begin
|
||
spi_cmd_valid <= 1'b0;
|
||
spi_cmd_write <= 1'b0;
|
||
spi_cmd_addr <= 0;
|
||
spi_cmd_wdata <= 0;
|
||
spi_cmd_len <= 8'd1;
|
||
end else begin
|
||
spi_cmd_valid <= 1'b0;
|
||
|
||
if (seq_active && !spi_master_done && spi_cmd_ready && !spi_cmd_valid) begin
|
||
case (seq_id)
|
||
// 向 ASIC 控制寄存器写 1 字节。
|
||
SEQ_WRITE_CONTROL: begin
|
||
spi_cmd_valid <= 1'b1;
|
||
spi_cmd_write <= 1'b1;
|
||
spi_cmd_addr <= CSI_REG_CONTROL;
|
||
spi_cmd_wdata <= {{(8*MAX_BURST_BYTES-8){1'b0}}, control_shadow};
|
||
spi_cmd_len <= 8'd1;
|
||
end
|
||
|
||
// 两个 1 字节写:先写通道 0-7,再写通道 8-15。
|
||
SEQ_CH_ENABLE: begin
|
||
spi_cmd_valid <= 1'b1;
|
||
spi_cmd_write <= 1'b1;
|
||
spi_cmd_addr <= (seq_step == 0) ? CSI_REG_CH_ENABLE1 : CSI_REG_CH_ENABLE2;
|
||
spi_cmd_wdata <= (seq_step == 0) ?
|
||
{{(8*MAX_BURST_BYTES-8){1'b0}}, ch_enable_shadow[7:0]} :
|
||
{{(8*MAX_BURST_BYTES-8){1'b0}}, ch_enable_shadow[15:8]};
|
||
spi_cmd_len <= 8'd1;
|
||
end
|
||
|
||
// 通过 ASIC trigger register 启动刺激。
|
||
SEQ_TRIGGER: begin
|
||
spi_cmd_valid <= 1'b1;
|
||
spi_cmd_write <= 1'b1;
|
||
spi_cmd_addr <= CSI_REG_STIM_TRIGGER;
|
||
spi_cmd_wdata <= {{(8*MAX_BURST_BYTES-8){1'b0}}, 8'hAA};
|
||
spi_cmd_len <= 8'd1;
|
||
end
|
||
|
||
// 特殊 key 序列都写同一个 ASIC key 寄存器;
|
||
// seq_key_data 根据当前 step 选择写入字节。
|
||
SEQ_DCS_UNLOCK,
|
||
SEQ_TEST_UNLOCK,
|
||
SEQ_MODE_EXIT: begin
|
||
spi_cmd_valid <= 1'b1;
|
||
spi_cmd_write <= 1'b1;
|
||
spi_cmd_addr <= CSI_REG_TEST_DCS_KEY;
|
||
spi_cmd_wdata <= {{(8*MAX_BURST_BYTES-8){1'b0}}, seq_key_data(seq_id, seq_step)};
|
||
spi_cmd_len <= 8'd1;
|
||
end
|
||
|
||
// 写一个选中通道的参数字节。
|
||
SEQ_CH_WRITE: begin
|
||
spi_cmd_valid <= 1'b1;
|
||
spi_cmd_write <= 1'b1;
|
||
spi_cmd_addr <= ch_addr(ch_index, ch_offset);
|
||
spi_cmd_wdata <= {{(8*MAX_BURST_BYTES-8){1'b0}}, ch_data};
|
||
spi_cmd_len <= 8'd1;
|
||
end
|
||
|
||
// 软件直接控制的原始 SPI 访问。
|
||
SEQ_DIRECT: begin
|
||
spi_cmd_valid <= 1'b1;
|
||
spi_cmd_write <= direct_write;
|
||
spi_cmd_addr <= direct_addr;
|
||
spi_cmd_wdata <= direct_wdata;
|
||
spi_cmd_len <= direct_len;
|
||
end
|
||
endcase
|
||
end
|
||
end
|
||
end
|
||
|
||
//-------------------------------------------------------------------------
|
||
// BRAM 读数据选择。只有有效读周期才更新 bram_dout。
|
||
//-------------------------------------------------------------------------
|
||
always @(posedge clk or negedge rst_n) begin
|
||
if (!rst_n) begin
|
||
bram_dout <= 0;
|
||
end else if (bram_rd_valid) begin
|
||
case (bram_addr)
|
||
BRAM_ADDR_VERSION: bram_dout <= VERSION;
|
||
BRAM_ADDR_STATUS: bram_dout <= {16'd0, seq_id, seq_step, error_busy, done_latched, spi_cmd_ready, ctrl_busy};
|
||
BRAM_ADDR_SPI_ADDR: bram_dout <= {{(32-REG_ADDR_WIDTH){1'b0}}, direct_addr};
|
||
BRAM_ADDR_SPI_WDATA: bram_dout <= direct_wdata;
|
||
BRAM_ADDR_SPI_RDATA: bram_dout <= last_rdata;
|
||
BRAM_ADDR_PIN_CTRL: bram_dout <= {30'd0, asic_trig, asic_rst_n};
|
||
BRAM_ADDR_LAST_ADDR: bram_dout <= {{(32-REG_ADDR_WIDTH){1'b0}}, last_addr};
|
||
BRAM_ADDR_CONTROL: bram_dout <= {24'd0, control_shadow};
|
||
BRAM_ADDR_CH_ENABLE: bram_dout <= {16'd0, ch_enable_shadow};
|
||
BRAM_ADDR_CH_INDEX: bram_dout <= {28'd0, ch_index};
|
||
default: bram_dout <= 0;
|
||
endcase
|
||
end
|
||
end
|
||
|
||
endmodule
|