cocotbext-ral 0.5.2__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_ral-0.5.2/LICENSE +21 -0
- cocotbext_ral-0.5.2/MANIFEST.in +3 -0
- cocotbext_ral-0.5.2/PKG-INFO +211 -0
- cocotbext_ral-0.5.2/README.md +177 -0
- cocotbext_ral-0.5.2/cocotbext/ral/__init__.py +22 -0
- cocotbext_ral-0.5.2/cocotbext/ral/access_policy.py +66 -0
- cocotbext_ral-0.5.2/cocotbext/ral/adapters/__init__.py +6 -0
- cocotbext_ral-0.5.2/cocotbext/ral/adapters/json_loader.py +133 -0
- cocotbext_ral-0.5.2/cocotbext/ral/adapters/rdl_loader.py +144 -0
- cocotbext_ral-0.5.2/cocotbext/ral/backdoor.py +67 -0
- cocotbext_ral-0.5.2/cocotbext/ral/checker.py +75 -0
- cocotbext_ral-0.5.2/cocotbext/ral/codegen.py +172 -0
- cocotbext_ral-0.5.2/cocotbext/ral/debug.py +32 -0
- cocotbext_ral-0.5.2/cocotbext/ral/experimental.py +22 -0
- cocotbext_ral-0.5.2/cocotbext/ral/integrated_runtime_ral.py +321 -0
- cocotbext_ral-0.5.2/cocotbext/ral/monitor.py +208 -0
- cocotbext_ral-0.5.2/cocotbext/ral/register_model.py +491 -0
- cocotbext_ral-0.5.2/cocotbext/ral/rmw_policy.py +54 -0
- cocotbext_ral-0.5.2/cocotbext/ral/runtime_predictor.py +133 -0
- cocotbext_ral-0.5.2/cocotbext/ral/runtime_ral.py +843 -0
- cocotbext_ral-0.5.2/cocotbext/ral/safe_runtime_ral.py +53 -0
- cocotbext_ral-0.5.2/cocotbext/ral/state.py +130 -0
- cocotbext_ral-0.5.2/cocotbext/ral/transaction_logger.py +494 -0
- cocotbext_ral-0.5.2/cocotbext/ral/version.py +1 -0
- cocotbext_ral-0.5.2/cocotbext/ral/volatile_policy.py +36 -0
- cocotbext_ral-0.5.2/cocotbext_ral.egg-info/PKG-INFO +211 -0
- cocotbext_ral-0.5.2/cocotbext_ral.egg-info/SOURCES.txt +44 -0
- cocotbext_ral-0.5.2/cocotbext_ral.egg-info/dependency_links.txt +1 -0
- cocotbext_ral-0.5.2/cocotbext_ral.egg-info/requires.txt +13 -0
- cocotbext_ral-0.5.2/cocotbext_ral.egg-info/top_level.txt +1 -0
- cocotbext_ral-0.5.2/pyproject.toml +43 -0
- cocotbext_ral-0.5.2/setup.cfg +4 -0
- cocotbext_ral-0.5.2/tests/test_access_policies.py +45 -0
- cocotbext_ral-0.5.2/tests/test_backdoor.py +125 -0
- cocotbext_ral-0.5.2/tests/test_checker.py +92 -0
- cocotbext_ral-0.5.2/tests/test_debug.py +80 -0
- cocotbext_ral-0.5.2/tests/test_json_loader.py +116 -0
- cocotbext_ral-0.5.2/tests/test_model_search.py +180 -0
- cocotbext_ral-0.5.2/tests/test_register_model.py +177 -0
- cocotbext_ral-0.5.2/tests/test_rmw_policy.py +100 -0
- cocotbext_ral-0.5.2/tests/test_runtime_predictor.py +38 -0
- cocotbext_ral-0.5.2/tests/test_runtime_predictor_comprehensive.py +254 -0
- cocotbext_ral-0.5.2/tests/test_runtime_ral.py +92 -0
- cocotbext_ral-0.5.2/tests/test_state.py +161 -0
- cocotbext_ral-0.5.2/tests/test_transaction_logger.py +303 -0
- cocotbext_ral-0.5.2/tests/test_volatile_policy.py +73 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 cocotbext-ral contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: cocotbext-ral
|
|
3
|
+
Version: 0.5.2
|
|
4
|
+
Summary: Python-native Register Abstraction Layer (RAL) for cocotb with a data-driven runtime architecture
|
|
5
|
+
License-Expression: MIT
|
|
6
|
+
Project-URL: Homepage, https://github.com/stephencoTT/cocotbext-ral
|
|
7
|
+
Project-URL: Issues, https://github.com/stephencoTT/cocotbext-ral/issues
|
|
8
|
+
Keywords: cocotb,ral,register model,verification,eda
|
|
9
|
+
Classifier: Development Status :: 3 - Alpha
|
|
10
|
+
Classifier: Framework :: cocotb
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: Intended Audience :: Science/Research
|
|
13
|
+
Classifier: Operating System :: OS Independent
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
20
|
+
Classifier: Topic :: Scientific/Engineering :: Electronic Design Automation (EDA)
|
|
21
|
+
Requires-Python: >=3.9
|
|
22
|
+
Description-Content-Type: text/markdown
|
|
23
|
+
License-File: LICENSE
|
|
24
|
+
Provides-Extra: cocotb
|
|
25
|
+
Requires-Dist: cocotb>=1.8.0; extra == "cocotb"
|
|
26
|
+
Requires-Dist: cocotbext-axi>=0.1.16; extra == "cocotb"
|
|
27
|
+
Provides-Extra: rdl
|
|
28
|
+
Requires-Dist: systemrdl-compiler>=1.15; extra == "rdl"
|
|
29
|
+
Provides-Extra: dev
|
|
30
|
+
Requires-Dist: pytest>=7.0; extra == "dev"
|
|
31
|
+
Provides-Extra: all
|
|
32
|
+
Requires-Dist: cocotbext-ral[cocotb,dev,rdl]; extra == "all"
|
|
33
|
+
Dynamic: license-file
|
|
34
|
+
|
|
35
|
+
# cocotbext-ral
|
|
36
|
+
|
|
37
|
+
A Python-native Register Abstraction Layer (RAL) for [cocotb](https://www.cocotb.org/).
|
|
38
|
+
|
|
39
|
+
`cocotbext-ral` provides UVM-like register modeling, access-type-aware prediction, and automated checking -- all in pure Python, designed for cocotb hardware verification.
|
|
40
|
+
|
|
41
|
+
## Features
|
|
42
|
+
|
|
43
|
+
- **Runtime-backed architecture** with clean separation of register spec vs mutable state
|
|
44
|
+
- **Full access-type coverage**: RW, RO, WO, W1C, W1S, RCLR, RSET
|
|
45
|
+
- **Smart volatile inference**: RO, RCLR, RSET fields default to volatile (overridable)
|
|
46
|
+
- **Safe field writes** that reject unsafe read-modify-write sequences
|
|
47
|
+
- **Backdoor resolution** that scales to replicated blocks and chiplets
|
|
48
|
+
- **Transaction logging** with detailed per-transaction file output
|
|
49
|
+
- **Pure-Python core** -- model, predictor, and policies work without cocotb
|
|
50
|
+
|
|
51
|
+
## Architecture
|
|
52
|
+
|
|
53
|
+
```
|
|
54
|
+
RegisterModel (spec)
|
|
55
|
+
|
|
|
56
|
+
RuntimeState (per-instance mutable state)
|
|
57
|
+
|
|
|
58
|
+
RuntimePredictor + AccessPolicy
|
|
59
|
+
|
|
|
60
|
+
RuntimeRAL -> SafeRuntimeRAL -> IntegratedRuntimeRAL
|
|
61
|
+
| |
|
|
62
|
+
Backdoor Transaction Log
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## Installation
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
pip install cocotbext-ral
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
Optional extras:
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
pip install cocotbext-ral[cocotb] # cocotb + cocotbext-axi
|
|
75
|
+
pip install cocotbext-ral[rdl] # SystemRDL compiler
|
|
76
|
+
pip install cocotbext-ral[all] # Everything
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## Quick start
|
|
80
|
+
|
|
81
|
+
### With cocotb simulation
|
|
82
|
+
|
|
83
|
+
```python
|
|
84
|
+
import cocotb
|
|
85
|
+
from cocotbext.axi import AxiLiteMaster, AxiLiteBus
|
|
86
|
+
from cocotbext.ral import IntegratedRuntimeRAL
|
|
87
|
+
from cocotbext.ral.adapters import load_json
|
|
88
|
+
|
|
89
|
+
@cocotb.test()
|
|
90
|
+
async def test_registers(dut):
|
|
91
|
+
model = load_json("registers.json")
|
|
92
|
+
|
|
93
|
+
ral = IntegratedRuntimeRAL("my_ip", model, dut_handle=dut, txn_log=True)
|
|
94
|
+
master = AxiLiteMaster(AxiLiteBus.from_prefix(dut, "s_axil"), dut.clk, dut.rst)
|
|
95
|
+
ral.attach_master(master, protocol="axil", interface="dut.s_axil")
|
|
96
|
+
|
|
97
|
+
# Write and read with automatic prediction checking
|
|
98
|
+
await ral.write("CTRL", 0x01)
|
|
99
|
+
val = await ral.read("CTRL")
|
|
100
|
+
|
|
101
|
+
# Field-level access with RMW safety
|
|
102
|
+
await ral.write_field("CTRL", "enable", 1)
|
|
103
|
+
en = await ral.read_field("CTRL", "enable")
|
|
104
|
+
|
|
105
|
+
# Check results
|
|
106
|
+
ral.raise_on_errors()
|
|
107
|
+
ral.close_txn_log()
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
### Standalone (no cocotb)
|
|
111
|
+
|
|
112
|
+
The core model, predictor, and policy layers have zero dependencies:
|
|
113
|
+
|
|
114
|
+
```python
|
|
115
|
+
from cocotbext.ral import RegisterModel, Register, RegisterField, SwAccess
|
|
116
|
+
from cocotbext.ral.runtime_predictor import RuntimePredictor
|
|
117
|
+
|
|
118
|
+
model = RegisterModel("my_ip")
|
|
119
|
+
reg = Register("CTRL", address=0x0, size_bits=32, fields=[
|
|
120
|
+
RegisterField("enable", lsb=0, msb=0, reset_value=0, sw_access=SwAccess.RW),
|
|
121
|
+
RegisterField("irq", lsb=8, msb=15, reset_value=0xFF, sw_access=SwAccess.W1C),
|
|
122
|
+
])
|
|
123
|
+
model.add_register(reg, hierarchical_name="block.CTRL")
|
|
124
|
+
|
|
125
|
+
pred = RuntimePredictor(model)
|
|
126
|
+
pred.predict_write(0x0, 0x01)
|
|
127
|
+
result = pred.predict_read(0x0, 0x01)
|
|
128
|
+
assert result.passed
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
## API classes
|
|
132
|
+
|
|
133
|
+
| Class | Description |
|
|
134
|
+
|---|---|
|
|
135
|
+
| `IntegratedRuntimeRAL` | **Recommended.** Runtime state + RMW safety + backdoor + transaction log |
|
|
136
|
+
| `SafeRuntimeRAL` | Runtime state + RMW safety checks |
|
|
137
|
+
| `RuntimeRAL` | Runtime state-backed prediction |
|
|
138
|
+
| `RuntimePredictor` | Standalone prediction engine (no cocotb needed) |
|
|
139
|
+
| `RuntimeState` | Per-instance mutable register state |
|
|
140
|
+
| `AccessPolicy` | Per-field read/write behavior |
|
|
141
|
+
| `TransactionLogger` | Detailed file-based transaction logging |
|
|
142
|
+
| `BackdoorResolver` | HDL path resolution (base, Prefix, Mapping variants) |
|
|
143
|
+
| `RegisterModel` | Structural register specification |
|
|
144
|
+
|
|
145
|
+
## Register loading
|
|
146
|
+
|
|
147
|
+
```python
|
|
148
|
+
from cocotbext.ral.adapters import load_json, load_rdl
|
|
149
|
+
|
|
150
|
+
model = load_json("registers.json") # RDL-generated JSON
|
|
151
|
+
model = load_rdl("regs.rdl", top_name="my_ip", incdir=[]) # SystemRDL source
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
## Access types
|
|
155
|
+
|
|
156
|
+
| SwAccess | Write behavior | Read behavior | Volatile by default |
|
|
157
|
+
|---|---|---|---|
|
|
158
|
+
| `RW` | Update value | Check prediction | No |
|
|
159
|
+
| `RO` | Ignored | Not checked | Yes |
|
|
160
|
+
| `WO` | Update value | Not checked | No |
|
|
161
|
+
| `W1C` | Clear written-1 bits | Check prediction | No |
|
|
162
|
+
| `W1S` | Set written-1 bits | Check prediction | No |
|
|
163
|
+
| `RCLR` | Ignored | Clears to 0 after read | Yes |
|
|
164
|
+
| `RSET` | Ignored | Sets to all-1s after read | Yes |
|
|
165
|
+
|
|
166
|
+
Aliases: `WOCLR`=`W1C`, `WOSET`=`W1S`, `RC`=`RCLR`, `RS`=`RSET`
|
|
167
|
+
|
|
168
|
+
## Transaction logging
|
|
169
|
+
|
|
170
|
+
Enable detailed per-transaction file output:
|
|
171
|
+
|
|
172
|
+
```python
|
|
173
|
+
# Default filename (register_txns.log)
|
|
174
|
+
ral = IntegratedRuntimeRAL("ip", model, txn_log=True)
|
|
175
|
+
|
|
176
|
+
# Custom path
|
|
177
|
+
ral = IntegratedRuntimeRAL("ip", model, txn_log="my_regs.log")
|
|
178
|
+
|
|
179
|
+
# Phase annotations
|
|
180
|
+
ral.set_txn_phase("Phase 1: Reset value check")
|
|
181
|
+
await ral.write(addr, value)
|
|
182
|
+
|
|
183
|
+
# Summary and close
|
|
184
|
+
ral.close_txn_log()
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
Each transaction entry includes: model path, address, data, protocol, interface HDL path, status (PASS/FAIL/SKIP), mirror state, per-field breakdown, and RMW safety assessment.
|
|
188
|
+
|
|
189
|
+
## Testing
|
|
190
|
+
|
|
191
|
+
```bash
|
|
192
|
+
pip install -e .[dev]
|
|
193
|
+
pytest
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
146 tests covering: register model (including search / group helpers), runtime predictor (all access types), access policies, RMW safety, backdoor resolvers, runtime state, volatile policy, debug helpers, checker, JSON loader, transaction logger, and RuntimeRAL construction / reset / check-toggle helpers.
|
|
197
|
+
|
|
198
|
+
## Documentation
|
|
199
|
+
|
|
200
|
+
Read in this order:
|
|
201
|
+
|
|
202
|
+
1. [Quick Start](docs/START_GUIDE.md) -- 10-step walkthrough covering all access types and the common flows.
|
|
203
|
+
2. [Architecture](docs/ARCHITECTURE.md) -- layer-by-layer design and the rationale behind each decision.
|
|
204
|
+
3. API reference (pick what you need):
|
|
205
|
+
- [Core Model](docs/api/CORE_MODEL.md) -- `RegisterModel`, `Register`, `RegisterField`, `SwAccess`.
|
|
206
|
+
- [Runtime API](docs/api/RUNTIME_API.md) -- `IntegratedRuntimeRAL`, `SafeRuntimeRAL`, mirror update modes, RMW logging, search / bulk APIs.
|
|
207
|
+
- [Integration and Loaders](docs/api/INTEGRATION_AND_LOADERS.md) -- JSON/RDL loaders, backdoor resolvers, transaction logging.
|
|
208
|
+
|
|
209
|
+
## License
|
|
210
|
+
|
|
211
|
+
MIT
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
# cocotbext-ral
|
|
2
|
+
|
|
3
|
+
A Python-native Register Abstraction Layer (RAL) for [cocotb](https://www.cocotb.org/).
|
|
4
|
+
|
|
5
|
+
`cocotbext-ral` provides UVM-like register modeling, access-type-aware prediction, and automated checking -- all in pure Python, designed for cocotb hardware verification.
|
|
6
|
+
|
|
7
|
+
## Features
|
|
8
|
+
|
|
9
|
+
- **Runtime-backed architecture** with clean separation of register spec vs mutable state
|
|
10
|
+
- **Full access-type coverage**: RW, RO, WO, W1C, W1S, RCLR, RSET
|
|
11
|
+
- **Smart volatile inference**: RO, RCLR, RSET fields default to volatile (overridable)
|
|
12
|
+
- **Safe field writes** that reject unsafe read-modify-write sequences
|
|
13
|
+
- **Backdoor resolution** that scales to replicated blocks and chiplets
|
|
14
|
+
- **Transaction logging** with detailed per-transaction file output
|
|
15
|
+
- **Pure-Python core** -- model, predictor, and policies work without cocotb
|
|
16
|
+
|
|
17
|
+
## Architecture
|
|
18
|
+
|
|
19
|
+
```
|
|
20
|
+
RegisterModel (spec)
|
|
21
|
+
|
|
|
22
|
+
RuntimeState (per-instance mutable state)
|
|
23
|
+
|
|
|
24
|
+
RuntimePredictor + AccessPolicy
|
|
25
|
+
|
|
|
26
|
+
RuntimeRAL -> SafeRuntimeRAL -> IntegratedRuntimeRAL
|
|
27
|
+
| |
|
|
28
|
+
Backdoor Transaction Log
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Installation
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
pip install cocotbext-ral
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Optional extras:
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
pip install cocotbext-ral[cocotb] # cocotb + cocotbext-axi
|
|
41
|
+
pip install cocotbext-ral[rdl] # SystemRDL compiler
|
|
42
|
+
pip install cocotbext-ral[all] # Everything
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## Quick start
|
|
46
|
+
|
|
47
|
+
### With cocotb simulation
|
|
48
|
+
|
|
49
|
+
```python
|
|
50
|
+
import cocotb
|
|
51
|
+
from cocotbext.axi import AxiLiteMaster, AxiLiteBus
|
|
52
|
+
from cocotbext.ral import IntegratedRuntimeRAL
|
|
53
|
+
from cocotbext.ral.adapters import load_json
|
|
54
|
+
|
|
55
|
+
@cocotb.test()
|
|
56
|
+
async def test_registers(dut):
|
|
57
|
+
model = load_json("registers.json")
|
|
58
|
+
|
|
59
|
+
ral = IntegratedRuntimeRAL("my_ip", model, dut_handle=dut, txn_log=True)
|
|
60
|
+
master = AxiLiteMaster(AxiLiteBus.from_prefix(dut, "s_axil"), dut.clk, dut.rst)
|
|
61
|
+
ral.attach_master(master, protocol="axil", interface="dut.s_axil")
|
|
62
|
+
|
|
63
|
+
# Write and read with automatic prediction checking
|
|
64
|
+
await ral.write("CTRL", 0x01)
|
|
65
|
+
val = await ral.read("CTRL")
|
|
66
|
+
|
|
67
|
+
# Field-level access with RMW safety
|
|
68
|
+
await ral.write_field("CTRL", "enable", 1)
|
|
69
|
+
en = await ral.read_field("CTRL", "enable")
|
|
70
|
+
|
|
71
|
+
# Check results
|
|
72
|
+
ral.raise_on_errors()
|
|
73
|
+
ral.close_txn_log()
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### Standalone (no cocotb)
|
|
77
|
+
|
|
78
|
+
The core model, predictor, and policy layers have zero dependencies:
|
|
79
|
+
|
|
80
|
+
```python
|
|
81
|
+
from cocotbext.ral import RegisterModel, Register, RegisterField, SwAccess
|
|
82
|
+
from cocotbext.ral.runtime_predictor import RuntimePredictor
|
|
83
|
+
|
|
84
|
+
model = RegisterModel("my_ip")
|
|
85
|
+
reg = Register("CTRL", address=0x0, size_bits=32, fields=[
|
|
86
|
+
RegisterField("enable", lsb=0, msb=0, reset_value=0, sw_access=SwAccess.RW),
|
|
87
|
+
RegisterField("irq", lsb=8, msb=15, reset_value=0xFF, sw_access=SwAccess.W1C),
|
|
88
|
+
])
|
|
89
|
+
model.add_register(reg, hierarchical_name="block.CTRL")
|
|
90
|
+
|
|
91
|
+
pred = RuntimePredictor(model)
|
|
92
|
+
pred.predict_write(0x0, 0x01)
|
|
93
|
+
result = pred.predict_read(0x0, 0x01)
|
|
94
|
+
assert result.passed
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
## API classes
|
|
98
|
+
|
|
99
|
+
| Class | Description |
|
|
100
|
+
|---|---|
|
|
101
|
+
| `IntegratedRuntimeRAL` | **Recommended.** Runtime state + RMW safety + backdoor + transaction log |
|
|
102
|
+
| `SafeRuntimeRAL` | Runtime state + RMW safety checks |
|
|
103
|
+
| `RuntimeRAL` | Runtime state-backed prediction |
|
|
104
|
+
| `RuntimePredictor` | Standalone prediction engine (no cocotb needed) |
|
|
105
|
+
| `RuntimeState` | Per-instance mutable register state |
|
|
106
|
+
| `AccessPolicy` | Per-field read/write behavior |
|
|
107
|
+
| `TransactionLogger` | Detailed file-based transaction logging |
|
|
108
|
+
| `BackdoorResolver` | HDL path resolution (base, Prefix, Mapping variants) |
|
|
109
|
+
| `RegisterModel` | Structural register specification |
|
|
110
|
+
|
|
111
|
+
## Register loading
|
|
112
|
+
|
|
113
|
+
```python
|
|
114
|
+
from cocotbext.ral.adapters import load_json, load_rdl
|
|
115
|
+
|
|
116
|
+
model = load_json("registers.json") # RDL-generated JSON
|
|
117
|
+
model = load_rdl("regs.rdl", top_name="my_ip", incdir=[]) # SystemRDL source
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
## Access types
|
|
121
|
+
|
|
122
|
+
| SwAccess | Write behavior | Read behavior | Volatile by default |
|
|
123
|
+
|---|---|---|---|
|
|
124
|
+
| `RW` | Update value | Check prediction | No |
|
|
125
|
+
| `RO` | Ignored | Not checked | Yes |
|
|
126
|
+
| `WO` | Update value | Not checked | No |
|
|
127
|
+
| `W1C` | Clear written-1 bits | Check prediction | No |
|
|
128
|
+
| `W1S` | Set written-1 bits | Check prediction | No |
|
|
129
|
+
| `RCLR` | Ignored | Clears to 0 after read | Yes |
|
|
130
|
+
| `RSET` | Ignored | Sets to all-1s after read | Yes |
|
|
131
|
+
|
|
132
|
+
Aliases: `WOCLR`=`W1C`, `WOSET`=`W1S`, `RC`=`RCLR`, `RS`=`RSET`
|
|
133
|
+
|
|
134
|
+
## Transaction logging
|
|
135
|
+
|
|
136
|
+
Enable detailed per-transaction file output:
|
|
137
|
+
|
|
138
|
+
```python
|
|
139
|
+
# Default filename (register_txns.log)
|
|
140
|
+
ral = IntegratedRuntimeRAL("ip", model, txn_log=True)
|
|
141
|
+
|
|
142
|
+
# Custom path
|
|
143
|
+
ral = IntegratedRuntimeRAL("ip", model, txn_log="my_regs.log")
|
|
144
|
+
|
|
145
|
+
# Phase annotations
|
|
146
|
+
ral.set_txn_phase("Phase 1: Reset value check")
|
|
147
|
+
await ral.write(addr, value)
|
|
148
|
+
|
|
149
|
+
# Summary and close
|
|
150
|
+
ral.close_txn_log()
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
Each transaction entry includes: model path, address, data, protocol, interface HDL path, status (PASS/FAIL/SKIP), mirror state, per-field breakdown, and RMW safety assessment.
|
|
154
|
+
|
|
155
|
+
## Testing
|
|
156
|
+
|
|
157
|
+
```bash
|
|
158
|
+
pip install -e .[dev]
|
|
159
|
+
pytest
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
146 tests covering: register model (including search / group helpers), runtime predictor (all access types), access policies, RMW safety, backdoor resolvers, runtime state, volatile policy, debug helpers, checker, JSON loader, transaction logger, and RuntimeRAL construction / reset / check-toggle helpers.
|
|
163
|
+
|
|
164
|
+
## Documentation
|
|
165
|
+
|
|
166
|
+
Read in this order:
|
|
167
|
+
|
|
168
|
+
1. [Quick Start](docs/START_GUIDE.md) -- 10-step walkthrough covering all access types and the common flows.
|
|
169
|
+
2. [Architecture](docs/ARCHITECTURE.md) -- layer-by-layer design and the rationale behind each decision.
|
|
170
|
+
3. API reference (pick what you need):
|
|
171
|
+
- [Core Model](docs/api/CORE_MODEL.md) -- `RegisterModel`, `Register`, `RegisterField`, `SwAccess`.
|
|
172
|
+
- [Runtime API](docs/api/RUNTIME_API.md) -- `IntegratedRuntimeRAL`, `SafeRuntimeRAL`, mirror update modes, RMW logging, search / bulk APIs.
|
|
173
|
+
- [Integration and Loaders](docs/api/INTEGRATION_AND_LOADERS.md) -- JSON/RDL loaders, backdoor resolvers, transaction logging.
|
|
174
|
+
|
|
175
|
+
## License
|
|
176
|
+
|
|
177
|
+
MIT
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"""Cocotb Register Abstraction Layer (RAL).
|
|
2
|
+
|
|
3
|
+
Generic, reusable RAL with zero Quasar-specific dependencies.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from .version import __version__
|
|
7
|
+
from .register_model import RegisterField, Register, RegisterBlock, RegisterModel, SwAccess, Memory
|
|
8
|
+
from .runtime_predictor import RuntimePredictor, PredictionResult, FieldResult
|
|
9
|
+
from .state import RuntimeState, RegisterState, FieldState
|
|
10
|
+
from .checker import Checker
|
|
11
|
+
|
|
12
|
+
# Runtime-backed RAL APIs.
|
|
13
|
+
from .runtime_ral import RuntimeRAL
|
|
14
|
+
from .safe_runtime_ral import SafeRuntimeRAL
|
|
15
|
+
from .integrated_runtime_ral import IntegratedRuntimeRAL
|
|
16
|
+
from .transaction_logger import TransactionLogger
|
|
17
|
+
|
|
18
|
+
# Cocotb-dependent classes.
|
|
19
|
+
try:
|
|
20
|
+
from .monitor import ApbRalMonitor, AxiLiteRalMonitor, AxiRalMonitor
|
|
21
|
+
except ImportError:
|
|
22
|
+
pass
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
"""Field access policy helpers for the runtime path.
|
|
2
|
+
|
|
3
|
+
Supports common SystemRDL-style semantics including RW, RO, WO, W1C, W1S,
|
|
4
|
+
RCLR, and RSET.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from dataclasses import dataclass
|
|
8
|
+
|
|
9
|
+
from .register_model import RegisterField, SwAccess
|
|
10
|
+
from .state import FieldState
|
|
11
|
+
from .volatile_policy import check_allowed as _volatile_check_allowed
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@dataclass(frozen=True)
|
|
15
|
+
class AccessPolicy:
|
|
16
|
+
"""Behavioral policy for a field's software-visible access semantics."""
|
|
17
|
+
|
|
18
|
+
sw_access: SwAccess
|
|
19
|
+
|
|
20
|
+
def apply_write(self, field: RegisterField, state: FieldState, write_value: int) -> None:
|
|
21
|
+
"""Apply a software write to runtime field state."""
|
|
22
|
+
value = write_value & field.mask
|
|
23
|
+
|
|
24
|
+
if self.sw_access == SwAccess.RW:
|
|
25
|
+
state.mirrored = value
|
|
26
|
+
elif self.sw_access == SwAccess.WO:
|
|
27
|
+
state.mirrored = value
|
|
28
|
+
elif self.sw_access == SwAccess.W1C:
|
|
29
|
+
state.mirrored &= ~value
|
|
30
|
+
state.mirrored &= field.mask
|
|
31
|
+
elif self.sw_access == SwAccess.W1S:
|
|
32
|
+
state.mirrored |= value
|
|
33
|
+
state.mirrored &= field.mask
|
|
34
|
+
elif self.sw_access in (SwAccess.RO, SwAccess.RCLR, SwAccess.RSET):
|
|
35
|
+
return
|
|
36
|
+
|
|
37
|
+
state.desired = state.mirrored
|
|
38
|
+
state.dirty = False
|
|
39
|
+
|
|
40
|
+
def apply_read_side_effect(self, field: RegisterField, state: FieldState) -> None:
|
|
41
|
+
"""Apply any state change caused by reading the field."""
|
|
42
|
+
if self.sw_access == SwAccess.RCLR:
|
|
43
|
+
state.mirrored = 0
|
|
44
|
+
state.desired = 0
|
|
45
|
+
elif self.sw_access == SwAccess.RSET:
|
|
46
|
+
state.mirrored = field.mask
|
|
47
|
+
state.desired = field.mask
|
|
48
|
+
|
|
49
|
+
def check_on_read(self, field: RegisterField, state: FieldState) -> bool:
|
|
50
|
+
"""Return True if the field should be checked on reads."""
|
|
51
|
+
if not _volatile_check_allowed(field, state):
|
|
52
|
+
return False
|
|
53
|
+
return self.sw_access in (
|
|
54
|
+
SwAccess.RW,
|
|
55
|
+
SwAccess.W1C,
|
|
56
|
+
SwAccess.W1S,
|
|
57
|
+
SwAccess.RCLR,
|
|
58
|
+
SwAccess.RSET,
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
class PolicyRegistry:
|
|
63
|
+
"""Minimal registry from field spec to runtime policy object."""
|
|
64
|
+
|
|
65
|
+
def policy_for(self, field: RegisterField) -> AccessPolicy:
|
|
66
|
+
return AccessPolicy(field.sw_access)
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
"""Load a RegisterModel from RDL-generated JSON.
|
|
2
|
+
|
|
3
|
+
Parses the hierarchical JSON format produced by create_reg_json.py
|
|
4
|
+
(addrmap -> regfile -> reg -> field) into the RAL RegisterModel.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import json
|
|
8
|
+
from typing import Union
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
|
|
11
|
+
from ..register_model import (
|
|
12
|
+
Memory,
|
|
13
|
+
RegisterField,
|
|
14
|
+
Register,
|
|
15
|
+
RegisterBlock,
|
|
16
|
+
RegisterModel,
|
|
17
|
+
SwAccess,
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def _map_sw_access(sw_access_str: str, woclr: int) -> SwAccess:
|
|
22
|
+
"""Map JSON sw_access string + woclr flag to SwAccess enum."""
|
|
23
|
+
sw = sw_access_str.lower()
|
|
24
|
+
if sw == "rw" and woclr:
|
|
25
|
+
return SwAccess.WOCLR
|
|
26
|
+
if sw == "rw":
|
|
27
|
+
return SwAccess.RW
|
|
28
|
+
if sw == "r":
|
|
29
|
+
return SwAccess.RO
|
|
30
|
+
if sw == "w":
|
|
31
|
+
return SwAccess.WO
|
|
32
|
+
# Default to RW for unknown access types
|
|
33
|
+
return SwAccess.RW
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def _parse_node(
|
|
37
|
+
node: dict,
|
|
38
|
+
model: RegisterModel,
|
|
39
|
+
base_address: int,
|
|
40
|
+
name_prefix: str,
|
|
41
|
+
):
|
|
42
|
+
"""Recursively walk the JSON hierarchy, accumulating addresses and names."""
|
|
43
|
+
node_type = node.get("type", "")
|
|
44
|
+
inst_name = node.get("inst_name", "")
|
|
45
|
+
addr_offset = node.get("addr_offset", 0)
|
|
46
|
+
current_address = base_address + addr_offset
|
|
47
|
+
|
|
48
|
+
# Build hierarchical name
|
|
49
|
+
if name_prefix:
|
|
50
|
+
current_name = f"{name_prefix}.{inst_name}"
|
|
51
|
+
else:
|
|
52
|
+
current_name = inst_name
|
|
53
|
+
|
|
54
|
+
if node_type == "reg":
|
|
55
|
+
# Parse fields
|
|
56
|
+
fields = []
|
|
57
|
+
for child in node.get("children", []):
|
|
58
|
+
if child.get("type") == "field":
|
|
59
|
+
reset_val = child.get("reset", 0)
|
|
60
|
+
if not isinstance(reset_val, (int, float)):
|
|
61
|
+
reset_val = 0
|
|
62
|
+
reset_val = int(reset_val)
|
|
63
|
+
|
|
64
|
+
sw_access = _map_sw_access(
|
|
65
|
+
child.get("sw_access", "rw"),
|
|
66
|
+
child.get("woclr", 0),
|
|
67
|
+
)
|
|
68
|
+
field = RegisterField(
|
|
69
|
+
name=child["inst_name"],
|
|
70
|
+
lsb=child["lsb"],
|
|
71
|
+
msb=child["msb"],
|
|
72
|
+
reset_value=reset_val,
|
|
73
|
+
sw_access=sw_access,
|
|
74
|
+
)
|
|
75
|
+
fields.append(field)
|
|
76
|
+
|
|
77
|
+
reg = Register(
|
|
78
|
+
name=inst_name,
|
|
79
|
+
address=current_address,
|
|
80
|
+
size_bits=node.get("regsize", 32),
|
|
81
|
+
fields=fields,
|
|
82
|
+
description=node.get("desc", ""),
|
|
83
|
+
)
|
|
84
|
+
model.add_register(reg, hierarchical_name=current_name)
|
|
85
|
+
return
|
|
86
|
+
|
|
87
|
+
if node_type == "mem":
|
|
88
|
+
# Parse memory regions (SRAM, apertures, FIFOs, etc.)
|
|
89
|
+
memwidth = node.get("memwidth", 32)
|
|
90
|
+
mementries = node.get("mementries", 0)
|
|
91
|
+
size_bytes = (memwidth * mementries) // 8
|
|
92
|
+
mem = Memory(
|
|
93
|
+
name=inst_name,
|
|
94
|
+
base_address=current_address,
|
|
95
|
+
size_bytes=size_bytes,
|
|
96
|
+
description=node.get("desc", ""),
|
|
97
|
+
)
|
|
98
|
+
mem.hierarchical_name = current_name
|
|
99
|
+
model.add_memory(mem, hierarchical_name=current_name)
|
|
100
|
+
return
|
|
101
|
+
|
|
102
|
+
# For addrmap, regfile, or any container: recurse into children
|
|
103
|
+
for child in node.get("children", []):
|
|
104
|
+
child_type = child.get("type", "")
|
|
105
|
+
if child_type in ("addrmap", "regfile", "reg", "mem"):
|
|
106
|
+
_parse_node(child, model, current_address, current_name)
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def load_json(
|
|
110
|
+
json_path: Union[str, Path],
|
|
111
|
+
model_name: str = "",
|
|
112
|
+
) -> RegisterModel:
|
|
113
|
+
"""Load a RegisterModel from an RDL-generated JSON file.
|
|
114
|
+
|
|
115
|
+
Args:
|
|
116
|
+
json_path: Path to the JSON register description file.
|
|
117
|
+
model_name: Optional name for the model. Defaults to the root
|
|
118
|
+
inst_name from the JSON.
|
|
119
|
+
|
|
120
|
+
Returns:
|
|
121
|
+
A populated RegisterModel.
|
|
122
|
+
"""
|
|
123
|
+
json_path = Path(json_path)
|
|
124
|
+
with open(json_path, "r") as f:
|
|
125
|
+
root = json.load(f)
|
|
126
|
+
|
|
127
|
+
name = model_name or root.get("inst_name", json_path.stem)
|
|
128
|
+
model = RegisterModel(name=name)
|
|
129
|
+
|
|
130
|
+
# The root is typically an addrmap — recurse from there
|
|
131
|
+
_parse_node(root, model, base_address=0, name_prefix="")
|
|
132
|
+
|
|
133
|
+
return model
|