0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

先楫HPM片上Cache使用指南经验分享

先楫半导体HPMicro 来源:先楫半导体HPMicro 2024-01-22 16:07 次阅读

概 述

高速缓存(Cache)主要是为了解决CPU运算速度与内存(Memory)读写速度不匹配的矛盾而存在, 是CPU与存储设备之间的临时存贮器,容量小,但是交换速度比内存快。内置高速缓存通常对CPU的性能提升具有较大作用。

CPU要读取一个数据时,首先从Cache中查找,如果找到就立即读取并送给CPU处理;如果没有找到,就用相对慢的速度从内存中读取并送给CPU处理,同时把这个数据所在的数据块调入Cache中,可以使得以后对整块数据的读取都从Cache中进行,不必再调用内存。

1f9f6af8-b8fd-11ee-8b88-92fbcf53809c.png

这样的读取机制使CPU读取Cache的命中率非常高(大多数CPU可达90%左右),也就是说CPU下一次要读取的数据90%都在Cache中,只有大约10%需要从内存读取。HPM CPU访问片上的Cache内数据是零等待的,这大大节省了CPU直接读取内存数据的时间,使CPU读取数据时基本无需等待。总的来说,CPU读取数据的顺序是先Cache后存储设备。

一、Cacheable Memory 相关概念

在访问HPM片上ILM与DLM(Local Memory)时,芯片物理结构决定了CPU不会使用Cache去缓存Local Memory的数据。访问其它存储设备如flash、sramsdram等,则Cache可以发挥其缓存机制来加快访问速度。在Cache生效的地址空间内,用户可以设置Memory的物理存储属性来设置是否对指定的地址空间使用Cache。

1fa781c0-b8fd-11ee-8b88-92fbcf53809c.png

PMA(Physical Memory Attributes)是指一段存储地址空间的可读写、可执行、可缓存等属性。读、写、执行等属性容易理解,此处不赘述。下面介绍几个其它属性及相关的概念。(注意:HPM5300系列不支持PMA设置)

首先介绍一些Cache基本概念。

1. Cache Line/dirty/invalidate

Cache Line:一次最少缓存多少字节的数据是有要求的,通常以Cache Line为单位。HPM6000系列MCU Cache Line为64byte,HPM5300系列MCU Cache Line为32byte。在进行PMA设置时,要求起始地址按Cache Line字节数对齐,大小为Cache Line大小的整数倍。声明数组时最好也遵循此规则。

Dirty:表示某Cache Line的数据是否与Memory保持一致,如果只将数据写入Cache而没有写入Memory,会将该Cache Line标记为dirty。

Invalidate:将某地址范围的Cache Line数据失效掉,当Cache Line状态被Invalidate时,不管读取是否命中,CPU都会到Memory拿数据。

对Cache的标准操作包括 write-back,invalidate,flush。

Write-back表示把cache内dirty的数据写入Memory,invalidate表示忽略某地址范围的Cache line,flush操作则先对某Cache Line 进行write-back操作,再进行invalidate操作。HPM SDK的hpm_l1c_drv.h文件提供了这3种操作的接口函数。

2. Bufferable

Bufferable是指MCU在写入一片内存区时,是否可使用Write buffer进行加速。例如向sram内写入64个字节:

1)不使用Bufferable:CPU等待64字节数据写入完成后再去执行其它指令;

2)使用Bufferable:CPU将64字节数据写入Write buffer,不等Write buffer内的数据写入sram,CPU就去执行其它指令;写入动作则自动进行直至完成。

3. Cacheable

Cacheable与 non-Cacheable,决定了CPU是否启用缓存特性。如果启用Cacheable特性,则HPM芯片上的内存区域可以分区指定PMA,可选的属性选项如下(详细信息可参考先楫官方文档HPM6200 UM 2.8章节):

Write-Back

Write-Back(与Write-Through互斥)是指向存储设备内写数据命中时,CPU将数据写入Cache,并不立马向存储设备写入数据,如下图所示:数据先写入到Cache内(①),在Cache内标记该Cache Line为dirty,即表示该Cache Line内容与Memory内容不符;Cache内数据写入Memory(②),则在Cache Line被替换或手动执行write-back操作或flush操作时(把dirty的数据写入Memory)才执行。

未命中时,则写入Memory。是否写入Cache 由xxx-Allocate决定。

1fae35d8-b8fd-11ee-8b88-92fbcf53809c.png

Write-Through

