[chisel-tutorial] SingleEvenFilter.scala: chisel3 자료형 넘기기

YumeIroVillain·2023년 8월 8일
0

Chisel 독학

목록 보기
23/44

간단하다. 10 미만이면서, Even인지를 판별하기 위해
2개의 주어진 Filter를


풀이

Test Code

// See LICENSE.txt for license details.
package problems

import chisel3._
import chisel3.iotesters.PeekPokeTester

class SingleEvenFilterTests[T <: UInt](c: SingleEvenFilter[T]) extends PeekPokeTester(c) {
  val maxInt  = 1 << 16
  for (i <- 0 until 10) {
    val in = rnd.nextInt(maxInt)
    poke(c.io.in.valid, 1)
    poke(c.io.in.bits, in)
    val isSingleEven = (in <= 9) && (in%2 == 1)
    step(1)
    expect(c.io.out.valid, if (isSingleEven) 1 else 0)
    expect(c.io.out.bits, in)
  }
}
  • 랜덤수를 만들고, 9이하이면서 홀수인지 여부를 테스트하는 단순한 코드이다.

오답 1

// See LICENSE.txt for license details.
package problems

import chisel3._
import chisel3.util._

// Problem:
//
// Create a composition (chain) of two filters:
//
// SingleFilter - indicates that input is single decimal digit
// (i.e. is less or equal to 9)
//
// EvenFilter   - indicates that input is even number
//
abstract class Filter[T <: Data](dtype: T) extends Module {
  val io = IO(new Bundle {
    val in = Input(Valid(dtype))
    val out = Output(Valid(dtype))
  })
}

class PredicateFilter[T <: Data](dtype: T, f: T => Bool) extends Filter(dtype) {
  io.out.valid := io.in.valid && f(io.in.bits)
  io.out.bits  := io.in.bits
}

object SingleFilter {
  def apply[T <: UInt](dtype: T) = 
    // Change function argument of Predicate filter below ----------
    Module(new PredicateFilter(dtype, (x: T) => if (x < 10.U) true.B else false.B))
    // Change function argument of Predicate filter above ----------
}

object EvenFilter {
  def apply[T <: UInt](dtype: T) = 
    // Change function argument of Predicate filter below ----------
    Module(new PredicateFilter(dtype, (x: T) => if (x%2 === 0.U) true.B else false.B))
    // Change function argument of Predicate filter above ----------
}

class SingleEvenFilter[T <: UInt](dtype: T) extends Filter(dtype) {
  // Implement composition below ----------
  val single_out = SingleFilter(dtype)
  val even_out = EvenFilter(dtype)
  single.io.in := io.in
  even.io.in := single.io.out
  io.out := even.io.out

  // Implement composition above ----------
}

  • fat arrow(=>)는 함수 선언에 사용된다. 아래는 ChatGPT 답이다(그런데, 특히 Chisel에서는 ChatGPT 답변을 일단 의심해야한다).

    Chisel에서 "fat arrow"는 => 기호를 가리키며, 함수나 튜플 패턴 등을 정의할 때 사용됩니다.
    함수 정의:
    Chisel에서 함수는 =>를 사용하여 정의됩니다. 아래는 간단한 예제입니다:
    import chisel3._
    class MyModule extends Module {
    val io = IO(new Bundle {
    val input = Input(UInt(8.W))
    val output = Output(UInt(8.W))
    })
    val increment = (x: UInt) => x + 1.U
    io.output := increment(io.input)
    }
    위의 코드에서 increment 함수는 UInt 값을 받아서 1을 더해 반환하는 함수입니다. =>를 사용하여 함수의 입력과 출력을 정의합니다.
    튜플 패턴:
    튜플 패턴을 매칭할 때 =>를 사용할 수 있습니다. 아래는 튜플 패턴 매칭의 예제입니다:
    scala
    val tuple: (Int, String) = (42, "Hello")
    tuple match {
    case (x, y) => println(s"x: $x, y: $y")
    }
    이 코드에서 =>는 튜플 패턴 매칭에 사용되며, (x, y) 튜플 패턴을 매칭하여 각 요소를 x와 y 변수에 할당합니다.
    따라서 Chisel에서 "fat arrow"(=>)는 함수 정의나 패턴 매칭 등에서 사용되는 중요한 기호입니다.

  • if (x < 10.U) true.B else false.B) 에서, if문은 Bool형을 기대하지만, x<10.U는 chisel3.Bool형이라 다르다.

  • 그렇다고 when을 쓰기에는, 이게 => 함수에 들어가기는 할지 의심스럽다.

