cocotbext-interface 0.1.2__tar.gz → 0.1.4__tar.gz

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 (21) hide show
  1. {cocotbext_interface-0.1.2 → cocotbext_interface-0.1.4}/PKG-INFO +115 -116
  2. cocotbext_interface-0.1.4/README.md +276 -0
  3. {cocotbext_interface-0.1.2 → cocotbext_interface-0.1.4}/cocotbext/interface/__init__.py +0 -4
  4. cocotbext_interface-0.1.4/cocotbext/interface/_version.py +1 -0
  5. {cocotbext_interface-0.1.2 → cocotbext_interface-0.1.4}/cocotbext/interface/interface.py +99 -34
  6. cocotbext_interface-0.1.4/cocotbext/interface/prototypes/__init__.py +7 -0
  7. {cocotbext_interface-0.1.2/cocotbext/interface → cocotbext_interface-0.1.4/cocotbext/interface/prototypes}/memorymappedinterface.py +1 -2
  8. {cocotbext_interface-0.1.2/cocotbext/interface → cocotbext_interface-0.1.4/cocotbext/interface/prototypes}/streaminterface.py +1 -1
  9. {cocotbext_interface-0.1.2 → cocotbext_interface-0.1.4}/cocotbext/interface/utils.py +1 -0
  10. {cocotbext_interface-0.1.2 → cocotbext_interface-0.1.4}/cocotbext_interface.egg-info/PKG-INFO +115 -116
  11. {cocotbext_interface-0.1.2 → cocotbext_interface-0.1.4}/cocotbext_interface.egg-info/SOURCES.txt +3 -2
  12. {cocotbext_interface-0.1.2 → cocotbext_interface-0.1.4}/pyproject.toml +16 -7
  13. {cocotbext_interface-0.1.2 → cocotbext_interface-0.1.4}/tests/test_interface.py +127 -29
  14. {cocotbext_interface-0.1.2 → cocotbext_interface-0.1.4}/tests/test_utils.py +4 -1
  15. cocotbext_interface-0.1.2/README.md +0 -277
  16. cocotbext_interface-0.1.2/cocotbext/interface/_version.py +0 -1
  17. {cocotbext_interface-0.1.2 → cocotbext_interface-0.1.4}/LICENSE +0 -0
  18. {cocotbext_interface-0.1.2 → cocotbext_interface-0.1.4}/cocotbext_interface.egg-info/dependency_links.txt +0 -0
  19. {cocotbext_interface-0.1.2 → cocotbext_interface-0.1.4}/cocotbext_interface.egg-info/requires.txt +0 -0
  20. {cocotbext_interface-0.1.2 → cocotbext_interface-0.1.4}/cocotbext_interface.egg-info/top_level.txt +0 -0
  21. {cocotbext_interface-0.1.2 → cocotbext_interface-0.1.4}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cocotbext-interface
3
- Version: 0.1.2
3
+ Version: 0.1.4
4
4
  Summary: System Verilog style interface class for connecting Cocotb to DUT
5
5
  Author-email: Rasmus Grøndahl Olsen <simplhdl@gmail.com>
6
6
  Maintainer-email: Rasmus Grøndahl Olsen <simplhdl@gmail.com>
@@ -34,6 +34,7 @@ License: BSD 3-Clause License
34
34
  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
35
35
 
36
36
  Classifier: Development Status :: 4 - Beta
37
+ Classifier: Framework :: cocotb
37
38
  Classifier: Intended Audience :: Developers
38
39
  Classifier: Operating System :: OS Independent
39
40
  Classifier: Programming Language :: Python :: 3 :: Only
@@ -43,10 +44,9 @@ Classifier: Programming Language :: Python :: 3.10
43
44
  Classifier: Programming Language :: Python :: 3.11
44
45
  Classifier: Programming Language :: Python :: 3.12
45
46
  Classifier: Programming Language :: Python :: 3.13
46
- Classifier: Programming Language :: Python :: 3.14
47
47
  Classifier: Topic :: Scientific/Engineering
48
48
  Classifier: Topic :: Scientific/Engineering :: Electronic Design Automation (EDA)
49
- Requires-Python: >=3.8
49
+ Requires-Python: <3.14,>=3.8
50
50
  Description-Content-Type: text/markdown
51
51
  License-File: LICENSE
52
52
  Requires-Dist: cocotb>=2
@@ -54,57 +54,62 @@ Dynamic: license-file
54
54
 
55
55
  # Interface Documentation
56
56
 
