nortl 1.4.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. nortl/__init__.py +85 -0
  2. nortl/components/__init__.py +8 -0
  3. nortl/components/channel.py +132 -0
  4. nortl/components/timer.py +73 -0
  5. nortl/core/__init__.py +40 -0
  6. nortl/core/checker.py +135 -0
  7. nortl/core/common/__init__.py +4 -0
  8. nortl/core/common/access.py +25 -0
  9. nortl/core/common/debug.py +6 -0
  10. nortl/core/common/naming_helper.py +33 -0
  11. nortl/core/constructs/__init__.py +13 -0
  12. nortl/core/constructs/condition.py +143 -0
  13. nortl/core/constructs/fork_join.py +84 -0
  14. nortl/core/constructs/loop.py +138 -0
  15. nortl/core/engine.py +575 -0
  16. nortl/core/exceptions.py +139 -0
  17. nortl/core/manager/__init__.py +6 -0
  18. nortl/core/manager/scratch_manager.py +128 -0
  19. nortl/core/manager/signal_manager.py +71 -0
  20. nortl/core/modifiers.py +136 -0
  21. nortl/core/module.py +181 -0
  22. nortl/core/operations.py +834 -0
  23. nortl/core/parameter.py +88 -0
  24. nortl/core/process.py +451 -0
  25. nortl/core/protocols.py +628 -0
  26. nortl/core/renderers/__init__.py +0 -0
  27. nortl/core/renderers/operations/__init__.py +34 -0
  28. nortl/core/renderers/operations/arithmetics.py +38 -0
  29. nortl/core/renderers/operations/base.py +111 -0
  30. nortl/core/renderers/operations/comparison.py +44 -0
  31. nortl/core/renderers/operations/logic.py +38 -0
  32. nortl/core/renderers/operations/misc.py +26 -0
  33. nortl/core/renderers/operations/slice.py +30 -0
  34. nortl/core/signal.py +878 -0
  35. nortl/core/state.py +201 -0
  36. nortl/py.typed +0 -0
  37. nortl/renderer/__init__.py +5 -0
  38. nortl/renderer/mermaid_renderer.py +38 -0
  39. nortl/renderer/networkx_renderer.py +29 -0
  40. nortl/renderer/verilog_renderer.py +325 -0
  41. nortl/renderer/verilog_utils/__init__.py +6 -0
  42. nortl/renderer/verilog_utils/formatter.py +29 -0
  43. nortl/renderer/verilog_utils/process.py +226 -0
  44. nortl/renderer/verilog_utils/structural.py +146 -0
  45. nortl/renderer/verilog_utils/utils.py +23 -0
  46. nortl/utils/__init__.py +0 -0
  47. nortl/utils/parse_utils.py +37 -0
  48. nortl/utils/templates/testbench.sv +41 -0
  49. nortl/utils/test_wrapper.py +218 -0
  50. nortl/utils/type_aliases.py +15 -0
  51. nortl/verilog_library/__init__.py +74 -0
  52. nortl/verilog_library/nortl_clock_gate.sv +20 -0
  53. nortl/verilog_library/nortl_count_down_timer.sv +50 -0
  54. nortl/verilog_library/nortl_delay.sv +66 -0
  55. nortl/verilog_library/nortl_edge_detector.sv +34 -0
  56. nortl/verilog_library/nortl_sync.sv +28 -0
  57. nortl-1.4.0.dist-info/METADATA +105 -0
  58. nortl-1.4.0.dist-info/RECORD +60 -0
  59. nortl-1.4.0.dist-info/WHEEL +4 -0
  60. nortl-1.4.0.dist-info/licenses/LICENSE +11 -0
