hdl代码学习不明白的地方
1.可能确实是冗余
2.可能是为了便于阅读
3.可能你没有想明白
另外HDL是描述语言,你觉得简洁的代码未必底层器件有对应的结构。
简单滴语句,的确人读起来易懂,但是综合机器就不一定了,它可能会综合处好几种结果!
我觉得这是要根据你的需求,还有编码风格来定的!
谢谢诸位,受教了
贴出来才有讨论余地啊。 空对空,没啥用
没贴出来,都是在凭空猜测。
VERILOG 还是很好学的。
学学代码风格吧。
不仅设计出来的电路简单,门数少,还要跑的频率高。这就是技术。
vERILOG HDL,多看看国外公司的代码对你很有好处的。
verilog 可综合的代码风格
#1:当为时序逻辑建模,使用“非阻塞赋值”。
#2:当为锁存器(latch)建模,使用“非阻塞赋值”。
#3:当用always块为组合逻辑建模,使用“阻塞赋值”
#4:当在同一个always块里面既为组合逻辑又为时序逻辑建模,使用“非阻塞赋值”。
#5:不要在同一个always块里面混合使用“阻塞赋值”和“非阻塞赋值”。
#6:不要在两个或两个以上always块里面对同一个变量进行赋值。
#7:使用$strobe以显示已被“非阻塞赋值”的值。
#8:不要使用#0延迟的赋值。
9:在VERILOG语法中, if...else if ... else 语句是有优先级的,一般说来第一个IF的优先级最高,最后一个ELSE的优先级最低。如果描述一个编码器,在XILINX的XST综合参数就有一个关于优先级编码器硬件原语句的选项Priority Encoder Extraction. 而CASE语句是"平行"的结构,所有的CASE的条件和执行都没有“优先级”。而建立优先级结构会消耗大量的组合逻辑,所以如果能够使用CASE语句的地方,尽量使用CASE替换IF...ELSE结构。
#10:XILINX的底层可编程硬件资源叫SLICE,由2个FF和2个LUT组成。 FF触发器 LUT查找表
ALTERA的底层可编程硬件资源叫LE, 由1个FF和1个LUT组成。
11:慎用锁存器(latch),同步时序设计要尽量避免使用锁存器,综合出非目的性latch的主要原因在于不完全的条件判断句。另外一种情况是设计中有组合逻辑的反馈环路(combinatorial feedback loops)。
#12:状态机的一般设计原则,Biary, gray-code 编码使用最少的触发器,较多的组合逻辑。而one-hot编码反之。所以CPLD多使用GRAY-CODE, 而FPGA多使用ONE-HOT编码。另一方面,小型设计使用GRAY-CODE和BINARY编码更有效,而大型状态机使用ONE-HOT更有效。
#13:业界主流CPLD产品是lattice的LC4000系列和ALTERA的MAX3000系列。
#14:复位使初始状态可预测,防止出现禁用状态。FPGA 和CPLD 的复位信号采用异步低电平有效信号,连接到其全局复位输入端,使用专用路径通道,复位信号必须连接到FPGA 和CPLD 的全局复位管脚。
#15:不要用时钟或复位信号作数据或使能信号,也不能用数据信号作为时钟或复位信号,否则HDL 综合时会出现时序验证问题。信号穿过时钟的两半个周期时,要在前后分别取样;防止出现半稳定状态。
16:fpga设计中 不要使用门时钟(don't use gated clock)。时钟信号必须连接到全局时钟管脚上。
#17:不要使用内部三态信号,否则增加功耗。
#18:只使用同步设计,不要使用延时单元。
#19:避免使用负延触发的双稳态多谐振荡器(flip flop)。
#20:不要使用信号和变量的默认值(或初始值),用复位脉冲初始化
信号和变量。
#21:不要在代码中使用buffer 类型的端口读取输出数据;要使用out 类型,再增加另外变量或信号,以获取输出值。
这是因为buffer 类型的端口不能连接到其他类型的端口上,因此buffer 类型就会在整个设计的端口中传播下去。
#22:对变量要先读后写;如果先写后读,就会产生长的组合逻辑和锁存器(或寄存器)。这是因为变量值是立即获取的。
#23:在组合逻辑进程中,其敏感向量标中要包含所有要读取得信号;这是为了防止出现不必要的锁存器。
Description
本文主要是收集一些重要的Verilog coding style。一个好的coding style可以减少错误的发生,增加电路的效能,以及较好的可读性。
Text
The order of module signals
一个module signal顺序如下 (由左至右):
Input
clock signals(clk_*)
set/reset signals(set_*, rst_*)
enable/disble signals(en_*, dis_*)
read/write enable signals(we_*, re_*, rw_*)
control signals(i_*)
address signals(i_*)
data signals(i_*)
Output
clock signals(o_clk_*)
set/reset signals(o_set_*, o_rst_*)
enable/disable signals(o_en_*, o_dis_*)
control signals(o_*)
address signals(o_*)
data signals(o_*)
In/Out
control signals(io_*)
address signals(io_*)
data signals(io_*)
Naming Rule
以下的namign rule为个人使用的规则。
命名方式分类
底线分隔型:xxx_yyy_zzz
大写底线分隔型:XXX_YYY_ZZZ
首字大写型:AbcDefGhi
首字小写型:avcDefGhi
各种元素所使用的命名
文件名称:底线分隔型, Ex: xxx_yyy_zzz.v
module名称:底线分隔型, Ex: xxx_yyy_zzz
module instance名称:底线分隔型, Ex: xxx_yyy_zzz
local wire名称:底线分隔型:Ex: xxx_yyy_zzz
local reg名称:底线分隔型, Ex: xxx_yyy_zzz
input signal名称:前置i_的底线分隔型, Ex: i_xxx_yyy_zzz
output signal名称:前置o_的底线分隔型, Ex: o_xxx_yyy_zzz
input/output signal名称:前置io_的底线分隔型, Ex: io_xxx_yyy_zzz
常数名称:大写底线分隔型, `XXX_YYY_ZZZ
parameter参数名称:大写底线分隔型, Ex: XXX_YYY_ZZZ
block名称:大写底线分隔型, Ex: XXX_YYY_ZZZ
特殊讯号名称
单一的clock signal: clk
多个clock signal: clk_xxx
负缘触发的clock signal: clk_n, clk_xxx_n
单一的reset signal: rst
多个reset signal: rst_xxx
负缘触发的reset signal: rst_n , rst_xxx_n
单一的set signal: set
多个set signals: set_xxx
负缘触发的set signals: set_n, set_xxx_n
致能讯号: en_xxx
除能讯号: dis_xxx
Procedural Assignments
使用指引
在撰写sequential logic时,使用nonblocking assignment。
在撰写latches电路时,使用nonblocking assignment。
在always block中撰写conbinational logic时,使用blocking assignment。
在同一个always block中同时撰写sequential及combinational logic时,一律使用nonblocking assignment。
别在同一个always block中混合使用nonblocking及blocking assignment。
别在两个以上的always block中对同一个变量设定数值。
使用$strobe来显示由nonblocking assignment所给定的变量。
不要对assignment使用#0延迟设定。
在procedural assignment中,LHS一定要是reg型态。非procedural assignment一定是net的型态。
常用的style
Combination logic
在always block中,如果使用combination logic,应当使用blocking assignment.
// All inputs used within always block should be listed in sensitive list.
always @(in1 or in2 or in3)
begin
xxx = in1 ^ in2 & in3;
end
Sequential logic
在always block中,如果使用sequential logic,应当使用nonblocking assignment.
// Synchronous reset. The rst_n is NOT in sensitive list.
always @(posedge clk)
begin
if (~rst_n)
begin
xxx <= `INIT_VAL;
end
else
begin
xxx <= yyy;
end
end
// Asynchronous reset. The rst_n IS in sensitive list.
always @(posedge clk or negedge rst_n)
begin
if (~rst_n)
begin
xxx <= `INIT_VAL;
end
else
begin
xxx <= yyy;
end
end
Delay的建模
Combination logic
建模没有delay时,使用blocking assignment(ex: a = b;)
建模有惯性(inertial) delay时(即glitch不会传到后面的电路中)。使用delayed evaluation blocking assignments(#10 a = b;).
建模传输(transport)delay时(即glitch也会一并传到后面的电路中)。使用delayed assignment nonblocking assignments(ex: a <= #10 b;).
Sequential logic
建模没有delay时,使用non-blocking assignments (ex: q <= d; ).
建模有delay时,使用delayed assignment nonblocking assignments(ex: q <= #10 d;).
Finite State Machine
Moore FSM: 输出与输入没有直接关系。
由两个always block构成,一个是sequential block用来处理状态的变化。另一个为combinational block用来处理状态与输入之间的关系。注意,在sequential block中应全部使用nonblocking assignment。在combinational block中应使用blocking assignment。在某些简单的case中,combinational block也可直接由continuous assignment来取代。下面的范例是一般简单的FSM。
// Sequential always block.
always @(posedge clk or posedge rst)
begin
if (rst)
state <= STATE_IDLE;
else
state <= next;
end
// Combinational always block.
always @(state or input1 or input2 ... or inputN)
begin
next = STATE_IDLE;
outputs = OUTPUT_IDLE:
case (state)
STATE_IDLE:
begin
... // the logic to determine the next state.
next = STATE_?;
end
STATE_?:
begin
... // the logic to determine the next state.
next = STATE_?;
outputs = ?; // The output of this state.
end
endcase
end
针对simplified one-hot encoding的FSM范例:
// Sequential always block.
always @(posedge clk or posedge rst)
begin
if (rst)
state <= n'b0;
state[STATE_DEFAULT] <= 1'b0;
else
state <= next;
end
// Combinational always block.
always @(state or input1 or input2 ... or inputN)
begin
next = n'b0;
outputs = OUTPUT_DEFAULT:
case (1'b1) // synopsys full_case parallel_case
state[STATE_DEFAULT]:
begin
... // the logic to determine the next state.
next[STATE_?] = 1'b1;
end
state[STATE_?]:
begin
... // the logic to determine the next state.
next[STATE_?] = 1'b1;
outputs = ?; // The output of this state.
end
// synopsys translate_off
default:
$display("Bad state!");
// synopsys translate_on
endcase
end
针对simplified one-hot with zero-idle encoding的FSM:
// Sequential always block.
always @(posedge clk or posedge rst)
begin
if (rst)
state <= n'b0;
else
state <= next;
end
// Combinational always block.
always @(state or input1 or input2 ... or inputN)
begin
next = n'b0;
outputs = OUTPUT_DEFAULT:
case (1'b1) // synopsys full_case parallel_case
~|state: // IDLE
begin
... // the logic to determine the next state.
next[STATE_?] = 1'b1;
end
state[STATE_?]:
begin
... // the logic to determine the next state.
next[STATE_?] = 1'b1;
outputs = ?; // The output of this state.
end
// synopsys translate_off
default:
$display("Bad state!");
// synopsys translate_on
endcase
end
Mealy FSM: 输出与输入有直接关系。
Mealy FSM的作法与上面的范例相类似。唯一的不同在于outputs的指定,需加上与input相关的逻辑判断。例如:
case(state) // synopsys parallel_case full_case
...
STATE_?:
begin
...
if (input1 & input2)
outputs = ?;
else
outputs = ?;
end
// synopsys translate_off
default:
$display("Bad FSM.");
// synopsys translate_on
default
endcase
Datapath
参考:Coding Guidelines for Datapath Synthesis.
有号数的计算:若有需要关于有号数的计算,应当利用Verilog 2001所提供的signed及$signed()机制。
input signed [7:0] a, b;
output signed [15:0] o;
assign o = a * b;
or
input [7:0] a, b;
output [15:0] o;
wire signed [15:0] o_sgn;
assugb o_sgn = $signed(a) * $signed(b);
assign o = $unsigned(o_sgn);
正负号的扩展:应多加利用Verilog的implicity signed extension,避免手动进行转换。
input signed [7:0] a, b;
input signed [8:0] o;
assign o = a + b; // Verilog会自动进行符号的扩展。
可以拿出一个你所说的例子 分析下
学习了
多看看综合后的电路
可能与每一个公司的代码风格,和接口规范有关,也不排除,为了调整时序
哈
其实语句不重要,关键是你对电路的理解,硬件跟软件不一样,代码长也许电路就很简单,别片面理解
