第十九章 Chisel基础——常用的硬件原语

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

一、多路选择器

因为多路选择器是一个很常用的电路模块,所以Chisel内建了几种多路选择器。第一种形式是二输入多路选择器“Mux(sel, in1, in2)”。sel是Bool类型,in1和in2的类型相同,都是Data的任意子类型。当sel为true.B时,返回in1,否则返回in2。

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

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

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

内建的多路选择器会转换成Verilog的三元操作符“? :”,这对于构建组合逻辑而言是完全足够的,而且更推荐这种做法,所以when语句常用于给寄存器赋值,而很少用来给线网赋值。读者可能习惯用always语句块来编写电路,但这存在一些问题:首先,always既可以综合出时序逻辑又能综合出组合逻辑,导致reg变量存在二义性,常常使得新手误解reg就是寄存器;其次,if...else if...else不能传播控制变量的未知态x(某些EDA工具可以),使得仿真阶段无法发现一些错误,但是assign语句会在控制变量为x时也输出x。工业级的Verilog,都是用assign语句来构建电路。时序逻辑也是通过例化触发器模块来完成的,相应的端口都是由assign来驱动,而且触发器会使用SystemVerilog的断言来寻找always语句里的x和z。整个设计应该尽量避免使用always语句。 

二、ROM

可以通过工厂方法“VecInit[T <: Data](elt0: T, elts: T*)”或“VecInit[T <: Data](elts: Seq[T])”来创建一个只读存储器,参数就是ROM里的常量数值,对应的Verilog代码就是给读取ROM的线网或寄存器赋予常量值。例如:

// rom.scala
package test

import chisel3._

class ROM extends Module {
  val io = IO(new Bundle {
    val sel = Input(UInt(2.W))
    val out = Output(UInt(8.W))  
  })

  val rom = VecInit(1.U, 2.U, 3.U, 4.U)

  io.out := rom(io.sel)
}

对应的Verilog为:

// ROM.v
module ROM(
  input        clock,
  input        reset,
  input  [1:0] io_sel,
  output [7:0] io_out
);
  wire [2:0] _GEN_1; 
  wire [2:0] _GEN_2; 
  wire [2:0] _GEN_3; 
  assign _GEN_1 = 2'h1 == io_sel ? 3'h2 : 3'h1; 
  assign _GEN_2 = 2'h2 == io_sel ? 3'h3 : _GEN_1; 
  assign _GEN_3 = 2'h3 == io_sel ? 3'h4 : _GEN_2; 
  assign io_out = {
  {5'd0}, _GEN_3};
endmodule

在这个例子里需要提的一点是,Vec[T

  • 15
    点赞
  • 38
    收藏
    觉得还不错? 一键收藏
  • 8
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值