nortl/__init__.py ADDED
@@ -0,0 +1,85 @@
1
+ """noRTL Code Generation Engine."""
2
+
3
+ from typing import Union
4
+
5
+ from nortl import verilog_library
6
+ from nortl.components import Channel, ElasticChannel, Timer
7
+ from nortl.core import All, Any, Concat, Const, CoreEngine, IfThenElse, Var, Volatile
8
+ from nortl.core.constructs import Condition, ElseCondition, Fork, ForLoop, WhileLoop
9
+ from nortl.core.protocols import ParameterProto, Renderable
10
+
11
+ __all__ = [
12
+ 'All',
13
+ 'Any',
14
+ 'Concat',
15
+ 'Const',
16
+ 'CoreEngine',
17
+ 'Engine',
18
+ 'IfThenElse',
19
+ 'Var',
20
+ 'Volatile',
21
+ ]
22
+
23
+
24
+ class ComponentsMixin(CoreEngine):
25
+ """Mixin class providing noRTL Components.
26
+
27
+ This allows easier access to the components, without needing to import them.
28
+ """
29
+
30
+ def create_channel(self, width: int, name: str = 'channel') -> Channel:
31
+ """Create a channel."""
32
+ return Channel(self, width, name=name)
33
+
34
+ def create_elastic_channel(self, width: int, name: str = 'channel') -> ElasticChannel:
35
+ """Create an elastic channel."""
36
+ return ElasticChannel(self, width, name=name)
37
+
38
+ def create_timer(self, width: Union[int, ParameterProto] = 16, instance_name_prefix: str = 'I_TIMER', clock_gating: bool = False) -> Timer:
39
+ """Create a timer."""
40
+ return Timer(self, width=width, instance_name_prefix=instance_name_prefix, clock_gating=clock_gating)
41
+
42
+
43
+ class ConstructsMixin(CoreEngine):
44
+ """Mixin class providing noRTL Constructs.
45
+
46
+ This allows easier access to the flow control constructs, without needing to import them.
47
+ """
48
+
49
+ def condition(self, condition: Renderable) -> Condition:
50
+ """Adds a Condition."""
51
+ return Condition(self, condition)
52
+
53
+ def else_condition(self) -> ElseCondition:
54
+ """Adds a Else Condition."""
55
+ return ElseCondition(self)
56
+
57
+ def fork(self, threadname: str) -> Fork:
58
+ """Adds a Fork."""
59
+ return Fork(self, threadname)
60
+
61
+ def for_loop(
62
+ self, start: Union[Renderable, int], stop: Union[Renderable, int], step: Union[Renderable, int] = Const(1), counter_width: int = 16
63
+ ) -> ForLoop:
64
+ """Adds a For loop."""
65
+ return ForLoop(self, start, stop, step=step, counter_width=counter_width)
66
+
67
+ def while_loop(self, condition: Renderable) -> WhileLoop:
68
+ """Adds a While loop."""
69
+ return WhileLoop(self, condition)
70
+
71
+
72
+ class Engine(ComponentsMixin, ConstructsMixin, CoreEngine):
73
+ """noRTL Engine."""
74
+
75
+ def __init__(self, module_name: str, reset_state_name: str = 'IDLE') -> None:
76
+ """Initialize a new noRTL Engine.
77
+
78
+ Arguments:
79
+ module_name: Name of the resulting SystemVerilog module.
80
+ reset_state_name: Default name for the reset state.
81
+ """
82
+ super().__init__(module_name, reset_state_name)
83
+
84
+ for module in verilog_library.get_modules():
85
+ self.add_module(module)
@@ -0,0 +1,8 @@
1
+ from .channel import Channel, ElasticChannel
2
+ from .timer import Timer
3
+
4
+ __all__ = [
5
+ 'Channel',
6
+ 'ElasticChannel',
7
+ 'Timer',
8
+ ]
@@ -0,0 +1,132 @@
1
+ import math
2
+ from typing import List, Sequence
3
+
4
+ from nortl.core import Concat, Const, IfThenElse, Volatile
5
+ from nortl.core.operations import to_renderable
6
+ from nortl.core.protocols import EngineProto, Renderable, ScratchSignalProto, SignalProto
7
+
8
+ # FIXME: Create Abstract base class for Channel
9
+
10
+
11
+ class Channel:
12
+ """A simple Channel with a ready/valid handshake to send data from one thread to another.
13
+
14
+ The send and rec functions realize a blocking transfer. This channel cannot be used for a elastic pipeline!
15
+ """
16
+
17
+ def __init__(self, engine: EngineProto, width: int, name: str = 'channel') -> None:
18
+ self.engine = engine
19
+ self.width = width
20
+
21
+ # FIXME validate if this channel already exists!
22
+ # Maybe turn into named entity
23
+
24
+ # All our signals cross the thread boundaries, so we have non-id rw access
25
+ self.ready = Volatile(self.engine.define_local(f'channel_{name}_ready', 1, 0), 'identical_rw')
26
+ self.valid = Volatile(self.engine.define_local(f'channel_{name}_valid', 1, 0), 'identical_rw')
27
+ self.data = Volatile(self.engine.define_local(f'channel_{name}_data', width, 0), 'identical_rw')
28
+
29
+ def send(self, data: Renderable | int) -> None:
30
+ """Sends the data over the channel to the target. The execution is blocked until the data has been received."""
31
+ tx_data = to_renderable(data)
32
+ self.engine.set(self.data, tx_data)
33
+ self.engine.set(self.valid, 1)
34
+ self.engine.wait_for(self.ready == 1)
35
+ self.engine.set(self.valid, 0)
36
+ self.engine.set(self.data, 0)
37
+ self.engine.sync()
38
+
39
+ def receive(self) -> ScratchSignalProto:
40
+ """Fetches a data item from the channel. The execution is blocked until the data has been received."""
41
+ target = self.engine.define_scratch(self.width)
42
+ self.engine.set(self.ready, 1)
43
+ self.engine.wait_for(self.valid == 1)
44
+ self.engine.set(target, self.data)
45
+ self.engine.set(self.ready, 0)
46
+ self.engine.sync()
47
+ return target
48
+
49
+
50
+ class ElasticChannel:
51
+ """A Channel with a FIFO for rate matching."""
52
+
53
+ def __init__(self, engine: EngineProto, width: int, name: str = 'channel', depth: int = 16) -> None:
54
+ ptr_width = math.ceil(math.log2(depth))
55
+
56
+ if not ptr_width.is_integer():
57
+ # FIXME this does not work
58
+ raise ValueError('The depth of a FIFO in an ElasticChannel must be 2**N!')
59
+
60
+ self.name = name
61
+
62
+ self.engine = engine
63
+ self.width = width
64
+ self.depth = depth
65
+
66
+ self.read_ptr = Volatile(self.engine.define_local(f'channel_{name}_readctr', ptr_width, 0))
67
+ self.write_ptr = Volatile(self.engine.define_local(f'channel_{name}_writectr', ptr_width, 0))
68
+
69
+ self.level = ((Concat('0b0', self.write_ptr) + self.depth) - self.read_ptr) % self.depth
70
+
71
+ # Create data store
72
+ self.data: List[Volatile[SignalProto]] = []
73
+ for i in range(depth):
74
+ data_item = Volatile(self.engine.define_local(f'channel_{name}_data_{i}', width, 0))
75
+ self.data.append(data_item)
76
+
77
+ def send(self, data: Renderable | int) -> None:
78
+ """Sends the data over the channel to the target. The execution is blocked until the data has been sent."""
79
+ if isinstance(data, int):
80
+ tx_data: Renderable = Const(data, self.width)
81
+ else:
82
+ tx_data = to_renderable(data)
83
+
84
+ self.engine.wait_for(self.level < (self.depth - 2))
85
+
86
+ for i in range(self.depth):
87
+ self.engine.set(self.data[i], IfThenElse(self.write_ptr == i, tx_data, self.data[i]))
88
+
89
+ self.engine.set(self.write_ptr, self.write_ptr + 1)
90
+ self.engine.sync()
91
+
92
+ def send_multiple(self, data_lst: List[Renderable | int]) -> None:
93
+ """Sends several data items over the channel to the target. The execution is blocked until the data has been sent.
94
+
95
+ Note that the list order is MSB first!
96
+ """
97
+
98
+ if (length := len(data_lst)) > self.depth - 2:
99
+ raise RuntimeError('send_multiple expects the item list to be smaller than (Channel.depth - 2)!')
100
+
101
+ tx_data: Sequence[Renderable] = tuple(Const(item, self.width) if isinstance(item, int) else to_renderable(item) for item in data_lst)
102
+
103
+ self.engine.wait_for(self.level < (self.depth - 1 - length))
104
+
105
+ tmp_data: List[Renderable] = [i for i in self.data]
106
+
107
+ for j, item in enumerate(tx_data[::-1]):
108
+ for i in range(self.depth):
109
+ tmp_data[i] = IfThenElse(((self.write_ptr + j) % self.depth) == (i), item, tmp_data[i])
110
+
111
+ for i in range(self.depth):
112
+ self.engine.set(self.data[i], tmp_data[i])
113
+
114
+ self.engine.sync()
115
+ self.engine.set(self.write_ptr, self.write_ptr + length)
116
+
117
+ self.engine.sync()
118
+
119
+ def receive(self) -> ScratchSignalProto:
120
+ """Fetches a data item from the channel. The execution is blocked until the data has been received."""
121
+ target = self.engine.define_scratch(self.width)
122
+ self.engine.wait_for(self.level != 0)
123
+
124
+ res: Renderable = Const(0, self.width)
125
+
126
+ for i in range(self.depth):
127
+ res = IfThenElse(self.read_ptr == i, self.data[i], res)
128
+
129
+ self.engine.set(target, res)
130
+ self.engine.set(self.read_ptr, self.read_ptr + 1)
131
+ self.engine.sync()
132
+ return target
@@ -0,0 +1,73 @@
1
+ from typing import Union
2
+
3
+ from nortl.core.protocols import EngineProto, ParameterProto, Renderable
4
+
5
+
6
+ class Timer:
7
+ """This class represents a timer in the engine. It provides functions for operating the timer conveniently.
8
+
9
+ It depends on the availability of a verilog module `nortl_count_down_timer`
10
+ """
11
+
12
+ def __init__(
13
+ self, engine: EngineProto, width: Union[int, ParameterProto] = 16, instance_name_prefix: str = 'I_TIMER', clock_gating: bool = False
14
+ ) -> None:
15
+ """Initializes a timer object in the engine.
16
+
17
+ This function instantiates a (count-down) timer module in the engine and holds the signals for the delay, reload and finish / zero.
18
+
19
+ Arguments:
20
+ engine (CoreEngine): Target engine object
21
+ width (int): width of the counter register
22
+ instance_name_prefix (str): Prefix for the instance name used in verilog. This also prefixes the names of the connected signals
23
+ clock_gating (bool): If true, the unit is connected to the gated clock. If clock gating is disable, this setting is ignored.
24
+ """
25
+ self.engine = engine
26
+
27
+ timer_idx = 0
28
+ while (instance_name := f'{instance_name_prefix}_{timer_idx}') in engine.module_instances:
29
+ timer_idx = timer_idx + 1
30
+
31
+ self.instance_name = instance_name
32
+
33
+ self.timer_module = self.engine.create_module_instance('nortl_count_down_timer', self.instance_name, clock_gating)
34
+
35
+ self.engine.override_module_parameter(self.instance_name, 'DATA_WIDTH', width)
36
+
37
+ self.delay = self.engine.define_local(f'{self.instance_name}_delay', reset_value=0, width=width) # Todo: Could be an async signal
38
+ self.reload = self.engine.define_local(f'{self.instance_name}_reload', reset_value=0) # Todo: Could be an async signal
39
+ self.zero = self.engine.define_local(f'{self.instance_name}_zero') # Todo: Could be an async signal
40
+
41
+ self.engine.connect_module_port(self.instance_name, 'DELAY', self.delay)
42
+ self.engine.connect_module_port(self.instance_name, 'RELOAD', self.reload)
43
+ self.engine.connect_module_port(self.instance_name, 'ZERO', self.zero)
44
+
45
+ def wait_delay(self, delay: Union[Renderable, int, bool]) -> None:
46
+ """Wait for a given delay without returning control to the engine. This can be seen as blocking delay.
47
+
48
+ Arguments:
49
+ delay (Renderable): Delay to wait for (in cycles)
50
+ """
51
+ self.start_delay(delay)
52
+ self.engine.wait_for(self.finished)
53
+
54
+ def start_delay(self, delay: Union[Renderable, int, bool]) -> None:
55
+ """Start the timer with a given delay. This acts as a non-blocking delay and therefore returns control after starting the timer.
56
+
57
+ Arguments:
58
+ delay (Renderable): Delay to wait for (in cycles)
59
+ """
60
+ self.engine.set(self.delay, delay)
61
+ self.engine.set(self.reload, 1)
62
+ self.engine.sync()
63
+ self.engine.set(self.reload, 0)
64
+ self.engine.sync()
65
+
66
+ @property
67
+ def finished(self) -> Renderable:
68
+ """Returns the signal (Renderable) that signals, if the timer has finished, i.e. the counter register is zero.
69
+
70
+ Returns:
71
+ Renderable: Zero-Flag of the counter
72
+ """
73
+ return self.zero
nortl/core/__init__.py ADDED
@@ -0,0 +1,40 @@
1
+ from .engine import CoreEngine
2
+ from .exceptions import (
3
+ ConflictingAssignmentError,
4
+ ExclusiveReadError,
5
+ ExclusiveWriteError,
6
+ ForbiddenAssignmentError,
7
+ NonIdenticalRWError,
8
+ OwnershipError,
9
+ TransitionLockError,
10
+ TransitionRestrictionError,
11
+ UnfinishedForwardDeclarationError,
12
+ WriteViolationError,
13
+ )
14
+ from .modifiers import Volatile
15
+ from .operations import All, Any, Concat, Const, IfThenElse, Var
16
+ from .parameter import Parameter
17
+ from .signal import Signal
18
+
19
+ __all__ = [
20
+ 'All',
21
+ 'Any',
22
+ 'Concat',
23
+ 'ConflictingAssignmentError',
24
+ 'Const',
25
+ 'CoreEngine',
26
+ 'ExclusiveReadError',
27
+ 'ExclusiveWriteError',
28
+ 'ForbiddenAssignmentError',
29
+ 'IfThenElse',
30
+ 'NonIdenticalRWError',
31
+ 'OwnershipError',
32
+ 'Parameter',
33
+ 'Signal',
34
+ 'TransitionLockError',
35
+ 'TransitionRestrictionError',
36
+ 'UnfinishedForwardDeclarationError',
37
+ 'Var',
38
+ 'Volatile',
39
+ 'WriteViolationError',
40
+ ]
nortl/core/checker.py ADDED
@@ -0,0 +1,135 @@
1
+ from logging import getLogger
2
+ from typing import Callable, ClassVar, Dict, List, Literal, Protocol, Set, Type
3
+ from warnings import warn
4
+
5
+ from .exceptions import ExclusiveReadError, ExclusiveWriteError, NonIdenticalRWError
6
+ from .protocols import ACCESS_CHECKS, SIGNAL_ACCESS_CHECKS, StaticAccessProto
7
+
8
+ __all__ = [
9
+ 'StaticAccessChecker',
10
+ 'set_severity_level',
11
+ ]
12
+
13
+ logger = getLogger(__name__)
14
+
15
+
16
+ T_SEVERITY_LEVEL = Literal['raise', 'warn', 'log', 'suppress']
17
+
18
+
19
+ def set_severity_level(level: T_SEVERITY_LEVEL) -> None:
20
+ """Set severity level for noRTL access checks."""
21
+ BaseChecker.severity = level
22
+
23
+
24
+ class AccessControlledSignal(Protocol):
25
+ """Minimal protocol for access controlled signals.
26
+
27
+ This supports both real signals and Volatile modifiers.
28
+ """
29
+
30
+ @property
31
+ def name(self) -> str: ...
32
+
33
+ @property
34
+ def read_accesses(self) -> Set[StaticAccessProto]: ...
35
+
36
+ @property
37
+ def write_accesses(self) -> Set[StaticAccessProto]: ...
38
+
39
+
40
+ class BaseChecker:
41
+ """Baseclass for checkers."""
42
+
43
+ severity: ClassVar[T_SEVERITY_LEVEL] = 'raise'
44
+
45
+ @staticmethod
46
+ def throw(exception_type: Type[Exception], msg: str) -> None:
47
+ """Throw exception."""
48
+ match BaseChecker.severity:
49
+ case 'raise':
50
+ raise exception_type(msg)
51
+ case 'warn':
52
+ warn(msg, stacklevel=2)
53
+ case 'log':
54
+ logger.error(f'[{exception_type.__name__}] {msg}', stacklevel=2, stack_info=True)
55
+
56
+
57
+ class StaticAccessChecker(BaseChecker):
58
+ """Static access checker for signals."""
59
+
60
+ # FIXME: Adapt traceback of the checks to show position in the actual user-code where the exception happened
61
+
62
+ def __init__(self, signal: AccessControlledSignal) -> None:
63
+ self.signal = signal
64
+ self.enabled_checks: List[SIGNAL_ACCESS_CHECKS] = ['exclusive_read', 'exclusive_write', 'identical_rw']
65
+ self._cached_reading_thread_names: Set[str] = set()
66
+ self._cached_writing_thread_names: Set[str] = set()
67
+
68
+ self.check_mapping: Dict[ACCESS_CHECKS, Callable[[], None]] = {
69
+ 'exclusive_read': self.check_exclusive_read,
70
+ 'exclusive_write': self.check_exclusive_write,
71
+ 'identical_rw': self.check_identical_rw,
72
+ }
73
+
74
+ @property
75
+ def reading_thread_names(self) -> Set[str]:
76
+ if len(self._cached_reading_thread_names) != 0:
77
+ return self._cached_reading_thread_names
78
+
79
+ threadnames: Set[str] = set()
80
+ for access in self.signal.read_accesses:
81
+ if access.active and access.thread.running:
82
+ threadnames.add(f'{access.thread.worker.name}.{access.thread.name}')
83
+
84
+ self._cached_reading_thread_names = threadnames
85
+
86
+ return threadnames
87
+
88
+ @property
89
+ def writing_thread_names(self) -> Set[str]:
90
+ if len(self._cached_writing_thread_names) != 0:
91
+ return self._cached_writing_thread_names
92
+
93
+ threadnames: Set[str] = set()
94
+ for access in self.signal.write_accesses:
95
+ if access.active and access.thread.running:
96
+ threadnames.add(f'{access.thread.worker.name}.{access.thread.name}')
97
+
98
+ self._cached_writing_thread_names = threadnames
99
+
100
+ return threadnames
101
+
102
+ def disable_check(self, check: SIGNAL_ACCESS_CHECKS) -> None:
103
+ self.enabled_checks.remove(check)
104
+
105
+ def check(self, ignore: Set[ACCESS_CHECKS] = set()) -> None:
106
+ """This function executes all checks and raises errors, if needed."""
107
+
108
+ # Clear out caches
109
+ self._cached_reading_thread_names = set()
110
+ self._cached_writing_thread_names = set()
111
+
112
+ # Actual Check
113
+ for check in self.enabled_checks:
114
+ if check not in ignore:
115
+ self.check_mapping[check]()
116
+
117
+ def check_exclusive_read(self) -> None:
118
+ if len(self.reading_thread_names) > 1:
119
+ self.throw(
120
+ ExclusiveReadError, f'Signal {self.signal} has been read by more than one thread! Reading Threads: {self.reading_thread_names}'
121
+ )
122
+
123
+ def check_exclusive_write(self) -> None:
124
+ if len(self.writing_thread_names) > 1:
125
+ self.throw(
126
+ ExclusiveWriteError, f'Signal {self.signal} has been written by more than one thread! Writing Threads: {self.writing_thread_names}'
127
+ )
128
+
129
+ def check_identical_rw(self) -> None:
130
+ if len(self.writing_thread_names) == 0 or len(self.reading_thread_names) == 0:
131
+ pass
132
+ elif self.writing_thread_names != self.reading_thread_names:
133
+ self.throw(
134
+ NonIdenticalRWError, f'Signal {self.signal} has been written by {self.writing_thread_names} and read by {self.reading_thread_names}.'
135
+ )
@@ -0,0 +1,4 @@
1
+ from .access import StaticAccess
2
+ from .naming_helper import NamedEntity
3
+
4
+ __all__ = ['NamedEntity', 'StaticAccess']
@@ -0,0 +1,25 @@
1
+ from nortl.core.protocols import ThreadProto
2
+
3
+ from .debug import DebugEntity
4
+
5
+
6
+ class StaticAccess(DebugEntity):
7
+ """Model for an access to a signal.
8
+
9
+ This class is used as a container to hold references to the thread that caused the access and information about what caused the access where in the code.
10
+ The signal object can then hold a list (or deque) of accesses for executing checks for e.g. two parallel-running threads accessing the register.
11
+
12
+ Note that this does not include checks like access counters that verify,
13
+ that the number of reads is equal to the number of writes so that no data gets lost.
14
+ This structure is only used for detecting concurrent accesses at assemble-time of the noRTL engine (where the code assembles the internal noRTL engine model).
15
+ Realizing access counters is a task to be performed at other levels of abstraction, e.g. in the rendered Verilog code where also formal properties
16
+ can be included for verification in simulation.
17
+ """
18
+
19
+ def __init__(self, thread: ThreadProto) -> None:
20
+ super().__init__()
21
+ self.thread = thread
22
+ self.active = True
23
+
24
+ def disable(self) -> None:
25
+ self.active = False
@@ -0,0 +1,6 @@
1
+ import inspect
2
+
3
+
4
+ class DebugEntity:
5
+ def __init__(self) -> None:
6
+ self.debug_stack = inspect.trace()
@@ -0,0 +1,33 @@
1
+ from typing import Any, Dict
2
+
3
+ from .debug import DebugEntity
4
+
5
+
6
+ class NamedEntity(DebugEntity):
7
+ """Model a named something in Verilog and implement the feature to add metadata.
8
+
9
+ This class will (in future) ensure that there are no naming collisions since all names of verilog objects will be stored here.
10
+ To add traceability, this class inherits from DebugEntity that stores the current execution trace.
11
+ """
12
+
13
+ def __init__(self, name: str) -> None:
14
+ super().__init__()
15
+ self._metadata: Dict[str, Any] = {}
16
+ self._name = name
17
+
18
+ @property
19
+ def name(self) -> str:
20
+ """Just return name."""
21
+ return self._name
22
+
23
+ def get_metadata(self, key: str, default: Any = None) -> Any:
24
+ """Get metadata item."""
25
+ return self._metadata.get(key, default)
26
+
27
+ def set_metadata(self, key: str, value: Any) -> None:
28
+ """Set metadata item."""
29
+ self._metadata[key] = value
30
+
31
+ def has_metadata(self, key: str) -> bool:
32
+ """Test if a certain key is present in metadata."""
33
+ return key in self._metadata
@@ -0,0 +1,13 @@
1
+ """This module will contain helpers for writing sub-engines and wrapping existing functions outside of the core."""
2
+
3
+ from .condition import Condition, ElseCondition
4
+ from .fork_join import Fork
5
+ from .loop import ForLoop, WhileLoop
6
+
7
+ __all__ = [
8
+ 'Condition',
9
+ 'ElseCondition',
10
+ 'ForLoop',
11
+ 'Fork',
12
+ 'WhileLoop',
13
+ ]