When this topic first came to my mind, I was hanging out on a beach in Tsingtao. It has been so long since the last time when I went to a beach. The warm and overwhelming salty smell of Tsingtao beach reminded me of the old time when I was cycling along the Southampton Water shore. It was also an windy day in late September. The red roofed terraces upon the hills looked so cute and so much like the house I wished to have after retirement that located right at the mouth of the River Itchen. You must wonder how did I come up with this topic when I was taking holidays? Well, since this place reminded me of Southampton, and Southampton reminded me of a very admired teacher of mine -- Iain McNally, and then BAAAAANG!!! -- I suddenly recalled the first mistake Iain had ever corrected me in my life.
"Stop creating clocks & resets in your module."
I. What Are Locally Created Clocks & Resets
I bet many of you guys, just like how I was in school lab, don't understand what are locally created clocks & resets. You may wonder what did I do to have made Iain utter that line. All right, the original code I handed to Iain when he asked me to do a synchronous design as coursework is very similar to the code showing below.
[source lang="verilog"]
//==============================================================================
// Copyright (C) 2015 By Kellen.Wang
// mail@kellen.wang, All Rights Reserved
//==============================================================================
// Module : example
// Author : Kellen Wang
// Contact : mail@kellen.wang
// Date : Oct.02.2015
//==============================================================================
// Description :
//==============================================================================
module example (
input wire data_en ,
input wire data_in ,
output reg data_even ,
output reg data_odd
);
parameter DLY = 1'd1;
reg odd_en;
wire even_en = ~odd_en;
always @(posedge clk_in or negedge rst_n) begin
if (!rst_n) begin
odd_en <= #DLY 1'd0;
end
else if (!data_en) begin
odd_en <= #DLY 1'd0;
end
else begin
odd_en <= #DLY ~odd_en;
end
end
always @(posedge odd_en or negedge rst_n) begin
if (!rst_n) begin
data_odd <= #DLY 1'd0;
end
else begin
data_odd <= #DLY data_in;
end
end
always @(posedge even_en or negedge rst_n) begin
if (!rst_n) begin
data_even <= #DLY 1'd0;
end
else begin
data_even <= #DLY data_in;
end
end
endmodule
[/source]
In the code above, when input signal data_en goes HIGH, the register odd_en starts to flip at positive edge of clock clk_in. When odd_en flips to HIGH, the input data data_in is registered into output data_odd; Whereas when it flip to LOW, wire even_en goes HIGH, and the input data data_en is registered into output data_even.
If you build a test bench for this module, you will find it works exactly as we expected. However, is it a good design? Is there any potential problem in this design? Is it really a synchronous design? Think them through, and let me explain them in the next paragraph.
First question, is it a good design? No, although it seems working in simulation, it doesn't necessarily working as expected when implemented into circuit. The second question, is there any potential problem in this design? Yes, cause in this design, two locally generated clocks are used. To be specific, registers data_odd and data_even are using generated clock odd_en and even_en respectively. Some people do not realise that when they write code as 'always @(posedge some_thing or negedge rst_n)', the signal some_thing here has implied a clock. If only not all registers in one module share the same clock signal, the module could not be taken as a synchronous design and thus multiple-clock-domain-crossing issues must be considered when registers in two clock domains have data transfers.
Take the previous code as example, the input signal data_in is synchronous to input clock clk_in. As internal logic of the example module, this incoming signal data_in is then registered to two registers -- data_even and data_odd -- that are driving by two locally generated clocks (even_en and odd_en). However, since data_in is synchronous to clk_in but not odd_en or even_en, how can we ensure that signal data_in will remain unchanged during the sampling edges of odd_en and even_en? What will happen if odd_en becomes HIGH while data_in is changing? Well, data_odd may enter metastable state (none HIGH nor LOW logic, just like 1'dZ) and then propagate this state through its following data path.
Someone may think that, since odd_en and even_en is created from clk_in, their flipping edges must be naturally aligned to the edges of clk_in, thus they are practically 'synchronous' to input signal data_in, problem solved! Indeed, I would say that it is possible to align the edges of these 'clocks', but to reach this goal, extra synthesis constraints have to be applied to the two locally generated clocks. This not only brings us more pending works to do, but also increases the risk that the design may fail to reach target performance.
In VHDL or SOC system design projects, normally we divide modules not only by functions, but also by clock domains. By constraining each module with sole clock, clock-domain-crossing problems could be systematically solved in betweens of modules using different clocks, and all generated clocks in the whole system should be gathered in a dedicated module to ensure all timing constraints are met as expected.
Locally created resets are risky too.
II. How To Avoid Locally Created Clocks & Resets
III. The Correct Way To Create Clocks & Resets
在FPGA的设计中有种叫做异步复位同步释放的结构,这种复位信号是了内部产生的,不行吗?
我想你说的是,在FPGA内部对外部输入的复位信号进行处理,重新产生一个新的异步复位同步释放的新复位,然后再把这个复位信号拿去给每个模块使用对吧。这个复位信号是内部产生的没错。在复杂的SOC系统中,内部时钟有很多很多,所谓的“同步释放”必须要有一个参考的时钟,才能说“同步”,因此实际上,在一个复杂的系统中,这个复位信号在送给任何内部“模块”之前,都要重新跟这个“模块”的主时钟进行重新“异步复位同步释放”。这种复位的处理,一般都是集中在系统中的一个特定模块中统一完成,处理完的复位信号,才会送到真正使用该复位信号的模块去,送去之前就已经保证了“异步复位,与该模块时钟同步释放”。而本文的主要观点是:在模块的内部,不要生成“本地时钟和复位信号”。这里暗含的一个前提是,复位的同步处理应该被剥离到模块外部完成,在这个前提下,对于一个模块来说,输入给该模块的复位是已经保证与该模块的时钟“异步复位同步释放”了的,所以复位送进来之后,就不要再自己内部重新生成复位信号了。把整个系统的复位信号都集中到一个特定模块中完成同步处理有很多好处,一个好处是方便进行复位链的顺序控制和设计,另一方面是方便后端人员进行时序约束。
第一次来就迷上了这个网站!加油!
期待 II与III的更新