国产安路FPGA(三)-TD软件仿真(Modelsim)实战:有符号乘法运算
1. 有符号乘法运算的设计思路有符号乘法在数字信号处理中非常常见比如音频处理、图像处理等领域都会用到。国产安路FPGA的PH1系列芯片提供了专门的乘法器原语PH1_LOGIC_MULT可以高效实现这一功能。我们先来看一个典型的应用场景假设我们要处理一个音频信号需要对两个8位有符号数进行乘法运算。在Verilog中虽然可以直接用*运算符实现乘法但使用原语可以获得更好的时序性能和资源利用率。这里有个小技巧在FPGA设计中我们通常会使用寄存器来暂存输入信号。这样做有两个好处一是可以确保乘法器的输入信号稳定二是便于时序约束。我在实际项目中就遇到过因为没加寄存器而导致时序违例的情况调试起来特别麻烦。2. 工程代码详解2.1 模块接口设计我们先来看模块的接口定义module Signed_test( input clk, input rst_n, input signed[7:0] a, input signed[7:0] b, output reg signed[15:0] out );这里定义了时钟clk、复位rst_n以及两个8位有符号输入a、b输出是16位有符号数out。注意输出用了reg类型因为我们要在时钟边沿更新输出值。2.2 乘法器原语调用安路FPGA的乘法器原语PH1_LOGIC_MULT配置非常灵活PH1_LOGIC_MULT#( .INPUT_WIDTH_A(8), .INPUT_WIDTH_B(8), .OUTPUT_WIDTH(16), .INPUTFORMAT(UNSIGNED), //...其他参数省略 ) red_dap_mult( .p(mult_out), .a(a_reg), .b(b_reg), //...其他信号连接省略 );这里有几个关键参数需要注意INPUT_WIDTH_A/B设置输入位宽OUTPUT_WIDTH设置输出位宽INPUTFORMAT虽然我们做的是有符号乘法但这里要设为UNSIGNED这是安路FPGA的一个特殊设定2.3 寄存器设计为了保证时序我们添加了输入寄存器reg signed [7:0] a_reg; reg signed [7:0] b_reg; always (posedge clk) begin a_reg a; b_reg b; out mult_out; end这个设计模式在FPGA中很常见我称之为输入寄存组合逻辑输出寄存的三段式设计。实测下来时序特别稳建议新手都采用这种设计模式。3. 仿真代码编写3.1 测试平台搭建仿真代码主要完成三个任务生成时钟和复位信号提供测试激励实例化被测模块timescale 1ns/1ns module Sim_mult; reg clk; reg rst_n; reg signed[7:0] a; reg signed[7:0] b; wire signed[15:0] out; always #10 clk ~clk; //50MHz时钟 initial begin clk 0; rst_n 0; a 8d0; b 8d0; #20 rst_n 1; a -13; //1000_1101 b -5; //1000_0101 #40 a 10; b -10; end endmodule3.2 测试用例设计我设计了两个测试用例两个负数相乘-13 × -5 65正负相乘10 × -10 -100这是最基本的边界测试实际项目中还应该考虑更多边界情况比如最大值相乘、最小值相乘等。4. ModelSim仿真环境配置4.1 准备工作在开始仿真前需要准备好以下内容ModelSim SE-64 10.5或更高版本安路TD软件安装包中的仿真模型文件设计好的Verilog代码4.2 详细配置步骤创建仿真库目录在ModelSim安装目录下新建Anlogic文件夹再在其下创建Src文件夹。将TD软件安装路径中的sim_release文件夹下的所有器件仿真模型复制到Src文件夹中。编译器件库打开ModelSim点击File → New → Library命名为ph1_lib点击Compile → Compile选择PH1系列的所有仿真模型文件创建仿真工程点击File → New → Project命名为signed_mult添加设计文件Signed_test.v和仿真文件Sim_mult.v点击Compile All编译所有文件这里有个容易出错的地方添加文件时一定要确保文件路径没有中文或特殊字符否则可能会导致编译失败。我之前就踩过这个坑调试了好久才发现是路径问题。5. 运行仿真与分析结果5.1 启动仿真点击Simulate → Start Simulation在work库中找到Sim_mult模块重要取消勾选Optimization选项否则部分信号可能被优化掉5.2 添加观察信号在Objects窗口中找到需要观察的信号右键点击Add to → Wave设置合适的仿真时间比如500ns点击Run按钮开始仿真5.3 结果分析我们主要观察三个时间点的输出复位期间输出应该为0第一个乘法-13 × -5 65 (0x0041)第二个乘法10 × -10 -100 (0xFF9C)如果结果不符合预期可以按照以下步骤排查检查输入寄存器是否正确采样确认乘法器原语参数配置是否正确验证输出寄存器是否在正确时钟边沿更新我在第一次调试时就发现输出全为0后来发现是忘了连接复位信号。这种低级错误新手特别容易犯建议大家在检查时先从最简单的连接关系开始排查。