Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Libraries/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ BUILD_ORDER = \
VerilogRepr \
Misc \
AMBA_Fabrics \
SequenceRules \

.PHONY: all
all: install
Expand Down
29 changes: 29 additions & 0 deletions Libraries/SequenceRules/MList.bs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package MList where

import List

-- Copyright (c) 2025 MatX, Inc. All Rights Reserved

-- SPDX-License-Identifier: BSD-3-Clause

-- A generic monadic List type that allows for the use of the "do" notation to
-- build lists

data MList_ t a = MList_ (a, List t)

instance Monad (MList_ t) where
return x = MList_ (x, Nil)
bind (MList_ (a, as)) f =
case f a of
MList_ (b, bs) -> MList_ (b, append as bs)

type MList t = MList_ t ()

mList :: List t -> MList t
mList as = MList_ ((), as)

unMList :: MList_ t a -> List t
unMList (MList_ (_, as)) = as

m :: t -> MList t
m x = mList $ Cons x Nil
16 changes: 16 additions & 0 deletions Libraries/SequenceRules/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
PWD:=$(shell pwd)
TOP:=$(PWD)/../..

LIBNAME=SequenceRules

# Requires that TOP and LIBNAME be set
# Sets BUILDDIR, and BSC and BSCFLAGS if not set
# and defines the install target
include ../common.mk

.PHONY: build
build:
$(BSC) -u $(BSCFLAGS) $(notdir $(LIBNAME)).bs

.PHONY: clean full_clean
clean full_clean:
148 changes: 148 additions & 0 deletions Libraries/SequenceRules/SequenceRules.bs
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
package SequenceRules where

import List
import MList

-- Copyright (c) 2025 MatX, Inc. All Rights Reserved

-- SPDX-License-Identifier: BSD-3-Clause

-- This is a simplified version of StmtFSM that only handles straight-line
-- sequences of statements.

------------------------------------------------------------------------------
-- These were selectively copied from different (much larger) libraries, and are
-- not specific to this library.

enumerateFrom :: Integer -> List a -> (Integer -> a -> b) -> List b
enumerateFrom _ Nil _ = Nil
enumerateFrom i (Cons x xs) fn = Cons (fn i x) (enumerateFrom (i + 1) xs fn)

enumerate :: List a -> (Integer -> a -> b) -> List b
enumerate = enumerateFrom 0

