5.2 代码编写中容易出现的问题
在for-loop中包括不变的表达式 浪费运算时间 for (i=0;i<4;i=i+1) begin sig1 = sig2; dataout[i] = datain[i]; end for-loop中第一条语句始终不变,浪费运算时间.
资源共享问题 条件算子中不存在 资源共享 ,如 z = (cond) ? (a + b) : (c + d); 必须使用两个加法器; 而等效的条件if-then-else语句则可以资源共享 如
if (cond) z = a + b; else z = c + d; 只要加法器的输入端复用,就可以实现加法器的共享,使用一个加法器实现。
由于组合逻辑的位置不同而引起过多的触发器综合 如下面两个例子 module count (andbits, clk, rst); output andbits; input clk, rst; reg andbits; //internal regreg [2:0] count; always @(posedge clk) begin begin if (rst) count <= #u_dly 0; else count <= #u_dly count + 1; end //end if andbits <= #u_dly & count; end //end always endmodule 在进程里的变量都综合成触发器了,有4个;
module count (andbits, clk, rst); output andbits; input clk, rst; reg andbits; //internal reg reg [2:0] count; always @(posedge clk) begin //synchronous if (rst) count <= #u_dly 0; else count <= #u_dly count + 1; end //end always always @(count) begin //asynchronous andbits = & count; end //end always endmodule //end count 组合逻辑单开,只有3个触发器.
谨慎使用异步逻辑 module count (z, enable, clk, rst); output [2:0] z; input rst,enable, clk; reg [2:0] z; always @(posedge clk) begin if (rst) begin z <= #u_dly 1'b0; end else if (enable == 1'b1) begin if (z == 3'd7) begin z <= #u_dly 1'b0; end else begin z <= #u_dly z + 1'b1; end end else ; end //end always endmodule //end count 是同步逻辑,而下例则使用了组合逻辑作时钟,以及异步复位.实际的运用中要加以避免.
module count (z, enable, clk, rst); output [2:0] z; input rst, enable, clk; reg [2:0] z; //internal wire wire gated_clk = clk & enable; always @(posedge gated_clk or posedge rst) begin if (rst) begin z <= #u_dly 1'b0; end else begin if (z == 3'd7) begin z <= #u_dly 1'b0;end else begin z <= #u_dly z + 1'b1; end end //end if end //end always endmodule //end module 对组合逻辑的描述有多种方式 其综合结果是等效的 c = a &b; 等效于 c[3:0] = a[3:0] & b[3:0]; 等效于 c[3] = a[3] & b[3]; c[2] = a[2] & b[2]; c[1] = a[1] & b[1]; c[0] = a[0] & b[0]; 等效于 for ( i=0; i<=3; i = i + 1) c[i] = a[i] & b[i]; 可以选择简洁的写法. 考虑综合的执行时间 通常会推荐将模块划分得越小越好, 事实上要从实际的设计目标, 面积和时序要求出发。好的时序规划和合适的约束条件要比电路的大小对综合时间的影响要大。要依照设计的目标来划分模块, 对该模块综合约束的scripts也可以集中在该特性上。要选择合适的约束条件, 过分的约束将导致漫长的综合时间。最好在设计阶段就做好时序规划 。通过综合的约束scripts来满足时序规划。这样就能获得既满足性能的结果 ,又使得综合时间最省 。从代码设计讲 ,500~5000行的长度是合适的。 避免点到点的例外 所谓点到点例外 point-to-point exception ,就是从一个寄存器的输出到另一个寄存器的输入的路径不能在一个周期内完成。多周期路径就是其典型情况 。多周期路径比较麻烦, 在静态时序分析中要标注为例外, 这样可能会因为人为因素将其他路径错误地标注为例外, 从而对该路径没有分析, 造成隐患。避免使用多周期路径, 如果确实要用 ,应将它放在单独一个模块, 并且在代码中加以注释。 避免伪路径(false path) 伪路径是那些静态时序分析 sta 认为是时序失败, 而设计者认为是正确的路径。通常会人为忽略这些warning ,但如果数量较多时 ,就可能将其他真正的问题错过了。 避免使用latch 使用latch必须有所记录, 可以用all_registers -level_sensitive来报告设计中用到的latch 。不希望使用latch时 ,应该对所有输入情况都对输出赋值, 或者将条件赋值语 句写全, 如在if语句最后加一个else, case语句加defaults。 当你必须使用latch时 ,为了提高可测性, 需要加入测试逻辑。 不完整的if和case语句导致不必要的latch的产生, 下面的语句中 dataout会被综合成锁存器 。如果不希望在电路中使用锁存器, 它就是错误。always @(cond)begin if (cond) dataout <= datain end 避免使用门控时钟 使用门控时钟(gated clock)不利于移植 ,可能引起毛刺, 带来时序问题 ,同时对扫描链的形成带来问题。门控钟在低功耗设计中要用到 ,但通常不要在模块级代码中使用 。可以借助于power compiler来生成 ,或者在顶层产生。
避免使用内部产生的时钟 在设计中最好使用同步设计。如果要使用内部时钟 ,可以考虑使用多个时钟。因为使用内部时钟的电路要加到扫描链中比较麻烦,降低了可测性, 也不利于使用约束条件来综合。
避免使用内部复位信号。 模块中所有的寄存器最好同时复位。如果要使用内部复位, 最好将其相关逻辑放在单独的模块中, 这样可以提高可阅读性。
如果确实要使用内部时钟, 门控时钟 ,或内部的复位信号 ,将它们放在顶层。 将这些信号的产生放在顶层的一个独立模块, 这样所有的子模块分别使用单一的时钟和复位信号。一般情况下内部门控时钟可以用同步置数替代。
6 附录 6.1 module 编写示例 /* * filename ﹕ author ﹕ description ﹕ called by ﹕ revision history ﹕mm/dd/yy revision 1.0 email ﹕ m@sz.huawei.com.cn company ﹕ huawei technology .inc copyright(c) 1999, huawei technology inc, all right reserved* */ module module_name( output_ports, //comment ; port description input_ports, //comment ; port description io_ports, //comment ; port descripttion clk_port, //comment ; port description rst_port //comment ; port description ); //port declarations output [31:;0] dataout; input [31:0] datain; inout bi_dir_signal; input input1, input2; //interrnal wire/reg declarationswire [31:0] internal_data; reg output_enable; //module instantiations , self-build module module_name1 uinstance_name1(...); module_name2 uinstance_name2(...); // tsc4000 cell dtc12 v1 (.clk(clk), .clrz(clr), .d(data), .q(qout)); //continuous assignment assign data_out = out_enable ? internal_data : 32’hz; //always block always @(input2) begin ... end //function and task definitions functiom [function_type] function_name; declarations_of_inputs; [declarations_of_local_variables]; begin behavirol_statement; function_name = function_express; end endfunction //end function_name endmodule //end module_name 6.2 testbench编写示例 下面是一个格雷码的测试模块,module tb_gray; reg clock; reg reset; wire [7:0] qout; integer fout; //输出文件指针 parameter cyc = 20; gray dut(.clock(clock),.reset(reset),.qout(qout));initialbegin clock = 1'b0; reset =1'b1; #(5*cyc) reset = 1'b0; #(5*cyc) reset = 1'b1; #(5000*cyc) $fclose(fout); $finish;end initial begin $shm_open(gray.shm); $shm_probe(as); fout=$fopen(gray.dat); end always #cyc clock = ~ clock;//输出数据到文件gray.datalways @(posedge clock)begin $fwrite(fout,%d %b,qout,qout);endendmodule 在testbench中避免使用绝对的时间,如#20,#15或#(cyc+15)等,应该在文件前面使用parameter定义一些常量,使得时间的定义象#(cyc+off0)的形式,便于修改。
观测结果可以输出到波形文件gray.shm ,或数据文件gray.dat 。生成波形文件可以用simwave观测结果 ,比较直观。而生成数据文件则既可以快速定位 ,也可以通过编写的小程序工具对它进行进一步的处理。
对大的设计的顶层仿真 ,一般不要对所有信号跟踪, 波形文件会很大, 仿真时间延长,可以有选择的观测一些信号。
传特斯拉明年11月生产ModelY_与Model3同一平台
Molex推出最新互连技术MediSpec产品系列
智能电子水表的原理及设计
泰来三维|防灾救灾应急预案_三维扫描在地质灾害监测中的应用
中瓷电子存货处于健康水位 盈利却低于同行
代码编写中verilog的设计规范
基于FPGA的IJF数字基带编码的实现
工业 4.0:矩阵式生产带来的灵活性
基于发动机测试的便携式振动分析仪
深跨协走访3D打印品牌纵维立方,探讨国货出海趋势
三星家电2022年新品发布会成功举办
华为率先搭载麒麟980的Mate 20
盘点机器人不能代替人类所做的10件事
互问W03芯片是端云一体的语音处理芯片
印度政府正鼓励本地公司推广BSNL 4G网络
医疗设备中的嵌入式数据库扩展了功能并提高了安全性
TSMC授予Cadence两项“年度合作伙伴”奖项
华米科技CEO确认,8月27日发布会将发Amazfit智能运动手表3
贴片电阻电容电感磁珠封装尺寸及其公制英制比对
HTC U11+全面评测解析