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

clock-generation.mddocs/

0

# Clock Generation

1

2

Cocotb's clock generation system provides utilities for creating periodic signals with configurable periods, duty cycles, and phase relationships. The clock system integrates with the trigger system to enable precise timing control in testbenches.

3

4

## Capabilities

5

6

### Basic Clock Generation

7

8

Simple clock generation with 50% duty cycle and configurable period.

9

10

```python { .api }

11

class Clock(signal, period, units="step"):

12

"""

13

Simple 50:50 duty cycle clock generator.

14

15

Parameters:

16

- signal: HDL signal to drive as clock

17

- period: Clock period value

18

- units: Time units ("fs", "ps", "ns", "us", "ms", "sec", "step")

19

20

Methods:

21

- start(cycles=None, start_high=True): Start clock generation

22

"""

23

24

async def start(self, cycles=None, start_high=True):

25

"""

26

Start clock generation task.

27

28

Parameters:

29

- cycles: Number of cycles to generate (None for infinite)

30

- start_high: True to start with high level, False for low

31

32

Returns:

33

Task object for the clock generation coroutine

34

"""

35

```

36

37

**Usage Examples:**

38

39

```python

40

@cocotb.test()

41

async def basic_clock_test(dut):

42

"""Test demonstrating basic clock generation."""

43

44

# Create 100MHz clock (10ns period)

45

clock = Clock(dut.clk, 10, units="ns")

46

47

# Start infinite clock

48

clock_task = cocotb.start_soon(clock.start())

49

50

# Run test with clock running

51

await Timer(1000, units="ns") # 100 clock cycles

52

53

# Stop clock

54

clock_task.kill()

55

56

@cocotb.test()

57

async def finite_clock_test(dut):

58

"""Test with finite number of clock cycles."""

59

60

# Create clock

61

clock = Clock(dut.clk, 20, units="ns") # 50MHz

62

63

# Generate exactly 50 cycles

64

await clock.start(cycles=50, start_high=True)

65

66

# Clock automatically stops after 50 cycles

67

cocotb.log.info("50 clock cycles completed")

68

69

@cocotb.test()

70

async def multiple_clocks_test(dut):

71

"""Test with multiple independent clocks."""

72

73

# Create multiple clocks with different frequencies

74

main_clock = Clock(dut.main_clk, 10, units="ns") # 100MHz

75

slow_clock = Clock(dut.slow_clk, 100, units="ns") # 10MHz

76

fast_clock = Clock(dut.fast_clk, 2, units="ns") # 500MHz

77

78

# Start all clocks

79

main_task = cocotb.start_soon(main_clock.start())

80

slow_task = cocotb.start_soon(slow_clock.start())

81

fast_task = cocotb.start_soon(fast_clock.start())

82

83

# Run test

84

await Timer(2000, units="ns")

85

86

# Stop all clocks

87

main_task.kill()

88

slow_task.kill()

89

fast_task.kill()

90

```

91

92

### Advanced Clock Generation

93

94

Base class for custom clock implementations with advanced features.

95

96

```python { .api }

97

class BaseClock:

98

"""

99

Abstract base class for clock generators.

100

101

Provides foundation for custom clock implementations

102

with non-standard duty cycles, jitter, or phase relationships.

103

"""

104

```

105

106

**Usage Examples:**

107

108

```python

109

class CustomClock(BaseClock):

110

"""Custom clock with configurable duty cycle."""

111

112

def __init__(self, signal, period, duty_cycle=0.5, units="step"):

113

self.signal = signal

114

self.period = period

115

self.duty_cycle = duty_cycle

116

self.units = units

117

self.high_time = period * duty_cycle

118

self.low_time = period * (1 - duty_cycle)

119

120

async def start(self, cycles=None, start_high=True):

121

"""Start custom clock generation."""

122

cycle_count = 0

123

current_level = start_high

124

125

while cycles is None or cycle_count < cycles:

126

self.signal.value = 1 if current_level else 0

127

128

if current_level:

129

await Timer(self.high_time, units=self.units)

130

else:

131

await Timer(self.low_time, units=self.units)

132

cycle_count += 1 # Count on falling edge

133

134

current_level = not current_level

135

136

@cocotb.test()

137

async def custom_clock_test(dut):

138

"""Test demonstrating custom clock generation."""

139

140

# Create clock with 25% duty cycle

141

clock_25 = CustomClock(dut.clk_25, 100, duty_cycle=0.25, units="ns")

142

143

# Create clock with 75% duty cycle

144

clock_75 = CustomClock(dut.clk_75, 100, duty_cycle=0.75, units="ns")

145

146

# Start custom clocks

147

task_25 = cocotb.start_soon(clock_25.start(cycles=20))

148

task_75 = cocotb.start_soon(clock_75.start(cycles=20))

149

150

# Wait for completion

151

await task_25.join()

152

await task_75.join()

153

154

cocotb.log.info("Custom clocks completed")

155

```

