1
0
mirror of https://github.com/corundum/corundum.git synced 2025-01-16 08:12:53 +08:00

Improve PTP CDC module testbench

Signed-off-by: Alex Forencich <alex@alexforencich.com>
This commit is contained in:
Alex Forencich 2023-09-23 14:33:14 -07:00
parent 5a37442706
commit f9ae6da8bd
3 changed files with 255 additions and 58 deletions

View File

@ -25,7 +25,7 @@ THE SOFTWARE.
// Language: Verilog 2001
`resetall
`timescale 1ns / 1ps
`timescale 1ns / 1fs
`default_nettype none
/*

View File

@ -24,7 +24,7 @@ SIM ?= icarus
WAVES ?= 0
COCOTB_HDL_TIMEUNIT = 1ns
COCOTB_HDL_TIMEPRECISION = 1ps
COCOTB_HDL_TIMEPRECISION = 1fs
DUT = ptp_clock_cdc
TOPLEVEL = $(DUT)

View File

@ -1,7 +1,7 @@
#!/usr/bin/env python
"""
Copyright (c) 2020 Alex Forencich
Copyright (c) 2020-2023 Alex Forencich
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
@ -25,6 +25,7 @@ THE SOFTWARE.
import logging
import os
from statistics import mean, stdev
import pytest
import cocotb_test.simulator
@ -32,7 +33,7 @@ import cocotb_test.simulator
import cocotb
from cocotb.clock import Clock
from cocotb.triggers import RisingEdge, Timer
from cocotb.utils import get_sim_steps
from cocotb.utils import get_sim_steps, get_sim_time
from cocotbext.eth import PtpClock
@ -44,9 +45,7 @@ class TB:
self.log = logging.getLogger("cocotb.tb")
self.log.setLevel(logging.DEBUG)
cocotb.start_soon(Clock(dut.input_clk, 6.4, units="ns").start())
cocotb.start_soon(Clock(dut.sample_clk, 10, units="ns").start())
cocotb.start_soon(Clock(dut.sample_clk, 9.9, units="ns").start())
if len(dut.input_ts) == 64:
self.ptp_clock = PtpClock(
@ -65,8 +64,13 @@ class TB:
period_ns=6.4
)
self._clock_cr = None
self.set_output_clock_period(6.4)
self.input_clock_period = 6.4
dut.input_clk.setimmediatevalue(0)
cocotb.start_soon(self._run_input_clock())
self.output_clock_period = 6.4
dut.output_clk.setimmediatevalue(0)
cocotb.start_soon(self._run_output_clock())
async def reset(self):
self.dut.input_rst.setimmediatevalue(0)
@ -82,17 +86,33 @@ class TB:
for k in range(10):
await RisingEdge(self.dut.input_clk)
def set_output_clock_period(self, period):
if self._clock_cr is not None:
self._clock_cr.kill()
def set_input_clock_period(self, period):
self.input_clock_period = period
self._clock_cr = cocotb.start_soon(self._run_clock(period))
async def _run_clock(self, period):
half_period = get_sim_steps(period / 2.0, 'ns')
t = Timer(half_period)
async def _run_input_clock(self):
period = None
steps_per_ns = get_sim_steps(1.0, 'ns')
while True:
if period != self.input_clock_period:
period = self.input_clock_period
t = Timer(int(steps_per_ns * period / 2.0))
await t
self.dut.input_clk.value = 1
await t
self.dut.input_clk.value = 0
def set_output_clock_period(self, period):
self.output_clock_period = period
async def _run_output_clock(self):
period = None
steps_per_ns = get_sim_steps(1.0, 'ns')
while True:
if period != self.output_clock_period:
period = self.output_clock_period
t = Timer(int(steps_per_ns * period / 2.0))
await t
self.dut.output_clk.value = 1
await t
@ -112,14 +132,45 @@ class TB:
else:
return (ts >> 48) + ((ts & 0xffffffffffff)/2**16*1e-9)
async def measure_ts_diff(self, N=1000):
total = 0
async def measure_ts_diff(self, N=100):
input_ts_lst = []
output_ts_lst = []
async def collect_timestamps(clk, get_ts, lst):
while True:
await RisingEdge(clk)
lst.append((get_sim_time('sec'), get_ts()))
input_cr = cocotb.start_soon(collect_timestamps(self.dut.input_clk, self.get_input_ts_ns, input_ts_lst))
output_cr = cocotb.start_soon(collect_timestamps(self.dut.output_clk, self.get_output_ts_ns, output_ts_lst))
for k in range(N):
input_ts_ns = self.get_input_ts_ns()
output_ts_ns = self.get_output_ts_ns()
total += input_ts_ns-output_ts_ns
await Timer(100, 'ps')
return total/N
await RisingEdge(self.dut.output_clk)
input_cr.kill()
output_cr.kill()
diffs = []
its1 = input_ts_lst.pop(0)
its2 = input_ts_lst.pop(0)
for ots in output_ts_lst:
while its2[0] < ots[0] and input_ts_lst:
its1 = its2
its2 = input_ts_lst.pop(0)
if its2[0] < ots[0]:
break
dt = its2[0] - its1[0]
dts = its2[1] - its1[1]
its = its1[1]+dts/dt*(ots[0]-its1[0])
diffs.append(ots[1] - its)
return diffs
@cocotb.test()
@ -132,90 +183,236 @@ async def run_test(dut):
await RisingEdge(dut.input_clk)
tb.log.info("Same clock speed")
tb.set_input_clock_period(6.4)
tb.set_output_clock_period(6.4)
await RisingEdge(dut.input_clk)
for i in range(40000):
for i in range(100000):
await RisingEdge(dut.input_clk)
assert tb.dut.locked.value.integer
diff = await tb.measure_ts_diff()*1e9
tb.log.info(f"Difference: {diff} ns")
assert abs(diff) < 10
diffs = await tb.measure_ts_diff()
tb.log.info(f"Difference: {mean(diffs)*1e9} ns (stdev: {stdev(diffs)*1e9})")
assert abs(mean(diffs)*1e9) < 5
await RisingEdge(dut.input_clk)
tb.log.info("Slightly faster")
tb.log.info("10 ppm slower")
tb.set_output_clock_period(6.2)
tb.set_input_clock_period(6.4)
tb.set_output_clock_period(6.4*(1+.00001))
await RisingEdge(dut.input_clk)
for i in range(40000):
for i in range(100000):
await RisingEdge(dut.input_clk)
assert tb.dut.locked.value.integer
diff = await tb.measure_ts_diff()*1e9
tb.log.info(f"Difference: {diff} ns")
assert abs(diff) < 10
diffs = await tb.measure_ts_diff()
tb.log.info(f"Difference: {mean(diffs)*1e9} ns (stdev: {stdev(diffs)*1e9})")
assert abs(mean(diffs)*1e9) < 5
await RisingEdge(dut.input_clk)
tb.log.info("Slightly slower")
tb.log.info("10 ppm faster")
tb.set_output_clock_period(6.6)
tb.set_input_clock_period(6.4)
tb.set_output_clock_period(6.4*(1-.00001))
await RisingEdge(dut.input_clk)
for i in range(40000):
for i in range(100000):
await RisingEdge(dut.input_clk)
assert tb.dut.locked.value.integer
diff = await tb.measure_ts_diff()*1e9
tb.log.info(f"Difference: {diff} ns")
assert abs(diff) < 10
diffs = await tb.measure_ts_diff()
tb.log.info(f"Difference: {mean(diffs)*1e9} ns (stdev: {stdev(diffs)*1e9})")
assert abs(mean(diffs)*1e9) < 5
await RisingEdge(dut.input_clk)
tb.log.info("Significantly faster")
tb.log.info("200 ppm slower")
tb.set_input_clock_period(6.4)
tb.set_output_clock_period(6.4*(1+.0002))
await RisingEdge(dut.input_clk)
for i in range(100000):
await RisingEdge(dut.input_clk)
assert tb.dut.locked.value.integer
diffs = await tb.measure_ts_diff()
tb.log.info(f"Difference: {mean(diffs)*1e9} ns (stdev: {stdev(diffs)*1e9})")
assert abs(mean(diffs)*1e9) < 5
await RisingEdge(dut.input_clk)
tb.log.info("200 ppm faster")
tb.set_input_clock_period(6.4)
tb.set_output_clock_period(6.4*(1-.0002))
await RisingEdge(dut.input_clk)
for i in range(100000):
await RisingEdge(dut.input_clk)
assert tb.dut.locked.value.integer
diffs = await tb.measure_ts_diff()
tb.log.info(f"Difference: {mean(diffs)*1e9} ns (stdev: {stdev(diffs)*1e9})")
assert abs(mean(diffs)*1e9) < 5
await RisingEdge(dut.input_clk)
tb.log.info("Coherent tracking (+/- 10 ppm)")
tb.set_input_clock_period(6.4)
tb.set_output_clock_period(6.4)
await RisingEdge(dut.input_clk)
period = 6.400
step = 0.000002
period_min = 6.4*(1-.00001)
period_max = 6.4*(1+.00001)
for i in range(500):
period += step
if period <= period_min:
step = abs(step)
if period >= period_max:
step = -abs(step)
tb.set_output_clock_period(period)
for i in range(200):
await RisingEdge(dut.input_clk)
assert tb.dut.locked.value.integer
diffs = await tb.measure_ts_diff()
tb.log.info(f"Difference: {mean(diffs)*1e9} ns (stdev: {stdev(diffs)*1e9})")
assert abs(mean(diffs)*1e9) < 5
await RisingEdge(dut.input_clk)
tb.log.info("Coherent tracking (+/- 200 ppm)")
tb.set_input_clock_period(6.4)
tb.set_output_clock_period(6.4)
await RisingEdge(dut.input_clk)
period = 6.400
step = 0.000002
period_min = 6.4*(1-.0002)
period_max = 6.4*(1+.0002)
for i in range(5000):
period += step
if period <= period_min:
step = abs(step)
if period >= period_max:
step = -abs(step)
tb.set_output_clock_period(period)
for i in range(20):
await RisingEdge(dut.input_clk)
assert tb.dut.locked.value.integer
diffs = await tb.measure_ts_diff()
tb.log.info(f"Difference: {mean(diffs)*1e9} ns (stdev: {stdev(diffs)*1e9})")
assert abs(mean(diffs)*1e9) < 5
await RisingEdge(dut.input_clk)
tb.log.info("Slightly faster (6.3 ns)")
tb.set_input_clock_period(6.4)
tb.set_output_clock_period(6.3)
await RisingEdge(dut.input_clk)
for i in range(100000):
await RisingEdge(dut.input_clk)
assert tb.dut.locked.value.integer
diffs = await tb.measure_ts_diff()
tb.log.info(f"Difference: {mean(diffs)*1e9} ns (stdev: {stdev(diffs)*1e9})")
assert abs(mean(diffs)*1e9) < 5
await RisingEdge(dut.input_clk)
tb.log.info("Slightly slower (6.5 ns)")
tb.set_input_clock_period(6.4)
tb.set_output_clock_period(6.5)
await RisingEdge(dut.input_clk)
for i in range(100000):
await RisingEdge(dut.input_clk)
assert tb.dut.locked.value.integer
diffs = await tb.measure_ts_diff()
tb.log.info(f"Difference: {mean(diffs)*1e9} ns (stdev: {stdev(diffs)*1e9})")
assert abs(mean(diffs)*1e9) < 5
await RisingEdge(dut.input_clk)
tb.log.info("Significantly faster (250 MHz)")
tb.set_input_clock_period(6.4)
tb.set_output_clock_period(4.0)
await RisingEdge(dut.input_clk)
for i in range(40000):
for i in range(100000):
await RisingEdge(dut.input_clk)
assert tb.dut.locked.value.integer
diff = await tb.measure_ts_diff()*1e9
tb.log.info(f"Difference: {diff} ns")
assert abs(diff) < 10
diffs = await tb.measure_ts_diff()
tb.log.info(f"Difference: {mean(diffs)*1e9} ns (stdev: {stdev(diffs)*1e9})")
assert abs(mean(diffs)*1e9) < 5
await RisingEdge(dut.input_clk)
tb.log.info("Significantly slower")
tb.log.info("Significantly slower (100 MHz)")
tb.set_input_clock_period(6.4)
tb.set_output_clock_period(10.0)
await RisingEdge(dut.input_clk)
for i in range(30000):
for i in range(100000):
await RisingEdge(dut.input_clk)
assert tb.dut.locked.value.integer
diff = await tb.measure_ts_diff()*1e9
diffs = await tb.measure_ts_diff()
tb.log.info(f"Difference: {mean(diffs)*1e9} ns (stdev: {stdev(diffs)*1e9})")
assert abs(mean(diffs)*1e9) < 5
tb.log.info(f"Difference: {diff} ns")
await RisingEdge(dut.input_clk)
tb.log.info("Significantly faster (390.625 MHz)")
assert abs(diff) < 10
tb.set_input_clock_period(6.4)
tb.set_output_clock_period(2.56)
await RisingEdge(dut.input_clk)
for i in range(100000):
await RisingEdge(dut.input_clk)
assert tb.dut.locked.value.integer
diffs = await tb.measure_ts_diff()
tb.log.info(f"Difference: {mean(diffs)*1e9} ns (stdev: {stdev(diffs)*1e9})")
assert abs(mean(diffs)*1e9) < 5
await RisingEdge(dut.input_clk)
await RisingEdge(dut.input_clk)