您当前的位置: 首页 > 

耐心的小黑

暂无认证

  • 1浏览

    0关注

    323博文

    0收益

  • 0浏览

    0点赞

    0打赏

    0留言

私信
关注
热门博文

chisel使用自定义/标准库中的函数简化设计(更新)

耐心的小黑 发布时间:2021-06-21 20:01:51 ,浏览量:1

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

函数是编程语言的常用语法,即使是Verilog这样的硬件描述语言,也会用函数来构建组合逻辑。对于Chisel这样的高级语言,函数的使用更加方便,还能节省不少代码量。不管是用户自己写的函数、Chisel语言库里的函数还是Scala标准库里的函数,都能帮助用户节省构建电路的时间。

零、scala函数基础 1、普通函数
// No inputs or outputs (two versions).
def hello1(): Unit = print("Hello!")
def hello2 = print("Hello again!")

// Math operation: one input and one output.
def times2(x: Int): Int = 2 * x

// Inputs can have default values, and explicitly specifying the return type is optional.
// Note that we recommend specifying the return types to avoid surprises/bugs.
def timesN(x: Int, n: Int = 2) = n * x

// Call the functions listed above.
hello1()
hello2
times2(4)
timesN(4)         // no need to specify n to use the default value
timesN(4, 3)      // argument order is the same as the order where the function was defined
timesN(n=7, x=2)  // arguments may be reordered and assigned to explicitly

Hello!Hello again! defined function hello1 defined function hello2 defined function times2 defined function timesN res2_6: Int = 8 res2_7: Int = 8 res2_8: Int = 12 res2_9: Int = 14

2、函数字面量

Scala中的函数是一等对象。这意味着我们可以将一个函数分配给一个Val,并将它作为参数传递给类、对象或其他函数。

// These are normal functions.
def plus1funct(x: Int): Int = x + 1
def times2funct(x: Int): Int = x * 2

// These are functions as vals.
// The first one explicitly specifies the return type.
val plus1val: Int => Int = x => x + 1
val times2val = (x: Int) => x * 2

// Calling both looks the same.
plus1funct(4)
plus1val(4)
plus1funct(x=4)
//plus1val(x=4) // this doesn't work

defined function plus1funct defined function times2funct plus1val: Int => Int = ammonite. s e s s . c m d 3 sess.cmd3 sess.cmd3HelperKaTeX parse error: Can't use function '$' in math mode at position 7: Lambda$̲3224/925602942@…Lambda$3225/1395281840@1c22db5a res3_4: Int = 5 res3_5: Int = 5 res3_6: Int = 5

为什么要创建一个Val而不是def?因为使用Val,可以将该函数传递给其他函数,如下所示。您甚至可以创建接受其他函数作为参数的自己的函数。形式上,接受或产生函数的函数称为高阶函数。

3、高阶函数
// create our function
val plus1 = (x: Int) => x + 1
val times2 = (x: Int) => x * 2

// pass it to map, a list function
val myList = List(1, 2, 5, 9)
val myListPlus = myList.map(plus1)
val myListTimes = myList.map(times2)

