いにしえ的ななにか、カニか?
RISC-V
picoRV32, e203 改造
オレオレ命令追加した
2023/DEC/02
たけおか@AXE
@takeoka
…
今は無き Tang Primer
廃番。EG4S20 , Tang Primer FPGA
あらかじめ RISC-Vが焼かれている
Humming bird E203 core
Verilogで書かれている
秋月で¥2,580円 だった
結構いいものだった…
Tang Nano9K
Gowin GW1NR-9 FPGAチップ
LUT4:8640個
https://akizukidenshi.com/catalog/g/gM-17448/
https://wiki.sipeed.com/hardware/en/tang/Tang-Nano-
9K/Nano-9K.html
Humming bird E203 core

2段パイプライン

RV32IMAC 命令セット

密結合メモリ

ITCM (Instruction Tightly
Coupled Memory)

DTCM (Data Tightly
Coupled Memory)

オープンソース Githubにある

Apacheライセンス
下記より引用解説:
https://content.riscv.org/wp-content/uploads/2018/07/Shanghai-
1110_HummingBirdE200forShanghaiDay_v1.pdf
Hummingbird Ultra-Low-Power 32bits RISC-V Processor Core
picorv32

picorv32 , nano 9k用
https://github.com/sipeed/TangNano-9K-example/tree/main/picotiny

pico rv32
https://github.com/YosysHQ/picorv32

picosoc
https://github.com/YosysHQ/picorv32/blob/master/picosoc/README.md

大変、単純なCPUコア

コンパクト

改造しやすい

クロック周波数は上げにくい
picorv32,e203に命令追加した
“B”拡張命令が無かった。

Parity: ワード中の”1”の個数の偶/奇を得る

Population Count:ワード中の”1”の数を数える
Alpha(CTPOP)にはある

Count Leading Zeros(CLZ)
x86(lzcnt,bsf), ARM(clz), Power(cntlzd,cntlzw),
Sparc2011(lzcnt),Mips32(clz),Alpha(CTLZ),
VAX(Find First Set(ffs)),68020(bfffo)にはある

Count Trailing Zero(CTZ)
x86(tzcnt),PowerISA3(cnttzw,cnttzd),Alpha(CTTZ)にある

bit reverse: ワードのビット順を逆転

Halt Exchange: ワードを半分に切って、その上下を入れ替え

