diff --git a/ports/cortex_a8/gnu/example_build/MP_GIC.h b/ports/cortex_a8/gnu/example_build/MP_GIC.h new file mode 100644 index 00000000..82f2ea13 --- /dev/null +++ b/ports/cortex_a8/gnu/example_build/MP_GIC.h @@ -0,0 +1,74 @@ +// ------------------------------------------------------------ +// Cortex-A8 MPCore - Interrupt Controller functions +// Header File +// +// Copyright (c) 2011-2018 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#ifndef _CORTEXA_GIC_ +#define _CORTEXA_GIC_ + +// ------------------------------------------------------------ +// GIC +// ------------------------------------------------------------ + +// Typical calls to enable interrupt ID X: +// enable_irq_id(X) <-- Enable that ID +// set_irq_priority(X, 0) <-- Set the priority of X to 0 (the max priority) +// set_priority_mask(0x1F) <-- Set Core's priority mask to 0x1F (the lowest priority) +// enable_GIC() <-- Enable the GIC (global) +// enable_gic_processor_interface() <-- Enable the CPU interface (local to the core) +// + + +// Global enable of the Interrupt Distributor +void enableGIC(void); + +// Global disable of the Interrupt Distributor +void disableGIC(void); + +// Enables the interrupt source number ID +void enableIntID(unsigned int ID); + +// Disables the interrupt source number ID +void disableIntID(unsigned int ID); + +// Sets the priority of the specified ID +void setIntPriority(unsigned int ID, unsigned int priority); + +// Enables the processor interface +// Must be done on each core separately +void enableGICProcessorInterface(void); + +// Disables the processor interface +// Must be done on each core separately +void disableGICProcessorInterface(void); + +// Sets the Priority mask register for the core run on +// The reset value masks ALL interrupts! +void setPriorityMask(unsigned int priority); + +// Sets the Binary Point Register for the core run on +void setBinaryPoint(unsigned int priority); + +// Returns the value of the Interrupt Acknowledge Register +unsigned int readIntAck(void); + +// Writes ID to the End Of Interrupt register +void writeEOI(unsigned int ID); + +// ------------------------------------------------------------ +// SGI +// ------------------------------------------------------------ + +// Send a software generate interrupt +void sendSGI(unsigned int ID, unsigned int core_list, unsigned int filter_list); + +#endif + +// ------------------------------------------------------------ +// End of MP_GIC.h +// ------------------------------------------------------------ diff --git a/ports/cortex_a8/gnu/example_build/MP_GIC.s b/ports/cortex_a8/gnu/example_build/MP_GIC.s new file mode 100644 index 00000000..6626d583 --- /dev/null +++ b/ports/cortex_a8/gnu/example_build/MP_GIC.s @@ -0,0 +1,294 @@ +//---------------------------------------------------------------- +// Copyright (c) 2005-2018 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// +// Cortex-A8MP example - Startup Code +//---------------------------------------------------------------- + .text + +//---------------------------------------------------------------- +// GIC. Generic Interrupt Controller Architecture Specification +//---------------------------------------------------------------- + + // CPU Interface offset from base of private peripheral space --> 0x0100 + // Interrupt Distributor offset from base of private peripheral space --> 0x1000 + + // Typical calls to enable interrupt ID X: + // enableIntID(X) <-- Enable that ID + // setIntPriority(X, 0) <-- Set the priority of X to 0 (the max priority) + // setPriorityMask(0x1F) <-- Set CPU's priority mask to 0x1F (the lowest priority) + // enableGIC() <-- Enable the GIC (global) + // enableGICProcessorInterface() <-- Enable the CPU interface (local to the CPU) + + + // void enableGIC(void) + // Global enable of the Interrupt Distributor + .global enableGIC + .type enableGIC,function +enableGIC: + // Get base address of private peripheral space + MRC p15, 4, r0, c15, c0, 0 // Read periph base address + ADD r0, r0, #0x1000 // Add the GIC offset + + LDR r1, [r0] // Read the GIC Enable Register (ICDDCR) + ORR r1, r1, #0x01 // Set bit 0, the enable bit + STR r1, [r0] // Write the GIC Enable Register (ICDDCR) + + BX lr + +// ------------------------------------------------------------ + + .global disableGIC + .type disableGIC,function + // void disableGIC(void) + // Global disable of the Interrupt Distributor +disableGIC: + // Get base address of private peripheral space + MRC p15, 4, r0, c15, c0, 0 // Read periph base address + ADD r0, r0, #0x1000 // Add the GIC offset + + LDR r1, [r0] // Read the GIC Enable Register (ICDDCR) + BIC r1, r1, #0x01 // Clear bit 0, the enable bit + STR r1, [r0] // Write the GIC Enable Register (ICDDCR) + + BX lr + + +// ------------------------------------------------------------ + + .global enableIntID + .type enableIntID,function + // void enableIntID(unsigned int ID) + // Enables the interrupt source number ID +enableIntID: + // Get base address of private peripheral space + MOV r1, r0 // Back up passed in ID value + MRC p15, 4, r0, c15, c0, 0 // Read periph base address + + // Each interrupt source has an enable bit in the GIC. These + // are grouped into registers, with 32 sources per register + // First, we need to identify which 32-bit block the interrupt lives in + MOV r2, r1 // Make working copy of ID in r2 + MOV r2, r2, LSR #5 // LSR by 5 places, affective divide by 32 + // r2 now contains the 32-bit block this ID lives in + MOV r2, r2, LSL #2 // Now multiply by 4, to convert offset into an address offset (four bytes per reg) + + // Now work out which bit within the 32-bit block the ID is + AND r1, r1, #0x1F // Mask off to give offset within 32-bit block + MOV r3, #1 // Move enable value into r3 + MOV r3, r3, LSL r1 // Shift it left to position of ID + + ADD r2, r2, #0x1100 // Add the base offset of the Enable Set registers to the offset for the ID + STR r3, [r0, r2] // Store out (ICDISER) + + BX lr + + +// ------------------------------------------------------------ + + .global disableIntID + .type disableIntID,function + // void disableIntID(unsigned int ID) + // Disables the interrupt source number ID +disableIntID: + // Get base address of private peripheral space + MOV r1, r0 // Back up passed in ID value + MRC p15, 4, r0, c15, c0, 0 // Read periph base address + + // First, we need to identify which 32-bit block the interrupt lives in + MOV r2, r1 // Make working copy of ID in r2 + MOV r2, r2, LSR #5 // LSR by 5 places, affective divide by 32 + // r2 now contains the 32-bit block this ID lives in + MOV r2, r2, LSL #2 // Now multiply by 4, to convert offset into an address offset (four bytes per reg) + + // Now work out which bit within the 32-bit block the ID is + AND r1, r1, #0x1F // Mask off to give offset within 32-bit block + MOV r3, #1 // Move enable value into r3 + MOV r3, r3, LSL r1 // Shift it left to position of ID in 32-bit block + + ADD r2, r2, #0x1180 // Add the base offset of the Enable Clear registers to the offset for the ID + STR r3, [r0, r2] // Store out (ICDICER) + + BX lr + + +// ------------------------------------------------------------ + + .global setIntPriority + .type setIntPriority,function + // void setIntPriority(unsigned int ID, unsigned int priority) + // Sets the priority of the specified ID + // r0 = ID + // r1 = priority +setIntPriority: + // Get base address of private peripheral space + MOV r2, r0 // Back up passed in ID value + MRC p15, 4, r0, c15, c0, 0 // Read periph base address + + // r0 = base addr + // r1 = priority + // r2 = ID + + // Make sure that priority value is only 5 bits, and convert to expected format + AND r1, r1, #0x1F + MOV r1, r1, LSL #3 + + // Find which register this ID lives in + BIC r3, r2, #0x03 // Make a copy of the ID, clearing off the bottom two bits + // There are four IDs per reg, by clearing the bottom two bits we get an address offset + ADD r3, r3, #0x1400 // Now add the offset of the Priority Level registers from the base of the private peripheral space + ADD r0, r0, r3 // Now add in the base address of the private peripheral space, giving us the absolute address + + // Now work out which ID in the register it is + AND r2, r2, #0x03 // Clear all but the bottom two bits, leaves which ID in the reg it is (which byte) + MOV r2, r2, LSL #3 // Multiply by 8, this gives a bit offset + + // Read -> Modify -> Write + MOV r12, #0xFF // 8 bit field mask + MOV r12, r12, LSL r2 // Move mask into correct bit position + MOV r1, r1, LSL r2 // Also, move passed in priority value into correct bit position + + LDR r3, [r0] // Read current value of the Priority Level register (ICDIPR) + BIC r3, r3, r12 // Clear appropriate field + ORR r3, r3, r1 // Now OR in the priority value + STR r3, [r0] // And store it back again (ICDIPR) + + BX lr + + +// ------------------------------------------------------------ + + .global enableGICProcessorInterface + .type enableGICProcessorInterface,function + // void enableGICProcessorInterface(void) + // Enables the processor interface + // Must be done on each core separately +enableGICProcessorInterface: + MRC p15, 4, r0, c15, c0, 0 // Read periph base address + ADD r0, r0, #0x2000 + + LDR r1, [r0, #0x0] // Read the Processor Interface Control register (ICCICR/ICPICR) + ORR r1, r1, #0x03 // Bit 0: Enables secure interrupts, Bit 1: Enables Non-Secure interrupts + BIC r1, r1, #0x08 // Bit 3: Ensure Group 0 interrupts are signalled using IRQ, not FIQ + STR r1, [r0, #0x0] // Write the Processor Interface Control register (ICCICR/ICPICR) + + BX lr + + + +// ------------------------------------------------------------ + + .global disableGICProcessorInterface + .type disableGICProcessorInterface,function + // void disableGICProcessorInterface(void) + // Disables the processor interface + // Must be done on each core separately +disableGICProcessorInterface: + + MRC p15, 4, r0, c15, c0, 0 // Read periph base address + ADD r0, r0, #0x2000 + + LDR r1, [r0, #0x0] // Read the Processor Interface Control register (ICCICR/ICPICR) + BIC r1, r1, #0x03 // Bit 0: Enables secure interrupts, Bit 1: Enables Non-Secure interrupts + STR r1, [r0, #0x0] // Write the Processor Interface Control register (ICCICR/ICPICR) + + BX lr + + + +// ------------------------------------------------------------ + + .global setPriorityMask + .type setPriorityMask,function + // void setPriorityMask(unsigned int priority) + // Sets the Priority mask register for the CPU run on + // The reset value masks ALL interrupts! +setPriorityMask: + + // Get base address of private peripheral space + MRC p15, 4, r1, c15, c0, 0 // Read periph base address + ADD r1, r1, #0x2000 + + STR r0, [r1, #0x4] // Write the Priority Mask register (ICCPMR/ICCIPMR) + + BX lr + + +// ------------------------------------------------------------ + + .global setBinaryPoint + .type setBinaryPoint,function + // void setBinaryPoint(unsigned int priority) + // Sets the Binary Point Register for the CPU run on +setBinaryPoint: + + // Get base address of private peripheral space + MRC p15, 4, r1, c15, c0, 0 // Read periph base address + ADD r1, r1, #0x2000 + + STR r0, [r1, #0x8] // Write the Binary register (ICCBPR/ICCBPR) + + BX lr + + +// ------------------------------------------------------------ + + .global readIntAck + .type readIntAck,function + // unsigned int readIntAck(void) + // Returns the value of the Interrupt Acknowledge Register +readIntAck: + MRC p15, 4, r0, c15, c0, 0 // Read periph base address + ADD r0, r0, #0x2000 + + LDR r0, [r0, #0xC] // Read the Interrupt Acknowledge Register (ICCIAR) + BX lr + + +// ------------------------------------------------------------ + + .global writeEOI + .type writeEOI,function + // void writeEOI(unsigned int ID) + // Writes ID to the End Of Interrupt register +writeEOI: + + // Get base address of private peripheral space + MRC p15, 4, r1, c15, c0, 0 // Read periph base address + ADD r1, r1, #0x2000 + + STR r0, [r1, #0x10] // Write ID to the End of Interrupt register (ICCEOIR) + + BX lr + + +//---------------------------------------------------------------- +// SGI +//---------------------------------------------------------------- + + .global sendSGI + .type sendSGI,function + // void sendSGI(unsigned int ID, unsigned int target_list, unsigned int filter_list)// + // Send a software generate interrupt +sendSGI: + AND r3, r0, #0x0F // Mask off unused bits of ID, and move to r3 + AND r1, r1, #0x0F // Mask off unused bits of target_filter + AND r2, r2, #0x0F // Mask off unused bits of filter_list + + ORR r3, r3, r1, LSL #16 // Combine ID and target_filter + ORR r3, r3, r2, LSL #24 // and now the filter list + + // Get the address of the GIC + MRC p15, 4, r0, c15, c0, 0 // Read periph base address + ADD r0, r0, #0x1F00 // Add offset of the sgi_trigger reg + + STR r3, [r0] // Write to the Software Generated Interrupt Register (ICDSGIR) + + BX lr + + +//---------------------------------------------------------------- +// End of MP_GIC.s +//---------------------------------------------------------------- diff --git a/ports/cortex_a8/gnu/example_build/MP_PrivateTimer.S b/ports/cortex_a8/gnu/example_build/MP_PrivateTimer.S new file mode 100644 index 00000000..b1e2701b --- /dev/null +++ b/ports/cortex_a8/gnu/example_build/MP_PrivateTimer.S @@ -0,0 +1,84 @@ +// ------------------------------------------------------------ +// Cortex-A MPCore - Private timer functions +// +// Copyright ARM Ltd 2009. All rights reserved. +// ------------------------------------------------------------ + + .text + .align 3 + + // PPI ID 29 + + + // Typical set of calls to enable Timer: + // init_private_timer(0xXXXX, 0) <-- Counter down value of 0xXXXX, with auto-reload + // start_private_timer() + + // Timer offset from base of private peripheral space --> 0x600 + +// ------------------------------------------------------------ + + // void init_private_timer(unsigned int load_value, unsigned int auto_reload) + // Sets up the private timer + // r0: initial load value + // r1: IF 0 (AutoReload) ELSE (SingleShot) AutoReload not supported on Cortex-A7 + .global init_private_timer + .type init_private_timer,function +init_private_timer: + + // Setup timeout value (CNTP_TVAL) + MCR p15, 0, r0, c14, c2, 0 + BX lr + +// ------------------------------------------------------------ + + // void start_private_timer(void) + // Starts the private timer + .global start_private_timer + .type start_private_timer,function +start_private_timer: + + MOV r0, #0x1 + + // Enable timer (CNTP_CTL) + MCR p15, 0, r0, c14, c2, 1 + + BX lr + +// ------------------------------------------------------------ + + // void stop_private_timer(void) + // Stops the private timer + .global stop_private_timer + .type stop_private_timer,function +stop_private_timer: + + BX lr + +// ------------------------------------------------------------ + + // unsigned int read_private_timer(void) + // Reads the current value of the timer count register + .global get_private_timer_count + .type get_private_timer_count,function +get_private_timer_count: + + BX lr + +// ------------------------------------------------------------ + + // void clear_private_timer_irq(void) + // Clears the private timer interrupt + .global clear_private_timer_irq + .type clear_private_timer_irq,function +clear_private_timer_irq: + + BX lr + +// ------------------------------------------------------------ +// End of code +// ------------------------------------------------------------ + +// ------------------------------------------------------------ +// End of MP_PrivateTimer.s +// ------------------------------------------------------------ diff --git a/ports/cortex_a8/gnu/example_build/MP_PrivateTimer.h b/ports/cortex_a8/gnu/example_build/MP_PrivateTimer.h new file mode 100644 index 00000000..b0ab212a --- /dev/null +++ b/ports/cortex_a8/gnu/example_build/MP_PrivateTimer.h @@ -0,0 +1,36 @@ +// ------------------------------------------------------------ +// Cortex-A MPCore - Private timer functions +// Header Filer +// +// Copyright ARM Ltd 2009. All rights reserved. +// ------------------------------------------------------------ + +#ifndef _CORTEXA_PRIVATE_TIMER_ +#define _CORTEXA_PRIVATE_TIMER_ + +// Typical set of calls to enable Timer: +// init_private_timer(0xXXXX, 0) <-- Counter down value of 0xXXXX, with auto-reload +// start_private_timer() + +// Sets up the private timer +// r0: initial load value +// r1: IF 0 (AutoReload) ELSE (SingleShot) +void init_private_timer(unsigned int load_value, unsigned int auto_reload); + +// Starts the private timer +void start_private_timer(void); + +// Stops the private timer +void stop_private_timer(void); + +// Reads the current value of the timer count register +unsigned int get_private_timer_count(void); + +// Clears the private timer interrupt +void clear_private_timer_irq(void); + +#endif + +// ------------------------------------------------------------ +// End of MP_PrivateTimer.h +// ------------------------------------------------------------ diff --git a/ports/cortex_a8/gnu/example_build/build_threadx_sample.bat b/ports/cortex_a8/gnu/example_build/build_threadx_sample.bat index 725aeda9..ad30a7d7 100644 --- a/ports/cortex_a8/gnu/example_build/build_threadx_sample.bat +++ b/ports/cortex_a8/gnu/example_build/build_threadx_sample.bat @@ -1,6 +1,8 @@ arm-none-eabi-gcc -c -g -mcpu=cortex-a8 reset.S arm-none-eabi-gcc -c -g -mcpu=cortex-a8 crt0.S arm-none-eabi-gcc -c -g -mcpu=cortex-a8 tx_initialize_low_level.S +arm-none-eabi-gcc -c -g -mcpu=cortex-a8 MP_GIC.s +arm-none-eabi-gcc -c -g -mcpu=cortex-a8 MP_PrivateTimer.s +arm-none-eabi-gcc -c -g -mcpu=cortex-a8 V7.s arm-none-eabi-gcc -c -g -mcpu=cortex-a8 -I../../../../common/inc -I../inc sample_threadx.c -arm-none-eabi-gcc -g -mcpu=cortex-a8 -T sample_threadx.ld --specs=nosys.specs -o sample_threadx.out -Wl,-Map=sample_threadx.map tx_initialize_low_level.o sample_threadx.o tx.a - +arm-none-eabi-gcc -g -nostartfiles -mcpu=cortex-a8 -T sample_threadx.ld --specs=nosys.specs -o sample_threadx.out -Wl,-Map=sample_threadx.map MP_GIC.o MP_PrivateTimer.o V7.o crt0.o reset.o tx_initialize_low_level.o sample_threadx.o tx.a diff --git a/ports/cortex_a8/gnu/example_build/tx_initialize_low_level.S b/ports/cortex_a8/gnu/example_build/tx_initialize_low_level.S index 4b324e0a..96a52fa0 100644 --- a/ports/cortex_a8/gnu/example_build/tx_initialize_low_level.S +++ b/ports/cortex_a8/gnu/example_build/tx_initialize_low_level.S @@ -41,8 +41,13 @@ SYS_STACK_SIZE = 1024 // System stack size .global _end .global _sp .global _stack_bottom - - + .global __vectors + .global disableHighVecs + .global enableGIC + .global enableGICProcessorInterface + .global enableCaches + .global init_private_timer + .global start_private_timer /* Define the 16-bit Thumb mode veneer for _tx_initialize_low_level for applications calling this function from to 16-bit Thumb mode. */ @@ -160,6 +165,42 @@ _stack_error_loop: ADD r1, r1, #8 // Increment to next free word STR r1, [r2] // Save first free memory address + PUSH {lr} + + /* Setup the vector table. */ + LDR r0, =__vectors // Get address of vector table + MCR p15, 0, r0, c12, c0, 0 // Write vector table address to CP15 + BL disableHighVecs // Disable high vectors + + // + // GIC Init + // --------- + BL enableGIC + BL enableGICProcessorInterface + + // + // Enable Private Timer for periodic IRQ + // -------------------------------------- + MOV r0, #0x1F + BL setPriorityMask // Set priority mask (local) + + // Enable the Private Timer Interrupt Source + MOV r0, #29 + MOV r1, #0 + BL enableIntID + + // Set the priority + MOV r0, #29 + MOV r1, #0 + BL setIntPriority + + // Configure Timer + MOV r0, #0xF0000 + MOV r1, #0x0 + BL init_private_timer + BL start_private_timer + + POP {lr} #ifdef __THUMB_INTERWORK BX lr // Return to caller #else @@ -202,16 +243,18 @@ __tx_irq_processing_return: if nested IRQ interrupts are desired. Interrupts may be re-enabled over small code sequences where lr is saved before enabling interrupts and restored after interrupts are again disabled. */ + + PUSH {r4, r5} // Save some preserved registers (r5 is saved just for 8-byte alignment) + BL readIntAck + MOV r4, r0 - /* Interrupt nesting is allowed after calling _tx_thread_irq_nesting_start - from IRQ mode with interrupts disabled. This routine switches to the - system mode and returns with IRQ interrupts enabled. + CMP r0, #29 // If not Private Timer interrupt (ID 29), by pass + BNE by_pass_timer_interrupt - NOTE: It is very important to ensure all IRQ interrupts are cleared - prior to enabling nested IRQ interrupts. */ -#ifdef TX_ENABLE_IRQ_NESTING - BL _tx_thread_irq_nesting_start -#endif + MOV r0, #0xF0000 + MOV r1, #0x0 + BL init_private_timer + DSB /* For debug purpose, execute the timer interrupt processing here. In a real system, some kind of status indication would have to be checked @@ -219,13 +262,10 @@ __tx_irq_processing_return: BL _tx_timer_interrupt // Timer interrupt handler - - /* If interrupt nesting was started earlier, the end of interrupt nesting - service must be called before returning to _tx_thread_context_restore. - This routine returns in processing in IRQ mode with interrupts disabled. */ -#ifdef TX_ENABLE_IRQ_NESTING - BL _tx_thread_irq_nesting_end -#endif +by_pass_timer_interrupt: + MOV r0, r4 + BL writeEOI + POP {r4, r5} // Recover preserved registers /* Jump to context restore to restore system context. */ B _tx_thread_context_restore diff --git a/test/ports/azrtos_test_tx_gnu_cortex_a8.log.expected b/test/ports/azrtos_test_tx_gnu_cortex_a8.log.expected index 3d35b442..21efba52 100644 --- a/test/ports/azrtos_test_tx_gnu_cortex_a8.log.expected +++ b/test/ports/azrtos_test_tx_gnu_cortex_a8.log.expected @@ -1,18 +1,18 @@ >output thread_0_counter -2913840557 +10 >output thread_1_counter -2913840557 +299267 >output thread_2_counter -2913840557 +299268 >output thread_3_counter -2913840557 +23 >output thread_4_counter -2913840557 +23 >output thread_5_counter -2913840557 +9 >output thread_6_counter -2913840557 +23 >output thread_7_counter -2913840557 +23 >log file Stopped duplicating logging output