前些天在Altera的参考设计中发现了一个好东西:Avalon_Microsequencer。这是一个SOPC Builder模块,实现了Avalon-MM总线上主设备的功能,可以通过编辑该模块的程序ROM实现对Avalon总线的读写操作,还支持简单的逻辑指令和跳转指令。这是一个可以挂接在Avalon总线上的简单的微控制器——设计者谦虚地把这个模块称为“微序列器”。该模块主要用来实现复杂的状态转移功能——用CPU太大,用FSM又太复杂。
这些天利用业余时间一直在调试这个“小东西”。说它小,是因为它的代码只有200行,占用的LE只有130个。就算实现高级指令,占用的LE也不过267 个。经过两个晚上,终于读懂了全部代码,熟悉了它的配置方式,利用它构建了自己的SOPC系统,下载到了开发板上,通过修改源代码实现了在系统更新程序,通过按键控制了LED。在这一过程中,我还额外地收获了以下几点知识:
1. SOPC Builder的7.1版采用了新的系统配置文件格式(.sopc),但是兼容以前的文件格式(.ptf)。采用新的文件格式有利于版本控制。根据旧的文件格式设计的系统可以升级到新的文件格式,根据旧的文件格式开发的模块也可以用于新的系统设计。
在我用 7.1版的SOPC Builder打开参考设计时,由于版本不兼容给出了警告信息。通过点击察看警告信息的详细内容,我才看到了开发者给出的版本升级过程的解释。这一信息在Help文档和Altera的网站上都还找不到。
2. 新模块可以通过添加和修改SOPC_BUILDER_PATH系统环境变量,添加到SOPC Builder的模块搜索路径中。一些提供安装程序的SOPC Builder模块就是这样把自己添加到模块库中的。
需要说明的是:初始安装的SOPC Builder是没有设置这一环境变量的,搜索路径是默认的。如果安装了新模块或IP Megafunction,就会自动添加这一环境变量。
发现过程如下:由于我没带U盘,而安装程序又太大(34MB),我只好通过邮箱把部分安装好的文件拷回家。由于没有手动执行修改系统环境变量的操作,SOPC Builder始终找不到新的模块。幸好我在SOPC Builder的启动画面上发现了一个链接,点击后得到了一条加快SOPC Builder启动速度的信息。这条信息说的是,可以通过删除SOPC_BUILDER_PATH系统环境变量中的搜索路径,减少搜索时间,从而加快启动速度。我把这条信息反过来用,添加了新模块的搜索路径。
3. Quartus II的综合工具支持代码中的综合指令,通过这些综合指令,即使是注释掉的代码也可以被当作正式代码(//synthesis read_comments_as_HDL on和//synthesis read_comments_as_HDL off),而正常的代码也可以被综合器忽略(//synthesis translate_off和//synthesis translate_on)。
这一发现也是出于偶然。由于这一模块例化的altsyncram没有设置“在系统编辑功能”,在上板调试过程中不方便更新二进制指令代码。我尝试着把 “lpm_hint = "ENABLE_RUNTIME_MOD=YES, INSTANCE_NAME=inst"”添加到源代码中以支持这一功能。第一次修改并编译后,运行In-System Memory Content Editor工具,很意外地没有找到可编辑的ROM。我仔细检查了编译过程的信息,并检查了编译报告中关于altsyncram的综合结果,没有找到应有的信息。在Verilog代码中直接调用Megafunction功能是我很熟悉的操作,没有道理不成功。无奈之下,我只得再回到源代码中查找原因,结果在一段注释信息里发现了蹊跷。代码如下:
//synthesis translate_off
//////////////// SIMULATION-ONLY CONTENTS
altsyncram the_altsyncram
(
.aclr0 (reset),
.address_a (next_rom_addr),
.clock0 (clk),
.clocken0 (clk_next_addr),
.q_a (rom_data)
);
defparam the_altsyncram.address_aclr_a = "CLEAR0",
the_altsyncram.init_file =
`ifdef NO_PLI
"E:/try/quartus/sopc/avalon_seq_master/us_pio/microsequencer_classic.dat"
`else
"microsequencer_classic.mif"
`endif
,
the_altsyncram.intended_device_family = "CYCLONE",
the_altsyncram.lpm_hint = "ENABLE_RUNTIME_MOD=YES, INSTANCE_NAME=inst",
the_altsyncram.lpm_type = "altsyncram",
the_altsyncram.numwords_a = 256,
the_altsyncram.operation_mode = "ROM",
the_altsyncram.outdata_aclr_a = "CLEAR0",
the_altsyncram.outdata_reg_a = "CLOCK0",
the_altsyncram.width_a = 36,
the_altsyncram.width_byteena_a = 1,
the_altsyncram.widthad_a = 8;
//////////////// END SIMULATION-ONLY CONTENTS
//synthesis translate_on
//synthesis read_comments_as_HDL on
// altsyncram microsequencer_classic_state_rom
// (
// .aclr0 (reset),
// .address_a (next_rom_addr),
// .clock0 (clk),
// .clocken0 (clk_next_addr),
// .q_a (rom_data)
// );
//
// defparam microsequencer_classic_state_rom.address_aclr_a = "CLEAR0",
// microsequencer_classic_state_rom.init_file = "microsequencer_classic.mif",
// microsequencer_classic_state_rom.intended_device_family = "CYCLONE",
// microsequencer_classic_state_rom.lpm_hint = "ENABLE_RUNTIME_MOD=YES, INSTANCE_NAME=inst",
// microsequencer_classic_state_rom.lpm_type = "altsyncram",
// microsequencer_classic_state_rom.numwords_a = 256,
// microsequencer_classic_state_rom.operation_mode = "ROM",
// microsequencer_classic_state_rom.outdata_aclr_a = "CLEAR0",
// microsequencer_classic_state_rom.outdata_reg_a = "CLOCK0",
// microsequencer_classic_state_rom.width_a = 36,
// microsequencer_classic_state_rom.width_byteena_a = 1,
// microsequencer_classic_state_rom.widthad_a = 8;
//
//synthesis read_comments_as_HDL off
其中红色部分是我添加进去的。以前,对于“//synthesis translate_off”这一类的信息我是从不关心的——注释掉的内容不是有效代码。但是在排除了一切可能因素之后,我把注意力转向了注释掉的代码部分。我原本以为这部分代码是程序设计者不小心遗留下来的,但是仔细一读才发现里面有名堂:注释掉的代码才是综合工具能够看到的代码,而我先前修改的是仿真工具能够看到的代码,所以综合后没有得到应有的结果。找到了原因,原本奇怪的问题也就迎刃而解了。
解决了这一问题,更加强了我的一个调试信念:不合理的现象背后,一定有合理的原因。只有抱定这一信念,才能解决调试中遇到的各种问题。(与此相反的是:遇到不合理的现象,不是去寻求合理的解释,而是猜想有一些不可控的,不可证明的原因。比如猜想综合工具有bug,电路的信号完整性有问题,芯片过热等。这样一想,问题往往不了了之,调试也就半途而废了。)
如果没有解决这一问题,今后在Verilog代码中直接调用Megafunction功能时,我心里是难免会发虚的。由此我想起了“库哥”说过的一句话: “遇到拦路虎一定要把它解决掉,否则日后碰到它你会更发怵”。 |