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.
- CocoTBFramework/__init__.py +3 -0
- CocoTBFramework/components/__init__.py +0 -0
- CocoTBFramework/components/apb/__init__.py +0 -0
- CocoTBFramework/components/apb/apb_components.py +476 -0
- CocoTBFramework/components/apb/apb_factories.py +426 -0
- CocoTBFramework/components/apb/apb_packet.py +417 -0
- CocoTBFramework/components/apb/apb_sequence.py +206 -0
- CocoTBFramework/components/apb5/__init__.py +38 -0
- CocoTBFramework/components/apb5/apb5_components.py +554 -0
- CocoTBFramework/components/apb5/apb5_factories.py +219 -0
- CocoTBFramework/components/apb5/apb5_packet.py +628 -0
- CocoTBFramework/components/axi4/__init__.py +0 -0
- CocoTBFramework/components/axi4/axi4_compliance_checker.py +591 -0
- CocoTBFramework/components/axi4/axi4_factories.py +484 -0
- CocoTBFramework/components/axi4/axi4_field_configs.py +585 -0
- CocoTBFramework/components/axi4/axi4_interfaces.py +1393 -0
- CocoTBFramework/components/axi4/axi4_packet.py +398 -0
- CocoTBFramework/components/axi4/axi4_packet_utils.py +365 -0
- CocoTBFramework/components/axi4/axi4_randomization_config.py +689 -0
- CocoTBFramework/components/axi4/axi4_randomization_manager.py +339 -0
- CocoTBFramework/components/axi4/axi4_timing_config.py +103 -0
- CocoTBFramework/components/axi4/axi4_transaction.py +99 -0
- CocoTBFramework/components/axi5/__init__.py +260 -0
- CocoTBFramework/components/axi5/axi5_compliance_checker.py +758 -0
- CocoTBFramework/components/axi5/axi5_factories.py +419 -0
- CocoTBFramework/components/axi5/axi5_field_configs.py +989 -0
- CocoTBFramework/components/axi5/axi5_interfaces.py +1188 -0
- CocoTBFramework/components/axi5/axi5_packet.py +619 -0
- CocoTBFramework/components/axi5/axi5_packet_utils.py +628 -0
- CocoTBFramework/components/axi5/axi5_randomization_config.py +718 -0
- CocoTBFramework/components/axi5/axi5_randomization_manager.py +478 -0
- CocoTBFramework/components/axi5/axi5_timing_config.py +227 -0
- CocoTBFramework/components/axi5/axi5_transaction.py +336 -0
- CocoTBFramework/components/axil4/__init__.py +0 -0
- CocoTBFramework/components/axil4/axil4_compliance_checker.py +731 -0
- CocoTBFramework/components/axil4/axil4_factories.py +607 -0
- CocoTBFramework/components/axil4/axil4_field_configs.py +282 -0
- CocoTBFramework/components/axil4/axil4_interfaces.py +728 -0
- CocoTBFramework/components/axil4/axil4_packet.py +322 -0
- CocoTBFramework/components/axil4/axil4_packet_utils.py +439 -0
- CocoTBFramework/components/axis4/__init__.py +75 -0
- CocoTBFramework/components/axis4/axis_example_usage.py +409 -0
- CocoTBFramework/components/axis4/axis_factories.py +475 -0
- CocoTBFramework/components/axis4/axis_field_configs.py +172 -0
- CocoTBFramework/components/axis4/axis_master.py +361 -0
- CocoTBFramework/components/axis4/axis_monitor.py +366 -0
- CocoTBFramework/components/axis4/axis_packet.py +236 -0
- CocoTBFramework/components/axis4/axis_slave.py +341 -0
- CocoTBFramework/components/axis5/__init__.py +88 -0
- CocoTBFramework/components/axis5/axis5_factories.py +537 -0
- CocoTBFramework/components/axis5/axis5_field_configs.py +262 -0
- CocoTBFramework/components/axis5/axis5_master.py +327 -0
- CocoTBFramework/components/axis5/axis5_monitor.py +381 -0
- CocoTBFramework/components/axis5/axis5_packet.py +425 -0
- CocoTBFramework/components/axis5/axis5_slave.py +295 -0
- CocoTBFramework/components/fifo/__init__.py +0 -0
- CocoTBFramework/components/fifo/fifo_command_handler.py +263 -0
- CocoTBFramework/components/fifo/fifo_component_base.py +342 -0
- CocoTBFramework/components/fifo/fifo_factories.py +606 -0
- CocoTBFramework/components/fifo/fifo_master.py +373 -0
- CocoTBFramework/components/fifo/fifo_monitor.py +320 -0
- CocoTBFramework/components/fifo/fifo_monitor_base.py +251 -0
- CocoTBFramework/components/fifo/fifo_packet.py +84 -0
- CocoTBFramework/components/fifo/fifo_sequence.py +551 -0
- CocoTBFramework/components/fifo/fifo_slave.py +297 -0
- CocoTBFramework/components/gaxi/__init__.py +21 -0
- CocoTBFramework/components/gaxi/coverage_hooks.py +317 -0
- CocoTBFramework/components/gaxi/gaxi_command_handler.py +720 -0
- CocoTBFramework/components/gaxi/gaxi_component_base.py +433 -0
- CocoTBFramework/components/gaxi/gaxi_factories.py +480 -0
- CocoTBFramework/components/gaxi/gaxi_master.py +542 -0
- CocoTBFramework/components/gaxi/gaxi_monitor.py +236 -0
- CocoTBFramework/components/gaxi/gaxi_monitor_base.py +266 -0
- CocoTBFramework/components/gaxi/gaxi_packet.py +84 -0
- CocoTBFramework/components/gaxi/gaxi_sequence.py +568 -0
- CocoTBFramework/components/gaxi/gaxi_slave.py +498 -0
- CocoTBFramework/components/shared/__init__.py +0 -0
- CocoTBFramework/components/shared/arbiter_compliance.py +1093 -0
- CocoTBFramework/components/shared/arbiter_master.py +1133 -0
- CocoTBFramework/components/shared/arbiter_monitor.py +1461 -0
- CocoTBFramework/components/shared/data_strategies.py +578 -0
- CocoTBFramework/components/shared/debug_object.py +90 -0
- CocoTBFramework/components/shared/field_config.py +829 -0
- CocoTBFramework/components/shared/flex_config_gen.py +413 -0
- CocoTBFramework/components/shared/flex_randomizer.py +700 -0
- CocoTBFramework/components/shared/master_statistics.py +427 -0
- CocoTBFramework/components/shared/memory_model.py +531 -0
- CocoTBFramework/components/shared/monitor_statistics.py +69 -0
- CocoTBFramework/components/shared/packet.py +648 -0
- CocoTBFramework/components/shared/packet_factory.py +403 -0
- CocoTBFramework/components/shared/protocol_error_handler.py +131 -0
- CocoTBFramework/components/shared/randomization_config.py +498 -0
- CocoTBFramework/components/shared/signal_mapping_helper.py +1770 -0
- CocoTBFramework/components/smbus/__init__.py +45 -0
- CocoTBFramework/components/smbus/smbus_components.py +1086 -0
- CocoTBFramework/components/smbus/smbus_packet.py +252 -0
- CocoTBFramework/components/uart/__init__.py +66 -0
- CocoTBFramework/components/uart/uart_components.py +411 -0
- CocoTBFramework/components/uart/uart_packet.py +116 -0
- CocoTBFramework/components/wavedrom/__init__.py +0 -0
- CocoTBFramework/components/wavedrom/constraint_solver.py +1639 -0
- CocoTBFramework/components/wavedrom/signal_binder.py +195 -0
- CocoTBFramework/components/wavedrom/utility.py +874 -0
- CocoTBFramework/components/wavedrom/wavejson_gen.py +1027 -0
- CocoTBFramework/scoreboards/__init__.py +0 -0
- CocoTBFramework/scoreboards/apb_gaxi_scoreboard.py +538 -0
- CocoTBFramework/scoreboards/apb_gaxi_transformer.py +320 -0
- CocoTBFramework/scoreboards/apb_scoreboard.py +285 -0
- CocoTBFramework/scoreboards/axi4/__init__.py +0 -0
- CocoTBFramework/scoreboards/axi4/axi4_dwidth_converter_scoreboard.py +145 -0
- CocoTBFramework/scoreboards/axi4_scoreboard.py +423 -0
- CocoTBFramework/scoreboards/base_scoreboard.py +202 -0
- CocoTBFramework/scoreboards/fifo_scoreboard.py +152 -0
- CocoTBFramework/scoreboards/gaxi_scoreboard.py +277 -0
- cocotb_framework-0.1.0.dist-info/METADATA +333 -0
- cocotb_framework-0.1.0.dist-info/RECORD +119 -0
- cocotb_framework-0.1.0.dist-info/WHEEL +5 -0
- cocotb_framework-0.1.0.dist-info/licenses/LICENSE +21 -0
- cocotb_framework-0.1.0.dist-info/top_level.txt +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)
|