(筆記) 如何對一變數指定某一個bit的值? (SOC) (C/C++) (Verilog)

Abstract
寫Firmware或HDL與寫AP其中一個差異就是,寫Firmware或HDL常常需要對bit去做控制,因為每一個bit都代表不同register的設定值,但是寫AP通常不會這樣去設計,所以如何對bit去處理,在寫Firmware與HDL非常基本也非常重要。

Introduction
使用環境:Visual C++ 9.0 + ModelSim SE 6.3e + Debussy 5.4 v9

Firmware常常要對register去做讀寫,每一個bit在datasheet都有他所代表的意思,所以常必須對某一個bit去做寫入,比如說對第2 bit強迫寫入0或1,在Verilog這很平常,因為Verilog提供bit select語法,只要vec[2] = 1'b0或者vec[2] = 1'b1即可,但在C語言卻沒有這種語法,必須透過一些小技巧才能達成。

基礎知識

C沒有如Verilog的bit select語法,要對某一bit做控制,主要是靠MASK的方式。

假如一個flag原本以2 bit表示為0000_1100,若要強迫使第2個bit為0,也就是最後結果為0000_1000,我們可以將MASK設為0000_0100,則flag & ~MASK將得到0000_1000。

假如一個flag原本以2 bit表示為0000_1100,若要強迫使第1個bit為1,也就是最後結果為0000_1110,我們可以將MASK設為0000_0010,則flag |    MASK將得到0000_1110。

簡單的說,就是

&0可以強迫特定bit為0,其他不變則&1
|1可以強迫特定bit為1,其他不變則|
0

不過一般我們在書上看到的範例程式,都是教我們將MASK先設定成常數,如define或者enum,若我們想做成活的,也就是希望MASK可以透過function的方式動態去設定,我們該怎麼做呢?

bit_ctrl.c / C

1 /*
2 (C) OOMusou 2011 http://oomusou.cnblogs.com
3
4 Filename : bit_ctrl.c
5 Compiler : Visual Studio 2010 / Visual C++ 10.0
6 Description : bit control in C
7 Release : 02/16/2011 1.0
8  */
9 #include <stdio.h>
10
11  void bit_ctrl_0 (char* pflag, int bit) {
12 *pflag &= ~(1 << bit);
13 }
14
15  void bit_ctrl_1 (char* pflag, int bit) {
16 *pflag |= (1 << bit);
17 }
18
19  int main() {
20 char flag = 0xC; // 00001100
21  
22 bit_ctrl_0(&flag, 2);
23 printf("bit 2 to be zero:%#x\n", flag);
24
25 bit_ctrl_1(&flag, 2);
26 printf("bit 2 to be one:%#x\n", flag);
27 }

執行結果

bit 2 to be zero:8
bit
2 to be one:12

11行

void bit_ctrl_0 (char* pflag, int bit) {
*pflag &= ~(1 << bit);
}

bit_ctrl_0() function,傳入原flag的pointer與第幾bit要設定成0,看到一堆*&=<你可能會昏頭了。將其展開得到如下:

void bit_ctrl_0 (char* pflag, int bit) {
(
*pflag) = (*pflag) & ~(1 << bit);
}

展開後(*pflag)應該就沒問題了,現在問題在於~(1 << bit)是什麼意思?

回想基礎知識所談的,假設我們要將一數的第2個bit設為0,我們會 &~MASK,也就是&~(0000_0100),其中(0000_0100)就是(1 << bit),在此處bit為2,所以透過 << 這個shift operator,我們就能將MASK寫成活的。

15行

void bit_ctrl_1 (char* pflag, int bit) {
*pflag |= (1 << bit);
}

同理,bit_ctrl_1() function,傳入原flag的pointer與第幾bit要設定成1,展開如下:

void bit_ctrl_1 (char* pflag, int bit) {
(
*pflag) = (*pflag) | (1 << bit);
}

回想基礎知識所談的,假設我們要將一數的第2個bit設為1,我們會 | MASK,也就是 | (0000_0100),其中(0000_0100)就是(1 << bit),在此處bit為2,所以透過 << 這個shift operator,我們就能將MASK寫成活的。

