本章讲解的内容比较繁杂,没有一个统一的中心思想。这些问题与实际编程没有太大关系,但是读者需要稍微留意。
一、动态命名模块
Chisel可以动态定义模块的名字,也就是转成Verilog时的模块名不使用定义的类名,而是使用重写的desiredName方法的返回字符串。模块和黑盒都适用。例如:
class Coffee extends BlackBox {
val io = IO(new Bundle {
val I = Input(UInt(32.W))
val O = Output(UInt(32.W))
})
override def desiredName = "Tea"
}
class Salt extends Module {
val io = IO(new Bundle {})
val drink = Module(new Coffee)
override def desiredName = "SodiumMonochloride"
}
对应的Verilog为:
module SodiumMonochloride(
input clock,
input reset
);
wire [31:0] drink_O;
wire [31:0] drink_I;
Tea drink (
.O(drink_O),
.I(drink_I)
);
assign drink_I = 32'h0;
endmodule
二、动态修改端口
Chisel通过引入Scala的Boolean参数、可选值以及if语句可以创建出可选的端口,在例化该模块时可以通过控制Boolean入参来生成不同的端口。例如:
class ModuleWithOptionalIOs(flag: Boolean) extends Module {
val io = IO(new Bundle {
val in = Input(UInt(12.W))
val out = Output(UInt(12.W))
val out2 = if (flag) Some(Output(UInt(12.W))) else None
})
io.out := io.in
if(flag) {
io.out2.get := io.in
}
}
注意,端口应该包装成可选值,这样不需要端口时就能用对象None代替,编译出来的Verilog就不会生成这个端口。在给可选端口赋值时,应该先用可选值的get方法把端口解放出来。这里也体现了可选值语法的便利性。
三、生成正确的块内信号名
一般情况下,在when、withClockAndReset等语句块里定义的信号(线网和寄存器),转换成Verilog时不会生成正确的变量名。例如:
// name.scala
package test
import chisel3._
class TestMod extends Module {
val io = IO(new Bundle {
val a = Input(Bool())
val b = Output(UInt(4.W))
})
when (io.a) {
val innerReg = RegInit(5.U(4.W))
innerReg := innerReg + 1.U
io.b := innerReg
} .otherwise {
io.b := 10.U
}
}