关于LRU(Least Recently Used)的逻辑实现

描述

凑巧看到一个有关LRU(Least Recently Used)的逻辑实现,其采用矩阵方式进行实现,看起来颇有意思,但文章中只写方法不说原理,遂来研究下。

LRU

LRU(Least Recently Used)算法是一种常用的缓存淘汰策略,其核心思想是:如果一个数据在最近一段时间内没有被访问到,那么在未来它被访问的可能性也很小。因此,当缓存满了的时候,最久未使用的数据会被淘汰。

LRU在Cache替换策略里还是有较大的用途的。对于一个N路组相连,当对应的entry满了之后,当有新的访问请求到来后需从N个entry中挑选出一个替换entry。通过LRU可以挑选出最久没有被使用的元素。

LRU矩阵实现原理

对于一个具有N个Entry空间,LRU实现可以采用矩阵方式实现。这里以四个Entry空间为例:

矩阵

对于矩阵中的元素Value[i,j](第i行第j个元素),定义如下:

1: 为1时表示元素i在j访问之后被访问过。

0:为0时表示元素i在j被访问之后没有被访问过。

搞清楚了这两个元素定义,那么我们自然可以定义规则。当entry i被访问时,执行如下操作:

将第i行所有元素全部设置为1.

将第i列所有元素全部设置为0.

这里有一个点就是Value[i,i]将会被设置为0,也没有毛病,毕竟元素i在元素i被访问之后没有被访问过也是对的。

  那么基于此,很明显一定存在某一行存在所有元素全为0的情况,从而也表示了其是Least Recently Used。

下图给出了基于矩阵的LRU检测过程:

矩阵

Demo

基于上面的原理,下面的代码基于SpinalHDL写了一个简单的Demo,包含仿真代码:

 

 

package LRU

import spinal.core._
import spinal.lib._

case class LRUUpdateRequest(entryNum: Int) extends Bundle {
  val lru_state = Bits(entryNum * entryNum bits) //LRU State entryNum row,entryNum col
  val lru_update_entry = UInt(log2Up(entryNum) bits)
}

case class LruControl(entryNum: Int) extends Component {
  val io = new Bundle {
    val lru_state = in Bits (entryNum * entryNum bits) //LRU状态
    val lru_entry = out UInt (log2Up(entryNum) bits) //最近最少被使用

    val lru_update_req = in(LRUUpdateRequest(entryNum))
    val lru_update_result = out(Bits(entryNum * entryNum bits))
  }
  noIoPrefix()
  /** *****************************************************************************************
   * 计算最近最少被使用者
   * 寻找矩阵中为0的行,只需找到一个即可,找最低位
   * 实现机制:每一行做按位或,随后取反,转换为寻找最低位为1的位置
   * ***************************************************************************************** */
  val lru_row_state = io.lru_state.subdivideIn(entryNum bits).map(~_.orR).asBits()
  val lru_row_state_oh = OHMasking.first(lru_row_state) //先转换成独热码
  io.lru_entry := OHToUInt(lru_row_state_oh) //再转换成二进制
  /** *****************************************************************************************
   * lru更新
   * 更新规则,当更新entry i时,矩阵中第i行全部填1,矩阵中第i列全部填0
   * ***************************************************************************************** */
  val lru_update_entry_oh = UIntToOh(io.lru_update_req.lru_update_entry) //转换成独热码
  for {
    rowIndex <- 0 until entryNum;
    colIndex <- 0 until entryNum
  } {
    io.lru_update_result(rowIndex * entryNum + colIndex) := Mux(
      sel = lru_update_entry_oh(colIndex),
      whenTrue = False,
      whenFalse = Mux(
        sel = lru_update_entry_oh(rowIndex),
        whenTrue = True,
        whenFalse = io.lru_update_req.lru_state(rowIndex * entryNum + colIndex))
    )
  }
}

import spinal.core.sim._
import spinal.lib.tools.BigIntToListBoolean

object LruControlApp extends App {
  SimConfig.withFstWave.compile(LruControl(4)).doSim(dut => {
    var lruState = BigInt(0)

    def showLruState() = {
      val lru_state_boolean = BigIntToListBoolean(lruState, 16 bits)
      for (rowIndex <- 0 until 4) {
        println(s"${lru_state_boolean.slice(rowIndex * 4, rowIndex * 4 + 4).map(_.toInt)}")
      }
    }

    def updateRequest(entry: Int) = {
      println(s"Update Entry:${entry}")
      dut.io.lru_update_req.lru_update_entry #= entry
      dut.io.lru_update_req.lru_state #= lruState
      sleep(1)
      lruState = dut.io.lru_update_result.toBigInt
      //show 矩阵
      showLruState()

      dut.io.lru_state #= lruState
      sleep(1)
      val lruEntry = dut.io.lru_entry.toInt
      println(s"The Lru Entry is $lruEntry")
      sleep(1)
    }

    showLruState()
    dut.io.lru_state #= lruState
    sleep(1)
    val lruEntry = dut.io.lru_entry.toInt
    println(s"The Lru Entry is $lruEntry")
    sleep(1)
    for (entry <- Array(0, 1, 2, 3, 2, 3, 0, 1, 2, 0, 3)) {
      updateRequest(entry)
    }
  })
}

对于LRU矩阵实现,由于矩阵的存在导致其所需要的资源会随着entry的增加而膨胀。实现一个四路组相连则需要16bit的空间用于存储矩阵信息,8路就需要64bit信息了。
 

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分