https://mp.weixin.qq.com/s/TSwKL_qm-b-0e8x7r--hhg
?
簡單介紹Atomics中數學運算、邏輯運算的實現。
?
?
?
?
1. io
?
Atomics是一個硬件模塊,他繼承自Modules:
?
?
?
IO端口定義如下:
?
?
其中:
a. write: 是否寫操作;
b. a:輸入操作類型及一個操作數;
c. data_in:輸入另一個操作數;
d. data_out:輸出計算結果;
?
2. Arithmetic params
?
處理channel a傳入的params:
?
?
?
Channel A上的ArithmeticData消息的params定義如下:
?
?
對應代碼,可以看到params各比特的意義:
a. io.a.params(2):如果這一位為1,則表明運算為加法運算;
b. io.a.params(1):如果這一位為1,則表明為無符號運算;
c. io.a.params(0):如果這一位為1,則表明為取最大值運算;如果為0,則為取最小值運算;
?
3. Arithmetic
?
數學運算邏輯的實現如下:
?
?
?
1) signBit
?
io.a.mask中的每一位對應著io.a.data中的一個字節。mask中為0的位對應的字節無效,不參與運算。mask中為1的位對應的字節才可以參與運算。那么io.a.data的符號,則由io.a.mask中最高的值為1的位對應的字節決定。
?
signBit的意義即為取出mask中最高的一個為1的位。只有mask中最高的值為1的位對應的比特值為1,其余比特的值都為0。這樣可以作為掩碼選出io.a.data中決定符號的字節。
?
?
?
這個左移一位相與的實現,有一個隱含條件:mask中值為1的比特要連續。
?
這一點由規范中對io.a.mask的要求來滿足:
?
?
?
2) inv_d
?
?
如果是加法,則直接使用原值即可。如果是減法,則需要取反加一。
?
3) sum
?
相加:
?
?
a. 把io.a.mask中的每一位擴展為8位:FillInterleaved(8, io.a.mask)
b. 使用擴展之后的掩碼去掩io.a.data,把無效的字節掩掉:FillInterleaved(8, io.a.mask) & io.a.data
c. 與inv_d相加: + inv_d
?
根據inv_d的取值,決定結果是和還是差:io.a.data - io.data_in = io.a.data + ~io.data_in + 1。
所以如果sum是差值,那么還需要加1才是真正的差值。
?
4) sign
?
計算輸入參數的符號:
?
?
a. 輸入參數x是一個UInt;
b. 取出x中每一個字節的最高位:x.toBools().grouped(8).map(_.last).toList;
c. 把最高位組成一個UInt;
d. 取出最高有效字節的最高位:& signBit;
e. 以Bool類型輸出最高位:.orR();
?
5) pick_a
?
如果運算是比較大小的話,是選擇第一個參與比較的數字a,還是選擇比較對象d:
?
?
如果選擇較大的數,而a大于d則選擇a;
?
6) a_bigger
?
a是不是比d大:
?
?
?
a. 如果a和d的符號不同,則取決于a_bigger_uneq的值,這個下面介紹。
?
b. 如果a和d的符號相同,則取決于兩者之差的符號,即取決于sum的符號,及sign_s的值。
?
這里的一個小trick是sum并不是真正的差值,而是差值減了1的值。即:
?
?
?
sign_s是sum的符號,sign_s == 0等價于sum >= 0:
?
?
a - d >= 1的情況下,a一定大于d,不包括a == d的情況。
?
這里利用了a == d的情況下,取a或者d都是一樣的,來規避求差值需要減1的邏輯消耗。
?
7) a_bigger_uneq
?
這個名字中的uneq意思就是a和d的符號不同:
?
?
A. 如果unsigned == 1:
a. 如果sign_a == 1,那么sign_d == 0,a更大,a_bigger_uneq == 1;
b. 如果sign_a == 0,那么sign_d == 0,a更小,a_bigger_uneq == 0;
?
B. 如果unsigned == 0:
a. 如果sign_a == 1為負數,那么sign_d == 0為正數,a更小,a_bigger_uneq == 0;
b. 如果sign_a == 0為正數,那么sign_d == 0為負數,a更大,a_bigger_uneq == 1;
?
可以看到a_bigger_uneq可以很好的表示兩個數值的大小。
?
4. Logical
?
Logical的計算比較簡單,把第一個參數和第二個參數相應的位組合在一起,總共有四種情況,組成序號0/1/2/3,根據這個序號去查表即可得到邏輯運算的結果:
?
?
?
5. 運算結果輸出
?
可能輸出的有四個值:0=d, 1=a, 2=sum, 3=logical
?
?
?
根據每一種操作(io.a.opcode決定)所需要值確定一個序號,然后再使用這個序號從序列中取值即可:
?
?
這里根據io.a.mask,逐個字節取值。無效字節使用io.data_in中的值。