본문으로 건너뛰기
code0.HDL

Bundle과 Vec

이름 있는 필드는 Bundle로, 번호가 붙은 원소는 Vec으로 묶고, asUInt와 asTypeOf가 따르는 비트 배치를 읽고, Scala 인덱스와 하드웨어 인덱스를 구분하며, Flipped로 인터페이스 선언 하나를 연결의 양쪽 끝에 씁니다.

이 장에서 배울 내용

  • Bundle을 선언하고 new를 붙여 포트나 wire 타입으로 씁니다
  • 선언 순서에서 Bundle과 Vec의 변환 값을 예측하고 asTypeOf로 비트를 다시 읽습니다
  • Scala Int 인덱스와 하드웨어 UInt 인덱스가 각각 만드는 회로를 구분하고, n > 1일 때 선택 신호 폭을 log2Ceil(n)비트로 정합니다
  • Bundle 필드에 방향을 붙이고 Flipped로 뒤집으며, 모듈이 구동할 수 있는 필드를 가려냅니다
이 장에서 배우는 개념
  • Bundle과 Vec
먼저 알아야 할 개념

1. 집합체

실제 인터페이스는 숫자 하나로 끝나는 경우가 드뭅니다. 픽셀에는 색 채널이 세 개 있고, 레지스터 파일에는 항목이 여러 개 있으며, 핸드셰이크는 데이터와 응답을 서로 반대 방향으로 전달합니다. Chisel의 집합체 타입인 Bundle과 Vec은 이런 묶음을 값 하나로 기술하므로, 묶음 전체를 한 번에 선언하고 연결하고 비트로 변환할 수 있습니다. 집합체 타입은 elaboration 중에 존재하는 Scala 클래스이며, UInt(8.W)와 마찬가지로 IO, Wire, 레지스터 안에 들어가야 하드웨어가 됩니다. 폭·SInt·산술 장의 기본 타입, 폭, asUInt와 Bool·UInt와 연산자 장의 Cat, Mux·when·Wire 장의 :=와 Wire를 그대로 사용합니다.

모든 집합체는 여전히 비트 벡터입니다. Bundle은 벡터의 구간에 이름을 붙이고, Vec은 번호를 붙입니다.

집합체원소접근asUInt 배치
Bundle이름 있는 필드. 필드마다 타입이 다를 수 있습니다b.field. elaboration 중에 정해집니다먼저 선언한 필드가 최상위 비트
Vec(n, t)같은 타입 t의 원소 n개Scala Int로 v(i), 하드웨어 UInt로 v(idx)원소 0이 최하위 비트

Bundle, Vec, UInt, SInt, Bool은 모두 Data의 하위 클래스입니다. 따라서 Data를 받는 구문은 집합체도 받습니다. 집합체는 자유롭게 중첩할 수 있어서 Bundle 안에 Vec을 두거나, Bundle을 원소로 하는 Vec을 만들 수 있습니다.

2. Bundle

2.1 Bundle 정의

Bundle은 Bundle을 상속한 Scala 클래스입니다. 필드마다 Chisel 타입을 담은 val을 선언합니다.

형식

class 번들_이름 extends Bundle {
  val 필드1 = Chisel_타입
  val 필드2 = Chisel_타입
}
class Header extends Bundle {
  val valid = Bool()       // 1비트
  val tag   = UInt(3.W)    // 3비트
  val len   = UInt(4.W)    // 4비트
}
코드의미
class Header extends BundleHeader라는 새 집합체 타입
val valid = Bool()타입이 Bool인 필드 valid
필드 순서val을 선언한 순서가 비트 배치를 정합니다. 2.3절에서 확인합니다

Header의 폭은 1 + 3 + 4 = 8비트입니다. UInt(8.W)와 마찬가지로 이 클래스는 타입만 기술하며, 결합하기 전에는 회로에 아무것도 생기지 않습니다.

참고. Chisel 3.5부터 매개변수가 있는 Bundle에 필요한 복제 코드를 컴파일러 플러그인이 생성합니다. cloneType을 직접 작성하지 마세요.