156

157

## Clock Control Patterns

158

159

### Conditional Clock Generation

160

161

```python

162

class ConditionalClock:

163

"""Clock that can be enabled/disabled during operation."""

164

165

def __init__(self, signal, period, units="step"):

166

self.signal = signal

167

self.period = period

168

self.units = units

169

self.enabled = False

170

171

def enable(self):

172

"""Enable clock generation."""

173

self.enabled = True

174

175

def disable(self):

176

"""Disable clock generation."""

177

self.enabled = False

178

179

async def start(self):

180

"""Start conditional clock generation."""

181

while True:

182

if self.enabled:

183

self.signal.value = 1

184

await Timer(self.period / 2, units=self.units)

185

self.signal.value = 0

186

await Timer(self.period / 2, units=self.units)

187

else:

188

await Timer(self.period / 4, units=self.units) # Check enable more frequently

189

190

@cocotb.test()

191

async def conditional_clock_test(dut):

192

"""Test demonstrating conditional clock control."""

193

194

# Create conditional clock

195

cond_clock = ConditionalClock(dut.gated_clk, 20, units="ns")

196

clock_task = cocotb.start_soon(cond_clock.start())

197

198

# Initially disabled

199

await Timer(100, units="ns")

200

cocotb.log.info("Clock should be stopped")

201

202

# Enable clock

203

cond_clock.enable()

204

await Timer(200, units="ns")

205

cocotb.log.info("Clock should be running")

206

207

# Disable clock

208

cond_clock.disable()

209

await Timer(100, units="ns")

210

cocotb.log.info("Clock should be stopped again")

211

212

# Clean up

213

clock_task.kill()

214

```

215

216

### Phase-Shifted Clocks

217

218

```python

219

class PhaseShiftedClock:

220

"""Clock generator with configurable phase shift."""

221

222

def __init__(self, signal, period, phase_shift=0, units="step"):

223

self.signal = signal

224

self.period = period

225

self.phase_shift = phase_shift

226

self.units = units

227

228

async def start(self, cycles=None):

229

"""Start phase-shifted clock generation."""

230

231

# Apply initial phase shift

232

if self.phase_shift > 0:

233

await Timer(self.phase_shift, units=self.units)

234

235

# Generate clock

236

cycle_count = 0

237

while cycles is None or cycle_count < cycles:

238

self.signal.value = 1

239

await Timer(self.period / 2, units=self.units)

240

241

self.signal.value = 0

242

await Timer(self.period / 2, units=self.units)

243

244

cycle_count += 1

245

246

@cocotb.test()

247

async def phase_shifted_test(dut):

248

"""Test demonstrating phase-shifted clocks."""

249

250

period = 20 # 50MHz base frequency

251

252

# Create phase-shifted clocks

253

clk_0 = PhaseShiftedClock(dut.clk_0, period, phase_shift=0, units="ns")

254

clk_90 = PhaseShiftedClock(dut.clk_90, period, phase_shift=period/4, units="ns")

255

clk_180 = PhaseShiftedClock(dut.clk_180, period, phase_shift=period/2, units="ns")

256

clk_270 = PhaseShiftedClock(dut.clk_270, period, phase_shift=3*period/4, units="ns")

257

258

# Start all clocks

259

tasks = [

260

cocotb.start_soon(clk_0.start(cycles=10)),

261

cocotb.start_soon(clk_90.start(cycles=10)),

262

cocotb.start_soon(clk_180.start(cycles=10)),

263

cocotb.start_soon(clk_270.start(cycles=10))

264

]

265

266

# Wait for all to complete

267

for task in tasks:

268

await task.join()

269

270

cocotb.log.info("Phase-shifted clocks completed")

271

```

