or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

binary-types.mdclock-generation.mdindex.mdlogging-utilities.mdsignal-handling.mdtask-management.mdtest-framework.mdtriggers-timing.md

signal-handling.mddocs/

0

# Signal Handling

1

2

Cocotb's signal handling system provides hierarchical access to HDL design objects through a comprehensive handle system. It supports reading, writing, forcing, and releasing signals across different HDL types with automatic type conversion and simulator integration.

3

4

## Capabilities

5

6

### Base Handle Classes

7

8

Foundation classes for all HDL object access with common properties and methods.

9

10

```python { .api }

11

class SimHandleBase:

12

"""

13

Base class for all simulation object handles.

14

15

Properties:

16

- _name: Signal name

17

- _type: HDL type information

18

- _fullname: Full hierarchical name

19

- _path: Hierarchical path

20

- _def_name: Definition name

21

- _def_file: Definition file

22

"""

23

24

def get_definition_name(self):

25

"""

26

Get the definition name of the object.

27

28

Returns:

29

Definition name string or empty string if not available

30

"""

31

32

def get_definition_file(self):

33

"""

34

Get the definition file of the object.

35

36

Returns:

37

Definition file path or empty string if not available

38

"""

39

```

40

41

**Usage Examples:**

42

43

```python

44

@cocotb.test()

45

async def handle_info_test(dut):

46

"""Test demonstrating handle information access."""

47

48

# Access handle properties

49

cocotb.log.info(f"DUT name: {dut._name}")

50

cocotb.log.info(f"DUT type: {dut._type}")

51

cocotb.log.info(f"DUT full name: {dut._fullname}")

52

53

# Get definition information

54

def_name = dut.get_definition_name()

55

def_file = dut.get_definition_file()

56

57

cocotb.log.info(f"Defined as: {def_name} in {def_file}")

58

59

# Access sub-signals

60

if hasattr(dut, 'cpu'):

61

cpu_def = dut.cpu.get_definition_name()

62

cocotb.log.info(f"CPU module: {cpu_def}")

63

```

64

65

### Hierarchical Objects

66

67

Objects that contain child elements and support attribute-based access to design hierarchy.

68

69

```python { .api }

70

class HierarchyObject(SimHandleBase):

71

"""

72

Hierarchical objects with child attribute access.

73

74

Supports:

75

- Attribute access to child objects (dut.cpu.registers.pc)

76

- Dynamic discovery of child objects

77

- Hierarchical navigation

78

"""

79

80

class HierarchyArrayObject(HierarchyObject):

81

"""

82

Arrays of hierarchical objects.

83

84

Supports:

85

- Indexed access to array elements (dut.memory[0])

86

- Slicing operations (dut.memory[0:7])

87

- Iteration over elements

88

"""

89

90

class RegionObject(HierarchyObject):

91

"""

92

Region/namespace objects in design hierarchy.

93

94

Represents logical groupings like packages, scopes,

95

and generate blocks.

96

"""

97

```

98

99

**Usage Examples:**

100

101

```python

102

@cocotb.test()

103

async def hierarchy_test(dut):

104

"""Test demonstrating hierarchical object access."""

105

106

# Navigate design hierarchy

107

cpu = dut.cpu_subsystem.cpu_core

108

registers = cpu.register_file

109

110

# Access specific register

111

program_counter = registers.pc

112

cocotb.log.info(f"PC value: {program_counter.value}")

113

114

# Access array elements

115

memory = dut.memory_controller.ram

116

117

# Access specific memory location

118

mem_location = memory[0x100]

119

cocotb.log.info(f"Memory[256]: {mem_location.value}")

120

121

# Iterate over array elements (if supported)

122

for i in range(8):

123

register = registers._id(f"r{i}", extended=False)

124

cocotb.log.info(f"R{i}: {register.value}")

125

126

# Access generate block instances

127

for i in range(4):

128

instance = dut._id(f"gen_block[{i}].instance", extended=False)

129

cocotb.log.info(f"Generated instance {i}: {instance._name}")

130

```

131

132

### Value Objects

133

134

Objects representing actual signal values with read/write capabilities.

135

136