2.2 Bundle을 포트와 wire로 사용하기

Bundle은 new로 만들고, Chisel 타입이 필요한 곳 어디에나 사용합니다. UInt 같은 내장 타입은 companion object가 apply를 제공하므로 new가 필요 없지만, 직접 정의한 클래스에는 new가 필요합니다.

형식

Input(new 번들_이름)
Output(new 번들_이름)
Wire(new 번들_이름)
val io = IO(new Bundle {
  val hdr  = Input(new Header)
  val bits = Output(UInt(8.W))
})
val h = Wire(new Header)
h.valid := true.B
h.tag   := 5.U
h.len   := 2.U

io.hdr.tag, h.len 같은 필드 접근은 Scala 멤버 접근이므로 논리를 만들지 않습니다. Input(new Header)는 hdr의 모든 필드를 입력으로 만들므로, 테스트벤치는 필드를 하나씩 인가합니다.

2.3 비트로 변환하고 되돌리기

asUInt는 필드를 선언 순서대로 이어 붙이며 첫 필드를 최상위에 둡니다. Cat과 같은 규칙입니다. asTypeOf는 평탄한 값을 같은 배치로 다시 읽습니다.

형식

번들_값.asUInt                  // Bundle을 비트로
비트_값.asTypeOf(new 번들_이름)   // 비트를 Bundle로
h.asUInt                           // valid=1, tag=5, len=2: 1_101_0010 = 0xd2
"hd2".U.asTypeOf(new Header).tag   // 5
필드변환된 값에서의 비트0xd2에서의 값
valid, 첫 번째 선언71
tag6–4101 = 5
len, 마지막 선언3–00010 = 2

주의. 두 변환 모두 의미를 검사하지 않습니다. 필드 순서를 바꾸면 변환된 값이 모두 달라지지만 경고는 없습니다. 레지스터 맵이나 패킷 헤더처럼 외부 형식을 기술하는 Bundle은 명세에 적힌 순서대로 필드를 선언하세요.

3. 예제: Bundle의 비트 배치

Pixel은 RGB565 픽셀입니다. 5비트 빨강, 6비트 녹색, 5비트 파랑 필드를 이 순서로 선언합니다. PixelPack은 Pixel 입력 in을 packed로 변환하고, 평탄한 16비트 입력 raw에서 asTypeOf로 녹색 필드를 꺼냅니다. io.in.g와 되돌린 값의 .g는 논리 없이 필드 하나에 닿습니다. println은 elaboration 중에 Bundle 전체 폭을 출력합니다.

Pixel.scala
import chisel3._

class Pixel extends Bundle {
  val r = UInt(5.W)
  val g = UInt(6.W)
  val b = UInt(5.W)
}

class PixelPack extends Module {
  val io = IO(new Bundle {
    val in     = Input(new Pixel)
    val packed = Output(UInt(16.W))
    val raw    = Input(UInt(16.W))
    val green  = Output(UInt(6.W))
  })
  println(s"WIDTH Pixel=${(new Pixel).getWidth}")

  // 먼저 선언한 필드가 최상위 비트에 놓인다.
  io.packed := io.in.asUInt
  // asTypeOf는 같은 비트를 Bundle 배치에 따라 다시 읽는다.
  io.green := io.raw.asTypeOf(new Pixel).g
}
Tb.scala
import chisel3._
import chiseltest._
import chiseltest.RawTester.test


object Main extends App {
  test(new PixelPack) { dut =>
    for ((r, g, b) <- Seq((31, 0, 0), (0, 63, 0), (0, 0, 31))) {
      dut.io.in.r.poke(r.U)
      dut.io.in.g.poke(g.U)
      dut.io.in.b.poke(b.U)
      println(f"PACK r=$r g=$g b=$b packed=${dut.io.packed.peek().litValue}%04x")
    }
    for (raw <- Seq(0x07e0, 0x1234)) {
      dut.io.raw.poke(raw.U)
      println(f"UNPACK raw=$raw%04x green=${dut.io.green.peek().litValue}")
    }
  }
}

