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:
parent
5a37442706
commit
f9ae6da8bd
@ -25,7 +25,7 @@ THE SOFTWARE.
|
|||||||
// Language: Verilog 2001
|
// Language: Verilog 2001
|
||||||
|
|
||||||
`resetall
|
`resetall
|
||||||
`timescale 1ns / 1ps
|
`timescale 1ns / 1fs
|
||||||
`default_nettype none
|
`default_nettype none
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -24,7 +24,7 @@ SIM ?= icarus
|
|||||||
WAVES ?= 0
|
WAVES ?= 0
|
||||||
|
|
||||||
COCOTB_HDL_TIMEUNIT = 1ns
|
COCOTB_HDL_TIMEUNIT = 1ns
|
||||||
COCOTB_HDL_TIMEPRECISION = 1ps
|
COCOTB_HDL_TIMEPRECISION = 1fs
|
||||||
|
|
||||||
DUT = ptp_clock_cdc
|
DUT = ptp_clock_cdc
|
||||||
TOPLEVEL = $(DUT)
|
TOPLEVEL = $(DUT)
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
#!/usr/bin/env python
|
#!/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
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
@ -25,6 +25,7 @@ THE SOFTWARE.
|
|||||||
|
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
|
from statistics import mean, stdev
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
import cocotb_test.simulator
|
import cocotb_test.simulator
|
||||||
@ -32,7 +33,7 @@ import cocotb_test.simulator
|
|||||||
import cocotb
|
import cocotb
|
||||||
from cocotb.clock import Clock
|
from cocotb.clock import Clock
|
||||||
from cocotb.triggers import RisingEdge, Timer
|
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
|
from cocotbext.eth import PtpClock
|
||||||
|
|
||||||
@ -44,9 +45,7 @@ class TB:
|
|||||||
self.log = logging.getLogger("cocotb.tb")
|
self.log = logging.getLogger("cocotb.tb")
|
||||||
self.log.setLevel(logging.DEBUG)
|
self.log.setLevel(logging.DEBUG)
|
||||||
|
|
||||||
cocotb.start_soon(Clock(dut.input_clk, 6.4, units="ns").start())
|
cocotb.start_soon(Clock(dut.sample_clk, 9.9, units="ns").start())
|
||||||
|
|
||||||
cocotb.start_soon(Clock(dut.sample_clk, 10, units="ns").start())
|
|
||||||
|
|
||||||
if len(dut.input_ts) == 64:
|
if len(dut.input_ts) == 64:
|
||||||
self.ptp_clock = PtpClock(
|
self.ptp_clock = PtpClock(
|
||||||
@ -65,8 +64,13 @@ class TB:
|
|||||||
period_ns=6.4
|
period_ns=6.4
|
||||||
)
|
)
|
||||||
|
|
||||||
self._clock_cr = None
|
self.input_clock_period = 6.4
|
||||||
self.set_output_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):
|
async def reset(self):
|
||||||
self.dut.input_rst.setimmediatevalue(0)
|
self.dut.input_rst.setimmediatevalue(0)
|
||||||
@ -82,17 +86,33 @@ class TB:
|
|||||||
for k in range(10):
|
for k in range(10):
|
||||||
await RisingEdge(self.dut.input_clk)
|
await RisingEdge(self.dut.input_clk)
|
||||||
|
|
||||||
def set_output_clock_period(self, period):
|
def set_input_clock_period(self, period):
|
||||||
if self._clock_cr is not None:
|
self.input_clock_period = period
|
||||||
self._clock_cr.kill()
|
|
||||||
|
|
||||||
self._clock_cr = cocotb.start_soon(self._run_clock(period))
|
async def _run_input_clock(self):
|
||||||
|
period = None
|
||||||
async def _run_clock(self, period):
|
steps_per_ns = get_sim_steps(1.0, 'ns')
|
||||||
half_period = get_sim_steps(period / 2.0, 'ns')
|
|
||||||
t = Timer(half_period)
|
|
||||||
|
|
||||||
while True:
|
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
|
await t
|
||||||
self.dut.output_clk.value = 1
|
self.dut.output_clk.value = 1
|
||||||
await t
|
await t
|
||||||
@ -112,14 +132,45 @@ class TB:
|
|||||||
else:
|
else:
|
||||||
return (ts >> 48) + ((ts & 0xffffffffffff)/2**16*1e-9)
|
return (ts >> 48) + ((ts & 0xffffffffffff)/2**16*1e-9)
|
||||||
|
|
||||||
async def measure_ts_diff(self, N=1000):
|
async def measure_ts_diff(self, N=100):
|
||||||
total = 0
|
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):
|
for k in range(N):
|
||||||
input_ts_ns = self.get_input_ts_ns()
|
await RisingEdge(self.dut.output_clk)
|
||||||
output_ts_ns = self.get_output_ts_ns()
|
|
||||||
total += input_ts_ns-output_ts_ns
|
input_cr.kill()
|
||||||
await Timer(100, 'ps')
|
output_cr.kill()
|
||||||
return total/N
|
|
||||||
|
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()
|
@cocotb.test()
|
||||||
@ -132,90 +183,236 @@ async def run_test(dut):
|
|||||||
await RisingEdge(dut.input_clk)
|
await RisingEdge(dut.input_clk)
|
||||||
tb.log.info("Same clock speed")
|
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)
|
await RisingEdge(dut.input_clk)
|
||||||
|
|
||||||
for i in range(40000):
|
for i in range(100000):
|
||||||
await RisingEdge(dut.input_clk)
|
await RisingEdge(dut.input_clk)
|
||||||
|
|
||||||
assert tb.dut.locked.value.integer
|
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})")
|
||||||
tb.log.info(f"Difference: {diff} ns")
|
assert abs(mean(diffs)*1e9) < 5
|
||||||
|
|
||||||
assert abs(diff) < 10
|
|
||||||
|
|
||||||
await RisingEdge(dut.input_clk)
|
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)
|
await RisingEdge(dut.input_clk)
|
||||||
|
|
||||||
for i in range(40000):
|
for i in range(100000):
|
||||||
await RisingEdge(dut.input_clk)
|
await RisingEdge(dut.input_clk)
|
||||||
|
|
||||||
assert tb.dut.locked.value.integer
|
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})")
|
||||||
tb.log.info(f"Difference: {diff} ns")
|
assert abs(mean(diffs)*1e9) < 5
|
||||||
|
|
||||||
assert abs(diff) < 10
|
|
||||||
|
|
||||||
await RisingEdge(dut.input_clk)
|
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)
|
await RisingEdge(dut.input_clk)
|
||||||
|
|
||||||
for i in range(40000):
|
for i in range(100000):
|
||||||
await RisingEdge(dut.input_clk)
|
await RisingEdge(dut.input_clk)
|
||||||
|
|
||||||
assert tb.dut.locked.value.integer
|
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})")
|
||||||
tb.log.info(f"Difference: {diff} ns")
|
assert abs(mean(diffs)*1e9) < 5
|
||||||
|
|
||||||
assert abs(diff) < 10
|
|
||||||
|
|
||||||
await RisingEdge(dut.input_clk)
|
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)
|
tb.set_output_clock_period(4.0)
|
||||||
|
|
||||||
await RisingEdge(dut.input_clk)
|
await RisingEdge(dut.input_clk)
|
||||||
|
|
||||||
for i in range(40000):
|
for i in range(100000):
|
||||||
await RisingEdge(dut.input_clk)
|
await RisingEdge(dut.input_clk)
|
||||||
|
|
||||||
assert tb.dut.locked.value.integer
|
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})")
|
||||||
tb.log.info(f"Difference: {diff} ns")
|
assert abs(mean(diffs)*1e9) < 5
|
||||||
|
|
||||||
assert abs(diff) < 10
|
|
||||||
|
|
||||||
await RisingEdge(dut.input_clk)
|
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)
|
tb.set_output_clock_period(10.0)
|
||||||
|
|
||||||
await RisingEdge(dut.input_clk)
|
await RisingEdge(dut.input_clk)
|
||||||
|
|
||||||
for i in range(30000):
|
for i in range(100000):
|
||||||
await RisingEdge(dut.input_clk)
|
await RisingEdge(dut.input_clk)
|
||||||
|
|
||||||
assert tb.dut.locked.value.integer
|
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)
|
||||||
await RisingEdge(dut.input_clk)
|
await RisingEdge(dut.input_clk)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user