sync chunk 11/12
This commit is contained in:
@@ -0,0 +1,572 @@
|
||||
`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
|
||||
@@ -0,0 +1,208 @@
|
||||
`timescale 1 ns / 1 ps
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// csi040_ctrl 的自检查 testbench。
|
||||
//
|
||||
// 这个 testbench 驱动 BRAM 风格控制口,监视 DUT 产生的 SPI 总线,
|
||||
// 并检查每个期望的 32 bit 单字节 SPI 帧:
|
||||
// [opcode][addr high][addr low][data]
|
||||
//
|
||||
// ASIC SO 只做了最小模型,用于 direct read 测试:
|
||||
// 在读帧的 payload byte 阶段返回 read_response。
|
||||
//------------------------------------------------------------------------------
|
||||
module csi040_ctrl_tb;
|
||||
|
||||
localparam CLK_PERIOD = 10;
|
||||
|
||||
// DUT 寄存器总线和 ASIC 侧引脚。
|
||||
reg clk;
|
||||
reg rst_n;
|
||||
reg [12:0] bram_addr;
|
||||
reg [31:0] bram_din;
|
||||
wire [31:0] bram_dout;
|
||||
reg bram_en;
|
||||
reg [3:0] bram_we;
|
||||
wire busy;
|
||||
wire spi_done;
|
||||
wire error_busy;
|
||||
wire asic_rst_n;
|
||||
wire asic_trig;
|
||||
wire asic_csn;
|
||||
wire asic_sck;
|
||||
wire asic_si;
|
||||
reg asic_so;
|
||||
|
||||
// SPI monitor 状态。CSN 为低时,shift_in 捕获 SI bit。
|
||||
integer bit_cnt;
|
||||
integer frame_bits;
|
||||
reg [31:0] shift_in;
|
||||
reg [31:0] frame_in;
|
||||
reg [7:0] read_response;
|
||||
|
||||
// 使用较小分频和常见 0x02/0x03 opcode,让仿真更短,
|
||||
// 波形里的 SPI 帧也更好读。
|
||||
csi040_ctrl#
|
||||
(
|
||||
.SPI_CLK_DIV ( 2 ),
|
||||
.SPI_OPCODE_WRITE ( 8'h02 ),
|
||||
.SPI_OPCODE_READ ( 8'h03 )
|
||||
)
|
||||
dut
|
||||
(
|
||||
.clk ( clk ),
|
||||
.rst_n ( rst_n ),
|
||||
.bram_addr ( bram_addr ),
|
||||
.bram_clk ( clk ),
|
||||
.bram_din ( bram_din ),
|
||||
.bram_dout ( bram_dout ),
|
||||
.bram_en ( bram_en ),
|
||||
.bram_rst ( 1'b0 ),
|
||||
.bram_we ( bram_we ),
|
||||
.busy ( busy ),
|
||||
.spi_done ( spi_done ),
|
||||
.error_busy ( error_busy ),
|
||||
.asic_rst_n ( asic_rst_n ),
|
||||
.asic_trig ( asic_trig ),
|
||||
.asic_csn ( asic_csn ),
|
||||
.asic_sck ( asic_sck ),
|
||||
.asic_si ( asic_si ),
|
||||
.asic_so ( asic_so )
|
||||
);
|
||||
|
||||
// 100 MHz 时钟。
|
||||
initial begin
|
||||
clk = 1'b0;
|
||||
forever #(CLK_PERIOD/2) clk = ~clk;
|
||||
end
|
||||
|
||||
// 主定向测试序列。每个 write_bram 要么启动命令,要么更新 shadow 寄存器;
|
||||
// 每个 check_next_frame 等待 CSN 拉高,并比较 monitor 捕获到的 SPI bit。
|
||||
initial begin
|
||||
rst_n = 1'b0;
|
||||
bram_addr = 0;
|
||||
bram_din = 0;
|
||||
bram_en = 1'b0;
|
||||
bram_we = 4'h0;
|
||||
asic_so = 1'b0;
|
||||
read_response = 8'h28;
|
||||
#(CLK_PERIOD*8);
|
||||
rst_n = 1'b1;
|
||||
|
||||
// CONTROL 写:ASIC register 0x0002 <= 0x10。
|
||||
write_bram(13'h0020, 32'h00000010);
|
||||
check_next_frame(32'h02000210);
|
||||
|
||||
// CH_ENABLE 写会把 16 bit 拆到 ASIC register 0x0007/0x0008。
|
||||
write_bram(13'h0024, 32'h0000A55A);
|
||||
check_next_frame(32'h0200075A);
|
||||
check_next_frame(32'h020008A5);
|
||||
|
||||
// 通道参数写。ch_index=3,CH_CUR_A offset=1,
|
||||
// ASIC 地址 = 0x0010 + 3*0x10 + 1 = 0x0041。
|
||||
write_bram(13'h0040, 32'h00000003);
|
||||
write_bram(13'h0048, 32'h0000007F);
|
||||
check_next_frame(32'h0200417F);
|
||||
|
||||
// DCS 解锁序列:向 key register 0x0009 连续写 3 个魔术字节。
|
||||
write_bram(13'h002C, 32'h00000001);
|
||||
check_next_frame(32'h02000955);
|
||||
check_next_frame(32'h020009B3);
|
||||
check_next_frame(32'h0200090A);
|
||||
|
||||
// Direct 方式从 Part ID register 0x011D 读 1 字节。
|
||||
// 下面的 ASIC 模型返回 0x28,随后通过 BRAM 读 SPI_RDATA 检查。
|
||||
write_bram(13'h0008, 32'h0000011D);
|
||||
write_bram(13'h0010, 32'h00000100);
|
||||
check_next_frame(32'h03011D00);
|
||||
read_bram(13'h0014);
|
||||
if (bram_dout[7:0] !== 8'h28) begin
|
||||
$display("ERROR read data expected 28 got %h", bram_dout[7:0]);
|
||||
$stop;
|
||||
end
|
||||
|
||||
#(CLK_PERIOD*20);
|
||||
$finish;
|
||||
end
|
||||
|
||||
// CSN 拉低时,开始捕获一帧新的 SPI。
|
||||
always @(negedge asic_csn) begin
|
||||
bit_cnt <= 0;
|
||||
shift_in <= 0;
|
||||
end
|
||||
|
||||
// CSN 拉高时,锁存刚捕获完的一帧。
|
||||
always @(posedge asic_csn) begin
|
||||
frame_in <= shift_in;
|
||||
frame_bits <= bit_cnt;
|
||||
end
|
||||
|
||||
// SPI monitor:CSN 有效期间,在 SCK 上升沿采样 MOSI/SI。
|
||||
always @(posedge asic_sck) begin
|
||||
if (!asic_csn) begin
|
||||
shift_in <= {shift_in[30:0], asic_si};
|
||||
bit_cnt <= bit_cnt + 1;
|
||||
end
|
||||
end
|
||||
|
||||
// 最小 ASIC 读模型。对当前 32 bit 测试帧来说,bit 24..31 是返回数据字节。
|
||||
// 在下降沿驱动 SO,保证 DUT 在之后的上升沿能采到。
|
||||
always @(negedge asic_sck) begin
|
||||
if (!asic_csn && (bit_cnt >= 24) && (bit_cnt < 32))
|
||||
asic_so <= read_response[31-bit_cnt];
|
||||
else
|
||||
asic_so <= 1'b0;
|
||||
end
|
||||
|
||||
// 整字 BRAM 写 helper。先等待 DUT 空闲,避免定向测试误触发 busy-error。
|
||||
task write_bram;
|
||||
input [12:0] addr;
|
||||
input [31:0] data;
|
||||
begin
|
||||
wait(busy == 1'b0);
|
||||
@(negedge clk);
|
||||
bram_addr = addr;
|
||||
bram_din = data;
|
||||
bram_en = 1'b1;
|
||||
bram_we = 4'hF;
|
||||
@(posedge clk);
|
||||
@(negedge clk);
|
||||
bram_en = 1'b0;
|
||||
bram_we = 4'h0;
|
||||
end
|
||||
endtask
|
||||
|
||||
// BRAM 读 helper。bram_en 为高且 bram_we 为 0 的时钟沿更新 bram_dout。
|
||||
task read_bram;
|
||||
input [12:0] addr;
|
||||
begin
|
||||
@(negedge clk);
|
||||
bram_addr = addr;
|
||||
bram_din = 0;
|
||||
bram_en = 1'b1;
|
||||
bram_we = 4'h0;
|
||||
@(posedge clk);
|
||||
@(negedge clk);
|
||||
bram_en = 1'b0;
|
||||
end
|
||||
endtask
|
||||
|
||||
// 等待下一帧 SPI 完成,并和期望的 32 bit 单字节帧比较。
|
||||
task check_next_frame;
|
||||
input [31:0] expected;
|
||||
begin
|
||||
@(posedge asic_csn);
|
||||
#(CLK_PERIOD/2);
|
||||
|
||||
if (frame_bits != 32) begin
|
||||
$display("ERROR frame expected 32 bits got %0d bits", frame_bits);
|
||||
$stop;
|
||||
end
|
||||
|
||||
if (frame_in !== expected) begin
|
||||
$display("ERROR frame expected %h got %h", expected, frame_in);
|
||||
$stop;
|
||||
end
|
||||
end
|
||||
endtask
|
||||
|
||||
endmodule
|
||||
@@ -0,0 +1,228 @@
|
||||
`timescale 1 ns / 1 ps
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// 简单的 CSI040 SPI master。
|
||||
//
|
||||
// 命令侧是一个 request/ready 接口:
|
||||
// cmd_valid 和 cmd_ready 同时为 1 时启动一帧 SPI。
|
||||
// cmd_write 选择写 opcode 或读 opcode。
|
||||
// cmd_addr 在 opcode 后发送,高字节先发。
|
||||
// cmd_wdata 的 payload byte0 来自 cmd_wdata[7:0]。
|
||||
// cmd_len 选择 payload 字节数,并被限制到 1..MAX_BURST_BYTES。
|
||||
//
|
||||
// 当前 SPI 模式:
|
||||
// csn 空闲为高,低有效
|
||||
// sck 空闲为低
|
||||
// si 在下降沿后更新
|
||||
// 读数据阶段在上升沿采样 so
|
||||
//------------------------------------------------------------------------------
|
||||
module csi040_spi_master#
|
||||
(
|
||||
parameter CLK_DIV = 50, // SCK 半周期对应的 clk 周期数
|
||||
parameter REG_ADDR_WIDTH = 16,
|
||||
parameter ADDR_BYTES = 2,
|
||||
parameter MAX_BURST_BYTES = 4,
|
||||
parameter [7:0] OPCODE_WRITE = 8'h00,
|
||||
parameter [7:0] OPCODE_READ = 8'h80
|
||||
)
|
||||
(
|
||||
input clk,
|
||||
input rst_n,
|
||||
|
||||
// 来自 csi040_ctrl 的命令请求。
|
||||
output cmd_ready,
|
||||
input cmd_valid,
|
||||
input cmd_write,
|
||||
input [REG_ADDR_WIDTH-1:0]cmd_addr,
|
||||
input [8*MAX_BURST_BYTES-1:0] cmd_wdata,
|
||||
input [7:0] cmd_len,
|
||||
|
||||
output reg done,
|
||||
output reg [8*MAX_BURST_BYTES-1:0] cmd_rdata,
|
||||
output reg busy,
|
||||
|
||||
// ASIC SPI 引脚。
|
||||
output csn,
|
||||
output sck,
|
||||
output si,
|
||||
input so
|
||||
);
|
||||
|
||||
// 内部最大帧宽度包含 opcode + 地址 + 最大 payload。
|
||||
// 实际只会移出 HEADER_BITS + 8*safe_cmd_len 个 bit。
|
||||
localparam FRAME_MAX_BITS = 8 * (1 + ADDR_BYTES + MAX_BURST_BYTES);
|
||||
localparam HEADER_BITS = 8 * (1 + ADDR_BYTES);
|
||||
localparam [15:0] MAX_BURST_BYTES_W = MAX_BURST_BYTES;
|
||||
localparam [15:0] HEADER_BITS_W = HEADER_BITS;
|
||||
|
||||
localparam IDLE_ST = 2'd0;
|
||||
localparam TRANS_ST = 2'd1;
|
||||
localparam DONE_ST = 2'd2;
|
||||
|
||||
// 传输状态、移位寄存器和计数器。
|
||||
reg [1:0] cstate;
|
||||
reg [FRAME_MAX_BITS-1:0] tx_shift;
|
||||
reg [8*MAX_BURST_BYTES-1:0] rx_shift;
|
||||
reg [15:0] half_cnt;
|
||||
reg [15:0] total_bits;
|
||||
reg [15:0] sampled_bits;
|
||||
reg csn_r;
|
||||
reg sck_r;
|
||||
reg si_r;
|
||||
reg cmd_write_r;
|
||||
reg [7:0] cmd_len_r;
|
||||
reg last_sampled;
|
||||
|
||||
// 组合逻辑组帧寄存器。
|
||||
reg [FRAME_MAX_BITS-1:0] frame;
|
||||
reg [REG_ADDR_WIDTH-1:0] addr_pad;
|
||||
integer n;
|
||||
|
||||
wire half_tick;
|
||||
wire data_bit_phase;
|
||||
wire sample_data_bit;
|
||||
wire [15:0] safe_cmd_len;
|
||||
wire [15:0] cmd_total_bits;
|
||||
|
||||
// 把 cmd_len 限制在合法范围。因为 CSI040 寄存器按 byte 访问,
|
||||
// 长度为 0 时按 1 字节处理。
|
||||
assign safe_cmd_len = (cmd_len == 0) ? 16'd1 :
|
||||
(cmd_len > MAX_BURST_BYTES) ? MAX_BURST_BYTES_W :
|
||||
{8'd0, cmd_len};
|
||||
|
||||
// 总移位 bit 数 = opcode + 地址字节 + payload 字节。
|
||||
assign cmd_total_bits = HEADER_BITS_W + (safe_cmd_len << 3);
|
||||
|
||||
// 一个 SCK 半周期已经到达。
|
||||
assign half_tick = (half_cnt == (CLK_DIV - 1));
|
||||
|
||||
// 只有发完 opcode/address 头部之后,读数据阶段才有效。
|
||||
assign data_bit_phase = (sampled_bits >= HEADER_BITS);
|
||||
assign sample_data_bit = (!cmd_write_r) && data_bit_phase;
|
||||
|
||||
assign cmd_ready = (cstate == IDLE_ST);
|
||||
assign csn = csn_r;
|
||||
assign sck = sck_r;
|
||||
assign si = si_r;
|
||||
|
||||
// 构造最大长度 SPI 帧:
|
||||
// [opcode][addr MSB..LSB][payload byte0][payload byte1]...
|
||||
// payload byte0 来自 cmd_wdata[7:0]。
|
||||
always @(*) begin
|
||||
frame = {FRAME_MAX_BITS{1'b0}};
|
||||
addr_pad = cmd_addr;
|
||||
|
||||
frame[FRAME_MAX_BITS-1 -: 8] = cmd_write ? OPCODE_WRITE : OPCODE_READ;
|
||||
|
||||
for (n = 0; n < ADDR_BYTES; n = n + 1) begin
|
||||
frame[FRAME_MAX_BITS-1-8*(n+1) -: 8] =
|
||||
addr_pad[8*(ADDR_BYTES-n)-1 -: 8];
|
||||
end
|
||||
|
||||
for (n = 0; n < MAX_BURST_BYTES; n = n + 1) begin
|
||||
frame[FRAME_MAX_BITS-1-8*(1+ADDR_BYTES+n) -: 8] =
|
||||
cmd_wdata[8*n +: 8];
|
||||
end
|
||||
end
|
||||
|
||||
// SPI 主状态机。每 CLK_DIV 个 clk 周期翻转一次 SCK。
|
||||
// SCK 上升沿采样数据,SCK 下降沿准备下一位 SI。
|
||||
always @(posedge clk or negedge rst_n) begin
|
||||
if (!rst_n) begin
|
||||
cstate <= IDLE_ST;
|
||||
tx_shift <= 0;
|
||||
rx_shift <= 0;
|
||||
half_cnt <= 0;
|
||||
total_bits <= 0;
|
||||
sampled_bits <= 0;
|
||||
csn_r <= 1'b1;
|
||||
sck_r <= 1'b0;
|
||||
si_r <= 1'b0;
|
||||
cmd_write_r <= 1'b0;
|
||||
cmd_len_r <= 0;
|
||||
last_sampled <= 1'b0;
|
||||
done <= 1'b0;
|
||||
cmd_rdata <= 0;
|
||||
busy <= 1'b0;
|
||||
end else begin
|
||||
done <= 1'b0;
|
||||
|
||||
case (cstate)
|
||||
IDLE_ST: begin
|
||||
csn_r <= 1'b1;
|
||||
sck_r <= 1'b0;
|
||||
si_r <= 1'b0;
|
||||
half_cnt <= 0;
|
||||
sampled_bits <= 0;
|
||||
last_sampled <= 1'b0;
|
||||
busy <= 1'b0;
|
||||
|
||||
if (cmd_valid) begin
|
||||
// 帧开始时锁存命令,这样上层后续改变命令总线也不影响本帧。
|
||||
tx_shift <= frame;
|
||||
rx_shift <= 0;
|
||||
total_bits <= cmd_total_bits;
|
||||
cmd_write_r <= cmd_write;
|
||||
cmd_len_r <= safe_cmd_len[7:0];
|
||||
csn_r <= 1'b0;
|
||||
si_r <= frame[FRAME_MAX_BITS-1];
|
||||
busy <= 1'b1;
|
||||
cstate <= TRANS_ST;
|
||||
end
|
||||
end
|
||||
|
||||
TRANS_ST: begin
|
||||
busy <= 1'b1;
|
||||
|
||||
if (half_tick) begin
|
||||
half_cnt <= 0;
|
||||
|
||||
if (!sck_r) begin
|
||||
// 上升沿:ASIC 采样 SI;读 payload 阶段 master 采样 SO。
|
||||
sck_r <= 1'b1;
|
||||
|
||||
if (sample_data_bit)
|
||||
rx_shift <= {rx_shift[8*MAX_BURST_BYTES-2:0], so};
|
||||
|
||||
sampled_bits <= sampled_bits + 1'b1;
|
||||
|
||||
if (sampled_bits == (total_bits - 1))
|
||||
last_sampled <= 1'b1;
|
||||
end else begin
|
||||
// 下降沿:准备下一位输出;如果最后一位已经采完则结束。
|
||||
sck_r <= 1'b0;
|
||||
|
||||
if (last_sampled) begin
|
||||
cstate <= DONE_ST;
|
||||
cmd_rdata <= rx_shift;
|
||||
end else begin
|
||||
tx_shift <= {tx_shift[FRAME_MAX_BITS-2:0], 1'b0};
|
||||
si_r <= tx_shift[FRAME_MAX_BITS-2];
|
||||
end
|
||||
end
|
||||
end else begin
|
||||
half_cnt <= half_cnt + 1'b1;
|
||||
end
|
||||
end
|
||||
|
||||
DONE_ST: begin
|
||||
// 拉高 CSN,并产生 1 个 clk 周期的 done 脉冲。
|
||||
csn_r <= 1'b1;
|
||||
sck_r <= 1'b0;
|
||||
si_r <= 1'b0;
|
||||
busy <= 1'b0;
|
||||
done <= 1'b1;
|
||||
last_sampled <= 1'b0;
|
||||
cstate <= IDLE_ST;
|
||||
end
|
||||
|
||||
default: begin
|
||||
cstate <= IDLE_ST;
|
||||
end
|
||||
endcase
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
endmodule
|
||||
Reference in New Issue
Block a user