이 개념 실습하기

다음 단계로 짧은 Chisel 문제에 이 개념을 적용해 보세요.

실행하기 전에 예측해 보세요.

  1. WIDTH 줄은 몇 비트를 보고할까요?
  2. 각 필드를 최댓값으로 둔 빨강, 녹색, 파랑 픽셀의 packed 값은 각각 무엇일까요?
  3. 0x1234에서 꺼낸 green은 얼마일까요?

확인 문제

r = 31, g = 0, b = 0일 때 packed는 16진수로 무엇입니까?

결과 확인.

필드비트최댓값 픽셀의 packed
r, 첫 번째 선언15–11f800
g10–507e0
b, 마지막 선언4–0001f
출력 줄확인할 내용
WIDTH5 + 6 + 5 = 16비트입니다
PACK 줄위 표와 같습니다. 첫 필드가 최상위에 놓입니다
UNPACK raw=07e0녹색 비트가 모두 1이므로 green은 63입니다
UNPACK raw=12340001_0010_0011_0100의 비트 10–5는 010001이며 값은 17입니다

직접 바꿔 보세요.

  1. 필드 선언 순서를 b, g, r로 바꾸고 나머지는 그대로 두세요. 빨강 픽셀은 001f, 파랑 픽셀은 f800이 됩니다. 녹색은 여전히 가운데 6비트이므로 07e0이고, UNPACK 두 줄도 그대로입니다. 컴파일 오류는 없습니다. 비트 배치는 선언 순서만으로 정해집니다.
  2. Tb.scala에서 한 줄에 r = 1, g = 1, b = 1을 인가해 보세요. 실행하기 전에 각 1이 필드의 최하위 비트인 비트 11, 5, 0에 놓인다고 계산하면 packed는 0821입니다.

4. Vec

4.1 Vec 타입

Vec은 같은 타입의 원소 n개를 담습니다.

형식

Vec(개수, 원소_타입)
val io = IO(new Bundle {
  val regs  = Input(Vec(4, UInt(8.W)))   // 8비트 값 네 개, 모두 32비트
  val flags = Output(Vec(8, Bool()))     // 개별 Bool 출력 여덟 개
})

원소는 모두 타입과 폭이 같습니다. Bundle을 원소로 하는 Vec도 만들 수 있으며, 16비트 픽셀 Bundle 두 개로 이루어진 Vec의 폭은 32비트입니다. 원소마다 폭이 달라야 하는 드문 경우에는 chisel3.util.MixedVec을 사용합니다.

4.2 VecInit: 하드웨어 값으로 만든 Vec

Vec(n, t)는 타입입니다. VecInit은 이미 있는 하드웨어 값으로 Vec 하드웨어 값을 만들며, 상수 조회 테이블을 작성할 때 사용합니다.

형식

VecInit(값0, 값1, ...)
VecInit(하드웨어_값의_Scala_시퀀스)
VecInit.fill(n)(값)
val pair  = VecInit(1.U(4.W), 2.U(4.W))
pair.asUInt                                 // 0x21: 원소 0이 하위 4비트
val fives = VecInit.fill(3)(5.U(4.W))
fives.asUInt                                // 0x555
val table = VecInit(Seq(3, 1, 4).map(_.U(8.W)))

VecInit(Seq(...).map(_.U(8.W)))는 elaboration 중에 Scala Int 목록을 하드웨어 테이블로 변환합니다. 비트 배치 순서는 Bundle과 반대입니다. 배열의 0번 원소가 가장 낮은 주소에 있는 것처럼 원소 0이 최하위 비트에 놓입니다.

4.3 Scala 인덱스와 하드웨어 인덱스

인덱스의 종류에 따라 만들어지는 회로가 달라집니다.