```python { .api }

137

class NonHierarchyObject(SimHandleBase):

138

"""Base class for non-hierarchical (leaf) objects."""

139

140

class ConstantObject(NonHierarchyObject):

141

"""

142

Read-only constant objects.

143

144

Properties:

145

- value: Current value (read-only)

146

"""

147

148

class NonConstantObject(NonHierarchyObject):

149

"""

150

Non-constant signal objects with read capability.

151

152

Properties:

153

- value: Current signal value

154

"""

155

156

class ModifiableObject(NonConstantObject):

157

"""

158

Modifiable signal objects with read/write capability.

159

160

Properties:

161

- value: Current signal value (read/write)

162

"""

163

164

class IntegerObject(ModifiableObject):

165

"""

166

Integer signal objects.

167

168

Optimized for integer values with automatic conversion.

169

"""

170

171

class RealObject(ModifiableObject):

172

"""

173

Real number signal objects.

174

175

Supports floating-point signal values.

176

"""

177

178

class EnumObject(ModifiableObject):

179

"""

180

Enumeration signal objects.

181

182

Supports enumerated type values.

183

"""

184

185

class StringObject(ModifiableObject):

186

"""

187

String variable objects.

188

189

Supports string values in HDL variables.

190

"""

191

192

class NonHierarchyIndexableObject(NonConstantObject):

193

"""

194

Indexable arrays and memories.

195

196

Supports:

197

- Indexed access (signal[index])

198

- Slicing operations (signal[start:end])

199

- Bit manipulation

200

"""

201

```

202

203

**Usage Examples:**

204

205

```python

206

@cocotb.test()

207

async def value_objects_test(dut):

208

"""Test demonstrating value object operations."""

209

210

# Read constant values

211

version_reg = dut.version_register # ConstantObject

212

cocotb.log.info(f"Version: {version_reg.value}")

213

214

# Read/write integer signals

215

counter = dut.counter # IntegerObject

216

original_value = counter.value

217

counter.value = 42

218

assert counter.value == 42

219

220

# Real number signals

221

if hasattr(dut, 'voltage_level'):

222

voltage = dut.voltage_level # RealObject

223

voltage.value = 3.3

224

cocotb.log.info(f"Voltage set to: {voltage.value}")

225

226

# Enumeration signals

227

if hasattr(dut, 'state'):

228

state = dut.state # EnumObject

229

state.value = "IDLE"

230

cocotb.log.info(f"State: {state.value}")

231

232

# String variables

233

if hasattr(dut, 'debug_message'):

234

message = dut.debug_message # StringObject

235

message.value = "Test message"

236

237

# Indexable objects (arrays, bit vectors)

238

data_bus = dut.data_bus # NonHierarchyIndexableObject

239

240

# Access individual bits

241

lsb = data_bus[0]

242

msb = data_bus[31]

243

244

# Access bit slices

245

lower_byte = data_bus[7:0]

246

upper_word = data_bus[31:16]

247

248

# Modify bit slices

249

lower_byte.value = 0xFF

250

upper_word.value = 0xABCD

251

252

cocotb.log.info(f"Data bus: {data_bus.value}")

253

```

254

255

### Signal Actions

256

257

Control signal behavior beyond simple value assignment.

258

259

```python { .api }

260

class Deposit(value):

261

"""

262

Deposit value action (simulator-dependent behavior).

263

264

Parameters:

265

- value: Value to deposit

266

267

Usage:

268

signal.value = Deposit(42)

269

"""

270

271

class Force(value):

272

"""

273

Force signal to specific value.

274

275

Overrides normal signal driving until released.

276

277

Parameters:

278

- value: Value to force

279

280

Usage:

281

signal.value = Force(1)

282

"""

283

284

class Freeze():

285

"""

286

Freeze signal at current value.

287

288

Prevents signal changes until released.

289

290

Usage:

291

signal.value = Freeze()

292

"""

293

294

class Release():

295

"""

296

Release forced or frozen signal.

297

298

Returns signal to normal operation.

299

300

Usage:

301

signal.value = Release()

302

"""

303

```

304

305

**Usage Examples:**

306

307

