Showing results for 
Show  only  | Search instead for 
Did you mean: 
Registered: ‎06-27-2019

Zynq Dual Core GIC

Good morning,

I have spent quite some time reading material in the Zynq TRM, on the ARM website, and on this forums about working with the GIC in dual core mode. I cannot find a solution, and I'm not happy with the my current mechanism of getting this working, trial and error, I'd like to understand what's happening.

I have FreeRTOS running on Core 0 and it has been working flawlessly. I am trying to put FreeRTOS on Core 1 and I am breaking interrupts on Core 0 when I initialize ScuGic from Core 1, but not all interrupts. I am using the private ScuTimer for each FreeRTOS to implement the 1ms task scheduler tick increment-er. When re-initializing ScuGic from Core 1, it breaks the TTC0 ISR handler in Core 0, but not the ScuTimer, maybe because it's private?

How do I initialize the ScuGic from Core 1 when it is already initialized from Core 0. Each FreeRTOS requires a ScuTimer (which I believe is private) for the task scheduler and for my scenario Core 0 will be handling TTC0 interrupts and Core 1 will take care of TTC1 interrupts.


This is the function in portZynq7000.c that initializes the ScuGic. Each FreeRTOS has a copy of this file and I believe here is where the problem lies, but I cannot figure out how to fix it. Any help is appreciated!


void FreeRTOS_SetupTickInterrupt( void )
BaseType_t xStatus;
extern void FreeRTOS_Tick_Handler( void );
XScuTimer_Config *pxTimerConfig;
XScuGic_Config *pxGICConfig;
const uint8_t ucRisingEdge = 3;

	/* This function is called with the IRQ interrupt disabled, and the IRQ
	interrupt should be left disabled.  It is enabled automatically when the
	scheduler is started. */

	/* Ensure XScuGic_CfgInitialize() has been called.  In this demo it has
	already been called from prvSetupHardware() in main(). */
	pxGICConfig = XScuGic_LookupConfig( XPAR_SCUGIC_SINGLE_DEVICE_ID );
	xStatus = XScuGic_CfgInitialize( &xInterruptController, pxGICConfig, pxGICConfig->CpuBaseAddress );
	configASSERT( xStatus == XST_SUCCESS );
	( void ) xStatus; /* Remove compiler warning if configASSERT() is not defined. */

	/* The priority must be the lowest possible. */
	XScuGic_SetPriorityTriggerType( &xInterruptController, XPAR_SCUTIMER_INTR, portLOWEST_USABLE_INTERRUPT_PRIORITY << portPRIORITY_SHIFT, ucRisingEdge );

	/* Install the FreeRTOS tick handler. */
	xStatus = XScuGic_Connect( &xInterruptController, XPAR_SCUTIMER_INTR, (Xil_ExceptionHandler) FreeRTOS_Tick_Handler, ( void * ) &xTimer );
	configASSERT( xStatus == XST_SUCCESS );
	( void ) xStatus; /* Remove compiler warning if configASSERT() is not defined. */

	/* Initialise the timer. */
	pxTimerConfig = XScuTimer_LookupConfig( XPAR_SCUTIMER_DEVICE_ID );
	xStatus = XScuTimer_CfgInitialize( &xTimer, pxTimerConfig, pxTimerConfig->BaseAddr );
	configASSERT( xStatus == XST_SUCCESS );
	( void ) xStatus; /* Remove compiler warning if configASSERT() is not defined. */

	/* Enable Auto reload mode. */
	XScuTimer_EnableAutoReload( &xTimer );

	/* Ensure there is no prescale. */
	XScuTimer_SetPrescaler( &xTimer, 0 );

	/* Load the timer counter register. */
	XScuTimer_LoadTimer( &xTimer, XSCUTIMER_CLOCK_HZ / configTICK_RATE_HZ );

	/* Start the timer counter and then wait for it to timeout a number of
	times. */
	XScuTimer_Start( &xTimer );

	/* Enable the interrupt for the xTimer in the interrupt controller. */
	XScuGic_Enable( &xInterruptController, XPAR_SCUTIMER_INTR );

	/* Enable the interrupt in the xTimer itself. */
	XScuTimer_EnableInterrupt( &xTimer );