인덱스결정 시점만들어지는 회로범위를 벗어난 인덱스
Scala Int, 예: v(2)elaboration원소 하나로 가는 배선elaboration 예외
하드웨어 UInt, 예: v(sel)시뮬레이션모든 원소에 대한 멀티플렉서하위 비트만 사용
io.out0 := io.regs(0)        // 배선만 생김
io.outS := io.regs(io.sel)   // 4입력 멀티플렉서. sel은 2비트여야 함

n > 1이면 하드웨어 인덱스의 폭은 log2(n)을 올림한 log2Ceil(n)비트로 정하세요. 더 넓으면 Chisel이 경고를 출력하고 그 폭의 하위 비트만 사용합니다. n이 2의 거듭제곱이 아니면 그 비트 폭 안에도 원소를 가리키지 않는 값이 있으므로, 인덱스를 n 미만으로 제한해야 합니다.

5. 예제: Vec, VecInit, 인덱싱

VecPick의 입력은 8비트 항목 네 개로 이루어진 Vec table과 2비트 선택 신호 sel입니다. 출력 네 개를 차례로 읽으세요.

출력구성
picked하드웨어 sel로 table을 인덱싱합니다
mask같은 sel로 상수 VecInit 테이블을 인덱싱합니다
thirdScala 리터럴 2로 table을 인덱싱합니다
packedtable 전체를 asUInt로 평탄화합니다
VecPick.scala
import chisel3._

class VecPick extends Module {
  val io = IO(new Bundle {
    val table  = Input(Vec(4, UInt(8.W)))
    val sel    = Input(UInt(2.W))
    val picked = Output(UInt(8.W))
    val mask   = Output(UInt(8.W))
    val third  = Output(UInt(8.W))
    val packed = Output(UInt(32.W))
  })

  // 하드웨어 인덱스는 네 원소 전체에 대한 멀티플렉서를 만든다.
  io.picked := io.table(io.sel)
  // VecInit은 Scala 상수를 하드웨어 조회 테이블로 변환한다.
  val masks = VecInit(Seq(0x01, 0x02, 0x04, 0x80).map(_.U(8.W)))
  io.mask := masks(io.sel)
  // Scala Int 인덱스는 elaboration 중에 원소 하나를 선택한다.
  io.third := io.table(2)
  // 원소 0이 최하위 바이트에 놓인다.
  io.packed := io.table.asUInt
}
Tb.scala
import chisel3._
import chiseltest._
import chiseltest.RawTester.test


object Main extends App {
  test(new VecPick) { dut =>
    for ((v, i) <- Seq(0x11, 0x22, 0x33, 0x44).zipWithIndex) {
      dut.io.table(i).poke(v.U)
    }
    for (sel <- 0 until 4) {
      dut.io.sel.poke(sel.U)
      val picked = dut.io.picked.peek().litValue
      val mask = dut.io.mask.peek().litValue
      println(f"SELECT sel=$sel picked=$picked%02x mask=$mask%02x")
    }
    println(f"FIXED third=${dut.io.third.peek().litValue}%02x packed=${dut.io.packed.peek().litValue}%08x")
  }
}

이 개념 실습하기

다음 단계로 짧은 Chisel 문제에 이 개념을 적용해 보세요.

테스트벤치는 항목 0–3에 0x11, 0x22, 0x33, 0x44를 쓰고 sel을 0부터 3까지 바꿉니다.

실행하기 전에 예측해 보세요.

  1. sel마다 picked와 mask는 각각 무엇일까요?
  2. sel이 바뀌면 third도 바뀔까요?
  3. packed는 11223344일까요, 44332211일까요?

확인 문제

table의 항목 0부터 3에 0x11, 0x22, 0x33, 0x44가 들어 있습니다. packed는 무엇을 보입니까?

답

결과 확인.

출력 줄확인할 내용
SELECT 줄의 picked11, 22, 33, 44. 멀티플렉서가 sel을 따릅니다
SELECT 줄의 mask01, 02, 04, 80. Scala Int 네 개로 만든 상수 테이블입니다
FIXED의 third항상 33입니다. 인덱스 2는 elaboration 중에 정해졌으므로 멀티플렉서가 아니라 배선입니다
FIXED의 packed44332211입니다. 원소 0이 최하위 바이트이며, Bundle의 첫 필드와 반대입니다

