Chisel 手册(中文part1)

Chisel 手册(part1)

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

译者:智能物联(优快云)   

 

1 简介

本文为Chisel手册 (Constructing Hardware In a Scala Embedded Language)。Chisel是一种内置于scala高级编程语言之上的硬件构建语言。我们还提供了一个单独的Chisel Tutorial作为简单的入门课程文档,用于早期阅读。本手册对Chisel语言给出了全面的概述和定义,Chisel实际上仅仅是包括特殊定义的class类,预定义的对象以及scala的使用习惯的一个集合。当你在写Chisel程序的时候,你实际上写的是Scala程序。本手册假定你已经基本掌握Scala语言。如果你尚不熟悉如果使用Scala,我们推荐你查阅优秀的Scala书籍 ([3], [2]).

 

2 节点(Nodes)

Chisel编写的任何硬件设计最终能用一个节点(node)对象的图表(graph)来表示。Chisel用户代码生成节点的图表,然后传给Chisel后端,翻译成Verilog或者C++ 代码。节点定义如下所示:

 
class Node { 
  // name assigned by user or from introspection 
  var name: String = "" 
  // incoming graph edges 
  def inputs: ArrayBuffer[Node] 
  // outgoing graph edges 
  def consumers: ArrayBuffer[Node] 
  // node specific width inference 
  def inferWidth: Int 
  // get width immediately inferrable 
  def getWidth: Int 
  // get first raw node 
  def getRawNode: Node 
  // convert to raw bits 
  def toBits: Bits 
  // convert to raw bits 
  def fromBits(x: Bits): this.type 
  // return lit value if inferrable else null 
  def litOf: Lit 
  // return value of lit if litOf is non null 
  def litValue(default: BigInt = BigInt(-1)): BigInt 
}

节点的最上面层级参见图1。节点的基本类别如下:

Lit

– 常数或文字

Op

– 逻辑或者算数运算符

Updateable

– 条件更新节点

Data

– typed wires 或者 ports,

Reg

– 正沿触发寄存器

Mem

– 存储器


pict 

图1: 节点层次结构


Lits

Raw literals 作为 Lit nodes 定义如下:

 
class Lit extends Node { 
  // original value 
  val inputVal: BigInt 
}
 

Raw literals为bits的集合。用户并不直接创建raw literals,而是使用type constructors,参见Section 5.

 

Ops

Raw operations 作为Op nodes 定义如下:

 
class Op extends Node { 
  // op name used during emission 
  val op: String 
}
 

Ops运算操作符运算输入的组合函数。

 

Types

一个能够表示硬件设计的Chisel图表包括raw和type节点。Chisel type系统独立于Scala type系统,单独维护,type节点与raw节点杂处,以便Chisel检查和对应到Chisel types。硬件设计被转换成 C++ 或者 Verilog代码以后,Chisel type节点被全部删除。Node基类定义的getRawNode运算符,跳过type nodes,返回第一个raw node。图 2 展示了内建Chisel type层次结构,Data为最顶层的节点(node)


pict 

图2:Chisel type层次结构


内建scalar类型包括Bool,SInt,UInt;内建集合(aggregate)类型Bundle和Vec方便用户扩展其他的Chisel数据类型。

Data本身是一个节点:

 
abstract class Data extends Node { 
  override def cloneType(): this.type = 
    this.getClass.newInstance. 
      asInstanceOf[this.type] 
  // simple conversions 
  def toSInt: SInt 
  def toUInt: UInt 
  def toBool: Bool 
  def toBits: Bits 
  // flatten out to leaves of tree 
  def flatten: Array[(String, Data)] 
  // port direction if leaf 
  def dir: PortDir 
  // change dir to OUTPUT 
  def asOutput: this.type 
  // change dir to INPUT 
  def asInput: this.type 
  // change polarity of dir 
  def flip: this.type 
  // assign to input 
  def :=[T <: Data](t: T) 
  // bulk assign to input 
  def <>(t: Data) 
}
 

Data类拥有用于type转换的method,代理port method到输入。我们将在第10节讨论port。最后,为了反映克隆所必要的构造参数,用户可以在自己的type节点(比如bundle)里面重载cloneType方法。