* CfgInitialize a specific interrupt controller instance/driver. The
* initialization entails:
* - Initialize fields of the XScuGic structure
* - Initial vector table with stub function calls
* - All interrupt sources are disabled
* @param	InstancePtr is a pointer to the XScuGic instance.
* @param	ConfigPtr is a pointer to a config table for the particular
*		device this driver is associated with.
* @param	EffectiveAddr is the device base address in the virtual memory
*		address space. The caller is responsible for keeping the address
*		mapping from EffectiveAddr to the device physical base address
*		unchanged once this function is invoked. Unexpected errors may
*		occur if the address mapping changes after this function is
*		called. If address translation is not used, use
*		Config->BaseAddress for this parameters, passing the physical
*		address instead.
* @return
*		- XST_SUCCESS if initialization was successful
* @note		None.
s32  XScuGic_CfgInitialize(XScuGic *InstancePtr,
				XScuGic_Config *ConfigPtr,
				u32 EffectiveAddr)
	u32 Int_Id;
	u32 Cpu_Id = CpuId + (u32)1;
	(void) EffectiveAddr;

	Xil_AssertNonvoid(InstancePtr != NULL);
	Xil_AssertNonvoid(ConfigPtr != NULL);
     * Detect Zynq-7000 base silicon configuration,Dual or Single CPU.
     * If it is single CPU cnfiguration then invoke assert for CPU ID=1
#ifdef ARMA9
	if (XPAR_CPU_ID == 0x01) {

	if(InstancePtr->IsReady != XIL_COMPONENT_IS_READY) {

		InstancePtr->IsReady = 0U;
		InstancePtr->Config = ConfigPtr;

		for (Int_Id = 0U; Int_Id < XSCUGIC_MAX_NUM_INTR_INPUTS;
				Int_Id++) {
			* Initalize the handler to point to a stub to handle an
			* interrupt which has not been connected to a handler
			* Only initialize it if the handler is 0 which means it
			* was not initialized statically by the tools/user. Set
			* the callback reference to this instance so that
			* unhandled interrupts can be tracked.
			if ((InstancePtr->Config->HandlerTable[Int_Id].Handler
					== (Xil_InterruptHandler)NULL)) {
						= (Xil_InterruptHandler)StubHandler;
			InstancePtr->Config->HandlerTable[Int_Id].CallBackRef =
#if defined (versal) && !defined(ARMR5)
	u32 Waker_State;
	xil_printf("Execuing on the a72\n");
	Waker_State = XScuGic_ReDistReadReg(InstancePtr,XSCUGIC_RDIST_WAKER_OFFSET);
		/* Enable system reg interface through ICC_SRE_EL1 */
		#if EL3
		DistributorInit(InstancePtr, Cpu_Id);
#if defined (versal) && !defined(ARMR5)

		InstancePtr->IsReady = XIL_COMPONENT_IS_READY;

	return XST_SUCCESS;


I would like to stay away from openAMP or any other libraries of that sort. It seems like it'll complicate things quite a bit and also overkill.


And a side (maybe not) question, what is the purpose of InterruptMaptoCPU? I don't believe it is what I need, but I don't understand what it does.

* Sets the target CPU for the interrupt of a peripheral
* @param	InstancePtr is a pointer to the instance to be worked on.
* @param	Cpu_Id is a CPU number for which the interrupt has to be targeted
* @param	Int_Id is the IRQ source number to modify
* @return	None.
* @note		None
void XScuGic_InterruptMaptoCpu(XScuGic *InstancePtr, u8 Cpu_Id, u32 Int_Id)
	u32 RegValue;
	u32 Offset;
	RegValue = XScuGic_DistReadReg(InstancePtr,

	Offset = (Int_Id & 0x3U);
	Cpu_Id = (0x1U << Cpu_Id);

	RegValue = (RegValue & (~(0xFFU << (Offset*8U))));
	RegValue |= ((Cpu_Id) << (Offset*8U));

0 Kudos
2 Replies
Registered: ‎06-27-2019

I narrowed down the issue to a single line of code, but I have a feeling that this is just coincidence and the right way to fix the problem is elsewhere. Stepping over this line of code (from Core 1) is when I loose the TTC0 handler for Core 0.

	 * If the CPU operates only in the secure domain, setup the
	 * control_s register.
	 * 1. Set FIQen=1,
	 * 2. Set EnableS=1, to enable the CPU interface to signal secure
	 *  interrupts. Only enable the IRQ output unless secure interrupts
	 * are needed.
	XScuGic_CPUWriteReg(InstancePtr, XSCUGIC_CONTROL_OFFSET, 0x07U);

Which that 0x07U kind of confuses me as well. Looking at the ARM documentation of the GIC. Bits 1-4 are reserved.Page 8-589 of this document.

0 Kudos
Registered: ‎04-13-2015

@maty you have reasons to be confused about the GIC because the version of the MPcore in Zynq is r3p0 and it has the GIC v1.0 (The doc you use is GIC v3.0 and v4.0).  Here's the proper doc to refer to:

0 Kudos