Write-Through(与Write-Back互斥)是指向存储设备内写数据时,无论命中与否,CPU都将数据写入Memory。

命中时,数据同时写入Cache 与Memory;

未命中时,数据写入Memory,是否写入Cache 由xxx-Allocate决定。

xxx-Allocate

xxx-Allocate则用于控制读/写未命中Cache时,是否要在Cache内申请Cache Line用于缓存读/写的数据。例如:

Read-Allocate代表读未命中时,CPU不只从Memory将数据读入,还将数据在Cache放了一份,那么下次再读的时候就不用去Memory读了;

Write-Allocate代表写未命中时,会在Cache内分配Cache Line储存写入的数据,那么下次读的时候就可以从Cache读了;具体是否写入Memory取决于使用的是Write-Back还是Write-Through。

Non-Allocate和 Read-and-Write-Allocate就不再进行解释了。

/* Init noncachable memory */

externuint32_t__noncacheable_start__[];

externuint32_t__noncacheable_end__[];

start_addr = (uint32_t) __noncacheable_start__;

end_addr = (uint32_t) __noncacheable_end__;

length = end_addr - start_addr;

if(length > 0) {

/* Ensure the address and the length are power of 2 aligned */

assert((length & (length - 1U)) == 0U);

assert((start_addr & (length - 1U)) == 0U);

pmp_entry[index].pmp_addr= PMP_NAPOT_ADDR(start_addr, length);

pmp_entry[index].pmp_cfg.val= PMP_CFG(READ_EN, WRITE_EN, EXECUTE_EN, ADDR_MATCH_NAPOT, REG_UNLOCK);

pmp_entry[index].pma_addr= PMA_NAPOT_ADDR(start_addr, length);

pmp_entry[index].pma_cfg.val= PMA_CFG(ADDR_MATCH_NAPOT, MEM_TYPE_MEM_NON_CACHE_BUF, AMO_EN);

index++;

}

pmp_config(&pmp_entry[0], index);

以上代码设置了__noncacheable_start__至__noncacheable_end__地址范围内的存储区域PMA属性为noncacheable,bufferable。

通过以上解释,相信开发者可以看懂UM手册内的相关描述了,以HPM6200系列为例,User Manual v2.0 2.8章节的内容对PMA有详细描述。

二、HPM L1-Cache相关函数

HPM系列芯片L1-Cache分为 iCache与 dCache,指令缓存与数据缓存。开发者们经常遇到的问题是开启dCache导致的CPU拿到的数据与Memory内数据不一致(Cache内的数据与Memory不一致时,读取命中Cache会发生这样的结果)。因此,此处主要介绍 dCache相关函数。

打开hpm_l1c_drv.h文件即可看到先楫提供的Cache相关的函数,部分如下:

*

* @brief D-cache disable

*/

voidl1c_dc_disable(void);

/*

* @brief D-cache enable

*/

voidl1c_dc_enable(void);

/*

* @brief D-cache invalidate by address

* @param[in] address Start address to be invalidated

* @param[in] size Size of memory to be invalidated

*/

voidl1c_dc_invalidate(uint32_taddress, uint32_tsize);

/*

* @brief D-cache writeback by address

* @param[in] address Start address to be writtenback

* @param[in] size Size of memory to be writtenback

*/

voidl1c_dc_writeback(uint32_taddress, uint32_tsize);

/*

* @brief D-cache invalidate and writeback by address

* @param[in] address Start address to be invalidated and writtenback

* @param[in] size Size of memory to be invalidted and writtenback

*/

voidl1c_dc_flush(uint32_taddress, uint32_tsize);

/*

* @brief D-cache fill and lock by address

* @param[in] address Start address to be filled and locked

* @param[in] size Size of memory to be filled and locked

*/

voidl1c_dc_fill_lock(uint32_taddress, uint32_tsize);

/*

* @brief Invalidate all icache and writeback all dcache

*/

voidl1c_fence_i(void);

/*

* @brief Invalidate all d-cache

*/

voidl1c_dc_invalidate_all(void);

/*

* @brief Writeback all d-cache

*/

voidl1c_dc_writeback_all(void);

/*

* @brief Flush all d-cache

*/

voidl1c_dc_flush_all(void);

l1c_dc_disable:关闭dCache。此函数特别有用,在debug时如果怀疑是Cache导致的问题,在main函数开始关闭dCache再次运行即可排查是否是Cache导致的问题。注意,如果是用户程序运行过程中关闭dCache,需要在关闭前将执行l1c_dc_writeback_all,保证Cache数据写入Memory。

