如何为FPGA编写可综合的代码?

一、verilog 编码风格
(本文的语法高亮因为浏览器的缘故,所以不准确)
1.1 使用“`include编译器指令”
文件包含“`include编译器指令”用于在合成过程中将源文件的全部内容插入到另一个文件中。它通常用于包括全局项目定义,而无需在多个文件中重复相同的代码。另一个用例是将代码的一部分插入模块,如以下示例所示:
// file test_bench_top.v // top-level simulation testbench module test_bench_top; `include “test_case.v” endmodule // file test_case.v initial begin //… end task my_task; //… endtask
> include编译器指令的语法定义为:`include  
可以是文件名,还可以包含绝对或相对路径名:
`include “test_case.v” `include “../../includes/test_case.v” `include “/home/myprojects/test/includes/test_case.v”
建议仅在include中使用文件名,而不要使用绝对或相对路径名。这将使代码位置独立,因此更加可移植。另一个建议是保持包含文件简单而不使用嵌套的include指令。
1.2 使用`define编译器指令,parameter和localparam
`define是文本宏替换编译器指令。它定义为:`define
可以包含带有可选参数列表的单行或多行文本。
`define具有全局范围。一旦定义了文本宏名称,就可以在项目中的任何地方使用它。文本宏通常是用于定义状态名称,常量或字符串的简单标识符。
parameter关键字定义模块特定的参数,该参数在特定模块实例的范围生效。参数用于为模块实例提供不同的自定义,例如,输入或输出端口的宽度。以下是使用parameter关键字的示例:
module adder #(parameter width = 8) ( input [width-1:0] a,b, output [width-1:0] sum ); assign sum = a + b; endmodule // adder // an instance of adder module adder # (16) adder1 (.a(a[15:0]),.b(b[15:0]),.sum(sum[15:0]));
localparam关键字与parameter相似。它被分配了一个常量表达式,并在特定模块内具有作用域。它定义为:
1.3 使用函数
以下是执行xor操作的verilog函数的简单示例:
module function_example( input a,b, output func_out); function func_xor; input a, b;   begin     func_xor = a ^ b;   end endfunction    assign func_out = func_xor(a,b); endmodule // function_example
建议使用verilog函数来实现组合逻辑和其他不需要非阻塞分配的操作,例如同步逻辑。使用函数可以编写更紧凑和模块化的代码。所有综合工具均支持verilog函数。
1.4 使用 generate 块
在verilog-2001中引入了generate块,以使对同一模块,函数,变量,网络和连续分配的多个实例的实例化变得容易。以下是使用generate的两个示例:
// a conditional instantiation of modules parameter cond1 = 1; generate if (cond1) begin : my_module1_inst my_module1 inst (.clk(clk), .di(di), .do(do)); end else begin : my_module2_inst my_module2 inst (.clk(clk), .di(di), .do(do)); end endgenerate // using for loop in generate block genvar ii; generate     for (ii = 0; ii < 32; ii = ii+1) begin: for_loop     my_module1 inst (.clk(clk), .di(di[ii]), .do(do[ii]));     end end endgenerate
1.5 开发简单的代码
始终努力开发简单的代码。与每种编程语言一样,verilog允许编写详细的语句,从功能的角度来看,这些语句很优美,但可读性不高。下面的简单示例说明了这一点:
reg [5:0] sel; reg [3:0] result1,result2,a,b; always @(*) begin result1 = sel[0] ? a + b : sel[1] ? a - b : sel[2] ? a & b : sel[3] ? a ^ b : sel[4] ? ~a : ~ b; if(~|sel) result1 = 4'b0; end // always
reg [5:0] sel; reg [3:0] result1,result2,a,b; always @(*) begin  casex(sel)     6'bxxxxx1: result2 = a + b;     6'bxxxx10: result2 = a - b;     6'bxxx100: result2 = a & b;     6'bxx1000: result2 = a ^+ b;     6'bx10000: result2 = ~a;     6'b100000: result2 = ~b;     default: result2 = 4'b0;  endcase end // always
实现result1和result2的逻辑在功能上是等效的。但是,在result1中使用嵌套三元运算符和两个赋值语句不太透明,并且与result2逻辑的更清晰的case语句相比,需要花更多的精力来理解。
通常,代码清晰度高容易实现高效率。同一段代码能在其生命周期内被多个开发人员读取。编写更清晰的代码更容易调试,并且一般不容易包含错误。
二、为fpga编写可综合的代码
2.1 考虑资源
verilog语言参考手册(lrm)提供了丰富的功能来描述硬件。但是,只有一部分语言可以为fpga综和。即使有些特定的语言结构是可综合的,也不能保证该代码能在特定fpga上实现物理电路。考虑以下示例:
reg [7:0] memory[1:2**22]; initial begin memory[1] = 8’h1; memory[2] = 8’h2; end
该示例能正确模拟出来,但会导致fpga物理实现失败。该代码需要4 mb的内存,这是一些fpga所没有的。此外,综合工具将忽略初始块,该块将初始化内存的最低两个字节。
该技巧提供了一些指导方针和建议,以帮助编写用于fpga的可综合代码。
2.2 遵循同步设计原则
建议开发人员遵守fpga同步设计的原则,其中包括以下内容:
1、使用同步复位。后续会详细讨论,同步,异步复位的问题
2、避免使用锁存 
3、避免使用门控,派生或分频时钟
4、使用时钟使能而不是多个时钟
5、对所有异步信号实行正确同步
原文标题:学习fpga的小tips(一)
文章出处:【微信公众号:fpga之家】欢迎添加关注!文章转载请注明出处。


传感器行业还有哪一些矛盾需要去解决
诺基亚8真机曝光:6月发布,外形太丑配置劲爆,大涨价!
芯科科技发布新版蓝牙开发流程
特斯拉拿下第二个新能源项目,推动澳大利亚新能源电池发展
一文详解数值比较器
如何为FPGA编写可综合的代码?
中软国际@HDC.Cloud 2023 精彩盘点|全面拥抱华为云盘古大模型,携手开发者共建创新未来
工控机开机慢如何解决
中国信通院正式启动了SD-WAN系列标准的制定工作
OPPO首发索尼旗舰传感器
机构:Q3中国智能手机市场荣耀第一,华为飙升50%
如何治理氨水罐渗漏问题
防雷电源电涌保护器选用标准
得邦照明LED灯具产品、车用照明产品同比实现较大幅度增长
智能音箱出货量呈爆发式增长,线上线下普及上升空间较大
一种新的神经机器翻译架构,它脱离了编码器-解码器的范畴
三极管驱动继电器控制电路分析_继电器控制电路图设计
地面在哪里
1N4148二极管电路图和引脚图 1N4148二极管的工作原理和应用
一段话教会你熟练掌握单片机