您当前的位置: 首页 > 

耐心的小黑

暂无认证

  • 2浏览

    0关注

    323博文

    0收益

  • 0浏览

    0点赞

    0打赏

    0留言

私信
关注
热门博文

chisel常用的硬件原语(更新)

耐心的小黑 发布时间:2021-06-21 15:30:17 ,浏览量:2

主体内容摘自:https://blog.csdn.net/qq_34291505/article/details/87862433

前两章介绍了基本的数据类型和硬件类型,已经足够编写基本的小规模电路。至于要如何生成Verilog,会在后续章节讲解。如果要编写大型电路,当然也可以一砖一瓦地搭建,但是费时费力,完全体现不出软件语言的优势。Chisel在语言库里定义了很多常用的硬件原语,读者可以直接导入相应的包来使用。让编译器多干活,让程序员少费力。

一、多路选择器

因为多路选择器是一个很常用的电路模块,所以Chisel内建了几种多路选择器。

  • 第一种形式是二输入多路选择器“Mux(sel, in1, in2)”。sel是Bool类型,in1和in2的类型相同,都是Data的任意子类型。当sel为true.B时,返回in1,否则返回in2。Mux在chisel3包里。

  • 因为Mux仅仅是把一个输入返回,所以Mux可以内嵌Mux,构成n输入多路选择器,类似于嵌套的三元操作符。其形式为“Mux(c1, a, Mux(c2, b, Mux(..., default)))”。第二种就是针对上述n输入多路选择器的简便写法,形式为“MuxCase(default, Array(c1 -> a, c2 -> b, ...))”,它的展开与嵌套的Mux是一样的。第一个参数是默认情况下返回的结果,第二个参数是一个数组,数组的元素是对偶“(成立条件(Bool类型),被选择的输入)”。MuxCase在chisel3.util包里。

  • 第三种是MuxCase的变体,它相当于把MuxCase的成立条件依次换成从0开始的索引值,就好像一个查找表,其形式为“MuxLookup(idx, default, Array(0.U -> a, 1.U -> b, ...))”。它的展开相当于“MuxCase(default, Array((idx === 0.U) -> a, (idx === 1.U) -> b, ...))”。MuxLookup也在chisel3.util包里。

  • 第四种多路选择器是Mux1H ,是chisel3.util包里的独热码多路选择器,它的选择信号是一个独热码。如果零个或多个选择信号有效,则行为未定义。它有以下几种常用的定义形式:

val hotValue = Mux1H(io.selector,Seq(2.U,4.U,8.U,11.U))

val hotValue = Mux1H(Seq(io.selector(0),io.selector(1),io.selector(2),io.selector(3)),Seq(2.U,4.U,8.U,11.U))

val hotValue = Mux1H(Seq(
    io.selector(0) -> 2.U,
    io.selector(1) -> 4.U,
    io.selector(2) -> 8.U,
    io.selector(3) -> 11.U
))

以上三种形式是等价的,io.selector是一个UInt类型的数据,并且位宽不能小于待选择数据的个数。在第一种形式中,Mux1H会从低到高依次将io.selector的每一位作为一个选择信号,并和提供的被选择数据一一对应。

  • 第五种多路选择器是chisel3.util包里的优先级选择器PriorityMux,当多个选择信号有效时,按照定义时的顺序,返回更靠前的被选数据。有以下三种定义形式:
val priorityValue = PriorityMux(io.selector,Seq(2.U,4.U,8.U,11.U))

val priorityValue = PriorityMux(Seq(io.selector(0),io.selector(1),
									io.selector(2),io.selector(3)),Seq(2.U,4.U,8.U,11.U))

val priorityValue = PriorityMux(Seq(
    io.selector(0) -> 2.U,
    io.selector(1) -> 4.U,
    io.selector(2) -> 8.U,
    io.selector(3) -> 11.U,
  ))

以上三种形式是等价的,io.selector是一个Bits类型的数据,位宽不小于待选择数据的个数。

内建的多路选择器会转换成Verilog的三元操作符“? :”,这对于构建组合逻辑而言是完全足够的,而且更推荐这种做法,所以when语句常用于给寄存器赋值,而很少用来给线网赋值。

读者可能习惯用always语句块来编写电路,但这存在一些问题:

  • 首先,always既可以综合出时序逻辑又能综合出组合逻辑,导致reg变量存在二义性,常常使得新手误解reg就是寄存器;
  • 其次,if…else 不能传播控制变量的未知态x(某些EDA工具可以),使得仿真阶段无法发现一些错误,但是assign语句会在控制变量为x时也输出x。

注1:

对这句话的理解是,verilog中的if是不能写成if(a==x) b=0这种形式的,即使你在testbench里面将a赋值为x,这条分支也不成立,总之这样写是不对的。正是因为这样,在仿真的时候,由于分支if(a==x)不成立,那么b就不会为0,那么你就错误的认为a不等于x,所以a的未知态就没有传播出去。

工业级的Verilog,都是用assign语句来构建电路。时序逻辑也是通过例化触发器模块来完成的,相应的端口都是由assign来驱动,而且触发器会使用SystemVerilog的断言来寻找always语句里的x和z。整个设计应该尽量避免使用always语句。

二、优先编码器

Chisel内建了两种优先编码器,它的作用是对多个输入信号中优先级最高的一个信号进行编码。

  • 第一种优先编码器是PriorityEncoder,它有两种定义形式:
PriorityEncoder("b1010".U)

PriorityEncoder(Seq(true.B, false.B, true.B, false.B))

以上两种形式是等价的,返回值类型都是UInt,值为1.U。

  • 第二种优先编码器是PriorityEncoderOH,它也有两种定义形式:
PriorityEncoderOH("b1010".U)
PriorityEncoderOH(Seq(false.B, true.B, true.B, false.B))

它和第一种编码器的区别在于该编码器会把编码结果转换成独热码。第一种形式返回一个UInt的数据2.U,第二种形式返回一个Seq:Seq(false.B, true.B, false.B, false.B)。

三、ROM

可以通过apply方法“VecInit[T

关注
打赏
1640088279
查看更多评论
0.0436s