//
// Included Files
//
#include "device.h"
#include "sysctl.h"
#include "ipc.h"

//
// Defines
//
#define IPC_CMD_READ_CAN  0x1002
#define IPC_CMD_RESP      0x2002

#define CPU1_TO_CPU2_FLAG IPC_FLAG0
#define CPU2_TO_CPU1_FLAG IPC_FLAG1

#define MSG_DATA_LENGTH   8
#define LED_GPIO          31

#pragma DATA_SECTION(rxMsgData, "MSGRAM_CPU1_TO_CPU2");
volatile uint16_t rxMsgData[MSG_DATA_LENGTH] = {0};

//
// Function Prototypes
//
__interrupt void IPC_ISR1(void);

//
// Main Function (CPU1)
//
void main(void)
{
    // Initialize CPU1
    Device_init();
    Device_initGPIO();

    // Configure LED GPIO31 as output
    GPIO_setPadConfig(LED_GPIO, GPIO_PIN_TYPE_STD);
    GPIO_setDirectionMode(LED_GPIO, GPIO_DIR_MODE_OUT);

    // Allocate CANB ownership to CPU2
    SysCtl_selectCPUForPeripheralInstance(SYSCTL_CPUSEL_CANB, SYSCTL_CPUSEL_CPU2);

    // Notify CPU2 that CANB is now assigned
    IPC_setFlagLtoR(IPC_CPU1_L_CPU2_R, CPU1_TO_CPU2_FLAG);

    // IPC Initialization
    IPC_clearFlagLtoR(IPC_CPU1_L_CPU2_R, IPC_FLAG_ALL);

    // Register IPC Interrupt
    Interrupt_register(INT_IPC_1, IPC_ISR1);
    Interrupt_enable(INT_IPC_1);

    // Enable Global Interrupts
    EINT;
    ERTM;

    while (1)
    {
        //Request CAN data from CPU2
        IPC_sendCommand(IPC_CPU1_L_CPU2_R, CPU1_TO_CPU2_FLAG, IPC_ADDR_CORRECTION_ENABLE, IPC_CMD_READ_CAN, 0, 0);

        // Wait for CPU2 to send the response
        IPC_waitForAck(IPC_CPU1_L_CPU2_R, CPU1_TO_CPU2_FLAG);


        GPIO_togglePin(LED_GPIO);
        DEVICE_DELAY_US(500000);
    }
}

//
// IPC Interrupt Service Routine (Receives CAN Data from CPU2)
//
__interrupt void IPC_ISR1(void)
{
    uint32_t command, i, data;

    // Read IPC response from CPU2
    for (i = 0; i < MSG_DATA_LENGTH; i++)
    {
        IPC_readCommand(IPC_CPU1_L_CPU2_R, CPU2_TO_CPU1_FLAG, IPC_ADDR_CORRECTION_ENABLE, &command, &data, &i);

        if (command == IPC_CMD_RESP)
        {
            rxMsgData[i] = (uint16_t)data;
        }
    }

    //Acknowledge CPU2 that data is received
    IPC_ackFlagRtoL(IPC_CPU1_L_CPU2_R, CPU2_TO_CPU1_FLAG);
    Interrupt_clearACKGroup(INTERRUPT_ACK_GROUP1);
}
