基于Verilog语言的可维护性设计技术
发布时间:2008/8/12 0:00:00 访问次数:688
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是不言自明的
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是不言自明的