Chisel的基本概念
1、Chisel硬件表达
此版本的Chisel只支持二进制逻辑,不支持三态信号。
2、Chisel数据类型
数据类型用于指定状态元素中保存的值或wire上传输的值。
类型有SInt,UInt,Bool,Bundle,Vcc。
Bundles&Vecs
Bundle和Vec是可以允许用户使用其他数据类型来扩展Chisel数据类型集合的类。Bundle用class来定义,用户可以通过将一个类定义为Bundle的子类来定义自己的bundle。
原始类(SInt,UInt和Bool)加上聚合类(Bundles和Vecs)都继承自一个公共的超类Data。在电路中,每个最终继承自Data的对象都可以表示为一个位向量。
3、组合电路
在Chisel中,电路会被表示为一张节点图。每个节点是具有零个或多个输入并驱动一个输出的硬件运算符。
Uint是一种退化类型的节点,它没有输入,并且在其输出上驱动一个恒定的值。创建和连接节点的一种方法是使用字面表达式。
eg. (a&b)|(c&d)
任何简单的表达式都可以直接转换成电路树,在叶子处使用命名的导线和操作符形成内部节点。表达式的电路输出取自树根处的运算符,在本示例中是按位或运算。
3、函数
我们可以定义函数来分解一个重复的逻辑,这样可以在后续设计中重复使用。
eg. def clb(a: UInt, b: UInt, c: UInt, d: UInt): UInt = (a & b) | (~c & d)
其中clb是表示以a,b,c,d为参数的函数,并返回一个布尔电路的输出。def关键字是Scala的一部分,表示引入了一个函数定义,每个语句后面跟一个冒号,然后是它的类型,函数返回类型在参数列表之后的冒号之后。(=)符号将函数参数列表与函数定义分隔开。
然后我们就可以在其他的电路中使用了:
val out = clb(a,b,c,d)
4、端口
端口用作硬件组件的接口。一个端口可以是任意的Data对象,但它是具有方向的。 Chisel提供端口构造函数,以允许在构建时给对象添加(输入或输出)。原始的端口构造函数需要将方向作为第一个参数(方向为INPUT或OUTPUT),将位数作为第二个参数(除了始终为1位的布尔值)。
eg. 端口声明如下所示
class Decoupled extends Bundle {
val ready = Bool(OUTPUT)
val data = UInt(INPUT, 32)
val valid = Bool(INPUT)
}
5、模块
我们现在可以构建电路层次,我们可以从较小的子模块开开始构建更大的模块。
Module用class来定义,继承Module。
6、状态元素
Chisel支持的状态元素的最简单形式是上升沿触发寄存器,可以实例化为:
val reg = Reg(next = in)
该电路具有输出,该输出是前一个时钟周期的输入信号产生的值。在当前版本的Chisel中,时钟和复位是全局信号,在需要时可以隐式包含。
6.1、转发声明
纯组合电路在节点之间不存在周期,如果检测到这样的周期,则Chisel将报告错误。因为它们不具有周期,所以可以总是以前馈方式构建组合电路,通过添加一些输入从已经定义的节点导出的新节点。时序电路在节点之间具有反馈,因此有时需要在生成节点被定义之前输出。因为Scala顺序执行程序语句,所以我们允许数据节点作为wire来提供节点声明,这样可以立即被使用,但其输入将稍后设置。如下例所示,在简单的CPU中,我们需要定义pcPlus4和brTarget的线,以便在定义之前引用它们:
val pcPlus4 = UInt()
val brTarget = UInt()
val pcNext = Mux(io.ctrl.pcSel, brTarget, pcPlus4)
val pcReg = Reg(next = pcNext, init = UInt(0, 32))
pcPlus4 := pcReg + UInt(4)
...
brTarget := addOut
接线操作符:=用于在pcReg和addOut定义后连接。
6.2、条件更新
(1)在前面使用到寄存器的示例中,我们简单地将组合逻辑块连接到寄存器的输入。当描述状态元素的操作时,指定何时将发生寄存器更新并且用几个单独的语句指明这些更新。Chisel以when的形式提供条件更新规则,以支持这种顺序逻辑描述的风格。
val r = Reg(init = UInt(0, 16))
when (cond) {
r := r + UInt(1)
}
其中只有在cond为真时,才在当前时钟周期的结尾更新寄存器r。
when的参数是返回Bool值。后面的更新块只能包含使用赋值运算符:=,简单表达式和用val定义的命名引线的更新语句。
(2)在条件更新序列中,条件为真的最近条件更新优先。
6.3、有限状态机
class Parity extends Module {
val io = new Bundle {
val in = Bool(dir = INPUT)
val out = Bool(dir = OUTPUT)
}
val s_even :: s_odd :: Nil = Enum(UInt(), 2)
val state = Reg(init = s_even)
when (io.in) {
when (state === s_even) { state := s_odd }
when (state === s_odd) { state := s_even }
}
io.out := (state === s_odd)
}
其中Enum(Uint(), 2)生成两个UInt数。当io.in为true时更新状态。需要注意的是,FSM的所有机制都建立在寄存器,线和条件更新的基础上。