我正在为FPGA和ASIC开发通用图像处理核心。我们的想法是将标准处理器与它连接起来。我遇到的一个问题是如何“编程”它。让我解释一下:核心有一个指令解码器,用于我的“自定义”扩展。例如:
vector_addition $vector[0], $vector[1], $vector[2] // (i.e. v2 = v0+v1)
还有更多这样的人。该操作由处理器通过总线发送到核心,使用处理器进行循环,非向量操作等,如下所示:
for (i=0; i<15;i++) // to be executed in the processor
vector_add(v0, v1, v2) // to be executed in my custom core
程序是用C / C ++编写的。核心只需要指令本身,在机器代码中
register_dst = v2 = 0x02h
机器代码= opcore | v0 | v1 | v2 = 0x7606E600h
(或者其他,只是用二进制构建指令的不同字段的连接)
通过总线将其发送到核心后,核心能够通过专用总线从内存中请求所有数据,并在不使用处理器的情况下处理所有内容。最大的问题是:如何将前一条指令转换为十六进制表示?(通过总线发送它不是问题)。想到的一些选项是
问题在于软件/编译器,但对于那些对本主题有深入了解的人来说,这是一个FPGA中的SoC,主处理器是MicroBlaze,而IP Core则使用AXI4总线。
我希望我能正确解释...提前致谢!
答案 0 :(得分:1)
我不确定我完全理解,但我认为我之前遇到过类似的事情。根据对rodrigo的回应的评论,听起来你的代码中散布着一些小的指令。你也提到外部编译器是可能的,只是一个痛苦。如果将外部编译器与C宏结合使用,可以获得一些体面的东西。
考虑以下代码:
for (i=0; i<15;i++)
CORE_EXEC(vector_add(v0, v1, v2), ref1)
CORE_EXEC宏有两个目的:
因此第1阶段将生成一个已编译的二进制核心指令文件,例如上面可能有这样的字符串:
const char * const cx_ref1[] = { 0x12, 0x00, 0x01, 0x02 };
您可以像这样定义CORE_EXEC:
#define CORE_EXEC( code, name ) send_core_exec( cx_##name )
显然你可以选择你想要的前缀,不过在C ++中你可能希望使用命名空间。
就工具链而言,您可以为所有位生成一个文件,或者为每个C ++文件生成一个文件 - 这可能更容易进行脏检测。然后,您只需在源代码中包含生成的文件即可。
答案 1 :(得分:0)
你无法在程序开始时将所有代码段翻译成机器代码(只需一次),将它们以二进制格式保存在内存块中,然后在需要时使用这些二进制文件。
这基本上是OpenGL着色器的工作原理,我觉得很容易管理。
主要缺点是内存消耗,因为在内存中有相同脚本的文本和二进制表示。我不知道这对你来说是否有问题。如果是,则有部分解决方案,因为在编译源文本后将其卸载。
答案 2 :(得分:0)
让我们说我要修改一个arm核心来添加一些自定义指令,我想要运行的操作在编译时就已知(将在一秒内到达运行时)。
我会使用汇编,例如:
.globl vecabc
vecabc:
.word 0x7606E600 ;@ special instruction
bx lr
或者内联它与编译器的内联语法无关,如果您需要使用处理器寄存器,例如c编译器以内联汇编语言填充寄存器,那么汇编程序就会组装这些指令。我发现编写实际的asm并且只是像上面那样在指令流中注入单词,只有编译器将一些字节作为数据并且将一些字节作为指令进行删除,核心将按照所写的顺序看到它们。
如果你需要实时做事,你可以使用自我修改代码,我再次喜欢使用asm到蹦床。构建你想要在ram中运行的指令,比如地址0x20000000,然后让蹦床调用它:
.globl tramp
tramp:
bx r0 ;@ assuming you encoded a return in your instructions
用
调用它tramp(0x20000000);
与上述相关的其他路径是修改汇编程序以添加新指令,为这些指令创建语法。然后你可以随意使用直接汇编语言或内联汇编语言,你不会让编译器在不修改编译器的情况下使用它们,这是修改汇编程序后的另一条路径。