riscv-bluespec-classic/bs/TagEngine.bs

131 lines
5.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
let reifiedNumTags = fromInteger |> valueOf numTags
-- we really only use tagUsageTracker after emptying out our
-- `initialTagDistributor` buffer
tagUsageTracker :: Vector numTags (Reg Bool)
tagUsageTracker <- replicateM |> mkReg False
-- Since Bluespec doesn't allow us to initialize FIFOs with values at
-- reset, we can pretend as if the buffer within our tagFIFO 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 (numTags - 1) for the first n tag requests made
-- to TagEngine where `n := numTags`.
initialTagDistributor :: (Reg (Maybe UIntLog2N(numTags)))
initialTagDistributor <- mkReg |> Just |> reifiedNumTags - 1
validatedTagToRetire :: FIFO UIntLog2N(numTags)
validatedTagToRetire <- mkBypassFIFO
updateUsageRetireTag :: RWire UIntLog2N(numTags)
updateUsageRetireTag <- mkRWire
updateUsageRequestTag :: RWire UIntLog2N(numTags)
updateUsageRequestTag <- mkRWire
tagFIFO :: FIFOF UIntLog2N(numTags)
tagFIFO <- mkSizedFIFOF reifiedNumTags
debugOnce <- mkReg True
addRules $
rules
"display": when (debugOnce == True) ==>
do
$display "tagUsageTracker : " (fshow |> readVReg tagUsageTracker)
debugOnce := False
"update_usage_just_retire": when
Just tag <- updateUsageRetireTag.wget,
Nothing <- updateUsageRequestTag.wget ==>
do
$display $time " tagUsageTracker after update retire: " (fshow |> readVReg tagUsageTracker)
(select tagUsageTracker tag) := False
"update_usage_just_request": when
Nothing <- updateUsageRetireTag.wget,
Just tag <- updateUsageRequestTag.wget ==>
do
$display $time " tagUsageTracker after update request: " (fshow |> readVReg tagUsageTracker)
(select tagUsageTracker tag) := True
"update_usage_request_and_retire": when
Just retireTag <- updateUsageRetireTag.wget,
Just requestTag <- updateUsageRequestTag.wget ==>
do
$display $time " tagUsageTracker after update request and retire: " (fshow |> readVReg tagUsageTracker)
let
tagUsageTrackerInner = readVReg tagUsageTracker
tagUsageTracker' = update tagUsageTrackerInner requestTag True
tagUsageTracker'' = update tagUsageTracker' retireTag False
writeVReg tagUsageTracker tagUsageTracker''
"retire_tags": when True ==>
do
let tagToRetire = validatedTagToRetire.first
$display "firing retire_tags" (fshow tagToRetire)
validatedTagToRetire.deq
tagFIFO.enq tagToRetire
updateUsageRetireTag.wset tagToRetire
return $
interface TagEngine
requestTag :: ActionValue UIntLog2N(numTags)
requestTag =
do
case initialTagDistributor of
Just 0 -> do
initialTagDistributor := Nothing
updateUsageRequestTag.wset 0
return 0
Just tag -> do
initialTagDistributor := Just |> tag - 1
updateUsageRequestTag.wset tag
return tag
Nothing -> do
let tag = tagFIFO.first
tagFIFO.deq
updateUsageRequestTag.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 < reifiedNumTags
tagInUse = readReg (select tagUsageTracker tag)
if (tagValid && tagInUse)
then do
validatedTagToRetire.enq tag
else do
action {}