[CSE228A] Lecture 4 - Sequential Circuit

YumeIroVillain·2023년 8월 10일
0

Chisel 독학

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

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

  • Sync Reset을 가지는 Reg를 만듬
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을 수행함.

RegNext(next): Attach Input

  • 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 // RANDOMIZE_REG_INIT
  reg  r_1; // @[Reg.scala 27:20]
  assign io_out = r_1; // @[cmd14.sc 12:12]
  always @(posedge clock) begin
    if (reset) begin // @[Reg.scala 27:20]
      r_1 <= 1'h0; // @[Reg.scala 27:20]
    end else if (io_en) begin // @[Reg.scala 28:19]
      r_1 <= io_in; // @[Reg.scala 28:23]
    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 =>
// test(new MyCounter(3), Seq(WriteVcdAnnotation)) { 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 뽑는 법

  1. import treadle._ 하기
  2. test 클래스의 2번째 인자로,
    Seq(WriteVcdAnnotation) 추가하기
  3. test 돌리기

profile
HW SW 둘다 공부하는 혼종의 넋두리 블로그 / SKKU SSE 17 / SWM 11th
post-custom-banner

0개의 댓글