직접 바꿔 보세요.

  1. sel을 UInt(3.W)로 넓히고 5를 인가하세요. elaboration 중에 Dynamic index with width 3 is too wide for Vec of size 4 (expected index width 2)라는 W004 경고가 동적 인덱스마다 한 번씩 출력되고 실행은 계속됩니다. sel = 5이면 picked는 22, mask는 02입니다. 101의 하위 2비트만 사용되어 원소 1을 고른 것입니다. 넓은 선택 신호는 범위 오류를 만들지 않고 다른 원소를 가리킵니다.
  2. io.table(2)를 io.table(4)로 바꾸세요. Scala 인덱스는 회로를 구성할 때 검사되므로 elaboration이 IndexOutOfBoundsException: 4 is out of bounds (min 0, max 3)로 멈춥니다.

6. 포트 방향과 Flipped

6.1 필드의 방향

Input과 Output은 타입 전체를 감쌀 수도 있고, Bundle 안의 필드를 하나씩 감쌀 수도 있습니다. 필드에 방향을 붙이면 Bundle 하나로 양방향 인터페이스를 기술할 수 있습니다.

형식

class 인터페이스_이름 extends Bundle {
  val 필드1 = Output(Chisel_타입)
  val 필드2 = Input(Chisel_타입)
}
class Req extends Bundle {
  val addr = Output(UInt(8.W))   // 요청하는 쪽에서 나감
  val ack  = Input(Bool())       // 요청하는 쪽으로 들어옴
}

방향은 한쪽, 여기서는 요청하는 쪽을 기준으로 씁니다. IO(...)가 Bundle을 모듈 경계에 결합할 때 방향이 적용됩니다.

6.2 Flipped

Flipped(t)는 t 안의 모든 방향을 재귀적으로 뒤집습니다. 그러면 선언 하나를 연결의 양쪽 끝에서 함께 사용할 수 있습니다.

형식

IO(new 인터페이스_이름)            // 선언한 방향 그대로
IO(Flipped(new 인터페이스_이름))   // 모든 방향을 뒤집음
val toMem   = IO(new Req)            // addr: Output, ack: Input
val fromCpu = IO(Flipped(new Req))   // addr: Input,  ack: Output
포트addrack
IO(new Req)OutputInput
IO(Flipped(new Req))InputOutput

6.3 모듈이 구동할 수 있는 포트

모듈은 자기 출력을 구동하고 입력을 읽습니다. 반대는 허용되지 않습니다. 모듈 안에서 입력 포트에 연결하면 elaboration 중에 cannot be written from module 예외가 발생합니다. Flipped는 어떤 필드가 입력인지를 바꾸므로, 허용되는 연결도 함께 바꿉니다.

7. 예제: Flipped와 포트 방향

Link는 단순한 핸드셰이크의 한쪽을 기술합니다. data는 나가고 ready는 들어옵니다. Stage는 Link를 두 번 사용합니다. 다음 단계를 향하는 down은 선언한 방향 그대로이고, 이전 단계를 향하는 up은 Flipped(new Link)입니다. println 반복문은 DataMirror.directionOf로 각 필드의 실제 방향을 출력합니다. 회로는 data에 1을 더해 앞으로 전달하고 ready를 뒤로 전달합니다.

Stage.scala
import chisel3._
import chisel3.reflect.DataMirror

class Link extends Bundle {
  val data  = Output(UInt(8.W))
  val ready = Input(Bool())
}

class Stage extends Module {
  // Flipped는 Link의 모든 Output을 Input으로, Input을 Output으로 바꾼다.
  val up   = IO(Flipped(new Link))
  val down = IO(new Link)
  for ((name, port) <- Seq("up.data" -> up.data, "up.ready" -> up.ready,
                           "down.data" -> down.data, "down.ready" -> down.ready)) {
    println(s"DIRECTION $name = ${DataMirror.directionOf(port)}")
  }

