如何使 Chisel 生成带启用的触发器?
Posted
技术标签:
【中文标题】如何使 Chisel 生成带启用的触发器?【英文标题】:How to make Chisel generate flip flops with enable? 【发布时间】:2017-01-12 11:32:02 【问题描述】:我们正在实现一个处理器管道,并且想要一种有效的方法来停止它。如果我们可以控制电路,那么我们将使用具有启用输入的锁存器。要停止,只需禁用锁存器,然后它们不会在下一个时钟沿更新,它们将保持不变,直到再次启用。
但是,如何在 Chisel 中做到这一点?目前尚不清楚“何时”声明将转化为什么。对于幕后黑魔法的帮助,以及如何控制末端电路的提示将不胜感激。
【问题讨论】:
【参考方案1】:when
语句本质上只是帮助构建多路复用树的简写方式。例如:
class SimpleWhen extends Module
val io = IO(new Bundle
val cond = Input(Bool())
val a = Input(UInt(32.W))
val b = Input(UInt(32.W))
val z = Output(UInt(32.W))
)
when (io.cond) io.z := io.a
.otherwise io.z := io.b
如果我们由此生成 Verilog,我们会得到(在 Chisel 3 中):
module SimpleWhen(
input clock,
input reset,
input io_cond,
input [31:0] io_a,
input [31:0] io_b,
output [31:0] io_z
);
wire _T_11;
wire [31:0] _GEN_1;
assign io_z = _GEN_1;
assign _T_11 = io_cond == 1'h0;
assign _GEN_1 = _T_11 ? io_b : io_a;
endmodule
正如人们所预料的那样,这个模块基本上只是根据条件cond
在a
和b
之间多路复用。
关于启用寄存器的更有趣用例:
class RegEnableTest extends Module
val io = IO(new Bundle
val en = Input(Bool())
val a = Input(Valid(UInt(32.W)))
val b = Input(Valid(UInt(32.W)))
val out = Output(UInt(32.W))
)
val regNext = Wire(UInt(32.W))
val myReg = Reg(UInt(32.W))
when (io.en) myReg := regNext
regNext := myReg
when (io.a.valid) regNext := io.a.bits
when (io.b.valid) regNext := io.b.bits
io.out := myReg
这里我们有一个寄存器myReg
,它仅在输入en
为高电平时更新。生成的 Verilog 为:
module RegEnableTest(
input clock,
input reset,
input io_en,
input io_a_valid,
input [31:0] io_a_bits,
input io_b_valid,
input [31:0] io_b_bits,
output [31:0] io_out
);
wire [31:0] regNext;
reg [31:0] myReg;
reg [31:0] _GEN_1;
wire [31:0] _GEN_0;
wire [31:0] _GEN_2;
assign io_out = myReg;
assign regNext = _GEN_2;
assign _GEN_0 = io_en ? regNext : myReg;
assign _GEN_2 = io_b_valid ? io_b_bits : io_a_bits;
// ... Randomization removed for clarity ...
always @(posedge clock) begin
if (io_en) begin
myReg <= regNext;
end
end
endmodule
myReg 仅在 io.en
为高电平时更新。使用 RegEnable (https://chisel.eecs.berkeley.edu/api/index.html#chisel3.util.RegEnable$) 可以使这段代码更加简洁
【讨论】:
这些捕获了功能,但问题是关于其他的。有使能触发器的本机电路实现。在这些中,启用是通过晶体管以更紧凑、更快速和更节能的方式实现的。问题是关于强制 Chisel 在综合过程中从代工厂库中选择这些特定的电路元件。如果 Chisel 使用多路复用器实现该功能,那么综合工具将无法发现它们可以使用更好的原生电路版本。 Chisel 以这样一种方式生成 Verilog,以便综合工具 可以 推断诸如启用的触发器之类的事情。在最近的rocket-chip (DefaultConfig) 运行中,我们发现 99% 的寄存器被合成(在教育过程中)作为门控寄存器。【参考方案2】:这里是如何使用 RegEnable 来实现的,RegEnable 是构建启用寄存器的辅助对象: https://github.com/ucb-bar/chisel3/blob/master/src/main/scala/chisel3/util/Reg.scala
import chisel3._
import chisel3.util._
class RegEnableTest extends Module
val io = IO(new Bundle
val en = Input(Bool())
val a = Input(Valid(UInt(32.W)))
val b = Input(Valid(UInt(32.W)))
val out = Output(UInt(32.W))
)
val regNext = Wire(UInt(32.W))
val myReg = RegEnable(regNext,io.en)
when (io.a.valid) regNext := io.a.bits
when (io.b.valid) regNext := io.b.bits
io.out := myReg
这会产生以下verilog(经过编辑以删除不需要的宏)
module RegEnableTest(
input clock,
input reset,
input io_en,
input io_a_valid,
input [31:0] io_a_bits,
input io_b_valid,
input [31:0] io_b_bits,
output [31:0] io_out
);
wire [31:0] regNext;
reg [31:0] myReg;
reg [31:0] _GEN_1;
wire [31:0] _GEN_0;
wire [31:0] _GEN_2;
assign io_out = myReg;
assign regNext = _GEN_2;
assign _GEN_0 = io_en ? regNext : myReg;
assign _GEN_2 = io_b_valid ? io_b_bits : io_a_bits;
always @(posedge clock) begin
if (io_en) begin
myReg <= regNext;
end
end
endmodule
【讨论】:
【参考方案3】:或者你可以这样做:
val regNext = Wire(UInt(32.W))
when (io.a.valid) regNext := io.a.bits
when (io.b.valid) regNext := io.b.bits
io.out := RegEnable(regNext,io.en)
但是状态在verilog代码中没有一个好名字
assign io_out = _T_30;
assign regNext = _GEN_1;
assign _GEN_1 = io_b_valid ? io_b_bits : io_a_bits;
assign _GEN_2 = io_en ? regNext : _T_30;
always @(posedge clock) begin
if (io_en) begin
_T_30 <= regNext;
end
end
endmodule
【讨论】:
以上是关于如何使 Chisel 生成带启用的触发器?的主要内容,如果未能解决你的问题,请参考以下文章