Lecture 4 - Sequential Circuit
Registers
- Reg(type)로 명시적 선언해야함
Chisel은 반드시 명시적 Reg 선언을 요구한다는 점에서 Verilog와 다르다.
Verilog의 경우, Register는 reg로 선언해도 물리적으로 구현될수도 있고, 아닐수도 있음. 그것과 대조된다.
- Clock과 Reset은 묵시적임. 굳이 언급 안해도 된다는 말.
- Multi-clock(얘 혼자 딴 클럭 쓰게끔 설계하는거)이랑, Async Reset도 다 하는 방법이 있음. 나중에 공부하게 될 것이다.
- Register는 단순한 Block이다. time에 대한 semantic은 없다.
단순히 input과 output만 명시하면 됨.
class RegLand extends Module {
val io = IO(new Bundle {
val in = Input(Bool())
val en = Input(Bool())
val out = Output(Bool())
})
val r = Reg(Bool())
// val r = RegInit(0.B)
r := io.in
io.out := r
// io.out := RegNext(io.in, 0.B)
// io.out := RegEnable(io.in, 0.B, io.en)
}
println(getVerilog(new RegLand))
- 각 경우, Verilog로 어떻게 elaborate되는지 차이점을 비교분석할 것이다.
Reg(type): reset 없는 깡통 Register만 선언
module RegLand(
input clock,
input reset,
input io_in,
input io_en,
output io_out
);
reg r;
assign io_out = r;
always @(posedge clock) begin
r <= io_in;
end
endmodule
- 보다시피, top은 reset을 받지만 reg는 reset을 수행하지 않음
RegInit(init): Set Initial Value
module RegLand(
input clock,
input reset,
input io_in,
input io_en,
output io_out
);
`ifdef RANDOMIZE_REG_INIT
reg [31:0] _RAND_0;
`endif // RANDOMIZE_REG_INIT
reg r; // @[cmd7.sc 8:20]
assign io_out = r; // @[cmd7.sc 10:12]
always @(posedge clock) begin
if (reset) begin // @[cmd7.sc 8:20]
r <= 1'h0; // @[cmd7.sc 8:20]
end else begin
r <= io_in; // @[cmd7.sc 9:7]
end
- 보다시피, reset이 생겼고 sync reset을 수행함.
- 1 Cycle동안 Signal을 Delay 시키는데 사용됨
- input을 Same line에 Connect함.
실험결과
- 좌는 Reg(Bool())만 선언했을 때
- 우는 RegNext(io.in, 0.B)를 설정했을 때
- 엄...1 Cycle 늦추는 차이는 없는데..?
RegEnable(next, en): Write enable for when to update
Done elaborating.
module RegLand(
input clock,
input reset,
input io_in,
input io_en,
output io_out
);
`ifdef RANDOMIZE_REG_INIT
reg [31:0] _RAND_0;
`endif
reg r_1;
assign io_out = r_1;
always @(posedge clock) begin
if (reset) begin
r_1 <= 1'h0;
end else if (io_en) begin
r_1 <= io_in;
end
end
...
- 보다시피, io_en 신호가 register에 들어간다.
참고) Chisel은 Latch를 지원하지 않는다. 그러나 Blackbox라는 것으로 "굳이" 필요하면, instantiate 할 수 있다고 한다.
MyCounter 예제
Class MyCounter
Mux 사용버전
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))
val nextVal = Mux(count < maxVal.U, count + 1.U, 0.U)
count := Mux(io.en, nextVal, count)
io.out := count
}
println(getVerilog(new MyCounter(15)))
- log2Ceil을 통해, 동적으로 width를 지정할 수 있다.
when 사용버전
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
}
println(getVerilog(new MyCounter(15)))
숏코딩 버전
class MyCounter(maxVal: Int) extends Module {
val io = IO(new Bundle {
val en = Input(Bool())
val out = Output(UInt(log2Ceil(maxVal+1).W))
})
io.out := RegEnable(Mux(io.out < maxVal.U, io.out + 1.U, 0.U), 0.U, io.en)
}
println(getVerilog(new MyCounter(15)))
- RegEnable을 통해, Reg 선언도 겸할 수 있다.
test(new MyCounter(3)){ c => ... }
test(new MyCounter(3)) { c =>
c.io.en.poke(1.B)
c.io.out.expect(0.U)
c.clock.step()
c.io.en.poke(1.B)
c.io.out.expect(1.U)
c.clock.step()
c.io.en.poke(1.B)
c.io.out.expect(2.U)
c.clock.step()
c.io.en.poke(0.B)
c.io.out.expect(3.U)
c.clock.step()
c.io.en.poke(1.B)
c.io.out.expect(3.U)
c.clock.step()
c.io.en.poke(1.B)
c.io.out.expect(0.U)
c.clock.step()
}
결과
Chisel Enum
Enum이란?
- Enum은 Chisel Object이다.
Enumeration은 Scala Object인데, 이 부분에서 다르다.
- UInt를 할당한다.
- 선언은 아래처럼 하면 된다.
val nameA :: nameB :: nameC :: Nil = Enum(3)
Example use case
- FSM의 상태
- MUX의 selection option 라벨링
- interface 옵션 라벨링
- ...
Chisel FSM
- chisel-tutorial에서 인지했지만, NextState 변수는 따로 필요없음.
- State와 Enum을 비교할 때, == 대신 ===을 사용함에 유의
Collecting Useful Output from Simulation
개요
- peek poke 테스터를 통해, 모듈과 interact하는 법을 배웠음.
- print할수도 있고, vcd를 출력할수도 있음.
- Test할때 말고, Debugging시에만 사용해라.
Test는 ChiselTest로 자동화되어야 한다.
Test할때마다 사람이 waveform보면서 합불판정하면 효율 안나온다는, 지극히 당연한 소리다.
그러나, 반대로 Debugging할 때는 Waveform을 보는걸 추천한다.
Printing
println(s"this is $myVal and ${foo.bar}")
- print During Generation
- python의 f-string과 유사하다. scala에서 지원하는 문법.
- {} 안에 있는 것들은, 전부 evaluate 된 뒤에 그 값을 출력하게 된다.
printf("myVal: %d", myVal)
- print During Simulation
- C style printf와 유사하다. 이또한 scala에서 지원하는 문법
- C style 말고, interpolation Style로 작성해도 된다.
printf(p"myVal: $myVal")
- 예시) Simulation 시에 수행된다는 것이 어떤 의미인지
- 한 사이클마다 printf문의 evaluate 여부가 판별된다는 것을 인지하자.
vcd 뽑는 법
- import treadle._ 하기
- test 클래스의 2번째 인자로,
Seq(WriteVcdAnnotation) 추가하기
- test 돌리기