UART 통신과 달리 SPI 통신은 putty와 같은 프로그램으로 PC에서 데이터를 받을 수 없다
-> 별도의 HW와 SW툴이 필요한데 가격이 너무 비쌈
-> PC에서 SPI 데이터 받는 라이브러리와 이를 가능하게 하는? 칩이 들어있는 케이블을 이용해서 인터페이스 개발
PC-FPGA: 6Mbps 필요


2가지 중 한가지 방법으로 하면 되는데 2번째 방법으로 구현해주면 더 좋다
1️⃣ Tx와 Rx 데이터를 buffer에 담아서 보내기(fifo memory로 구현하면 될 듯)
2️⃣ Tx 신호-Rx 신호 차이 구해서 beat signal 데이터로 보내기(이걸 보내주면 PC단에서는 더 좋다!)

SPI도 DAC와 마찬가지로 필요한 클럭을 클럭 위저드로 생성해준다

spi 입력으로 넣어준 클럭(clk_SPI)이 12MHz고 CLK_PER_HALF_BIT가 1이니까 clk_SPI의 2배인 6MHz로 나옴→스펙에 맞음! good
회로 합성해보면

//top 모듈
`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company:
// Engineer:
//
// Create Date: 2024/07/27 16:15:40
// Design Name:
// Module Name: top_SPI
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//////////////////////////////////////////////////////////////////////////////////
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 = 3,
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;
// 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);
// 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 (i_TX_DV)
begin
o_TX_Ready <= 1'b0;
r_SPI_Clk_Edges <= 16; // Total # edges in one byte ALWAYS 16
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
begin
o_TX_Ready <= 1'b1;
end
end // else: !if(~i_Rst_L)
end // always @ (posedge i_Clk or negedge i_Rst_L)
//여기까지 내가 원하는 SCK 생성 파트
// 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 i_Clk or negedge i_Rst_L)
begin
if (~i_Rst_L)
begin
r_TX_Byte <= 8'h00;
r_TX_DV <= 1'b0;
end
else
begin
r_TX_DV <= i_TX_DV; // 1 clock cycle delay
if (i_TX_DV)
begin
r_TX_Byte <= 8'b10010010;
end
end // else: !if(~i_Rst_L)
end // always @ (posedge i_Clk or negedge i_Rst_L)
// 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_TX_DV & ~w_CPHA)
begin
o_SPI_MOSI <= r_TX_Byte[3'b111]; //MSB 전송
r_TX_Bit_Count <= 3'b110;
end
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
## This file is a general .xdc for the Zybo Z7 Rev. B
## It is compatible with the Zybo Z7-20 and Zybo Z7-10
## To use it in a project:
## - uncomment the lines corresponding to used pins
## - rename the used ports (in each line, after get_ports) according to the top level signal names in the project
##Clock signal
set_property -dict { PACKAGE_PIN K17 IOSTANDARD LVCMOS33 } [get_ports {clk}]; #IO_L12P_T1_MRCC_35 Sch=sysclk
#create_clock -add -name sys_clk_pin -period 8000000 -waveform {0 4000000} [get_ports { i_Clk }];
##Switches
set_property -dict { PACKAGE_PIN G15 IOSTANDARD LVCMOS33 } [get_ports { i_Rst_L_ }]; #IO_L19N_T3_VREF_35 Sch=sw[0]
#set_property -dict { PACKAGE_PIN P15 IOSTANDARD LVCMOS33 } [get_ports { sw[1] }]; #IO_L24P_T3_34 Sch=sw[1]
#set_property -dict { PACKAGE_PIN W13 IOSTANDARD LVCMOS33 } [get_ports { sw[2] }]; #IO_L4N_T0_34 Sch=sw[2]
#set_property -dict { PACKAGE_PIN T16 IOSTANDARD LVCMOS33 } [get_ports { sw[3] }]; #IO_L9P_T1_DQS_34 Sch=sw[3]
##Buttons
set_property -dict { PACKAGE_PIN K18 IOSTANDARD LVCMOS33 } [get_ports { i_TX_DV_ }]; #IO_L12N_T1_MRCC_35 Sch=btn[0]
#set_property -dict { PACKAGE_PIN P16 IOSTANDARD LVCMOS33 } [get_ports { btn[1] }]; #IO_L24N_T3_34 Sch=btn[1]
#set_property -dict { PACKAGE_PIN K19 IOSTANDARD LVCMOS33 } [get_ports { btn[2] }]; #IO_L10P_T1_AD11P_35 Sch=btn[2]
#set_property -dict { PACKAGE_PIN Y16 IOSTANDARD LVCMOS33 } [get_ports { btn[3] }]; #IO_L7P_T1_34 Sch=btn[3]
##LEDs
#set_property -dict { PACKAGE_PIN M14 IOSTANDARD LVCMOS33 } [get_ports { test_LED }]; #IO_L23P_T3_35 Sch=led[0]
#set_property -dict { PACKAGE_PIN M15 IOSTANDARD LVCMOS33 } [get_ports { led[1] }]; #IO_L23N_T3_35 Sch=led[1]
#set_property -dict { PACKAGE_PIN G14 IOSTANDARD LVCMOS33 } [get_ports { led[2] }]; #IO_0_35 Sch=led[2]
#set_property -dict { PACKAGE_PIN D18 IOSTANDARD LVCMOS33 } [get_ports { test_LED }]; #IO_L3N_T0_DQS_AD1N_35 Sch=led[3]
##RGB LED 5 (Zybo Z7-20 only)
#set_property -dict { PACKAGE_PIN Y11 IOSTANDARD LVCMOS33 } [get_ports { led5_r }]; #IO_L18N_T2_13 Sch=led5_r
#set_property -dict { PACKAGE_PIN T5 IOSTANDARD LVCMOS33 } [get_ports { led5_g }]; #IO_L19P_T3_13 Sch=led5_g
#set_property -dict { PACKAGE_PIN Y12 IOSTANDARD LVCMOS33 } [get_ports { led5_b }]; #IO_L20P_T3_13 Sch=led5_b
##RGB LED 6
#set_property -dict { PACKAGE_PIN V16 IOSTANDARD LVCMOS33 } [get_ports { led6_r }]; #IO_L18P_T2_34 Sch=led6_r
#set_property -dict { PACKAGE_PIN F17 IOSTANDARD LVCMOS33 } [get_ports { led6_g }]; #IO_L6N_T0_VREF_35 Sch=led6_g
#set_property -dict { PACKAGE_PIN M17 IOSTANDARD LVCMOS33 } [get_ports { led6_b }]; #IO_L8P_T1_AD10P_35 Sch=led6_b
##Audio Codec
#set_property -dict { PACKAGE_PIN R19 IOSTANDARD LVCMOS33 } [get_ports { ac_bclk }]; #IO_0_34 Sch=ac_bclk
#set_property -dict { PACKAGE_PIN R17 IOSTANDARD LVCMOS33 } [get_ports { ac_mclk }]; #IO_L19N_T3_VREF_34 Sch=ac_mclk
#set_property -dict { PACKAGE_PIN P18 IOSTANDARD LVCMOS33 } [get_ports { ac_muten }]; #IO_L23N_T3_34 Sch=ac_muten
#set_property -dict { PACKAGE_PIN R18 IOSTANDARD LVCMOS33 } [get_ports { ac_pbdat }]; #IO_L20N_T3_34 Sch=ac_pbdat
#set_property -dict { PACKAGE_PIN T19 IOSTANDARD LVCMOS33 } [get_ports { ac_pblrc }]; #IO_25_34 Sch=ac_pblrc
#set_property -dict { PACKAGE_PIN R16 IOSTANDARD LVCMOS33 } [get_ports { ac_recdat }]; #IO_L19P_T3_34 Sch=ac_recdat
#set_property -dict { PACKAGE_PIN Y18 IOSTANDARD LVCMOS33 } [get_ports { ac_reclrc }]; #IO_L17P_T2_34 Sch=ac_reclrc
#set_property -dict { PACKAGE_PIN N18 IOSTANDARD LVCMOS33 } [get_ports { ac_scl }]; #IO_L13P_T2_MRCC_34 Sch=ac_scl
#set_property -dict { PACKAGE_PIN N17 IOSTANDARD LVCMOS33 } [get_ports { ac_sda }]; #IO_L23P_T3_34 Sch=ac_sda
##Additional Ethernet signals
#set_property -dict { PACKAGE_PIN F16 IOSTANDARD LVCMOS33 PULLUP true } [get_ports { eth_int_pu_b }]; #IO_L6P_T0_35 Sch=eth_int_pu_b
#set_property -dict { PACKAGE_PIN E17 IOSTANDARD LVCMOS33 } [get_ports { eth_rst_b }]; #IO_L3P_T0_DQS_AD1P_35 Sch=eth_rst_b
##USB-OTG over-current detect pin
#set_property -dict { PACKAGE_PIN U13 IOSTANDARD LVCMOS33 } [get_ports { otg_oc }]; #IO_L3P_T0_DQS_PUDC_B_34 Sch=otg_oc
##Fan (Zybo Z7-20 only)
#set_property -dict { PACKAGE_PIN Y13 IOSTANDARD LVCMOS33 PULLUP true } [get_ports { fan_fb_pu }]; #IO_L20N_T3_13 Sch=fan_fb_pu
##HDMI RX
#set_property -dict { PACKAGE_PIN W19 IOSTANDARD LVCMOS33 } [get_ports { hdmi_rx_hpd }]; #IO_L22N_T3_34 Sch=hdmi_rx_hpd
#set_property -dict { PACKAGE_PIN W18 IOSTANDARD LVCMOS33 } [get_ports { hdmi_rx_scl }]; #IO_L22P_T3_34 Sch=hdmi_rx_scl
#set_property -dict { PACKAGE_PIN Y19 IOSTANDARD LVCMOS33 } [get_ports { hdmi_rx_sda }]; #IO_L17N_T2_34 Sch=hdmi_rx_sda
#set_property -dict { PACKAGE_PIN U19 IOSTANDARD TMDS_33 } [get_ports { hdmi_rx_clk_n }]; #IO_L12N_T1_MRCC_34 Sch=hdmi_rx_clk_n
#set_property -dict { PACKAGE_PIN U18 IOSTANDARD TMDS_33 } [get_ports { hdmi_rx_clk_p }]; #IO_L12P_T1_MRCC_34 Sch=hdmi_rx_clk_p
#set_property -dict { PACKAGE_PIN W20 IOSTANDARD TMDS_33 } [get_ports { hdmi_rx_n[0] }]; #IO_L16N_T2_34 Sch=hdmi_rx_n[0]
#set_property -dict { PACKAGE_PIN V20 IOSTANDARD TMDS_33 } [get_ports { hdmi_rx_p[0] }]; #IO_L16P_T2_34 Sch=hdmi_rx_p[0]
#set_property -dict { PACKAGE_PIN U20 IOSTANDARD TMDS_33 } [get_ports { hdmi_rx_n[1] }]; #IO_L15N_T2_DQS_34 Sch=hdmi_rx_n[1]
#set_property -dict { PACKAGE_PIN T20 IOSTANDARD TMDS_33 } [get_ports { hdmi_rx_p[1] }]; #IO_L15P_T2_DQS_34 Sch=hdmi_rx_p[1]
#set_property -dict { PACKAGE_PIN P20 IOSTANDARD TMDS_33 } [get_ports { hdmi_rx_n[2] }]; #IO_L14N_T2_SRCC_34 Sch=hdmi_rx_n[2]
#set_property -dict { PACKAGE_PIN N20 IOSTANDARD TMDS_33 } [get_ports { hdmi_rx_p[2] }]; #IO_L14P_T2_SRCC_34 Sch=hdmi_rx_p[2]
##HDMI RX CEC (Zybo Z7-20 only)
#set_property -dict { PACKAGE_PIN Y8 IOSTANDARD LVCMOS33 } [get_ports { hdmi_rx_cec }]; #IO_L14N_T2_SRCC_13 Sch=hdmi_rx_cec
##HDMI TX
#set_property -dict { PACKAGE_PIN E18 IOSTANDARD LVCMOS33 } [get_ports { hdmi_tx_hpd }]; #IO_L5P_T0_AD9P_35 Sch=hdmi_tx_hpd
#set_property -dict { PACKAGE_PIN G17 IOSTANDARD LVCMOS33 } [get_ports { hdmi_tx_scl }]; #IO_L16P_T2_35 Sch=hdmi_tx_scl
#set_property -dict { PACKAGE_PIN G18 IOSTANDARD LVCMOS33 } [get_ports { hdmi_tx_sda }]; #IO_L16N_T2_35 Sch=hdmi_tx_sda
#set_property -dict { PACKAGE_PIN H17 IOSTANDARD TMDS_33 } [get_ports { hdmi_tx_clk_n }]; #IO_L13N_T2_MRCC_35 Sch=hdmi_tx_clk_n
#set_property -dict { PACKAGE_PIN H16 IOSTANDARD TMDS_33 } [get_ports { hdmi_tx_clk_p }]; #IO_L13P_T2_MRCC_35 Sch=hdmi_tx_clk_p
#set_property -dict { PACKAGE_PIN D20 IOSTANDARD TMDS_33 } [get_ports { hdmi_tx_n[0] }]; #IO_L4N_T0_35 Sch=hdmi_tx_n[0]
#set_property -dict { PACKAGE_PIN D19 IOSTANDARD TMDS_33 } [get_ports { hdmi_tx_p[0] }]; #IO_L4P_T0_35 Sch=hdmi_tx_p[0]
#set_property -dict { PACKAGE_PIN B20 IOSTANDARD TMDS_33 } [get_ports { hdmi_tx_n[1] }]; #IO_L1N_T0_AD0N_35 Sch=hdmi_tx_n[1]
#set_property -dict { PACKAGE_PIN C20 IOSTANDARD TMDS_33 } [get_ports { hdmi_tx_p[1] }]; #IO_L1P_T0_AD0P_35 Sch=hdmi_tx_p[1]
#set_property -dict { PACKAGE_PIN A20 IOSTANDARD TMDS_33 } [get_ports { hdmi_tx_n[2] }]; #IO_L2N_T0_AD8N_35 Sch=hdmi_tx_n[2]
#set_property -dict { PACKAGE_PIN B19 IOSTANDARD TMDS_33 } [get_ports { hdmi_tx_p[2] }]; #IO_L2P_T0_AD8P_35 Sch=hdmi_tx_p[2]
##HDMI TX CEC
#set_property -dict { PACKAGE_PIN E19 IOSTANDARD LVCMOS33 } [get_ports { hdmi_tx_cec }]; #IO_L5N_T0_AD9N_35 Sch=hdmi_tx_cec
##Pmod Header JA (XADC)
#set_property -dict { PACKAGE_PIN N15 IOSTANDARD LVCMOS33 } [get_ports { ja[0] }]; #IO_L21P_T3_DQS_AD14P_35 Sch=JA1_R_p
#set_property -dict { PACKAGE_PIN L14 IOSTANDARD LVCMOS33 } [get_ports { ja[1] }]; #IO_L22P_T3_AD7P_35 Sch=JA2_R_P
#set_property -dict { PACKAGE_PIN K16 IOSTANDARD LVCMOS33 } [get_ports { ja[2] }]; #IO_L24P_T3_AD15P_35 Sch=JA3_R_P
#set_property -dict { PACKAGE_PIN K14 IOSTANDARD LVCMOS33 } [get_ports { ja[3] }]; #IO_L20P_T3_AD6P_35 Sch=JA4_R_P
#set_property -dict { PACKAGE_PIN N16 IOSTANDARD LVCMOS33 } [get_ports { ja[4] }]; #IO_L21N_T3_DQS_AD14N_35 Sch=JA1_R_N
#set_property -dict { PACKAGE_PIN L15 IOSTANDARD LVCMOS33 } [get_ports { ja[5] }]; #IO_L22N_T3_AD7N_35 Sch=JA2_R_N
#set_property -dict { PACKAGE_PIN J16 IOSTANDARD LVCMOS33 } [get_ports { ja[6] }]; #IO_L24N_T3_AD15N_35 Sch=JA3_R_N
#set_property -dict { PACKAGE_PIN J14 IOSTANDARD LVCMOS33 } [get_ports { ja[7] }]; #IO_L20N_T3_AD6N_35 Sch=JA4_R_N
##Pmod Header JB (Zybo Z7-20 only)
#set_property -dict { PACKAGE_PIN V8 IOSTANDARD LVCMOS33 } [get_ports { jb[0] }]; #IO_L15P_T2_DQS_13 Sch=jb_p[1]
#set_property -dict { PACKAGE_PIN W8 IOSTANDARD LVCMOS33 } [get_ports { jb[1] }]; #IO_L15N_T2_DQS_13 Sch=jb_n[1]
#set_property -dict { PACKAGE_PIN U7 IOSTANDARD LVCMOS33 } [get_ports { jb[2] }]; #IO_L11P_T1_SRCC_13 Sch=jb_p[2]
#set_property -dict { PACKAGE_PIN V7 IOSTANDARD LVCMOS33 } [get_ports { jb[3] }]; #IO_L11N_T1_SRCC_13 Sch=jb_n[2]
#set_property -dict { PACKAGE_PIN Y7 IOSTANDARD LVCMOS33 } [get_ports { jb[4] }]; #IO_L13P_T2_MRCC_13 Sch=jb_p[3]
#set_property -dict { PACKAGE_PIN Y6 IOSTANDARD LVCMOS33 } [get_ports { jb[5] }]; #IO_L13N_T2_MRCC_13 Sch=jb_n[3]
#set_property -dict { PACKAGE_PIN V6 IOSTANDARD LVCMOS33 } [get_ports { jb[6] }]; #IO_L22P_T3_13 Sch=jb_p[4]
#set_property -dict { PACKAGE_PIN W6 IOSTANDARD LVCMOS33 } [get_ports { jb[7] }]; #IO_L22N_T3_13 Sch=jb_n[4]
##Pmod Header JC
#set_property -dict { PACKAGE_PIN V15 IOSTANDARD LVCMOS33 } [get_ports { jc[0] }]; #IO_L10P_T1_34 Sch=jc_p[1]
#set_property -dict { PACKAGE_PIN W15 IOSTANDARD LVCMOS33 } [get_ports { jc[1] }]; #IO_L10N_T1_34 Sch=jc_n[1]
#set_property -dict { PACKAGE_PIN T11 IOSTANDARD LVCMOS33 } [get_ports { jc[2] }]; #IO_L1P_T0_34 Sch=jc_p[2]
#set_property -dict { PACKAGE_PIN T10 IOSTANDARD LVCMOS33 } [get_ports { jc[3] }]; #IO_L1N_T0_34 Sch=jc_n[2]
#set_property -dict { PACKAGE_PIN W14 IOSTANDARD LVCMOS33 } [get_ports { jc[4] }]; #IO_L8P_T1_34 Sch=jc_p[3]
#set_property -dict { PACKAGE_PIN Y14 IOSTANDARD LVCMOS33 } [get_ports { jc[5] }]; #IO_L8N_T1_34 Sch=jc_n[3]
#set_property -dict { PACKAGE_PIN T12 IOSTANDARD LVCMOS33 } [get_ports { jc[6] }]; #IO_L2P_T0_34 Sch=jc_p[4]
#set_property -dict { PACKAGE_PIN U12 IOSTANDARD LVCMOS33 } [get_ports { jc[7] }]; #IO_L2N_T0_34 Sch=jc_n[4]
##Pmod Header JD
#set_property -dict { PACKAGE_PIN T14 IOSTANDARD LVCMOS33 } [get_ports { o_SPI_MOSI }]; #IO_L5P_T0_34 Sch=jd_p[1]
#set_property -dict { PACKAGE_PIN T15 IOSTANDARD LVCMOS33 } [get_ports { i_SPI_MISO }]; #IO_L5N_T0_34 Sch=jd_n[1]
#set_property -dict { PACKAGE_PIN P14 IOSTANDARD LVCMOS33 } [get_ports { o_SPI_Clk }]; #IO_L6P_T0_34 Sch=jd_p[2]
#set_property -dict { PACKAGE_PIN R14 IOSTANDARD LVCMOS33 } [get_ports { o_RX_DV}]; #IO_L6N_T0_VREF_34 Sch=jd_n[2]
#set_property -dict { PACKAGE_PIN U14 IOSTANDARD LVCMOS33 } [get_ports { o_TX_Ready }]; #IO_L11P_T1_SRCC_34 Sch=jd_p[3]
#set_property -dict { PACKAGE_PIN U15 IOSTANDARD LVCMOS33 } [get_ports { i_Clk }]; #IO_L11N_T1_SRCC_34 Sch=jd_n[3]
#set_property -dict { PACKAGE_PIN V17 IOSTANDARD LVCMOS33 } [get_ports { jd[6] }]; #IO_L21P_T3_DQS_34 Sch=jd_p[4]
#set_property -dict { PACKAGE_PIN V18 IOSTANDARD LVCMOS33 } [get_ports { jd[7] }]; #IO_L21N_T3_DQS_34 Sch=jd_n[4]
##Pmod Header JE
set_property -dict { PACKAGE_PIN V12 IOSTANDARD LVCMOS33 } [get_ports { o_SPI_MOSI_ }]; #IO_L4P_T0_34 Sch=je[1]
set_property -dict { PACKAGE_PIN W16 IOSTANDARD LVCMOS33 } [get_ports { i_SPI_MISO_ }]; #IO_L18N_T2_34 Sch=je[2]
set_property -dict { PACKAGE_PIN J15 IOSTANDARD LVCMOS33 } [get_ports { o_SPI_Clk_ }]; #IO_25_35 Sch=je[3]
set_property -dict { PACKAGE_PIN H15 IOSTANDARD LVCMOS33 } [get_ports { o_RX_DV_ }]; #IO_L19P_T3_35 Sch=je[4]
set_property -dict { PACKAGE_PIN V13 IOSTANDARD LVCMOS33 } [get_ports { o_TX_Ready_}]; #IO_L3N_T0_DQS_34 Sch=je[7]
set_property -dict { PACKAGE_PIN U17 IOSTANDARD LVCMOS33 } [get_ports {clk_SPI }]; #IO_L9N_T1_DQS_34 Sch=je[8]
#set_property -dict { PACKAGE_PIN T17 IOSTANDARD LVCMOS33 } [get_ports { je[6] }]; #IO_L20P_T3_34 Sch=je[9]
#set_property -dict { PACKAGE_PIN Y17 IOSTANDARD LVCMOS33 } [get_ports { je[7] }]; #IO_L7N_T1_34 Sch=je[10]
##Pcam MIPI CSI-2 Connector
## This configuration expects the sensor to use 672Mbps/lane = 336 MHz HS_Clk
#create_clock -period 2.976 -name dphy_hs_clock_clk_p -waveform {0.000 1.488} [get_ports dphy_hs_clock_clk_p]
#set_property INTERNAL_VREF 0.6 [get_iobanks 35]
#set_property -dict { PACKAGE_PIN J19 IOSTANDARD HSUL_12 } [get_ports { dphy_clk_lp_n }]; #IO_L10N_T1_AD11N_35 Sch=lp_clk_n
#set_property -dict { PACKAGE_PIN H20 IOSTANDARD HSUL_12 } [get_ports { dphy_clk_lp_p }]; #IO_L17N_T2_AD5N_35 Sch=lp_clk_p
#set_property -dict { PACKAGE_PIN M18 IOSTANDARD HSUL_12 } [get_ports { dphy_data_lp_n[0] }]; #IO_L8N_T1_AD10N_35 Sch=lp_lane_n[0]
#set_property -dict { PACKAGE_PIN L19 IOSTANDARD HSUL_12 } [get_ports { dphy_data_lp_p[0] }]; #IO_L9P_T1_DQS_AD3P_35 Sch=lp_lane_p[0]
#set_property -dict { PACKAGE_PIN L20 IOSTANDARD HSUL_12 } [get_ports { dphy_data_lp_n[1] }]; #IO_L9N_T1_DQS_AD3N_35 Sch=lp_lane_n[1]
#set_property -dict { PACKAGE_PIN J20 IOSTANDARD HSUL_12 } [get_ports { dphy_data_lp_p[1] }]; #IO_L17P_T2_AD5P_35 Sch=lp_lane_p[1]
#set_property -dict { PACKAGE_PIN H18 IOSTANDARD LVDS_25 } [get_ports { dphy_hs_clock_clk_n }]; #IO_L14N_T2_AD4N_SRCC_35 Sch=mipi_clk_n
#set_property -dict { PACKAGE_PIN J18 IOSTANDARD LVDS_25 } [get_ports { dphy_hs_clock_clk_p }]; #IO_L14P_T2_AD4P_SRCC_35 Sch=mipi_clk_p
#set_property -dict { PACKAGE_PIN M20 IOSTANDARD LVDS_25 } [get_ports { dphy_data_hs_n[0] }]; #IO_L7N_T1_AD2N_35 Sch=mipi_lane_n[0]
#set_property -dict { PACKAGE_PIN M19 IOSTANDARD LVDS_25 } [get_ports { dphy_data_hs_p[0] }]; #IO_L7P_T1_AD2P_35 Sch=mipi_lane_p[0]
#set_property -dict { PACKAGE_PIN L17 IOSTANDARD LVDS_25 } [get_ports { dphy_data_hs_n[1] }]; #IO_L11N_T1_SRCC_35 Sch=mipi_lane_n[1]
#set_property -dict { PACKAGE_PIN L16 IOSTANDARD LVDS_25 } [get_ports { dphy_data_hs_p[1] }]; #IO_L11P_T1_SRCC_35 Sch=mipi_lane_p[1]
#set_property -dict { PACKAGE_PIN G19 IOSTANDARD LVCMOS33 } [get_ports { cam_clk }]; #IO_L18P_T2_AD13P_35 Sch=cam_clk
#set_property -dict { PACKAGE_PIN G20 IOSTANDARD LVCMOS33 PULLUP true} [get_ports { cam_gpio }]; #IO_L18N_T2_AD13N_35 Sch=cam_gpio
#set_property -dict { PACKAGE_PIN F20 IOSTANDARD LVCMOS33 } [get_ports { cam_scl }]; #IO_L15N_T2_DQS_AD12N_35 Sch=cam_scl
#set_property -dict { PACKAGE_PIN F19 IOSTANDARD LVCMOS33 } [get_ports { cam_sda }]; #IO_L15P_T2_DQS_AD12P_35 Sch=cam_sda
##Unloaded Crypto Chip SWI (for future use)
#set_property -dict { PACKAGE_PIN P19 IOSTANDARD LVCMOS33 } [get_ports { crypto_sda }]; #IO_L13N_T2_MRCC_34 Sch=crypto_sda
##Unconnected Pins (Zybo Z7-20 only)
#set_property PACKAGE_PIN T9 [get_ports {netic19_t9}]; #IO_L12P_T1_MRCC_13
#set_property PACKAGE_PIN U10 [get_ports {netic19_u10}]; #IO_L12N_T1_MRCC_13
#set_property PACKAGE_PIN U5 [get_ports {netic19_u5}]; #IO_L19N_T3_VREF_13
#set_property PACKAGE_PIN U8 [get_ports {netic19_u8}]; #IO_L17N_T2_13
#set_property PACKAGE_PIN U9 [get_ports {netic19_u9}]; #IO_L17P_T2_13
#set_property PACKAGE_PIN V10 [get_ports {netic19_v10}]; #IO_L21N_T3_DQS_13
#set_property PACKAGE_PIN V11 [get_ports {netic19_v11}]; #IO_L21P_T3_DQS_13
#set_property PACKAGE_PIN V5 [get_ports {netic19_v5}]; #IO_L6N_T0_VREF_13
#set_property PACKAGE_PIN W10 [get_ports {netic19_w10}]; #IO_L16P_T2_13
#set_property PACKAGE_PIN W11 [get_ports {netic19_w11}]; #IO_L18P_T2_13
#set_property PACKAGE_PIN W9 [get_ports {netic19_w9}]; #IO_L16N_T2_13
#set_property PACKAGE_PIN Y9 [get_ports {netic19_y9}]; #IO_L14P_T2_SRCC_13

https://www.youtube.com/watch?v=mvpX3D-ddmE
https://ftdichip.com/products/c232hm-ddhsl-0-2/
https://ftdichip.com/software-examples/mpsse-projects/libmpsse-spi-examples/
아무래도 같은 케이블을 써도 저 영상에서는 자이로 센서의 값을 받아오는거고 나는 FPGA 출력핀을 연결하는거라 분명 인터페이스 코드 상에 차이점이 존재할텐데
내가 일단 짠 코드로는 동작을 안한다.. 해결 필요한 상태
#include <stdio.h>
#include <stdint.h>
#include "ftd2xx.h"
#include "libMPSSE_spi.h"
#define CHECK_STATUS(result) if (result != FT_OK) { printf("Error: %s\n", ErrorString(result)); exit(1); }
const char* ErrorString(FT_STATUS status) {
switch (status) {
case FT_OK: return "FT_OK";
case FT_INVALID_HANDLE: return "FT_INVALID_HANDLE";
case FT_DEVICE_NOT_FOUND: return "FT_DEVICE_NOT_FOUND";
case FT_DEVICE_NOT_OPENED: return "FT_DEVICE_NOT_OPENED";
case FT_IO_ERROR: return "FT_IO_ERROR";
case FT_INSUFFICIENT_RESOURCES: return "FT_INSUFFICIENT_RESOURCES";
default: return "UNKNOWN_ERROR";
}
}
FT_STATUS read_data_from_FPGA(FT_HANDLE ftHandle, uint8_t* data) {
FT_STATUS status;
uint32_t sizeTransferred = 0;
// Directly read data without addressing
status = SPI_Read(ftHandle, data, 1, (LPDWORD) & sizeTransferred, SPI_TRANSFER_OPTIONS_SIZE_IN_BYTES | SPI_TRANSFER_OPTIONS_CHIPSELECT_ENABLE | SPI_TRANSFER_OPTIONS_CHIPSELECT_DISABLE);
if (status != FT_OK) {
printf("Failed to read data: %s\n", ErrorString(status));
}
else {
printf("Read 1 byte: 0x%X\n", *data);
}
return status;
}
int main() {
FT_STATUS status;
FT_HANDLE ftHandle;
uint32_t channels;
// Initialize the library
Init_libMPSSE();
printf("LibMPSSE initialized.\n");
// Get the number of available SPI channels
status = SPI_GetNumChannels((DWORD*) & channels);
CHECK_STATUS(status);
printf("Number of available SPI channels: %u\n", channels);
if (channels > 0) {
// Open the first available channel
status = SPI_OpenChannel(0, &ftHandle);
CHECK_STATUS(status);
printf("SPI channel opened successfully.\n");
while (1) {
uint8_t receivedData;
// Read data from FPGA
read_data_from_FPGA(ftHandle, &receivedData);
}
// Close the channel
status = SPI_CloseChannel(ftHandle);
CHECK_STATUS(status);
printf("SPI channel closed.\n");
}
// Cleanup the library
Cleanup_libMPSSE();
printf("LibMPSSE cleaned up.\n");
return 0;
}

펄스 줘도(임의로 만들어줬던 데이터를 줘도) 인터페이스 출력에는 아무 변화 없다
-> SPI를 펄스없이 데이터 주도록 수정 중 ...
---------------------0804 업데이트(이전에 한거고 글만 지금 업데이트)-------------
안되는 이유 찾는 중
너무 짧은 순간에 데이터를 줘서 인터페이스쪽에서 못 받을 수도 있을 것 같아서
일단 SPI 코드를 i_TX_DV없이 계속 데이터가 출력되도록 만들자(스위치나 버튼 안써도 데이터 나올 수 있도록)
module SPI_Master
#(parameter SPI_MODE = 0,
parameter CLKS_PER_HALF_BIT = 4) //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 -다음 바이트를 보낼 준비가 되었다(바이트를 비트로 바꾸기 위해서 필요?)
output reg o_RX_DV, // Data Valid pulse (1 clock cycle)
// SPI Interface
output reg o_SPI_Clk,
input i_SPI_MISO,
output reg o_SPI_MOSI
);
// 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;
assign w_CPOL = (SPI_MODE == 2) | (SPI_MODE == 3);
assign w_CPHA = (SPI_MODE == 1) | (SPI_MODE == 3);
// 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 i_Clk or negedge i_Rst_L)
begin
if (~i_Rst_L)
begin
r_TX_Byte <= 8'h00;
r_TX_DV <= 1'b0;
end
else
begin
r_TX_DV <=1; // 1 clock cycle delay
if (1)
begin
r_TX_Byte <= i_TX_Byte;
end
end // else: !if(~i_Rst_L)
end // always @ (posedge i_Clk or negedge i_Rst_L)
// 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: 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)
);
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;
// Test single byte
SendSingleByte(8'hC1);
$display("Sent out 0xC1, Received 0x%X", r_Master_RX_Byte);
// Test double byte
#50 SendSingleByte(8'hBE);
$display("Sent out 0xBE, Received 0x%X", r_Master_RX_Byte);
SendSingleByte(8'hEF);
$display("Sent out 0xEF, Received 0x%X", r_Master_RX_Byte);
SendSingleByte(8'h0A);
$display("Sent out 0xEF, Received 0x%X", r_Master_RX_Byte);
repeat(10) @(posedge r_Clk);
$finish();
end // initial begin
endmodule // SPI_Slave
o_TX_Ready가 내려온 후 바로 다음 SCK 하강에지에서 다음 데이터 수신

MOSI, TX_Byte, o_TX_Ready에 해당하는 변수만 파형 보면 됨.
내부 데이터 LUT에서 넘겨주기
`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
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;
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)
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))
//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


