cocotbext-interface 0.1.3__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.
- {cocotbext_interface-0.1.3 → cocotbext_interface-0.1.4}/PKG-INFO +41 -21
- {cocotbext_interface-0.1.3 → cocotbext_interface-0.1.4}/README.md +40 -20
- cocotbext_interface-0.1.4/cocotbext/interface/_version.py +1 -0
- {cocotbext_interface-0.1.3 → cocotbext_interface-0.1.4}/cocotbext/interface/interface.py +14 -4
- {cocotbext_interface-0.1.3 → cocotbext_interface-0.1.4}/cocotbext_interface.egg-info/PKG-INFO +41 -21
- {cocotbext_interface-0.1.3 → cocotbext_interface-0.1.4}/tests/test_interface.py +49 -1
- cocotbext_interface-0.1.3/cocotbext/interface/_version.py +0 -1
- {cocotbext_interface-0.1.3 → cocotbext_interface-0.1.4}/LICENSE +0 -0
- {cocotbext_interface-0.1.3 → cocotbext_interface-0.1.4}/cocotbext/interface/__init__.py +0 -0
- {cocotbext_interface-0.1.3 → cocotbext_interface-0.1.4}/cocotbext/interface/prototypes/__init__.py +0 -0
- {cocotbext_interface-0.1.3 → cocotbext_interface-0.1.4}/cocotbext/interface/prototypes/memorymappedinterface.py +0 -0
- {cocotbext_interface-0.1.3 → cocotbext_interface-0.1.4}/cocotbext/interface/prototypes/streaminterface.py +0 -0
- {cocotbext_interface-0.1.3 → cocotbext_interface-0.1.4}/cocotbext/interface/utils.py +0 -0
- {cocotbext_interface-0.1.3 → cocotbext_interface-0.1.4}/cocotbext_interface.egg-info/SOURCES.txt +0 -0
- {cocotbext_interface-0.1.3 → cocotbext_interface-0.1.4}/cocotbext_interface.egg-info/dependency_links.txt +0 -0
- {cocotbext_interface-0.1.3 → cocotbext_interface-0.1.4}/cocotbext_interface.egg-info/requires.txt +0 -0
- {cocotbext_interface-0.1.3 → cocotbext_interface-0.1.4}/cocotbext_interface.egg-info/top_level.txt +0 -0
- {cocotbext_interface-0.1.3 → cocotbext_interface-0.1.4}/pyproject.toml +0 -0
- {cocotbext_interface-0.1.3 → cocotbext_interface-0.1.4}/setup.cfg +0 -0
- {cocotbext_interface-0.1.3 → cocotbext_interface-0.1.4}/tests/test_utils.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: cocotbext-interface
|
|
3
|
-
Version: 0.1.
|
|
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>
|
|
@@ -83,11 +83,21 @@ By mimicking the SystemVerilog `interface` structure, verification engineers can
|
|
|
83
83
|
|
|
84
84
|
---
|
|
85
85
|
|
|
86
|
-
## 2.
|
|
86
|
+
## 2. Installation
|
|
87
|
+
|
|
88
|
+
Install the package from PyPI using pip:
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
pip install cocotbext-interface
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
---
|
|
95
|
+
|
|
96
|
+
## 3. Part I: Designing the Interface
|
|
87
97
|
|
|
88
98
|
This section covers how to define the structure, timing, and access rules for a interface protocol.
|
|
89
99
|
|
|
90
|
-
###
|
|
100
|
+
### 3.1 The Interface Class
|
|
91
101
|
|
|
92
102
|
To define a bus, inherit from the Interface class. Signals are now defined as class attributes using type hints, which
|
|
93
103
|
determines whether they are required or optional during RTL binding.
|
|
@@ -112,7 +122,7 @@ class MyBus(Interface):
|
|
|
112
122
|
|
|
113
123
|
These attribute names are used directly by the framework to search for matching signal names within the RTL hierarchy.
|
|
114
124
|
|
|
115
|
-
###
|
|
125
|
+
### 3.2 Defining Clocking Blocks
|
|
116
126
|
|
|
117
127
|
Use the `@clocking` decorator on an inner class. This defines the temporal behavior for synchronous signals.
|
|
118
128
|
|
|
@@ -121,7 +131,7 @@ Use the `@clocking` decorator on an inner class. This defines the temporal behav
|
|
|
121
131
|
- `input`: Input skew (Sample delay). Can be a `Timer` or another `Trigger`.
|
|
122
132
|
- `output`: Output skew (Drive delay).
|
|
123
133
|
|
|
124
|
-
###
|
|
134
|
+
### 3.3 Defining Modports
|
|
125
135
|
|
|
126
136
|
Use the `@modport` decorator to group signals and clocking blocks for specific roles like `source`, `sink` or `monitor`.
|
|
127
137
|
|
|
@@ -130,12 +140,13 @@ Use the `@modport` decorator to group signals and clocking blocks for specific r
|
|
|
130
140
|
- `Input`/`Output`/`InOut`: Signals accessible in this modport.
|
|
131
141
|
- `Import`: Method names (APIs) exposed to this modport.
|
|
132
142
|
|
|
133
|
-
###
|
|
143
|
+
### 3.4 Complete Definition Example
|
|
134
144
|
|
|
135
145
|
```python
|
|
146
|
+
from typing import Callable
|
|
136
147
|
from cocotb.handles import LogicArrayObject, LogicObject
|
|
137
148
|
from cocotb.triggers import RisingEdge, FallingEdge, Timer
|
|
138
|
-
from
|
|
149
|
+
from cocotbext.interface import Interface, modport, clocking, Import, Input, Output
|
|
139
150
|
|
|
140
151
|
class AxiStream(Interface):
|
|
141
152
|
clk: LogicObject
|
|
@@ -175,11 +186,11 @@ class AxiStream(Interface):
|
|
|
175
186
|
|
|
176
187
|
---
|
|
177
188
|
|
|
178
|
-
##
|
|
189
|
+
## 4. Part II: Using the Interface
|
|
179
190
|
|
|
180
191
|
This section covers how to connect the interface to the DUT and use it in a test.
|
|
181
192
|
|
|
182
|
-
###
|
|
193
|
+
### 4.1 Instantiation & Connection
|
|
183
194
|
|
|
184
195
|
Interfaces are connected to the RTL using named constructors. These methods automatically map your class attributes to
|
|
185
196
|
the corresponding signals in the HDL hierarchy.
|
|
@@ -192,8 +203,7 @@ Use `from_entity` when the signal names in the RTL match your class attribute na
|
|
|
192
203
|
level. This is a strict connection method that does not support overrides or pattern substitutions.
|
|
193
204
|
|
|
194
205
|
```python
|
|
195
|
-
# Connects to dut.
|
|
196
|
-
dut.rdy directly
|
|
206
|
+
# Connects to dut.data and dut.rdy directly
|
|
197
207
|
bus = MyBus.from_entity(dut)
|
|
198
208
|
```
|
|
199
209
|
|
|
@@ -244,7 +254,8 @@ bus = MyBus.from_pattern(dut, pattern="/u_axi_[0-9]_%/")
|
|
|
244
254
|
While `from_entity` is strict, `from_pattern` allows for flexibility when the RTL structure is non-standard:
|
|
245
255
|
|
|
246
256
|
- **Keyword Overrides:** Pass a signal handle as a keyword argument to skip the pattern search for that specific
|
|
247
|
-
attribute.
|
|
257
|
+
attribute.
|
|
258
|
+
- **Indexing:** Use the `idx` argument to index into all signals discovered via the pattern (e.g.,
|
|
248
259
|
`dut.u_axi_tdata[1]`).
|
|
249
260
|
|
|
250
261
|
```python
|
|
@@ -255,29 +266,38 @@ bus = MyBus.from_pattern(dut, pattern="u_axi_%", idx=1, rdy=dut.global_rdy)
|
|
|
255
266
|
> [!TIP] To confirm your signals are correctly bound, you can print `print(bus)` or `print(f"{bus=}")` the bus object to
|
|
256
267
|
> inspect the resolved RTL paths.
|
|
257
268
|
|
|
258
|
-
###
|
|
269
|
+
### 4.2 Synchronous Driving (Non-Blocking)
|
|
259
270
|
|
|
260
271
|
When using a modport's clocking block, driving a signal schedules an update for the next clock edge + output skew. It
|
|
261
272
|
does not block the current coroutine.
|
|
262
273
|
|
|
263
274
|
```python
|
|
264
275
|
# Drive data through the source modport
|
|
265
|
-
bus.source.
|
|
266
|
-
bus.source.
|
|
276
|
+
bus.source.cb.tdata.value = 0xABCD
|
|
277
|
+
bus.source.cb.tvalid.value = 1
|
|
267
278
|
```
|
|
268
279
|
|
|
269
|
-
###
|
|
280
|
+
### 4.3 Synchronous Sampling (Blocking)
|
|
270
281
|
|
|
271
282
|
To read a signal synchronously, you **must** use `await ...capture()`. This ensures the simulation waits for the clock
|
|
272
283
|
edge and the defined input skew before returning the value.
|
|
273
284
|
|
|
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
|
+
|
|
274
291
|
```python
|
|
275
292
|
# Wait until sink is ready
|
|
276
|
-
while await bus.source.
|
|
277
|
-
await bus.source.
|
|
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)
|
|
278
298
|
```
|
|
279
299
|
|
|
280
|
-
###
|
|
300
|
+
### 4.4 Using Interface APIs
|
|
281
301
|
|
|
282
302
|
Methods defined in the interface and imported in the modport can be called directly.
|
|
283
303
|
|
|
@@ -288,7 +308,7 @@ await bus.source.reset()
|
|
|
288
308
|
|
|
289
309
|
---
|
|
290
310
|
|
|
291
|
-
##
|
|
311
|
+
## 5. Technical Summary
|
|
292
312
|
|
|
293
313
|
| SystemVerilog Concept | Framework Implementation | Behavior |
|
|
294
314
|
| :-------------------- | :---------------------------------- | :------------------------------------- |
|
|
@@ -303,7 +323,7 @@ await bus.source.reset()
|
|
|
303
323
|
|
|
304
324
|
---
|
|
305
325
|
|
|
306
|
-
##
|
|
326
|
+
## 6. Best Practices
|
|
307
327
|
|
|
308
328
|
1. Define separate clocking blocks for Source and Sink roles to account for different signal access and physical skews.
|
|
309
329
|
2. Don't give access to synchronous signals in the `modport`, this forces the user to use the clocking block.
|
|
@@ -29,11 +29,21 @@ By mimicking the SystemVerilog `interface` structure, verification engineers can
|
|
|
29
29
|
|
|
30
30
|
---
|
|
31
31
|
|
|
32
|
-
## 2.
|
|
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
|
|
33
43
|
|
|
34
44
|
This section covers how to define the structure, timing, and access rules for a interface protocol.
|
|
35
45
|
|
|
36
|
-
###
|
|
46
|
+
### 3.1 The Interface Class
|
|
37
47
|
|
|
38
48
|
To define a bus, inherit from the Interface class. Signals are now defined as class attributes using type hints, which
|
|
39
49
|
determines whether they are required or optional during RTL binding.
|
|
@@ -58,7 +68,7 @@ class MyBus(Interface):
|
|
|
58
68
|
|
|
59
69
|
These attribute names are used directly by the framework to search for matching signal names within the RTL hierarchy.
|
|
60
70
|
|
|
61
|
-
###
|
|
71
|
+
### 3.2 Defining Clocking Blocks
|
|
62
72
|
|
|
63
73
|
Use the `@clocking` decorator on an inner class. This defines the temporal behavior for synchronous signals.
|
|
64
74
|
|
|
@@ -67,7 +77,7 @@ Use the `@clocking` decorator on an inner class. This defines the temporal behav
|
|
|
67
77
|
- `input`: Input skew (Sample delay). Can be a `Timer` or another `Trigger`.
|
|
68
78
|
- `output`: Output skew (Drive delay).
|
|
69
79
|
|
|
70
|
-
###
|
|
80
|
+
### 3.3 Defining Modports
|
|
71
81
|
|
|
72
82
|
Use the `@modport` decorator to group signals and clocking blocks for specific roles like `source`, `sink` or `monitor`.
|
|
73
83
|
|
|
@@ -76,12 +86,13 @@ Use the `@modport` decorator to group signals and clocking blocks for specific r
|
|
|
76
86
|
- `Input`/`Output`/`InOut`: Signals accessible in this modport.
|
|
77
87
|
- `Import`: Method names (APIs) exposed to this modport.
|
|
78
88
|
|
|
79
|
-
###
|
|
89
|
+
### 3.4 Complete Definition Example
|
|
80
90
|
|
|
81
91
|
```python
|
|
92
|
+
from typing import Callable
|
|
82
93
|
from cocotb.handles import LogicArrayObject, LogicObject
|
|
83
94
|
from cocotb.triggers import RisingEdge, FallingEdge, Timer
|
|
84
|
-
from
|
|
95
|
+
from cocotbext.interface import Interface, modport, clocking, Import, Input, Output
|
|
85
96
|
|
|
86
97
|
class AxiStream(Interface):
|
|
87
98
|
clk: LogicObject
|
|
@@ -121,11 +132,11 @@ class AxiStream(Interface):
|
|
|
121
132
|
|
|
122
133
|
---
|
|
123
134
|
|
|
124
|
-
##
|
|
135
|
+
## 4. Part II: Using the Interface
|
|
125
136
|
|
|
126
137
|
This section covers how to connect the interface to the DUT and use it in a test.
|
|
127
138
|
|
|
128
|
-
###
|
|
139
|
+
### 4.1 Instantiation & Connection
|
|
129
140
|
|
|
130
141
|
Interfaces are connected to the RTL using named constructors. These methods automatically map your class attributes to
|
|
131
142
|
the corresponding signals in the HDL hierarchy.
|
|
@@ -138,8 +149,7 @@ Use `from_entity` when the signal names in the RTL match your class attribute na
|
|
|
138
149
|
level. This is a strict connection method that does not support overrides or pattern substitutions.
|
|
139
150
|
|
|
140
151
|
```python
|
|
141
|
-
# Connects to dut.
|
|
142
|
-
dut.rdy directly
|
|
152
|
+
# Connects to dut.data and dut.rdy directly
|
|
143
153
|
bus = MyBus.from_entity(dut)
|
|
144
154
|
```
|
|
145
155
|
|
|
@@ -190,7 +200,8 @@ bus = MyBus.from_pattern(dut, pattern="/u_axi_[0-9]_%/")
|
|
|
190
200
|
While `from_entity` is strict, `from_pattern` allows for flexibility when the RTL structure is non-standard:
|
|
191
201
|
|
|
192
202
|
- **Keyword Overrides:** Pass a signal handle as a keyword argument to skip the pattern search for that specific
|
|
193
|
-
attribute.
|
|
203
|
+
attribute.
|
|
204
|
+
- **Indexing:** Use the `idx` argument to index into all signals discovered via the pattern (e.g.,
|
|
194
205
|
`dut.u_axi_tdata[1]`).
|
|
195
206
|
|
|
196
207
|
```python
|
|
@@ -201,29 +212,38 @@ bus = MyBus.from_pattern(dut, pattern="u_axi_%", idx=1, rdy=dut.global_rdy)
|
|
|
201
212
|
> [!TIP] To confirm your signals are correctly bound, you can print `print(bus)` or `print(f"{bus=}")` the bus object to
|
|
202
213
|
> inspect the resolved RTL paths.
|
|
203
214
|
|
|
204
|
-
###
|
|
215
|
+
### 4.2 Synchronous Driving (Non-Blocking)
|
|
205
216
|
|
|
206
217
|
When using a modport's clocking block, driving a signal schedules an update for the next clock edge + output skew. It
|
|
207
218
|
does not block the current coroutine.
|
|
208
219
|
|
|
209
220
|
```python
|
|
210
221
|
# Drive data through the source modport
|
|
211
|
-
bus.source.
|
|
212
|
-
bus.source.
|
|
222
|
+
bus.source.cb.tdata.value = 0xABCD
|
|
223
|
+
bus.source.cb.tvalid.value = 1
|
|
213
224
|
```
|
|
214
225
|
|
|
215
|
-
###
|
|
226
|
+
### 4.3 Synchronous Sampling (Blocking)
|
|
216
227
|
|
|
217
228
|
To read a signal synchronously, you **must** use `await ...capture()`. This ensures the simulation waits for the clock
|
|
218
229
|
edge and the defined input skew before returning the value.
|
|
219
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
|
+
|
|
220
237
|
```python
|
|
221
238
|
# Wait until sink is ready
|
|
222
|
-
while await bus.source.
|
|
223
|
-
await bus.source.
|
|
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)
|
|
224
244
|
```
|
|
225
245
|
|
|
226
|
-
###
|
|
246
|
+
### 4.4 Using Interface APIs
|
|
227
247
|
|
|
228
248
|
Methods defined in the interface and imported in the modport can be called directly.
|
|
229
249
|
|
|
@@ -234,7 +254,7 @@ await bus.source.reset()
|
|
|
234
254
|
|
|
235
255
|
---
|
|
236
256
|
|
|
237
|
-
##
|
|
257
|
+
## 5. Technical Summary
|
|
238
258
|
|
|
239
259
|
| SystemVerilog Concept | Framework Implementation | Behavior |
|
|
240
260
|
| :-------------------- | :---------------------------------- | :------------------------------------- |
|
|
@@ -249,7 +269,7 @@ await bus.source.reset()
|
|
|
249
269
|
|
|
250
270
|
---
|
|
251
271
|
|
|
252
|
-
##
|
|
272
|
+
## 6. Best Practices
|
|
253
273
|
|
|
254
274
|
1. Define separate clocking blocks for Source and Sink roles to account for different signal access and physical skews.
|
|
255
275
|
2. Don't give access to synchronous signals in the `modport`, this forces the user to use the clocking block.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.1.4"
|
|
@@ -112,6 +112,7 @@ class ClockedSignal:
|
|
|
112
112
|
else:
|
|
113
113
|
logger.warning(f"{self._handle} is not resolvable")
|
|
114
114
|
return value
|
|
115
|
+
return default
|
|
115
116
|
|
|
116
117
|
def __getattr__(self, name):
|
|
117
118
|
"""Forward other cocotb handle methods (like ._name, etc)"""
|
|
@@ -204,12 +205,21 @@ class ModportView:
|
|
|
204
205
|
origin = getattr(_type, "__origin__", None)
|
|
205
206
|
|
|
206
207
|
if origin is Import:
|
|
207
|
-
#
|
|
208
|
-
|
|
209
|
-
|
|
208
|
+
# Resolve class methods before instance attributes, which may
|
|
209
|
+
# contain an HDL signal with the same name as the imported method.
|
|
210
|
+
imported = inspect.getattr_static(type(parent), name, None)
|
|
211
|
+
if imported is not None:
|
|
212
|
+
descriptor_get = getattr(imported, "__get__", None)
|
|
213
|
+
if descriptor_get is not None:
|
|
214
|
+
imported = descriptor_get(parent, type(parent))
|
|
215
|
+
else:
|
|
216
|
+
imported = getattr(parent, name, None)
|
|
217
|
+
|
|
218
|
+
if callable(imported):
|
|
219
|
+
setattr(self, name, imported)
|
|
210
220
|
else:
|
|
211
221
|
raise AttributeError(
|
|
212
|
-
f"Modport '{mp_cls.__name__}' expected imported
|
|
222
|
+
f"Modport '{mp_cls.__name__}' expected imported callable '{name}', "
|
|
213
223
|
f"but it was not found in {parent.__class__.__name__}"
|
|
214
224
|
)
|
|
215
225
|
|
{cocotbext_interface-0.1.3 → cocotbext_interface-0.1.4}/cocotbext_interface.egg-info/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: cocotbext-interface
|
|
3
|
-
Version: 0.1.
|
|
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>
|
|
@@ -83,11 +83,21 @@ By mimicking the SystemVerilog `interface` structure, verification engineers can
|
|
|
83
83
|
|
|
84
84
|
---
|
|
85
85
|
|
|
86
|
-
## 2.
|
|
86
|
+
## 2. Installation
|
|
87
|
+
|
|
88
|
+
Install the package from PyPI using pip:
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
pip install cocotbext-interface
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
---
|
|
95
|
+
|
|
96
|
+
## 3. Part I: Designing the Interface
|
|
87
97
|
|
|
88
98
|
This section covers how to define the structure, timing, and access rules for a interface protocol.
|
|
89
99
|
|
|
90
|
-
###
|
|
100
|
+
### 3.1 The Interface Class
|
|
91
101
|
|
|
92
102
|
To define a bus, inherit from the Interface class. Signals are now defined as class attributes using type hints, which
|
|
93
103
|
determines whether they are required or optional during RTL binding.
|
|
@@ -112,7 +122,7 @@ class MyBus(Interface):
|
|
|
112
122
|
|
|
113
123
|
These attribute names are used directly by the framework to search for matching signal names within the RTL hierarchy.
|
|
114
124
|
|
|
115
|
-
###
|
|
125
|
+
### 3.2 Defining Clocking Blocks
|
|
116
126
|
|
|
117
127
|
Use the `@clocking` decorator on an inner class. This defines the temporal behavior for synchronous signals.
|
|
118
128
|
|
|
@@ -121,7 +131,7 @@ Use the `@clocking` decorator on an inner class. This defines the temporal behav
|
|
|
121
131
|
- `input`: Input skew (Sample delay). Can be a `Timer` or another `Trigger`.
|
|
122
132
|
- `output`: Output skew (Drive delay).
|
|
123
133
|
|
|
124
|
-
###
|
|
134
|
+
### 3.3 Defining Modports
|
|
125
135
|
|
|
126
136
|
Use the `@modport` decorator to group signals and clocking blocks for specific roles like `source`, `sink` or `monitor`.
|
|
127
137
|
|
|
@@ -130,12 +140,13 @@ Use the `@modport` decorator to group signals and clocking blocks for specific r
|
|
|
130
140
|
- `Input`/`Output`/`InOut`: Signals accessible in this modport.
|
|
131
141
|
- `Import`: Method names (APIs) exposed to this modport.
|
|
132
142
|
|
|
133
|
-
###
|
|
143
|
+
### 3.4 Complete Definition Example
|
|
134
144
|
|
|
135
145
|
```python
|
|
146
|
+
from typing import Callable
|
|
136
147
|
from cocotb.handles import LogicArrayObject, LogicObject
|
|
137
148
|
from cocotb.triggers import RisingEdge, FallingEdge, Timer
|
|
138
|
-
from
|
|
149
|
+
from cocotbext.interface import Interface, modport, clocking, Import, Input, Output
|
|
139
150
|
|
|
140
151
|
class AxiStream(Interface):
|
|
141
152
|
clk: LogicObject
|
|
@@ -175,11 +186,11 @@ class AxiStream(Interface):
|
|
|
175
186
|
|
|
176
187
|
---
|
|
177
188
|
|
|
178
|
-
##
|
|
189
|
+
## 4. Part II: Using the Interface
|
|
179
190
|
|
|
180
191
|
This section covers how to connect the interface to the DUT and use it in a test.
|
|
181
192
|
|
|
182
|
-
###
|
|
193
|
+
### 4.1 Instantiation & Connection
|
|
183
194
|
|
|
184
195
|
Interfaces are connected to the RTL using named constructors. These methods automatically map your class attributes to
|
|
185
196
|
the corresponding signals in the HDL hierarchy.
|
|
@@ -192,8 +203,7 @@ Use `from_entity` when the signal names in the RTL match your class attribute na
|
|
|
192
203
|
level. This is a strict connection method that does not support overrides or pattern substitutions.
|
|
193
204
|
|
|
194
205
|
```python
|
|
195
|
-
# Connects to dut.
|
|
196
|
-
dut.rdy directly
|
|
206
|
+
# Connects to dut.data and dut.rdy directly
|
|
197
207
|
bus = MyBus.from_entity(dut)
|
|
198
208
|
```
|
|
199
209
|
|
|
@@ -244,7 +254,8 @@ bus = MyBus.from_pattern(dut, pattern="/u_axi_[0-9]_%/")
|
|
|
244
254
|
While `from_entity` is strict, `from_pattern` allows for flexibility when the RTL structure is non-standard:
|
|
245
255
|
|
|
246
256
|
- **Keyword Overrides:** Pass a signal handle as a keyword argument to skip the pattern search for that specific
|
|
247
|
-
attribute.
|
|
257
|
+
attribute.
|
|
258
|
+
- **Indexing:** Use the `idx` argument to index into all signals discovered via the pattern (e.g.,
|
|
248
259
|
`dut.u_axi_tdata[1]`).
|
|
249
260
|
|
|
250
261
|
```python
|
|
@@ -255,29 +266,38 @@ bus = MyBus.from_pattern(dut, pattern="u_axi_%", idx=1, rdy=dut.global_rdy)
|
|
|
255
266
|
> [!TIP] To confirm your signals are correctly bound, you can print `print(bus)` or `print(f"{bus=}")` the bus object to
|
|
256
267
|
> inspect the resolved RTL paths.
|
|
257
268
|
|
|
258
|
-
###
|
|
269
|
+
### 4.2 Synchronous Driving (Non-Blocking)
|
|
259
270
|
|
|
260
271
|
When using a modport's clocking block, driving a signal schedules an update for the next clock edge + output skew. It
|
|
261
272
|
does not block the current coroutine.
|
|
262
273
|
|
|
263
274
|
```python
|
|
264
275
|
# Drive data through the source modport
|
|
265
|
-
bus.source.
|
|
266
|
-
bus.source.
|
|
276
|
+
bus.source.cb.tdata.value = 0xABCD
|
|
277
|
+
bus.source.cb.tvalid.value = 1
|
|
267
278
|
```
|
|
268
279
|
|
|
269
|
-
###
|
|
280
|
+
### 4.3 Synchronous Sampling (Blocking)
|
|
270
281
|
|
|
271
282
|
To read a signal synchronously, you **must** use `await ...capture()`. This ensures the simulation waits for the clock
|
|
272
283
|
edge and the defined input skew before returning the value.
|
|
273
284
|
|
|
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
|
+
|
|
274
291
|
```python
|
|
275
292
|
# Wait until sink is ready
|
|
276
|
-
while await bus.source.
|
|
277
|
-
await bus.source.
|
|
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)
|
|
278
298
|
```
|
|
279
299
|
|
|
280
|
-
###
|
|
300
|
+
### 4.4 Using Interface APIs
|
|
281
301
|
|
|
282
302
|
Methods defined in the interface and imported in the modport can be called directly.
|
|
283
303
|
|
|
@@ -288,7 +308,7 @@ await bus.source.reset()
|
|
|
288
308
|
|
|
289
309
|
---
|
|
290
310
|
|
|
291
|
-
##
|
|
311
|
+
## 5. Technical Summary
|
|
292
312
|
|
|
293
313
|
| SystemVerilog Concept | Framework Implementation | Behavior |
|
|
294
314
|
| :-------------------- | :---------------------------------- | :------------------------------------- |
|
|
@@ -303,7 +323,7 @@ await bus.source.reset()
|
|
|
303
323
|
|
|
304
324
|
---
|
|
305
325
|
|
|
306
|
-
##
|
|
326
|
+
## 6. Best Practices
|
|
307
327
|
|
|
308
328
|
1. Define separate clocking blocks for Source and Sink roles to account for different signal access and physical skews.
|
|
309
329
|
2. Don't give access to synchronous signals in the `modport`, this forces the user to use the clocking block.
|
|
@@ -32,7 +32,7 @@ from cocotb.triggers import RisingEdge, FallingEdge
|
|
|
32
32
|
|
|
33
33
|
@pytest.fixture(autouse=True)
|
|
34
34
|
def mock_cocotb_scheduler(monkeypatch):
|
|
35
|
-
"""Auto-patch cocotb.start_soon to avoid scheduler errors in tests.
|
|
35
|
+
"""Auto-patch cocotb.start_soon and ReadOnlyManager to avoid scheduler errors in tests.
|
|
36
36
|
|
|
37
37
|
Properly closes coroutines to avoid RuntimeWarning about unawaited coroutines.
|
|
38
38
|
"""
|
|
@@ -42,7 +42,11 @@ def mock_cocotb_scheduler(monkeypatch):
|
|
|
42
42
|
if inspect.iscoroutine(coro):
|
|
43
43
|
coro.close()
|
|
44
44
|
|
|
45
|
+
async def mock_wait():
|
|
46
|
+
pass
|
|
47
|
+
|
|
45
48
|
monkeypatch.setattr("cocotb.start_soon", mock_start_soon)
|
|
49
|
+
monkeypatch.setattr("cocotbext.interface.interface.ReadOnlyManager.wait", mock_wait)
|
|
46
50
|
|
|
47
51
|
|
|
48
52
|
# ============================================================================
|
|
@@ -121,6 +125,7 @@ def mock_hierarchy():
|
|
|
121
125
|
hierarchy.add_signal("clk")
|
|
122
126
|
hierarchy.add_signal("rst")
|
|
123
127
|
hierarchy.add_signal("data") # Generic data signal
|
|
128
|
+
hierarchy.add_signal("write")
|
|
124
129
|
hierarchy.add_signal("data_in")
|
|
125
130
|
hierarchy.add_signal("data_out")
|
|
126
131
|
hierarchy.add_signal("valid")
|
|
@@ -162,6 +167,24 @@ class InterfaceWithModport(Interface):
|
|
|
162
167
|
pass
|
|
163
168
|
|
|
164
169
|
|
|
170
|
+
class InterfaceWithCollidingImport(Interface):
|
|
171
|
+
"""Test a modport callable sharing its name with an HDL signal."""
|
|
172
|
+
|
|
173
|
+
clk: Output[object]
|
|
174
|
+
write: Input[object]
|
|
175
|
+
|
|
176
|
+
@clocking(clock="clk")
|
|
177
|
+
class cb:
|
|
178
|
+
write: Input[object]
|
|
179
|
+
|
|
180
|
+
@modport(clocking="cb")
|
|
181
|
+
class master:
|
|
182
|
+
write: Import[Callable]
|
|
183
|
+
|
|
184
|
+
def write(self) -> str:
|
|
185
|
+
return "method"
|
|
186
|
+
|
|
187
|
+
|
|
165
188
|
class InterfaceWithClocking(Interface):
|
|
166
189
|
"""Test interface with clocking block."""
|
|
167
190
|
|
|
@@ -385,6 +408,13 @@ class TestModports:
|
|
|
385
408
|
assert hasattr(iface, "send")
|
|
386
409
|
assert hasattr(master, "send")
|
|
387
410
|
|
|
411
|
+
def test_modport_import_callable_wins_signal_name_collision(self, mock_hierarchy):
|
|
412
|
+
iface = InterfaceWithCollidingImport.from_entity(mock_hierarchy)
|
|
413
|
+
|
|
414
|
+
assert callable(iface.master.write)
|
|
415
|
+
assert iface.master.write() == "method"
|
|
416
|
+
assert iface.cb.write._handle is mock_hierarchy._children["write"]
|
|
417
|
+
|
|
388
418
|
def test_modport_dont_contains_callable(self, mock_hierarchy):
|
|
389
419
|
"""Test that modport contains the mapped callable."""
|
|
390
420
|
iface = InterfaceWithModport.from_entity(mock_hierarchy)
|
|
@@ -577,6 +607,24 @@ class TestClockedSignal:
|
|
|
577
607
|
|
|
578
608
|
asyncio.run(inner())
|
|
579
609
|
|
|
610
|
+
def test_clocked_signal_capture_default(self, mock_hierarchy):
|
|
611
|
+
"""Test capturing with a default value for unconnected signals."""
|
|
612
|
+
|
|
613
|
+
async def inner():
|
|
614
|
+
clk = mock_hierarchy._children["clk"]
|
|
615
|
+
# Signal with no handle
|
|
616
|
+
cs = ClockedSignal(None, clk, RisingEdge, is_input=True)
|
|
617
|
+
|
|
618
|
+
# Capture should return the default value
|
|
619
|
+
val = await cs.capture(default=123)
|
|
620
|
+
assert val == 123
|
|
621
|
+
|
|
622
|
+
# Capture without default should return None
|
|
623
|
+
val_none = await cs.capture()
|
|
624
|
+
assert val_none is None
|
|
625
|
+
|
|
626
|
+
asyncio.run(inner())
|
|
627
|
+
|
|
580
628
|
|
|
581
629
|
# ============================================================================
|
|
582
630
|
# Tests: Modport + Clocking Integration
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "0.1.3"
|
|
File without changes
|
|
File without changes
|
{cocotbext_interface-0.1.3 → cocotbext_interface-0.1.4}/cocotbext/interface/prototypes/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{cocotbext_interface-0.1.3 → cocotbext_interface-0.1.4}/cocotbext_interface.egg-info/SOURCES.txt
RENAMED
|
File without changes
|
|
File without changes
|
{cocotbext_interface-0.1.3 → cocotbext_interface-0.1.4}/cocotbext_interface.egg-info/requires.txt
RENAMED
|
File without changes
|
{cocotbext_interface-0.1.3 → cocotbext_interface-0.1.4}/cocotbext_interface.egg-info/top_level.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|