cocotb-framework 0.1.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 (119) hide show
  1. CocoTBFramework/__init__.py +3 -0
  2. CocoTBFramework/components/__init__.py +0 -0
  3. CocoTBFramework/components/apb/__init__.py +0 -0
  4. CocoTBFramework/components/apb/apb_components.py +476 -0
  5. CocoTBFramework/components/apb/apb_factories.py +426 -0
  6. CocoTBFramework/components/apb/apb_packet.py +417 -0
  7. CocoTBFramework/components/apb/apb_sequence.py +206 -0
  8. CocoTBFramework/components/apb5/__init__.py +38 -0
  9. CocoTBFramework/components/apb5/apb5_components.py +554 -0
  10. CocoTBFramework/components/apb5/apb5_factories.py +219 -0
  11. CocoTBFramework/components/apb5/apb5_packet.py +628 -0
  12. CocoTBFramework/components/axi4/__init__.py +0 -0
  13. CocoTBFramework/components/axi4/axi4_compliance_checker.py +591 -0
  14. CocoTBFramework/components/axi4/axi4_factories.py +484 -0
  15. CocoTBFramework/components/axi4/axi4_field_configs.py +585 -0
  16. CocoTBFramework/components/axi4/axi4_interfaces.py +1393 -0
  17. CocoTBFramework/components/axi4/axi4_packet.py +398 -0
  18. CocoTBFramework/components/axi4/axi4_packet_utils.py +365 -0
  19. CocoTBFramework/components/axi4/axi4_randomization_config.py +689 -0
  20. CocoTBFramework/components/axi4/axi4_randomization_manager.py +339 -0
  21. CocoTBFramework/components/axi4/axi4_timing_config.py +103 -0
  22. CocoTBFramework/components/axi4/axi4_transaction.py +99 -0
  23. CocoTBFramework/components/axi5/__init__.py +260 -0
  24. CocoTBFramework/components/axi5/axi5_compliance_checker.py +758 -0
  25. CocoTBFramework/components/axi5/axi5_factories.py +419 -0
  26. CocoTBFramework/components/axi5/axi5_field_configs.py +989 -0
  27. CocoTBFramework/components/axi5/axi5_interfaces.py +1188 -0
  28. CocoTBFramework/components/axi5/axi5_packet.py +619 -0
  29. CocoTBFramework/components/axi5/axi5_packet_utils.py +628 -0
  30. CocoTBFramework/components/axi5/axi5_randomization_config.py +718 -0
  31. CocoTBFramework/components/axi5/axi5_randomization_manager.py +478 -0
  32. CocoTBFramework/components/axi5/axi5_timing_config.py +227 -0
  33. CocoTBFramework/components/axi5/axi5_transaction.py +336 -0
  34. CocoTBFramework/components/axil4/__init__.py +0 -0
  35. CocoTBFramework/components/axil4/axil4_compliance_checker.py +731 -0
  36. CocoTBFramework/components/axil4/axil4_factories.py +607 -0
  37. CocoTBFramework/components/axil4/axil4_field_configs.py +282 -0
  38. CocoTBFramework/components/axil4/axil4_interfaces.py +728 -0
  39. CocoTBFramework/components/axil4/axil4_packet.py +322 -0
  40. CocoTBFramework/components/axil4/axil4_packet_utils.py +439 -0
  41. CocoTBFramework/components/axis4/__init__.py +75 -0
  42. CocoTBFramework/components/axis4/axis_example_usage.py +409 -0
  43. CocoTBFramework/components/axis4/axis_factories.py +475 -0
  44. CocoTBFramework/components/axis4/axis_field_configs.py +172 -0
  45. CocoTBFramework/components/axis4/axis_master.py +361 -0
  46. CocoTBFramework/components/axis4/axis_monitor.py +366 -0
  47. CocoTBFramework/components/axis4/axis_packet.py +236 -0
  48. CocoTBFramework/components/axis4/axis_slave.py +341 -0
  49. CocoTBFramework/components/axis5/__init__.py +88 -0
  50. CocoTBFramework/components/axis5/axis5_factories.py +537 -0
  51. CocoTBFramework/components/axis5/axis5_field_configs.py +262 -0
  52. CocoTBFramework/components/axis5/axis5_master.py +327 -0
  53. CocoTBFramework/components/axis5/axis5_monitor.py +381 -0
  54. CocoTBFramework/components/axis5/axis5_packet.py +425 -0
  55. CocoTBFramework/components/axis5/axis5_slave.py +295 -0
  56. CocoTBFramework/components/fifo/__init__.py +0 -0
  57. CocoTBFramework/components/fifo/fifo_command_handler.py +263 -0
  58. CocoTBFramework/components/fifo/fifo_component_base.py +342 -0
  59. CocoTBFramework/components/fifo/fifo_factories.py +606 -0
  60. CocoTBFramework/components/fifo/fifo_master.py +373 -0
  61. CocoTBFramework/components/fifo/fifo_monitor.py +320 -0
  62. CocoTBFramework/components/fifo/fifo_monitor_base.py +251 -0
  63. CocoTBFramework/components/fifo/fifo_packet.py +84 -0
  64. CocoTBFramework/components/fifo/fifo_sequence.py +551 -0
  65. CocoTBFramework/components/fifo/fifo_slave.py +297 -0
  66. CocoTBFramework/components/gaxi/__init__.py +21 -0
  67. CocoTBFramework/components/gaxi/coverage_hooks.py +317 -0
  68. CocoTBFramework/components/gaxi/gaxi_command_handler.py +720 -0
  69. CocoTBFramework/components/gaxi/gaxi_component_base.py +433 -0
  70. CocoTBFramework/components/gaxi/gaxi_factories.py +480 -0
  71. CocoTBFramework/components/gaxi/gaxi_master.py +542 -0
  72. CocoTBFramework/components/gaxi/gaxi_monitor.py +236 -0
  73. CocoTBFramework/components/gaxi/gaxi_monitor_base.py +266 -0
  74. CocoTBFramework/components/gaxi/gaxi_packet.py +84 -0
  75. CocoTBFramework/components/gaxi/gaxi_sequence.py +568 -0
  76. CocoTBFramework/components/gaxi/gaxi_slave.py +498 -0
  77. CocoTBFramework/components/shared/__init__.py +0 -0
  78. CocoTBFramework/components/shared/arbiter_compliance.py +1093 -0
  79. CocoTBFramework/components/shared/arbiter_master.py +1133 -0
  80. CocoTBFramework/components/shared/arbiter_monitor.py +1461 -0
  81. CocoTBFramework/components/shared/data_strategies.py +578 -0
  82. CocoTBFramework/components/shared/debug_object.py +90 -0
  83. CocoTBFramework/components/shared/field_config.py +829 -0
  84. CocoTBFramework/components/shared/flex_config_gen.py +413 -0
  85. CocoTBFramework/components/shared/flex_randomizer.py +700 -0
  86. CocoTBFramework/components/shared/master_statistics.py +427 -0
  87. CocoTBFramework/components/shared/memory_model.py +531 -0
  88. CocoTBFramework/components/shared/monitor_statistics.py +69 -0
  89. CocoTBFramework/components/shared/packet.py +648 -0
  90. CocoTBFramework/components/shared/packet_factory.py +403 -0
  91. CocoTBFramework/components/shared/protocol_error_handler.py +131 -0
  92. CocoTBFramework/components/shared/randomization_config.py +498 -0
  93. CocoTBFramework/components/shared/signal_mapping_helper.py +1770 -0
  94. CocoTBFramework/components/smbus/__init__.py +45 -0
  95. CocoTBFramework/components/smbus/smbus_components.py +1086 -0
  96. CocoTBFramework/components/smbus/smbus_packet.py +252 -0
  97. CocoTBFramework/components/uart/__init__.py +66 -0
  98. CocoTBFramework/components/uart/uart_components.py +411 -0
  99. CocoTBFramework/components/uart/uart_packet.py +116 -0
  100. CocoTBFramework/components/wavedrom/__init__.py +0 -0
  101. CocoTBFramework/components/wavedrom/constraint_solver.py +1639 -0
  102. CocoTBFramework/components/wavedrom/signal_binder.py +195 -0
  103. CocoTBFramework/components/wavedrom/utility.py +874 -0
  104. CocoTBFramework/components/wavedrom/wavejson_gen.py +1027 -0
  105. CocoTBFramework/scoreboards/__init__.py +0 -0
  106. CocoTBFramework/scoreboards/apb_gaxi_scoreboard.py +538 -0
  107. CocoTBFramework/scoreboards/apb_gaxi_transformer.py +320 -0
  108. CocoTBFramework/scoreboards/apb_scoreboard.py +285 -0
  109. CocoTBFramework/scoreboards/axi4/__init__.py +0 -0
  110. CocoTBFramework/scoreboards/axi4/axi4_dwidth_converter_scoreboard.py +145 -0
  111. CocoTBFramework/scoreboards/axi4_scoreboard.py +423 -0
  112. CocoTBFramework/scoreboards/base_scoreboard.py +202 -0
  113. CocoTBFramework/scoreboards/fifo_scoreboard.py +152 -0
  114. CocoTBFramework/scoreboards/gaxi_scoreboard.py +277 -0
  115. cocotb_framework-0.1.0.dist-info/METADATA +333 -0
  116. cocotb_framework-0.1.0.dist-info/RECORD +119 -0
  117. cocotb_framework-0.1.0.dist-info/WHEEL +5 -0
  118. cocotb_framework-0.1.0.dist-info/licenses/LICENSE +21 -0
  119. cocotb_framework-0.1.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,3 @@
