diff --git a/arch/nios2/core/Makefile b/arch/nios2/core/Makefile index acb31b30c59..662c6e8fa69 100644 --- a/arch/nios2/core/Makefile +++ b/arch/nios2/core/Makefile @@ -4,6 +4,6 @@ ccflags-y += -I$(srctree)/kernel/microkernel/include obj-y += reset.o irq_manage.o fatal.o swap.o thread.o \ cpu_idle.o irq_offload.o prep_c.o crt0.o \ - exception.o sw_isr_table.o + exception.o sw_isr_table.o cache.o obj-$(CONFIG_IRQ_OFFLOAD) += irq_offload.o diff --git a/arch/nios2/core/cache.c b/arch/nios2/core/cache.c new file mode 100644 index 00000000000..bcaa9dbc56d --- /dev/null +++ b/arch/nios2/core/cache.c @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2016 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + + +/** + * Flush the entire instruction cache and pipeline. + * + * You will need to call this function if the application writes new program + * text to memory, such as a boot copier or runtime synthesis of code. If the + * new text was written with instructions that do not bypass cache memories, + * this should immediately be followed by an invocation of + * _nios2_dcache_flush_all() so that cached instruction data is committed to + * RAM. + * + * See Chapter 9 of the Nios II Gen 2 Software Developer's Handbook for more + * information on cache considerations. + */ +#if NIOS2_ICACHE_SIZE > 0 +void _nios2_icache_flush_all(void) +{ + uint32_t i; + + for (i = 0; i < NIOS2_ICACHE_SIZE; i += NIOS2_ICACHE_LINE_SIZE) { + _nios2_icache_flush(i); + } + + /* Get rid of any stale instructions in the pipeline */ + _nios2_pipeline_flush(); +} +#endif + +/** + * Flush the entire data cache. + * + * This will be typically needed after writing new program text to memory + * after flushing the instruction cache. + * + * The Nios II does not support hardware cache coherency for multi-master + * or multi-processor systems and software coherency must be implemented + * when communicating with shared memory. If support for this is introduced + * in Zephyr additional APIs for flushing ranges of the data cache will need + * to be implemented. + * + * See Chapter 9 of the Nios II Gen 2 Software Developer's Handbook for more + * information on cache considerations. + */ +#if NIOS2_DCACHE_SIZE > 0 +void _nios2_dcache_flush_all(void) +{ + uint32_t i; + + for (i = 0; i < NIOS2_DCACHE_SIZE; i += NIOS2_DCACHE_LINE_SIZE) { + _nios2_dcache_flush(i); + } +} +#endif diff --git a/arch/nios2/core/crt0.S b/arch/nios2/core/crt0.S index 9bea2aef7e9..d2d92747541 100644 --- a/arch/nios2/core/crt0.S +++ b/arch/nios2/core/crt0.S @@ -31,6 +31,7 @@ GTEXT(_interrupt_stack) */ .set noat + #if CONFIG_INCLUDE_RESET_VECTOR /* * Reset vector entry point into the system. Placed into special 'reset' @@ -42,15 +43,39 @@ GTEXT(_interrupt_stack) */ SECTION_FUNC(reset, __reset) - /* TODO initialize instruction cache, if present - * ZEP-275 +#if NIOS2_ICACHE_SIZE > 0 + /* Aside from the instruction cache line associated with the reset + * vector, the contents of the cache memories are indeterminate after + * reset. To ensure cache coherency after reset, the reset handler + * located at the reset vector must immediately initialize the + * instruction cache. Next, either the reset handler or a subsequent + * routine should proceed to initialize the data cache. + * + * The cache memory sizes are *always* a power of 2. */ +#if NIOS2_ICACHE_SIZE > 0x8000 + movhi r2, %hi(NIOS2_ICACHE_SIZE) +#else + movui r2, NIOS2_ICACHE_SIZE +#endif +0: + /* If ECC present, need to execute initd for each word address + * to ensure ECC parity bits in data RAM get initialized + */ +#if NIOS2_ECC_PRESENT + subi r2, r2, 4 +#else + subi r2, r2, NIOS2_ICACHE_LINE_SIZE +#endif + initi r2 + bgt r2, zero, 0b +#endif /* NIOS2_ICACHE_SIZE > 0 */ /* Done all we need to do here, jump to __text_start */ movhi r1, %hi(__start) ori r1, r1, %lo(__start) jmp r1 -#endif +#endif /* CONFIG_INCLUDE_RESET_VECTOR */ /* Remainder of asm-land initialization code before we can jump into * the C domain @@ -61,9 +86,29 @@ SECTION_FUNC(TEXT, __start) * ZEP-258 */ - /* TODO initialize data cache, if present - * ZEP-275 + /* Initialize the data cache if booting from bare metal. If + * we're not booting from our reset vector, either by a bootloader + * or JTAG, assume caches already initialized. */ +#if NIOS2_DCACHE_SIZE > 0 && defined(CONFIG_INCLUDE_RESET_VECTOR) + /* Per documentation data cache size is always a power of two. */ +#if NIOS2_DCACHE_SIZE > 0x8000 + movhi r2, %hi(NIOS2_DCACHE_SIZE) +#else + movui r2, NIOS2_DCACHE_SIZE +#endif +0: + /* If ECC present, need to execute initd for each word address + * to ensure ECC parity bits in data RAM get initialized + */ +#if NIOS2_ECC_PRESENT + subi r2, r2, 4 +#else + subi r2, r2, NIOS2_DCACHE_LINE_SIZE +#endif + initd 0(r2) + bgt r2, zero, 0b +#endif /* NIOS2_DCACHE_SIZE && defined(CONFIG_INCLUDE_RESET_VECTOR) */ #ifdef CONFIG_INIT_STACKS /* Pre-populate all bytes in _interrupt_stack with 0xAA */ diff --git a/arch/nios2/core/prep_c.c b/arch/nios2/core/prep_c.c index aafb600e14f..842542d1bb9 100644 --- a/arch/nios2/core/prep_c.c +++ b/arch/nios2/core/prep_c.c @@ -29,6 +29,7 @@ #include #include #include +#include /** * @@ -68,6 +69,18 @@ static void dataCopy(void) for (n = 0; n < (unsigned int)&__data_num_words; n++) { pRAM[n] = pROM[n]; } + + /* In most XIP scenarios we copy the exception code into RAM, so need + * to flush instruction cache. + */ + _nios2_icache_flush_all(); +#if NIOS2_ICACHE_SIZE > 0 + /* Only need to flush the data cache here if there actually is an + * instruction cache, so that the cached instruction data written is + * actually committed. + */ + _nios2_dcache_flush_all(); +#endif } #else static void dataCopy(void) diff --git a/arch/nios2/include/nano_private.h b/arch/nios2/include/nano_private.h index f81fa2d1b20..2e42452380b 100644 --- a/arch/nios2/include/nano_private.h +++ b/arch/nios2/include/nano_private.h @@ -200,6 +200,18 @@ static ALWAYS_INLINE int _IS_IN_ISR(void) void _irq_do_offload(void); #endif +#if NIOS2_ICACHE_SIZE > 0 +void _nios2_icache_flush_all(void); +#else +#define _nios2_icache_flush_all() do { } while (0) +#endif + +#if NIOS2_DCACHE_SIZE > 0 +void _nios2_dcache_flush_all(void); +#else +#define _nios2_dcache_flush_all() do { } while (0) +#endif + #endif /* _ASMLANGUAGE */ #endif /* _NANO_PRIVATE_H */ diff --git a/include/arch/nios2/nios2.h b/include/arch/nios2/nios2.h index 42c75fcd858..a71cf3214ee 100644 --- a/include/arch/nios2/nios2.h +++ b/include/arch/nios2/nios2.h @@ -94,6 +94,28 @@ static inline void _nios2_report_stack_overflow(void) __asm__ volatile("break 3"); } +/* + * Low-level cache management functions + */ +static inline void _nios2_dcache_addr_flush(void *addr) +{ + __asm__ volatile ("flushda (%0)" :: "r" (addr)); +} + +static inline void _nios2_dcache_flush(uint32_t offset) +{ + __asm__ volatile ("flushd (%0)" :: "r" (offset)); +} + +static inline void _nios2_icache_flush(uint32_t offset) +{ + __asm__ volatile ("flushi %0" :: "r" (offset)); +} + +static inline void _nios2_pipeline_flush(void) +{ + __asm__ volatile ("flushp"); +} /* * Functions for reading/writing control registers