本节对制作完成的AZPR SoC 的仿真进行讲解。我们首先要为仿真编写仿真模型和Testbench,然后讲解使用Icarus Verilog 进行仿真的方法。 1.11.1 仿真模型的编写 对整个系统进行仿真时,需要准备FPGA 中使用的DCM 和内存模型。虽然可以使用FPGA 厂商发布的官方模型,但我们选择自行设计仿真模型。 DCM 模型 我们配置的DCM 将依据输入的时钟,输出频率相同但相位为0 度和180 度的两种时钟。DCM 模型的信号线一览如表1-73 所示、源程序如代码1-44 所示。 表1-73 信号线一览(x_s3e_dcm.v) 代码1-44 DCM 模型(x_s3e_dcm.v) [Ⅰ]时钟的输出 因为CLK0_OUT 与CLKIN_IN 频率相同,所以直接将CLKIN_IN 代入CLK0_ OUT 输出。因为CLK180_OUT 与CLKIN_IN 频率相同但相位差为180 度,所以将CLKIN_IN 信号反转后代入CLK180_OUT 输出。RST_IN 反转后代入LOCKED_ OUT。复位信号接触的同时使能LOCKED_OUT。本来复位信号接触到锁频信号使能之间需要一段时间,但作为仿真模型我们进行了简化。 Single Port ROM模型 Single Port ROM 是只有一个读取端口的专用存储器。表1-74 列出了Single PortROM 模型的信号线一览,源程序如代码1-45 所示。 表1-74 信号线一览(x_s3e_sprom.v) 代码1-45 Single Port ROM 模型(x_s3e_sprom.v) [Ⅰ]读取访问 此处将地址(addra)指定的存储单元的数据输出到输出数据(douta)端口。 Dual Port RAM模型 Dual Port RAM 是具有两个可以同时读写端口的存储器。表1-75 列出了Dual Port RAM 模型的信号线一览、源程序如代码1-46 所示。 表1-75 信号线一览(x_s3e_dpram.v) 代码1-46 Dual Port RAM 模型(x_s3e_dpram.v) [Ⅰ]端口A 的读取访问 此处将地址(addra)指定的存储单元的数据输出到输出数据(douta)端口。当端口B 同时对同一地址进行写入操作时,将写入的值直通到输出。当端口B 写入有效信号(web)为有效、两个端口地址(addra 和addrb)相同时,读取的数据(douta)端口直接输出端口的B 写入的数据(dinb)。 [Ⅱ]端口A 的写入访问 此处将写入的数据(dina)存入地址(addra)指定的存储单元。 [Ⅲ]端口B 的读取访问 此处将地址(addrb)指定的存储单元的数据输出到输出数据(doutb)端口。当端口A 同时对同一地址进行写入操作时,将写入的值直通到输出。当端口A 写入有效信号(wea)为有效、两个端口地址(addrb 和addra)相同时,读取的数据(doutb)端口直接输出端口的A 写入的数据(dina)。 [Ⅳ]端口B 的写入访问 此处将写入的数据(dinb)存入地址(addrb)指定的存储单元。 1.11.2 Testbench 的编写 本节编写用以测试AZPR SoC 的Testbench。Testbench 使用的宏一览如表1-76 所示, 信号线一览如表1-77 所示。 表1-76 宏一览(chip_top_test.v) 表1-77 信号线一览(chip_top_test.v) Testbench 的基本功能是向被测电路输入时钟和复位信号,推进仿真周期。除此之外,我们还实现了几个便于仿真的功能。代码1-47 为监测GPIO 的源程序。当GPIO 端口值变化时,监测程序向画面打印输出仿真时刻与变化后端口值。 代码1-47 GPIO 监测(chip_top_test.v) [Ⅰ]输入端口的监测 当输入端口值变化时,此处打印输出仿真时刻与输入端口值。 [Ⅱ]输出端口的监测 当输出端口值变化时,此处打印输出仿真时刻与输出端口值。 [Ⅲ]输入输出端口的监测 当输入输出端口值变化时,此处打印输出仿真时刻与输入输出端口值。 代码1-48 为UART 模型的源程序。UART 模型用于将UART 输出的数据打印输出到画面上。 代码1-48 UART 模型(chip_top_test.v) [Ⅰ]接收信号的连写 此处一直向UART 接收信号(uart_rx)发送停止位H。被注释掉的部分代码是将发送信号(uart_tx)与接收信号(uart_rx)相连,实现循环数据回送。 [Ⅱ]UART 模型的实例化 此处将UART 接收模块实例化,作为UART 模型使用。由于UART 模型的时钟与复位信号需要与AZPR SoC 内部的相同,在这里直接使用实例化后的时钟(chip_top.clk)与复位(chip_top.chip_reset)信号。Verilog HDL 中使用英文句点“.”符号可以访问模块的下属模块。 [Ⅲ]发送信号的监测 当(1)处的if 语句判断到接收完成信号(rx_end)有效,则向显示器打印输出接收的数据(rx_data)。为了与程序输出的字符串显示效果一致,这里使用了不自动换行的$write 语句打印输出。 代码1-49 列出了测试用例和波形输出的程序部分。测试用例部分先对信号进行初始化,然后读取载入内存镜像。 代码1-49 测试用例与波形输出(chip_top_test.v) [Ⅰ]测试用例 (1)处对信号线进行初始化。输入时钟(clk_ref)设置为H、复位(reset_sw) 设置为有效。(2)处载入内存镜像文件。将宏ROM_PRG 指定的文件载入ROM、将宏SPM_PRG 指定的文件载入SPM。(3)处解除复位信号。(4)处执行仿真, 仿真循环SIM_CYCLE 次,然后结束。 [Ⅱ]波形输出 此处代码进行波形的输出。 1.11.3 执行仿真 我们使用Icarus Verilog 软件和做好的Testbench 对系统进行仿真。仿真时,先进入仿真目录(chip/top/test),然后执行图1-132 所示的命令。 图1-132 执行仿真 命令中定义了内存镜像宏与仿真循环数宏的值,指定了输出文件名与顶层模块名, 指定了引用文件的目录,指定了仿真模型的目录,还指定了源程序文件。 使用iverilog 命令生成编译文件后,如图1-133 所示,使用vvp 命令进行仿真。 C:Users…> vvp chip_top.out 图1-133 执行vvp 命令 接下来,我们就尝试执行一下仿真。作为将要执行的程序的镜像,将代码1-50 所示的内容写入名为test.dat 的文件中。 代码1-50 test.dat 0c008000 // ORI r0, r0, 0x8000 0c21ffff // ORI r1, r1, 0xFFFF 3c000010 // SHLLI r0, r0, 16 5c010004 // STW r0, r1, 0x0004 $readmemh 指令读取的文件中,“//”符号后的文字视为注释。这一段镜像的功能是向作为GPIO 输出端口的地址——0x8000_0004 写入0xFFFF。将test.dat 作为镜像载入ROM,执行仿真后,会得到如图1-134 所示的输出。由于所有程序都存储在ROM,不需要向SPM 载入镜像。此处我们将仿真循环数设为10 000 个循环。 VCD info: dumpfile chip_top.vcd opened for output. 0 gpio_in changed : 0000 1 gpio_out changed : 000000000000000000 1 gpio_io changed : zzzzzzzzzzzzzzzz 5101 gpio_out changed : 001111111111111111 图1-134 执行仿真 通过仿真结果我们可以看出,GPIO 的输出端口(gpio_out)的低16 位变为0xFFFF。仿真中使用的镜像,可以使用我们即将在第3 章介绍的汇编语言输出得到。 1.11.4 小结 本节对本章制作的AZPR SoC 的仿真环境进行了说明。仿真对硬件设计开发非常重要。在实际的硬件设备进行测试之前使用仿真程序进行动作测试,可以显著提高开发效率。