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 Bundle | Header라는 새 집합체 타입 |
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.Uio.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, 첫 번째 선언 | 7 | 1 |
tag | 6–4 | 101 = 5 |
len, 마지막 선언 | 3–0 | 0010 = 2 |
주의. 두 변환 모두 의미를 검사하지 않습니다. 필드 순서를 바꾸면 변환된 값이 모두 달라지지만
경고는 없습니다. 레지스터 맵이나 패킷 헤더처럼 외부 형식을 기술하는 Bundle은 명세에 적힌
순서대로 필드를 선언하세요.
3. 예제: Bundle의 비트 배치
Pixel은 RGB565 픽셀입니다. 5비트 빨강, 6비트 녹색, 5비트 파랑 필드를 이 순서로 선언합니다.
PixelPack은 Pixel 입력 in을 packed로 변환하고, 평탄한 16비트 입력 raw에서 asTypeOf로
녹색 필드를 꺼냅니다. io.in.g와 되돌린 값의 .g는 논리 없이 필드 하나에 닿습니다. println은
elaboration 중에 Bundle 전체 폭을 출력합니다.
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
}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 문제에 이 개념을 적용해 보세요.
실행하기 전에 예측해 보세요.
WIDTH줄은 몇 비트를 보고할까요?- 각 필드를 최댓값으로 둔 빨강, 녹색, 파랑 픽셀의
packed값은 각각 무엇일까요? 0x1234에서 꺼낸green은 얼마일까요?
확인 문제
r = 31, g = 0, b = 0일 때 packed는 16진수로 무엇입니까?
결과 확인.
| 필드 | 비트 | 최댓값 픽셀의 packed |
|---|---|---|
r, 첫 번째 선언 | 15–11 | f800 |
g | 10–5 | 07e0 |
b, 마지막 선언 | 4–0 | 001f |
| 출력 줄 | 확인할 내용 |
|---|---|
WIDTH | 5 + 6 + 5 = 16비트입니다 |
PACK 줄 | 위 표와 같습니다. 첫 필드가 최상위에 놓입니다 |
UNPACK raw=07e0 | 녹색 비트가 모두 1이므로 green은 63입니다 |
UNPACK raw=1234 | 0001_0010_0011_0100의 비트 10–5는 010001이며 값은 17입니다 |
직접 바꿔 보세요.
- 필드 선언 순서를
b,g,r로 바꾸고 나머지는 그대로 두세요. 빨강 픽셀은001f, 파랑 픽셀은f800이 됩니다. 녹색은 여전히 가운데 6비트이므로07e0이고,UNPACK두 줄도 그대로입니다. 컴파일 오류는 없습니다. 비트 배치는 선언 순서만으로 정해집니다. 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 테이블을 인덱싱합니다 |
third | Scala 리터럴 2로 table을 인덱싱합니다 |
packed | table 전체를 asUInt로 평탄화합니다 |
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
}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까지 바꿉니다.
실행하기 전에 예측해 보세요.
sel마다picked와mask는 각각 무엇일까요?sel이 바뀌면third도 바뀔까요?packed는11223344일까요,44332211일까요?
확인 문제
table의 항목 0부터 3에 0x11, 0x22, 0x33, 0x44가 들어 있습니다. packed는 무엇을 보입니까?
결과 확인.
| 출력 줄 | 확인할 내용 |
|---|---|
SELECT 줄의 picked | 11, 22, 33, 44. 멀티플렉서가 sel을 따릅니다 |
SELECT 줄의 mask | 01, 02, 04, 80. Scala Int 네 개로 만든 상수 테이블입니다 |
FIXED의 third | 항상 33입니다. 인덱스 2는 elaboration 중에 정해졌으므로 멀티플렉서가 아니라 배선입니다 |
FIXED의 packed | 44332211입니다. 원소 0이 최하위 바이트이며, Bundle의 첫 필드와 반대입니다 |
직접 바꿔 보세요.
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을 고른 것입니다. 넓은 선택 신호는 범위 오류를 만들지 않고 다른 원소를 가리킵니다.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| 포트 | addr | ack |
|---|---|---|
IO(new Req) | Output | Input |
IO(Flipped(new Req)) | Input | Output |
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를 뒤로 전달합니다.
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
}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")
}
}
}실행하기 전에 예측해 보세요.
DIRECTION네 줄은 각각 무엇일까요?- 테스트벤치는 입력만 인가할 수 있습니다. 어느 두 필드를 인가할까요?
up.data = 255이면down.data는 얼마일까요?
확인 문제
테스트벤치는 Stage의 입력만 인가할 수 있습니다. 어느 두 필드를 인가합니까?
결과 확인.
| 필드 | 방향 | 테스트벤치 동작 |
|---|---|---|
up.data | Input. 뒤집혔습니다 | poke |
up.ready | Output. 뒤집혔습니다 | peek |
down.data | Output | peek |
down.ready | Input | poke |
첫 번째 SIMULATE 줄은 41을 보내 down.data에서 42를 받고, ready는 1로 전달됩니다. 두 번째
줄은 255를 인가하며 down.data는 0입니다. up.data + 1.U의 +가 8비트 안에서 순환하기 때문이며,
폭·SInt·산술 장의 폭 규칙과 같습니다. Link
선언 하나가 양쪽 끝에 사용되었고, 받는 쪽을 위해 방향을 거꾸로 쓴 Bundle을 따로 만들 필요가
없었습니다.
직접 바꿔 보세요.
up에서Flipped를 지우세요. 두 포트 모두data가 출력,ready가 입력이 됩니다.elaboration이up.ready := down.ready에서ChiselException과 up.ready in Stage cannot be written from module Stage로 멈춥니다.up.ready가 입력이 되었고, 모듈은 자기 입력을 구동할 수 없습니다.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.readyelaboration 예외는 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. 확인 문제
-
Pixel에서 파랑 픽셀은001f, 빨강 픽셀은f800으로 변환되는 이유는 무엇일까요? 둘을 맞바꾸는 소스 수정 하나는 무엇일까요?정답 보기
r을 먼저 선언했으므로 상위 5비트에 놓이고, 마지막에 선언한b는 하위 5비트에 놓입니다.r과b의 선언 순서를 바꾸면 두 값이 맞바뀝니다. -
VecInit(1.U(4.W), 2.U(4.W)).asUInt는 어떤 8비트 값일까요?정답 보기
0x21입니다. 원소 0인 1이 하위 4비트, 원소 1인 2가 상위 4비트에 놓입니다. -
io.table(2)는 멀티플렉서를 추가하지 않는데io.table(io.sel)은 추가하는 이유는 무엇일까요?정답 보기
2는 Scala
Int이므로 원소가elaboration중에 정해지고 결과는 배선입니다.io.sel은 시뮬레이션 중에 값이 바뀌는 하드웨어이므로 모든 원소에 대한 멀티플렉서가 필요합니다. -
모듈이
val in = IO(Flipped(new Link))를 선언했습니다. 모듈이 구동할 수 있는in의 필드는 무엇일까요?정답 보기
in.ready뿐입니다.Flipped가data를 입력으로,ready를 출력으로 바꾸고, 모듈은 출력만 구동할 수 있습니다. -
2.1절의 8비트
Header로 만든Vec(3, new Header)의 폭은 얼마일까요?정답 보기
24비트입니다. 8비트 원소가 세 개입니다.
아래 실습 문제 두 개는 비트 배치를 다룹니다. bundle 예제 아래의 문제는 명세에 필드 배치가 정해진
16비트 상태 워드를 변환하고 되돌리는데, 출력되는 두 줄이 모두 명세와 다릅니다. vec 예제 아래의
문제는 32비트 워드의 바이트 순서를 뒤집어야 하지만, 시작 코드는 모든 워드를 그대로 출력합니다.