ruleIf :: String -> Bool -> Action -> Rules
ruleIf s b a =
rules
{-# ASSERT no implicit conditions #-}
{-# ASSERT fire when enabled #-}
s: when b ==> a

-- The name 'rule' is unavailable, since it is a keyword.
rule_ :: String -> Action -> Rules
rule_ s a = ruleIf s True a

alwaysIf :: (IsModule m c) => String -> Bool -> Action -> m Empty
alwaysIf s b a = addRules $ ruleIf s b a

always :: (IsModule m c) => String -> Action -> m Empty
always s a = addRules $ rule_ s a

mkCycleCounter :: (IsModule m c, DefaultValue t, Bits t t_size, Arith t) => m t
mkCycleCounter = module
cycle :: Reg t <- mkReg 0
always "StepCycleCounter" $ cycle := cycle + 1
return cycle

pass :: Action
pass = do
$display "PASS"
$finish 0

fail :: Action
fail = do
$display "FAIL"
$fatal

doIf :: Bool -> Action -> Action
doIf True a = a
doIf False _ = noAction

failIf :: Bool -> Action
failIf cond = doIf cond fail

-- Useful for scripted SequenceRules based tests, which should call pass at the
-- end of their script. Detects if the script is missing a pass call, or hangs.
alwaysFailAtMaxCycleCount :: (IsModule m c) => UInt n -> m Empty
alwaysFailAtMaxCycleCount cycle =
alwaysIf "alwaysFailAtMaxCycleCount" (cycle == (unpack (0 - 1)))do
$display "Max cycle count reached: %d. Failing." cycle
fail

------------------------------------------------------------------------------

-- A sequence of actions, where each action is executed sequentially.
type Sequence = MList (Maybe Action)

-- "r" for "rule"
r :: Action -> Sequence
r a = m $ Valid a

-- Equivalent to "r noAction" but does not create an empty rule (which would
-- generate a compiler warning).
noR :: Sequence
noR = m Invalid

-- Repeat a sequence of actions n times.
rRepeat :: Integer -> Sequence -> Sequence
rRepeat n s = mList $ concat $ replicate n $ unMList s

-- Do nothing n times (does not create empty rules)
rDelay :: Integer -> Sequence
rDelay n = rRepeat n noR

-- Loop over a sequence of actions n times, with the index passed to the
-- function.
rLoopUpTo :: Integer -> Integer -> (Integer -> Sequence) -> Sequence
rLoopUpTo a b f = mList $ concat $ map unMList $ map f $ upto a b

-- Simpler version of rLoopUpTo, starting at 0, going to n-1
rLoop :: Integer -> (Integer -> Sequence) -> Sequence
rLoop n = rLoopUpTo 0 (n - 1)

-- A set of sequences, where each sequence is executed in parallel.
type Sequences = MList Sequence

-- "t" for "thread"
t :: Sequence -> Sequences
t = m

-- Takes a list of threads (sequences of actions), and combines them to run them
-- in parallel. If any of the sequences are shorter than the longest, the
-- shorter ones will be padded with "do nothing" states so that the resulting
-- action list is as long as the longest sequence.
rPar :: Sequences -> Sequence
rPar ts =
let headOrInvalid :: List (Maybe a) -> Maybe a
headOrInvalid Nil = Invalid
headOrInvalid (Cons a _) = a
tailOrNil :: List a -> List a
tailOrNil Nil = Nil
tailOrNil (Cons _ as) = as
zipLongest :: List (List (Maybe a)) -> List (List (Maybe a))
zipLongest xss =
if all null xss then Nil -- 0 threads, or all threads are 0 length
else Cons (map headOrInvalid xss) (zipLongest (map tailOrNil xss))
joinMaybeActions :: List (Maybe Action) -> Maybe Action
joinMaybeActions as =
if not (any isValid as) then Invalid -- All Invalid
else Valid $ joinActions $ map (fromMaybe noAction) as
in mList $ map joinMaybeActions $ zipLongest $ map unMList $ unMList ts

rParLoopUpTo :: Integer -> Integer -> (Integer -> Sequence) -> Sequence
rParLoopUpTo a b f = rPar $ mList $ map f $ upto a b

rParLoop :: Integer -> (Integer -> Sequence) -> Sequence
rParLoop n f = rParLoopUpTo 0 (n - 1) f

-- Given a cycle counter, turn a sequence of actions into a set of rules
-- controlled by that cycle counter.
mkSequenceRules :: (IsModule m c) => String -> UInt n -> Sequence -> m Empty
mkSequenceRules name cycle s = module
let actRules :: List Rules
actRules = enumerate (unMList s) $ \i ma ->
case ma of
Valid a -> ruleIf (name + "_" + (integerToString i))
(cycle == (fromInteger i)) a
Invalid -> emptyRules
addRules $ fold rJoin actRules
5 changes: 5 additions & 0 deletions testing/bsc.contrib/SequenceRules/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# for "make clean" to work everywhere

CONFDIR = $(realpath ../..)

include $(CONFDIR)/clean.mk
61 changes: 61 additions & 0 deletions testing/bsc.contrib/SequenceRules/SequenceRulesTest.bs
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package SequenceRulesTest where

import SequenceRules

{-# verilog sysSequenceRulesTest #-}
sysSequenceRulesTest :: Module Empty
sysSequenceRulesTest = module
cycle :: UInt 8 <- mkCycleCounter

let asUint3 :: Integer -> UInt 3
asUint3 = fromInteger

mkSequenceRules "SequenceRulesTest" cycle do
r $ $display "Cycle %d: rule 1" cycle
r $ $display "Cycle %d: rule 2" cycle
noR -- Skip a cycle
r $ $display "Cycle %d: rule 4" cycle
rRepeat 3 $ r $ $display "Cycle %d: repeated rule" cycle
rDelay 3 -- Skip 3 cycles
rLoopUpTo 2 4 $ \i -> r $ $display "Cycle %d: rLoopUpTo 2 4 (%d)" cycle (asUint3 i)
rLoop 3 $ \i -> r $ $display "Cycle %d: rLoop 3 (%d)" cycle (asUint3 i)
rPar do
t do
r $ $display "Cycle %d: rPar, thread 1, rule 1" cycle
r $ $display "Cycle %d: rPar, thread 1, rule 2" cycle
r $ $display "Cycle %d: rPar, thread 1, rule 3" cycle
noR
r $ $display "Cycle %d: rPar, thread 1, rule 4" cycle
t do
r $ $display "Cycle %d: rPar, thread 2, rule 1" cycle
r $ $display "Cycle %d: rPar, thread 2, rule 2" cycle
t $ rRepeat 3 do
noR
r $ $display "Cycle %d: rPar, thread 3, rule 1" cycle
rParLoopUpTo 1 3 $ \i -> do
r $ $display "Cycle %d: rParLoopUpTo 1 3, rule 1 (%d)" cycle (asUint3 i)
r $ $display "Cycle %d: rParLoopUpTo 1 3, rule 2 (%d)" cycle (asUint3 i)
rParLoop 4 $ \i -> do
r $ $display "Cycle %d: rParLoop 4, rule 1 (%d)" cycle (asUint3 i)
r $ $display "Cycle %d: rParLoop 4, rule 2 (%d)" cycle (asUint3 i)
rPar do
t do
r $ $display "Cycle %d: rPar, rParLoop 4, subThread 1, rule 1 (%d)" cycle (asUint3 i)
r $ $display "Cycle %d: rPar, rParLoop 4, subThread 1, rule 2 (%d)" cycle (asUint3 i)
t do
r $ $display "Cycle %d: rPar, rParLoop 4, subThread 2, rule 1 (%d)" cycle (asUint3 i)
r $ $display "Cycle %d: rPar, rParLoop 4, subThread 2, rule 2 (%d)" cycle (asUint3 i)
do
r $ $display "Cycle %d: subsequence rule 1" cycle
r $ $display "Cycle %d: subsequence rule 2" cycle
rLoop 2 $ \i ->
rLoop 3 $ \j ->
r $ $display "Cycle %d: rLoop 2, rLoop 3 (%d, %d)" cycle (asUint3 i) (asUint3 j)
rParLoop 2 $ \i ->
rParLoop 3 $ \j ->
r $ $display "Cycle %d: rParLoop 2, rParLoop 3 (%d, %d)" cycle (asUint3 i) (asUint3 j)

r $ $display "Cycle %d: last rule" cycle
r $ pass

alwaysFailAtMaxCycleCount cycle
14 changes: 14 additions & 0 deletions testing/bsc.contrib/SequenceRules/sequence_rules.exp
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Include the boilerplate for bsc-contrib tests
set here [file join [absolute $srcdir] $subdir]
source $here/../contrib.tcl

if { $contribtest } {

add_contrib_dirs_to_path { SequenceRules }

# Run the basic test
test_c_veri_bs_modules SequenceRulesTest {}

restore_path

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
Cycle 0: rule 1
Cycle 1: rule 2
Cycle 3: rule 4
Cycle 4: repeated rule
Cycle 5: repeated rule
Cycle 6: repeated rule
Cycle 10: rLoopUpTo 2 4 (2)
Cycle 11: rLoopUpTo 2 4 (3)
Cycle 12: rLoopUpTo 2 4 (4)
Cycle 13: rLoop 3 (0)
Cycle 14: rLoop 3 (1)
Cycle 15: rLoop 3 (2)
Cycle 16: rPar, thread 1, rule 1
Cycle 16: rPar, thread 2, rule 1
Cycle 17: rPar, thread 1, rule 2
Cycle 17: rPar, thread 2, rule 2
Cycle 17: rPar, thread 3, rule 1
Cycle 18: rPar, thread 1, rule 3
Cycle 19: rPar, thread 3, rule 1
Cycle 20: rPar, thread 1, rule 4
Cycle 21: rPar, thread 3, rule 1
Cycle 22: rParLoopUpTo 1 3, rule 1 (1)
Cycle 22: rParLoopUpTo 1 3, rule 1 (2)
Cycle 22: rParLoopUpTo 1 3, rule 1 (3)
Cycle 23: rParLoopUpTo 1 3, rule 2 (1)
Cycle 23: rParLoopUpTo 1 3, rule 2 (2)
Cycle 23: rParLoopUpTo 1 3, rule 2 (3)
Cycle 24: rParLoop 4, rule 1 (0)
Cycle 24: rParLoop 4, rule 1 (1)
Cycle 24: rParLoop 4, rule 1 (2)
Cycle 24: rParLoop 4, rule 1 (3)
Cycle 25: rParLoop 4, rule 2 (0)
Cycle 25: rParLoop 4, rule 2 (1)
Cycle 25: rParLoop 4, rule 2 (2)
Cycle 25: rParLoop 4, rule 2 (3)
Cycle 26: rPar, rParLoop 4, subThread 1, rule 1 (0)
Cycle 26: rPar, rParLoop 4, subThread 2, rule 1 (0)
Cycle 26: rPar, rParLoop 4, subThread 1, rule 1 (1)
Cycle 26: rPar, rParLoop 4, subThread 2, rule 1 (1)
Cycle 26: rPar, rParLoop 4, subThread 1, rule 1 (2)
Cycle 26: rPar, rParLoop 4, subThread 2, rule 1 (2)
Cycle 26: rPar, rParLoop 4, subThread 1, rule 1 (3)
Cycle 26: rPar, rParLoop 4, subThread 2, rule 1 (3)
Cycle 27: rPar, rParLoop 4, subThread 1, rule 2 (0)
Cycle 27: rPar, rParLoop 4, subThread 2, rule 2 (0)
Cycle 27: rPar, rParLoop 4, subThread 1, rule 2 (1)
Cycle 27: rPar, rParLoop 4, subThread 2, rule 2 (1)
Cycle 27: rPar, rParLoop 4, subThread 1, rule 2 (2)
Cycle 27: rPar, rParLoop 4, subThread 2, rule 2 (2)
Cycle 27: rPar, rParLoop 4, subThread 1, rule 2 (3)
Cycle 27: rPar, rParLoop 4, subThread 2, rule 2 (3)
Cycle 28: subsequence rule 1
Cycle 29: subsequence rule 2
Cycle 30: rLoop 2, rLoop 3 (0, 0)
Cycle 31: rLoop 2, rLoop 3 (0, 1)
Cycle 32: rLoop 2, rLoop 3 (0, 2)
Cycle 33: rLoop 2, rLoop 3 (1, 0)
Cycle 34: rLoop 2, rLoop 3 (1, 1)
Cycle 35: rLoop 2, rLoop 3 (1, 2)
Cycle 36: rParLoop 2, rParLoop 3 (0, 0)
Cycle 36: rParLoop 2, rParLoop 3 (0, 1)
Cycle 36: rParLoop 2, rParLoop 3 (0, 2)
Cycle 36: rParLoop 2, rParLoop 3 (1, 0)
Cycle 36: rParLoop 2, rParLoop 3 (1, 1)
Cycle 36: rParLoop 2, rParLoop 3 (1, 2)
Cycle 37: last rule
PASS
Loading