// create a custom function, which performs an operation on X N times using recursion
def opN(x: Int, n: Int, op: Int => Int): Int = {
  if (n  Int = ammonite.
     
      
       
        
         s
        
        
         e
        
        
         s
        
        
         s
        
        
         .
        
        
         c
        
        
         m
        
        
         d
        
        
         4
        
       
       
        sess.cmd4
       
      
     sess.cmd4HelperKaTeX parse error: Can't use function '$' in math mode at position 7: Lambda$̲3249/997984377@…Lambda$3250/1596149926@bf801a7 myList: List[Int] = List(1, 2, 5, 9) myListPlus: List[Int] = List(2, 3, 6, 10) myListTimes: List[Int] = List(2, 4, 10, 18) defined function opN res4_6: Int = 10 res4_7: Int = 56

当使用没有参数的函数时,1/2两种情况可能会出现混淆的情况。如下所示:

import scala.util.Random

// both x and y call the nextInt function, but x is evaluated immediately and y is a function
val x = Random.nextInt
def y = Random.nextInt

// x was previously evaluated, so it is a constant
println(s"x = $x")
println(s"x = $x")

// y is a function and gets reevaluated at each call, thus these produce different results
println(s"y = $y")
println(s"y = $y")

x = 1775160696 x = 1775160696 y = 804455125 y = 958584765

可以看到,x其实就是一个常量,是调用Random.nextInt返回的一个常量;而y是你定义的一个函数,该函数的函数体就是执行Random.nextInt这个函数,所以每次调用y都会执行Random.nextInt,也就会出现不一样的结果。

4、匿名函数

顾名思义,匿名函数是匿名的。如果我们只使用它一次,就没有必要为函数创建Val。

val myList = List(5, 6, 7, 8)

// add one to every item in the list using an anonymous function
// arguments get passed to the underscore variable
// these all do the same thing
myList.map( (x:Int) => x + 1 )
myList.map(_ + 1)

// a common situation is to use case statements within an anonymous function
val myAnyList = List(1, 2, "3", 4L, myList)
myAnyList.map {
  case (_:Int|_:Long) => "Number"
  case _:String => "String"
  case _ => "error"
}

myList: List[Int] = List(5, 6, 7, 8) res6_1: List[Int] = List(6, 7, 8, 9) res6_2: List[Int] = List(6, 7, 8, 9) myAnyList: List[Any] = List(1, 2, “3”, 4L, List(5, 6, 7, 8)) res6_4: List[String] = List(“Number”, “Number”, “String”, “Number”, “error”)

一、用自定义函数抽象组合逻辑

与Verilog一样,对于频繁使用的组合逻辑电路,可以定义成Scala的函数形式,然后通过函数调用的方式来使用它。这些函数既可以定义在某个单例对象里,供多个模块重复使用,也可以直接定义在电路模块里。例如:

// function.scala
import chisel3._
class UseFunc extends Module {
  val io = IO(new Bundle {
    val in = Input(UInt(4.W))
    val out1 = Output(Bool())
    val out2 = Output(Bool())
  })
  def clb(a: UInt, b: UInt, c: UInt, d: UInt): UInt =
    (a & b) | (~c & d)
  io.out1 := clb(io.in(0), io.in(1), io.in(2), io.in(3))
  io.out2 := clb(io.in(0), io.in(2), io.in(3), io.in(1))
}
二、用工厂方法简化模块的例化

在Scala里,往往在类的伴生对象里定义一个工厂方法,来简化类的实例化。同样,Chisel的模块也是Scala的类,也可以在其伴生对象里定义工厂方法来简化例化、连线模块。例如用双输入多路选择器构建四输入多路选择器:

// mux4.scala
import chisel3._
class Mux2 extends Module {
  val io = IO(new Bundle {
    val sel = Input(UInt(1.W))
    val in0 = Input(UInt(1.W))
    val in1 = Input(UInt(1.W))
    val out = Output(UInt(1.W))
  })
  io.out := (io.sel & io.in1) | (~io.sel & io.in0)
}
object Mux2 {
  def apply(sel: UInt, in0: UInt, in1: UInt) = {
    val m = Module(new Mux2)
    m.io.in0 := in0
    m.io.in1 := in1
    m.io.sel := sel
    m.io.out
  }
}
class Mux4 extends Module {
  val io = IO(new Bundle {
    val sel = Input(UInt(2.W))
    val in0 = Input(UInt(1.W))
    val in1 = Input(UInt(1.W))
    val in2 = Input(UInt(1.W))
    val in3 = Input(UInt(1.W))
    val out = Output(UInt(1.W))
  })
  io.out := Mux2(io.sel(1),
                 Mux2(io.sel(0), io.in0, io.in1),
                 Mux2(io.sel(0), io.in2, io.in3))
}

注:

其实就是把例化模块和端口连接的代码放入了伴生对象的apply方法里,这样可以被重复使用。当然也可以只定义一个普通函数,不使用伴生对象。函数的输入输出其实就是待使用的模块的输入输出。

三、用Scala的函数简化代码

Scala的函数也能在Chisel里使用,只要能通过Firrtl编译器的检查。比如在生成长的序列上,利用Scala的函数就能减少大量的代码。

假设要构建一个译码器:

  • 在Verilog里需要写多条case语句,当n很大时就会使代码显得冗长而枯燥。
  • 利用Scala的for、yield组合可以产生相应的判断条件与输出结果的序列,再用zip函数将两个序列组成一个对偶序列,再把对偶序列作为MuxCase的参数,就能用几行代码构造出任意位数的译码器。例如:
// decoder.scala
package decoder
 
import chisel3._
import chisel3.util._
import chisel3.experimental._
 
class Decoder(n: Int) extends RawModule {
  val io = IO(new Bundle {
    val sel = Input(UInt(n.W))
    val out = Output(UInt((1             
关注
打赏
1640088279
查看更多评论
0.0394s