当前位置:首页 > 工业控制 > 电子设计自动化
[导读]随着集成电路制造技术的发展,对设计提出了更多的挑战,随着设计复杂度的增加,又提出了片上系统(SoC)的概念。为了加速设计收敛,设计重用、可测性设计、可验证性设计和可维护性设计得到了更多重视。本文以VerilogHD

随着集成电路制造技术的发展,对设计提出了更多的挑战,随着设计复杂度的增加,又提出了片上系统(SoC)的概念。为了加速设计收敛,设计重用、可测性设计、可验证性设计和可维护性设计得到了更多重视。本文以VerilogHDL为例,对可维护性设计进行了初步探讨。

1、设计重用与可维护性设计

设计重用是一个很大的概念,严格来讲,可验证性设计和可维护性设计都在设计重用之列。可维护性设计的目的本身就是便于设计重用,便于让后来人读懂前人所写的代码,但设计重用包括的内容更广泛。

设计重用讲的是设计总体风格而不是设计的细节,“it is about forests, rather than trees”。最重要的概念:本地化(locality)――一定要尽量使问题本地化,这样有利于问题的排除。对于大的设计尤为重要。

三条基本原则

尽量采用全同步设计,将输入和输出寄存,这样有利于将时序优化本地化

采用bottom-up验证方法,保证每个子模块的功能是正确的,然后再进行整体验证。

花时间设计一个好的设计规范,力图使这样的规范具有好的体系结构和模块划分,这样有利于本地化的有效实现。

一个好的设计应该包括:完备的文档、好风格的代码、详尽的注释、良好易用的验证环境和鲁棒的脚本。

影响设计重用的障碍本质上说是管理和企业文化;开发和管理内部IP资源是最大的重用挑战。

关于设计重用这里不再讨论,否则就有喧宾夺主之嫌了。

2、关于代码可扩展性的一点讨论

代码可扩展性实际上也包括在设计重用之内。代码可扩展性或称可扩展性设计要求便于向将来新的设计过渡。比如64位的PCI标准出来的时候,现行的32位PCI接口设计模块可以通过比较简单的改动而变为64位,而不需要一切从头再来。一般设计中通过定义参数和宏来便于修改。典型例子就是总线:

`define WORD 16
`define DWORD 32
reg [`WORD-1:0] intruction
reg [`DWORD-1:0] data_bus,addr_bus;
当然代码可扩展性不仅仅包括参数和宏的使用,如下例:
module tri_buf(in,out, ena);
parameter WIDTH=8;
input [WIDTH-1:0] in;
output[WIDTH-1:0] out;
input ena;
assign out= ena?in:’bz;
endmodule

这是一段有潜在问题的代码,Verilog将高位扩展为0来匹配输出,所以当WIDTH>32时,上述代码是有问题的,仿真的时候甚至都看不出来,因此不利于可扩展性。象这样的问题可以用Verilog lint等工具来检查。

3、可读性最重要

可读性最重要。许多设计者有个习惯:追求尽量用短的代码来完成同样的功能,国内许多考试中也要求用少于多少行的代码完成某某功能(很不好的倾向)。对于HDL,我不推荐这样的风格,因为综合工具会帮助你完成优化工作。我的建议是:同样的代码可长可短,不要为了减少行数而影响可读性。如下例:

Module rrarb(request,grant,reset,clk);
Input [1:0] request;
Output[1:0]grant;
Input reset;
Input clk;
Wire winner;
Reg last_winner;
Reg [1:0] grant;
Wire[1:0] next_grant;

Assign next_grant[0]=~reset&(request[0] & (~request[1]|last_winner));
Assugn next_grant[1]=~reset& (request[1] &(~request[0]| ~last_winner));
Assign winner=~reset & ~next_grant[0] &(last_winner | next_grant[1]);
Always @(posedge clk)
Begin
Last_winner=winner;
Grant=next_grant;
End
当request[1:0]=2’b00,时,last_winner会发生什么变化?上面的代码可读性较之下面的代码差很多。
Module rrarb(request,grant,reset,clk);
Input [1:0] request;
Output[1:0]grant;
Input reset;
Input clk;
Wire winner;
Reg last_winner;
Reg [1:0] grant;
Always @(posedge clk)
Begin
If(reset) begin
Grant<=2’b00;
Last_winner<=0;
End
Else begin
Grant<=2’b00;
If(request!=2’b00) begin:find_winner
Reg winner;
Case(request)
2’b01:winner<=0;
2’b10:winner<=1;
2’b11:if (last_winner==1’b0) winner<=1;
else winner<=0;
default:winner<=0;
endcase
grant[winner] <=1’b1;
last_winner<=winner;
end
end
end
endmodule

上述两个代码的综合结果差不多。而且上面第一段代码在always块内采用阻塞赋值的方法也存在潜在问题。

