본문 바로가기
DSP, MCU/TMS320F2838x (C28x)

TMS320F28388D ] IPC 사용하여 CPU1과 CPU2 타이머 Tick 값 Synchronization

by eteo 2023. 1. 26.

 

CPU1 코드

 

// ...

IPC_clearFlagLtoR(IPC_CPU1_L_CPU2_R, IPC_FLAG_ALL);
IPC_sync(IPC_CPU1_L_CPU2_R, IPC_FLAG31);
msTick = 0;
CPUTimer_startTimer(CPUTIMER0_BASE);
    
// ...

 

 

 

CPU2 코드

 

// ...

IPC_clearFlagLtoR(IPC_CPU2_L_CPU1_R, IPC_FLAG_ALL);
IPC_sync(IPC_CPU2_L_CPU1_R, IPC_FLAG31);
msTick = 0;
CPUTimer_startTimer(CPUTIMER0_BASE);

// ...

 

 

 

 

두 코어간 동기화를 위한 API인 IPC_Sync() 함수 내부는 다음과 같다.

//! Synchronises the two cores
//!
//! \param ipcType is the enum corresponding to the IPC instance used
//! \param flag is the IPC flag mask with which synchronisation is done
//!
//! Allows the local and remote cores to synchronise. Neither core will return
//! from this function call before the other core enters it.
//!
//! \note Must be called with same flag mask on both the cores
//!
//! \return None

//*****************************************************************************
static inline void
IPC_sync(IPC_Type_t ipcType, uint32_t flag)
{
    IPC_setFlagLtoR(ipcType, flag);
    IPC_waitForFlag(ipcType, flag);
    IPC_ackFlagRtoL(ipcType, flag);
    IPC_waitForAck(ipcType, flag);
}

 

 

CPU1 입장에서 보면 흐름은 이렇다.

 

1. IPC_setFlagLtoR() : CPU1TOCPU2IPCSET 레지스터에 1을 써서 CPU1TOCPU2IPCFLG.IPC31 의 event flag를 켠다.  (0-7 event flag는 interrupt를 trigger 할 수 있는데 31은 해당이 없다.)

2. IPC_waitForFlag() : CPU2TOCPU1IPCSTS 레지스터(CPU2TOCPU1IPCFLG.IPC31 bit 의 상태를 반영함)를 확인하여 CPU2에 의해 set 될 때까지 기다린다. (CPU2에서 IPC_setFlagLtoR() 함수가 호출되고 나면 다음문장으로 넘어갈 수 있다.)

3. IPC_ackFlagRtoL() : CPU1TOCPU2IPCACK 레지스터에 1을 써서 CPU2TOCPU1IPCFLG.IPC31 bit 를 0으로 clear하여 ACK를 알린다.

4. IPC_waitForAck() : CPU1TOCPU2IPCFLG.IPC31 bit 상태를 확인하여 remote core 로부터 ACK되었는지 확인한다.

 

 

 

이렇게 동기를 맞추고 타이머 인터럽트에서 증가하는 tick 변수를 0으로 세팅하고 타이머 카운트를 리로드하고 시작한다.

 

 

물론 CPU2 boot 하고 각각 타이머랑 타이머 인터럽트 셋업하는 내용은 이보다 먼저 나와야 한다. 셋업까지만 해두고 동기화 이후에 타이머 start, 타이머 인터럽트 enable, global 인터럽트 enable 해도 될 것 같다.

 

만약 Non Blocking 방식으로 간단하게 구현하려면 어차피 CPU1이 먼저 리셋되니까 CPU1에서 플래그를 셋하고 CPU2가 ACK해줄 때까지 timeout시간 동안 기다렸다가 동기화하면 될꺼같다.

 

예를들면 이렇게 

// CPU1 코드
void SyncTimerWithCPU2(void)
{
    uint32 timeout = 0;

    IPC_clearFlagLtoR(IPC_CPU1_L_CPU2_R, IPC_FLAG_ALL);

    IPC_setFlagLtoR(IPC_CPU1_L_CPU2_R, IPC_FLAG31);

    timeout = timeTick + 2000UL;
    while(timeTick < timeout)
    {
        uint32 temp = IPC_Instance[IPC_CPU1_L_CPU2_R].IPC_Flag_Ctr_Reg->IPC_FLG;
        // CPU2로부터 ACK 수신시
        if((temp & IPC_FLAG31) == 0)
        {
            timeTick = 0;
            CPUTimer_startTimer(CPUTIMER0_BASE);
            IPC_ackFlagRtoL(IPC_CPU1_L_CPU2_R, IPC_FLAG31);
            break;
        }
        CheckIPC();
        CheckTimer();
    }
    // timeout시간동안 싱크 못맞춘 경우 IPC_STS31은 계속 켜있을것
}

// CPU2 코드
void SyncTimerWithCPU1(void)
{
    uint32 timeout = 0;

    timeout = timeTick + 2000UL;
    while(timeTick < timeout)
    {
        uint32 temp = IPC_Instance[IPC_CPU2_L_CPU1_R].IPC_Flag_Ctr_Reg->IPC_STS;
        if((temp & IPC_FLAG31) != 0)
        {
            IPC_ackFlagRtoL(IPC_CPU2_L_CPU1_R, IPC_FLAG31);
            timeTick = 0;
            CPUTimer_startTimer(CPUTIMER0_BASE);
            break;
        }
        CheckIPC();
        CheckTimer();
    }
    // timeout시간동안 싱크 못맞춘 경우 IPC_STS31은 계속 켜있을것
}

 

 

 

 

 

 

 

참고글 : 2022.12.05 - [DSP, MCU/TMS320F2838x (C28x)] - TMS320F28388D ] 타이머, Timer Interrupt 사용하기

 

TMS320F28388D ] 타이머, Timer Interrupt 사용하기

TMS320F28388D 모델에는 32bit Timer가 CPU당 각 3개씩 총 6개가 있다. 특이한 것은 Timer1,2 인터럽트는 각각 INT13, INT14에 연결되어 있고 Timer0는 PIE Interrupt Group 1의 7번째로 자리잡고 있다. 매뉴얼 153페이지

eteo.tistory.com