作者:猫叔 ,来源:科学计算Tech微信公众号
FPGA工程师们应该都会吐槽Verilog的语法,相当的不友好,尤其是对于有很多接口的模块,像AXI4/AXI-Lite这种常用的总线接口,动不动就好几十根线,写起来是相当费劲。
当然现在Xilinx推荐使用纯bd文件的方式来设计FPGA,这样HDL代码就会少了很多。但我们大多数的工程还是无法避免使用HDL来连接两个module。所以本文就推荐使用SystemVerilog来简化FPGA中接口的连接方式。
也许很多FPGA工程师对SystemVerilog并不是很了解,因为以前的FPGA开发工具是不支持SystemVerilog的,导致大家都是用VHDL或者Verilog来开发,但现在Vivado对SystemVerilog的支持已经比较好了,完全可以使用SystemVerilog写出可综合的FPGA程序,而且FPGA开发中只会使用的SystemVerilog语法的一小部分,入门也很快,因此建议FPGA工程师学一下SystemVerilog。
本文中用到的logic关键字的解释可以参考SystemVerilog教程之数据类型1
此次例程也比较简单,有两个模块module1和module2,module1中输出a和b,在module2中完成加法后再返还给module1,最终输出的led=c | a。
首先用Verilog来实现,代码也比较简单,就简单解释一句:文件格式都是.sv,这是因为SystemVerilog的语法都是包含Verilog的。
//top.sv module top( input clk, input rst, output [3:0] led ); logic [3:0] a ; logic [3:0] b ; logic [3:0] c ; module1 inst_module1( .clk (clk ), .rst (rst ), .a (a), .b (b), .c (c), .led (led) ); module2 inst_module2( .clk (clk ), .rst (rst ), .a (a), .b (b), .c (c) ); endmodule // module1.sv module module1( input clk, input rst, output logic [3:0] a, output logic [3:0] b, input logic [3:0] c, output logic [3:0] led ); assign led = c | a; always @ ( posedge clk ) begin if(rst) begin a <= 4'd1; b <= 4'd2; end else begin a <= a + 1'b1; b <= b + 1'b1; end end endmodule // module2.sv module module2( input clk, input rst, input logic [3:0] a, input logic [3:0] b, output logic [3:0] c ); always @ ( posedge clk ) begin if(rst) c <= 4'd1; else c <= a + b; end endmodule
综合之后的Schematic如下图所示:(为了更好的表示电路结构,我将flatten_hierarchy选为了none)
下面我们把程序稍作改动,将a/b/c三个接口使用SystemVerilog中的interface来连接。
在工程中添加my_itf.sv文件如下:
// my_itf.sv interface my_itf; logic [3:0] a, b, c; modport mod1 (input c, output a, b); modport mod2 (input a, b, output c ); endinterface : my_itf
关键字interface就表示要创建一个接口模块,里面包含了3个接口:a/b/c。
modport定义了这三个接口的方向,对于module1来说,a和b是输出,c是输入;对于module2来说,a和b是输入,c是输出。
注:也可以不使用modport,Vivado会根据代码自动推断出接口的方向,但不建议这么做
修改module1.sv如下,其中a/b/c端口换成了my_itf.mod1 itf_abc,my_itf.mod1就表示my_itf接口的方向按照mod1中指定的,而且代码中的a、b、c要相应的换成itf_abc.a、itf_abc.b、itf_abc.c.
// module1.sv module module1( input clk, input rst, my_itf.mod1 itf_abc, output logic [3:0] led ); assign led = itf_abc.c | itf_abc.a; always @ ( posedge clk ) begin if(rst) begin itf_abc.a <= 4'd1; itf_abc.b <= 4'd2; end else begin itf_abc.a <= itf_abc.a + 1'b1; itf_abc.b <= itf_abc.b + 1'b1; end end endmodule
修改module2.sv代码如下,原则跟上面是一样的,不再赘述。
// module2.sv module module2( input clk, input rst, my_itf.mod2 itf_abc ); always @ ( posedge clk ) begin if(rst) itf_abc.c <= 4'd1; else itf_abc.c <= itf_abc.a + itf_abc.b; end endmodule
修改top.sv如下,例化my_itf接口,将itf_abc.mod1传给module1,将itf_abc.mod2传给module2.
// top.sv module top( input clk, input rst, output [3:0] led ); logic [3:0] a ; logic [3:0] b ; logic [3:0] c ; my_itf itf_abc(); module1 inst_module1( .clk (clk ), .rst (rst ), .itf_abc (itf_abc.mod1), .led (led) ); module2 inst_module2( .clk (clk ), .rst (rst ), .itf_abc (itf_abc.mod2) ); endmodule
大功告成!!!
综合后Schematic如下,跟上面的图只是名字不同,电路是一样的。