Compare commits

...

9 commits

Author SHA1 Message Date
Yehowshua Immanuel 271148e538 better names in TagEngine 2025-04-03 09:15:53 -04:00
Yehowshua Immanuel d1e3358197 now using wire instead of FIFO 2025-04-02 03:03:39 -04:00
Yehowshua Immanuel e055b1bbdf reduced latency 2025-04-02 02:59:49 -04:00
Yehowshua Immanuel d436209f54 seemingly reasonable stopping point 2025-04-02 02:09:41 -04:00
Yehowshua Immanuel 6247ae3b70 clean unused experiments 2025-03-25 08:48:43 -04:00
Yehowshua Immanuel e89254ebef eliminate warnings 2025-03-25 08:03:04 -04:00
Yehowshua Immanuel ed8e0b8337 tag engine now allows from simultaneous retire and request - but had to use unsafe 2025-03-25 01:15:49 -04:00
Yehowshua Immanuel 5588fafebd refactor into standalone TagEngineTester 2025-03-24 23:23:39 -04:00
Yehowshua Immanuel 6e3b3e9178 Preliminary cleaning before repairing TagEngine
* clean up state machine in Top
 * `requestTag` method now emits Maybe type
 * put more thought into comments around asynchronous bus
2025-03-24 22:46:42 -04:00
5 changed files with 121 additions and 132 deletions

View file

@ -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:

View file

@ -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

View file

@ -5,30 +5,43 @@ package TagEngine(
import Vector
import Util
import FIFO
import FIFOF
#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
freeStackVec :: Vector numTags (Reg UIntLog2N(numTags))
freeStackVec <- mapM (\i -> mkReg |> fromInteger i) genVector
-- we really only use tagUsageTracker after emptying out our
-- `initialTagDistributor` buffer
tagUsageTracker :: Vector numTags (Reg Bool)
tagUsageTracker <- replicateM |> mkReg False
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 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
stackPtr :: (Reg (Maybe(UIntLog2N(numTags))))
stackPtr <- mkReg |> Just |> reifiedNumTags - 1
retireTag :: Wire UIntLog2N(numTags)
retireTag <- mkWire
tagFIFO :: FIFOF UIntLog2N(numTags)
tagFIFO <- mkSizedFIFOF reifiedNumTags
debugOnce <- mkReg True
@ -36,46 +49,47 @@ mkTagEngine =
rules
"display": when (debugOnce == True) ==>
do
$display "freeStackVec : " (fshow |> readVReg freeStackVec)
$display "inUseVec : " (fshow |> readVReg inUseVec)
$display "stackPtr : " (fshow stackPtr)
$display "tagUsageTracker : " (fshow |> readVReg tagUsageTracker)
debugOnce := False
"retire_tags": when True ==>
do
$display "firing retire_tags" (fshow retireTag)
tagFIFO.enq retireTag
(select tagUsageTracker retireTag) := False
counter <- mkReg(0 :: UIntLog2N(numTags))
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
case initialTagDistributor of
Just 0 -> do
initialTagDistributor := Nothing
(select tagUsageTracker 0) := True
return 0
Just tag -> do
initialTagDistributor := Just |> tag - 1
(select tagUsageTracker tag) := True
return tag
Nothing -> do
let tag = tagFIFO.first
tagFIFO.deq
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
tagInUse = readReg (select tagUsageTracker tag)
if (tagValid && tagInUse)
then do
(select inUseVec tag) := False
(select freeStackVec nextStackPtrUint) := tag
stackPtr := Just nextStackPtrUint
return Success
retireTag := tag
else do
return Failure
action {}

View file

@ -0,0 +1,65 @@
package TagEngineTester(mkTagEngineTester) where
import TagEngine
import ActionSeq
mkTagEngineTester :: Module Empty
mkTagEngineTester = do
tagEngine :: TagEngine 5 <- mkTagEngine
count :: Reg (UInt 32) <- mkReg 0;
runOnce :: Reg Bool <- mkReg False
s :: ActionSeq
s <-
let
requestTagAction :: Action
requestTagAction =
do
tag <- tagEngine.requestTag
$display "got tag : " (fshow tag)
retireTagAction :: UInt 3 -> Action
retireTagAction tag =
do
res <- tagEngine.retireTag tag
$display "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

View file

@ -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