  down.data := up.data + 1.U
  up.ready  := down.ready
}
Tb.scala
import chisel3._
import chiseltest._
import chiseltest.RawTester.test


object Main extends App {
  test(new Stage) { dut =>
    for ((data, ready) <- Seq((41, true), (255, false))) {
      dut.up.data.poke(data.U)
      dut.down.ready.poke(ready.B)
      val out = dut.down.data.peek().litValue
      val back = dut.up.ready.peek().litValue
      println(s"SIMULATE up.data=$data down.ready=$ready down.data=$out up.ready=$back")
    }
  }
}

실행하기 전에 예측해 보세요.

  1. DIRECTION 네 줄은 각각 무엇일까요?
  2. 테스트벤치는 입력만 인가할 수 있습니다. 어느 두 필드를 인가할까요?
  3. up.data = 255이면 down.data는 얼마일까요?

확인 문제

테스트벤치는 Stage의 입력만 인가할 수 있습니다. 어느 두 필드를 인가합니까?

답

결과 확인.

필드방향테스트벤치 동작
up.dataInput. 뒤집혔습니다poke
up.readyOutput. 뒤집혔습니다peek
down.dataOutputpeek
down.readyInputpoke

첫 번째 SIMULATE 줄은 41을 보내 down.data에서 42를 받고, ready는 1로 전달됩니다. 두 번째 줄은 255를 인가하며 down.data는 0입니다. up.data + 1.U의 +가 8비트 안에서 순환하기 때문이며, 폭·SInt·산술 장의 폭 규칙과 같습니다. Link 선언 하나가 양쪽 끝에 사용되었고, 받는 쪽을 위해 방향을 거꾸로 쓴 Bundle을 따로 만들 필요가 없었습니다.

직접 바꿔 보세요.

  1. up에서 Flipped를 지우세요. 두 포트 모두 data가 출력, ready가 입력이 됩니다. elaboration이 up.ready := down.ready에서 ChiselException과 up.ready in Stage cannot be written from module Stage로 멈춥니다. up.ready가 입력이 되었고, 모듈은 자기 입력을 구동할 수 없습니다.
  2. Flipped를 유지하고 up.data := 0.U를 추가하세요. 같은 예외가 이번에는 up.data를 지목합니다. 방향은 필드마다 검사되므로, 메시지는 방향 때문에 연결할 수 없는 필드를 정확히 알려 줍니다.

8. 자주 하는 실수

8.1 직접 정의한 Bundle에 new 빠뜨리기

// 잘못된 코드: Pixel은 값이 아니라 클래스
val in = Input(Pixel)

// 바른 코드
val in = Input(new Pixel)

Scala 컴파일러는 not found: value Pixel을 보고합니다. UInt(8.W) 같은 내장 타입에는 new가 필요 없지만, 직접 정의한 Bundle에는 필요합니다.

8.2 필드를 잘못된 순서로 선언하기

// 잘못된 코드: 상위 2비트가 kind인 형식인데 payload가 최상위에 놓임
class Packet extends Bundle {
  val payload = UInt(6.W)
  val kind    = UInt(2.W)
}

// 바른 코드: 최상위 필드부터 선언
class Packet extends Bundle {
  val kind    = UInt(2.W)
  val payload = UInt(6.W)
}

두 코드 모두 컴파일되고 폭도 8비트로 같습니다. 변환된 값만 다르므로, 실수는 링크 반대편에서 틀린 데이터로 드러납니다.

8.3 Vec이 Bundle처럼 배치되리라 기대하기

VecInit(1.U(4.W), 2.U(4.W)).asUInt   // 0x21: 원소 0이 하위
Cat(1.U(4.W), 2.U(4.W))              // 0x12: 첫 인자가 상위

Vec은 원소 0을 하위 비트에 두고, Bundle과 Cat은 첫 항목을 상위 비트에 둡니다. 변환된 값의 바이트 순서가 뒤집혀 보이면 둘 가운데 무엇을 썼는지 확인하세요.