57
- This interface package provides a Pythonic implementation of SystemVerilog-style
58
- **Interface**, **Modport**, and **Clocking Block** for cocotb. It is designed
59
- to bridge the gap between hardware-centric verification concepts and the
60
- flexibility of Python, ensuring race-free simulations and clean, reusable
61
- verification IP.
57
+ [![CI Status](https://github.com/RasmusGOlsen/cocotbext-interface/actions/workflows/ci.yml/badge.svg)](https://github.com/RasmusGOlsen/cocotbext-interface/actions)
58
+ [![codecov](https://codecov.io/gh/RasmusGOlsen/cocotbext-interface/graph/badge.svg?token=24GVY2TN6N)](https://app.codecov.io/gh/RasmusGOlsen/cocotbext-interface/tree/main)
59
+ [![PyPI version](https://img.shields.io/pypi/v/cocotbext-interface.svg)](https://pypi.org/project/cocotbext-interface/)
60
+ [![License](https://img.shields.io/badge/License-BSD%203--Clause-blue.svg)](https://opensource.org/licenses/BSD-3-Clause)
61
+ [![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff)
62
+
63
+ This interface package provides a Pythonic implementation of SystemVerilog-style **Interface**, **Modport**, and
64
+ **Clocking Block** for cocotb. It is designed to bridge the gap between hardware-centric verification concepts and the
65
+ flexibility of Python, ensuring race-free simulations and clean, reusable verification IP.
62
66
 
63
67
  ---
64
68
 
65
69
  ## 1. Project Overview
66
70
 
67
- In standard cocotb, signal assignments are immediate and reads are subject to
68
- simulator delta-cycle races. This package introduces a timing-accurate
69
- **Clocking Block** mechanism.
71
+ In standard cocotb, signal assignments are immediate and reads are subject to simulator delta-cycle races. This package
72
+ introduces a timing-accurate **Clocking Block** mechanism.
73
+
74
+ By mimicking the SystemVerilog `interface` structure, verification engineers can:
75
+
76
+ - **Group Signals:** Define structural signal groups once in a central class.
77
+ - **Restrict Access:** Use **Modports** to define directional access for different VIP components (e.g., Sources, Sinks
78
+ and Monitors).
79
+ - **Enforce Synchronicity:** Use **Clocking Blocks** to ensure signals are driven and sampled at the correct time
80
+ relative to clock edges, supporting both time-based and event-based skews.
81
+ - **Define Functional APIs:** Using Python methods (`def` and `async def`) within the interface to build API tasks for
82
+ implementing interface protocols such as AXI, Avalon, I2C, SPI, etc.
83
+
84
+ ---
85
+
86
+ ## 2. Installation
70
87
 
71
- By mimicking the SystemVerilog `interface` structure, verification engineers
72
- can:
88
+ Install the package from PyPI using pip:
73
89
 
74
- * **Group Signals:** Define structural signal groups once in a central class.
75
- * **Restrict Access:** Use **Modports** to define directional access for
76
- different VIP components (e.g., Sources, Sinks and Monitors).
77
- * **Enforce Synchronicity:** Use **Clocking Blocks** to ensure signals are
78
- driven and sampled at the correct time relative to clock edges, supporting
79
- both time-based and event-based skews.
80
- * **Define Functional APIs:** Using Python methods (`def` and `async def`)
81
- within the interface to build API tasks for implementing interface protocols
82
- such as AXI, Avalon, I2C, SPI, etc.
90
+ ```bash
91
+ pip install cocotbext-interface
92
+ ```
83
93
 
84
94
  ---
85
95
 
86
- ## 2. Part I: Designing the Interface
96
+ ## 3. Part I: Designing the Interface
87
97
 
88
- This section covers how to define the structure, timing, and access rules for a
89
- interface protocol.
98
+ This section covers how to define the structure, timing, and access rules for a interface protocol.
90
99
 
91
- ### 2.1 The Interface Class
100
+ ### 3.1 The Interface Class
92
101
 
93
- To define a bus, inherit from the Interface class. Signals are now defined as
94
- class attributes using type hints, which determines whether they are required
95
- or optional during RTL binding.
102
+ To define a bus, inherit from the Interface class. Signals are now defined as class attributes using type hints, which
103
+ determines whether they are required or optional during RTL binding.
96
104
 
97
105
  #### Defining Signals
98
106
 
99
- The framework uses the presence of a default value to distinguish between
100
- signal types:
107
+ The framework uses the presence of a default value to distinguish between signal types:
101
108
 
102
- **Mandatory Signals**: Define these with a type hint (e.g., `LogicArrayObject`).
103
- If no default value is assigned, the framework will raise an error if the
104
- signal cannot be found in the RTL.
109
+ **Mandatory Signals**: Define these with a type hint (e.g., `LogicArrayObject`). If no default value is assigned, the
110
+ framework will raise an error if the signal cannot be found in the RTL.
105
111
 
106
- **Optional Signals**: Assign None as a default value. This indicates that the
107
- signal may or may not exist in the design.
112
+ **Optional Signals**: Assign None as a default value. This indicates that the signal may or may not exist in the design.
108
113
 
109
114
  ```python
110
115
  class MyBus(Interface):
@@ -117,32 +122,31 @@ class MyBus(Interface):
117
122
 
118
123
  These attribute names are used directly by the framework to search for matching signal names within the RTL hierarchy.
119
124
 
120
- ### 2.2 Defining Clocking Blocks
125
+ ### 3.2 Defining Clocking Blocks
121
126
 
122
- Use the `@clocking` decorator on an inner class. This defines the temporal
123
- behavior for synchronous signals.
127
+ Use the `@clocking` decorator on an inner class. This defines the temporal behavior for synchronous signals.
124
128
 
125
- * `clock`: The name of the clock signal in the `signals` list.
126
- * `edge`: The trigger (e.g., `RisingEdge`, `FallingEdge`, `Edge`).
127
- * `input`: Input skew (Sample delay). Can be a `Timer` or another `Trigger`.
128
- * `output`: Output skew (Drive delay).
129
+ - `clock`: The name of the clock signal in the `signals` list.
130
+ - `edge`: The trigger (e.g., `RisingEdge`, `FallingEdge`, `Edge`).
131
+ - `input`: Input skew (Sample delay). Can be a `Timer` or another `Trigger`.
132
+ - `output`: Output skew (Drive delay).
129
133
 
130
- ### 2.3 Defining Modports
134
+ ### 3.3 Defining Modports
131
135
 
132
- Use the `@modport` decorator to group signals and clocking blocks for specific
133
- roles like `source`, `sink` or `monitor`.
136
+ Use the `@modport` decorator to group signals and clocking blocks for specific roles like `source`, `sink` or `monitor`.
134
137
 
135
- * `name`: Name of the modport
136
- * `clocking`: Name of the clocking block to link.
137
- * `Input`/`Output`/`InOut`: Signals accessible in this modport.
138
- * `Import`: Method names (APIs) exposed to this modport.
138
+ - `name`: Name of the modport
139
+ - `clocking`: Name of the clocking block to link.
140
+ - `Input`/`Output`/`InOut`: Signals accessible in this modport.
141
+ - `Import`: Method names (APIs) exposed to this modport.
139
142
 
140
- ### 2.4 Complete Definition Example
143
+ ### 3.4 Complete Definition Example
141
144
 
142
145
  ```python
146
+ from typing import Callable
143
147
  from cocotb.handles import LogicArrayObject, LogicObject
144
148
  from cocotb.triggers import RisingEdge, FallingEdge, Timer
145
- from interface_framework import Interface, modport, clocking, Import, Input, Output
149
+ from cocotbext.interface import Interface, modport, clocking, Import, Input, Output
146
150
 
147
151
  class AxiStream(Interface):
148
152
  clk: LogicObject
@@ -182,40 +186,34 @@ class AxiStream(Interface):
182
186
 
183
187
  ---
184
188
 
185
- ## 3. Part II: Using the Interface
189
+ ## 4. Part II: Using the Interface
186
190
 
187
- This section covers how to connect the interface to the DUT and use it in a
188
- test.
191
+ This section covers how to connect the interface to the DUT and use it in a test.
189
192
 
190
- ### 3.1 Instantiation & Connection
193
+ ### 4.1 Instantiation & Connection
191
194
 
192
- Interfaces are connected to the RTL using named constructors. These methods
193
- automatically map your class attributes to the corresponding signals in the HDL
194
- hierarchy.
195
+ Interfaces are connected to the RTL using named constructors. These methods automatically map your class attributes to
196
+ the corresponding signals in the HDL hierarchy.
195
197
 
196
198
  #### Connection Methods
197
199
 
198
200
  ##### 1. Direct Mapping (`from_entity`)
199
201
 
200
- Use `from_entity` when the signal names in the RTL match your class attribute
201
- names exactly under a specific hierarchy level. This is a strict connection
202
- method that does not support overrides or pattern substitutions.
202
+ Use `from_entity` when the signal names in the RTL match your class attribute names exactly under a specific hierarchy
203
+ level. This is a strict connection method that does not support overrides or pattern substitutions.
203
204
 
204
205
  ```python
205
- # Connects to dut.tdata and
206
- dut.rdy directly
206
+ # Connects to dut.data and dut.rdy directly
207
207
  bus = MyBus.from_entity(dut)
208
208
  ```
209
209
 
210
210
  ##### 2. Explicit Assignment (`from_signal`)
211
211
 
212
- The `from_signal` constructor allows for manual binding of signal handles to
213
- attributes. This is used when signals do not follow a pattern or exist in
214
- different hierarchies.
212
+ The `from_signal` constructor allows for manual binding of signal handles to attributes. This is used when signals do
213
+ not follow a pattern or exist in different hierarchies.
215
214
 
216
- * **Behavior:** Uses keyword arguments to map handles to class attributes.
217
- * **Validation:** Still enforces that all mandatory signals defined in the
218
- class are provided.
215
+ - **Behavior:** Uses keyword arguments to map handles to class attributes.
216
+ - **Validation:** Still enforces that all mandatory signals defined in the class are provided.
219
217
 
220
218
  ```python
221
219
  # Manual connection: Explicitly pass handles for each attribute
@@ -227,9 +225,8 @@ bus = MyBus.from_signal(
227
225
 
228
226
  ##### 3. Pattern Matching (`from_pattern`)
229
227
 
230
- The `from_pattern` method is used when signals follow a specific naming
231
- convention. The pattern argument must contain the % wildcard, which is
232
- substituted with each attribute name defined in your class.
228
+ The `from_pattern` method is used when signals follow a specific naming convention. The pattern argument must contain
229
+ the % wildcard, which is substituted with each attribute name defined in your class.
233
230
 
234
231
  ```python
235
232
  # Replaces % with attribute names: e.g. 'u_axi_tdata'
@@ -238,10 +235,8 @@ bus = MyBus.from_pattern(dut, pattern="u_axi_%")
238
235
 
239
236
  In addition to the % wildcard, `from_pattern` supports flexible discovery:
240
237
 
241
- * **Globbing:** Use * (any characters), ? (single character), or + (one or more
242
- to match signals.
243
- * **Regex:** Wrap the pattern in /.../ (e.g., /u_axi_.*_%/) for complex matching
244
- logic.
238
+ - **Globbing:** Use \* (any characters), ? (single character), or + (one or more to match signals.
239
+ - **Regex:** Wrap the pattern in /.../ (e.g., /u*axi*.\*\_%/) for complex matching logic.
245
240
 
246
241
  ```python
247
242
  # Simple substitution: Matches 'u_axi_tdata'
@@ -256,48 +251,55 @@ bus = MyBus.from_pattern(dut, pattern="/u_axi_[0-9]_%/")
256
251
 
257
252
  #### Advanced Pattern Options
258
253
 
259
- While `from_entity` is strict, `from_pattern` allows for flexibility when the
260
- RTL structure is non-standard:
254
+ While `from_entity` is strict, `from_pattern` allows for flexibility when the RTL structure is non-standard:
261
255
 
262
- * **Keyword Overrides:** Pass a signal handle as a keyword argument to skip the
263
- pattern search for that specific attribute.Indexing: Use the `idx` argument to
264
- index into all signals discovered via the pattern (e.g., `dut.u_axi_tdata[1]`).
256
+ - **Keyword Overrides:** Pass a signal handle as a keyword argument to skip the pattern search for that specific
257
+ attribute.
258
+ - **Indexing:** Use the `idx` argument to index into all signals discovered via the pattern (e.g.,
259
+ `dut.u_axi_tdata[1]`).
265
260
 
266
261
  ```python
267
262
  # Connect to index 1 and manually override 'rdy'
268
263
  bus = MyBus.from_pattern(dut, pattern="u_axi_%", idx=1, rdy=dut.global_rdy)
269
264
  ```
270
265
 
271
- > [!TIP] To confirm your signals are correctly bound, you can print `print(bus)`
272
- > or `print(f"{bus=}")` the bus object to inspect the resolved RTL paths.
266
+ > [!TIP] To confirm your signals are correctly bound, you can print `print(bus)` or `print(f"{bus=}")` the bus object to
267
+ > inspect the resolved RTL paths.
273
268
 
274
- ### 3.2 Synchronous Driving (Non-Blocking)
269
+ ### 4.2 Synchronous Driving (Non-Blocking)
275
270
 
276
- When using a modport's clocking block, driving a signal schedules an update for
277
- the next clock edge + output skew. It does not block the current coroutine.
271
+ When using a modport's clocking block, driving a signal schedules an update for the next clock edge + output skew. It
272
+ does not block the current coroutine.
278
273
 
279
274
  ```python
280
275
  # Drive data through the source modport
281
- bus.source.src_cb.tdata.value = 0xABCD
282
- bus.source.src_cb.tvalid.value = 1
276
+ bus.source.cb.tdata.value = 0xABCD
277
+ bus.source.cb.tvalid.value = 1
283
278
  ```
284
279
 
285
- ### 3.3 Synchronous Sampling (Blocking)
280
+ ### 4.3 Synchronous Sampling (Blocking)
281
+
282
+ To read a signal synchronously, you **must** use `await ...capture()`. This ensures the simulation waits for the clock
283
+ edge and the defined input skew before returning the value.
286
284
 
287
- To read a signal synchronously, you **must** use `await ...capture()`. This
288
- ensures the simulation waits for the clock edge and the defined input skew
289
- before returning the value.
285
+ The `capture()` method accepts two optional arguments:
286
+
287
+ - **`resolver`**: A resolution function (e.g., `cocotb.types.Logic.resolve`) to resolve 4-state logic values ('X', 'Z')
288
+ to a simpler 2-state representation.
289
+ - **`default`**: The value returned if the signal is an **optional signal** that is unconnected.
290
290
 
291
291
  ```python
292
292
  # Wait until sink is ready
293
- while await bus.source.src_cb.tready.capture() == 0:
294
- await bus.source.src_cb.wait() # Wait for 1 clock cycle
293
+ while await bus.source.cb.tready.capture() == 0:
294
+ await bus.source.cb.wait() # Wait for 1 clock cycle
295
+
296
+ # Capture with resolution and a default value for optional signals
297
+ data = await bus.sink.cb.tdata.capture(resolver=Logic.resolve, default=0)
295
298
  ```
296
299
 
297
- ### 3.4 Using Interface APIs
300
+ ### 4.4 Using Interface APIs
298
301
 
299
- Methods defined in the interface and imported in the modport can be called
300
- directly.
302
+ Methods defined in the interface and imported in the modport can be called directly.
301
303
 
302
304
  ```python
303
305
  # Call the reset task defined by the VIP developer
@@ -306,26 +308,23 @@ await bus.source.reset()
306
308
 
307
309
  ---
308
310
 
309
- ## 4. Technical Summary
311
+ ## 5. Technical Summary
310
312
 
311
- | SystemVerilog Concept | Framework Implementation | Behavior |
312
- | :--- | :--- | :--- |
313
- | `interface` | `class MyBus(Interface):` | Structural container. |
314
- | `.*` | `pattern="%"` | Substitution-based wildcard discovery. |
315
- | `.clk(sys_clk)` | `clk=dut.sys_clk` | Explicit named mapping override. |
316
- | `modport` | `@modport` | Role-based grouping (Source/Sink). |
317
- | `clocking` | `@clocking` | Temporal skews and edge triggers. |
318
- | `cb.sig <= val` | `cb_name.sig.value = val` | Non-blocking drive (Setter). |
319
- | `val = cb.sig` | `val = await cb_name.sig.capture()` | Synchronous sample (Coroutine). |
320
- | `##N` | `await cb_name.wait(N)` | Cycle-based delay. |
313
+ | SystemVerilog Concept | Framework Implementation | Behavior |
314
+ | :-------------------- | :---------------------------------- | :------------------------------------- |
315
+ | `interface` | `class MyBus(Interface):` | Structural container. |
316
+ | `.*` | `pattern="%"` | Substitution-based wildcard discovery. |
317
+ | `.clk(sys_clk)` | `clk=dut.sys_clk` | Explicit named mapping override. |
318
+ | `modport` | `@modport` | Role-based grouping (Source/Sink). |
319
+ | `clocking` | `@clocking` | Temporal skews and edge triggers. |
320
+ | `cb.sig <= val` | `cb_name.sig.value = val` | Non-blocking drive (Setter). |
321
+ | `val = cb.sig` | `val = await cb_name.sig.capture()` | Synchronous sample (Coroutine). |
322
+ | `##N` | `await cb_name.wait(N)` | Cycle-based delay. |
321
323
 
322
324
  ---
323
325
 
324
- ## 5. Best Practices
326
+ ## 6. Best Practices
325
327
 
326
- 1. Define separate clocking blocks for Source and Sink roles to account for
327
- different signal access and physical skews.
328
- 2. Don't give access to synchronous signals in the `modport`, this forces the
329
- user to use the clocking block.
330
- 3. Leverage the `%` wildcard in patterns to avoid manually connecting dozens of
331
- signals.
328
+ 1. Define separate clocking blocks for Source and Sink roles to account for different signal access and physical skews.
329
+ 2. Don't give access to synchronous signals in the `modport`, this forces the user to use the clocking block.
330
+ 3. Leverage the `%` wildcard in patterns to avoid manually connecting dozens of signals.
@@ -0,0 +1,276 @@
1
+ # Interface Documentation
2
+
3
+ [![CI Status](https://github.com/RasmusGOlsen/cocotbext-interface/actions/workflows/ci.yml/badge.svg)](https://github.com/RasmusGOlsen/cocotbext-interface/actions)
4
+ [![codecov](https://codecov.io/gh/RasmusGOlsen/cocotbext-interface/graph/badge.svg?token=24GVY2TN6N)](https://app.codecov.io/gh/RasmusGOlsen/cocotbext-interface/tree/main)
5
+ [![PyPI version](https://img.shields.io/pypi/v/cocotbext-interface.svg)](https://pypi.org/project/cocotbext-interface/)
6
+ [![License](https://img.shields.io/badge/License-BSD%203--Clause-blue.svg)](https://opensource.org/licenses/BSD-3-Clause)
7
+ [![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff)
8
+
9
+ This interface package provides a Pythonic implementation of SystemVerilog-style **Interface**, **Modport**, and
10
+ **Clocking Block** for cocotb. It is designed to bridge the gap between hardware-centric verification concepts and the
11
+ flexibility of Python, ensuring race-free simulations and clean, reusable verification IP.
12
+
13
+ ---
14
+
15
+ ## 1. Project Overview
16
+
17
+ In standard cocotb, signal assignments are immediate and reads are subject to simulator delta-cycle races. This package
18
+ introduces a timing-accurate **Clocking Block** mechanism.
19
+
20
+ By mimicking the SystemVerilog `interface` structure, verification engineers can:
21
+
22
+ - **Group Signals:** Define structural signal groups once in a central class.
23
+ - **Restrict Access:** Use **Modports** to define directional access for different VIP components (e.g., Sources, Sinks
24
+ and Monitors).
25
+ - **Enforce Synchronicity:** Use **Clocking Blocks** to ensure signals are driven and sampled at the correct time
26
+ relative to clock edges, supporting both time-based and event-based skews.
27
+ - **Define Functional APIs:** Using Python methods (`def` and `async def`) within the interface to build API tasks for
28
+ implementing interface protocols such as AXI, Avalon, I2C, SPI, etc.
29
+
30
+ ---
31
+
32
+ ## 2. Installation
33
+
34
+ Install the package from PyPI using pip:
35
+
36
+ ```bash
37
+ pip install cocotbext-interface
38
+ ```
39
+
40
+ ---
41
+
42
+ ## 3. Part I: Designing the Interface
43
+
44
+ This section covers how to define the structure, timing, and access rules for a interface protocol.
45
+
46
+ ### 3.1 The Interface Class
47
+
48
+ To define a bus, inherit from the Interface class. Signals are now defined as class attributes using type hints, which
49
+ determines whether they are required or optional during RTL binding.
50
+
51
+ #### Defining Signals
52
+
53
+ The framework uses the presence of a default value to distinguish between signal types:
54
+
55
+ **Mandatory Signals**: Define these with a type hint (e.g., `LogicArrayObject`). If no default value is assigned, the
56
+ framework will raise an error if the signal cannot be found in the RTL.
57
+
58
+ **Optional Signals**: Assign None as a default value. This indicates that the signal may or may not exist in the design.
59
+
60
+ ```python
61
+ class MyBus(Interface):
62
+ # Mandatory signal: Must be present in the RTL
63
+ data: LogicArrayObject
64
+
65
+ # Optional signal: Defaults to None if not found
66
+ rdy: LogicObject | None = None
67
+ ```
68
+
69
+ These attribute names are used directly by the framework to search for matching signal names within the RTL hierarchy.
70
+
71
+ ### 3.2 Defining Clocking Blocks
72
+
73
+ Use the `@clocking` decorator on an inner class. This defines the temporal behavior for synchronous signals.
74
+
75
+ - `clock`: The name of the clock signal in the `signals` list.
76
+ - `edge`: The trigger (e.g., `RisingEdge`, `FallingEdge`, `Edge`).
77
+ - `input`: Input skew (Sample delay). Can be a `Timer` or another `Trigger`.
78
+ - `output`: Output skew (Drive delay).
79
+
80
+ ### 3.3 Defining Modports
81
+
82
+ Use the `@modport` decorator to group signals and clocking blocks for specific roles like `source`, `sink` or `monitor`.
83
+
84
+ - `name`: Name of the modport
85
+ - `clocking`: Name of the clocking block to link.
86
+ - `Input`/`Output`/`InOut`: Signals accessible in this modport.
87
+ - `Import`: Method names (APIs) exposed to this modport.
88
+
89
+ ### 3.4 Complete Definition Example
90
+
91
+ ```python
92
+ from typing import Callable
93
+ from cocotb.handles import LogicArrayObject, LogicObject
94
+ from cocotb.triggers import RisingEdge, FallingEdge, Timer
95
+ from cocotbext.interface import Interface, modport, clocking, Import, Input, Output
96
+
97
+ class AxiStream(Interface):
98
+ clk: LogicObject
99
+ rst_n: LogicObject
100
+ tdata: LogicArrayObject
101
+ tvalid: LogicObject
102
+ tready: LogicObject
103
+
104
+ async def reset(self):
105
+ self.rst_n.value = 0
106
+ await Timer(10, 'ns')
107
+ self.rst_n.value = 1
108
+
109
+ # Define Source-side timing
110
+ @clocking(clock="clk", edge=RisingEdge, input=Timer(1, 'ns'), output=Timer(2, 'ns'))
111
+ class source_cb:
112
+ tready: Input[LogicObject]
113
+ tdata: Output[LogicArrayObject]
114
+ tvalid: Output[LogicObject]
115
+
116
+ # Define Sink-side timing
117
+ @clocking(clock="clk", edge=RisingEdge, input=Timer(1, 'ns'))
118
+ class sink_cb:
119
+ tdata: Input[LogicArrayObject]
120
+ tvalid: Input[LogicObject]
121
+ tready: Output[LogicObject]
122
+
123
+ @modport(clocking="source_cb")
124
+ class source:
125
+ rst_n: Output[LogicObject]
126
+ reset: Import[Callable]
127
+
128
+ @modport(clocking="sink_cb")
129
+ class sink:
130
+ reset: Import[Callable]
131
+ ```
132
+
133
+ ---
134
+
135
+ ## 4. Part II: Using the Interface
136
+
137
+ This section covers how to connect the interface to the DUT and use it in a test.
138
+
139
+ ### 4.1 Instantiation & Connection
140
+
141
+ Interfaces are connected to the RTL using named constructors. These methods automatically map your class attributes to
142
+ the corresponding signals in the HDL hierarchy.
143
+
144
+ #### Connection Methods
145
+
146
+ ##### 1. Direct Mapping (`from_entity`)
147
+
148
+ Use `from_entity` when the signal names in the RTL match your class attribute names exactly under a specific hierarchy
149
+ level. This is a strict connection method that does not support overrides or pattern substitutions.
150
+
151
+ ```python
152
+ # Connects to dut.data and dut.rdy directly
153
+ bus = MyBus.from_entity(dut)
154
+ ```
155
+
156
+ ##### 2. Explicit Assignment (`from_signal`)
157
+
158
+ The `from_signal` constructor allows for manual binding of signal handles to attributes. This is used when signals do
159
+ not follow a pattern or exist in different hierarchies.
160
+
161
+ - **Behavior:** Uses keyword arguments to map handles to class attributes.
162
+ - **Validation:** Still enforces that all mandatory signals defined in the class are provided.
163
+
164
+ ```python
165
+ # Manual connection: Explicitly pass handles for each attribute
166
+ bus = MyBus.from_signal(
167
+ tdata = dut.top.data_bus,
168
+ rdy = dut.extra_logic.ready_bit
169
+ )
170
+ ```
171
+
172
+ ##### 3. Pattern Matching (`from_pattern`)
173
+
174
+ The `from_pattern` method is used when signals follow a specific naming convention. The pattern argument must contain
175
+ the % wildcard, which is substituted with each attribute name defined in your class.
176
+
177
+ ```python
178
+ # Replaces % with attribute names: e.g. 'u_axi_tdata'
179
+ bus = MyBus.from_pattern(dut, pattern="u_axi_%")
180
+ ```
181
+
182
+ In addition to the % wildcard, `from_pattern` supports flexible discovery:
183
+
184
+ - **Globbing:** Use \* (any characters), ? (single character), or + (one or more to match signals.
185
+ - **Regex:** Wrap the pattern in /.../ (e.g., /u*axi*.\*\_%/) for complex matching logic.
186
+
187
+ ```python
188
+ # Simple substitution: Matches 'u_axi_tdata'
189
+ bus = MyBus.from_pattern(dut, pattern="u_axi_%")
190
+
191
+ # Globbing: Matches 'u_axi_0_tdata' or 'u_axi_stage1_tdata'
192
+ bus = MyBus.from_pattern(dut, pattern="u_axi_*_%")
193
+
194
+ # Regex: Matches signals with specific numeric suffixes
195
+ bus = MyBus.from_pattern(dut, pattern="/u_axi_[0-9]_%/")
196
+ ```
197
+
198
+ #### Advanced Pattern Options
199
+
200
+ While `from_entity` is strict, `from_pattern` allows for flexibility when the RTL structure is non-standard:
201
+
202
+ - **Keyword Overrides:** Pass a signal handle as a keyword argument to skip the pattern search for that specific
203
+ attribute.
204
+ - **Indexing:** Use the `idx` argument to index into all signals discovered via the pattern (e.g.,
205
+ `dut.u_axi_tdata[1]`).
206
+
207
+ ```python
208
+ # Connect to index 1 and manually override 'rdy'
209
+ bus = MyBus.from_pattern(dut, pattern="u_axi_%", idx=1, rdy=dut.global_rdy)
210
+ ```
211
+
212
+ > [!TIP] To confirm your signals are correctly bound, you can print `print(bus)` or `print(f"{bus=}")` the bus object to
213
+ > inspect the resolved RTL paths.
214
+
215
+ ### 4.2 Synchronous Driving (Non-Blocking)
216
+
217
+ When using a modport's clocking block, driving a signal schedules an update for the next clock edge + output skew. It
218
+ does not block the current coroutine.
219
+
220
+ ```python
221
+ # Drive data through the source modport
222
+ bus.source.cb.tdata.value = 0xABCD
223
+ bus.source.cb.tvalid.value = 1
224
+ ```
225
+
226
+ ### 4.3 Synchronous Sampling (Blocking)
227
+
228
+ To read a signal synchronously, you **must** use `await ...capture()`. This ensures the simulation waits for the clock
229
+ edge and the defined input skew before returning the value.
230
+
231
+ The `capture()` method accepts two optional arguments:
232
+
233
+ - **`resolver`**: A resolution function (e.g., `cocotb.types.Logic.resolve`) to resolve 4-state logic values ('X', 'Z')
234
+ to a simpler 2-state representation.
235
+ - **`default`**: The value returned if the signal is an **optional signal** that is unconnected.
236
+
237
+ ```python
238
+ # Wait until sink is ready
239
+ while await bus.source.cb.tready.capture() == 0:
240
+ await bus.source.cb.wait() # Wait for 1 clock cycle
241
+
242
+ # Capture with resolution and a default value for optional signals
243
+ data = await bus.sink.cb.tdata.capture(resolver=Logic.resolve, default=0)
244
+ ```
245
+
246
+ ### 4.4 Using Interface APIs
247
+
248
+ Methods defined in the interface and imported in the modport can be called directly.
249
+
250
+ ```python
251
+ # Call the reset task defined by the VIP developer
252
+ await bus.source.reset()
253
+ ```
254
+
255
+ ---
256
+
257
+ ## 5. Technical Summary
258
+
259
+ | SystemVerilog Concept | Framework Implementation | Behavior |
260
+ | :-------------------- | :---------------------------------- | :------------------------------------- |
261
+ | `interface` | `class MyBus(Interface):` | Structural container. |
262
+ | `.*` | `pattern="%"` | Substitution-based wildcard discovery. |
263
+ | `.clk(sys_clk)` | `clk=dut.sys_clk` | Explicit named mapping override. |
264
+ | `modport` | `@modport` | Role-based grouping (Source/Sink). |
265
+ | `clocking` | `@clocking` | Temporal skews and edge triggers. |
266
+ | `cb.sig <= val` | `cb_name.sig.value = val` | Non-blocking drive (Setter). |
267
+ | `val = cb.sig` | `val = await cb_name.sig.capture()` | Synchronous sample (Coroutine). |
268
+ | `##N` | `await cb_name.wait(N)` | Cycle-based delay. |
269
+
270
+ ---
271
+
272
+ ## 6. Best Practices
273
+
274
+ 1. Define separate clocking blocks for Source and Sink roles to account for different signal access and physical skews.
275
+ 2. Don't give access to synchronous signals in the `modport`, this forces the user to use the clocking block.
276
+ 3. Leverage the `%` wildcard in patterns to avoid manually connecting dozens of signals.
@@ -1,7 +1,5 @@
1
1
  from ._version import __version__
2
2
  from .interface import Interface, modport, clocking, Import, InOut, Input, Output
3
- from .memorymappedinterface import MemoryMappedInterface
4
- from .streaminterface import StreamInterface
5
3
 
6
4
  __all__ = [
7
5
  "__version__",
@@ -12,6 +10,4 @@ __all__ = [
12
10
  "Output",
13
11
  "modport",
14
12
  "clocking",
15
- "MemoryMappedInterface",
16
- "StreamInterface"
17
13
  ]
@@ -0,0 +1 @@
1
+ __version__ = "0.1.4"