`timescale 1ns / 1ps
module top_SPI(input clk, i_Rst_L_, i_TX_DV_, i_SPI_MISO_, output clk_SPI, o_TX_Ready_, o_RX_DV_, o_SPI_Clk_, o_SPI_MOSI_);
clk_wiz_0 instance1(
.clk_SPI(clk_SPI), //12MHz
.clk_in1(clk)
);
SPI_Master instance2(
.i_Rst_L(i_Rst_L_),
.i_Clk(clk_SPI),
//지금 당장은 쓸 일 없음 (i_TX_Byte)
.i_TX_DV(i_TX_DV_),
.o_TX_Ready(o_TX_Ready_),
.o_RX_DV(o_RX_DV_),
.o_SPI_Clk(o_SPI_Clk_),
.i_SPI_MISO(i_SPI_MISO_),
.o_SPI_MOSI(o_SPI_MOSI_)
);
endmodule
module SPI_Master
#(parameter SPI_MODE = 0,
parameter CLKS_PER_HALF_BIT = 1) //SPI Clock 주기의 반에 i_clk이 얼마나 들어가냐
(
// Control/Data Signals,
input i_Rst_L, // FPGA Reset //reset 신호
input i_Clk, // FPGA Clock -fpga reference clock 말하는듯
// TX (MOSI) Signals
input [7:0] i_TX_Byte, // Byte to transmit on MOSI -MOSI핀으로 전송할 8비트 데이터
input i_TX_DV, // Data Valid Pulse with i_TX_Byte -데이터 다 보냈다고 보내는 펄스인듯
output reg o_TX_Ready, // Transmit Ready for next byte -다음 바이트를 보낼 준비가 되었다(바이트를 비트로 바꾸기 위해서 필요?)
//MISO 안쓰니까 필요 없을듯
// RX (MISO) Signals
output reg o_RX_DV, // Data Valid pulse (1 clock cycle)
//output reg [7:0] o_RX_Byte, // Byte received on MISO
// SPI Interface
output reg o_SPI_Clk, //SCK 만들어주기 위한거인듯
input i_SPI_MISO,
output reg o_SPI_MOSI //이게 내가 필요한 핀일듯-slave로 전달하기 위한 핀
);
// SPI Interface (All Runs at SPI Clock Domain)
wire w_CPOL; // Clock polarity
wire w_CPHA; // Clock phase
reg [$clog2(CLKS_PER_HALF_BIT*2)-1:0] r_SPI_Clk_Count;
reg r_SPI_Clk;
reg [4:0] r_SPI_Clk_Edges;
reg r_Leading_Edge;
reg r_Trailing_Edge;
reg r_TX_DV;
reg [7:0] r_TX_Byte;
reg [2:0] r_RX_Bit_Count;
reg [2:0] r_TX_Bit_Count;
reg [7:0] lut[0:15]; //for LUT
// CPOL: Clock Polarity
// CPOL=0 means clock idles at 0, leading edge is rising edge.
// CPOL=1 means clock idles at 1, leading edge is falling edge.
assign w_CPOL = (SPI_MODE == 2) | (SPI_MODE == 3);
// CPHA: Clock Phase
// CPHA=0 means the "out" side changes the data on trailing edge of clock
// the "in" side captures data on leading edge of clock
// CPHA=1 means the "out" side changes the data on leading edge of clock
// the "in" side captures data on the trailing edge of clock
assign w_CPHA = (SPI_MODE == 1) | (SPI_MODE == 3);
integer j;
integer i;
initial begin
for (i = 0; i < 16; i = i + 1) begin //클럭 상관없이 그냥 데이터 넣기
case (i)
0: lut[i] = 8'hFF;
1: lut[i] = 8'h1E;
2: lut[i] = 8'h7C;
3: lut[i] = 8'h55;
4: lut[i] = 8'hD3;
4: lut[i] = 8'h11;
5: lut[i] = 8'h74;
6: lut[i] = 8'hE3;
7: lut[i] = 8'h9A;
8: lut[i] = 8'h83;
9: lut[i] = 8'hE6;
10: lut[i] = 8'h77;
11: lut[i] = 8'hB4;
12: lut[i] = 8'h36;
13: lut[i] = 8'hC3;
14: lut[i] = 8'hF3;
15: lut[i] = 8'h8F;
default: lut[i] = 8'h00; // 기본값 설정
endcase
end
end
// Purpose: Generate SPI Clock correct number of times when DV pulse comes
//내가 원하는 SPI_clk(SCK) 만들어주는 부분
always @(posedge i_Clk or negedge i_Rst_L)
begin
if (~i_Rst_L) //active-low로 동작하는 reset
begin //초기
o_TX_Ready <= 1'b0;
r_SPI_Clk_Edges <= 0;
r_Leading_Edge <= 1'b0;
r_Trailing_Edge <= 1'b0;
r_SPI_Clk <= w_CPOL; // assign default state to idle state, w_CPOL 0이면 clk값 0부터 시작, 1이면 clk값 1부터 시작
r_SPI_Clk_Count <= 0;
end
else
begin
// Default assignments
r_Leading_Edge <= 1'b0;
r_Trailing_Edge <= 1'b0;
if(r_SPI_Clk_Edges==0)
begin
r_SPI_Clk_Edges=16;
o_TX_Ready <= 1'b1;
end
else if (r_SPI_Clk_Edges > 0)
begin
o_TX_Ready <= 1'b0;
if (r_SPI_Clk_Count == CLKS_PER_HALF_BIT*2-1) //r_SPI_Clk_count==7(시뮬레이션에서 4로해놔서)
begin
r_SPI_Clk_Edges <= r_SPI_Clk_Edges - 1'b1;
r_Trailing_Edge <= 1'b1; //trailing: 하강
r_SPI_Clk_Count <= 0;
r_SPI_Clk <= ~r_SPI_Clk;
end
else if (r_SPI_Clk_Count == CLKS_PER_HALF_BIT-1) //3
begin
r_SPI_Clk_Edges <= r_SPI_Clk_Edges - 1'b1;
r_Leading_Edge <= 1'b1; //leading: 상승
r_SPI_Clk_Count <= r_SPI_Clk_Count + 1'b1;
r_SPI_Clk <= ~r_SPI_Clk;
end
else
begin
r_SPI_Clk_Count <= r_SPI_Clk_Count + 1'b1;
end
end
//
else if(r_SPI_Clk_Edges==16)
begin
o_TX_Ready <= 1'b0;
r_SPI_Clk_Edges <= 16; // Total # edges in one byte ALWAYS 16
end
end // always @ (posedge i_Clk or negedge i_Rst_L)
//여기까지 내가 원하는 SCK 생성 파트
end
// Purpose: Register i_TX_Byte when Data Valid is pulsed.
// Keeps local storage of byte in case higher level module changes the data
always@(posedge o_TX_Ready or negedge i_Rst_L)
begin
if (~i_Rst_L)
begin
r_TX_Byte <= 8'h00;
r_TX_DV <= 1'b0;
j<=0;
end
else
begin
r_TX_Byte = lut[j];
j=j+1;
if(j==16) j=0;
end
end
// Purpose: Generate MOSI data
// Works with both CPHA=0 and CPHA=1
always@(posedge i_Clk or negedge i_Rst_L)
begin
if (~i_Rst_L)
begin
o_SPI_MOSI <= 1'b0;
r_TX_Bit_Count <= 3'b111; // send MSb first
end
else
begin
// If ready is high, reset bit counts to default
if (o_TX_Ready)
begin
r_TX_Bit_Count <= 3'b111; //8비트니까
end
// Catch the case where we start transaction and CPHA = 0
else if ((r_Leading_Edge & w_CPHA) | (r_Trailing_Edge & ~w_CPHA)) //시뮬레이션에서 모드 3으로 해서 CPHA 계속 1임
//CPHA 1일때는 상승에지일 때, CPHA 0일 때는 하강에지일 때 (CPOL 1일때로 가정하는건가?)
//1비트씩 수신
begin
r_TX_Bit_Count <= r_TX_Bit_Count - 1'b1;
o_SPI_MOSI <= r_TX_Byte[r_TX_Bit_Count]; //나머지 7비트 전송
end
end
end
/*
// Purpose: Read in MISO data.
always @(posedge i_Clk or negedge i_Rst_L)
begin
if (~i_Rst_L)
begin
o_RX_Byte <= 8'h00;
o_RX_DV <= 1'b0;
r_RX_Bit_Count <= 3'b111;
end
else
begin
// Default Assignments
o_RX_DV <= 1'b0;
if (o_TX_Ready) // Check if ready is high, if so reset bit count to default
begin
r_RX_Bit_Count <= 3'b111;
end
else if ((r_Leading_Edge & ~w_CPHA) | (r_Trailing_Edge & w_CPHA))
begin
o_RX_Byte[r_RX_Bit_Count] <= i_SPI_MISO; // Sample data
r_RX_Bit_Count <= r_RX_Bit_Count - 1'b1;
if (r_RX_Bit_Count == 3'b000)
begin
o_RX_DV <= 1'b1; // Byte done, pulse Data Valid
end
end
end
end
*/
// Purpose: Add clock delay to signals for alignment.
always@(posedge i_Clk or negedge i_Rst_L)
begin
if (~i_Rst_L)
begin
o_SPI_Clk <= w_CPOL;
end
else
begin
o_SPI_Clk <= r_SPI_Clk;
end // else: !if(~i_Rst_L)
end // always @ (posedge i_Clk or negedge i_Rst_L)
endmodule // SPI_Master
///////////////////////////////////////////////////////////////////////////////
// Description: Simple test bench for SPI Master module
///////////////////////////////////////////////////////////////////////////////
module SPI_Master_TB ();
parameter SPI_MODE = 0; // CPOL = 1, CPHA = 1
parameter CLKS_PER_HALF_BIT = 4; // 6.25 MHz SCK의 반주기에 fpga clk이 4번 들어감.
parameter MAIN_CLK_DELAY = 2; // 25 MHz
reg r_Rst_L = 1'b0;
wire w_SPI_Clk;
reg r_Clk = 1'b0;
wire w_SPI_MOSI;
// Master Specific
reg [7:0] r_Master_TX_Byte = 0;
reg r_Master_TX_DV = 1'b0;
wire w_Master_TX_Ready;
wire r_Master_RX_DV;
wire [7:0] r_Master_RX_Byte;
// Clock Generators:
always #(MAIN_CLK_DELAY) r_Clk = ~r_Clk;
// Instantiate UUT
SPI_Master
#(.SPI_MODE(SPI_MODE),
.CLKS_PER_HALF_BIT(CLKS_PER_HALF_BIT)) SPI_Master_UUT
(
// Control/Data Signals,
.i_Rst_L(r_Rst_L), // FPGA Reset
.i_Clk(r_Clk), // FPGA Clock
// TX (MOSI) Signals
.i_TX_Byte(r_Master_TX_Byte), // Byte to transmit on MOSI
.i_TX_DV(r_Master_TX_DV), // Data Valid Pulse with i_TX_Byte
.o_TX_Ready(w_Master_TX_Ready), // Transmit Ready for Byte
// RX (MISO) Signals
.o_RX_DV(r_Master_RX_DV), // Data Valid pulse (1 clock cycle)
// Byte received on MISO
// SPI Interface
.o_SPI_Clk(w_SPI_Clk),
.i_SPI_MISO(w_SPI_MOSI),
.o_SPI_MOSI(w_SPI_MOSI)
);
// Sends a single byte from master.
task SendSingleByte(input [7:0] data);
begin
@(posedge r_Clk);
r_Master_TX_Byte <= data;
r_Master_TX_DV <= 1'b1;
@(posedge r_Clk);
r_Master_TX_DV <= 1'b0;
@(posedge w_Master_TX_Ready);
end
endtask // SendSingleByte
initial
begin
// Required for EDA Playground
// $dumpfile("dump.vcd");
// $dumpvars;
repeat(10) @(posedge r_Clk);
r_Rst_L = 1'b0;
repeat(10) @(posedge r_Clk);
r_Rst_L = 1'b1;
#100 $finish();
end // initial begin
endmodule // SPI_Slave