Data节点有四种用法:

  • types – UInt(width = 8) – 记录图表的中间态types,使用最小的比特宽度(本节所描述),
  • wires – UInt(width = 8) – 数据的前向声明,可以条件更新 (第6节描述),
  • ports – UInt(dir = OUTPUT, width = 8) – module接口专用wire,另加direction属性(参见第十节),
  • literals – UInt(1) or UInt(1, 8) – 使用type对象构造器生成,指定值和可选的位宽。

5.1 Bits

在Chisel中,Bits的原始集合可以如下定义:

 
object Bits { 
  def apply(dir: PortDir = null, 
            width: Int = -1): Bits 
  // create literal from BigInt or Int 
  def apply(value: BigInt, width: Int = -1): Bits 
  // create literal from String using 
  // base_char digit+ string format 
  def apply(value: String, width: Int = -1): Bits 
} 
 
class Bits extends Data with Updateable { 
  // bitwise-not 
  def unary_~(): Bits 
  // bitwise-and 
  def &  (b: Bits): Bits 
  // bitwise-or 
  def |  (b: Bits): Bits 
  // bitwise-xor 
  def ^  (b: Bits): Bits 
  // and-reduction 
  def andR(): Bool 
  // or-reduction 
  def orR():  Bool 
  // xor-reduction 
  def xorR():  Bool 
  // logical NOT 
  def unary_!(): Bool 
  // logical AND 
  def && (b: Bool): Bool 
  // logical OR 
  def || (b: Bool): Bool 
  // equality 
  def ===(b: Bits): Bool 
  // inequality 
  def != (b: Bits): Bool 
  // logical left shift 
  def << (b: UInt): Bits 
  // logical right shift 
  def >> (b: UInt): Bits 
  // concatenate 
  def ## (b: Bits): Bits 
  // extract single bit, LSB is 0 
  def apply(x: Int): Bits 
  // extract bit field from end to start bit pos 
  def apply(hi: Int, lo: Int): Bits 
} 
 
def Cat[T <: Data](elt: T, elts: T*): Bits
 

Bits拥有简单比特操作的method。请注意## 为二进制串接操作,而Cat为任意进制串接操作。为了避免与Scala的内建双联等于号==起冲突,Chisel的按比特比较使用三联等于号===。

宽度为n的比特可以使用单比特和Fill函数生成:

 
def Fill(n: Int, field: Bits): Bits
    

两个输入使用Mux来选择:

 
def Mux[T <: Data](sel: Bits, cons: T, alt: T): T
 

Constant或者literal值,使用Scala整数或者字符串传递给类型构造器来表示:

 
UInt(1)       // decimal 1-bit lit from Scala Int. 
UInt("ha")    // hex 4-bit lit from string. 
UInt("o12")   // octal 4-bit lit from string. 
UInt("b1010") // binary 4-bit lit from string.
 

生成Lit,参见 图3 中最左边的子图

运算符操作返回一个实际的运算符节点,包括一个组合了所有输入type节点的type节点。参见图3,查看连续的更复杂例子。


 

pictpictpict
a = UInt(1)b = a & UInt(2)b | UInt(3)
 

 

图 3: Chisel Op/Lit 图表


5.2 Bools

布尔值可以定义为 Bools:

 
object Bool { 
  def apply(dir: PortDir = null): Bool 
  // create literal 
  def apply(value: Boolean): Bool 
} 
 
class Bool extends UInt
 

Bool 等同于 UInt(width = 1).

 

5.3 Nums

Num是定义算数运算的type节点:

 
class Num extends Bits { 
  // Negation 
  def unary_-(): Bits 
  // Addition 
  def +(b: Num): Num 
  // Subtraction 
  def -(b: Num): Num 
  // Multiplication 
  def *(b: Num): Num 
  // Greater than 
  def >(b: Num): Bool 
  // Less than 
  def <(b: Num): Bool 
  // Less than or equal 
  def <=(b: Num): Bool 
  // Greater than or equal 
  def >=(b: Num): Bool 
}
 

