2

我正在为 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++ 编写的。核心只需要指令本身,机器码

  1. 操作码 = vector_add = 0x12h
  2. register_src_1 = v0 = 0x00h
  3. register_src_2 = v1 = 0x01h
  4. register_dst = v2 = 0x02h

    机器码 = opcore | v0 | v1 | v2 = 0x7606E600h

(或其他任何东西,只是不同字段的连接以构建二进制指令)

一旦通过总线将其发送到内核,内核就能够使用专用总线从内存中请求所有数据,并在不使用处理器的情况下处理所有事情。最大的问题是:我怎样才能将前面的指令翻译成它的十六进制表示?(通过公共汽车发送不是问题)。想到的一些选择是

  • 运行解释代码(在处理器中运行时转换为机器代码)-> 非常慢,即使使用某种内联宏
  • 使用外部自定义编译器编译自定义部分,从外部存储器加载二进制文件并使用一些独特的指令将其移动到核心 --> 难以阅读/理解源代码,SDK 集成不佳,如果代码非常分段,则部分过多
  • JIT 编译 --> 就为了这个复杂?
  • 扩展编译器 --> 一场噩梦!
  • 一个连接到自定义核心的自定义处理器来处理所有事情:循环、指针、内存分配、变量... --> 工作量太大

问题是关于软件/编译器的,但对于那些对该主题有深入了解的人来说,这是 FPGA 中的 SoC,主处理器是 MicroBlaze,IP 核采用 AXI4 总线。

我希望我解释正确......提前谢谢!

4

3 回答 3

1

我不确定我是否完全理解,但我想我以前也遇到过类似的事情。根据对 rodrigo 回复的评论,听起来您的代码中散布着一些小指令。您还提到外部编译器是可能的,只是很痛苦。如果你将外部编译器与 C 宏结合起来,你会得到一些不错的东西。

考虑这段代码:

for (i=0; i<15;i++)
     CORE_EXEC(vector_add(v0, v1, v2), ref1)

CORE_EXEC 宏有两个用途:

  1. 您可以使用外部工具扫描源文件以查找这些条目并编译核心代码。此代码将使用“ref1”名称作为变量链接到 C(只需生成带有二进制位的 C 文件)。
  2. 在 C 中,您将定义 CORE_EXEC 宏以将“ref1”字符串传递给核心进行处理。

所以第一阶段将产生一个编译二进制核心指令的文件,例如上面可能有一个这样的字符串:

const char * const cx_ref1[] = { 0x12, 0x00, 0x01, 0x02 };

你可以像这样定义 CORE_EXEC :

#define CORE_EXEC( code, name ) send_core_exec( cx_##name )

显然,您可以根据需要选择前缀,尽管在 C++ 中您可能希望使用命名空间。

就工具链而言,您可以为所有位生成一个文件,或者为每个 C++ 文件生成一个文件——这可能更容易进行脏检测。然后,您可以简单地将生成的文件包含在源代码中。

于 2012-01-13T14:05:51.773 回答
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);

与上述相关的另一条路径是修改汇编程序以添加新指令,为这些指令创建语法。然后你可以随意使用直接汇编语言或内联汇编语言,不修改编译器就不会让编译器使用它们,这是汇编器修改后的另一条路径。

于 2012-01-13T15:48:08.380 回答
0

难道您不能在程序开始时将所有代码部分转换为机器代码(仅一次),将它们以二进制格式保存在内存块中,然后在需要时使用这些二进制文件。

这基本上就是 OpenGL 着色器的工作方式,我发现这很容易管理。

主要缺点是内存消耗,因为您在内存中有相同脚本的文本和二进制表示。我不知道这对你来说是不是一个问题。如果是,则有部分解决方案,例如在编译源文本后将其卸载。

于 2012-01-13T13:38:32.727 回答