데이터가 반복적으로 들어오는 것 확인 가능
정리
사용하고 있는 인터페이스 코드
#include <stdio.h>
#include <stdint.h>
#include <Windows.h>
#include "ftd2xx.h"
#include "libmpsse_spi.h"
void print_and_quit(const char cstring[]) {
printf("%s\n", cstring);
getc(stdin);
exit(1);
}
int main(int argc, char** argv) {
Init_libMPSSE();
FT_STATUS status;
FT_HANDLE handle;
// SPI 채널 초기화
uint32_t channel = 0; // 사용할 채널 번호, 필요에 따라 변경 가능
ChannelConfig channelConfig;
channelConfig.ClockRate = 6000000; // 6MHz 클록 설정
channelConfig.configOptions = SPI_CONFIG_OPTION_MODE0 | SPI_CONFIG_OPTION_CS_DBUS3 | SPI_CONFIG_OPTION_CS_ACTIVELOW;
channelConfig.LatencyTimer = 255;
status = SPI_OpenChannel(channel, &handle);
if (status != FT_OK)
print_and_quit("Error while opening the MPSSE channel.");
status = SPI_InitChannel(handle, &channelConfig);
if (status != FT_OK)
print_and_quit("Error while initializing the MPSSE channel.");
// 연속 데이터 수신 루프
uint8_t rx_byte = 0;
uint8_t bit_count = 0;
while (1) {
uint8_t rx_buffer[1] = { 0 }; // 1바이트 수신 버퍼
uint32_t transferCount = 0;
uint32_t options = SPI_TRANSFER_OPTIONS_SIZE_IN_BYTES | SPI_TRANSFER_OPTIONS_CHIPSELECT_ENABLE | SPI_TRANSFER_OPTIONS_CHIPSELECT_DISABLE;
status = SPI_Read(handle, rx_buffer, 1, (LPDWORD)&transferCount, options);
if (status != FT_OK)
print_and_quit("Error while reading data.");
// 비트 누적
rx_byte |= (rx_buffer[0] & 0x01) << bit_count;
bit_count++;
// 8비트가 모이면 출력
if (bit_count == 8) {
printf("%02X\n", rx_byte); // 16진수 형태로 출력
rx_byte = 0; // 변수 초기화
bit_count = 0; // 비트 카운트 초기화
}
}
Cleanup_libMPSSE();
return 0;
}
출력해보면

이상하다..

그러나 FPGA출력단은 깔끔하게 잘 나옴(sck 0일때만 송신)
SPI 통신을 위한 케이블을 연결하면 파형이 진폭 줄거나 노이즈 심하거나...
문제점으로 추론된 것들
케이블 관련 참고 자료
https://ftdichip.com/products/c232hm-ddhsl-0-2/
https://ftdichip.com/software-examples/mpsse-projects/libmpsse-spi-examples/
https://ftdichip.com/wp-content/uploads/2023/08/AN_178_User-Guide-for-LibMPSSE-SPI.pdf