114 lines
4.6 KiB
Haskell
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 {}
|