```python

308

@cocotb.test()

309

async def signal_actions_test(dut):

310

"""Test demonstrating signal actions."""

311

312

# Normal assignment

313

dut.control_signal.value = 1

314

await Timer(100, units="ns")

315

316

# Force signal to specific value

317

dut.control_signal.value = Force(0)

318

cocotb.log.info("Control signal forced to 0")

319

320

# Signal will remain 0 even if logic tries to change it

321

await Timer(200, units="ns")

322

assert dut.control_signal.value == 0

323

324

# Freeze at current value

325

dut.status_signal.value = Freeze()

326

original_status = dut.status_signal.value

327

328

await Timer(100, units="ns")

329

assert dut.status_signal.value == original_status

330

331

# Release forced/frozen signals

332

dut.control_signal.value = Release()

333

dut.status_signal.value = Release()

334

335

cocotb.log.info("Signals released to normal operation")

336

337

# Deposit value (simulator-specific behavior)

338

dut.memory_data.value = Deposit(0xDEADBEEF)

339

await Timer(50, units="ns")

340

341

@cocotb.test()

342

async def debug_force_test(dut):

343

"""Test using force for debugging."""

344

345

# Force clock for debugging

346

dut.clk.value = Force(0)

347

await Timer(50, units="ns")

348

349

dut.clk.value = Force(1)

350

await Timer(50, units="ns")

351

352

# Release and return to normal clock

353

dut.clk.value = Release()

354

355

# Continue with normal test

356

await Timer(100, units="ns")

357

```

358

359

### Handle Factory

360

361

Create appropriate handle objects for simulator objects.

362

363

```python { .api }

364

def SimHandle(handle, path=None):

365

"""

366

Factory function to create appropriate handle object.

367

368

Automatically selects correct handle type based on

369

simulator object characteristics.

370

371

Parameters:

372

- handle: Simulator handle object

373

- path: Optional hierarchical path

374

375

Returns:

376

Appropriate SimHandleBase subclass instance

377

"""

378

```

379

380

**Usage Examples:**

381

382

```python

383

@cocotb.test()

384

async def handle_factory_test(dut):

385

"""Test demonstrating handle factory usage."""

386

387

# Factory automatically selects appropriate handle type

388

from cocotb import simulator

389

390

# Get raw simulator handle

391

raw_handle = simulator.get_handle_by_name("dut.cpu.pc")

392

393

# Convert to appropriate cocotb handle

394

pc_handle = SimHandle(raw_handle, "dut.cpu.pc")

395

396

# Use the handle normally

397

cocotb.log.info(f"PC handle type: {type(pc_handle)}")

398

cocotb.log.info(f"PC value: {pc_handle.value}")

399

400

# Factory handles different signal types automatically

401

signals_to_test = [

402

"data_bus", # Likely NonHierarchyIndexableObject

403

"counter", # Likely IntegerObject

404

"enable", # Likely ModifiableObject

405

"cpu", # Likely HierarchyObject

406

"memory" # Likely HierarchyArrayObject

407

]

408

409

for signal_name in signals_to_test:

410

if hasattr(dut, signal_name):

411

signal = getattr(dut, signal_name)

412

cocotb.log.info(f"{signal_name}: {type(signal).__name__}")

413

```

414

415

## Advanced Signal Handling

416

417

### Dynamic Signal Discovery

418

419

```python

420

@cocotb.test()

421

async def discovery_test(dut):

422

"""Test demonstrating dynamic signal discovery."""

423

424

# Discover all signals in a module

425

def discover_signals(module, prefix=""):

426

signals = []

427

428

# Try common signal name patterns

429

test_names = [

430

"clk", "clock", "reset", "rst", "enable", "valid", "ready",

431

"data", "addr", "address", "control", "status"

432

]

433

434

for name in test_names:

435

try:

436

signal = module._id(name, extended=False)

437

signals.append(f"{prefix}.{name}")

438

cocotb.log.info(f"Found signal: {prefix}.{name}")

439

except:

440

pass # Signal doesn't exist

441

442

return signals

443

444

# Discover signals in DUT

445

dut_signals = discover_signals(dut, "dut")

446

447

# Try to discover sub-modules

448

sub_modules = ["cpu", "memory", "controller", "interface"]

449

for sub_name in sub_modules:

450

try:

451

sub_module = dut._id(sub_name, extended=False)

452

sub_signals = discover_signals(sub_module, f"dut.{sub_name}")

453

dut_signals.extend(sub_signals)

454

except:

455

pass # Sub-module doesn't exist

456

457

cocotb.log.info(f"Total signals discovered: {len(dut_signals)}")

458

```

