问题:linux 和 裸板通信 不采用ocm的时候 ,各自对内存访问不体现在另一个核的内存上。
zynq 的两级缓存 和ddr访问问题
linux + 裸核,linux那边的缓存行为还不确定
裸核的两级数据缓存 由 Xil_DCache 决定;
cache几个操作:clean、invalidate与flush的含义
cache line
cache line是cache的基本访问单元。
cache line一般都会包含valid和dirty两个状态位,如下图的v和d。
valid位表示当前cache line的内容是否有效。dirty位表示当前cache line的内容是否比内存上的要更新(即是否修改过)。
清除(flush) cache的意思是清除cache中存储的全部数据。对处理器而言,清除操作只要清零相应cache行的有效位即可。当存储器配置上有变化时,整体或部分cache可能需要进行清除(flush)操作。有时也用术语作废(invalidate)来代替术语“清除(flush)”。然而,对于采用写回策略的D-cache,就需要使用清理(clean)操作(注:dcache也有flush(清除)操作,见flush_dcache_range函数)。
清理(clean) cache的意思是把脏的(即被改写过的)cache行强制写到主存,并把cache行中的脏位清零。清理(clean)cache可以重建cache与主存之间的一致性,它只用在使用写回策略的D-cache上。
arm a9系列 v7架构 的cache设置功能
linux 启动CPU过程:
https://www.cnblogs.com/linhaostudy/p/16384935.html
一般主处理器启动从处理器有以下三种:
(1).ACPI
(2).spin-table
(3).PSCI
第一种ACPI是高级配置与电源接口(Advanced Configuration and Power Interface)一般在x86平台用的比较多,而后两种spin-table(自旋表)和PSCI(电源状态协调协议 Power State Coordination)会在arm平台上使用,本系列主要讲解后两种。主要内容分为上下两篇如下:
soc启动流程:soc启动的一般会从片内的rom, 也叫bootrom开始执行第一条指令,这个地址是系统默认的启动地址,会在bootrom中由芯片厂家固化一段启动代码来加载启动bootloader到片内的sram,启动完成后的bootloader除了做一些硬件初始化之外做的最重要的事情是初始化ddr,因为sram的空间比较小所以需要初始化拥有大内存 ddr,最后会从网络/usb下载 或从存储设备分区上加载内核到ddr某个地址,为内核传递参数之后,然后bootloader就完成了它的使命,跳转到内核,就进入了操作系统内核的世界。
4)linux内核启动流程:bootloader将系统的控制权交给内核之后,他首先会进行处理器架构相关初始化部分,如设置异常向量表,初始化mmu(之后内核就从物理地址空间进入了虚拟地址空间的世界,一切是那么的虚无缥缈,又是那么的恰到好处)等等,然后会清bss段,设置sp之后跳转到C语言部分进行更加复杂通用的初始化,其中会进行内存方面的初始化,调度器初始化,文件系统等内核基础组件 初始化工作,随后会进行关键的从处理器的引导过程,然后是各种实质性的设备驱动的初始化,最后 创建系统的第一个用户进程init后进入用户空间执行用户进程宣誓内核初始化完成,可以进程正常的调度执行。
5)系统初始化阶段大多数都是主处理器做初始化工作,所有不用考虑处理器并发情况,一旦从处理器被bingup起来,调度器和各自的运行队列准备就绪,多个任务就会均衡到各个处理器,开始了并发的世界,一切是那么的神奇
在spin_table_secondary_jump中:首先会执行wfe指令,使得从处理器睡眠等待。如果被唤醒,则从处理器会判断spin_table_cpu_release_addr这个地址是否为0,为0则继续跳转到wfe处继续睡眠,否则跳转到spin_table_cpu_release_addr指定的地址处执行。
那么这个地址什么时候会被设置呢?答案是:主处理器在uboot中读取设备树的相关节点属性获得,我们来看下如何获得。执行路径为:
do_bootm_linux
->boot_prep_linux
->image_setup_linux
->image_setup_libfdt
->arch_fixup_fdt
->spin_table_update_dt
那么什么时候释放地址spin_table_cpu_release_addr 的内容不是0呢?
那么我们得回到主处理器流程上来:主处理器设置好了设备树,传递给内核设备树地址之后就要启动内核,启动内核之后,执行初始化工作,执行如下路径:
setup_arch //arch/arm64/kernel/setup.c:
->smp_init_cpus //arch/arm64/kernel/smp.c
->**smp_cpu_setup**
->cpu_ops[cpu]->cpu_init(cpu)
->smp_spin_table_ops->cpu_init //arch/arm64/kernel/cpu_ops.c
->**smp_spin_table_cpu_init**//arch/arm64/kernel/smp_spin_table.c
但是事与愿违,在这个函数中又有了一层关卡:689行到701行 判断是否secondary_holding_pen_release被设置为了从处理器的编号,如果设置的不是我的编号,则我再次进入705行执行wfe睡眠等待,行吧,那就等待啥时候主处理器来将secondary_holding_pen_release设置为我的处理器编号吧。那么何时会设置呢?答案是最终要启动从处理器的时候。
我们再次回到主处理器的处理流程,上面主处理器执行到了smp_prepare_cpus之后,继续往下执行,代码路径如下:
start_kernel ->arch_call_rest_init ->rest_init ->kernel_init, ->kernel_init_freeable ->smp_prepare_cpus //arch/arm64/kernel/smp.c ->smp_init //kernel/smp.c (这是从处理器启动的函数) ->cpu_up ->do_cpu_up ->_cpu_up ->cpuhp_up_callbacks ->cpuhp_invoke_callback ->cpuhp_hp_states[CPUHP_BRINGUP_CPU] ->**bringup_cpu** ->__cpu_up //arch/arm64/kernel/smp.c ->boot_secondary ->cpu_ops[cpu]->cpu_boot(cpu) ->smp_spin_table_ops.cpu_boot //arch/arm64/kernel/cpu_ops.c ->smp_spin_table_cpu_boot //arch/arm64/kernel/smp_spin_table.c
https://pic1.zhimg.com/80/v2-5152c326e8ce7e2ddde144b908102ec0_720w.jpg
zynq
Xil_DCacheInvalidateRange
发送前Xil_DCacheFlushRange,接收数据Xil_DCacheInvalidateRange
但感觉没有直接disable 效果好,核1 FLUSH,修改的数据,核0 关了cache还是没收到。
zynq相关
SCU: Snoop Control Unit,用来保持双核之间的数据Cache的一致性。也就是说,第一个A9 处理器写存储时,只是写在了缓存里,没有进主存,如果第二个A9读操作,涉及到第一个写脏了的数据段,SCU要保证第二个A9的缓存里是最新的数据。如果第二个A9写同样数据段的数据,需要在第一个中体现出写的内容。SCU的存在,才使得两个核成互相联系的 “双核”,才能成为MPsoc。
4、赛灵思 7 系列器件包含 32 个全局时钟缓冲器 (BUFG)。其中 16 个全局时钟缓冲器位于 FPGA 器件水平方向中心的上半部分,而另外 16 个则位于水平方向中心的下半部分。芯片上半部的 PLL 和 MMCM只能连接到水平方向中心以上的 16 个 BUFG 上。而芯片下半部的PLL 和 MMCM 只能连接到水平方向中心以下的 16 个 BUFG 上。选择 PLL 或 MMCM 时,请尽量使用 PLL,因为其具有更严格的抖动控制。在如下情况下也可以使用 MMCM :(1) PLL 已用尽 ; (2)MMCM 可提供所需的高级功能,但 PLL 则不能。
Zynq的AMP开发注意事项之禁用L2 cache
2.原理说明
首先,在AMP模式中,core 0与core 1共用512K L2 Cache,这势必会引起两个核的Cache竞争问题。
通常情况下,L2 Cache被core 0,core 1共享。那么会出现以下情况:
core 0的内存访问操作可能会清除core 1所使用的L2缓存内容,从而使core 1的软件性能有不确定性。
有时,我们需要为core 0 或core 1提供更多的确定性行为,尤其是架构为AMP时。
3.L2 Cache的锁定
通过寄存器的控制可以将L2 Cache锁定在不同core上,这让用户可以将L2 Cache的功能保留在特定的core上。
(1)相关说明
第一:Cache way是分区的宽度,Zynq的L2 Cache有8 Cache ways。
第二:L2 Cache控制器只能被锁定8个不同的方式。
如图 3.1所示,在Zynq 7000中,Cortex-A9 MP核的64个AXI被分为8个可以锁定的组。
问题验证步骤:
1.Core0-Linux初始化L2-cache,Core1-FreeRTOS BSP不对L2-cache初始化,Core1-FreeRTOS DMA搬运数据
core0的memtester 100M 1 操作影响core1的响应速度
2.Core0-Linux不初始化L2-cache,Core1-FreeRTOS BSP对L2-cache初始化,Core1-FreeRTOS DMA搬运数据
core0的memtester 100M 1 操作影响core1的响应速度
3.Core0-Linux不初始化L2-cache,Core1-FreeRTOS BSP对L2-cache初始化,lockdown by master方法,将L2-Cache空间一半用于Core0-Linux,一半用于缓存Core1-FreeRTOS的数据,Core1-FreeRTOS DMA搬运数据
网口不通,core0的memtester 100M 1 操作影响core1的响应速度
4.Core0-Linux不初始化L2-cache,Core1-FreeRTOS BSP对L2-cache初始化,lockdown by master方法,将L2-Cache空间全部用于缓存Core1-FreeRTOS的数据,Core1-FreeRTOS DMA搬运数据
core0网口OK,core0启动速度变慢,core0的memtester 100M 1操作影响core1的响应速度
5.Core0-Linux不初始化L2-cache,Core1-FreeRTOS BSP对L2-cache初始化,lockdown by master方法,将L2-Cache空间全部用于缓存Core1-FreeRTOS的数据,Core1-FreeRTOS CPU搬运数据
网口OK,core0启动速度变慢,core0的memtester 100M 1 对core1没有明显的影响
6.Core0-Linux初始化L2-cache,Core1-FreeRTOS BSP不对L2-cache初始化,Core1-FreeRTOS CPU搬运数据
网口OK,core0启动速度不受影响,memtester 100M 1操作影响core1的响应速度
7.Core0-Linux初始化L2-cache,Core1-FreeRTOS BSP不对L2-cache初始化,Core1-FreeRTOS BSP配置lockdown相关寄存器 将L2-Cache空间一半用于Core0-Linux,一半用于缓存Core1-FreeRTOS的数据,CPU搬运数据
网口OK,core0启动速度不受影响,core0的memtester 100M 1 对core1没有明显的影响
————————————————
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/Doriswang84/article/details/97272500
lockdown by master的相关寄存器:
/i/ll/?i=20190725165118762.png?,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0Rvcmlzd2FuZzg0,size_16,color_FFFFFF,t_70
ldr r0, =L2CCDLockdown0 /* Load L2CC base address base + reg9_d_lockdown0 register*/
ldr r1, =0x0f
str r1, [r0] /* lockdown by cpu0 */
ldr r0, =L2CCILockdown0 /* Load L2CC base address base + way register*/
ldr r1, =0x0f
str r1, [r0] /* lockdown by cpu0 */
ldr r0, =L2CCDLockdown1 /* Load L2CC base address base + reg9_d_lockdown1 register */
ldr r1, =0xf0
str r1, [r0] /* lockdown by cpu1 */
ldr r0, =L2CCILockdown1 /* Load L2CC base address base + reg9_i_lockdown1 register */
ldr r1, =0xf0
str r1, [r0] /* lockdown by cpu1 */
————————————————
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/Doriswang84/article/details/97272500
如何初始化L2 cache
mrc p15,1,r0,c9,c0,2
orr r0,r0,#0x1
mcr p15,1,r0,c9,c0,2
失效:
mov ro,#0
mcr p15,0,r0,c7,c14,2
缓存读写策略
use_amp 以及进制初始化CPU1的l2缓存,但还有l1缓存。
那么总结一下就是,“-DUSE_AMP=1”编译选项将影响到工程代码里的USE_AMP预编译指令,使得Cache操作函数、全局时钟以及中断控制器SCUGIC的初始化函数不被编译进CPU1的应用程序中,避免可能会出现的CPU0和CPU1Cache一致性维护异常和中断异常等问题。
两个核心各自拥有独立的L1 DCache,并且共享同一个L2 DCache,在ZYNQ中存在一个Snoop Control Unit (SCU)用于维护CORE0和CORE1的L1 DCache与L2 DCache之间的一致性
但好像没更新是为啥(linux smp那边设置的?)
DCache一致性维护的原理为:在多级存储器结构中,CPU通过1级或多级Cache与DDR产生连接,CPU本身不直接访问DDR,而是通过Cache访问DDR。Cache中始终会暂存一小部分(通常是KB~几MB量级)CPU最近访问的DDR某些地址区域中的数据。因此,在应用程序中对DDR进行读或写操作,实际上都是CPU对Cache进行读或写操作。当DDR中某个地址范围内的数据突然被除CPU以外的Master(如DMA)改变时,若此时Cache中保存了这些区域的数据,且这些数据在Cache中状态为有效时,当CPU需要再次读取DDR这片区域的数据时,就不会让Cache去读取DDR中此区域内最新的数据来更新Cache,再从Cache里读取最新的数据,而是直接从Cache中读取原来的旧数据,显然这不是我们所期望的结果。这时,便引入了所谓的Cache一致性问题。
ZYNQ中维护DCache一致性的方法为:写入方将数据写入DDR对应地址区域后,需将残留在DCache中相应地址范围内的数据全部刷入DDR3中。读取方在从DDR相应地址读取数据之前,需将DCache中DDR相应地址范围内的数据全部设置为invalid,然后CPU会再次通过DCache从DDR3中读取该地址范围内最新的数据。
在get_data_from_region函数中,在将CORE0传递的数据复制到本地缓存区域之前,调用Xil_DCacheInvalidateRange函数将DCache中该数据指针所指向内存区域的数据设置为invalid,进行DCache一致性维护
CORE0通过shared_mem.c中的put_data_to_region()函数将所需传递的数据长度及指针存入shared region结构体中。在put_data_to_region函数中调用Xil_DCacheFlushRange函数将DCache中该数据指针所指向内存区域的数据刷入DDR中,进行DCache一致性维护。
五、清理和清除cache
ARM使用 清理和 清除表示对cache的两种基本操作。
清理就是把脏的(即被改写过的)cache行强制写到主存,并把cache行中的脏位清零。清理cache可以重建cache和主存之间的一致性,它只是用在回写策略的D-cache上。
清除就是指清除cache中存储的全部数据,其实就是将cache使无效,将cache行中的有效位清零,让所有访问这个cache行的操作都不命中。
对cache进项清理和清除的操作是通过协处理器CP15中的寄存器C7实现的,具体操作指令看CP15协处理器寄存器详解。
————————————————
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/gameit/article/details/13169445
标签:初始化,cache,Cache,访问,多核,内存,L2,Core1,cpu From: https://www.cnblogs.com/ycjstudy/p/18228075