Chisel 手册(中文part2)

Chisel 手册(中文part2)

作者:Jonathan Bachrach, Huy Vo, Krste Asanović;  EECS Department, UC Berkeley 

译者:智能物联(CSDN) 

 

6 Updateables

当描述wire和state节点的运作时,我们通常为输出端口指定一系列的条件更新,然后用若干独立语句把这些更新散播出去。举例来说,Data节点的输出可以立刻引用,但是输入可以延后设置。Updateable表示一个条件更新节点,它累积针对节点的路径,然后生成mux并对这些电路的路径进行组合逻辑运算。

 
abstract class Updateable extends Node { // conditional reads def reads: Queue[(Bool, UInt)] // conditional writes def writes: Queue[(Bool, UInt, Node)] // gen mux integrating all conditional writes def genMuxes(default: Node) override def := (x: Node): this.type 
}

Chisel提供条件更新规则,用when语句支持这类时序逻辑描述:

 
object when { def apply(cond: Bool)(block: => Unit): when 
} class when (prevCond: Bool) { def elsewhen (cond: Bool)(block: => Unit): when def otherwise (block: => Unit): Unit 
}

when语句使用动态作用域scope来操纵全局条件栈,因此,when语句生成新的条件,将在函数调用路径内一直起作用。举个例子:

 
def updateWhen (c: Bool, d: Data) = when (c) { r := d } 
when (a) { updateWhen(b, x) 
}

与下列相同:

 
when (a) { when (b) { r := x } 
}

Chisel为其他常用类型的条件更新提供一些语法糖:

 
def unless(c: Bool)(block: => Unit) = when (!c) { block )

还有

 
def otherwise(block: => Unit) = when (Bool(true)) { block }

我们介绍了switch语句,用于根据一系列普通key的比较结果进行条件更新:

 
def switch(c: UInt)(block: => Unit): Unit def is(v: Bool)(block: => Unit)

 

7 Forward Declarations

纯组合电路不允许出现节点的闭环。如果发现闭环,Chisel会报错。因为不存在闭环,所以可以生成单方向的合法的组合逻辑电路,通过添加新的节点,输入取自那些已定义的节点。时序电路具有节点之间的反馈,所以有时候必须在定义生成某个节点之前声明这个输出。因为Scala顺序执行程序语句,我们一般让数据节点作为wire立即引用,但是它们的输入延后设置。举例来说,在一个简单的CPU中,我们需要预先声明pcPlus4和brTarget这两个wires,以便后续引用:

 
val pcPlus4  = UInt() 
val brTarget = UInt() 
val pcNext   = Mux(pcSel, brTarget, pcPlus4) 
val pcReg    = RegUpdate(pcNext) 
pcPlus4     := pcReg + UInt(4) 
... 
brTarget    := addOut

连线运算符 := 用于连接pcReg与addOut。所有的赋值完成,所有的电路都计算以后,如果还有前向声明还没有被赋值的话,系统将会报告错误。

 

8 Regs

Chisel支持的最简单的状态单元是正沿触发的寄存器,如下所示:

 
object Reg { def apply[T <: Data] (data: T, next: T = null, init: T = null): T 
} object RegNext { def apply[T <: Data] (next: T, init: T = null): T 
} object RegInit { def apply[T <: Data] (init: T): T 
} class Reg extends Updateable

创建方法如下所示:

 
val r1 = RegUpdate(io.in) 
val r2 = RegReset(UInt(1, 8)) 
val r3 = RegUpdate(io.in, UInt(1)) 
val r4 = Reg(UInt(width = 8))

复位值resetVal是隐含的reset信号为真时候所取的reg值。

 

9 Mems

Chisel通过Mem语句支持RAM。写入Mem是正沿触发,读取是组合逻辑或者正沿触发。

 
object Mem { def apply[T <: Data](depth: Int, gen: => T, seqRead: Boolean = false): Mem 
} class Mem[T <: Data](gen: () => T, depth: Int, seqRead: Boolean = false) extends Updateable { def apply(idx: UInt): T 
}

Mems内部的Ports被赋值UInt的索引 。一个具有一个写端口和两个组合逻辑读端口的32-entry的寄存器组描述如下:

 
val rf = Mem(32, UInt(width = 64)) 
when (wen) { rf(waddr) := wdata } 
val dout1 = rf(waddr1) 
val dout2 = rf(waddr2)

如果设置可选参数seqRead,当Reg被赋予Mem的输出,Chisel会生成时序读取端口。一个单口读,单口写的SRAM可以描述如下:

 
val ram1r1w = Mem(1024, UInt(width = 32), seqRead = true) 
val dout = Reg(UInt()) 
when (wen) { ram1r1w(waddr) := wdata } 
when (ren) { dout := ram1r1w(raddr) }

在when语句链条中,如果读和写的条件互斥,将生成单端口的SRAM。

 
val ram1p = Mem(1024, UInt(width = 32), seqRead = true) 
val dout = Reg(UInt()) 
when (wen) { ram1p(waddr) := wdata } 
.elsewhen (ren) { dout := ram1p(raddr) }

如果同一个Mem地址在同一个clock边沿同步读写,或者如果sequential read使能被清除,读数据是用户自定义的 

Mem 也支持写入mask,以便部分字节写入。 

 
val ram = Mem(256, UInt(width = 32)) 
when (wen) { ram.write(waddr, wdata, wmask) }

 

10 Ports

端口继承自Data节点,用于硬件模块的接口。端口是原始Data对象的有方向版本。端口方向定义如下:

 
trait PortDir 
object INPUT  extends PortDir 
object OUTPUT extends PortDir

复合端口可以递归创建,使用vec或者bundle组合简单的子端口。

 

11 Modules

在Chisel中,模块Module与Verilog中的模块非常相似,模块定义了一个被生成电路的层次结构。模块的命名空间可以被下游的工具来访问,用于调试和物理布局。一个用户自定义模块被定义成一个class:

  • 继承自Module,
  • 包含一个接口Bundle,保存于一个io,
  • 在构造器中用连线连接子电路。

用户通过module的子类来写自己的模块:

 
abstract class Module { val io: Bundle var name: String = "" def compileV: Unit def compileC: Unit 
}

定义它们自己的io。下例定义了一个双输入Mux的模块:

 
class Mux2 extends Module { val io = new Bundle{ val sel = Bool(INPUT) val in0 = Bool(INPUT) val in1 = Bool(INPUT) val out = Bool(OUTPUT) } io.out := (io.sel & io.in1) | (~io.sel & io.in0) 
}

:= 赋值运算符,用于module内部,是Chisel的一个特殊运算符,连接左侧的输入到右侧的输出。典型地用于连接输出端口和它们的定义。

<> 运算符成捆连接兄弟模块之间的相反的接口,或者父子模块之间的相同的接口。成捆连接使用路径名匹配方式连接叶端口。连接只在port非无效的时候进行,允许用户重复成捆连接那些只有部分使用的接口。所有的连接完成以后电路将被优化,Chisel 如果发现端口不止一个连接,将会发出警告。

模块内部的节点和子模块的命名,由C++或者Verilog后端负责,取自模块的域名,使用Scala内审机制。使用函数setName()也可以设置节点和子模块的名字。

Published by

风君子

独自遨游何稽首 揭天掀地慰生平

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注