有符号和无符号整数是定点数的子集,分别用SInt和UInt来表示:

 
object SInt { 
  def apply (dir: PortDir = null, 
             width: Int = -1): SInt 
  // create literal 
  def apply (value: BigInt, width: Int = -1): SInt 
  def apply (value: String, width: Int = -1): SInt 
} 
 
class SInt extends Num 
 
object UInt { 
  def apply(dir: PortDir = null, 
            width: Int = -1): UInt 
  // create literal 
  def apply(value: BigInt, width: Int = -1): UInt 
  def apply(value: String, width: Int = -1): UInt 
} 
 
class UInt extends Num { 
  // arithmetic right shift 
  override def >> (b: UInt): SInt 
}
 

有符号定点数,包括整数,使用补码表示。

 

5.4 Bundles

Bundles可以把若干可能不同的数据类型组合在一起,很类似C中的struct:

 
class Bundle extends Data { 
  // shallow named bundle elements 
  def elements: ArrayBuffer[(String, Data)] 
}
 

Bundle中每个元素的名称和类型通过element方法获取,flatten method返回嵌套aggregate末端的元素。用户可以通过扩展子类来定义新的bundle:

 
class MyFloat extends Bundle { 
  val sign        = Bool() 
  val exponent    = UInt(width = 8) 
  val significand = UInt(width = 23) 
}
 

 Bundle元素访问使用Scala的域field:

 
val x  = new MyFloat() 
val xs = x.sign
 

C++ 或者Verilog后端内对bundle元素的命名,取自其bundle的域名,使用Scala introspection。

 

5.5 Vecs

Vecs 生成可索引的元素矢量:

 
object Vec { 
  def apply[T <: Data](elts: Seq[T]): Vec[T] 
  def apply[T <: Data](elt0: T, elts: T*): Vec[T] 
  def fill[T <: Data](n: Int)(gen: => T): Vec[T] 
  def tabulate[T <: Data](n: Int) 
        (gen: (Int) => T): Vec[T] 
  def tabulate[T <: Data](n1: Int, n2: Int) 
        (gen: (Int, Int) => T): Vec[Vec[T]] 
} 
 
class Vec[T <: Data](n: Int, val gen: () => T) 
    extends Data { 
  def apply(idx: UInt): T 
  def apply(idx: Int): T 
  def forall(p: T => Bool): Bool 
  def exists(p: T => Bool): Bool 
  def contains[T <: Bits](x: T): Bool 
  def count(p: T => Bool): UInt 
  def indexWhere(p: T => Bool): UInt 
  def lastIndexWhere(p: T => Bool): UInt 
}
 

with n elements of type defined with the gen thunk. Users can access elements statically with an Int index or dynamically using a UInt index, where dynamic access creates a virtual type node (representing a read “port”) that records the read using the given address. In either case, users can wire to the result of a read as follows:

 
v(a) := d
 

只读存储可以表示成literal的矢量:

 
val rom = Vec(UInt(3), UInt(7), UInt(4), UInt(0)) { UInt(width=3) } 
val dout = rom(addr)
 

5.6 位宽推定

用户必须设置端口和寄存器的位宽,但是,节点的位宽是自动推定的,除非由用户手动设置(使用Extract 或者 Cat)。位宽推定引擎从图表的输入端口开始计算节点的输出位宽,按照下列表格所列的规则:

operationbit width
z = x + ywz = max(wx, wy)
z = x - ywz = max(wx, wy)
z = x & ywz = max(wx, wy)
z = Mux(c, x, y)wz = max(wx, wy)
z = w * ywz = wx + wy
z = x << nwz = wx + maxNum(n)
z = x >> nwz = wx - minNum(n)
z = Cat(x, y)wz = wx + wy
z = Fill(n, x)wz = wx * maxNum(n)
 


wz为wire z的位宽,规则适用于所有按比特逻辑操作。

位宽推定持续,直到位宽不再变化。除了向右移位常数个位移,位宽推定规则导致的输出位宽不可能小于输入位宽,因此,输出位宽要么变大,要么保持不变。更进一步,寄存器的宽度必须由用户显式指定,或者通过reset值的位宽指定。通过这两个要求,我们能够确保位宽推定过程将收敛导一个固定的位置。

& 操作不应该返回输入位宽的min()?

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值