riscv-bluespec-classic/bs/TagEngine.bs

114 lines
4.6 KiB
Haskell

package TagEngine(
TagEngine(..),
Util.BasicResult(..),
mkTagEngine) where
import Vector
import Util
import FIFO
import FIFOF
import SpecialFIFOs
#define UIntLog2N(n) (UInt (TLog n))
interface (TagEngine :: # -> *) numTags =
requestTag :: ActionValue UIntLog2N(numTags)
retireTag :: UIntLog2N(numTags) -> Action
-- The tag engine returns a tag that is unique for the duration of
-- the lifetime of the tag. Useful when you need to tag transactions
-- on a bus for example.
-- This implementation is FIFO based.
mkTagEngine :: Module (TagEngine numTags)
mkTagEngine = do
-- Constants
let maxTagCount = fromInteger (valueOf numTags)
tagUsage :: Vector numTags (Reg Bool) <- replicateM (mkReg False) -- Tracks which tags are in use
-- Since Bluespec doesn't allow us to initialize FIFOs with values at
-- reset, we can pretend as if the buffer within our freeTagQueue is populated
-- with sequentially incrementing values(starting from 0) on reset
-- by having our tag engine effectively return the value of a decrementing
-- counter initialized to (maxTagCount - 1) for the first n tag requests made
-- to TagEngine where `n := maxTagCount`.
initialTagDistributor <- mkReg (Just (maxTagCount - 1)) -- Distributes initial tags
retireQueue <- mkBypassFIFO -- Queue for tags being retired
freeTagQueue <- mkSizedFIFOF maxTagCount -- Queue of available tags
-- Signals
retireSignal <- mkRWire -- Signals a tag retirement
requestSignal <- mkRWire -- Signals a tag request
-- Debug
debugOnce <- mkReg True
-- Rules
addRules $
rules
"debug_initial_state": when debugOnce ==> do
$display "tagUsage: " (fshow (readVReg tagUsage))
debugOnce := False
"retire_tag": when True ==> do
let tag = retireQueue.first
$display "Retiring tag: " (fshow tag)
retireQueue.deq
freeTagQueue.enq tag
retireSignal.wset tag
-- Combined update rules (simplified below)
"update_usage": when True ==> do
let mRetireTag = retireSignal.wget
mRequestTag = requestSignal.wget
case (mRetireTag, mRequestTag) of
(Just retireTag, Just requestTag) -> do
let usage = readVReg tagUsage
usage' = update usage requestTag True
usage'' = update usage' retireTag False
writeVReg tagUsage usage''
$display $time " Updated usage (request + retire): " (fshow |> readVReg tagUsage)
(Just retireTag, Nothing) -> do
(select tagUsage retireTag) := False
$display $time " Updated usage (retire): " (fshow (readVReg tagUsage))
(Nothing, Just requestTag) -> do
(select tagUsage requestTag) := True
$display $time " Updated usage (request): " (fshow (readVReg tagUsage))
(Nothing, Nothing) -> action {}
-- Interface
return $
interface TagEngine
requestTag :: ActionValue UIntLog2N(numTags)
requestTag = do
case initialTagDistributor of
Just 0 -> do
initialTagDistributor := Nothing
requestSignal.wset 0
return 0
Just tag -> do
initialTagDistributor := Just (tag - 1)
requestSignal.wset tag
return tag
Nothing -> do
let tag = freeTagQueue.first
freeTagQueue.deq
requestSignal.wset tag
return tag
-- `retireTag` isn't guarded on tag validity(this would break Bluespec's safety model)
-- so it is advisable that the caller of `retireTag` only attempt to retire valid tags.
-- Internally, the tagEngine will keep a correct and consistent state since TagEngine
-- validates tags before attempting to retire them.
retireTag :: UIntLog2N(numTags) -> Action
retireTag tag =
do
let
tagValid = tag < maxTagCount
tagInUse = readReg (select tagUsage tag)
if (tagValid && tagInUse)
then do
retireQueue.enq tag
else do
action {}