我正在编写一个嵌入式powerpc 32系统,该系统具有32 KB的8路组关联L2指令缓存。为了避免高速缓存抖动,我们以一种方式对齐函数,使得以高频率调用的一组函数的文本(想想中断代码)最终在单独的高速缓存集中。我们通过根据需要插入虚函数来实现这一点,例如
void high_freq1(void)
{
...
}
void dummy(void)
{
__asm__(/* Silly opcodes to fill ~100 to ~1000 bytes of text segment */);
}
void high_freq2(void)
{
...
}
这让我感到丑陋和不理想。我想做的是
__asm__
并使用纯C89(可能是C99)dummy()
间隔符dummy()
spacer的大小应该可以配置为4个字节的倍数。典型的垫片是260到1000个字节。我也愿意探索以一种方式放置一组选定函数的全新技术,这样它们就不会映射到相同的缓存行。链接描述文件可以这样做吗?
答案 0 :(得分:4)
使用GCC的__attribute__(( aligned(size) ))
。
或者,在GCC命令行上传递-falign-functions=n
。
答案 1 :(得分:3)
也许 linker scripts 是可行的方法。我认为GNU链接器可以使用这些...我已经在AVR和MQX上使用了LD文件,我们使用基于GCC的编译器......可能有帮助......
你可以定义你的记忆部分等以及那里的内容......每次我来写一个它已经这么久了,因为我不得不再读一遍......
搜索SVR3样式的命令文件到gem。
免责声明:以下是一个非常具体的编译器示例...但类似SVR3的格式非常通用......您必须阅读系统
例如,您可以使用像......
这样的命令ApplicationStart = 0x...;
MemoryBlockSize = 0x...;
ApplicationDataSize = 0x...;
ApplicationLength = MemoryBlockSize - ApplicationDataSize;
MEMORY {
RAM: ORIGIN = 0x... LENGTH = 1M
ROM: ORIGIN = ApplicationStart LENGTH = ApplicationLength
}
这为链接器定义了三个内存部分。然后你可以说像
这样的话SECTIONS
{
GROUP :
{
.text :
{
* (.text)
* (.init , '.init$*')
* (.fini , '.fini$*')
}
.my_special_text ALIGN(32):
{
* (.my_special_text)
}
.initdat ALIGN(4):
// Blah blah
} > ROM
// SNIP
}
SECTIONS
命令告诉链接器如何将输入节映射到输出节,以及如何将输出节放在内存中...这里我们说的是什么进入ROM输出部分,我们在上面的MEMORY
定义中定义。你可能感兴趣的是.my_special_text
。在您的代码中,您可以执行诸如...之类的操作。
__attribute__ ((section(".my_special_text")))
void MySpecialFunction(...)
{
....
}
链接器会将__attribute__
语句前面的任何函数放入my_special_text
部分。在上面的例子中,它被放置在text
部分之后的下一个4字节对齐边界的ROM中,但你可以随意放置它。所以你可以为你描述的每个函数创建几个部分,并确保地址不会引起冲突......
您可以使用表单
的链接器定义变量来表示节的大小和内存位置extern char_fsection_name[]; // Set to the address of the start of section_name
extern char_esection_name[]; // Set to the first byte following section_name
所以对于这个例子......
extern char _fmy_special_text[]; // Set to the address of the start of section_name
extern char _emy_special_text[]; // Set to the first byte following section_name
答案 2 :(得分:3)
如果您愿意付出一些努力,可以使用
__attribute__((section(".text.hotpath.a")))
将函数放入单独的部分,然后在自定义链接描述文件中显式放置函数。
这比简单地要求对齐函数提供了更精细的控制,但需要更多的手持。
示例,假设您要将4KiB锁定到缓存中:
SECTIONS {
.text.hotpath.one BLOCK(0x1000) {
*(.text.hotpath.a)
*(.text.hotpath.b)
}
}
ASSERT(SIZEOF(.text.hotpath.one) <= 0x1000, "Hot Path functions do not fit into 4KiB")
这将确保热路径函数a
和b
彼此相邻,并且都适合在4 KiB边界上对齐的4 KiB的同一块,因此您可以简单地将该页面锁定到缓存中;如果代码不合适,则会出错。
您甚至可以使用
NOCROSSREFS(.text.hotpath.one .text)
禁止调用其他函数的热路径函数。
答案 3 :(得分:2)
假设您正在使用GCC和GAS,这可能是一个简单的解决方案:
void high_freq1(void)
{
...
}
asm(".org .+288"); /* Advance location by 288 bytes */
void high_freq2(void)
{
...
}
您甚至可以使用它来设置函数的绝对位置,而不是使用地址中的相对增量,这样可以使您在修改函数时将函数与大小改变的后果隔离开来。
当然,这不是纯粹的C89,但它可能不如使用虚拟功能那么难看。 :)
(然后,应该提到链接器脚本也没有标准化。)
编辑:正如评论中所述,在这种情况下将-fno-toplevel-reorder
标志传递给GCC似乎很重要。