Quarter Exchange:ハーフ・ワード内でその半分(1/4)を入れ替え
parity
ワード中の 各ビットの XORを取る
1のビットが奇数個:1
1のビットが偶数個:0
シリアル通信で使うかも…
Verilog記述
(^ reg_op1)
前置きの^ :リダクション XOR。各ビットのXORを取る
前置きの| :リダクション OR。各ビットのORを取る。0判定
前置きの& :リダクション AND。各ビットのANDを取る。0xFF判定
遅いclz Count Leading Zeros
ワード中で、MSB(最左)から0が何個 続くかを数える。
一番上(左)の1の位置がわかる。
この実装だと、32個の比較の後、
結果が得られる。
遅い
↓
クロック周波数の足を引っ張る
Verilog記述→
/* clz 24bit for pmmp0, Slow*/
assign nsh_pmmp[5:0] =
pmmp0[23] ? 6'd0
:( pmmp0[22] ? 6'd1
:( pmmp0[21] ? 6'd2
:( pmmp0[20] ? 6'd3
:( pmmp0[19] ? 6'd4
:( pmmp0[18] ? 6'd5
:( pmmp0[17] ? 6'd6
:( pmmp0[16] ? 6'd7
:( pmmp0[15] ? 6'd8
:( pmmp0[14] ? 6'd9
:( pmmp0[13] ? 6'd10
:( pmmp0[12] ? 6'd11
:( pmmp0[11] ? 6'd12
:( pmmp0[10] ? 6'd13
:( pmmp0[9] ? 6'd14
:( pmmp0[8] ? 6'd15
:( pmmp0[7] ? 6'd16
:( pmmp0[6] ? 6'd17
:( pmmp0[5] ? 6'd18
:( pmmp0[4] ? 6'd19
:( pmmp0[3] ? 6'd20
:( pmmp0[2] ? 6'd21
:( pmmp0[1] ? 6'd22
:( pmmp0[0] ? 6'd23
: 6'd24
)))))))))))))))))))))));
「ハッカーのたのしみ」本を読むべし
エエこと書いてある!
Hacker's Delight (原書は2nd Editionになっている)
出版社: エスアイビーアクセス (2004/09)
ISBN-10: 4434046683
clz,clz,population は、この本で勉強
速い clz Count Leading Zeros
ワード中で、MSB(最左)から0が何個 続くかを数える。
一番上(左)の1の位置がわかる。
●判断(?)5段、 !( |(clzx0[X:Y]))演算4段 の9段の演算になる
Verilog記述
//// clz
wire [31:0] clzx0,clzx1,clzx2,clzx3,clzx4,clzx5,n1,n2,n3,n4,n5,xx1,xx2,xx3,xx4;
assign clzx0[31:0] = reg_op1;
assign xx1 = !( |(clzx0[31:16])); // & 32'hFFFF0000
assign clzx1[31:0] = xx1 ?
clzx0[31-16:0] << 16
: clzx0[31:0] ;
assign n1 = (xx1 ? 32'd16 : 32'd0);
assign xx2 = !(|(clzx1[31:24]));
assign clzx2[31:0] = xx2 ?
clzx1[31-8:0] << 8
: clzx1[31:0] ;
assign n2 = n1 + (xx2 ? 32'd8 : 32'd0);
assign xx3 = !(|(clzx2[31:28]));
assign clzx3[31:0] = xx3 ?
clzx2[31-4:0] << 4
: clzx2[31:0] ;
assign n3 = n2 + (xx3 ? 32'd4 : 32'd0);
assign xx4 = !(|(clzx3[31:30]));
assign clzx4[31:0] = xx4 ?
clzx3[31-2:0] << 2
: clzx3[31:0] ;
assign n4 = n3 + (xx4 ? 32'd2 : 32'd0);
assign n5 = n4 + !clzx4[31]; //add
inverse of MSB
wire [31:0] clz_res = !(|(reg_op1))?
最下位(?) 細粒度 並列
assign xx1 = !( |(clzx0[31:16]));
assign clzx1[31:0] = xx1 ?
clzx0[31-16:0] << 16
: clzx0[31:0] ;
assign n1 = (xx1 ? 32'd16 : 32'd0);
●clzx1 と n1 は、同時に計算される
●
n1 が参照されるのは、clzx1 …
より 少し後だが
●assign clzx1[31:0] = xx1 ?
clzx0[15:0] << 16
は、配線の接続となって、シフト演算されない(ことを願う)
{ clzx0[15:0], 16'd0 }
…
とか書いた方がいいか
ビット選択と線(ワイヤ)
ワード中で、MSB(最左)から0が何個 続くかを数える。
一番上(左)の1の位置がわかる。
Verilog記述
assign xx1 = !( |(clzx0[31:16]));
↓あまり良くないことが起きるかも…(通常なら、最適化されて↑になるはず)
assign xx1 = !( clzx0[31:0] & 32'hFFFF0000)
1
1
1
1
ctz Count Trailing Zero
ワード中で、LSB(最右)から0が何個 続くかを数える。
一番下(右)の1の位置がわかる。
clzと同様
Verilog記述
/// ctz
wire [31:0] ctzx0;wire [15:0] ctzx1; wire [7:0] ctzx2; wire [3:0] ctzx3; wire [1:0] ctzx4;
wire [5:0] m1, m2, m3, m4, m5; wire yy1,yy2,yy3,yy4;
assign ctzx0[31:0] = reg_op1;
assign yy1 = !( |(ctzx0[15:0])); // & 32'h0000FFFF
assign ctzx1[15:0] = yy1 ?
(ctzx0[31:16])
: ctzx0[15:0] ;
assign m1 = (yy1 ? 32'd16 : 32'd0);
assign yy2 = !(|(ctzx1[7:0])); // & 32'h000000FF
assign ctzx2[7:0] = yy2 ?
(ctzx1[15:8])
: ctzx1[7:0] ;
assign m2 = m1 + (yy2 ? 32'd8 : 32'd0);
assign yy3 = !(|(ctzx2[3:0])); // & 32'h000000F
assign ctzx3[3:0] = yy3 ?
(ctzx2[7:4])
: ctzx2[3:0] ;
assign m3 = m2 + (yy3 ? 32'd4 : 32'd0);
assign yy4 = !(|(ctzx3[1:0])); // & 32'h0000003
assign ctzx4[1:0] = yy4 ?
(ctzx3[3:2])
: ctzx3[1:0] ;
assign m4 = m3 + (yy4 ? 32'd2 : 32'd0);
assign m5 = m4 + !ctzx4[0]; //add inverse of LSB
wire [31:0] ctz_res = !(|(reg_op1)) ? 32'd32 : m5;
population
ワード中の 1のビットの 数を数える
Verilog記述
//// population
wire [31:0] popx0, popx1, popx2, popx3, popx4, pop_res;
assign popx0 = reg_op1;
assign popx1 = (popx0 & 32'h55555555) + ((popx0 >> 1)& 32'h55555555);
assign popx2 = (popx1 & 32'h33333333) + ((popx1 >> 2)& 32'h33333333);
assign popx3 = (popx2 & 32'h0f0f0f0f) + ((popx2 >> 4)& 32'h0f0f0f0f);
assign popx4 = (popx3 & 32'h00ff00ff) + ((popx3 >> 8)& 32'h00ff00ff);
assign pop_r = (popx4 & 32'h0000ffff) + ((popx4 >>16)& 32'h0000ffff);
bit reverse
ワード中のビットの順序を逆にする
Verilog記述
//// bit reverse
alu_brev <= {reg_op1[0],reg_op1[1],reg_op1[2],reg_op1[3],
reg_op1[4],reg_op1[5],reg_op1[6],reg_op1[7],
reg_op1[8],reg_op1[9],reg_op1[10],reg_op1[11],
reg_op1[12],reg_op1[13],reg_op1[14],reg_op1[15],
reg_op1[16],reg_op1[17],reg_op1[18],reg_op1[19],
reg_op1[20],reg_op1[21],reg_op1[22],reg_op1[23],
reg_op1[24],reg_op1[25],reg_op1[26],reg_op1[27],
reg_op1[28],reg_op1[29],reg_op1[30],reg_op1[31]
};
Half Exchange,Quarter Exchange
●Halt Exchange: ワードを半分に切って、その上下を入れ替え
元 3,2 , 1,0
3,2 ⇔ 1,0
結果1,0 , 3,2
●Quarter Exchange:ハーフ・ワード内でその半分(1/4)を入れ替え
元 3,2 , 1,0
3⇔2 , 1⇔0
結果 2,3 , 0,1
Verilog記述
//// Half Exchange
alu_hexg <= {reg_op1[15:0],reg_op1[31:16]};
//// Quarter Exchange
alu_qexg <=
{reg_op1[23:16],reg_op1[31:24],reg_op1[7:0],reg_op1[15:8]};
//// word Ex (おまけ)
alu_wexg <=
{reg_op1[7:0],reg_op1[15:8],reg_op1[23:16],reg_op1[31:24]};
ワード中のバイトを逆順にするには、
H-Exg と Q-Exg を行う
以上

いにしえ的ななにか、カニか? RISC-V picoRV32, e203 改造 オレオレ命令追加した

  • 1.
  • 2.
    … 今は無き Tang Primer 廃番。EG4S20, Tang Primer FPGA あらかじめ RISC-Vが焼かれている Humming bird E203 core Verilogで書かれている 秋月で¥2,580円 だった 結構いいものだった… Tang Nano9K Gowin GW1NR-9 FPGAチップ LUT4:8640個 https://akizukidenshi.com/catalog/g/gM-17448/ https://wiki.sipeed.com/hardware/en/tang/Tang-Nano- 9K/Nano-9K.html
  • 3.
    Humming bird E203core  2段パイプライン  RV32IMAC 命令セット  密結合メモリ  ITCM (Instruction Tightly Coupled Memory)  DTCM (Data Tightly Coupled Memory)  オープンソース Githubにある  Apacheライセンス 下記より引用解説: https://content.riscv.org/wp-content/uploads/2018/07/Shanghai- 1110_HummingBirdE200forShanghaiDay_v1.pdf Hummingbird Ultra-Low-Power 32bits RISC-V Processor Core
  • 4.
    picorv32  picorv32 , nano9k用 https://github.com/sipeed/TangNano-9K-example/tree/main/picotiny  pico rv32 https://github.com/YosysHQ/picorv32  picosoc https://github.com/YosysHQ/picorv32/blob/master/picosoc/README.md  大変、単純なCPUコア  コンパクト  改造しやすい  クロック周波数は上げにくい
  • 5.
    picorv32,e203に命令追加した “B”拡張命令が無かった。  Parity: ワード中の”1”の個数の偶/奇を得る  Population Count:ワード中の”1”の数を数える Alpha(CTPOP)にはある  CountLeading Zeros(CLZ) x86(lzcnt,bsf), ARM(clz), Power(cntlzd,cntlzw), Sparc2011(lzcnt),Mips32(clz),Alpha(CTLZ), VAX(Find First Set(ffs)),68020(bfffo)にはある  Count Trailing Zero(CTZ) x86(tzcnt),PowerISA3(cnttzw,cnttzd),Alpha(CTTZ)にある  bit reverse: ワードのビット順を逆転  Halt Exchange: ワードを半分に切って、その上下を入れ替え  Quarter Exchange:ハーフ・ワード内でその半分(1/4)を入れ替え
  • 6.
    parity ワード中の 各ビットの XORを取る 1のビットが奇数個:1 1のビットが偶数個:0 シリアル通信で使うかも… Verilog記述 (^reg_op1) 前置きの^ :リダクション XOR。各ビットのXORを取る 前置きの| :リダクション OR。各ビットのORを取る。0判定 前置きの& :リダクション AND。各ビットのANDを取る。0xFF判定
  • 7.
    遅いclz Count LeadingZeros ワード中で、MSB(最左)から0が何個 続くかを数える。 一番上(左)の1の位置がわかる。 この実装だと、32個の比較の後、 結果が得られる。 遅い ↓ クロック周波数の足を引っ張る Verilog記述→ /* clz 24bit for pmmp0, Slow*/ assign nsh_pmmp[5:0] = pmmp0[23] ? 6'd0 :( pmmp0[22] ? 6'd1 :( pmmp0[21] ? 6'd2 :( pmmp0[20] ? 6'd3 :( pmmp0[19] ? 6'd4 :( pmmp0[18] ? 6'd5 :( pmmp0[17] ? 6'd6 :( pmmp0[16] ? 6'd7 :( pmmp0[15] ? 6'd8 :( pmmp0[14] ? 6'd9 :( pmmp0[13] ? 6'd10 :( pmmp0[12] ? 6'd11 :( pmmp0[11] ? 6'd12 :( pmmp0[10] ? 6'd13 :( pmmp0[9] ? 6'd14 :( pmmp0[8] ? 6'd15 :( pmmp0[7] ? 6'd16 :( pmmp0[6] ? 6'd17 :( pmmp0[5] ? 6'd18 :( pmmp0[4] ? 6'd19 :( pmmp0[3] ? 6'd20 :( pmmp0[2] ? 6'd21 :( pmmp0[1] ? 6'd22 :( pmmp0[0] ? 6'd23 : 6'd24 )))))))))))))))))))))));
  • 8.
    「ハッカーのたのしみ」本を読むべし エエこと書いてある! Hacker's Delight (原書は2ndEditionになっている) 出版社: エスアイビーアクセス (2004/09) ISBN-10: 4434046683 clz,clz,population は、この本で勉強
  • 9.
    速い clz CountLeading Zeros ワード中で、MSB(最左)から0が何個 続くかを数える。 一番上(左)の1の位置がわかる。 ●判断(?)5段、 !( |(clzx0[X:Y]))演算4段 の9段の演算になる Verilog記述 //// clz wire [31:0] clzx0,clzx1,clzx2,clzx3,clzx4,clzx5,n1,n2,n3,n4,n5,xx1,xx2,xx3,xx4; assign clzx0[31:0] = reg_op1; assign xx1 = !( |(clzx0[31:16])); // & 32'hFFFF0000 assign clzx1[31:0] = xx1 ? clzx0[31-16:0] << 16 : clzx0[31:0] ; assign n1 = (xx1 ? 32'd16 : 32'd0); assign xx2 = !(|(clzx1[31:24])); assign clzx2[31:0] = xx2 ? clzx1[31-8:0] << 8 : clzx1[31:0] ; assign n2 = n1 + (xx2 ? 32'd8 : 32'd0); assign xx3 = !(|(clzx2[31:28])); assign clzx3[31:0] = xx3 ? clzx2[31-4:0] << 4 : clzx2[31:0] ; assign n3 = n2 + (xx3 ? 32'd4 : 32'd0); assign xx4 = !(|(clzx3[31:30])); assign clzx4[31:0] = xx4 ? clzx3[31-2:0] << 2 : clzx3[31:0] ; assign n4 = n3 + (xx4 ? 32'd2 : 32'd0); assign n5 = n4 + !clzx4[31]; //add inverse of MSB wire [31:0] clz_res = !(|(reg_op1))?
  • 10.
    最下位(?) 細粒度 並列 assignxx1 = !( |(clzx0[31:16])); assign clzx1[31:0] = xx1 ? clzx0[31-16:0] << 16 : clzx0[31:0] ; assign n1 = (xx1 ? 32'd16 : 32'd0); ●clzx1 と n1 は、同時に計算される ● n1 が参照されるのは、clzx1 … より 少し後だが ●assign clzx1[31:0] = xx1 ? clzx0[15:0] << 16 は、配線の接続となって、シフト演算されない(ことを願う) { clzx0[15:0], 16'd0 } … とか書いた方がいいか
  • 11.
    ビット選択と線(ワイヤ) ワード中で、MSB(最左)から0が何個 続くかを数える。 一番上(左)の1の位置がわかる。 Verilog記述 assign xx1= !( |(clzx0[31:16])); ↓あまり良くないことが起きるかも…(通常なら、最適化されて↑になるはず) assign xx1 = !( clzx0[31:0] & 32'hFFFF0000) 1 1 1 1
  • 12.
    ctz Count TrailingZero ワード中で、LSB(最右)から0が何個 続くかを数える。 一番下(右)の1の位置がわかる。 clzと同様 Verilog記述 /// ctz wire [31:0] ctzx0;wire [15:0] ctzx1; wire [7:0] ctzx2; wire [3:0] ctzx3; wire [1:0] ctzx4; wire [5:0] m1, m2, m3, m4, m5; wire yy1,yy2,yy3,yy4; assign ctzx0[31:0] = reg_op1; assign yy1 = !( |(ctzx0[15:0])); // & 32'h0000FFFF assign ctzx1[15:0] = yy1 ? (ctzx0[31:16]) : ctzx0[15:0] ; assign m1 = (yy1 ? 32'd16 : 32'd0); assign yy2 = !(|(ctzx1[7:0])); // & 32'h000000FF assign ctzx2[7:0] = yy2 ? (ctzx1[15:8]) : ctzx1[7:0] ; assign m2 = m1 + (yy2 ? 32'd8 : 32'd0); assign yy3 = !(|(ctzx2[3:0])); // & 32'h000000F assign ctzx3[3:0] = yy3 ? (ctzx2[7:4]) : ctzx2[3:0] ; assign m3 = m2 + (yy3 ? 32'd4 : 32'd0); assign yy4 = !(|(ctzx3[1:0])); // & 32'h0000003 assign ctzx4[1:0] = yy4 ? (ctzx3[3:2]) : ctzx3[1:0] ; assign m4 = m3 + (yy4 ? 32'd2 : 32'd0); assign m5 = m4 + !ctzx4[0]; //add inverse of LSB wire [31:0] ctz_res = !(|(reg_op1)) ? 32'd32 : m5;
  • 13.
    population ワード中の 1のビットの 数を数える Verilog記述 ////population wire [31:0] popx0, popx1, popx2, popx3, popx4, pop_res; assign popx0 = reg_op1; assign popx1 = (popx0 & 32'h55555555) + ((popx0 >> 1)& 32'h55555555); assign popx2 = (popx1 & 32'h33333333) + ((popx1 >> 2)& 32'h33333333); assign popx3 = (popx2 & 32'h0f0f0f0f) + ((popx2 >> 4)& 32'h0f0f0f0f); assign popx4 = (popx3 & 32'h00ff00ff) + ((popx3 >> 8)& 32'h00ff00ff); assign pop_r = (popx4 & 32'h0000ffff) + ((popx4 >>16)& 32'h0000ffff);
  • 14.
    bit reverse ワード中のビットの順序を逆にする Verilog記述 //// bitreverse alu_brev <= {reg_op1[0],reg_op1[1],reg_op1[2],reg_op1[3], reg_op1[4],reg_op1[5],reg_op1[6],reg_op1[7], reg_op1[8],reg_op1[9],reg_op1[10],reg_op1[11], reg_op1[12],reg_op1[13],reg_op1[14],reg_op1[15], reg_op1[16],reg_op1[17],reg_op1[18],reg_op1[19], reg_op1[20],reg_op1[21],reg_op1[22],reg_op1[23], reg_op1[24],reg_op1[25],reg_op1[26],reg_op1[27], reg_op1[28],reg_op1[29],reg_op1[30],reg_op1[31] };
  • 15.
    Half Exchange,Quarter Exchange ●HaltExchange: ワードを半分に切って、その上下を入れ替え 元 3,2 , 1,0 3,2 ⇔ 1,0 結果1,0 , 3,2 ●Quarter Exchange:ハーフ・ワード内でその半分(1/4)を入れ替え 元 3,2 , 1,0 3⇔2 , 1⇔0 結果 2,3 , 0,1 Verilog記述 //// Half Exchange alu_hexg <= {reg_op1[15:0],reg_op1[31:16]}; //// Quarter Exchange alu_qexg <= {reg_op1[23:16],reg_op1[31:24],reg_op1[7:0],reg_op1[15:8]}; //// word Ex (おまけ) alu_wexg <= {reg_op1[7:0],reg_op1[15:8],reg_op1[23:16],reg_op1[31:24]}; ワード中のバイトを逆順にするには、 H-Exg と Q-Exg を行う
  • 16.