8.4 너무 넓은 선택 신호 쓰기

// 잘못된 코드: 원소 4개인 Vec에 3비트. 인덱스 5가 경고 없이 원소 1을 고름
val sel = Input(UInt(3.W))

// 바른 코드: log2(4) = 2비트
val sel = Input(UInt(2.W))

elaboration은 W004 경고를 출력하고 계속 진행합니다. 이 경고는 버그로 취급하세요.

8.5 받는 쪽에 Flipped 빠뜨리기

// 잘못된 코드: up.ready가 입력이므로 모듈이 구동할 수 없음
val up = IO(new Link)
up.ready := down.ready

// 바른 코드
val up = IO(Flipped(new Link))
up.ready := down.ready

elaboration 예외는 cannot be written from module과 함께 필드 이름을 알려 줍니다. 그 포트를 뒤집어야 하는지 확인하세요.

9. 정리

주제핵심
Bundle이름 있는 필드를 가진 Scala 클래스입니다. 필드 타입은 무엇이든 되며 new로 만듭니다
Bundle 배치asUInt는 먼저 선언한 필드를 최상위에 두고, asTypeOf는 비트를 다시 읽습니다
Vec(n, t)같은 타입의 원소 n개입니다. 원소 0이 하위 비트에 놓입니다
VecInit상수 테이블 같은 Vec 하드웨어 값을 만듭니다
인덱스Scala Int 인덱스는 elaboration에 정해지는 배선이고, 하드웨어 인덱스는 멀티플렉서를 만듭니다
방향필드의 Input/Output이 양방향 인터페이스를 만들고, Flipped가 모두 뒤집습니다
구동모듈은 출력을 구동하고 입력을 읽습니다. 입력을 구동하면 elaboration 오류입니다

10. 확인 문제

  1. Pixel에서 파랑 픽셀은 001f, 빨강 픽셀은 f800으로 변환되는 이유는 무엇일까요? 둘을 맞바꾸는 소스 수정 하나는 무엇일까요?

    정답 보기

    r을 먼저 선언했으므로 상위 5비트에 놓이고, 마지막에 선언한 b는 하위 5비트에 놓입니다. r과 b의 선언 순서를 바꾸면 두 값이 맞바뀝니다.

  2. VecInit(1.U(4.W), 2.U(4.W)).asUInt는 어떤 8비트 값일까요?

    정답 보기

    0x21입니다. 원소 0인 1이 하위 4비트, 원소 1인 2가 상위 4비트에 놓입니다.

  3. io.table(2)는 멀티플렉서를 추가하지 않는데 io.table(io.sel)은 추가하는 이유는 무엇일까요?

    정답 보기

    2는 Scala Int이므로 원소가 elaboration 중에 정해지고 결과는 배선입니다. io.sel은 시뮬레이션 중에 값이 바뀌는 하드웨어이므로 모든 원소에 대한 멀티플렉서가 필요합니다.

  4. 모듈이 val in = IO(Flipped(new Link))를 선언했습니다. 모듈이 구동할 수 있는 in의 필드는 무엇일까요?

    정답 보기

    in.ready뿐입니다. Flipped가 data를 입력으로, ready를 출력으로 바꾸고, 모듈은 출력만 구동할 수 있습니다.

  5. 2.1절의 8비트 Header로 만든 Vec(3, new Header)의 폭은 얼마일까요?

    정답 보기

    24비트입니다. 8비트 원소가 세 개입니다.

아래 실습 문제 두 개는 비트 배치를 다룹니다. bundle 예제 아래의 문제는 명세에 필드 배치가 정해진 16비트 상태 워드를 변환하고 되돌리는데, 출력되는 두 줄이 모두 명세와 다릅니다. vec 예제 아래의 문제는 32비트 워드의 바이트 순서를 뒤집어야 하지만, 시작 코드는 모든 워드를 그대로 출력합니다.

확인 지점

예제를 실행해 예측과 비교합니다

너무 빠르게 느껴졌다면

참고 자료

참고한 자료의 라이선스와 고지