[chisel-tutorial] DynamicMemorySearch.scala: 그냥 어려움

YumeIroVillain·2023년 8월 7일
0

Chisel 독학

목록 보기
16/44
post-thumbnail
post-custom-banner

좀 빡셌다.

// See LICENSE.txt for license details.
package problems

import chisel3._
import chisel3.util.log2Ceil

// Problem:
//
// This module should be able to write 'data' to
// internal memory at 'wrAddr' if 'isWr' is asserted.
//
// This module should perform sequential search of 'data'
// in internal memory if 'en' was asserted at least 1 clock cycle
//
// While searching 'done' should remain 0,
// 'done' should be asserted when search is complete
//
// If 'data' has been found 'target' should be updated to the
// address of the first occurrence
//
class DynamicMemorySearch(val n: Int, val w: Int) extends Module {
  val io = IO(new Bundle {
    val isWr   = Input(Bool())
    val wrAddr = Input(UInt(log2Ceil(n).W))
    val data   = Input(UInt(w.W))
    val en     = Input(Bool())
    val target = Output(UInt(log2Ceil(n).W))
    val done   = Output(Bool())
  })
  // Implement below ----------

  val list = Mem(n, UInt(w.W)) // length w 짜리 line이 n개 row
  val index  = RegInit(0.U(log2Ceil(n).W))
  val memVal = list(index)

  // Implement above ----------
  val done   = !io.en && ((memVal === io.data) || (index === (n-1).asUInt))

  // Implement below ----------


  // Implement above ----------

  when(io.isWr){
    list(io.wrAddr) := io.data
  } .elsewhen (io.en) {
    index := 0.U
  } .elsewhen (done === false.B) {
    index := index + 1.U
  } .otherwise{
    // do nothing
  }
  io.done   := done
  io.target := index
}

DynamicMemorySearchTests.scala

// See LICENSE.txt for license details.
package problems

import chisel3.iotesters.PeekPokeTester

class DynamicMemorySearchTests(c: DynamicMemorySearch) extends PeekPokeTester(c) {
  val list = Array.fill(c.n){ 0 }
  // Initialize the memory.
  for (k <- 0 until c.n) {
    poke(c.io.en, 0)
    poke(c.io.isWr, 1)
    poke(c.io.wrAddr, k)
    poke(c.io.data, 0)
    step(1)
  }

  for (k <- 0 until 16) {
    println(k+"th TEST")
    // WRITE A WORD
    poke(c.io.en,   0)
    poke(c.io.isWr, 1)
    val wrAddr = rnd.nextInt(c.n-1)
    val data   = rnd.nextInt((1 << c.w) - 1) + 1 // can't be 0
    poke(c.io.wrAddr, wrAddr)
    poke(c.io.data,   data)
    step(1)
    list(wrAddr) = data

    println(list.mkString(" "))
    println(c.io.wrAddr+" <= "+c.io.data)
    // SETUP SEARCH
    val target = if (k > 12) rnd.nextInt(1 << c.w) else data
    poke(c.io.isWr, 0)
    poke(c.io.data, target)
    poke(c.io.en,   1)
    step(1)
    
    // println(list.mkString(" "))
    do {
      println(peek(c.io.target).toString)
      poke(c.io.en, 0)
      step(1)
    } while (peek(c.io.done) == BigInt(0))
    val addr = peek(c.io.target).toInt
    if (list contains target)
      assert(list(addr) == target, "LOOKING FOR " + target + " FOUND " + addr)
    else
      assert(addr==(list.length-1), "LOOKING FOR " + target + " FOUND " + addr)
  }
}

  • 임의의 주소에 쓰고, 선형적으로 읽은 뒤, 찾아진 index를 반환하는 것을 확인할 수 있다.

What I learned

  • Register가 HW적으로 구현되어야겠다는 생각이 들면, RegInit으로 Register를 명시적으로 만들어야 한다.

  • RegInit과 Mem은 다르다.
    Mem은 packed Array로 선언되고,
    RegInit은 Array로 선언된다.

    실제로, list변수는 packed array로, index변수는 reg로 구현되었음을 확인할 수 있다.

  • testbench 만들 때, test sequence는 Scala에서 지원하는 Array를 활용하여 저장할 수 있다.

  val list = Array.fill(c.n){ 0 }

위처럼 사용가능하다.

  • printf 디버깅 시, 아래 구문들을 참고하자.
    println(k+"th TEST")
    println(list.mkString(" ")) //  Array 내의 요소를 빈칸 간격으로 출력
    println(peek(c.io.wrAddr)+" <= "+peek(c.io.data))
    println(peek(c.io.target).toString)
    println(peek(c.io.target).toInt)
  • Chisel 그자체는 Register에 초기값 없는 것을 잡아주지 않는 모양이다. otherwise에 아무것도 선언하지 않아서, index가 어느 값이 될지 모르는 상황인데, 지적하지 않는다.
profile
HW SW 둘다 공부하는 혼종의 넋두리 블로그 / SKKU SSE 17 / SWM 11th
post-custom-banner

1개의 댓글

comment-user-thumbnail
2024년 7월 14일

안녕하세요, chisel 관련 포스트 잘 보고 있습니다.
혹시 test 과정에서 터미널에 출력되는 매 test 로그는 따로 설정하신건가요?

답글 달기