C談完了,現在來看Verilog部分,雖然Verilog提供bit select語法,不過Verilog規定,bit select只能用常數,也就是parameter、define或直接給個數值,不能使用變數或者reg,否則在NC-Verilog會得到以下錯誤訊息:

ncvlog: *E,NOTPAR (bit_ctrl2.v,20|15): only parameters and constant literals allowed in constant expressions [4(IEEE)].

也就是說,若要在Verilog能動態的去做bit control,還是得跟C一樣,利用shift operator走MASK的方式。

假設我們希望8顆Led依次第1顆亮->然後第2顆也亮->然後第3顆也亮->…..->8顆全亮->第1顆暗->第2顆暗->…..8顆全暗->第1顆亮->第2顆亮->….這樣週而復始

若以二進位表示,相當於

0000_0000 –> 0000_0001 –> 0000_0011 –> 0000_0111 –> … –> 1111_1111 –> 1111_1110 –> 1111_1100 –> 1111_1000 –> … –> 0000_0000 –> 0000_0001 –> 0000_0011

下圖是實際的波形圖,其中led_o為實際led的output。

bit_ctrl02

bit_ctrl01

bit_ctrl.v / Verilog

1 /*
2 (C) OOMusou 2011 http://oomusou.cnblogs.com
3
4 Filename : bit_ctrl.v
5 Simulator : ModelSim 6.3e, Debussy 5.4 v9
6 Description : bit control in Verilog
7 Release : 02/19/2010 1.0
8  */
9
10  module bit_ctrl (
11 clk,
12 rst_n,
13 led_o
14 );
15
16  input clk;
17  input rst_n;
18  output [7:0] led_o;
19
20  reg [2:0] cnt;
21 reg [7:0] led_o;
22 reg ctrl;
23
24 always@(posedge clk or negedge rst_n) begin
25 if (~rst_n)
26 cnt <= 3'h0;
27 else
28 cnt <= cnt + 1'b1;
29 end
30
31 always@(posedge clk or negedge rst_n) begin
32 if (~rst_n)
33 ctrl <= 1'b0;
34 else if (cnt == 3'b111)
35 ctrl <= ~ctrl;
36 else
37 ctrl <= ctrl;
38 end
39
40 always@(posedge clk or negedge rst_n) begin
41 if (~rst_n)
42 led_o <= 8'h0;
43 else if (ctrl)
44 led_o <= led_o & ~(1 << cnt);
45 else
46 led_o <= led_o | (1 << cnt);
47 end
48
49 endmodule

44行

led_o <= led_o & ~(1 << cnt);
else
led_o
<= led_o | (1 << cnt);

其他部分不是重點我就略過,cnt相當於動態要求MASK的1要在哪一位,也就是1必須shift幾位,這與之前C的技巧完全一樣。

Testbench
bit_ctrl_tb.v / Verilog

1 /*
2 (C) OOMusou 2011 http://oomusou.cnblogs.com
3
4 Filename : bit_ctrl_tb.v
5 Simulator : ModelSim 6.3e, Debussy 5.4 v9
6 Description : bit control in Verilog
7 Release : 02/19/2010 1.0
8 */
9
10 module bit_ctrl_tb;
11
12 reg clk = 1'b0;
13 reg rst_n = 1'b0;
14 wire [7:0] led_o;
15
16 always #10 clk = ~clk;
17
18 initial begin
19 #5;
20 rst_n = 1'b1;
21 end
22
23 initial begin
24 $fsdbDumpfile("bit_ctrl.fsdb");
25 $fsdbDumpvars(0, bit_ctrl_tb);
26 end
27
28 bit_ctrl u_bit_ctrl (
29 .clk (clk),
30 .rst_n (rst_n),
31 .led_o (led_o)
32 );
33
34 endmodule

完整程式碼下載
bit_ctrl_c.7z
bit_ctrl_verilog.7z

Conclusion
本文透過C與Verilog的shift operator,可以動態的去產生MASK,這樣就可以動態的去控制某一個bit的值。

See Also
(筆記) 常用設定暫存器值的編程技巧 (SOC) (C/C++) (C) (Verilog)

全文完。

posted on 2011-02-16 23:53  真 OO无双  阅读(34968)  评论(0编辑  收藏

导航