1
+ """CocoTB Verification Framework - Reusable BFMs, testbenches, and scoreboards for RTL testing."""
2
+
3
+ __version__ = "0.1.0"
File without changes
File without changes
@@ -0,0 +1,476 @@
1
+ # SPDX-License-Identifier: MIT
2
+ # SPDX-FileCopyrightText: 2024-2025 sean galloway
3
+ #
4
+ # RTL Design Sherpa - Industry-Standard RTL Design and Verification
5
+ # https://github.com/sean-galloway/RTLDesignSherpa
6
+ #
7
+ # Module: APBMonitor
8
+ # Purpose: APB Sequence, APB Packet, Transaction, Monitor, Master and Slave Classes
9
+ #
10
+ # Documentation: bin/CocoTBFramework/README.md
11
+ # Subsystem: framework
12
+ #
13
+ # Author: sean galloway
14
+ # Created: 2025-10-18
15
+
16
+ """APB Sequence, APB Packet, Transaction, Monitor, Master and Slave Classes"""
17
+
18
+ from collections import deque
19
+
20
+ import cocotb
21
+ from cocotb.triggers import FallingEdge, RisingEdge, Timer
22
+ from cocotb.utils import get_sim_time
23
+ from cocotb_bus.drivers import BusDriver
24
+ from cocotb_bus.monitors import BusMonitor
25
+
26
+ from ..shared.flex_randomizer import FlexRandomizer
27
+ from ..shared.memory_model import MemoryModel
28
+ from .apb_packet import APBPacket # Updated import
29
+
30
+ # define the PWRITE mapping
31
+ pwrite = ['READ', 'WRITE']
32
+ apb_signals = [
33
+ "PSEL",
34
+ "PWRITE",
35
+ "PENABLE",
36
+ "PADDR",
37
+ "PWDATA",
38
+ "PRDATA",
39
+ "PREADY"
40
+ ]
41
+ apb_optional_signals = [
42
+ "PPROT",
43
+ "PSLVERR",
44
+ "PSTRB"
45
+ ]
46
+
47
+
48
+ class APBMonitor(BusMonitor):
49
+ def __init__(self, entity, title, prefix, clock, signals=None,
50
+ bus_width=32, addr_width=12, log=None, **kwargs):
51
+
52
+ if signals:
53
+ self._signals = signals
54
+ else:
55
+ self._signals = apb_signals + apb_optional_signals
56
+ self._optional_signals = apb_optional_signals
57
+
58
+ self.count = 0
59
+ self.bus_width = bus_width
60
+
61
+ # Normalize prefix: remove trailing underscore if present
62
+ # BusMonitor adds underscore separator automatically
63
+ prefix = prefix.rstrip('_')
64
+
65
+ BusMonitor.__init__(self, entity, prefix, clock, **kwargs)
66
+ self.clock = clock
67
+ self.title = title
68
+ self.log = log or self.entity._log
69
+ self.bus_width = bus_width
70
+ self.addr_width = addr_width
71
+ self.strb_width = bus_width // 8
72
+ # if self.is_signal_present('PPROT'):
73
+ # msg = f'Monitor {self.title} PPROT {dir(self.bus.PPROT)}'
74
+ # self.log.debug(msg)
75
+
76
+ def is_signal_present(self, signal_name):
77
+ # Check if the bus has the attribute and that it is not None
78
+ return hasattr(self.bus, signal_name) and getattr(self.bus, signal_name) is not None
79
+
80
+ def print(self, transaction):
81
+ msg = f'{self.title} - APB Transaction #{self.count}: '
82
+ msg += transaction.formatted(compact=True)
83
+ self.log.debug(msg)
84
+
85
+ async def _monitor_recv(self):
86
+ # Track previous state to detect transaction boundaries
87
+ # APB Protocol: PSEL -> PSEL+PENABLE -> PSEL+PENABLE+PREADY (completion)
88
+ prev_penable = 0
89
+ prev_pready = 0
90
+
91
+ while True:
92
+ await FallingEdge(self.clock)
93
+ await Timer(200, units='ps')
94
+
95
+ # Sample current bus state
96
+ curr_psel = self.bus.PSEL.value.integer if self.bus.PSEL.value.is_resolvable else 0
97
+ curr_penable = self.bus.PENABLE.value.integer
98
+ curr_pready = self.bus.PREADY.value.integer if self.bus.PREADY.value.is_resolvable else 0
99
+
100
+ # APB transaction completes when:
101
+ # 1. PSEL & PENABLE & PREADY are ALL high (completion condition)
102
+ # 2. In previous cycle, EITHER:
103
+ # a) PREADY was low (most common - PREADY asserted this cycle), OR
104
+ # b) PENABLE was low (back-to-back transactions where PREADY stays high)
105
+ # This stricter check prevents spurious captures
106
+ transaction_complete = curr_psel and curr_penable and curr_pready
107
+
108
+ # Valid completion edges:
109
+ # - PREADY 0->1 (normal case: slave responds)
110
+ # - PENABLE 0->1 while PREADY already high (back-to-back with fast slave)
111
+ valid_edge = transaction_complete and (not prev_pready or not prev_penable)
112
+
113
+ if valid_edge:
114
+ start_time = get_sim_time('ns')
115
+ address = self.bus.PADDR.value.integer
116
+ direction = pwrite[self.bus.PWRITE.value.integer]
117
+ loc_pwrite = self.bus.PWRITE.value.integer
118
+ error = self.bus.PSLVERR.value.integer if self.is_signal_present('PSLVERR') else 0
119
+
120
+ if direction == 'READ':
121
+ if self.bus.PRDATA.value.is_resolvable:
122
+ data = self.bus.PRDATA.value.integer
123
+ else:
124
+ data = self.bus.PRDATA.value
125
+ else:
126
+ data = self.bus.PWDATA.value.integer
127
+ strb = self.bus.PSTRB.value.integer if self.is_signal_present('PSTRB') else 0
128
+ pprot = self.bus.PPROT.value.integer if self.is_signal_present('PPROT') else 0
129
+ self.count += 1
130
+
131
+ # Create APBPacket and capture immediately (data is stable)
132
+ transaction = APBPacket(
133
+ start_time=start_time,
134
+ count=self.count,
135
+ pwrite=loc_pwrite,
136
+ paddr=address,
137
+ pwdata=data if direction == 'WRITE' else 0,
138
+ prdata=data if direction == 'READ' else 0,
139
+ pstrb=strb,
140
+ pprot=pprot,
141
+ pslverr=error
142
+ )
143
+
144
+ # Dispatch immediately - APB data is stable when PREADY asserts
145
+ self._recv(transaction)
146
+ self.print(transaction)
147
+
148
+ # Update previous state for next iteration
149
+ prev_penable = curr_penable
150
+ prev_pready = curr_pready
151
+
152
+
153
+ class APBSlave(BusMonitor):
154
+ """AP Slave Class"""
155
+ def __init__(self, entity, title, prefix, clock, registers, signals=None,
156
+ bus_width=32, addr_width=12, randomizer=None,
157
+ log=None, error_overflow=False, **kwargs):
158
+ if signals:
159
+ self._signals = signals
160
+ else:
161
+ self._signals = apb_signals + apb_optional_signals
162
+ self._optional_signals = apb_optional_signals
163
+ if randomizer is None:
164
+ self.randomizer = FlexRandomizer({
165
+ 'ready': ([(0, 1), (2, 5), (6, 10)], [5, 2, 1]),
166
+ 'error': ([(0, 0), (1, 1)], [10, 0]),
167
+ })
168
+ else:
169
+ self.randomizer = randomizer
170
+
171
+ # Normalize prefix: remove trailing underscore if present
172
+ # BusMonitor adds underscore separator automatically
173
+ prefix = prefix.rstrip('_')
174
+
175
+ BusMonitor.__init__(self, entity, prefix, clock, **kwargs)
176
+ self.clock = clock
177
+ self.title = title
178
+ self.prefix = prefix
179
+ self.log = log or self.entity._log
180
+ self.addr_width = addr_width
181
+ self.bus_width = bus_width
182
+ self.strb_bits = bus_width // 8
183
+ self.addr_mask = (2**self.strb_bits - 1)
184
+ self.num_lines = len(registers) // self.strb_bits
185
+ self.count = 0
186
+ self.error_overflow = error_overflow
187
+ # Create the memory model
188
+ self.mem = MemoryModel(num_lines=self.num_lines, bytes_per_line=self.strb_bits, log=self.log, preset_values=registers)
189
+ self.sentQ = deque()
190
+
191
+ # initialise all outputs to zero
192
+ self.bus.PRDATA.setimmediatevalue(0)
193
+ self.bus.PREADY.setimmediatevalue(0)
194
+ if self.is_signal_present('PSLVERR'):
195
+ self.bus.PSLVERR.setimmediatevalue(0)
196
+ msg = f'Slave {self.title} {dir(self.bus)}'
197
+ # self.log.debug(msg)
198
+ # msg = f'Slave {self.title} PADDR {dir(self.bus.PADDR)}'
199
+ # self.log.debug(msg)
200
+ if self.is_signal_present('PPROT'):
201
+ msg = f'Slave {self.title} PPROT {dir(self.bus.PPROT)}'
202
+ self.log.debug(msg)
203
+
204
+ def set_randomizer(self, randomizer):
205
+ self.randomizer = randomizer
206
+ self.log.info(f"Set new randomizer for APB Slave ({self.title})")
207
+
208
+ def is_signal_present(self, signal_name):
209
+ # Check if the bus has the attribute and that it is not None
210
+ return hasattr(self.bus, signal_name) and getattr(self.bus, signal_name) is not None
211
+
212
+ def dump_registers(self):
213
+ msg = f"APB Slave {self.title} - Register Dump:"
214
+ self.log.info(msg)
215
+ self.log.info(self.mem.dump())
216
+
217
+ async def reset_bus(self):
218
+ msg = f'Resetting APB Bus {self.title}'
219
+ self.log.info(msg)
220
+ self.bus.PRDATA.value = 0
221
+ self.bus.PREADY.value = 0
222
+ if self.is_signal_present('PSLVERR'):
223
+ self.bus.PSLVERR.value = 0
224
+
225
+ def reset_registers(self):
226
+ self.mem.reset(to_preset=True)
227
+
228
+ async def _monitor_recv(self):
229
+ while True:
230
+ await RisingEdge(self.clock)
231
+ self.bus.PREADY.value = 0
232
+ self.bus.PRDATA.value = 0
233
+ if self.is_signal_present('PSLVERR'):
234
+ self.bus.PSLVERR.value = 0
235
+
236
+ await Timer(200, units='ps')
237
+ if self.bus.PSEL.value.is_resolvable and self.bus.PSEL.value.integer:
238
+ rand_dict = self.randomizer.next()
239
+ ready_delay = rand_dict['ready']
240
+ slv_error = rand_dict['error']
241
+ for _ in range(ready_delay):
242
+ await RisingEdge(self.clock)
243
+
244
+ self.bus.PREADY.value = 1
245
+ await Timer(200, units='ps')
246
+ while not self.bus.PENABLE.value.integer:
247
+ await RisingEdge(self.clock)
248
+ await Timer(200, units='ps')
249
+ self._finish_recv(slv_error)
250
+
251
+ def _finish_recv(self, slv_error):
252
+ address = self.bus.PADDR.value.integer
253
+ # Calculate word index by masking address to memory model size and dividing by bytes per word
254
+ # This ensures we don't try to allocate memory for huge addresses (e.g., 0x80000000)
255
+ addr_bits_needed = (self.num_lines * self.strb_bits - 1).bit_length()
256
+ memory_addr_mask = (1 << addr_bits_needed) - 1
257
+ word_index = (address & memory_addr_mask) >> (self.strb_bits.bit_length() - 1)
258
+ self.bus.PPROT.value.integer if self.is_signal_present('PPROT') else 0
259
+ self.count += 1
260
+
261
+ if word_index >= self.num_lines:
262
+ if self.error_overflow:
263
+ msg = f'APB {self.title} - Memory overflow error: {word_index}'
264
+ self.log.error(msg)
265
+ self.bus.PSLVERR.value = 1
266
+ else:
267
+ expand = word_index - self.num_lines + 10
268
+ msg = f'APB {self.title} - Memory overflow: {self.num_lines=} {word_index=}'
269
+ self.log.warning(msg)
270
+ # Extend the self.mem array to accommodate the overflow
271
+ self.mem.expand(expand)
272
+ self.num_lines += expand
273
+
274
+ if slv_error and self.is_signal_present('PSLVERR'):
275
+ self.bus.PSLVERR.value = 1
276
+
277
+ if self.bus.PWRITE.value.integer: # Write transaction
278
+ strobes = self.bus.PSTRB.value.integer if self.is_signal_present('PSTRB') else (1 << self.strb_bits) - 1
279
+ pwdata = self.bus.PWDATA.value.integer
280
+ pwdata_ba = self.mem.integer_to_bytearray(pwdata, self.strb_bits)
281
+ self.mem.write(address & memory_addr_mask, pwdata_ba, strobes)
282
+
283
+ else: # Read transaction
284
+ prdata_ba = self.mem.read(address & memory_addr_mask, self.strb_bits)
285
+ prdata = self.mem.bytearray_to_integer(prdata_ba)
286
+ self.bus.PRDATA.value = prdata
287
+
288
+
289
+ class APBMaster(BusDriver):
290
+ def __init__(self, entity, title, prefix, clock, signals=None,
291
+ bus_width=32, addr_width=12, randomizer=None,
292
+ log=None, **kwargs):
293
+ if signals:
294
+ self._signals = signals
295
+ else:
296
+ self._signals = apb_signals + apb_optional_signals
297
+ self._optional_signals = apb_optional_signals
298
+ if randomizer is None:
299
+ self.randomizer = FlexRandomizer({
300
+ 'psel': ([[0, 0], [1, 5], [6, 10]], [5, 2, 1]),
301
+ 'penable': ([[0, 0], [1, 2]], [4, 1]),
302
+ })
303
+ else:
304
+ self.randomizer = randomizer
305
+
306
+ # Normalize prefix: remove trailing underscore if present
307
+ # BusDriver adds underscore separator automatically
308
+ prefix = prefix.rstrip('_')
309
+
310
+ BusDriver.__init__(self, entity, prefix, clock, **kwargs)
311
+ self.title = title
312
+ self.prefix = prefix
313
+ self.log = log or self.entity._log
314
+ self.clock = clock
315
+ self.addr_width = addr_width
316
+ self.bus_width = bus_width
317
+ self.strb_bits = bus_width // 8
318
+ self.addr_mask = (2**self.strb_bits - 1)
319
+ self.sentQ = deque()
320
+
321
+ # initialise all outputs to zero
322
+ self.bus.PADDR.setimmediatevalue(0)
323
+ self.bus.PWRITE.setimmediatevalue(0)
324
+ self.bus.PSEL.setimmediatevalue(0)
325
+ self.bus.PENABLE.setimmediatevalue(0)
326
+ self.bus.PWDATA.setimmediatevalue(0)
327
+ if self.is_signal_present('PSTRB'):
328
+ self.bus.PSTRB.setimmediatevalue(0)
329
+ self.transmit_queue = deque()
330
+ self.transmit_coroutine = None
331
+ self.transfer_busy = False
332
+
333
+ def set_randomizer(self, randomizer):
334
+ self.randomizer = randomizer
335
+ self.log.info(f"Set new randomizer for APB Slave ({self.title})")
336
+
337
+ def is_signal_present(self, signal_name):
338
+ # Check if the bus has the attribute and that it is not None
339
+ return hasattr(self.bus, signal_name) and getattr(self.bus, signal_name) is not None
340
+
341
+ async def reset_bus(self):
342
+ # initialise the transmit queue
343
+ self.transmit_queue = deque()
344
+ self.transmit_coroutine = None # Use None, not 0, for proper Task checking
345
+ self.transfer_busy = False
346
+ self.bus.PSEL.value = 0
347
+ self.bus.PENABLE.value = 0
348
+ self.bus.PWRITE.value = 0
349
+ self.bus.PADDR.value = 0
350
+ self.bus.PWDATA.value = 0
351
+ if self.is_signal_present('PSTRB'):
352
+ self.bus.PSTRB.value = 0
353
+ if self.is_signal_present('PPROT'):
354
+ self.bus.PPROT.value = 0
355
+
356
+ async def busy_send(self, transaction):
357
+ '''
358
+ Provide a send method that waits for the transaction to complete.
359
+ '''
360
+ await self.send(transaction)
361
+ while (self.transfer_busy):
362
+ await RisingEdge(self.clock)
363
+
364
+ async def _driver_send(self, transaction, sync=True, hold=False, **kwargs):
365
+ '''
366
+ Append a new transaction to be transmitted
367
+ '''
368
+ # add new transaction
369
+ self.transmit_queue.append(transaction)
370
+ msg = f'Adding to the transmit_queue: {transaction.formatted(compact=True)}'
371
+ self.log.debug(msg)
372
+
373
+ # launch new transmit pipeline coroutine if aren't holding for and the
374
+ # the coroutine isn't already running.
375
+ # If it is running it will just collect the transactions in the
376
+ # queue once it gets to them.
377
+ # Use .done() method to properly check if Task is complete (CocoTB 1.8+)
378
+ if not hold and (self.transmit_coroutine is None or self.transmit_coroutine.done()):
379
+ # Set transfer_busy BEFORE starting pipeline to avoid race condition
380
+ # with busy_send() checking the flag
381
+ self.transfer_busy = True
382
+ self.transmit_coroutine = cocotb.start_soon(self._transmit_pipeline())
383
+
384
+
385
+ async def _transmit_pipeline(self):
386
+ """Internal function to transmit queued transactions."""
387
+ # Wait for clock edge to ensure we're not in a read-only phase
388
+ await RisingEdge(self.clock)
389
+
390
+ # default values
391
+ self.transfer_busy = True
392
+
393
+ # while there's data in the queue keep transmitting
394
+ while len(self.transmit_queue):
395
+ # clear out the bus
396
+ self.bus.PSEL.value = 0
397
+ self.bus.PENABLE.value = 0
398
+ self.bus.PWRITE.value = 0
399
+ self.bus.PADDR.value = 0
400
+ self.bus.PWDATA.value = 0
401
+ if self.is_signal_present('PPROT'):
402
+ self.bus.PPROT.value = 0
403
+ if self.is_signal_present('PSTRB'):
404
+ self.bus.PSTRB.value = 0
405
+
406
+ rand_dict = self.randomizer.next()
407
+ psel_delay = rand_dict['psel']
408
+ penable_delay = rand_dict['penable']
409
+
410
+ transaction = self.transmit_queue.popleft()
411
+ transaction.start_time = cocotb.utils.get_sim_time('ns')
412
+
413
+ # finish the packet transmit
414
+ await self._finish_xmit(transaction, psel_delay, penable_delay)
415
+
416
+ # clear out the bus
417
+ self.transfer_busy = False
418
+ self.bus.PSEL.value = 0
419
+ self.bus.PENABLE.value = 0
420
+ self.bus.PWRITE.value = 0
421
+ self.bus.PADDR.value = 0
422
+ self.bus.PWDATA.value = 0
423
+ if self.is_signal_present('PPROT'):
424
+ self.bus.PPROT.value = 0
425
+ if self.is_signal_present('PSTRB'):
426
+ self.bus.PSTRB.value = 0
427
+
428
+ async def _finish_xmit(self, transaction, psel_delay, penable_delay):
429
+ """Completes an APB transaction.
430
+
431
+ This method sets the APB signals, waits for the ready signal,
432
+ and handles the transaction data and error status.
433
+ """
434
+ for _ in range(psel_delay):
435
+ await RisingEdge(self.clock)
436
+
437
+ self.bus.PSEL.value = 1
438
+ # Access fields dictionary for APBPacket
439
+ self.bus.PWRITE.value = transaction.fields['pwrite']
440
+ self.bus.PADDR.value = transaction.fields['paddr']
441
+ if self.is_signal_present('PPROT'):
442
+ self.bus.PPROT.value = transaction.fields['pprot']
443
+ if self.is_signal_present('PSTRB'):
444
+ self.bus.PSTRB.value = transaction.fields['pstrb']
445
+ # Check direction from packet
446
+ if transaction.direction == 'WRITE':
447
+ self.bus.PWDATA.value = transaction.fields['pwdata']
448
+
449
+ await RisingEdge(self.clock)
450
+ await Timer(200, units='ps')
451
+
452
+ for _ in range(penable_delay):
453
+ await RisingEdge(self.clock)
454
+
455
+ self.bus.PENABLE.value = 1
456
+ await FallingEdge(self.clock)
457
+
458
+ while not self.bus.PREADY.value:
459
+ await FallingEdge(self.clock)
460
+
461
+ # Wait for signal values to settle before sampling (matches APBMonitor/APBSlave timing)
462
+ await Timer(200, units='ps')
463
+
464
+ # check if the slave is asserting an error
465
+ if self.is_signal_present('PSLVERR') and self.bus.PSLVERR.value:
466
+ transaction.fields['pslverr'] = self.bus.PSLVERR.value.integer
467
+
468
+ # if this is a read we should sample the data
469
+ if transaction.direction == 'READ':
470
+ if self.bus.PRDATA.value.is_resolvable:
471
+ transaction.fields['prdata'] = self.bus.PRDATA.value.integer
472
+ else:
473
+ transaction.fields['prdata'] = self.bus.PRDATA.value
474
+
475
+ self.sentQ.append(transaction)
476
+ await RisingEdge(self.clock)