459

460

### Signal Monitoring

461

462

```python

463

class SignalMonitor:

464

"""Generic signal monitor for value tracking."""

465

466

def __init__(self, signal, name=None):

467

self.signal = signal

468

self.name = name or str(signal._name)

469

self.history = []

470

self.monitor_task = None

471

472

async def start_monitoring(self):

473

"""Start monitoring signal changes."""

474

self.monitor_task = cocotb.start_soon(self._monitor_loop())

475

476

def stop_monitoring(self):

477

"""Stop monitoring signal changes."""

478

if self.monitor_task:

479

self.monitor_task.kill()

480

481

async def _monitor_loop(self):

482

"""Internal monitoring loop."""

483

last_value = self.signal.value

484

self.history.append((get_sim_time("ns"), last_value))

485

486

while True:

487

await Edge(self.signal)

488

current_value = self.signal.value

489

current_time = get_sim_time("ns")

490

491

self.history.append((current_time, current_value))

492

cocotb.log.info(f"{self.name}: {last_value} -> {current_value} @ {current_time}ns")

493

494

last_value = current_value

495

496

def get_history(self):

497

"""Get signal change history."""

498

return self.history.copy()

499

500

@cocotb.test()

501

async def monitoring_test(dut):

502

"""Test demonstrating signal monitoring."""

503

504

# Create monitors

505

clk_monitor = SignalMonitor(dut.clk, "Clock")

506

data_monitor = SignalMonitor(dut.data_bus, "Data Bus")

507

508

# Start monitoring

509

await clk_monitor.start_monitoring()

510

await data_monitor.start_monitoring()

511

512

# Generate activity

513

for i in range(10):

514

dut.data_bus.value = i

515

await Timer(50, units="ns")

516

517

# Stop monitoring

518

clk_monitor.stop_monitoring()

519

data_monitor.stop_monitoring()

520

521

# Analyze history

522

data_history = data_monitor.get_history()

523

cocotb.log.info(f"Data bus changed {len(data_history)} times")

524

525

for timestamp, value in data_history[-5:]: # Last 5 changes

526

cocotb.log.info(f" {timestamp}ns: {value}")

527

```

528

529

### Complex Signal Operations

530

531

```python

532

@cocotb.test()

533

async def complex_operations_test(dut):

534

"""Test demonstrating complex signal operations."""

535

536

# Multi-bit manipulation

537

control_reg = dut.control_register # 32-bit register

538

539

# Set individual control bits

540

control_reg[0].value = 1 # Enable bit

541

control_reg[1].value = 0 # Reset bit

542

control_reg[7:4].value = 0xA # Mode bits

543

544

# Read back constructed value

545

final_value = control_reg.value

546

cocotb.log.info(f"Control register: 0x{final_value:08x}")

547

548

# Array operations

549

if hasattr(dut, 'register_file'):

550

reg_file = dut.register_file

551

552

# Initialize register file

553

for i in range(32):

554

try:

555

reg = reg_file[i]

556

reg.value = i * 0x10 # Pattern

557

except:

558

break # Fewer registers than expected

559

560

# Verify pattern

561

for i in range(8): # Check first 8

562

try:

563

reg = reg_file[i]

564

expected = i * 0x10

565

assert reg.value == expected, f"R{i}: expected {expected}, got {reg.value}"

566

except:

567

break

568

569

# Memory-mapped operations

570

if hasattr(dut, 'memory'):

571

memory = dut.memory

572

573

# Write test pattern

574

test_data = [0xDEAD, 0xBEEF, 0xCAFE, 0xBABE]

575

for i, data in enumerate(test_data):

576

try:

577

memory[i].value = data

578

except:

579

pass # Memory might not support direct indexing

580

581

# Read back and verify

582

for i, expected in enumerate(test_data):

583

try:

584

actual = memory[i].value

585

assert actual == expected, f"Memory[{i}] mismatch"

586

except:

587

break

588

```