对于IT相关从业人员来说,看别人代码是必不可少的磨难。在学习阶段,我们经常需要从书上看别人的代码以吸取宝贵经验,这是相当枯燥无趣的过程,也时常无法领会作者的意图。在实际工作中,不可避免的出现需要接手做到一半的项目或是团队合作的项目,这时候就必须看以前的工程师的代码。如果说看书上的代码用痛苦来形容的话,那么这种情况时遇到代码不够规范或者设计不合理,简直就是苦不堪言。还有一些神一般的选手,设计者在编写代码时的“灵机一动”,其结果只有他自己和上帝才看得懂。这些代码能看得你觉得生不如死,甚至开始怀疑人生:到底是代码写得混乱or我水平不行?!你以为这是最痛苦的吗?NO!还有一种情况足以令你看得生不如死,甚至开始怀疑人生。那就是遇到运行不正常的代码,对问题排查错误花的时间和精力还不如重新写一遍,这时你的内心完全是崩溃的!
]
我们知道,实现功能可能有很多种方法,所以不同的人写出的代码不相同。通过看代码去知道它要实现的是什么功能是一件很困难的事情。有些初学者会想到采取仿真一下、看电路图、流程图、时序图、注释等等方法,都是不可取的,通过这些你还是无法知道它的功能,以及有没有错误。
那么正确的方法是什么呢?我们采取一种可称之为“反推法”或是“逆向法”的方式,这个问题就迎刃而解了。要知道,代码的目的是实现功能。无论你用那种代码,有一点完全相同的就是“实现功能”这个最终结果。了解到这一点,我们就可以通过结果(功能)去反推过程(代码),代码的思路、流程、用途就抽丝剥茧清晰的显露出来。好的,下面我们举个实例来说明怎么通过反推法有步骤的去看懂别人的代码。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
| //-------------------------------------------
//帧率采样计算
reg fps_state;
reg [7:0] fps_data;
always@(posedge iCLK or negedge iRST_N)
begin
if(!iRST_N)
begin
fps_data <= 0;
fps_state <= 0;
CMOS_FPS_DATA <= 0;
end
else if(Frame_valid)
begin
case(fps_state)
0: begin
CMOS_FPS_DATA <= CMOS_FPS_DATA;
if(delay_2s == 0)
begin
fps_state <= 0;
if(CMOS_VSYNC_over == 1'b1) //VS上升沿,1帧写入完毕
fps_data <= fps_data + 1'b1;
end
else
fps_state <= 1;
end
1: begin
fps_state <= 0;
fps_data <= 0;
CMOS_FPS_DATA <= fps_data >>1;
end
endcase
end
else
begin
fps_data <= 0;
fps_state <= 0;
CMOS_FPS_DATA <= 0;
end
end
endmodule
|
如果我们按照自上而下的顺序去看这个代码,通过代码的过程去看实现的功能会是很困难的事,甚至看不明白它要实现的是什么功能。Ok,我们现在从功能看起,这个代码要实现的是“帧率采样计算”这个功能,可以理解为图片每秒显示多少帧数。
1. 从代码中我们可以看出,CMOS_FPS_DATA这个信号是我们所要求的信号(一秒内的帧数率);
2. CMOS_FPS_DATA<= fps_data >>1在一段时间内保持不变,才是我们所要的结果;
3. 从CMOS_FPS_DATA<= fps_data >>1中可以看出,CMOS_FPS_DATA是通过fps_data 这个信号来实现;
4. fps_data这个信号是怎么来的?反推到fps_data <= 0和fps_data<= fps_data + 1'b1这两个信号。fps_data复位为零,在else if(Frame_valid)条件下加1;因此fps_data为帧数率标志信号;
5. 从CMOS_FPS_DATA<= fps_data >>1中可以看出是通过<= fps_data>>1右移一位,也就是说除以2得到这个值的;
6. 为什么要fps_data除以2来得到这个值?于是反推到if(delay_2s == 0)这个条件。
现在作者的意图就非常清晰了。满足帧数率的情况下不断+1,到2秒时间时根据统计结果除以2,由此得到1秒时间的帧数。到此为止,我们已经可以非常容易的看懂这个代码了。
通过反推法我们也能比较容易的去看代码是否有错误。首先我们去知道代码需要实现的功能,通过反推法得知是通过什么方法实现的,进而仿真时定位其目标,去看该代码是否完成了功能。如果没有完成功能,那么代码就有误。
对于学习者来说,反推法的意义还不仅在此。在本例中,这个设计思路完全满足功能要求。展这时,我们应该扩思考,本例是通过2秒来实现功能,为什么要用2秒?是否可以直接通过1秒,或是3秒,或是其他方案来实现呢?各种方法的优缺点在哪里?通过反推法得知作者实现项目的方法并思考,这种方法正确还是错误?如果是错误或者这种方法不太好,那么我们如何避免?如果是优秀的代码,我们如何借鉴并能举一反三地运用到其他项目中去?本例只是选取项目中的一个小的节点,对于看整个项目的代码来说可以运用反推法吗?敬请关注下一节。
|