421 lines
11 KiB
C
421 lines
11 KiB
C
|
/* ------------------------------------------
|
||
|
* 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();
|
||
|
}
|
||
|
|
||
|
}
|