sync chunk 11/12

This commit is contained in:
chunk-sync
2026-09-11 16:18:36 +08:00
parent c315cef09e
commit c2c1bb2648
922 changed files with 102283 additions and 6793 deletions
@@ -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_ADDRSPI_WDATASPI_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=3CH_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 monitorCSN 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 helperbram_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