Chiselでは、以下のようなビット列の一部に対する部分代入が許されない。 ハードウェア記述言語としてみると非常に不便だが、もともとChiselがソフトウェア記述言語Scalaがベースであるということを考えると何となく想像がつく。
val res = Wire(UInt(32.W)) res(idx) := true.B
ではこれ以外に部分代入を実現する方法は無いのか?
bitSet
を使う
bitSet
はある信号の特定の1ビットを0/1に設定して結果を返す。特定の1ビットのみを更新する場合に使用できる。
例えばこんな感じ。
class BitSet (WIDTH: Int) extends Module { val io = IO(new Bundle { val in = Input(UInt(WIDTH.W)) val valid = Input(Bool()) val idx = Input(UInt(log2Ceil(WIDTH).W)) val out = Output(UInt(WIDTH.W)) }) io.out := io.in when(io.valid) { io.out := io.in.bitSet(io.idx, true.B) } }
io.valid
が有効になると、io.in
のうちio.idx
で指定された1ビットのみが1にアップデートされてio.out
に返される。それ以外はio.in
がそのままio.out
に返されるという記述になる。
出力されたVerilogは以下の通り。
module BitSet( // @[:@3.2] input clock, // @[:@4.4] input reset, // @[:@5.4] input [31:0] io_in, // @[:@6.4] input io_valid, // @[:@6.4] input [4:0] io_idx, // @[:@6.4] output [31:0] io_out // @[:@6.4] ); wire [31:0] _T_15; // @[bitset.scala 18:27:@10.6] wire [31:0] _T_16; // @[bitset.scala 18:27:@11.6] assign _T_15 = 32'h1 << io_idx; // @[bitset.scala 18:27:@10.6] assign _T_16 = io_in | _T_15; // @[bitset.scala 18:27:@11.6] assign io_out = io_valid ? _T_16 : io_in; // @[bitset.scala 16:10:@8.4 bitset.scala 18:12:@16.6] endmodule
- 再代入可能な
var
変数を使う
これは若干やり方が汚いし、生成させる回路も汚い。なのであまりお勧めしないが、どうしてもこの書き方の方がScala的にきれいになる場合、この方法を使用する。
var
変数を使うことで、val
と異なり再代入を行うことができるようになる。
例えば、以下の記述は32ビット各ビットに対して特定の演算func()
を適用し、その結果を返す。
class VarLoopTest (WIDTH: Int) extends Module { val io = IO(new Bundle { val in1 = Input(UInt(WIDTH.W)) val in2 = Input(UInt(WIDTH.W)) val out = Output(UInt(WIDTH.W)) }) def func(a: Bool, b: Bool) : Bool = { return a ^ b } var res = WireInit(0.U(WIDTH.W)) for (i <- 0 until WIDTH) { res = res | (func(io.in1(i), io.in2(i)) << i) } io.out := res }
変数res
は再代入可能なので、32回ループを回しながら当該ビット位置に結果を当てはめる。
ちなみに生成されるコードはとても汚い。以下のようなVerilogが生成された。なんでこれしきのコードにこんなに長いVerilogファイルが生成されるのか悲しい気持ちになるが。。。
module VarLoopTest( // @[:@3.2] input clock, // @[:@4.4] input reset, // @[:@5.4] input [31:0] io_in1, // @[:@6.4] input [31:0] io_in2, // @[:@6.4] output [31:0] io_out // @[:@6.4] ); wire _T_14; // @[bitset.scala 35:29:@10.4] wire _T_15; // @[bitset.scala 35:40:@11.4] wire _T_16; // @[bitset.scala 31:14:@12.4] wire [31:0] _T_18; // @[bitset.scala 35:15:@14.4] wire _T_19; // @[bitset.scala 35:29:@15.4] wire _T_20; // @[bitset.scala 35:40:@16.4] wire _T_21; // @[bitset.scala 31:14:@17.4] wire [1:0] _GEN_0; // @[bitset.scala 35:45:@18.4] wire [1:0] _T_22; // @[bitset.scala 35:45:@18.4] wire [31:0] _GEN_1; // @[bitset.scala 35:15:@19.4] ... wire _T_171; // @[bitset.scala 31:14:@167.4] wire [31:0] _GEN_60; // @[bitset.scala 35:45:@168.4] wire [31:0] _T_172; // @[bitset.scala 35:45:@168.4] assign _T_14 = io_in1[0]; // @[bitset.scala 35:29:@10.4] assign _T_15 = io_in2[0]; // @[bitset.scala 35:40:@11.4] assign _T_16 = _T_14 ^ _T_15; // @[bitset.scala 31:14:@12.4] assign _T_18 = {{31'd0}, _T_16}; // @[bitset.scala 35:15:@14.4] ... assign _GEN_60 = {{31'd0}, _T_171}; // @[bitset.scala 35:45:@168.4] assign _T_172 = _GEN_60 << 31; // @[bitset.scala 35:45:@168.4] assign io_out = _T_168 | _T_172; // @[bitset.scala 38:10:@170.4] endmodule
まあ、この程度のサンプルコードであれば、以下のように書いた方がきれいだし、生成されるVerilog的にも(比較的)心の平安が保たれる(これでも生成されるコードはかなりどぎついが...)
class VarLoopTest (WIDTH: Int) extends Module { val io = IO(new Bundle { val in1 = Input(UInt(WIDTH.W)) val in2 = Input(UInt(WIDTH.W)) val out = Output(UInt(WIDTH.W)) }) def func(a: Bool, b: Bool) : Bool = { return a ^ b } var res = Wire(Vec(WIDTH, Bool())) for(i <- 0 until WIDTH) { res(i) := func(io.in1(i), io.in2(i)) } io.out := res.asUInt }