272

273

### Clock Domain Crossing

274

275

```python

276

@cocotb.test()

277

async def clock_domain_crossing_test(dut):

278

"""Test demonstrating clock domain crossing scenarios."""

279

280

# Create clocks for different domains

281

fast_clock = Clock(dut.fast_clk, 5, units="ns") # 200MHz

282

slow_clock = Clock(dut.slow_clk, 50, units="ns") # 20MHz

283

284

# Start clocks

285

fast_task = cocotb.start_soon(fast_clock.start())

286

slow_task = cocotb.start_soon(slow_clock.start())

287

288

# Test data transfer from fast to slow domain

289

await transfer_fast_to_slow(dut)

290

291

# Test data transfer from slow to fast domain

292

await transfer_slow_to_fast(dut)

293

294

# Clean up

295

fast_task.kill()

296

slow_task.kill()

297

298

async def transfer_fast_to_slow(dut):

299

"""Transfer data from fast clock domain to slow clock domain."""

300

301

# Synchronize to fast clock and send data

302

await RisingEdge(dut.fast_clk)

303

dut.fast_data.value = 0xABCD

304

dut.fast_valid.value = 1

305

306

await RisingEdge(dut.fast_clk)

307

dut.fast_valid.value = 0

308

309

# Wait for slow domain to capture (may take several slow cycles)

310

timeout_cycles = 10

311

for _ in range(timeout_cycles):

312

await RisingEdge(dut.slow_clk)

313

if dut.slow_data_valid.value:

314

break

315

else:

316

raise AssertionError("Data not transferred to slow domain")

317

318

# Verify data integrity

319

assert dut.slow_data.value == 0xABCD

320

cocotb.log.info("Fast-to-slow transfer successful")

321

322

async def transfer_slow_to_fast(dut):

323

"""Transfer data from slow clock domain to fast clock domain."""

324

325

# Synchronize to slow clock and send data

326

await RisingEdge(dut.slow_clk)

327

dut.slow_data.value = 0x1234

328

dut.slow_valid.value = 1

329

330

await RisingEdge(dut.slow_clk)

331

dut.slow_valid.value = 0

332

333

# Fast domain should capture quickly

334

await RisingEdge(dut.fast_clk)

335

await RisingEdge(dut.fast_clk) # Allow for synchronizer delay

336

337

if dut.fast_data_valid.value:

338

assert dut.fast_data.value == 0x1234

339

cocotb.log.info("Slow-to-fast transfer successful")

340

else:

341

cocotb.log.warning("Slow-to-fast transfer not detected")

342

```

343

344

## Clock Utilities

345

346

### Clock Measurement

347

348

```python

349

class ClockMeasurer:

350

"""Utility for measuring actual clock characteristics."""

351

352

def __init__(self, signal):

353

self.signal = signal

354

self.measurements = []

355

356

async def measure_period(self, num_cycles=10):

357

"""Measure actual clock period over multiple cycles."""

358

359

edge_times = []

360

361

# Collect edge timestamps

362

for _ in range(num_cycles + 1):

363

await RisingEdge(self.signal)

364

edge_times.append(get_sim_time("ps")) # High precision

365

366

# Calculate periods

367

periods = []

368

for i in range(1, len(edge_times)):

369

period = edge_times[i] - edge_times[i-1]

370

periods.append(period)

371

372

# Statistics

373

avg_period = sum(periods) / len(periods)

374

min_period = min(periods)

375

max_period = max(periods)

376

jitter = max_period - min_period

377

378

result = {

379

'average_period_ps': avg_period,

380

'min_period_ps': min_period,

381

'max_period_ps': max_period,

382

'jitter_ps': jitter,

383

'frequency_mhz': 1e6 / avg_period

384

}

385

386

self.measurements.append(result)

387

return result

388

389

@cocotb.test()

390

async def clock_measurement_test(dut):

391

"""Test demonstrating clock measurement."""

392

393

# Start clock under test

394

clock = Clock(dut.clk, 10, units="ns") # Nominal 100MHz

395

clock_task = cocotb.start_soon(clock.start())

396

397

# Measure actual characteristics

398

measurer = ClockMeasurer(dut.clk)

399

result = await measurer.measure_period(num_cycles=20)

400

401

# Report results

402

cocotb.log.info(f"Measured frequency: {result['frequency_mhz']:.2f} MHz")

403

cocotb.log.info(f"Average period: {result['average_period_ps']:.1f} ps")

404

cocotb.log.info(f"Jitter: {result['jitter_ps']:.1f} ps")

405

406

# Verify within tolerance

407

expected_freq = 100.0 # MHz

408

tolerance = 0.1 # MHz

409

410

assert abs(result['frequency_mhz'] - expected_freq) < tolerance, \

411

f"Frequency error too large: {result['frequency_mhz']} MHz"

412

413

# Clean up

414

clock_task.kill()

415

```