l1c_dc_enable:开启dCache。

l1c_dc_invalidate:将某地址范围内的Cache Line失效掉。无论某地址在Cache内是否命中,CPU会从Memory内拿数据。

l1c_dc_writeback:将Cache内数据写入某Memory地址。如果该地址在Cache内,则将该Cache Line写入Memory,并清除dirty标志。

l1c_dc_flush:该函数等于 l1c_dc_writeback + l1c_dc_invalidate,把数据写入到Memory并标记为invalidate,表示下次从Memory拿数据时不走Cache。

l1c_fence_i:将dCache内的数据全部writeback,将iCache内所有Cache Line invalidate。一般关闭Cache前会手动调用此函数。

l1c_dc_invalidate_all、l1c_dc_writeback_all、l1c_dc_flush_all:代表操作整个Cache中的Cache Line,具体含义不再赘述。

三、经验分享

l1c_dc_writeback:一般非CPU的总线host,如DMA访问某Memory地址前,通过l1c_dc_writeback将Cache内的数据写到Memory,保证DMA拿到的数据与CPU看到的数据是一致的。

例如I2C_DMA例程:

/* setup i2c dma tx */

#ifPLACE_BUFF_AT_CACHEABLE

if(l1c_dc_is_enabled()) {

/* cache writeback before DMA sent data */

l1c_dc_writeback((uint32_t)tx_buff, TEST_TRANSFER_DATA_IN_BYTE);

}

#endif

stat= i2c_tx_trigger_dma(TEST_I2C_DMA,

TEST_I2C_DMA_CH,

TEST_I2C,

core_local_mem_to_sys_address(BOARD_RUNNING_CORE, (uint32_t)tx_buff),

TEST_TRANSFER_DATA_IN_BYTE);

if(stat!= status_success) {

printf("i2c tx trigger dma failed ");

while(1) {

}

}

在DMA将tx_buff数据搬到I2C的发送寄存器之前,进行了writeback。

l1c_dc_invalidate:一般CPU在读取Memory数据时,如果该数据被其它总线host如DMA操作过(一般是DMA搬了某些数据过去),为了能读到Memory中的数据而不是Cache中的数据,要在读取之前对Cache Line进行invalidate处理(多数开发者遇到的都是这个问题)。

例如I2C_DMA例程:

/* setup i2c dma rx */

stat= i2c_rx_trigger_dma(TEST_I2C_DMA,

TEST_I2C_DMA_CH,

TEST_I2C,

core_local_mem_to_sys_address(BOARD_RUNNING_CORE, (uint32_t)rx_buff),

TEST_TRANSFER_DATA_IN_BYTE);

i2c_master_start_dma_read(TEST_I2C, TEST_I2C_SLAVE_ADDRESS, TEST_TRANSFER_DATA_IN_BYTE);

i2c_handle_dma_transfer_complete(TEST_I2C);

#ifPLACE_BUFF_AT_CACHEABLE

if(l1c_dc_is_enabled()) {

/* cache invalidate after DMA receive data */

l1c_dc_invalidate((uint32_t)rx_buff, TEST_TRANSFER_DATA_IN_BYTE);

}

#endif

check_transfer_data();

在进行check_transfer_data之前,先对数据进行了l1c_dc_invalidate处理。

l1c_fence_i:一般在CPU关闭Cache之前,或程序跳转之前(一般二级boot选择好要执行的固件进行跳转),为了保证所有dirty的Cache Line写入到Memory中,会进行l1c_dc_writeback_all,然后等 l1c_dc_writeback_all执行完毕后再跳转。

例如tinyuf2例程:

voiduf2_board_app_jump(void)

{

fencei();

l1c_dc_disable();

l1c_ic_disable();

__asm("la a0, %0"::"i"(BOARD_FLASH_APP_START+ 4));

__asm("jr a0");

}

uf2_board_app_jump函数在跳转前,执行了fencei,本质上就是l1c_fence_i。

另外,在执行writeback操作期间中断不可用,对实时性要求高的场景应进行合理规划l1c_dc_writeback_all的使用。

四、文末小结

Cache能大幅提高程序运行性能,但用不好Cache也会给开发者带来各种“奇奇怪怪”的问题现象。在阅读本文后,希望开发者对先楫的 L1-Cache有更深入的理解,使用先楫半导体高性能 MCU系列产品开发项目时,能更加得心应手。