4、要有好的注释风格

要有好的注释风格。减少注释的行数跟节约代码的行数一样,我不推荐。像代码有好坏一样,注释也有好坏之分。比如:

//increment addr
addr<=addr+1;

上例中的注释就是一句废话,因为地址加1是不言自明的。这样的注释反而会影响可读性。好的注释应该给出该段代码的使用目的,它能够给不熟悉这段代码的人以信息。如下例:

//In burst mode,the bytes are written in consecutive addresses.Need to access the next address to verify that the next byte was properly saved.
Addr<=addr+1;

关于如何注释我在设计重用里面讲过,这里不再重复,请参考[2]。

5、应尽量将声明本地化

这种办法有点类似于C++中封装的概念,应尽量保持变量的可见性在必要的范围内,不要产生不必要的交互。
如下例:
integer I;

always
begin
for(I=0;I<32;I=I+1) begin … end
end

always
begin
for(I=15;I>=0;I=I-1) begin … end
end

对于两段代码来讲,I是全局的,他们的不必要的交互会产生不可预料的结果。在verilog里面,你可以将I定义到always块里面,使I只对当前块可见。如下例:

always
begin:block1
integer I;
for(I=0;I<32;I=I+1) begin … end
end

always
begin:block2
integer I;
for(I=15;I>=0;I=I-1) begin … end
end

在Verilog里面其它使变量本地化的方法是采用function 和task。Function和task中定义的变量只对其内部可见。
Task send;
Input [7:0] data;

Reg parity;
Begin

end
endtask
function [31:0] average;
input [31:0] val1;
input[31:0] val2;
reg [32:0] sum;
begin
sum=val1+val2;
average=sum/2;
end
endfunction;

另外,还要提一句关于`define VS parameter。一般设计中都要有一个头文件,里面用`define定义了一些宏。而模块中又有可能用parameter定义一些参数。两者的区别是一个是全局的一个是本地的。设计者可以根据需要进行定义。定义成本地参数避免可以避免同其他模块的变量名字冲突。定义成全局的宏则可以在整个设计的各个文件中使用。千万不可为了方便全部定义成宏。

6、子程序封装

封装有利于程序的可维护性。如果程序中多次用到一段代码,应根据实际情况尽量将它定义为function、task或module。这样做可以减少代码长度,提高可维护行和可验证性。

下面以testbench为例说明。

通常一个SoC设计中,需要首先设计和验证各个子模块,然后才进行系统级验证,因此要多次用到testbench。每个testbench中都要初始化时钟、进行系统复位等。这些多次用到的代码不如写在单独的一个文件(不妨称作basic.v)中,各个testbench只要使用include将他们包括进来就可以直接使用了。

下面是我在最近的863项目中写的basic.v去掉一些注释后的内容,可以直接copy使用。

要求将时钟定义为clk,复位管脚定义为rst,高有效。
//start of basic.v
event ENDSIM;
// CYCLE monitor and end-of-simulation checker.
task monitor_cycles;
input max_cycles;
integer max_cycles;
integer cycles;
begin
cycles = 0;
fork
// Count cycles.
forever begin
@(posedge clk);
cycles = cycles + 1;
end

// Watch for max cycles. If we detect max cycles then throw our testbench ENDSIM event.
//
begin
wait (cycles == max_cycles);
$display ("MAXIMUM CYCLES EXCEEDED!");
->ENDSIM;
end
join
end
endtask
// Reset
task reset;
begin
rst = 1;
#200;
rst = 0;
$display ("End RESET.");
end
endtask

// Drive the clock input
task drive_clock;
begin
clk = 0;
forever begin
#(`CLKLO) clk = 1;
#(`CLKHI) clk = 0;
end
end
endtask

// ************* BASIC CONFIDENCE Test Tasks **************
//
// BASIC CONFIDENCE Test.
//
// This task will fork off all the other necessary tasks to cause reset, drive the clock, etc. etc.
//
//
task clk_rst_gen_and_stop_at;
input cycle_num;
begin
$display ("designed by Chenxi. Email:chenxiee@mails.tsinghua.edu.cn");
fork
// Capture data
capture_data;

// Run the clock
drive_clock;

