GOWIN Vol.4 第2部第1章 SPIマスタのテストベンチ

`default_nettype none

// SPI Slave モデル モジュール
// – MOSIで DATA_BITS [bits] のデータを受信し、その後受信したデータをMISOに出力する
// – SCLKは DATA_BITS*2 [cycle] 入力される想定
// – 前半の DATA_BITS [cycle] でMOSIからデータ受信 (MISO出力は無効値)
// – 後半の DATA_BITS [cycle] でMISOへデータ受信
module dummy_spi_slave #(
parameter DATA_BITS = 8,
parameter CPOL = 0,
parameter CPHA = 0
) (
input wire ncs,
input wire sclk,
input wire mosi,
output wire miso
);

reg mosi_reg = 0;
reg [DATA_BITS-1:0] data_reg = 0;

always@(sclk) begin
if(!ncs) begin
// Capture
if( (CPOL==0 && CPHA==0 && sclk == 1) ||
(CPOL==0 && CPHA==1 && sclk == 0) ||
(CPOL==1 && CPHA==0 && sclk == 0) ||
(CPOL==1 && CPHA==1 && sclk == 1)
)
begin
mosi_reg <= mosi;
end

// Shift
if( (CPOL==0 && CPHA==0 && sclk == 0) ||
(CPOL==0 && CPHA==1 && sclk == 1) ||
(CPOL==1 && CPHA==0 && sclk == 1) ||
(CPOL==1 && CPHA==1 && sclk == 0)
)
begin
data_reg <= {data_reg[DATA_BITS-2:0], mosi_reg};
end
end
end

assign miso = (ncs==0)? data_reg[DATA_BITS-1] : 1’b0;
endmodule

// テストベンチ
module tb_SPI_MASTER();
localparam SLAVE_DEVICE_NUM = 4; // 接続するスレーブデバイスの数
localparam MAX_TRANSFER_BITS = 16; // 1回の転送におけるSCLKサイクル数の最大値(=最大の転送ビット数)
localparam CLKDIV_CNT_WIDTH = 4; // クロック分周用カウンタのビット数
localparam PRE_TRANSFER_SYSCLK_CYCLES = 4; // nCSをアサートしてから転送を開始(SCLK発振を開始)するまでの待ち時間 (sys_clkのサイクル数で指定)
localparam POST_TRANSFER_SYSCLK_CYCLES = 4; // 転送を終了(SCLK発振を停止)してからnCSをネゲートするまでの待ち時間 (sys_clkのサイクル数で指定)

reg sys_clk = 0; // クロック入力
reg nrst = 0; // リセット入力 (負極性)

// 設定信号 / 送信データ (sys_clkに同期)
// “slave_device_sel” / “sclk_cycles” / “txd” は “start” がアサートされたサイクルで取り込まれる
reg start = 0; // 転送開始 (1サイクルパルス信号)
reg [CLKDIV_CNT_WIDTH-1:0] sclk_div_ratio_half = 0; // spi_scl 生成用の sys_clk の分周比率/2 (1以上の値を指定)
reg [$clog2(MAX_TRANSFER_BITS):0] sclk_cycles = 0; // SCLKのサイクル数
reg cpol = 0; // SCLKの極性 (0:負極性, 1:正極性)
reg cpha = 0; // SCLKの位相
reg [$clog2(SLAVE_DEVICE_NUM)-1:0] slave_device_sel = 0; // スレーブデバイスの選択
reg [MAX_TRANSFER_BITS-1:0] txd = 0; // 送信データ

// 受信データ (sys_clkに同期)
// “rxd” は done 信号がアサートされたタイミングで有効となる
wire done; // 受信完了 (1サイクルパルス信号)
wire [MAX_TRANSFER_BITS-1:0] rxd; // 受信データ

// SPI インタフェース信号
wire spi_sclk; // クロック信号 (SCLK)
wire spi_mosi; // 出力ビット (MOSI: Master-Out, Slave-In)
wire spi_miso; // 入力データ (MISO: Master-In, Slave-Out)
wire [SLAVE_DEVICE_NUM-1:0] spi_ncs; // スレーブデバイス選択 (nCS : 負極性 Chip Select)

// 波形をダンプする設定 (“dump.vcd” に出力)
initial begin
$dumpfile(“dump.vcd”);
$dumpvars(0, tb_SPI_MASTER);
end

// System Clockの生成 (Cycle: 10ns = 100MHz)
initial begin
sys_clk = 0;
forever begin
#5 sys_clk = ~sys_clk;
end
end

// SPI Master モジュール (テスト対象)
SPI_MASTER #(
.SLAVE_DEVICE_NUM(SLAVE_DEVICE_NUM),
.MAX_TRANSFER_BITS(MAX_TRANSFER_BITS),
.CLKDIV_CNT_WIDTH(CLKDIV_CNT_WIDTH),
.PRE_TRANSFER_SYSCLK_CYCLES(PRE_TRANSFER_SYSCLK_CYCLES),
.POST_TRANSFER_SYSCLK_CYCLES(POST_TRANSFER_SYSCLK_CYCLES)
) spi_master_i (.*);

// SPI Slave モデル モジュール x4個
wire [3:0] spi_miso_ary;
assign spi_miso = |spi_miso_ary;
dummy_spi_slave #(.DATA_BITS(8), .CPOL(0), .CPHA(0)) spi_slave_0 (.ncs(spi_ncs[0]), .sclk(spi_sclk), .mosi(spi_mosi), .miso(spi_miso_ary[0]));
dummy_spi_slave #(.DATA_BITS(4), .CPOL(0), .CPHA(1)) spi_slave_1 (.ncs(spi_ncs[1]), .sclk(spi_sclk), .mosi(spi_mosi), .miso(spi_miso_ary[1]));
dummy_spi_slave #(.DATA_BITS(8), .CPOL(1), .CPHA(0)) spi_slave_2 (.ncs(spi_ncs[2]), .sclk(spi_sclk), .mosi(spi_mosi), .miso(spi_miso_ary[2]));
dummy_spi_slave #(.DATA_BITS(8), .CPOL(1), .CPHA(1)) spi_slave_3 (.ncs(spi_ncs[3]), .sclk(spi_sclk), .mosi(spi_mosi), .miso(spi_miso_ary[3]));

// テストシーケンス
reg [7:0] sendData;
reg [7:0] recvData;

initial begin
// Reset
repeat(1)@(posedge sys_clk);
nrst <= ‘b0;
repeat(5)@(posedge sys_clk);
nrst <= ‘b1;

// Dummy SPI Slave モジュール 0 と通信 (DATA_BITS=8, CPOL=0, CPHA=0)
repeat(1)@(posedge sys_clk);
sendData = ‘h89;
start <= ‘b1;
sclk_div_ratio_half <= ‘d4;
sclk_cycles <= ‘d16;
cpol <= ‘b0;
cpha <= ‘b0;
slave_device_sel <= ‘d0;
txd <= {sendData, 8’h00}; // MSB側から順に転送するため、上位の8Bitに有効な値を入れておく
repeat(1)@(posedge sys_clk);
start <= ‘b0;
while(done != ‘b1) @(posedge sys_clk); // Done信号がアサートされるまでサイクルを進める
recvData <= rxd[7:0]; // Done信号がアサートされたときに受信データをキャプチャ
repeat(1)@(posedge sys_clk);
$display(“SPI Slave 0: SendData=0x%h, RecvData=0x%h”, sendData[7:0], recvData[7:0]); // 送信データと受信データを表示
repeat(5)@(posedge sys_clk);

// Reset
repeat(1)@(posedge sys_clk);
nrst <= ‘b0;
repeat(5)@(posedge sys_clk);
nrst <= ‘b1;

// Dummy SPI Slave モジュール 1 と通信 (DATA_BITS=4, CPOL=0, CPHA=1)
repeat(1)@(posedge sys_clk);
sendData = ‘ha;
start <= ‘b1;
sclk_div_ratio_half <= ‘d4;
sclk_cycles <= ‘d8;
cpol <= ‘b0;
cpha <= ‘b1;
slave_device_sel <= ‘d1;
txd <= {sendData, 12’h000}; // MSB側から順に転送するため、上位の4Bitに有効な値を入れておく
repeat(1)@(posedge sys_clk);
start <= ‘b0;
while(done != ‘b1) @(posedge sys_clk); // Done信号がアサートされるまでサイクルを進める
recvData <= rxd[3:0]; // Done信号がアサートされたときに受信データをキャプチャ
repeat(1)@(posedge sys_clk);
$display(“SPI Slave 1: SendData=0x%h, RecvData=0x%h”, sendData[3:0], recvData[3:0]); // 送信データと受信データを表示
repeat(5)@(posedge sys_clk);

// Reset
repeat(1)@(posedge sys_clk);
nrst <= ‘b0;
repeat(5)@(posedge sys_clk);
nrst <= ‘b1;

// Dummy SPI Slave モジュール 2 と通信 (DATA_BITS=8, CPOL=1, CPHA=0)
repeat(1)@(posedge sys_clk);
sendData = ‘hcd;
start <= ‘b1;
sclk_div_ratio_half <= ‘d4;
sclk_cycles <= ‘d16;
cpol <= ‘b1;
cpha <= ‘b0;
slave_device_sel <= ‘d2;
txd <= {sendData, 8’h00}; // MSB側から順に転送するため、上位の8Bitに有効な値を入れておく
repeat(1)@(posedge sys_clk);
start <= ‘b0;
while(done != ‘b1) @(posedge sys_clk); // Done信号がアサートされるまでサイクルを進める
recvData <= rxd[7:0]; // Done信号がアサートされたときに受信データをキャプチャ
repeat(1)@(posedge sys_clk);
$display(“SPI Slave 2: SendData=0x%h, RecvData=0x%h”, sendData[7:0], recvData[7:0]); // 送信データと受信データを表示
repeat(5)@(posedge sys_clk);

// Reset
repeat(1)@(posedge sys_clk);
nrst <= ‘b0;
repeat(5)@(posedge sys_clk);
nrst <= ‘b1;

// Dummy SPI Slave モジュール 3 と通信 (DATA_BITS=8, CPOL=1, CPHA=1)
repeat(1)@(posedge sys_clk);
sendData = ‘hef;
start <= ‘b1;
sclk_div_ratio_half <= ‘d4;
sclk_cycles <= ‘d16;
cpol <= ‘b1;
cpha <= ‘b1;
slave_device_sel <= ‘d3;
txd <= {sendData, 8’h00}; // MSB側から順に転送するため、上位の8Bitに有効な値を入れておく
repeat(1)@(posedge sys_clk);
start <= ‘b0;
while(done != ‘b1) @(posedge sys_clk); // Done信号がアサートされるまでサイクルを進める
recvData <= rxd[7:0]; // Done信号がアサートされたときに受信データをキャプチャ
repeat(1)@(posedge sys_clk);
$display(“SPI Slave 3: SendData=0x%h, RecvData=0x%h”, sendData[7:0], recvData[7:0]); // 送信データと受信データを表示
repeat(5)@(posedge sys_clk);

$finish;
end

endmodule

`default_nettype wire