审核编辑:刘清

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • sram
    +关注

    关注

    6

    文章

    742

    浏览量

    113905
  • 高速缓存
    +关注

    关注

    0

    文章

    29

    浏览量

    10980
  • Cache
    +关注

    关注

    0

    文章

    127

    浏览量

    27984
  • HPM
    HPM
    +关注

    关注

    1

    文章

    17

    浏览量

    7652

原文标题:经验分享 | 先楫 HPM片上 Cache使用指南

文章出处:【微信号:HPMicro,微信公众号:先楫半导体HPMicro】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    HPM5361EVK开发板试用体验】认识和了解HPM5361EVK开发板

    ,拆开第一眼看到HPM5361EVK开发板,黑色的多层主板主控芯片和其它元件排列整齐,做工非常精细,元件布局合理,元件标识清晰,提供的io接口和调试接口非常丰富。
    发表于 12-24 22:39

    HPM5361EVK开发板试用体验】HPM5361EVK开发板初体验

    收到HPM5361EVK开发板,被HPM5361EVK开发板的做工和电路板设计惊艳到了,
    发表于 12-24 22:58

    半导体HPM6750EVKMINI评估板试用体验】半导体HPM6750EVKMINI评估板开箱

    HPM6750EVKMINI评估板试用资格。RISCV是最近几年在国内逐渐流行起来的精简指令集,发源于美国伯克利大学。从去年2021年开始,市场逐渐涌现了非常多基于RISCV指令集的MCU。本次开箱的是
    发表于 06-26 16:24

    如何使用Segger Embedded Studio开发HPM6750?

    。今年3月,半导体与Segger公司达成合作,向所有使用其HPM6000系列RISC-V微控制器的客户提供免费的Segger的跨平台集成开发环境“Embedded Studio”,共同推动RISC-V
    发表于 07-26 14:48

    半导体HPM6700系列正式合入OpenHarmony社区主干

    近日,由上海半导体科技有限公司(以下简称:半导体)推出的基于HPM6700系列高性能MCU通用开发板代码已完成并合入OpenAtom
    发表于 11-11 10:03

    如何使用CodeViser调试HPM6750开发板?

    HPM6750EVK2是基于半导体的HPM6750高性能SOC的开发板,HPM6750是基于RISC-V的双核处理器,主频高达816M。
    发表于 03-21 16:35

    半导体产品体验官代码仓库集锦

    主要用途: 在 hpm6750搭载 CherryUSB 主从协议栈,从机部分演示 cdc + msc 数据收发,主机部分演示各类 class 的自动枚举和数据通信 产品体验官:
    发表于 05-25 16:13

    HPM6000系列微控制器的各类SRAM使用指南

    HPM6000系列微控制器SRAM使用指南
    发表于 06-01 06:19

    HPM6000系列微控制器闪存使用指南

    HPM6000系列MCUFlash使用指南
    发表于 06-01 06:20

    HPM6000系列微控制器闪存使用指南

    HPM6000系列MCUFlash使用指南
    发表于 06-02 08:54

    具有高性能MCU配套的HPM SDK使用指南

    HPM SDK使用指南
    发表于 06-05 07:51

    半导体HPM5361EVK开发板开发资料免费下载

    HPM5300EVK 提供了一系列 HPM5300 微控制器外设的接口,包括一个 ADC 输入 SMA 接口和一个标准的电机控制及传感器接口。H
    发表于 10-20 11:21

    HPM5361EVK开发板试用体验】1上手HPM5361

    在本地存储更多的指令和数据,而不必依赖外部存储器,从而极大地提高了性能。 开发工具安装:上海半导体提供了HPM系列SoC底层驱动软件包,这个软件包包含了SoC所集成的P模块底层驱
    发表于 11-28 12:18

    HPM5361EVK开发板试用体验】-- HPM5361初体验

    HPM5361EVK开发板试用体验】-- HPM5361初体验
    发表于 12-11 10:27

    先楫 HPM片上 Cache使用指南

    贾工先楫资深FAE工程师12年产品研发经验,具有变频器、伺服等工业产品开发经验,也负责过激光投影显示系统开发、AI应用开发、PYQT、Linux驱动开发等工作。概述高速缓存(Cache)主要
    的头像 发表于 01-26 10:00 236次阅读
    先楫 <b class='flag-5'>HPM</b>片上 <b class='flag-5'>Cache</b><b class='flag-5'>使用指南</b>