指令行是计算机体系中最重要的组件之一。它能够协助开发人员更便利的操控体系。在调试进程中,能够让开发人员随时改变测验办法。
FPGA的本钱越来越低,FPGA上的嵌入式体系(SOPC)也运用得越来越广泛。可是相对其他老练的计算机体系,SOPC体系现在还没有指令行。为了更好的推行SOPC运用,笔者开发了一个智能的指令行模块,能够调用体系中的恣意函数,降低了开发人员的运用难度。在最小装备时,它只要大约1000行代码,占用14KB存储容量,能够放在彻底由FPGA片内资源构成的计算机体系中。它的结构也具有很好的扩展性,开发人员能够结合自己的需求,轻松增加指令,丰厚它的特性。
指令行结构规划
1 总体规划
好的指令行要具有扩展性,开发人员能轻松增加自己的指令;也要傻瓜化,简略易于运用。
为了到达这两个意图,可采用编译器创立的数组作为指令的总索引数据结构。为每条指令创立一个变量,指定它放在一个指令行专用的段中;其顶用段“shell_cmd_tab”寄存用户主动创立的指令;用段“shell_cmd_tab_auto”寄存体系中一切大局函数。创立变量只需调用宏SHELL_CMD_DECL,供给指令名、指令阐明和函数名即可。这样就能够完结C言语函数、变量、段、内存区域的主动映射进程。
指令的数据结构如下。
typedef struct _ncommand_t {
const char *name;
const char *descr;
shell_st_call cfunc;
} ncommand_t;
为了简化指令的创立,界说下面这个宏来创立指令。
#define SHELL_CMD_DECL(name, usage, func) ncommand_t _shell_cmd_tab_##func _attribute_ ( (section( “.shell_cmd_tab”) ) )={ name, usage, func }; // small memory version
创立指令的实例如下:SHELL_CMD_DECL(“dump”, “Shows a memory dump”, hexdump);
2 衔接脚本
在衔接脚本里指定段的方位是简化指令行运用流程的要害。GCC运用PROVIDE界说变量。笔者运用PROVIDE界说了各个段的开始地址和完毕地址,然后能够在代码中运用段地址查询到一切指令。在编译器运用的衔接脚本中增加如下行界说函数表。
.shell :
{
PROVIDE (__ram_shell_start = ABSOLUTE(.));
. = ALIGN(32 / 8);
PROVIDE (__ram_shell_cmd_tab_begin = ABSOLUTE(.));
*(.shell_cmd_tab .shell_cmd_tab.*)
PROVIDE (__ram_shell_cmd_tab_end = ABSOLUTE(.));
PROVIDE (__ram_shell_cmd_tab_auto_begin = ABSOLUTE(.));
*(.shell_cmd_tab_auto .shell_cmd_tab_auto.*)
PROVIDE (__ram_shell_cmd_tab_auto_end = ABSOLUTE(.));
*(.shell .shell.*)
. = ALIGN(32 / 8);
PROVIDE (__ram_shell_end = ABSOLUTE(.));
} > ddr_sdram
3 动态函数表创立
由东西主动依据体系中的大局函数创立的指令被称为主动指令。主动指令运用户能够在指令行中调用恣意一般函数,即便开发人员没有声明这些函数为指令。主动指令也需求一个数组包含体系中一切大局函数的信息,这个表为动态函数表。运用一个脚本依据体系的可履行文件主动生成动态函数表。要先编译软件,生成可履行文件;再调用脚本创立动态函数表;最终再编译出包含动态函数表的可履行文件。
在脚本中先运用GNU的nm东西从可履行文件导出一切大局符号,再运用grep提取出包含函数名的行,接着运用cut删去函数名前的地址信息,运用sed将字符T替换为创立指令的宏SHELL_CMD_DECL_AUTO,并在行尾增加括号。最终将一切这些信息写入一个C源代码文件,交给编译器编译,就能得到一个动态函数表。
4 主动指令参数个数
为了便于开发人员运用,主动指令最好能习惯一切函数类型,假定都是unsigned int的函数类型。关于能够成功转化为unsigned int类型整数的参数,将实在的值传递给函数;关于其他参数,则将参数作为一个字符串传递给函数。
5 字符输入
指令行从规范输入设备中获取字符。指令行只承受可打印的字符和特定的操控字符。假如收到了回车或许换行字符,代表用户完结了指令输入,就解析指令。解析指令前,去掉剩余的空格符(0x20),即不允许有接连的两个或多个空格符存在,指令的最终也不能有空格符。
6 参数解析
指令行模块要从用户输入的字符串中解分出指令名和参数。为指令供给与DOS和Linux相似的两个参数,argc和argv。argc表明参数的个数;argv是字符串指针的数组,最多10个字符串。argv[0]是第一个参数,argv[1]是第二个参数,如此类推。
7 函数解析
在代码中运用SHELL_CMD_DECL创立了指令列表,运用东西创立了主动指令列表。这两个表实际上都是数据结构ncommand_t的数组,其中有指令名和函数地址的信息。解析函数实际上便是依据在这两个数组中顺次比较函数名。假如用户输入的指令名和数组中的指令名共同,就运用对应的函数地址。
8 指令履行
得到函数地址后,就能够履行指令对应的函数。关于主动创立的指令,运用argc和argv作为参数。关于主动指令,指令行模块先测验将原始参数转化无符号整数。假如成功,运用无符号整数作为参数;假如不成功,则将原始参数的地址作为参数,实际上是一个字符串。
指令规划
完结总体规划后,就能够逐一增加开发需求的指令了。
● 协助指令用于显现一切指令名及其用法。
● IO指令用来拜访输入输出设备的寄存器,能够完结对外部设备的操控。
● 存储器指令用来显现、修正存储器的值。最基本的包含:dump指令显现指定方位的内存的值;modify指令用来修正指定内存单元的值。
● 用户在运用进程中,会运用许多指令。能够查询前史指令、循环履行前史指令将给用户带来许多便利。因而笔者完结了三条指令。history_show用来显现一切现已运用了的指令;history_del用来删去指定的前史指令;history_exec用来履行指定的前史指令。
优化处理
Altera供给了简化版的输出函数alt_printf来代替printf,降低了嵌入式体系的开支,笔者在指令行中也支撑这个这个函数。指令行的代码中,只调用SHELL_PRINT;在头文件中,依据用户设置,SHELL_PRINT运用规范的printf或许简化的alt_printf。假如运用简化的alt_printf,这个指令行占用的内存小于14KB。
在SOPC体系中的运用
Altera为SOPC体系开发供给的集成开发环境NIOS II IDE附带了多个软件实例。在运用这些软件实例时,用户只需求挑选模块,彻底不必新编代码,十分简略。
智能指令行模块也能够作为NIOS II IDE的一个实例。先在“nios2edsexamplessoftware”目录下为指令行创立一个子目录shell_standard,再以其他软件实例的template.xml为模板创为指令行建一个template.xml文件,然后仿制指令行模块的一切文件到这个目录中。这样,用户在创立工程时,直接选中智能指令行模板,就能够运用智能指令行模块了。