Chisel 手册(中文part2)

12 篇文章 0 订阅
12 篇文章 0 订阅

Chisel 手册(中文part2)

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

译者:智能物联(CSDN) 

 

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)
 

 

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。所有的赋值完成,所有的电路都计算以后,如果还有前向声明还没有被赋值的话,系统将会报告错误。

 

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值。

 

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()也可以设置节点和子模块的名字。

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值