[CSE228A] Lecture 6 - Encapsulation

YumeIroVillain·2023년 8월 10일
0

Chisel 독학

목록 보기
35/44
post-custom-banner

Lecture 6 - Encapsulation

재귀를 활용한 DelayNCycles 모듈 리팩토링

class DelayNCycles(n: Int) extends Module {
    val io = IO(new Bundle {
        val in  = Input(Bool())
        val out = Output(Bool())
    })
    require(n >= 0)
    def helper(n: Int, lastConn: Bool): Bool = {
        if (n == 0) lastConn
        else helper(n-1, RegNext(lastConn))
    }
    io.out := helper(n, io.in)
}
println(getVerilog(new DelayNCycles(2)))


반복문으로 생성한 것과, 재귀적으로 생성한 것 모두 동일한 결과를 내는 것을 확인할 수 잇다.

Scala Object

  • Singleton Object를 위해 만들어졌다.
  • 인스턴스가 단 하나만 존재하는 클래스를 위한 것.
    • Shared state
    • Stateless Function
    • Factory Methods(as companion object)
    • ...
class MyPair(val a: Int, b: Int) {
    def sum() = a + b
}
val mpc = new MyPair(3,4)
mpc.a

참고) val a를 붙이면, global이 되기에 통과하지만,
그냥 a:Int, b:Int 하면, mpc.a를 접근시 에러가 발생한다.

Counter Companion Object

class MyCounter(maxVal: Int) extends Module {
    val io = IO(new Bundle {
        val en  = Input(Bool())
        val out = Output(UInt())
    })
    val count = RegInit(0.U(log2Ceil(maxVal+1).W))
    when (io.en) {
        when (count < maxVal.U) {
            count := count + 1.U
        } .otherwise {
            count := 0.U
        }
    }
    io.out := count
}

object MyCounter {
    def apply(maxVal: Int) = new MyCounter(maxVal)
}
  • 이렇게 object 안에 apply를 넣는 것을 Factory Method라고 하는 것 같다.
  • Companion Object: just calls 생성자

MyCounter without Module

  • Notice that in this example, Class DOES NOT extends Module
class MyCounter(maxVal: Int, en: Bool) {
    val count = RegInit(0.U(log2Ceil(maxVal+1).W))
    when (en) {
        when (count < maxVal.U) {
            count := count + 1.U
        } .otherwise {
            count := 0.U
        }
    }
}

object MyCounter {
    def apply(maxVal: Int, en: Bool) = {
        val mc = new MyCounter(maxVal, en)
        mc.count
    }
}

