From d436209f545e2d5ee576ec226eb4f0abde3fd9f7 Mon Sep 17 00:00:00 2001 From: Yehowshua Immanuel Date: Wed, 2 Apr 2025 02:09:41 -0400 Subject: [PATCH] seemingly reasonable stopping point --- bs/TagEngine.bs | 119 ++++++++++++++---------------------- bs/Tests/TagEngineTester.bs | 1 + 2 files changed, 48 insertions(+), 72 deletions(-) diff --git a/bs/TagEngine.bs b/bs/TagEngine.bs index edca9fa..91c5fdf 100644 --- a/bs/TagEngine.bs +++ b/bs/TagEngine.bs @@ -5,40 +5,41 @@ package TagEngine( import Vector import Util +import FIFO +import FIFOF #define UIntLog2N(n) (UInt (TLog n)) interface (TagEngine :: # -> *) numTags = - requestTag :: ActionValue (Maybe UIntLog2N(numTags)) - retireTag :: UIntLog2N(numTags) -> ActionValue BasicResult + 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 stack based. +-- This implementation is FIFO based. mkTagEngine :: Module (TagEngine numTags) mkTagEngine = do - let reifiedNumTags = fromInteger |> valueOf numTags - freeStackVec :: Vector numTags (Reg UIntLog2N(numTags)) - freeStackVec <- mapM (\i -> mkReg |> fromInteger i) genVector - inUseVec :: Vector numTags (Reg Bool) inUseVec <- replicateM |> mkReg False - stackPtr :: (Reg (Maybe(UIntLog2N(numTags)))) - stackPtr <- mkReg |> Just |> reifiedNumTags - 1 + -- 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`. + resetTagCounter :: (Reg (Maybe UIntLog2N(numTags))) + resetTagCounter <- mkReg |> Just |> reifiedNumTags - 1 - methodRequestTagCalled :: PulseWire - methodRequestTagCalled <- mkPulseWire + retireQueue :: FIFOF UIntLog2N(numTags) + retireQueue <- mkSizedFIFOF reifiedNumTags - methodRetireTagCalledValid :: RWire UIntLog2N(numTags) - methodRetireTagCalledValid <- mkUnsafeRWire - - -- computedTagResult :: BypassWire (Maybe UIntLog2N(numTags)) - -- methodRetireTagCalledValid <- mkBypassWire + freeQueue :: FIFOF UIntLog2N(numTags) + freeQueue <- mkSizedFIFOF reifiedNumTags debugOnce <- mkReg True @@ -46,71 +47,46 @@ mkTagEngine = rules "display": when (debugOnce == True) ==> do - $display "freeStackVec : " (fshow |> readVReg freeStackVec) $display "inUseVec : " (fshow |> readVReg inUseVec) - $display "stackPtr : " (fshow stackPtr) debugOnce := False - - "update stack pointer": when True ==> + + -- The "retire_tags" rule can't fire when the `requestTag` method is called. + -- In practice, this is Ok since: + -- 1. the `requestTag` method should take priority over the "retire_tags" rule + -- 2. the `requestTag` method handles the case where there are some tags to retire. + "retire_tags": when True ==> do - stackPtr := - case (methodRequestTagCalled, methodRetireTagCalledValid.wget) of - (True, Just _) -> stackPtr - (True, Nothing) -> - case stackPtr of - Just 0 -> Nothing - Just sampledStackPtr -> Just |> sampledStackPtr - 1 - Nothing -> Nothing - (False, Just _) -> - case stackPtr of - Just sampledStackPtr -> Just |> sampledStackPtr + 1 - Nothing -> Nothing - (False, Nothing) -> stackPtr - - "update free stack": when True ==> - do - case (methodRequestTagCalled, methodRetireTagCalledValid.wget) of - (True, Just _) -> do action {} - (True, Nothing) -> do action {} - (False, Just tag) -> do - case stackPtr of - Just sampledStackPtr -> do - (select freeStackVec (sampledStackPtr + 1)) := tag - Nothing -> do - (select freeStackVec 0) := tag - (False, Nothing) -> do action {} - - "update in use": when True ==> - do - case (methodRequestTagCalled, methodRetireTagCalledValid.wget) of - (True, Just _) -> do action {} - (True, Nothing) -> - case stackPtr of - Just sampledStackPtr -> do - (select inUseVec sampledStackPtr) := True - Nothing -> do action {} - (False, Just tag) -> do - (select inUseVec tag) := False - (False, Nothing) -> do action {} + let tag = retireQueue.first + $display "firing retire_tags" (fshow tag) + retireQueue.deq + freeQueue.enq tag + (select inUseVec tag) := False return $ interface TagEngine - requestTag :: ActionValue (Maybe UIntLog2N(numTags)) + requestTag :: ActionValue UIntLog2N(numTags) requestTag = do - methodRequestTagCalled.send - case methodRetireTagCalledValid.wget of + case resetTagCounter of + Just 0 -> do + resetTagCounter := Nothing + (select inUseVec 0) := True + return 0 Just tag -> do - return |> Just tag + resetTagCounter := Just |> tag - 1 + (select inUseVec tag) := True + return tag Nothing -> do - return |> - case stackPtr of - Just sampledStackPtr -> - Just |> readReg (select freeStackVec sampledStackPtr) - Nothing -> Nothing + let tag = freeQueue.first + freeQueue.deq + return tag - retireTag :: UIntLog2N(numTags) -> ActionValue BasicResult + -- `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 @@ -118,7 +94,6 @@ mkTagEngine = tagInUse = readReg (select inUseVec tag) if (tagValid && tagInUse) then do - methodRetireTagCalledValid.wset tag - return Success + retireQueue.enq tag else do - return Failure + action {} diff --git a/bs/Tests/TagEngineTester.bs b/bs/Tests/TagEngineTester.bs index 334b8d2..e4bf3c4 100644 --- a/bs/Tests/TagEngineTester.bs +++ b/bs/Tests/TagEngineTester.bs @@ -44,6 +44,7 @@ mkTagEngineTester = do |> do requestTagAction |> do retireTagAction 1 |> do requestTagAction + |> do $finish addRules $ rules