416

417

### Clock Synchronization

418

419

```python

420

async def wait_for_clock_edges(clock_signal, num_edges):

421

"""Wait for specific number of clock edges."""

422

423

for _ in range(num_edges):

424

await RisingEdge(clock_signal)

425

426

async def synchronize_to_clock(clock_signal):

427

"""Synchronize current coroutine to clock edge."""

428

429

await RisingEdge(clock_signal)

430

431

@cocotb.test()

432

async def clock_sync_test(dut):

433

"""Test demonstrating clock synchronization utilities."""

434

435

# Start system clock

436

sys_clock = Clock(dut.sys_clk, 8, units="ns") # 125MHz

437

clock_task = cocotb.start_soon(sys_clock.start())

438

439

# Perform clock-synchronized operations

440

await synchronize_to_clock(dut.sys_clk)

441

cocotb.log.info("Synchronized to system clock")

442

443

# Wait for specific number of cycles

444

await wait_for_clock_edges(dut.sys_clk, 16)

445

cocotb.log.info("16 clock cycles elapsed")

446

447

# Use built-in ClockCycles trigger

448

await ClockCycles(dut.sys_clk, 8)

449

cocotb.log.info("8 more clock cycles elapsed")

450

451

# Clean up

452

clock_task.kill()

453

```

454

455

## Best Practices

456

457

### Clock Management in Large Tests

458

459

```python

460

class ClockManager:

461

"""Centralized clock management for complex testbenches."""

462

463

def __init__(self):

464

self.clocks = {}

465

self.tasks = {}

466

467

def add_clock(self, name, signal, period, units="ns"):

468

"""Add a clock to the manager."""

469

clock = Clock(signal, period, units)

470

self.clocks[name] = clock

471

472

async def start_all_clocks(self):

473

"""Start all managed clocks."""

474

for name, clock in self.clocks.items():

475

task = cocotb.start_soon(clock.start())

476

self.tasks[name] = task

477

cocotb.log.info(f"Started clock: {name}")

478

479

def stop_all_clocks(self):

480

"""Stop all managed clocks."""

481

for name, task in self.tasks.items():

482

task.kill()

483

cocotb.log.info(f"Stopped clock: {name}")

484

self.tasks.clear()

485

486

def stop_clock(self, name):

487

"""Stop specific clock."""

488

if name in self.tasks:

489

self.tasks[name].kill()

490

del self.tasks[name]

491

cocotb.log.info(f"Stopped clock: {name}")

492

493

@cocotb.test()

494

async def managed_clocks_test(dut):

495

"""Test demonstrating centralized clock management."""

496

497

# Create clock manager

498

clock_mgr = ClockManager()

499

500

# Add clocks

501

clock_mgr.add_clock("system", dut.sys_clk, 10, "ns") # 100MHz

502

clock_mgr.add_clock("memory", dut.mem_clk, 5, "ns") # 200MHz

503

clock_mgr.add_clock("interface", dut.if_clk, 20, "ns") # 50MHz

504

505

# Start all clocks

506

await clock_mgr.start_all_clocks()

507

508

# Run test with all clocks active

509

await Timer(1000, units="ns")

510

511

# Stop specific clock for power testing

512

clock_mgr.stop_clock("interface")

513

await Timer(500, units="ns")

514

515

# Stop all clocks

516

clock_mgr.stop_all_clocks()

517

```