// Do a reset
reset;
begin
monitor_cycles(cycle_num);
end
// Catch end of simulation event due to max number of cycles or pattern from PIC code.
begin
@(ENDSIM); // Catch the event.
$display ("End of simulation signalled. Killing simulation in a moment.");
#0; // Let anything else see this event...
$stop;
end
join
end
endtask
//end of basiv .v
在testbench中可以直接例化使用。
//somemodule_tb.v
module somemodule_tb;
`include “basic.v”
initial begin
// ** This is our top-level "Basic Confidence" test.

//generatr rst,clk,and stop at 5000 cycles
clk_rst_gen_and_stop_at(5000);

end

endmodule //End of somemodule_tb.v

需要注意的是basic.v不能够单独编译。直接编译testbench就可以了。上述方法也在可综合的代码中使用。
参考文献[1]推荐的另外一种办法是将上述basic.v用模块封装起来。如下例:
module basic;
integer warnings;
integer errors;
initial
begin
warnings=0;
errors=0;
end
task warning;
input[80*8:1] msg;
begin
$write(“Warning at %t: %s”,$time,msg);
warnings=warnings+1;
end
endtask
task terminate;
begin
$write(“Simulation Completedn”);
$stop;
end

endmodule
调用办法:
module somemodule_tb;
initial
begin

if(…) basic.warning(”Unexpected response detected”);

basic.terminate;
end
endmodule

7、结论

本文VerilogHDL为例,从可重用性与可维护性的关系、代码可扩展性设计、尽量增加可读性和使变量本地化、参数(parameter)和宏(`define)的对比以及封装子程序的角度探讨了可维护性设计应遵守的几条基本原则。



参考文献:

[1].PCIdatasheethttp://www.dzsc.com/datasheet/PCI_1201469.html.


来源:ks990次

本站声明: 本文章由作者或相关机构授权发布,目的在于传递更多信息,并不代表本站赞同其观点,本站亦不保证或承诺内容真实性等。需要转载请联系该专栏作者,如若文章内容侵犯您的权益,请及时联系本站删除。
换一批
延伸阅读

在当今数字化时代,边缘 AI 正以前所未有的态势改变着我们的生活与产业格局。从智能安防到自动驾驶,从医疗健康到工业制造,边缘 AI 的身影无处不在。然而,要实现边缘 AI 的全面适用,仍面临诸多挑战,而负责任的赋能技术则...

关键字: 边缘 技术 数字化

在科技飞速发展的当下,汽车行业正经历着一场深刻变革,汽车通信系统作为其中的关键领域,展现出了极为光明的前景。其中,车对车(V2V)和车对基础设施(V2I)技术凭借其在避免事故方面的卓越潜力,成为了人们关注的焦点。

关键字: 汽车 通信系统 技术

在全球经济格局深度调整的当下,企业面临着日益激烈的市场竞争。为了在这一浪潮中脱颖而出,实现可持续发展,数字化转型已成为企业的必然选择。而技术创新作为推动数字化转型的核心驱动力,正引领着企业迈向高 “智” 量发展的新征程。

关键字: 数字化 技术 创新

随着人工智能、大数据、物联网等新一代技术的蓬勃发展,物流行业正经历着前所未有的变革。物流智慧化改造,即以智能化、自动化为核心,通过引入先进的技术手段,优化物流流程,提升运营效率,已成为物流行业发展的重要趋势。智能物流系统...

关键字: 物流 智能 技术

近年来,随着科技的飞速发展,自动驾驶技术正逐步从概念走向现实,并在全球范围内掀起了一场新的技术革命。在这场革命中,L3级自动驾驶作为迈向更高阶自动驾驶的关键一步,成为了各大车企和技术提供商竞相角逐的重头戏。本文将深入探讨...

关键字: 自动驾驶 技术 L3级

在21世纪的科技浪潮中,人类社会正以前所未有的速度迈向智能化时代。从智能家居到智慧城市,从智能制造到智慧医疗,技术的每一次飞跃都在深刻改变着我们的生活、工作与思维方式。在这个充满无限可能的时代,汇聚全球领先技术,共同绘制...

关键字: 智能化 技术 智慧蓝图

3D打印技术(3D printing technology)是一种以数字模型为基础,通过逐层累加材料的方式制造物体的技术。它已经在许多领域引起了广泛的关注和应用,包括制造业、医疗领域、建筑业、艺术设计等。本文将围绕3D打...

关键字: 3D打印 数字模型 技术

3D打印技术在医疗领域的应用日益广泛,它不仅能够提供个性化的医疗解决方案,还能够快速制造医疗器械和人体组织模型,为医生和患者带来了许多益处。本文将详细介绍3D打印技术在医疗方面的应用,并探讨其所起到的作用。

关键字: 3D打印 医疗 技术

6月21日消息,最近在法国巴黎举行的联合国教科文组织首届阿勒福赞奖颁奖仪式上,中国科学院古脊椎动物与古人类研究所付巧妹获得阿勒福赞科学、技术、工程、数学领域杰出青年科学家国际奖(简称"阿勒福赞奖")。

关键字: 科学 技术 工程

DSP又称数字信号处理器,数字信号处理是将信号以数字方式表示并处理的理论和技术。数字信号处理与模拟信号处理是信号处理的子集。数字信号处理的目的是对真实世界的连续模拟信号进行测量或滤波。因此在进行数字信号处理之前需要将信号...

关键字: DSP 技术 信号处理
关闭