class CounterInstMod(n: Int) extends Module {
    val io = IO(new Bundle {
        val en  = Input(Bool())
        val count = Output(UInt())
    })
    io.count := MyCounter(n, io.en)
}
println(getVerilog(new CounterInstMod(4)))
  1. new CounterInstMod(4) 호출
  2. CounterInstMod는 Module을 상속
  3. CounterInstMod는 object MyCounter를 호출
  4. object MyCounter는 new MyCounter로 class를 생성
    (apply() ==Companion Object's Factory Method)

Chisel's Counter

  • 사실, Chisel은 Counter를 지원한다.
class CounterInstMod(n: Int) extends Module {
    val io = IO(new Bundle {
        val en  = Input(Bool())
        val count = Output(UInt())
        val limit = Output(Bool())
    })
    val (value, wrap) = Counter(io.en, n)
//     val (value, wrap) = Counter(0 until n by 2, io.en)
    io.count := value
    io.limit := wrap
}
  • Counter(0 until n by 2, io.en)을 하면, 아래처럼 바뀐다
    wire [1:0] _wrap_value_T_1 = value + 2'h1; 에서
    wire [1:0] _wrap_value_T_1 = value + 2'h2; 로

Chisel Bundle

  • C의 struct마냥, type을 field name으로 aggregate함.
  • IO interface 만들어주는데, 그 이외에도 많이 사용됨.
  • 한 번 선언하고, 여러 군데에서 사용가능하다.
    • 기존 Verilog에서는, interface를 선언하지 않는 한 같은 in/out들이 consistent를 유지하면서 동일해야하는 번거로움이 있었다.
class Mag extends Bundle {
    val m = UInt(4.W)
}

class OutMod(a: Int) extends Module {
    val io = IO(Output(new Mag))
    io.m := a.U
}

println(getVerilog(new OutMod(2)))
  • 당연한 코드인데, 이렇게 짤 일이 있나 싶다.
  • new Mag는 Bundle을 상속하면서, 그저 4.W를 반환한다.
  • extend라는 것은, 상속인데, 그러므로 Mag는 Bundle이 가지는 모든 멤버변수와 멤버함수를 포함한다.

Bundle Composition

  • 그러면, Bundle을 상속하는 Mag을 상속하는 Class도 만들 수 있지 않을까?
  • 가능하다.
class Mag extends Bundle {
    val m = Output(UInt(4.W))
}

class SignMag extends Mag {
    val s = Output(Bool())
}

class PairSignMag extends Bundle {
    val nums = Vec(2, new SignMag)
}

class OutMod(a: Int, b: Int) extends Module {
    val io = IO(new PairSignMag)
    io.nums(0).m := a.U
    io.nums(0).s := false.B
    io.nums(1).m := b.U
    io.nums(1).s := false.B
}

println(getVerilog(new OutMod(3,4)))

해설

  • SignMag는 Mag의 val m까지 포함한다.
    즉, val m, val s 모두 가지고 있는 것이다.
  • PairSignMag Bundle은 SignMag 채널 2개를 input으로 가진다.
  • OutMod는, PairSignMag을 IO Port로 가진다.
    • 이 때, 방향은 이미 Mag와 SignMag에 모두 Output으로 정의되어있다.

  • 강의와 다르게, 이 블록 이후의 블록은
    내 Jupyter에서는 오류가 발생한다.

Bulk Connection <>

  • Connect an Entire Bundle
class Handshake(w: Int) extends Bundle {
    val ready = Input(Bool())
    val data  = Output(UInt(w.W))
}

class PassThru(w: Int) extends Module {
    val io = IO(new Bundle {
        val in = Flipped(new Handshake(w))
        val out = new Handshake(w)
    })
    io.in <> io.out
}

println(getVerilog(new PassThru(4)))

Scala Option[T]

  • Make clearly convey something is empty
  • None: Nothing There
  • Some(x): Something that x is there.
  • API들
    • isDefined: has something or not을 bool로 반환
    • get: 가지면 value 반환, 없으면 exception
// val o: Option[Int] = Some(4)
val o: Option[Int] = None
if (o.isDefined)
    println(o.get)
else
    println("empty")
  • Bundle의 Optional한 필드를 가져가는데 option을 사용가능.

Making Optional IOs in Chisel

  • 로컬에서는 에러가 발생해서, 스샷으로 대체
class MaybePair(w: Int, hasY: Boolean) extends Bundle {
    val x = Output(UInt(w.W))
    val y: Option[UInt] = if (hasY) Some(Output(UInt(w.W))) else None
}

class OutMod(w: Int, a: Int, useY: Boolean) extends Module {
    val io = IO(Output(new MaybePair(w, useY)))
    io.x := a.U
    if (useY)
//     if (io.y.isDefined)
        io.y.get := a.U
}

println(getVerilog(new OutMod(8,4,true)))

  • OutMod 생성자에 true가 넘어갔으니, if문을 진입하고
    get에 io_y을 넘겼으니, MaybePair로 넘어가서 y가 Some(Output(UInt...)))가 발동한다.

  • 반대로, false로 넘기면 None으로 넘어가기 때문에, io_y가 구현되지 않는다.
  • 아주 단적인 예이지만, Parameterize하기에 매우 강력한 Option이라고 한다.

Chisel Tabulate

  • Seq를 인수를 통해 만드는 다른 방법으로 이해했다.
profile
HW SW 둘다 공부하는 혼종의 넋두리 블로그 / SKKU SSE 17 / SWM 11th
post-custom-banner

0개의 댓글