반도체, 임베디드 Study/디지털 설계

Verilog - SPI (Master, Slave, FSM) (1) FSM, SLAVE

잇(IT) 2024. 6. 2. 12:25
728x90

* SPI에 대한 기본적인 통신 과정은 알고 있다는 가정에 글을 작성합니다.

https://insoobaik.tistory.com/571 - SPI  이론


FSM

FSM_SPI & SPI_SLAVE

FSM_SPI (Master)

SPI_SLAVE


FSM

FSM (Finite State Machine)

유한 상태 기계는 특정한 상태(state)들 사이를 전이(Transition)하며 동작하는 추상적인 모델이다.

이는 상태(state), 상태 전이(transition),  초기 상태(initial state), 종료 상태(final state)로 구성된다.

 

- 상태(state) : 시스템이나 장치가 가질 수 있는 다양한 조건이나 모드를 나타낸다. 각 상태는 시스템의 특정 동작을 나타낸다.

- 상태 전이(Transition) : 상태 간의 변화를 나타낸다. 특정 조건이 충족되면 시스템은 현재 상태에서 다음 상태로 이동한다.

- 초기 상태(Initial State) : FSM이 시작될 때의 상태이다.

- 종료 상태(Final State) : FSM이 동작을 완료하고 멈추는 상태이다.


FSM_SPI & SPI_SLAVE


FSM_SPI (Master)

 

1. sclk 생성

module fsm_spi (
    input wire clk,
    input wire rst,
    input wire tx_enable,
    output wire sclk
    );

    reg spi_sclk = 0;
    //clk count
    reg [2:0] ccount = 0;
    
    always @(posedge clk)
    begin
        if(!rst && tx_enable)
        begin
            if(ccount < 3)
                ccount <= ccount + 1;
            else
            begin
                ccount <= 0;
                spi_sclk <= ~spi_sclk;
            end
        end
    end
    
    assign sclk = spi_sclk;
endmodule

 

spi에 사용하기 위한 sclk를 생성하기 위해 기본 clk 주기를 ccount를 이용하여 sclk의 주기를 조절한다.

rst 신호 0, tx_enable 신호 1 => 즉, 리셋 상태가 아니고 전송이 가능한 상태일 때 sclk를 생성하여 보내기 시작한다.

 

2. FSM 생성

module fsm_spi #(parameter idle = 0, start_tx = 1, tx_data = 2, end_tx = 3)(
    input wire clk,
    input wire rst,
    input wire tx_enable,
    output reg mosi,
    output reg cs,
    output wire sclk
    );
    
    reg [1:0] state;
    reg [7:0] din = 8'hef;
    reg spi_sclk = 0;
    reg [2:0] count = 0;
    //clk count
    reg [2:0] ccount = 0;
    
    always @(posedge clk)
    begin
        if(!rst && tx_enable)
        begin
            if(ccount < 3)
                ccount <= ccount + 1;
            else
            begin
                ccount <= 0;
                spi_sclk <= ~spi_sclk;
            end
        end
    end
    
    
    always @(posedge sclk)
    begin
        case(state)
            idle :
            begin
                mosi <= 1'b0;
                cs <= 1'b1;
                if(!rst && tx_enable)
                begin
                    state <= tx_data;
                    cs <= 1'b0;
                end
                else
                    state <= idle;
                end
                
            tx_data :
            begin
                if(count < 8)
                begin
                    mosi <= din[7-count];
                    count <= count + 1;
                end
                else
                begin
                    mosi <= 0;
                    cs <= 1'b1;
                    state <= idle;
                end
            end
            
            default : state <= idle;
        endcase
    end

    assign sclk = spi_sclk;
    
endmodule

 

parameter 값을 통해 FSM의 상태 값을 부여하고, 각 상태에 따른 동작을 작성해준다.

 

1.  idle

1.1. 초기 상태로 해당 상태에선 mosi, cs 값을 0, 1(mosi 데이터를 전달하지 않고, spi는 cs가 0일 경우 전송하기 때문에 1로 초기화 시킨다.) 로 초기화 한다.

1.2. 만약 rst이 0이고, tx_enable 신호가 1(전송 가능) 상태로 변경되면 state를 tx_data(데이터 전송) 상태로 변경한다.

1.3. 또한 spi는 cs 신호에 의해 master slave가 데이터를 주고 받기 때문에 cs 값을 0으로 변경해준다.

2. tx_data

2.1. 데이터를 전송하는 단계로, 현재 상황에선 임시 데이터 din을 통해 mosi로 데이터를 전달한다.

2.2. sclk에 맞게 8비트 din 데이터를 MSB부터 하나씩 mosi로 전달한다.

2.3. 8비트 데이터를 전부 전달한 이후 다시 idle 상태로 변경 한다.

위 코드를 시뮬레이션으로 동작시키게 되면, rst = 0, tx_enable = 1가 된 이후 첫번째 sclk의 rising edge에서 state가 idle -> tx_data 상태로 변경되고, sclk의 rising edge에 맞게 count가 증가되고 din의 데이터가 하나씩 mosi를 통해 전달되는 것을 확인할 수 있다.

 

fsm_spi는 fsm 구성 및 master와 유사한 동작을 하는 코드에 해당한다.


SPI_SLAVE

 

module spi_slave #(parameter idle = 0, sample = 1) (
    input sclk, mosi, cs,
    output [7:0] dout,
    output reg done
    );
    
    reg [2:0] count = 0;
    reg [1:0] state;
    reg [7:0] data = 0;
    
    always @(negedge sclk)
    begin
        case (state)
            idle :
            begin
                done <= 1'b0;
                if(cs == 1'b0)
                    state <= sample;
                else
                    state <= idle; 
            end
            
            sample :
            begin
                if(count < 8)
                begin
                    count <= count + 1;
                    data <= {data[6:0], mosi};
                    state <= sample;
                end
                else
                begin
                    count <= 0;
                    state <= idle;
                    done <= 1'b1;
                end
            end
            
            default : state <= idle;
        endcase
    end
    
    assign dout = data; 
    
endmodule

 

spi_slave는 sclk를 기준으로 동작한다. 마찬가지로 fsm을 통해 2가지 상태를 정의한다.

idle은 초기 상태를 sample은 데이터를 샘플링 즉 mosi로 데이터를 받는 동작에 해당한다.

1. idle

1.1. done값이 0이 되고,  cs 즉 slave select 신호가 0이 되면 spi master slave가 신호를 주고 받을 수 있는 상태이기 때문에 state를 sample 상태로 변경시킨다.

2. sample

2.1. 샘플링 단계에서는 8bit 데이터를 받아오기 위해 count를 1씩 추가 시킨다.

2.2. mosi로 들어온 데이터는 레지스터의 LSB를 통해 쌓이게 된다.

2.3. count가 8 즉, 8bit 데이터가 전부 전송되면 slave의 상태를 idle 상태로 변경하고, 완료되었다는 done을 1 신호로 변경한다.

 

rst = 0, tx_enable = 1일 때 sclk의 주기에 따라 state가 FSM의 state가 idle로 변경된다. 

 

idle로 변경된 이후 다음 rising edge에서 cs의 값이 0이되고,  state는 tx_data로 변경된다.

 

임시 데이터인 din의 데이터에 의해 mosi를 통해 slave에 데이터가 전달되고, negedge에서 slave는 dout 즉 miso를 통해 데이터를 전달한다.

728x90