diff --git a/Makefile b/Makefile index 52863b1..7579e84 100644 --- a/Makefile +++ b/Makefile @@ -61,7 +61,7 @@ $(BDPI_OBJ): $(BDPI_SRC) BSC_LINK_FLAGS += -keep-fires -BSC_PATHS = -p bs/:bsv/:+ +BSC_PATHS = -p bs/:bs/Tests/:bsv/:+ .PHONY: help help: diff --git a/bs/BusTypes.bs b/bs/BusTypes.bs index d633ee6..99d0fb4 100644 --- a/bs/BusTypes.bs +++ b/bs/BusTypes.bs @@ -57,9 +57,9 @@ interface BusMaster = -- This has implications about for the implementor of BusMaster, namely, that it -- should hold its request until it's request method gets called. request :: BusRequest - -- From the masters's perspective, the response should not be called until - -- the client is ready to accept the response. In other words, response - -- should be guarded by the client. + -- From the masters's perspective, the response should not be called by the + -- arbiter until the master is ready to accept the response. In other words, + -- response should be guarded by the client. response :: BusResponse -> Action type Token = UInt 5 diff --git a/bs/TagEngine.bs b/bs/TagEngine.bs index df60839..921e58e 100644 --- a/bs/TagEngine.bs +++ b/bs/TagEngine.bs @@ -5,77 +5,109 @@ package TagEngine( 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) -> ActionValue BasicResult + 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 +mkTagEngine = do + -- Constants + let maxTagCount = fromInteger (valueOf numTags) - freeStackVec :: Vector numTags (Reg UIntLog2N(numTags)) - freeStackVec <- mapM (\i -> mkReg |> fromInteger i) genVector + tagUsage :: Vector numTags (Reg Bool) <- replicateM (mkReg False) -- Tracks which tags are in use - inUseVec :: Vector numTags (Reg Bool) - inUseVec <- 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 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 - stackPtr :: (Reg (Maybe(UIntLog2N(numTags)))) - stackPtr <- mkReg |> Just |> reifiedNumTags - 1 + -- Signals + retireSignal <- mkRWire -- Signals a tag retirement + requestSignal <- mkRWire -- Signals a tag request - debugOnce <- mkReg True + -- Debug + debugOnce <- mkReg True - addRules $ - rules - "display": when (debugOnce == True) ==> - do - $display "freeStackVec : " (fshow |> readVReg freeStackVec) - $display "inUseVec : " (fshow |> readVReg inUseVec) - $display "stackPtr : " (fshow stackPtr) - debugOnce := False + -- Rules + addRules $ + rules + "debug_initial_state": when debugOnce ==> do + $display "tagUsage: " (fshow (readVReg tagUsage)) + debugOnce := False - counter <- mkReg(0 :: UIntLog2N(numTags)) - return $ - interface TagEngine + "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 - stackPtr := - if sampledStackPtr == 0 - then Nothing - else Just |> sampledStackPtr - 1 - (select inUseVec sampledStackPtr) := True - return |> readReg (select freeStackVec sampledStackPtr) - when - Just sampledStackPtr <- stackPtr + 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 so its up to external module to only attempt to - -- retire valid tags... At any rate, we can notify the requestor of failures - -- to retire tags - although the requestor can merely ignore this - -- notification. - 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 - tagValid = tag < reifiedNumTags - tagInUse = readReg (select inUseVec tag) - nextStackPtrUint = - case stackPtr of - Nothing -> 0 - Just n -> n + 1 + tagValid = tag < maxTagCount + tagInUse = readReg (select tagUsage tag) if (tagValid && tagInUse) then do - (select inUseVec tag) := False - (select freeStackVec nextStackPtrUint) := tag - stackPtr := Just nextStackPtrUint - return Success + retireQueue.enq tag else do - return Failure + action {} diff --git a/bs/Tests/TagEngineTester.bs b/bs/Tests/TagEngineTester.bs new file mode 100644 index 0000000..4fc700e --- /dev/null +++ b/bs/Tests/TagEngineTester.bs @@ -0,0 +1,64 @@ +package TagEngineTester(mkTagEngineTester) where + +import TagEngine +import ActionSeq + +mkTagEngineTester :: Module Empty +mkTagEngineTester = do + tagEngine :: TagEngine 5 <- mkTagEngine + runOnce :: Reg Bool <- mkReg False + + s :: ActionSeq + s <- + let + requestTagAction :: Action + requestTagAction = + do + tag <- tagEngine.requestTag + $display $time " got tag : " (fshow tag) + + retireTagAction :: UInt 3 -> Action + retireTagAction tag = + do + res <- tagEngine.retireTag tag + $display $time " retiring tag : " (fshow tag) " " (fshow res) + action {} + + in + actionSeq $ + do requestTagAction + |> do requestTagAction + |> do requestTagAction + |> do requestTagAction + |> do requestTagAction + |> do retireTagAction 2 + -- |> do $display "BEGIN TRY SIMULTANEOUS RETIRE and REQUEST" + |> do + retireTagAction 4 + requestTagAction + -- |> do $display "END TRY SIMULTANEOUS RETIRE and REQUEST" + -- |> do $display "BEGIN TRY SIMULTANEOUS RETIRE and REQUEST" + |> do + retireTagAction 4 + requestTagAction + -- |> do $display "END TRY SIMULTANEOUS RETIRE and REQUEST" + |> do $finish + -- |> do retireTagAction 4 + -- |> do retireTagAction 4 + -- |> do retireTagAction 0 + -- |> do requestTagAction + -- |> do requestTagAction + -- |> do retireTagAction 1 + -- |> do requestTagAction + -- |> do $finish + + addRules $ + rules + -- "counter": when True ==> + -- do + -- count := count + 1 + -- $display "count : " (fshow count) + "testIncrement": when (runOnce == False) ==> + do + s.start + runOnce := True diff --git a/bs/Top.bs b/bs/Top.bs index 59344b2..f4f1cda 100644 --- a/bs/Top.bs +++ b/bs/Top.bs @@ -10,6 +10,8 @@ import TagEngine import List import ActionSeq +import TagEngineTester + type FCLK = 25000000 type BAUD = 9600 @@ -19,44 +21,6 @@ interface ITop = { ;ftdi_txd :: Bit 1 -> Action {-# always_ready , always_enabled #-} }; -interface BusClient = - request :: Bit 1 - response :: Bit 1 -> Action - -mkBusClient :: Module BusClient -mkBusClient = module - reqReg :: Reg (Bit 1) <- mkReg 0 - return $ - interface BusClient - request = reqReg - response resp = do - reqReg := 0 -- Reset request after receiving response - -interface Bus = - request :: Bit 1 -> Action - response :: Bit 1 - -mkBus :: Module Bus -mkBus = module - respReg :: Reg (Bit 1) <- mkReg 0 - return $ - interface Bus - request req = do - respReg := req -- Simple pass-through for this example - response = respReg - --- -- Function to connect Bus to BusClient -connectBusToClient :: Bus -> BusClient -> Rules -connectBusToClient bus client = - rules - "busConnection": when True ==> do - bus.request client.request - client.response bus.response - --- need to implement mkBus - --- need function that can connect Bus to BusClient - mkTop :: Module ITop mkTop = do fileHandle :: Handle <- openFile "compile.log" WriteMode @@ -64,17 +28,9 @@ mkTop = do serializer :: ISerializer FCLK BAUD <- mkSerialize fileHandle core :: Core FCLK <- mkCore - bus :: Bus <- mkBus - busClient :: BusClient <- mkBusClient - let a :: List Integer = 1 :> 2 :> Nil - b = length a - persistLed :: Reg (Bit 8) <- mkReg 0 messageM $ "Hallo!!" + (realToString 5) - -- need to instantiate a Bus and BusClient - addRules $ connectBusToClient bus busClient - addRules $ rules -- need new rule that always connects Bus to BusClient @@ -100,58 +56,12 @@ mkTop = do mkSim :: Module Empty mkSim = do - let cfg :: BRAM_Configure = defaultValue - - tagEngine :: TagEngine 5 <- mkTagEngine - count :: Reg (UInt 4) <- mkReg 0; + _ :: Empty <- mkTagEngineTester initCFunctions :: Reg Bool <- mkReg False; core :: Core FCLK <- mkCore; - s :: ActionSeq - s <- actionSeq - $ do - $display "got tag : " tagEngine.requestTag - |> do - $display "got tag : " tagEngine.requestTag - |> do - $display "got tag : " tagEngine.requestTag - |> do - res <- tagEngine.retireTag 3 - $display "retiring tag : 3 " (fshow res) - action {} - |> do - $display "got tag : " tagEngine.requestTag - |> do - $display "got tag : " tagEngine.requestTag - |> do - res <- tagEngine.retireTag 4 - $display "retiring tag : 4 " (fshow res) - action {} - |> do - res <- tagEngine.retireTag 4 - $display "retiring tag : 4 " (fshow res) - action {} - |> do - res <- tagEngine.retireTag 0 - $display "retiring tag : 0 " (fshow res) - action {} - |> do - $display "got tag : " tagEngine.requestTag - |> do - $display "got tag : " tagEngine.requestTag - |> do - res <- tagEngine.retireTag 1 - $display "retiring tag : 1 " (fshow res) - action {} - |> do - $display "got tag : " tagEngine.requestTag - addRules $ rules - "testIncrement": when (count < 10) ==> - do - count := count + 1 - s.start "initCFunctionsOnce": when not initCFunctions ==> do initTerminal