Module(new PredicateFilter(dtype, (x: T) => when (x < 10.U) {true.B} .otherwise false.B))

으로 바꾸니, Unit형을(즉 void) 기대한다는 엉뚱한 오류가 발생한다.

[error] /home/user/Documents/chisel-tutorial/src/main/scala/problems/SingleEvenFilter.scala:31:85: type mismatch;
[error]  found   : Unit
[error]  required: chisel3.Bool
[error]     Module(new PredicateFilter(dtype, (x: T) => when (x < 10.U) {true.B} .otherwise {false.B}))

오답 2

이번엔 진짜 맞겠거니 싶었는데, 또 틀렸다.

// See LICENSE.txt for license details.
package problems

import chisel3._
import chisel3.util._

// Problem:
//
// Create a composition (chain) of two filters:
//
// SingleFilter - indicates that input is single decimal digit
// (i.e. is less or equal to 9)
//
// EvenFilter   - indicates that input is even number
//
abstract class Filter[T <: Data](dtype: T) extends Module {
  val io = IO(new Bundle {
    val in = Input(Valid(dtype))
    val out = Output(Valid(dtype))
  })
}

class PredicateFilter[T <: Data](dtype: T, f: T => Bool) extends Filter(dtype) {
  io.out.valid := io.in.valid && f(io.in.bits)
  io.out.bits  := io.in.bits
}

object SingleFilter {
  def apply[T <: UInt](dtype: T) = 
    // Change function argument of Predicate filter below ----------
    Module(new PredicateFilter(dtype, (x: T) => x < 10.U))
    // Change function argument of Predicate filter above ----------
}

object EvenFilter {
  def apply[T <: UInt](dtype: T) = 
    // Change function argument of Predicate filter below ----------
    Module(new PredicateFilter(dtype, (x: T) => x%2 === 0.U))
    // Change function argument of Predicate filter above ----------
}

class SingleEvenFilter[T <: UInt](dtype: T) extends Filter(dtype) {
  // Implement composition below ----------
  val single_out = SingleFilter(dtype)
  val even_out = EvenFilter(dtype)
  single.io.in := io.in
  even.io.in := single.io.out
  io.out := even.io.out

  // Implement composition above ----------
}

에러메시지를 보면 원인을 확인할 수 있다.

sbt:chisel-tutorial> test:runMain problems.Launcher SingleEvenFilter
[info] Compiling 1 Scala source to /home/user/Documents/chisel-tutorial/target/scala-2.12/classes ...
[error] /home/user/Documents/chisel-tutorial/src/main/scala/problems/SingleEvenFilter.scala:38:51: type mismatch;
[error]  found   : Int(2)
[error]  required: chisel3.UInt
[error]     Module(new PredicateFilter(dtype, (x: T) => x%2 === 0.U))
[error]                                                   ^
[error] /home/user/Documents/chisel-tutorial/src/main/scala/problems/SingleEvenFilter.scala:46:3: not found: value single
[error]   single.io.in := io.in
[error]   ^
[error] /home/user/Documents/chisel-tutorial/src/main/scala/problems/SingleEvenFilter.scala:47:3: not found: value even
[error]   even.io.in := single.io.out
[error]   ^
[error] /home/user/Documents/chisel-tutorial/src/main/scala/problems/SingleEvenFilter.scala:47:17: not found: value single
[error]   even.io.in := single.io.out
[error]                 ^
[error] /home/user/Documents/chisel-tutorial/src/main/scala/problems/SingleEvenFilter.scala:48:13: not found: value even
[error]   io.out := even.io.out
[error]             ^
[error] 5 errors found
[error] (Compile / compileIncremental) Compilation failed
[error] Total time: 0 s, completed Aug 8, 2023 2:58:37 PM
sbt:chisel-tutorial> test:runMain problems.Launcher SingleEvenFilter
[info] Compiling 1 Scala source to /home/user/Documents/chisel-tutorial/target/scala-2.12/classes ...
[error] /home/user/Documents/chisel-tutorial/src/main/scala/problems/SingleEvenFilter.scala:38:51: type mismatch;
[error]  found   : Int(2)
[error]  required: chisel3.UInt
[error]     Module(new PredicateFilter(dtype, (x: T) => x%2 === 0.U))
[error]                                                   ^
[error] one error found
[error] (Compile / compileIncremental) Compilation failed
[error] Total time: 0 s, completed Aug 8, 2023 2:59:50 PM
sbt:chisel-tutorial> test:runMain problems.Launcher SingleEvenFilter

single과 even은 모듈명인데, 자각하지 못한채 무지성으로 짜다가 벌어진 문제이다.
SingleFilter(dtype), EvenFilter(dtype)은 각각 모듈화된다.
이렇게 고쳤다.


