/* ------------------------------------------ | |
* Copyright (c) 2016, Synopsys, Inc. All rights reserved. | |
* Redistribution and use in source and binary forms, with or without modification, | |
* are permitted provided that the following conditions are met: | |
* 1) Redistributions of source code must retain the above copyright notice, this | |
* list of conditions and the following disclaimer. | |
* 2) Redistributions in binary form must reproduce the above copyright notice, | |
* this list of conditions and the following disclaimer in the documentation and/or | |
* other materials provided with the distribution. | |
* 3) Neither the name of the Synopsys, Inc., nor the names of its contributors may | |
* be used to endorse or promote products derived from this software without | |
* specific prior written permission. | |
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND | |
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | |
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | |
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR | |
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | |
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | |
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON | |
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | |
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
* | |
* \version 2016.05 | |
* \date 2014-07-15 | |
* \author Wayne Ren(Wei.Ren@synopsys.com) | |
--------------------------------------------- */ | |
/** | |
* \file | |
* \ingroup ARC_HAL_MISC_CACHE | |
* \brief implementation of cache related functions | |
*/ | |
#include "inc/arc/arc_cache.h" | |
#define DIV_ROUND_UP(x, y) (((x) + (y) - 1) / (y)) | |
struct cache_config | |
{ | |
uint8_t ver; /* version */ | |
uint8_t assoc; /* Cache Associativity */ | |
uint16_t line; /* cache line/block size */ | |
uint32_t capacity; /* capacity */ | |
}; | |
static struct cache_config icache_config, dcache_config; | |
/** | |
* \brief invalidate multi instruction cache lines | |
* | |
* \param[in] start_addr start address in instruction cache | |
* \param[in] size the bytes to be invalidated | |
* \return 0, succeeded, -1, failed | |
*/ | |
int32_t icache_invalidate_mlines(uint32_t start_addr, uint32_t size) | |
{ | |
if (!icache_available()) { return -1; } | |
if ((size == 0) || (size > icache_config.capacity)) | |
{ | |
return -1; | |
} | |
uint32_t end_addr; | |
uint32_t line_size; | |
uint32_t status; | |
line_size = (uint32_t)(icache_config.line); | |
end_addr = start_addr + size - 1; | |
start_addr &= (uint32_t)(~(line_size - 1)); | |
status = cpu_lock_save(); | |
do | |
{ | |
_arc_aux_write(AUX_IC_IVIL, start_addr); | |
Asm("nop_s"); | |
Asm("nop_s"); | |
Asm("nop_s"); | |
start_addr += line_size; | |
} | |
while (start_addr <= end_addr); | |
cpu_unlock_restore(status); | |
return 0; | |
} | |
/** | |
* \brief lock multi lines in instruction cache | |
* | |
* \param[in] start_addr start address in instruction cache | |
* \param[in] size the bytes to be locked | |
* \return 0, succeeded, -1, failed (cache already locked or other reasons) | |
*/ | |
int32_t icache_lock_mlines(uint32_t start_addr, uint32_t size) | |
{ | |
if (!icache_available()) { return -1; } | |
if ((size == 0) || (size > icache_config.capacity)) | |
{ | |
return -1; | |
} | |
uint32_t end_addr; | |
uint32_t line_size; | |
uint32_t status; | |
int32_t ercd = 0; | |
line_size = (uint32_t)(icache_config.line); | |
end_addr = start_addr + size - 1; | |
start_addr &= (uint32_t)(~(line_size - 1)); | |
status = cpu_lock_save(); | |
do | |
{ | |
_arc_aux_write(AUX_IC_LIL, start_addr); | |
if (_arc_aux_read(AUX_IC_CTRL) & IC_CTRL_OP_SUCCEEDED) | |
{ | |
start_addr += line_size; | |
} | |
else | |
{ | |
ercd = -1; /* the operation failed */ | |
break; | |
} | |
} | |
while (start_addr <= end_addr); | |
cpu_unlock_restore(status); | |
return ercd; | |
} | |
/** | |
* \brief directly write icache internal ram | |
* | |
* \param[in] cache_addr, icache internal address(way+index+offset) | |
* \param[in] tag cache tag to write (tag+lock bit+valid bit) | |
* \param[in] data cache data to write | |
* \return 0, succeeded, -1, failed | |
*/ | |
int32_t icache_direct_write(uint32_t cache_addr, uint32_t tag, uint32_t data) | |
{ | |
if (!icache_available()) { return -1; } | |
if (_arc_aux_read(AUX_IC_CTRL) & IC_CTRL_INDIRECT_ACCESS) | |
{ | |
return -1; | |
} | |
_arc_aux_write(AUX_IC_RAM_ADDR, cache_addr); | |
_arc_aux_write(AUX_IC_TAG, tag); | |
_arc_aux_write(AUX_IC_DATA, data); | |
return 0; | |
} | |
/** | |
* \brief directly read icache internal ram | |
* | |
* \param[in] cache_addr, icache internal address(way+index+offset) | |
* \param[out] tag cache tag to read (tag+index+lock bit+valid bit) | |
* \param[out] data cache data to read | |
* \return 0, succeeded, -1, failed | |
*/ | |
int32_t icache_direct_read(uint32_t cache_addr, uint32_t *tag, uint32_t *data) | |
{ | |
if (!icache_available()) { return -1; } | |
if (_arc_aux_read(AUX_IC_CTRL) & IC_CTRL_INDIRECT_ACCESS) | |
{ | |
return -1; | |
} | |
_arc_aux_write(AUX_IC_RAM_ADDR, cache_addr); | |
*tag = _arc_aux_read(AUX_IC_TAG); | |
*data = _arc_aux_read(AUX_IC_DATA); | |
return 0; | |
} | |
/** | |
* \brief indirectly read icache internal ram | |
* | |
* \param[in] mem_addr, memory address | |
* \param[out] tag cache tag to read | |
* \param[out] data cache data to read | |
* \return 0, succeeded, -1, failed | |
*/ | |
int32_t icache_indirect_read(uint32_t mem_addr, uint32_t *tag, uint32_t *data) | |
{ | |
if (!icache_available()) { return -1; } | |
if (!(_arc_aux_read(AUX_IC_CTRL) & IC_CTRL_INDIRECT_ACCESS)) | |
{ | |
return -1; | |
} | |
_arc_aux_write(AUX_IC_RAM_ADDR, mem_addr); | |
if (_arc_aux_read(AUX_IC_CTRL) & IC_CTRL_OP_SUCCEEDED) | |
{ | |
*tag = _arc_aux_read(AUX_IC_TAG); | |
*data = _arc_aux_read(AUX_IC_DATA); | |
} | |
else | |
{ | |
return -1; /* the specified memory is not in icache */ | |
} | |
return 0; | |
} | |
/** | |
* \brief invalidate multi data cache lines | |
* | |
* \param[in] start_addr start address in data cache | |
* \param[in] size the bytes to be invalidated | |
* \return 0, succeeded, -1, failed | |
*/ | |
int32_t dcache_invalidate_mlines(uint32_t start_addr, uint32_t size) | |
{ | |
if (!dcache_available()) { return -1; } | |
uint32_t end_addr; | |
uint32_t line_size; | |
uint32_t status; | |
if ((size == 0) || (size > dcache_config.capacity)) | |
{ | |
return -1; | |
} | |
line_size = (uint32_t)(dcache_config.line); | |
end_addr = start_addr + size - 1; | |
start_addr &= (uint32_t)(~(line_size - 1)); | |
status = cpu_lock_save(); | |
do | |
{ | |
_arc_aux_write(AUX_DC_IVDL, start_addr); | |
Asm("nop_s"); | |
Asm("nop_s"); | |
Asm("nop_s"); | |
/* wait for flush completion */ | |
while (_arc_aux_read(AUX_DC_CTRL) & DC_CTRL_FLUSH_STATUS); | |
start_addr += line_size; | |
} | |
while (start_addr <= end_addr); | |
cpu_unlock_restore(status); | |
return 0; | |
} | |
/** | |
* \brief flush multi lines in data cache | |
* | |
* \param[in] start_addr start address | |
* \param[in] size the bytes to be flushed | |
* \return 0, succeeded, -1, failed | |
*/ | |
int32_t dcache_flush_mlines(uint32_t start_addr, uint32_t size) | |
{ | |
if (!dcache_available()) { return -1; } | |
if ((size == 0) || (size > dcache_config.capacity)) | |
{ | |
return -1; | |
} | |
uint32_t end_addr; | |
uint32_t line_size; | |
uint32_t status; | |
line_size = (uint32_t)(dcache_config.line); | |
end_addr = start_addr + size - 1; | |
start_addr &= (uint32_t)(~(line_size - 1)); | |
status = cpu_lock_save(); | |
do | |
{ | |
_arc_aux_write(AUX_DC_FLDL, start_addr); | |
Asm("nop_s"); | |
Asm("nop_s"); | |
Asm("nop_s"); | |
/* wait for flush completion */ | |
while (_arc_aux_read(AUX_DC_CTRL) & DC_CTRL_FLUSH_STATUS); | |
start_addr += line_size; | |
} | |
while (start_addr <= end_addr); | |
cpu_unlock_restore(status); | |
return 0; | |
} | |
/** | |
* \brief lock multi lines in data cache | |
* | |
* \param[in] start_addr start address in data cache | |
* \param[in] size the bytes to be locked | |
* \return 0, succeeded, -1, failed | |
*/ | |
int32_t dcache_lock_mlines(uint32_t start_addr, uint32_t size) | |
{ | |
if (!dcache_available()) { return -1; } | |
if ((size == 0) || (size > dcache_config.capacity)) | |
{ | |
return -1; | |
} | |
uint32_t end_addr; | |
uint32_t line_size; | |
uint32_t status; | |
int32_t ercd = 0; | |
line_size = (uint32_t)(dcache_config.line); | |
end_addr = start_addr + size - 1; | |
start_addr &= (uint32_t)(~(line_size - 1)); | |
status = cpu_lock_save(); | |
do | |
{ | |
_arc_aux_write(AUX_DC_LDL, start_addr); | |
Asm("nop_s"); | |
if (_arc_aux_read(AUX_DC_CTRL) & DC_CTRL_OP_SUCCEEDED) | |
{ | |
start_addr += line_size; | |
} | |
else | |
{ | |
ercd = -1; /* the operation failed */ | |
break; | |
} | |
} | |
while (start_addr <= end_addr); | |
cpu_unlock_restore(status); | |
return ercd; | |
} | |
/** | |
* \brief directly write dcache internal ram | |
* | |
* \param[in] cache_addr, dcache internal address(way+index+offset) | |
* \param[in] tag cache tag to write | |
* \param[in] data cache data to write | |
* \return 0, succeeded, -1, failed | |
*/ | |
int32_t dcache_direct_write(uint32_t cache_addr, uint32_t tag, uint32_t data) | |
{ | |
if (!dcache_available()) { return -1; } | |
if (_arc_aux_read(AUX_DC_CTRL) & DC_CTRL_INDIRECT_ACCESS) | |
{ | |
return -1; | |
} | |
_arc_aux_write(AUX_DC_RAM_ADDR, cache_addr); | |
_arc_aux_write(AUX_DC_TAG, tag); | |
_arc_aux_write(AUX_DC_DATA, data); | |
return 0; | |
} | |
/** | |
* \brief directly read dcache internal ram | |
* | |
* \param[in] cache_addr, dcache internal address(way+index+offset) | |
* \param[out] tag cache tag to read | |
* \param[out] data cache data to read | |
* \return 0, succeeded, -1, failed | |
*/ | |
int32_t dcache_direct_read(uint32_t cache_addr, uint32_t *tag, uint32_t *data) | |
{ | |
if (!dcache_available()) { return -1; } | |
if (_arc_aux_read(AUX_DC_CTRL) & DC_CTRL_INDIRECT_ACCESS) | |
{ | |
return -1; | |
} | |
_arc_aux_write(AUX_DC_RAM_ADDR, cache_addr); | |
*tag = _arc_aux_read(AUX_DC_TAG); | |
*data = _arc_aux_read(AUX_DC_DATA); | |
return 0; | |
} | |
/** | |
* \brief indirectly read dcache internal ram | |
* | |
* \param[in] mem_addr, memory address(tag+index+offset) | |
* \param[out] tag cache tag to read | |
* \param[out] data cache data to read | |
* \return 0, succeeded, -1, failed | |
*/ | |
int32_t dcache_indirect_read(uint32_t mem_addr, uint32_t *tag, uint32_t *data) | |
{ | |
if (!dcache_available()) { return -1; } | |
if (!(_arc_aux_read(AUX_DC_CTRL) & DC_CTRL_INDIRECT_ACCESS)) | |
{ | |
return -1; | |
} | |
_arc_aux_write(AUX_DC_RAM_ADDR, mem_addr); | |
if (_arc_aux_read(AUX_DC_CTRL) & DC_CTRL_OP_SUCCEEDED) | |
{ | |
*tag = _arc_aux_read(AUX_DC_TAG); | |
*data = _arc_aux_read(AUX_DC_DATA); | |
} | |
else | |
{ | |
return -1; /* the specified memory is not in dcache */ | |
} | |
return 0; | |
} | |
/** | |
* \brief initialize cache | |
* 1. invalidate icache and dcache | |
* 2. Only support ARCv2 cache | |
*/ | |
void arc_cache_init(void) | |
{ | |
uint32_t build_cfg; | |
build_cfg = _arc_aux_read(AUX_BCR_D_CACHE); | |
dcache_config.ver = build_cfg & 0xff; | |
if (dcache_config.ver >= 0x04) /* ARCv2 */ | |
{ | |
dcache_enable(DC_CTRL_DISABLE_FLUSH_LOCKED | | |
DC_CTRL_INDIRECT_ACCESS | DC_CTRL_INVALID_FLUSH); | |
dcache_invalidate(); | |
dcache_config.assoc = 1 << ((build_cfg >> 8) & 0xf); | |
dcache_config.capacity = 512 << ((build_cfg >> 12) & 0xf); | |
dcache_config.line = 16 << ((build_cfg >> 16) & 0xf); | |
} | |
build_cfg = _arc_aux_read(AUX_BCR_I_CACHE); | |
icache_config.ver = build_cfg & 0xff; | |
if (icache_config.ver >= 0x04) /* ARCv2 */ | |
{ | |
icache_config.assoc = 1 << ((build_cfg >> 8) & 0xf); | |
icache_config.capacity = 512 << ((build_cfg >> 12) & 0xf); | |
icache_config.line = 8 << ((build_cfg >> 16) & 0xf); | |
icache_enable(IC_CTRL_IC_ENABLE); | |
icache_invalidate(); | |
} | |
} |