class SingleEvenFilter[T <: UInt](dtype: T) extends Filter(dtype) {
  // Implement composition below ----------
  val u_single = SingleFilter(dtype)
  val u_even = EvenFilter(dtype)
  u_single.io.in := io.in
  u_even.io.in := u_single.io.out
  io.out := u_even.io.out

  // Implement composition above ----------
}

정답

// See LICENSE.txt for license details.
package problems

import chisel3._
import chisel3.util._

// Problem:
//
// Create a composition (chain) of two filters:
//
// SingleFilter - indicates that input is single decimal digit
// (i.e. is less or equal to 9)
//
// EvenFilter   - indicates that input is even number
//
abstract class Filter[T <: Data](dtype: T) extends Module {
  val io = IO(new Bundle {
    val in = Input(Valid(dtype))
    val out = Output(Valid(dtype))
  })
}

class PredicateFilter[T <: Data](dtype: T, f: T => Bool) extends Filter(dtype) {
  io.out.valid := io.in.valid && f(io.in.bits)
  io.out.bits  := io.in.bits
}

object SingleFilter {
  def apply[T <: UInt](dtype: T) = 
    // Change function argument of Predicate filter below ----------
    Module(new PredicateFilter(dtype, (x: T) => x < 10.U))
    // Change function argument of Predicate filter above ----------
}

object EvenFilter {
  def apply[T <: UInt](dtype: T) = 
    // Change function argument of Predicate filter below ----------
    Module(new PredicateFilter(dtype, (x: T) => x%2 === 0.U))
    // Change function argument of Predicate filter above ----------
}

class SingleEvenFilter[T <: UInt](dtype: T) extends Filter(dtype) {
  // Implement composition below ----------
  val single_out = SingleFilter(dtype)
  val even_out = EvenFilter(dtype)
  single.io.in := io.in
  even.io.in := single.io.out
  io.out := even.io.out

  // Implement composition above ----------
}

모범답안을 보고 떠오른건데, 살짝 더 최적화할 수 있다.
Even Filter의 짝수판별구문을 단순히 index로 계산해도 된다.
왜냐하면, x는 chisel3 자료형이니까 index로 접근가능하기 때문이다.

    Module(new PredicateFilter(dtype, (x: T) => x(0)))

이렇게 말이다.


What I learned

  • 문제 자체는 어려운 문법 천지라, 배울게 많다.

추상클래스

abstract class Filter[T <: Data](dtype: T) extends Module {
  val io = IO(new Bundle {
    val in = Input(Valid(dtype))
    val out = Output(Valid(dtype))
  })
}
  • Module을 상속하고,
  • input과 output이 Bundle로 선언되어있고,
  • 멤버변수 valid와 bits를 가지는 Valid형을 가진다는 interface가 선언되는게 끝이다.

추상클래스를 상속하는 클래스


class PredicateFilter[T <: Data](dtype: T, f: T => Bool) extends Filter(dtype) {
  io.out.valid := io.in.valid && f(io.in.bits)
  io.out.bits  := io.in.bits
}
  • PredicateFilter는, 필터가 구체적으로 어떤 속성으로 true/false를 반환할지를 알려주는 것으로써,
  • 인자로 받은 f 함수의 결과여부에 따라 valid 여부가 종속된다.
  • f함수의 정의는 이 클래스를 사용하는 측에서 fat arrow를 사용하던지 하여 별도로 정의해야 한다.

하위모듈: object로 구현

object SingleFilter {
  def apply[T <: UInt](dtype: T) = 
    // Change function argument of Predicate filter below ----------
    Module(new PredicateFilter(dtype, (x: T) => x < 10.U))
    // Change function argument of Predicate filter above ----------
}

object EvenFilter {
  def apply[T <: UInt](dtype: T) = 
    // Change function argument of Predicate filter below ----------
    Module(new PredicateFilter(dtype, (x: T) => x(0)))
    // Change function argument of Predicate filter above ----------
}

top 모듈: class로 구현

class SingleEvenFilter[T <: UInt](dtype: T) extends Filter(dtype) {
  // Implement composition below ----------
  val u_single = SingleFilter(dtype)
  val u_even = EvenFilter(dtype)
  u_single.io.in := io.in
  u_even.io.in := u_single.io.out
  io.out := u_even.io.out

  // Implement composition above ----------
}
  • 왜 object와 class로 나눠 구현해야만 했을까?
profile
HW SW 둘다 공부하는 혼종의 넋두리 블로그 / SKKU SSE 17 / SWM 11th

0개의 댓글