Compare commits
26 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 1636003e2c | |||
| bebafc3f73 | |||
|
|
c6872e6951 | ||
|
|
e9232d6bc9 | ||
|
|
71be7f50d5 | ||
|
|
7077200912 | ||
|
|
2e356c3fc5 | ||
|
|
c25e2e6b23 | ||
|
|
bdebdd4897 | ||
|
|
3f7a0ad919 | ||
|
|
b0c3671ca0 | ||
|
|
73df05c706 | ||
|
|
bc8dbb039c | ||
|
|
afb7d2bd10 | ||
|
|
3b75a9a1a7 | ||
|
|
7d8d214b57 | ||
|
|
c44f928bea | ||
|
|
357dd26aae | ||
|
|
6131079494 | ||
|
|
9079ca34f2 | ||
|
|
08fd179dac | ||
|
|
1fa122e4ee | ||
|
|
4b13e61dc8 | ||
|
|
72f7290488 | ||
|
|
6d1afe0904 | ||
|
|
d61362e79b |
63
.github/workflows/build.yaml
vendored
Normal file
63
.github/workflows/build.yaml
vendored
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
name: build
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
- 'dev/**'
|
||||||
|
pull_request:
|
||||||
|
branches: [ master ]
|
||||||
|
release:
|
||||||
|
types:
|
||||||
|
- published
|
||||||
|
|
||||||
|
# Allows you to run this workflow manually from the Actions tab
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
name: Build distributions
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- uses: actions/setup-python@v4
|
||||||
|
name: Install Python
|
||||||
|
with:
|
||||||
|
python-version: "3.10"
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: |
|
||||||
|
python -m pip install build
|
||||||
|
|
||||||
|
- name: Build sdist
|
||||||
|
run: python -m build
|
||||||
|
|
||||||
|
- uses: actions/upload-artifact@v3
|
||||||
|
with:
|
||||||
|
name: dist
|
||||||
|
path: |
|
||||||
|
dist/*.tar.gz
|
||||||
|
dist/*.whl
|
||||||
|
|
||||||
|
#-------------------------------------------------------------------------------
|
||||||
|
deploy:
|
||||||
|
needs:
|
||||||
|
- build
|
||||||
|
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
environment: release
|
||||||
|
permissions:
|
||||||
|
id-token: write
|
||||||
|
|
||||||
|
# Only publish when a Gitea Release is created.
|
||||||
|
if: gitea.event_name == 'release'
|
||||||
|
steps:
|
||||||
|
- uses: actions/download-artifact@v3
|
||||||
|
with:
|
||||||
|
name: dist
|
||||||
|
path: dist
|
||||||
|
|
||||||
|
- run: python3 -m pip install twine --user --break-system-packages
|
||||||
|
- run: python3 -m pip install -U packaging --user --break-system-packages
|
||||||
|
- run: TWINE_PASSWORD=${{ secrets.PYPI_PAT }} TWINE_USERNAME=bslathi19 python -m twine upload --repository-url ${{ vars.CI_API_URL }} dist/*
|
||||||
4
.github/workflows/regression-tests.yml
vendored
4
.github/workflows/regression-tests.yml
vendored
@@ -5,11 +5,11 @@ on: [push, pull_request]
|
|||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
name: Python ${{matrix.python-version}}
|
name: Python ${{matrix.python-version}}
|
||||||
runs-on: ubuntu-20.04
|
runs-on: ubuntu-24.04
|
||||||
|
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
python-version: ["3.7", "3.8", "3.9", "3.10"]
|
python-version: ["3.8", "3.9", "3.10", "3.11", "3.12", "3.13"]
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
|
|||||||
2
LICENSE
2
LICENSE
@@ -1,4 +1,4 @@
|
|||||||
Copyright (c) 2020 Alex Forencich
|
Copyright (c) 2020-2025 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
|
||||||
|
|||||||
112
README.md
112
README.md
@@ -1,6 +1,6 @@
|
|||||||
# Ethernet interface modules for Cocotb
|
# Ethernet interface modules for Cocotb
|
||||||
|
|
||||||
[](https://github.com/alexforencich/cocotbext-eth/actions/)
|
[](https://github.com/alexforencich/cocotbext-eth/actions/workflows/regression-tests.yml)
|
||||||
[](https://codecov.io/gh/alexforencich/cocotbext-eth)
|
[](https://codecov.io/gh/alexforencich/cocotbext-eth)
|
||||||
[](https://pypi.org/project/cocotbext-eth)
|
[](https://pypi.org/project/cocotbext-eth)
|
||||||
[](https://pepy.tech/project/cocotbext-eth)
|
[](https://pepy.tech/project/cocotbext-eth)
|
||||||
@@ -30,7 +30,7 @@ Installation for active development:
|
|||||||
|
|
||||||
## Documentation and usage examples
|
## Documentation and usage examples
|
||||||
|
|
||||||
See the `tests` directory, [verilog-ethernet](https://github.com/alexforencich/verilog-ethernet), and [corundum](https://github.com/corundum/corundum) for complete testbenches using these modules.
|
See the `tests` directory, [taxi](https://github.com/fpganinja/taxi), [verilog-ethernet](https://github.com/alexforencich/verilog-ethernet), and [corundum](https://github.com/corundum/corundum) for complete testbenches using these modules.
|
||||||
|
|
||||||
### GMII
|
### GMII
|
||||||
|
|
||||||
@@ -585,15 +585,15 @@ Methods:
|
|||||||
|
|
||||||
### PTP clock
|
### PTP clock
|
||||||
|
|
||||||
The `PtpClock` class implements a PTP hardware clock that produces IEEE 1588 format 96 and 64 bit PTP timestamps.
|
The `PtpClock` class implements a PTP hardware clock that produces IEEE 1588 format 96-bit time-of-day and 64-bit relative PTP timestamps.
|
||||||
|
|
||||||
To use this module, import it and connect it to the DUT:
|
To use this module, import it and connect it to the DUT:
|
||||||
|
|
||||||
from cocotbext.eth import PtpClock
|
from cocotbext.eth import PtpClock
|
||||||
|
|
||||||
ptp_clock = PtpClock(
|
ptp_clock = PtpClock(
|
||||||
ts_96=dut.ts_96,
|
ts_tod=dut.ts_tod,
|
||||||
ts_64=dut.ts_64,
|
ts_rel=dut.ts_rel,
|
||||||
ts_step=dut.ts_step,
|
ts_step=dut.ts_step,
|
||||||
pps=dut.pps,
|
pps=dut.pps,
|
||||||
clock=dut.clk,
|
clock=dut.clk,
|
||||||
@@ -603,17 +603,21 @@ To use this module, import it and connect it to the DUT:
|
|||||||
|
|
||||||
Once the clock is instantiated, it will generate a continuous stream of monotonically increasing PTP timestamps on every clock edge.
|
Once the clock is instantiated, it will generate a continuous stream of monotonically increasing PTP timestamps on every clock edge.
|
||||||
|
|
||||||
|
Internally, the `PtpClock` module uses 32-bit fractional ns fields for higher frequency resolution. Only the upper 16 bits are returned in the timestamps, but the full fns value can be accessed with the _ts_tod_fns_ and _ts_rel_fns_ attributes.
|
||||||
|
|
||||||
|
All APIs that handle fractional values use the `Decimal` type for maximum precision, as the combination of timestamp range and resolution is usually too much for normal floating point numbers to handle without significant loss of precision.
|
||||||
|
|
||||||
#### Signals
|
#### Signals
|
||||||
|
|
||||||
* `ts_96`: 96-bit timestamp (48 bit seconds, 32 bit ns, 16 bit fractional ns)
|
* `ts_tod`: 96-bit time-of-day timestamp (48 bit seconds, 32 bit ns, 16 bit fractional ns)
|
||||||
* `ts_64`: 64-bit timestamp (48 bit ns, 16 bit fractional ns)
|
* `ts_rel`: 64-bit relative timestamp (48 bit ns, 16 bit fractional ns)
|
||||||
* `ts_step`: step output, pulsed when non-monotonic step occurs
|
* `ts_step`: step output, pulsed when non-monotonic step occurs
|
||||||
* `pps`: pulse-per-second output, pulsed when ts_96 seconds field increments
|
* `pps`: pulse-per-second output, pulsed when ts_tod seconds field increments
|
||||||
|
|
||||||
#### Constructor parameters:
|
#### Constructor parameters:
|
||||||
|
|
||||||
* _ts_96_: 96-bit timestamp signal (optional)
|
* _ts_tod_: 96-bit time-of-day timestamp signal (optional)
|
||||||
* _ts_64_: 64-bit timestamp signal (optional)
|
* _ts_rel_: 64-bit relative timestamp signal (optional)
|
||||||
* _ts_step_: timestamp step signal (optional)
|
* _ts_step_: timestamp step signal (optional)
|
||||||
* _pps_: pulse-per-second signal (optional)
|
* _pps_: pulse-per-second signal (optional)
|
||||||
* _clock_: clock
|
* _clock_: clock
|
||||||
@@ -623,74 +627,84 @@ Once the clock is instantiated, it will generate a continuous stream of monotoni
|
|||||||
|
|
||||||
#### Attributes:
|
#### Attributes:
|
||||||
|
|
||||||
* _ts_96_s_: current 96-bit timestamp seconds field
|
* _ts_tod_s_: current 96-bit ToD timestamp seconds field
|
||||||
* _ts_96_ns_: current 96-bit timestamp ns field
|
* _ts_tod_ns_: current 96-bit ToD timestamp ns field
|
||||||
* _ts_96_fns_: current 96-bit timestamp fractional ns field
|
* _ts_tod_fns_: current 96-bit ToD timestamp fractional ns field
|
||||||
* _ts_64_ns_: current 64-bit timestamp ns field
|
* _ts_rel_ns_: current 64-bit relative timestamp ns field
|
||||||
* _ts_64_fns_: current 64-bit timestamp fractional ns field
|
* _ts_rel_fns_: current 64-bit relative timestamp fractional ns field
|
||||||
|
|
||||||
#### Methods
|
#### Methods
|
||||||
|
|
||||||
* `set_period(ns, fns)`: set clock period from separate fields
|
* `set_period(ns, fns)`: set clock period from separate fields
|
||||||
* `set_drift(ns, fns, rate)`: set clock drift from separate fields
|
* `set_drift(num, denom)`: set clock drift from separate fields
|
||||||
* `set_period_ns(t)`: set clock period in ns (float)
|
* `set_period_ns(t)`: set clock period and drift in ns (Decimal)
|
||||||
* `get_period_ns()`: return current clock period in ns (float)
|
* `get_period_ns()`: return current clock period in ns (Decimal)
|
||||||
* `set_ts_96(ts_s, ts_ns=None, ts_fns=None)`: set 96-bit timestamp from integer or from separate fields
|
* `set_ts_tod(ts_s, ts_ns, ts_fns)`: set 96-bit ToD timestamp from separate fields
|
||||||
* `set_ts_96_ns(t)`: set 96-bit timestamp from ns (float)
|
* `set_ts_tod_96(ts)`: set 96-bit ToD timestamp from integer
|
||||||
* `set_ts_96_s(t)`: set 96-bit timestamp from seconds (float)
|
* `set_ts_tod_ns(t)`: set 96-bit ToD timestamp from ns (Decimal)
|
||||||
* `get_ts_96()`: return current 96-bit timestamp as an integer
|
* `set_ts_tod_s(t)`: set 96-bit ToD timestamp from seconds (Decimal)
|
||||||
* `get_ts_96_ns()`: return current 96-bit timestamp in ns (float)
|
* `set_ts_tod_sim_time()`: set 96-bit ToD timestamp from sim time
|
||||||
* `get_ts_96_s()`: return current 96-bit timestamp in seconds (float)
|
* `get_ts_tod()`: return current 96-bit ToD timestamp as separate fields
|
||||||
* `set_ts_64(ts_ns, ts_fns=None)`: set 64-bit timestamp from integer or from separate fields
|
* `get_ts_tod_96()`: return current 96-bit ToD timestamp as an integer
|
||||||
* `set_ts_64_ns(t)`: set 64-bit timestamp from ns (float)
|
* `get_ts_tod_ns()`: return current 96-bit ToD timestamp in ns (Decimal)
|
||||||
* `set_ts_64_s(t)`: set 64-bit timestamp from seconds (float)
|
* `get_ts_tod_s()`: return current 96-bit ToD timestamp in seconds (Decimal)
|
||||||
* `get_ts_64()`: return current 64-bit timestamp as an integer
|
* `set_ts_rel(ts_ns, ts_fns)`: set 64-bit relative timestamp from separate fields
|
||||||
* `get_ts_64_ns()`: return current 64-bit timestamp in ns (float)
|
* `set_ts_rel_64(ts)`: set 64-bit relative timestamp from integer
|
||||||
* `get_ts_64_s()`: return current 64-bit timestamp in seconds (float)
|
* `set_ts_rel_ns(t)`: set 64-bit relative timestamp from ns (Decimal)
|
||||||
|
* `set_ts_rel_s(t)`: set 64-bit relative timestamp from seconds (Decimal)
|
||||||
|
* `set_ts_rel_sim_time()`: set 64-bit relative timestamp from sim time
|
||||||
|
* `get_ts_rel()`: return current 64-bit relative timestamp as separate fields
|
||||||
|
* `get_ts_rel_64()`: return current 64-bit relative timestamp as an integer
|
||||||
|
* `get_ts_rel_ns()`: return current 64-bit relative timestamp in ns (Decimal)
|
||||||
|
* `get_ts_rel_s()`: return current 64-bit relative timestamp in seconds (Decimal)
|
||||||
|
|
||||||
### PTP clock (sim time)
|
### PTP clock (sim time)
|
||||||
|
|
||||||
The `PtpClockSimTime` class implements a PTP hardware clock that produces IEEE 1588 format 96 and 64 bit PTP timestamps, derived from the current simulation time. This module can be used in place of `PtpClock` so that captured PTP timestamps can be easily compared to captured simulation time.
|
The `PtpClockSimTime` class implements a PTP hardware clock that produces IEEE 1588 format 96-bit time-of-day and 64-bit relative PTP timestamps, derived from the current simulation time. This module can be used in place of `PtpClock` so that captured PTP timestamps can be easily compared to captured simulation time.
|
||||||
|
|
||||||
To use this module, import it and connect it to the DUT:
|
To use this module, import it and connect it to the DUT:
|
||||||
|
|
||||||
from cocotbext.eth import PtpClockSimTime
|
from cocotbext.eth import PtpClockSimTime
|
||||||
|
|
||||||
ptp_clock = PtpClockSimTime(
|
ptp_clock = PtpClockSimTime(
|
||||||
ts_96=dut.ts_96,
|
ts_tod=dut.ts_tod,
|
||||||
ts_64=dut.ts_64,
|
ts_rel=dut.ts_rel,
|
||||||
pps=dut.pps,
|
pps=dut.pps,
|
||||||
clock=dut.clk
|
clock=dut.clk
|
||||||
)
|
)
|
||||||
|
|
||||||
Once the clock is instantiated, it will generate a continuous stream of monotonically increasing PTP timestamps on every clock edge.
|
Once the clock is instantiated, it will generate a continuous stream of monotonically increasing PTP timestamps on every clock edge.
|
||||||
|
|
||||||
|
All APIs that handle fractional values use the `Decimal` type for maximum precision, as the combination of timestamp range and resolution is usually too much for normal floating point numbers to handle without significant loss of precision.
|
||||||
|
|
||||||
#### Signals
|
#### Signals
|
||||||
|
|
||||||
* `ts_96`: 96-bit timestamp (48 bit seconds, 32 bit ns, 16 bit fractional ns)
|
* `ts_tod`: 96-bit time-of-day timestamp (48 bit seconds, 32 bit ns, 16 bit fractional ns)
|
||||||
* `ts_64`: 64-bit timestamp (48 bit ns, 16 bit fractional ns)
|
* `ts_rel`: 64-bit relative timestamp (48 bit ns, 16 bit fractional ns)
|
||||||
* `pps`: pulse-per-second output, pulsed when ts_96 seconds field increments
|
* `pps`: pulse-per-second output, pulsed when ts_tod seconds field increments
|
||||||
|
|
||||||
#### Constructor parameters:
|
#### Constructor parameters:
|
||||||
|
|
||||||
* _ts_96_: 96-bit timestamp signal (optional)
|
* _ts_tod_: 96-bit time-of-day timestamp signal (optional)
|
||||||
* _ts_64_: 64-bit timestamp signal (optional)
|
* _ts_rel_: 64-bit relative timestamp signal (optional)
|
||||||
* _pps_: pulse-per-second signal (optional)
|
* _pps_: pulse-per-second signal (optional)
|
||||||
* _clock_: clock
|
* _clock_: clock
|
||||||
|
|
||||||
#### Attributes:
|
#### Attributes:
|
||||||
|
|
||||||
* _ts_96_s_: current 96-bit timestamp seconds field
|
* _ts_tod_s_: current 96-bit ToD timestamp seconds field
|
||||||
* _ts_96_ns_: current 96-bit timestamp ns field
|
* _ts_tod_ns_: current 96-bit ToD timestamp ns field
|
||||||
* _ts_96_fns_: current 96-bit timestamp fractional ns field
|
* _ts_tod_fns_: current 96-bit ToD timestamp fractional ns field
|
||||||
* _ts_64_ns_: current 64-bit timestamp ns field
|
* _ts_rel_ns_: current 64-bit relative timestamp ns field
|
||||||
* _ts_64_fns_: current 64-bit timestamp fractional ns field
|
* _ts_rel_fns_: current 64-bit relative timestamp fractional ns field
|
||||||
|
|
||||||
#### Methods
|
#### Methods
|
||||||
|
|
||||||
* `get_ts_96()`: return current 96-bit timestamp as an integer
|
* `get_ts_tod()`: return current 96-bit ToD timestamp as separate fields
|
||||||
* `get_ts_96_ns()`: return current 96-bit timestamp in ns (float)
|
* `get_ts_tod_96()`: return current 96-bit ToD timestamp as an integer
|
||||||
* `get_ts_96_s()`: return current 96-bit timestamp in seconds (float)
|
* `get_ts_tod_ns()`: return current 96-bit ToD timestamp in ns (Decimal)
|
||||||
* `get_ts_64()`: return current 64-bit timestamp as an integer
|
* `get_ts_tod_s()`: return current 96-bit ToD timestamp in seconds (Decimal)
|
||||||
* `get_ts_64_ns()`: return current 64-bit timestamp in ns (float)
|
* `get_ts_rel()`: return current 64-bit relative timestamp as separate fields
|
||||||
* `get_ts_64_s()`: return current 64-bit timestamp in seconds (float)
|
* `get_ts_rel_96()`: return current 64-bit relative timestamp as an integer
|
||||||
|
* `get_ts_rel_ns()`: return current 64-bit relative timestamp in ns (Decimal)
|
||||||
|
* `get_ts_rel_s()`: return current 64-bit relative timestamp in seconds (Decimal)
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
Copyright (c) 2020 Alex Forencich
|
Copyright (c) 2020-2025 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
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
Copyright (c) 2020 Alex Forencich
|
Copyright (c) 2020-2025 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
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
Copyright (c) 2021 Alex Forencich
|
Copyright (c) 2021-2025 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
|
||||||
@@ -43,8 +43,8 @@ AxiStreamBus, AxiStreamTransaction, AxiStreamSource, AxiStreamSink, AxiStreamMon
|
|||||||
|
|
||||||
|
|
||||||
class EthMacFrame:
|
class EthMacFrame:
|
||||||
def __init__(self, data=None, tx_complete=None):
|
def __init__(self, data=b'', tx_complete=None):
|
||||||
self.data = bytearray()
|
self.data = b''
|
||||||
self.sim_time_start = None
|
self.sim_time_start = None
|
||||||
self.sim_time_sfd = None
|
self.sim_time_sfd = None
|
||||||
self.sim_time_end = None
|
self.sim_time_end = None
|
||||||
@@ -53,7 +53,7 @@ class EthMacFrame:
|
|||||||
self.tx_complete = None
|
self.tx_complete = None
|
||||||
|
|
||||||
if type(data) is EthMacFrame:
|
if type(data) is EthMacFrame:
|
||||||
self.data = bytearray(data.data)
|
self.data = bytes(data.data)
|
||||||
self.sim_time_start = data.sim_time_start
|
self.sim_time_start = data.sim_time_start
|
||||||
self.sim_time_sfd = data.sim_time_sfd
|
self.sim_time_sfd = data.sim_time_sfd
|
||||||
self.sim_time_end = data.sim_time_end
|
self.sim_time_end = data.sim_time_end
|
||||||
@@ -61,7 +61,7 @@ class EthMacFrame:
|
|||||||
self.ptp_tag = data.ptp_tag
|
self.ptp_tag = data.ptp_tag
|
||||||
self.tx_complete = data.tx_complete
|
self.tx_complete = data.tx_complete
|
||||||
else:
|
else:
|
||||||
self.data = bytearray(data)
|
self.data = bytes(data)
|
||||||
|
|
||||||
if tx_complete is not None:
|
if tx_complete is not None:
|
||||||
self.tx_complete = tx_complete
|
self.tx_complete = tx_complete
|
||||||
@@ -133,11 +133,14 @@ class EthMacTx(Reset):
|
|||||||
self.ptp_ts_valid = ptp_ts_valid
|
self.ptp_ts_valid = ptp_ts_valid
|
||||||
self.ifg = ifg
|
self.ifg = ifg
|
||||||
self.speed = speed
|
self.speed = speed
|
||||||
|
if bus._name:
|
||||||
self.log = logging.getLogger(f"cocotb.{bus._entity._name}.{bus._name}")
|
self.log = logging.getLogger(f"cocotb.{bus._entity._name}.{bus._name}")
|
||||||
|
else:
|
||||||
|
self.log = logging.getLogger(f"cocotb.{bus._entity._name}")
|
||||||
|
|
||||||
self.log.info("Ethernet MAC TX model")
|
self.log.info("Ethernet MAC TX model")
|
||||||
self.log.info("cocotbext-eth version %s", __version__)
|
self.log.info("cocotbext-eth version %s", __version__)
|
||||||
self.log.info("Copyright (c) 2020 Alex Forencich")
|
self.log.info("Copyright (c) 2021-2025 Alex Forencich")
|
||||||
self.log.info("https://github.com/alexforencich/cocotbext-eth")
|
self.log.info("https://github.com/alexforencich/cocotbext-eth")
|
||||||
|
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
@@ -176,7 +179,7 @@ class EthMacTx(Reset):
|
|||||||
self.log.info(" tuser width: %d bits", len(self.bus.tuser))
|
self.log.info(" tuser width: %d bits", len(self.bus.tuser))
|
||||||
else:
|
else:
|
||||||
self.log.info(" tuser: not present")
|
self.log.info(" tuser: not present")
|
||||||
if self.ptp_time:
|
if self.ptp_time is not None:
|
||||||
self.log.info(" ptp_time width: %d bits", len(self.ptp_time))
|
self.log.info(" ptp_time width: %d bits", len(self.ptp_time))
|
||||||
else:
|
else:
|
||||||
self.log.info(" ptp_time: not present")
|
self.log.info(" ptp_time: not present")
|
||||||
@@ -191,11 +194,11 @@ class EthMacTx(Reset):
|
|||||||
raise ValueError(f"Bus does not evenly divide into byte lanes "
|
raise ValueError(f"Bus does not evenly divide into byte lanes "
|
||||||
f"({self.byte_lanes} * {self.byte_size} != {self.width})")
|
f"({self.byte_lanes} * {self.byte_size} != {self.width})")
|
||||||
|
|
||||||
if self.ptp_ts:
|
if self.ptp_ts is not None:
|
||||||
self.ptp_ts.setimmediatevalue(0)
|
self.ptp_ts.setimmediatevalue(0)
|
||||||
if self.ptp_ts_tag:
|
if self.ptp_ts_tag is not None:
|
||||||
self.ptp_ts_tag.setimmediatevalue(0)
|
self.ptp_ts_tag.setimmediatevalue(0)
|
||||||
if self.ptp_ts_valid:
|
if self.ptp_ts_valid is not None:
|
||||||
self.ptp_ts_valid.setimmediatevalue(0)
|
self.ptp_ts_valid.setimmediatevalue(0)
|
||||||
|
|
||||||
self._run_cr = None
|
self._run_cr = None
|
||||||
@@ -246,13 +249,13 @@ class EthMacTx(Reset):
|
|||||||
if state:
|
if state:
|
||||||
self.log.info("Reset asserted")
|
self.log.info("Reset asserted")
|
||||||
if self._run_cr is not None:
|
if self._run_cr is not None:
|
||||||
self._run_cr.kill()
|
self._run_cr.cancel()
|
||||||
self._run_cr = None
|
self._run_cr = None
|
||||||
if self._run_ts_cr is not None:
|
if self._run_ts_cr is not None:
|
||||||
self._run_ts_cr.kill()
|
self._run_ts_cr.cancel()
|
||||||
self._run_ts_cr = None
|
self._run_ts_cr = None
|
||||||
|
|
||||||
if self.ptp_ts_valid:
|
if self.ptp_ts_valid is not None:
|
||||||
self.ptp_ts_valid.value = 0
|
self.ptp_ts_valid.value = 0
|
||||||
|
|
||||||
self.active = False
|
self.active = False
|
||||||
@@ -263,7 +266,7 @@ class EthMacTx(Reset):
|
|||||||
self.log.info("Reset de-asserted")
|
self.log.info("Reset de-asserted")
|
||||||
if self._run_cr is None:
|
if self._run_cr is None:
|
||||||
self._run_cr = cocotb.start_soon(self._run())
|
self._run_cr = cocotb.start_soon(self._run())
|
||||||
if self._run_ts_cr is None and self.ptp_ts:
|
if self._run_ts_cr is None and self.ptp_ts is not None:
|
||||||
self._run_ts_cr = cocotb.start_soon(self._run_ts())
|
self._run_ts_cr = cocotb.start_soon(self._run_ts())
|
||||||
|
|
||||||
async def _run(self):
|
async def _run(self):
|
||||||
@@ -274,7 +277,9 @@ class EthMacTx(Reset):
|
|||||||
# wait for data
|
# wait for data
|
||||||
cycle = await self.stream.recv()
|
cycle = await self.stream.recv()
|
||||||
|
|
||||||
frame = EthMacFrame(bytearray())
|
frame = EthMacFrame()
|
||||||
|
data = bytearray()
|
||||||
|
|
||||||
frame.sim_time_start = get_sim_time()
|
frame.sim_time_start = get_sim_time()
|
||||||
|
|
||||||
# wait for preamble time
|
# wait for preamble time
|
||||||
@@ -282,9 +287,9 @@ class EthMacTx(Reset):
|
|||||||
|
|
||||||
frame.sim_time_sfd = get_sim_time()
|
frame.sim_time_sfd = get_sim_time()
|
||||||
|
|
||||||
if self.ptp_time:
|
if self.ptp_time is not None:
|
||||||
frame.ptp_timestamp = self.ptp_time.value.integer
|
frame.ptp_timestamp = int(self.ptp_time.value)
|
||||||
frame.ptp_tag = cycle.tuser.integer >> 1
|
frame.ptp_tag = int(cycle.tuser) >> 1
|
||||||
self.ts_queue.put_nowait((frame.ptp_timestamp, frame.ptp_tag))
|
self.ts_queue.put_nowait((frame.ptp_timestamp, frame.ptp_tag))
|
||||||
|
|
||||||
# process frame data
|
# process frame data
|
||||||
@@ -292,14 +297,15 @@ class EthMacTx(Reset):
|
|||||||
byte_count = 0
|
byte_count = 0
|
||||||
|
|
||||||
for offset in range(self.byte_lanes):
|
for offset in range(self.byte_lanes):
|
||||||
if not hasattr(self.bus, "tkeep") or (cycle.tkeep.integer >> offset) & 1:
|
if not hasattr(self.bus, "tkeep") or (int(cycle.tkeep) >> offset) & 1:
|
||||||
frame.data.append((cycle.tdata.integer >> (offset * self.byte_size)) & self.byte_mask)
|
data.append((int(cycle.tdata) >> (offset * self.byte_size)) & self.byte_mask)
|
||||||
byte_count += 1
|
byte_count += 1
|
||||||
|
|
||||||
# wait for serialization time
|
# wait for serialization time
|
||||||
await Timer(self.time_scale*byte_count*8//self.speed, 'step')
|
await Timer(self.time_scale*byte_count*8//self.speed, 'step')
|
||||||
|
|
||||||
if cycle.tlast.integer:
|
if int(cycle.tlast):
|
||||||
|
frame.data = bytes(data)
|
||||||
frame.sim_time_end = get_sim_time()
|
frame.sim_time_end = get_sim_time()
|
||||||
self.log.info("RX frame: %s", frame)
|
self.log.info("RX frame: %s", frame)
|
||||||
|
|
||||||
@@ -346,11 +352,14 @@ class EthMacRx(Reset):
|
|||||||
self.ptp_time = ptp_time
|
self.ptp_time = ptp_time
|
||||||
self.ifg = ifg
|
self.ifg = ifg
|
||||||
self.speed = speed
|
self.speed = speed
|
||||||
|
if bus._name:
|
||||||
self.log = logging.getLogger(f"cocotb.{bus._entity._name}.{bus._name}")
|
self.log = logging.getLogger(f"cocotb.{bus._entity._name}.{bus._name}")
|
||||||
|
else:
|
||||||
|
self.log = logging.getLogger(f"cocotb.{bus._entity._name}")
|
||||||
|
|
||||||
self.log.info("Ethernet MAC RX model")
|
self.log.info("Ethernet MAC RX model")
|
||||||
self.log.info("cocotbext-eth version %s", __version__)
|
self.log.info("cocotbext-eth version %s", __version__)
|
||||||
self.log.info("Copyright (c) 2020 Alex Forencich")
|
self.log.info("Copyright (c) 2021-2025 Alex Forencich")
|
||||||
self.log.info("https://github.com/alexforencich/cocotbext-eth")
|
self.log.info("https://github.com/alexforencich/cocotbext-eth")
|
||||||
|
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
@@ -393,7 +402,7 @@ class EthMacRx(Reset):
|
|||||||
self.log.info(" tuser width: %d bits", len(self.bus.tuser))
|
self.log.info(" tuser width: %d bits", len(self.bus.tuser))
|
||||||
else:
|
else:
|
||||||
self.log.info(" tuser: not present")
|
self.log.info(" tuser: not present")
|
||||||
if self.ptp_time:
|
if self.ptp_time is not None:
|
||||||
self.log.info(" ptp_time width: %d bits", len(self.ptp_time))
|
self.log.info(" ptp_time width: %d bits", len(self.ptp_time))
|
||||||
else:
|
else:
|
||||||
self.log.info(" ptp_time: not present")
|
self.log.info(" ptp_time: not present")
|
||||||
@@ -462,7 +471,7 @@ class EthMacRx(Reset):
|
|||||||
if state:
|
if state:
|
||||||
self.log.info("Reset asserted")
|
self.log.info("Reset asserted")
|
||||||
if self._run_cr is not None:
|
if self._run_cr is not None:
|
||||||
self._run_cr.kill()
|
self._run_cr.cancel()
|
||||||
self._run_cr = None
|
self._run_cr = None
|
||||||
|
|
||||||
self.active = False
|
self.active = False
|
||||||
@@ -504,8 +513,8 @@ class EthMacRx(Reset):
|
|||||||
|
|
||||||
frame.sim_time_sfd = get_sim_time()
|
frame.sim_time_sfd = get_sim_time()
|
||||||
|
|
||||||
if self.ptp_time:
|
if self.ptp_time is not None:
|
||||||
frame.ptp_timestamp = self.ptp_time.value.integer
|
frame.ptp_timestamp = int(self.ptp_time.value)
|
||||||
tuser |= frame.ptp_timestamp << 1
|
tuser |= frame.ptp_timestamp << 1
|
||||||
|
|
||||||
# process frame data
|
# process frame data
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
Copyright (c) 2020 Alex Forencich
|
Copyright (c) 2020-2025 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
|
||||||
@@ -146,7 +146,7 @@ class GmiiSource(Reset):
|
|||||||
|
|
||||||
self.log.info("GMII source")
|
self.log.info("GMII source")
|
||||||
self.log.info("cocotbext-eth version %s", __version__)
|
self.log.info("cocotbext-eth version %s", __version__)
|
||||||
self.log.info("Copyright (c) 2020 Alex Forencich")
|
self.log.info("Copyright (c) 2020-2025 Alex Forencich")
|
||||||
self.log.info("https://github.com/alexforencich/cocotbext-eth")
|
self.log.info("https://github.com/alexforencich/cocotbext-eth")
|
||||||
|
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
@@ -239,7 +239,7 @@ class GmiiSource(Reset):
|
|||||||
if state:
|
if state:
|
||||||
self.log.info("Reset asserted")
|
self.log.info("Reset asserted")
|
||||||
if self._run_cr is not None:
|
if self._run_cr is not None:
|
||||||
self._run_cr.kill()
|
self._run_cr.cancel()
|
||||||
self._run_cr = None
|
self._run_cr = None
|
||||||
|
|
||||||
self.active = False
|
self.active = False
|
||||||
@@ -278,7 +278,7 @@ class GmiiSource(Reset):
|
|||||||
while True:
|
while True:
|
||||||
await clock_edge_event
|
await clock_edge_event
|
||||||
|
|
||||||
if self.enable is None or self.enable.value:
|
if self.enable is None or int(self.enable.value):
|
||||||
if ifg_cnt > 0:
|
if ifg_cnt > 0:
|
||||||
# in IFG
|
# in IFG
|
||||||
ifg_cnt -= 1
|
ifg_cnt -= 1
|
||||||
@@ -297,7 +297,7 @@ class GmiiSource(Reset):
|
|||||||
frame.normalize()
|
frame.normalize()
|
||||||
|
|
||||||
if self.mii_select is not None:
|
if self.mii_select is not None:
|
||||||
self.mii_mode = bool(self.mii_select.value.integer)
|
self.mii_mode = bool(int(self.mii_select.value))
|
||||||
|
|
||||||
if self.mii_mode:
|
if self.mii_mode:
|
||||||
# convert to MII
|
# convert to MII
|
||||||
@@ -361,7 +361,7 @@ class GmiiSink(Reset):
|
|||||||
|
|
||||||
self.log.info("GMII sink")
|
self.log.info("GMII sink")
|
||||||
self.log.info("cocotbext-eth version %s", __version__)
|
self.log.info("cocotbext-eth version %s", __version__)
|
||||||
self.log.info("Copyright (c) 2020 Alex Forencich")
|
self.log.info("Copyright (c) 2020-2025 Alex Forencich")
|
||||||
self.log.info("https://github.com/alexforencich/cocotbext-eth")
|
self.log.info("https://github.com/alexforencich/cocotbext-eth")
|
||||||
|
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
@@ -433,7 +433,7 @@ class GmiiSink(Reset):
|
|||||||
if state:
|
if state:
|
||||||
self.log.info("Reset asserted")
|
self.log.info("Reset asserted")
|
||||||
if self._run_cr is not None:
|
if self._run_cr is not None:
|
||||||
self._run_cr.kill()
|
self._run_cr.cancel()
|
||||||
self._run_cr = None
|
self._run_cr = None
|
||||||
|
|
||||||
self.active = False
|
self.active = False
|
||||||
@@ -457,10 +457,10 @@ class GmiiSink(Reset):
|
|||||||
while True:
|
while True:
|
||||||
await clock_edge_event
|
await clock_edge_event
|
||||||
|
|
||||||
if self.enable is None or self.enable.value:
|
if self.enable is None or int(self.enable.value):
|
||||||
d_val = self.data.value.integer
|
d_val = int(self.data.value)
|
||||||
dv_val = self.dv.value.integer
|
dv_val = int(self.dv.value)
|
||||||
er_val = 0 if self.er is None else self.er.value.integer
|
er_val = 0 if self.er is None else int(self.er.value)
|
||||||
|
|
||||||
if frame is None:
|
if frame is None:
|
||||||
if dv_val:
|
if dv_val:
|
||||||
@@ -472,7 +472,7 @@ class GmiiSink(Reset):
|
|||||||
# end of frame
|
# end of frame
|
||||||
|
|
||||||
if self.mii_select is not None:
|
if self.mii_select is not None:
|
||||||
self.mii_mode = bool(self.mii_select.value.integer)
|
self.mii_mode = bool(int(self.mii_select.value))
|
||||||
|
|
||||||
if self.mii_mode:
|
if self.mii_mode:
|
||||||
odd = True
|
odd = True
|
||||||
@@ -546,7 +546,7 @@ class GmiiPhy:
|
|||||||
raise ValueError("Invalid speed selection")
|
raise ValueError("Invalid speed selection")
|
||||||
|
|
||||||
if self._clock_cr is not None:
|
if self._clock_cr is not None:
|
||||||
self._clock_cr.kill()
|
self._clock_cr.cancel()
|
||||||
|
|
||||||
if self.speed == 1000e6:
|
if self.speed == 1000e6:
|
||||||
self._clock_cr = cocotb.start_soon(self._run_clocks(8*1e9/self.speed))
|
self._clock_cr = cocotb.start_soon(self._run_clocks(8*1e9/self.speed))
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
Copyright (c) 2020 Alex Forencich
|
Copyright (c) 2020-2025 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
|
||||||
@@ -48,7 +48,7 @@ class MiiSource(Reset):
|
|||||||
|
|
||||||
self.log.info("MII source")
|
self.log.info("MII source")
|
||||||
self.log.info("cocotbext-eth version %s", __version__)
|
self.log.info("cocotbext-eth version %s", __version__)
|
||||||
self.log.info("Copyright (c) 2020 Alex Forencich")
|
self.log.info("Copyright (c) 2020-2025 Alex Forencich")
|
||||||
self.log.info("https://github.com/alexforencich/cocotbext-eth")
|
self.log.info("https://github.com/alexforencich/cocotbext-eth")
|
||||||
|
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
@@ -140,7 +140,7 @@ class MiiSource(Reset):
|
|||||||
if state:
|
if state:
|
||||||
self.log.info("Reset asserted")
|
self.log.info("Reset asserted")
|
||||||
if self._run_cr is not None:
|
if self._run_cr is not None:
|
||||||
self._run_cr.kill()
|
self._run_cr.cancel()
|
||||||
self._run_cr = None
|
self._run_cr = None
|
||||||
|
|
||||||
self.active = False
|
self.active = False
|
||||||
@@ -179,7 +179,7 @@ class MiiSource(Reset):
|
|||||||
while True:
|
while True:
|
||||||
await clock_edge_event
|
await clock_edge_event
|
||||||
|
|
||||||
if self.enable is None or self.enable.value:
|
if self.enable is None or int(self.enable.value):
|
||||||
if ifg_cnt > 0:
|
if ifg_cnt > 0:
|
||||||
# in IFG
|
# in IFG
|
||||||
ifg_cnt -= 1
|
ifg_cnt -= 1
|
||||||
@@ -254,7 +254,7 @@ class MiiSink(Reset):
|
|||||||
|
|
||||||
self.log.info("MII sink")
|
self.log.info("MII sink")
|
||||||
self.log.info("cocotbext-eth version %s", __version__)
|
self.log.info("cocotbext-eth version %s", __version__)
|
||||||
self.log.info("Copyright (c) 2020 Alex Forencich")
|
self.log.info("Copyright (c) 2020-2025 Alex Forencich")
|
||||||
self.log.info("https://github.com/alexforencich/cocotbext-eth")
|
self.log.info("https://github.com/alexforencich/cocotbext-eth")
|
||||||
|
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
@@ -324,7 +324,7 @@ class MiiSink(Reset):
|
|||||||
if state:
|
if state:
|
||||||
self.log.info("Reset asserted")
|
self.log.info("Reset asserted")
|
||||||
if self._run_cr is not None:
|
if self._run_cr is not None:
|
||||||
self._run_cr.kill()
|
self._run_cr.cancel()
|
||||||
self._run_cr = None
|
self._run_cr = None
|
||||||
|
|
||||||
self.active = False
|
self.active = False
|
||||||
@@ -348,10 +348,10 @@ class MiiSink(Reset):
|
|||||||
while True:
|
while True:
|
||||||
await clock_edge_event
|
await clock_edge_event
|
||||||
|
|
||||||
if self.enable is None or self.enable.value:
|
if self.enable is None or int(self.enable.value):
|
||||||
d_val = self.data.value.integer
|
d_val = int(self.data.value)
|
||||||
dv_val = self.dv.value.integer
|
dv_val = int(self.dv.value)
|
||||||
er_val = 0 if self.er is None else self.er.value.integer
|
er_val = 0 if self.er is None else int(self.er.value)
|
||||||
|
|
||||||
if frame is None:
|
if frame is None:
|
||||||
if dv_val:
|
if dv_val:
|
||||||
@@ -432,7 +432,7 @@ class MiiPhy:
|
|||||||
raise ValueError("Invalid speed selection")
|
raise ValueError("Invalid speed selection")
|
||||||
|
|
||||||
if self._clock_cr is not None:
|
if self._clock_cr is not None:
|
||||||
self._clock_cr.kill()
|
self._clock_cr.cancel()
|
||||||
|
|
||||||
self._clock_cr = cocotb.start_soon(self._run_clocks(4*1e9/self.speed))
|
self._clock_cr = cocotb.start_soon(self._run_clocks(4*1e9/self.speed))
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
Copyright (c) 2020 Alex Forencich
|
Copyright (c) 2020-2025 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
|
||||||
@@ -23,7 +23,7 @@ THE SOFTWARE.
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
import math
|
from decimal import Decimal, Context
|
||||||
from fractions import Fraction
|
from fractions import Fraction
|
||||||
|
|
||||||
import cocotb
|
import cocotb
|
||||||
@@ -38,8 +38,8 @@ class PtpClock(Reset):
|
|||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
ts_96=None,
|
ts_tod=None,
|
||||||
ts_64=None,
|
ts_rel=None,
|
||||||
ts_step=None,
|
ts_step=None,
|
||||||
pps=None,
|
pps=None,
|
||||||
clock=None,
|
clock=None,
|
||||||
@@ -49,42 +49,42 @@ class PtpClock(Reset):
|
|||||||
*args, **kwargs):
|
*args, **kwargs):
|
||||||
|
|
||||||
self.log = logging.getLogger(f"cocotb.eth.{type(self).__name__}")
|
self.log = logging.getLogger(f"cocotb.eth.{type(self).__name__}")
|
||||||
self.ts_96 = ts_96
|
self.ts_tod = ts_tod
|
||||||
self.ts_64 = ts_64
|
self.ts_rel = ts_rel
|
||||||
self.ts_step = ts_step
|
self.ts_step = ts_step
|
||||||
self.pps = pps
|
self.pps = pps
|
||||||
self.clock = clock
|
self.clock = clock
|
||||||
self.reset = reset
|
self.reset = reset
|
||||||
|
|
||||||
self.period_ns = 0
|
|
||||||
self.period_fns = 0
|
|
||||||
self.drift_ns = 0
|
|
||||||
self.drift_fns = 0
|
|
||||||
self.drift_rate = 0
|
|
||||||
self.set_period_ns(period_ns)
|
|
||||||
|
|
||||||
self.log.info("PTP clock")
|
self.log.info("PTP clock")
|
||||||
self.log.info("cocotbext-eth version %s", __version__)
|
self.log.info("cocotbext-eth version %s", __version__)
|
||||||
self.log.info("Copyright (c) 2020 Alex Forencich")
|
self.log.info("Copyright (c) 2020-2025 Alex Forencich")
|
||||||
self.log.info("https://github.com/alexforencich/cocotbext-eth")
|
self.log.info("https://github.com/alexforencich/cocotbext-eth")
|
||||||
|
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
self.ts_96_s = 0
|
self.ctx = Context(prec=60)
|
||||||
self.ts_96_ns = 0
|
|
||||||
self.ts_96_fns = 0
|
|
||||||
|
|
||||||
self.ts_64_ns = 0
|
self.period_ns = 0
|
||||||
self.ts_64_fns = 0
|
self.period_fns = 0
|
||||||
|
self.drift_num = 0
|
||||||
|
self.drift_denom = 0
|
||||||
|
self.drift_cnt = 0
|
||||||
|
self.set_period_ns(period_ns)
|
||||||
|
|
||||||
|
self.ts_tod_s = 0
|
||||||
|
self.ts_tod_ns = 0
|
||||||
|
self.ts_tod_fns = 0
|
||||||
|
|
||||||
|
self.ts_rel_ns = 0
|
||||||
|
self.ts_rel_fns = 0
|
||||||
|
|
||||||
self.ts_updated = False
|
self.ts_updated = False
|
||||||
|
|
||||||
self.drift_cnt = 0
|
if self.ts_tod is not None:
|
||||||
|
self.ts_tod.setimmediatevalue(0)
|
||||||
if self.ts_96 is not None:
|
if self.ts_rel is not None:
|
||||||
self.ts_96.setimmediatevalue(0)
|
self.ts_rel.setimmediatevalue(0)
|
||||||
if self.ts_64 is not None:
|
|
||||||
self.ts_64.setimmediatevalue(0)
|
|
||||||
if self.ts_step is not None:
|
if self.ts_step is not None:
|
||||||
self.ts_step.setimmediatevalue(0)
|
self.ts_step.setimmediatevalue(0)
|
||||||
if self.pps is not None:
|
if self.pps is not None:
|
||||||
@@ -96,108 +96,121 @@ class PtpClock(Reset):
|
|||||||
|
|
||||||
def set_period(self, ns, fns):
|
def set_period(self, ns, fns):
|
||||||
self.period_ns = int(ns)
|
self.period_ns = int(ns)
|
||||||
self.period_fns = int(fns) & 0xffff
|
self.period_fns = int(fns) & 0xffffffff
|
||||||
|
|
||||||
def set_drift(self, ns, fns, rate):
|
def set_drift(self, num, denom):
|
||||||
self.drift_ns = int(ns)
|
self.drift_num = int(num)
|
||||||
self.drift_fns = int(fns) & 0xffff
|
self.drift_denom = int(denom)
|
||||||
self.drift_rate = int(rate)
|
|
||||||
|
|
||||||
def set_period_ns(self, t):
|
def set_period_ns(self, t):
|
||||||
drift, period = math.modf(t*2**16)
|
t = Decimal(t)
|
||||||
|
period, drift = self.ctx.divmod(Decimal(t) * Decimal(2**32), Decimal(1))
|
||||||
period = int(period)
|
period = int(period)
|
||||||
frac = Fraction(drift).limit_denominator(2**16)
|
frac = Fraction(drift).limit_denominator(2**16-1)
|
||||||
drift = frac.numerator
|
self.set_period(period >> 32, period & 0xffffffff)
|
||||||
rate = frac.denominator
|
self.set_drift(frac.numerator, frac.denominator)
|
||||||
self.period_ns = period >> 16
|
|
||||||
self.period_fns = period & 0xffff
|
self.log.info("Set period: %s ns", t)
|
||||||
self.drift_ns = drift >> 16
|
self.log.info("Period: 0x%x ns 0x%08x fns", self.period_ns, self.period_fns)
|
||||||
self.drift_fns = drift & 0xffff
|
self.log.info("Drift: 0x%04x / 0x%04x fns", self.drift_num, self.drift_denom)
|
||||||
self.drift_rate = rate
|
|
||||||
|
|
||||||
def get_period_ns(self):
|
def get_period_ns(self):
|
||||||
p = ((self.period_ns << 16) | self.period_fns) / 2**16
|
p = Decimal((self.period_ns << 32) | self.period_fns)
|
||||||
if self.drift_rate:
|
if self.drift_denom:
|
||||||
return p + ((self.drift_ns << 16) | self.drift_fns) / self.drift_rate / 2**16
|
p += Decimal(self.drift_num) / Decimal(self.drift_denom)
|
||||||
return p
|
return p / Decimal(2**32)
|
||||||
|
|
||||||
def set_ts_96(self, ts_s, ts_ns=None, ts_fns=None):
|
def set_ts_tod(self, ts_s, ts_ns, ts_fns):
|
||||||
ts_s = int(ts_s)
|
self.ts_tod_s = int(ts_s)
|
||||||
if ts_fns is not None:
|
self.ts_tod_ns = int(ts_ns)
|
||||||
# got separate fields
|
self.ts_tod_fns = int(ts_fns)
|
||||||
self.ts_96_s = ts_s
|
|
||||||
self.ts_96_ns = int(ts_ns)
|
|
||||||
self.ts_96_fns = int(ts_fns)
|
|
||||||
else:
|
|
||||||
# got timestamp as integer
|
|
||||||
self.ts_96_s = ts_s >> 48
|
|
||||||
self.ts_96_ns = (ts_s >> 16) & 0x3fffffff
|
|
||||||
self.ts_96_fns = ts_s & 0xffff
|
|
||||||
self.ts_updated = True
|
self.ts_updated = True
|
||||||
|
|
||||||
def set_ts_96_ns(self, t):
|
def set_ts_tod_96(self, ts):
|
||||||
self.set_ts_96_s(t*1e-9)
|
ts = int(ts)
|
||||||
|
self.set_ts_tod(ts >> 48, (ts >> 32) & 0x3fffffff, (ts & 0xffff) << 16)
|
||||||
|
|
||||||
def set_ts_96_s(self, t):
|
def set_ts_tod_ns(self, t):
|
||||||
ts_ns, ts_s = math.modf(t)
|
ts_s, ts_ns = self.ctx.divmod(Decimal(t), Decimal(1000000000))
|
||||||
ts_ns *= 1e9
|
ts_ns, ts_fns = self.ctx.divmod(ts_ns, Decimal(1))
|
||||||
ts_fns, ts_ns = math.modf(ts_ns)
|
ts_ns = ts_ns.to_integral_value()
|
||||||
ts_fns *= 2**16
|
ts_fns = (ts_fns * Decimal(2**32)).to_integral_value()
|
||||||
self.set_ts_96(ts_s, ts_ns, ts_fns)
|
self.set_ts_tod(ts_s, ts_ns, ts_fns)
|
||||||
|
|
||||||
def get_ts_96(self):
|
def set_ts_tod_s(self, t):
|
||||||
return (self.ts_96_s << 48) | (self.ts_96_ns << 16) | self.ts_96_fns
|
self.set_ts_tod_ns(Decimal(t).scaleb(9, self.ctx))
|
||||||
|
|
||||||
def get_ts_96_ns(self):
|
def set_ts_tod_sim_time(self):
|
||||||
return self.ts_96_s*1e9+self.ts_96_ns+self.ts_96_fns/2**16
|
self.set_ts_tod_ns(Decimal(get_sim_time('fs')).scaleb(-6))
|
||||||
|
|
||||||
def get_ts_96_s(self):
|
def get_ts_tod(self):
|
||||||
return self.get_ts_96_ns()*1e-9
|
return (self.ts_tod_s, self.ts_tod_ns, self.ts_tod_fns)
|
||||||
|
|
||||||
def set_ts_64(self, ts_ns, ts_fns=None):
|
def get_ts_tod_96(self):
|
||||||
ts_ns = int(ts_ns)
|
ts_s, ts_ns, ts_fns = self.get_ts_tod()
|
||||||
if ts_fns is not None:
|
return (ts_s << 48) | (ts_ns << 16) | (ts_fns >> 16)
|
||||||
# got separate fields
|
|
||||||
self.ts_64_ns = ts_ns
|
def get_ts_tod_ns(self):
|
||||||
self.ts_64_fns = int(ts_fns)
|
ts_s, ts_ns, ts_fns = self.get_ts_tod()
|
||||||
else:
|
ns = Decimal(ts_fns) / Decimal(2**32)
|
||||||
# got timestamp as integer
|
ns = self.ctx.add(ns, Decimal(ts_ns))
|
||||||
self.ts_64_ns = ts_ns >> 16
|
return self.ctx.add(ns, Decimal(ts_s).scaleb(9))
|
||||||
self.ts_64_fns = ts_ns & 0xffff
|
|
||||||
|
def get_ts_tod_s(self):
|
||||||
|
return self.get_ts_tod_ns().scaleb(-9, self.ctx)
|
||||||
|
|
||||||
|
def set_ts_rel(self, ts_ns, ts_fns):
|
||||||
|
self.ts_rel_ns = int(ts_ns)
|
||||||
|
self.ts_rel_fns = int(ts_fns)
|
||||||
self.ts_updated = True
|
self.ts_updated = True
|
||||||
|
|
||||||
def set_ts_64_ns(self, t):
|
def set_ts_rel_64(self, ts):
|
||||||
self.set_ts_64(t*2**16)
|
ts = int(ts)
|
||||||
|
self.set_ts_rel(ts >> 16, (ts & 0xffff) << 16)
|
||||||
|
|
||||||
def set_ts_64_s(self, t):
|
def set_ts_rel_ns(self, t):
|
||||||
self.set_ts_64_ns(t*1e9)
|
ts_ns, ts_fns = self.ctx.divmod(Decimal(t), Decimal(1))
|
||||||
|
ts_ns = ts_ns.to_integral_value()
|
||||||
|
ts_fns = (ts_fns * Decimal(2**32)).to_integral_value()
|
||||||
|
self.set_ts_rel(ts_ns, ts_fns)
|
||||||
|
|
||||||
def get_ts_64(self):
|
def set_ts_rel_s(self, t):
|
||||||
return (self.ts_64_ns << 16) | self.ts_64_fns
|
self.set_ts_rel_ns(Decimal(t).scaleb(9, self.ctx))
|
||||||
|
|
||||||
def get_ts_64_ns(self):
|
def set_ts_rel_sim_time(self):
|
||||||
return self.get_ts_64()/2**16
|
self.set_ts_rel_ns(Decimal(get_sim_time('fs')).scaleb(-6))
|
||||||
|
|
||||||
def get_ts_64_s(self):
|
def get_ts_rel(self):
|
||||||
return self.get_ts_64()*1e-9
|
return (self.ts_rel_ns, self.ts_rel_fns)
|
||||||
|
|
||||||
|
def get_ts_rel_64(self):
|
||||||
|
ts_ns, ts_fns = self.get_ts_rel()
|
||||||
|
return (ts_ns << 16) | (ts_fns >> 16)
|
||||||
|
|
||||||
|
def get_ts_rel_ns(self):
|
||||||
|
ts_ns, ts_fns = self.get_ts_rel()
|
||||||
|
return self.ctx.add(Decimal(ts_fns) / Decimal(2**32), Decimal(ts_ns))
|
||||||
|
|
||||||
|
def get_ts_rel_s(self):
|
||||||
|
return self.get_ts_rel_ns().scaleb(-9, self.ctx)
|
||||||
|
|
||||||
def _handle_reset(self, state):
|
def _handle_reset(self, state):
|
||||||
if state:
|
if state:
|
||||||
self.log.info("Reset asserted")
|
self.log.info("Reset asserted")
|
||||||
if self._run_cr is not None:
|
if self._run_cr is not None:
|
||||||
self._run_cr.kill()
|
self._run_cr.cancel()
|
||||||
self._run_cr = None
|
self._run_cr = None
|
||||||
|
|
||||||
self.ts_96_s = 0
|
self.ts_tod_s = 0
|
||||||
self.ts_96_ns = 0
|
self.ts_tod_ns = 0
|
||||||
self.ts_96_fns = 0
|
self.ts_tod_fns = 0
|
||||||
self.ts_64_ns = 0
|
self.ts_rel_ns = 0
|
||||||
self.ts_64_fns = 0
|
self.ts_rel_fns = 0
|
||||||
self.drift_cnt = 0
|
self.drift_cnt = 0
|
||||||
if self.ts_96 is not None:
|
if self.ts_tod is not None:
|
||||||
self.ts_96.value = 0
|
self.ts_tod.value = 0
|
||||||
if self.ts_64 is not None:
|
if self.ts_rel is not None:
|
||||||
self.ts_64.value = 0
|
self.ts_rel.value = 0
|
||||||
if self.ts_step is not None:
|
if self.ts_step is not None:
|
||||||
self.ts_step.value = 0
|
self.ts_step.value = 0
|
||||||
if self.pps is not None:
|
if self.pps is not None:
|
||||||
@@ -220,95 +233,112 @@ class PtpClock(Reset):
|
|||||||
if self.pps is not None:
|
if self.pps is not None:
|
||||||
self.pps.value = 0
|
self.pps.value = 0
|
||||||
|
|
||||||
# increment 96 bit timestamp
|
# increment tod bit timestamp
|
||||||
if self.ts_96 is not None or self.pps is not None:
|
self.ts_tod_fns += (self.period_ns << 32) + self.period_fns
|
||||||
t = ((self.ts_96_ns << 16) + self.ts_96_fns) + ((self.period_ns << 16) + self.period_fns)
|
|
||||||
|
|
||||||
if self.drift_rate and self.drift_cnt == 0:
|
if self.drift_denom and self.drift_cnt == 0:
|
||||||
t += (self.drift_ns << 16) + self.drift_fns
|
self.ts_tod_fns += self.drift_num
|
||||||
|
|
||||||
if t > (1000000000 << 16):
|
ns_inc = self.ts_tod_fns >> 32
|
||||||
self.ts_96_s += 1
|
self.ts_tod_fns &= 0xffffffff
|
||||||
t -= (1000000000 << 16)
|
|
||||||
|
self.ts_tod_ns += ns_inc
|
||||||
|
|
||||||
|
if self.ts_tod_ns >= 1000000000:
|
||||||
|
self.ts_tod_s += 1
|
||||||
|
self.ts_tod_ns -= 1000000000
|
||||||
if self.pps is not None:
|
if self.pps is not None:
|
||||||
self.pps.value = 1
|
self.pps.value = 1
|
||||||
|
|
||||||
self.ts_96_fns = t & 0xffff
|
if self.ts_tod is not None:
|
||||||
self.ts_96_ns = t >> 16
|
self.ts_tod.value = (self.ts_tod_s << 48) | (self.ts_tod_ns << 16) | (self.ts_tod_fns >> 16)
|
||||||
|
|
||||||
if self.ts_96 is not None:
|
# increment rel bit timestamp
|
||||||
self.ts_96.value = (self.ts_96_s << 48) | (self.ts_96_ns << 16) | (self.ts_96_fns)
|
self.ts_rel_fns += (self.period_ns << 32) + self.period_fns
|
||||||
|
|
||||||
# increment 64 bit timestamp
|
if self.drift_denom and self.drift_cnt == 0:
|
||||||
if self.ts_64 is not None:
|
self.ts_rel_fns += self.drift_num
|
||||||
t = ((self.ts_64_ns << 16) + self.ts_64_fns) + ((self.period_ns << 16) + self.period_fns)
|
|
||||||
|
|
||||||
if self.drift_rate and self.drift_cnt == 0:
|
ns_inc = self.ts_rel_fns >> 32
|
||||||
t += ((self.drift_ns << 16) + self.drift_fns)
|
self.ts_rel_fns &= 0xffffffff
|
||||||
|
|
||||||
self.ts_64_fns = t & 0xffff
|
self.ts_rel_ns = (self.ts_rel_ns + ns_inc) & 0xffffffffffff
|
||||||
self.ts_64_ns = t >> 16
|
|
||||||
|
|
||||||
self.ts_64.value = (self.ts_64_ns << 16) | self.ts_64_fns
|
if self.ts_rel is not None:
|
||||||
|
self.ts_rel.value = (self.ts_rel_ns << 16) | (self.ts_rel_fns >> 16)
|
||||||
|
|
||||||
if self.drift_rate:
|
if self.drift_denom:
|
||||||
if self.drift_cnt > 0:
|
if self.drift_cnt > 0:
|
||||||
self.drift_cnt -= 1
|
self.drift_cnt -= 1
|
||||||
else:
|
else:
|
||||||
self.drift_cnt = self.drift_rate-1
|
self.drift_cnt = self.drift_denom-1
|
||||||
|
|
||||||
|
|
||||||
class PtpClockSimTime:
|
class PtpClockSimTime:
|
||||||
|
|
||||||
def __init__(self, ts_96=None, ts_64=None, pps=None, clock=None, *args, **kwargs):
|
def __init__(self, ts_tod=None, ts_rel=None, pps=None, clock=None, *args, **kwargs):
|
||||||
self.log = logging.getLogger(f"cocotb.eth.{type(self).__name__}")
|
self.log = logging.getLogger(f"cocotb.eth.{type(self).__name__}")
|
||||||
self.ts_96 = ts_96
|
self.ts_tod = ts_tod
|
||||||
self.ts_64 = ts_64
|
self.ts_rel = ts_rel
|
||||||
self.pps = pps
|
self.pps = pps
|
||||||
self.clock = clock
|
self.clock = clock
|
||||||
|
|
||||||
self.log.info("PTP clock (sim time)")
|
self.log.info("PTP clock (sim time)")
|
||||||
self.log.info("cocotbext-eth version %s", __version__)
|
self.log.info("cocotbext-eth version %s", __version__)
|
||||||
self.log.info("Copyright (c) 2020 Alex Forencich")
|
self.log.info("Copyright (c) 2020-2025 Alex Forencich")
|
||||||
self.log.info("https://github.com/alexforencich/cocotbext-eth")
|
self.log.info("https://github.com/alexforencich/cocotbext-eth")
|
||||||
|
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
self.ts_96_s = 0
|
self.ctx = Context(prec=60)
|
||||||
self.ts_96_ns = 0
|
|
||||||
self.ts_96_fns = 0
|
|
||||||
|
|
||||||
self.ts_64_ns = 0
|
self.ts_tod_s = 0
|
||||||
self.ts_64_fns = 0
|
self.ts_tod_ns = 0
|
||||||
|
self.ts_tod_fns = 0
|
||||||
|
|
||||||
self.last_ts_96_s = 0
|
self.ts_rel_ns = 0
|
||||||
|
self.ts_rel_fns = 0
|
||||||
|
|
||||||
if self.ts_96 is not None:
|
self.last_ts_tod_s = 0
|
||||||
self.ts_96.setimmediatevalue(0)
|
|
||||||
if self.ts_64 is not None:
|
if self.ts_tod is not None:
|
||||||
self.ts_64.setimmediatevalue(0)
|
self.ts_tod.setimmediatevalue(0)
|
||||||
|
if self.ts_rel is not None:
|
||||||
|
self.ts_rel.setimmediatevalue(0)
|
||||||
if self.pps is not None:
|
if self.pps is not None:
|
||||||
self.pps.value = 0
|
self.pps.value = 0
|
||||||
|
|
||||||
self._run_cr = cocotb.start_soon(self._run())
|
self._run_cr = cocotb.start_soon(self._run())
|
||||||
|
|
||||||
def get_ts_96(self):
|
def get_ts_tod(self):
|
||||||
return (self.ts_96_s << 48) | (self.ts_96_ns << 16) | self.ts_96_fns
|
return (self.ts_tod_s, self.ts_tod_ns, self.ts_tod_fns)
|
||||||
|
|
||||||
def get_ts_96_ns(self):
|
def get_ts_tod_96(self):
|
||||||
return self.ts_96_s*1e9+self.ts_96_ns+self.ts_96_fns/2**16
|
ts_s, ts_ns, ts_fns = self.get_ts_tod()
|
||||||
|
return (ts_s << 48) | (ts_ns << 16) | (ts_fns >> 16)
|
||||||
|
|
||||||
def get_ts_96_s(self):
|
def get_ts_tod_ns(self):
|
||||||
return self.get_ts_96_ns()*1e-9
|
ts_s, ts_ns, ts_fns = self.get_ts_tod()
|
||||||
|
ns = Decimal(ts_fns) / Decimal(2**32)
|
||||||
|
ns = self.ctx.add(ns, Decimal(ts_ns))
|
||||||
|
return self.ctx.add(ns, Decimal(ts_s).scaleb(9))
|
||||||
|
|
||||||
def get_ts_64(self):
|
def get_ts_tod_s(self):
|
||||||
return (self.ts_64_ns << 16) | self.ts_64_fns
|
return self.get_ts_tod_ns().scaleb(-9, self.ctx)
|
||||||
|
|
||||||
def get_ts_64_ns(self):
|
def get_ts_rel(self):
|
||||||
return self.get_ts_64()/2**16
|
return (self.ts_rel_ns, self.ts_rel_fns)
|
||||||
|
|
||||||
def get_ts_64_s(self):
|
def get_ts_rel_64(self):
|
||||||
return self.get_ts_64()*1e-9
|
ts_ns, ts_fns = self.get_ts_rel()
|
||||||
|
return (ts_ns << 16) | (ts_fns >> 16)
|
||||||
|
|
||||||
|
def get_ts_rel_ns(self):
|
||||||
|
ts_ns, ts_fns = self.get_ts_rel()
|
||||||
|
return self.ctx.add(Decimal(ts_fns) / Decimal(2**32), Decimal(ts_ns))
|
||||||
|
|
||||||
|
def get_ts_rel_s(self):
|
||||||
|
return self.get_ts_rel_ns().scaleb(-9, self.ctx)
|
||||||
|
|
||||||
async def _run(self):
|
async def _run(self):
|
||||||
clock_edge_event = RisingEdge(self.clock)
|
clock_edge_event = RisingEdge(self.clock)
|
||||||
@@ -316,21 +346,24 @@ class PtpClockSimTime:
|
|||||||
while True:
|
while True:
|
||||||
await clock_edge_event
|
await clock_edge_event
|
||||||
|
|
||||||
self.ts_64_fns, self.ts_64_ns = math.modf(get_sim_time('ns'))
|
ts_ns, ts_fns = self.ctx.divmod(Decimal(get_sim_time('fs')).scaleb(-6), Decimal(1))
|
||||||
|
|
||||||
self.ts_64_ns = int(self.ts_64_ns)
|
self.ts_rel_ns = int(ts_ns.to_integral_value()) & 0xffffffffffff
|
||||||
self.ts_64_fns = int(self.ts_64_fns*0x10000)
|
self.ts_rel_fns = int((ts_fns * Decimal(2**16)).to_integral_value())
|
||||||
|
|
||||||
self.ts_96_s, self.ts_96_ns = divmod(self.ts_64_ns, 1000000000)
|
ts_s, ts_ns = self.ctx.divmod(ts_ns, Decimal(1000000000))
|
||||||
self.ts_96_fns = self.ts_64_fns
|
|
||||||
|
|
||||||
if self.ts_96 is not None:
|
self.ts_tod_s = int(ts_s.scaleb(-9).to_integral_value())
|
||||||
self.ts_96.value = (self.ts_96_s << 48) | (self.ts_96_ns << 16) | self.ts_96_fns
|
self.ts_tod_ns = int(ts_ns.to_integral_value())
|
||||||
|
self.ts_tod_fns = self.ts_rel_fns
|
||||||
|
|
||||||
if self.ts_64 is not None:
|
if self.ts_tod is not None:
|
||||||
self.ts_64.value = (self.ts_64_ns << 16) | self.ts_64_fns
|
self.ts_tod.value = (self.ts_tod_s << 48) | (self.ts_tod_ns << 16) | self.ts_tod_fns
|
||||||
|
|
||||||
|
if self.ts_rel is not None:
|
||||||
|
self.ts_rel.value = (self.ts_rel_ns << 16) | self.ts_rel_fns
|
||||||
|
|
||||||
if self.pps is not None:
|
if self.pps is not None:
|
||||||
self.pps.value = int(self.last_ts_96_s != self.ts_96_s)
|
self.pps.value = int(self.last_ts_tod_s != self.ts_tod_s)
|
||||||
|
|
||||||
self.last_ts_96_s = self.ts_96_s
|
self.last_ts_tod_s = self.ts_tod_s
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
Copyright (c) 2020 Alex Forencich
|
Copyright (c) 2020-2025 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
|
||||||
@@ -23,7 +23,7 @@ THE SOFTWARE.
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
import cocotb
|
import cocotb
|
||||||
from cocotb.triggers import RisingEdge, FallingEdge
|
from cocotb.triggers import Edge
|
||||||
|
|
||||||
|
|
||||||
class Reset:
|
class Reset:
|
||||||
@@ -56,11 +56,14 @@ class Reset:
|
|||||||
|
|
||||||
async def _run_reset(self, reset_signal, active_level):
|
async def _run_reset(self, reset_signal, active_level):
|
||||||
while True:
|
while True:
|
||||||
if bool(reset_signal.value):
|
await reset_signal.value_change
|
||||||
await FallingEdge(reset_signal)
|
try:
|
||||||
self._ext_reset = not active_level
|
level = bool(int(reset_signal.value))
|
||||||
self._update_reset()
|
except ValueError:
|
||||||
else:
|
continue
|
||||||
await RisingEdge(reset_signal)
|
if level:
|
||||||
self._ext_reset = active_level
|
self._ext_reset = active_level
|
||||||
self._update_reset()
|
self._update_reset()
|
||||||
|
else:
|
||||||
|
self._ext_reset = not active_level
|
||||||
|
self._update_reset()
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
Copyright (c) 2020 Alex Forencich
|
Copyright (c) 2020-2025 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
|
||||||
@@ -50,7 +50,7 @@ class RgmiiSource(Reset):
|
|||||||
|
|
||||||
self.log.info("RGMII source")
|
self.log.info("RGMII source")
|
||||||
self.log.info("cocotbext-eth version %s", __version__)
|
self.log.info("cocotbext-eth version %s", __version__)
|
||||||
self.log.info("Copyright (c) 2020 Alex Forencich")
|
self.log.info("Copyright (c) 2020-2025 Alex Forencich")
|
||||||
self.log.info("https://github.com/alexforencich/cocotbext-eth")
|
self.log.info("https://github.com/alexforencich/cocotbext-eth")
|
||||||
|
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
@@ -140,7 +140,7 @@ class RgmiiSource(Reset):
|
|||||||
if state:
|
if state:
|
||||||
self.log.info("Reset asserted")
|
self.log.info("Reset asserted")
|
||||||
if self._run_cr is not None:
|
if self._run_cr is not None:
|
||||||
self._run_cr.kill()
|
self._run_cr.cancel()
|
||||||
self._run_cr = None
|
self._run_cr = None
|
||||||
|
|
||||||
self.active = False
|
self.active = False
|
||||||
@@ -192,7 +192,7 @@ class RgmiiSource(Reset):
|
|||||||
self.data.value = d >> 4
|
self.data.value = d >> 4
|
||||||
self.ctrl.value = en ^ er
|
self.ctrl.value = en ^ er
|
||||||
|
|
||||||
if self.enable is None or self.enable.value:
|
if self.enable is None or int(self.enable.value):
|
||||||
in_ifg = False
|
in_ifg = False
|
||||||
|
|
||||||
if ifg_cnt > 0:
|
if ifg_cnt > 0:
|
||||||
@@ -214,7 +214,7 @@ class RgmiiSource(Reset):
|
|||||||
frame.normalize()
|
frame.normalize()
|
||||||
|
|
||||||
if self.mii_select is not None:
|
if self.mii_select is not None:
|
||||||
self.mii_mode = bool(self.mii_select.value.integer)
|
self.mii_mode = bool(int(self.mii_select.value))
|
||||||
|
|
||||||
if self.mii_mode:
|
if self.mii_mode:
|
||||||
# convert to MII
|
# convert to MII
|
||||||
@@ -278,7 +278,7 @@ class RgmiiSink(Reset):
|
|||||||
|
|
||||||
self.log.info("RGMII sink")
|
self.log.info("RGMII sink")
|
||||||
self.log.info("cocotbext-eth version %s", __version__)
|
self.log.info("cocotbext-eth version %s", __version__)
|
||||||
self.log.info("Copyright (c) 2020 Alex Forencich")
|
self.log.info("Copyright (c) 2020-2025 Alex Forencich")
|
||||||
self.log.info("https://github.com/alexforencich/cocotbext-eth")
|
self.log.info("https://github.com/alexforencich/cocotbext-eth")
|
||||||
|
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
@@ -347,7 +347,7 @@ class RgmiiSink(Reset):
|
|||||||
if state:
|
if state:
|
||||||
self.log.info("Reset asserted")
|
self.log.info("Reset asserted")
|
||||||
if self._run_cr is not None:
|
if self._run_cr is not None:
|
||||||
self._run_cr.kill()
|
self._run_cr.cancel()
|
||||||
self._run_cr = None
|
self._run_cr = None
|
||||||
|
|
||||||
self.active = False
|
self.active = False
|
||||||
@@ -375,17 +375,17 @@ class RgmiiSink(Reset):
|
|||||||
while True:
|
while True:
|
||||||
await clock_rising_edge_event
|
await clock_rising_edge_event
|
||||||
|
|
||||||
if self.enable is None or self.enable.value:
|
if self.enable is None or int(self.enable.value):
|
||||||
|
|
||||||
# capture low nibble on rising edge
|
# capture low nibble on rising edge
|
||||||
d_val = self.data.value.integer
|
d_val = int(self.data.value)
|
||||||
dv_val = self.ctrl.value.integer
|
dv_val = int(self.ctrl.value)
|
||||||
|
|
||||||
await clock_falling_edge_event
|
await clock_falling_edge_event
|
||||||
|
|
||||||
# capture high nibble on falling edge
|
# capture high nibble on falling edge
|
||||||
d_val |= self.data.value.integer << 4
|
d_val |= int(self.data.value) << 4
|
||||||
er_val = dv_val ^ self.ctrl.value.integer
|
er_val = dv_val ^ int(self.ctrl.value)
|
||||||
|
|
||||||
if frame is None:
|
if frame is None:
|
||||||
if dv_val:
|
if dv_val:
|
||||||
@@ -397,7 +397,7 @@ class RgmiiSink(Reset):
|
|||||||
# end of frame
|
# end of frame
|
||||||
|
|
||||||
if self.mii_select is not None:
|
if self.mii_select is not None:
|
||||||
self.mii_mode = bool(self.mii_select.value.integer)
|
self.mii_mode = bool(int(self.mii_select.value))
|
||||||
|
|
||||||
if self.mii_mode:
|
if self.mii_mode:
|
||||||
odd = True
|
odd = True
|
||||||
@@ -470,7 +470,7 @@ class RgmiiPhy:
|
|||||||
raise ValueError("Invalid speed selection")
|
raise ValueError("Invalid speed selection")
|
||||||
|
|
||||||
if self._clock_cr is not None:
|
if self._clock_cr is not None:
|
||||||
self._clock_cr.kill()
|
self._clock_cr.cancel()
|
||||||
|
|
||||||
if self.speed == 1000e6:
|
if self.speed == 1000e6:
|
||||||
self._clock_cr = cocotb.start_soon(self._run_clock(8*1e9/self.speed))
|
self._clock_cr = cocotb.start_soon(self._run_clock(8*1e9/self.speed))
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
__version__ = "0.1.20"
|
__version__ = "0.1.24"
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
Copyright (c) 2020 Alex Forencich
|
Copyright (c) 2020-2025 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
|
||||||
@@ -147,7 +147,7 @@ class XgmiiSource(Reset):
|
|||||||
|
|
||||||
self.log.info("XGMII source")
|
self.log.info("XGMII source")
|
||||||
self.log.info("cocotbext-eth version %s", __version__)
|
self.log.info("cocotbext-eth version %s", __version__)
|
||||||
self.log.info("Copyright (c) 2020 Alex Forencich")
|
self.log.info("Copyright (c) 2020-2025 Alex Forencich")
|
||||||
self.log.info("https://github.com/alexforencich/cocotbext-eth")
|
self.log.info("https://github.com/alexforencich/cocotbext-eth")
|
||||||
|
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
@@ -250,7 +250,7 @@ class XgmiiSource(Reset):
|
|||||||
if state:
|
if state:
|
||||||
self.log.info("Reset asserted")
|
self.log.info("Reset asserted")
|
||||||
if self._run_cr is not None:
|
if self._run_cr is not None:
|
||||||
self._run_cr.kill()
|
self._run_cr.cancel()
|
||||||
self._run_cr = None
|
self._run_cr = None
|
||||||
|
|
||||||
self.active = False
|
self.active = False
|
||||||
@@ -286,7 +286,7 @@ class XgmiiSource(Reset):
|
|||||||
while True:
|
while True:
|
||||||
await clock_edge_event
|
await clock_edge_event
|
||||||
|
|
||||||
if self.enable is None or self.enable.value:
|
if self.enable is None or int(self.enable.value):
|
||||||
if ifg_cnt + deficit_idle_cnt > self.byte_lanes-1 or (not self.enable_dic and ifg_cnt > 4):
|
if ifg_cnt + deficit_idle_cnt > self.byte_lanes-1 or (not self.enable_dic and ifg_cnt > 4):
|
||||||
# in IFG
|
# in IFG
|
||||||
ifg_cnt = ifg_cnt - self.byte_lanes
|
ifg_cnt = ifg_cnt - self.byte_lanes
|
||||||
@@ -390,7 +390,7 @@ class XgmiiSink(Reset):
|
|||||||
|
|
||||||
self.log.info("XGMII sink")
|
self.log.info("XGMII sink")
|
||||||
self.log.info("cocotbext-eth version %s", __version__)
|
self.log.info("cocotbext-eth version %s", __version__)
|
||||||
self.log.info("Copyright (c) 2020 Alex Forencich")
|
self.log.info("Copyright (c) 2020-2025 Alex Forencich")
|
||||||
self.log.info("https://github.com/alexforencich/cocotbext-eth")
|
self.log.info("https://github.com/alexforencich/cocotbext-eth")
|
||||||
|
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
@@ -461,7 +461,7 @@ class XgmiiSink(Reset):
|
|||||||
if state:
|
if state:
|
||||||
self.log.info("Reset asserted")
|
self.log.info("Reset asserted")
|
||||||
if self._run_cr is not None:
|
if self._run_cr is not None:
|
||||||
self._run_cr.kill()
|
self._run_cr.cancel()
|
||||||
self._run_cr = None
|
self._run_cr = None
|
||||||
|
|
||||||
self.active = False
|
self.active = False
|
||||||
@@ -488,9 +488,9 @@ class XgmiiSink(Reset):
|
|||||||
while True:
|
while True:
|
||||||
await clock_edge_event
|
await clock_edge_event
|
||||||
|
|
||||||
if self.enable is None or self.enable.value:
|
if self.enable is None or int(self.enable.value):
|
||||||
data_val = self.data.value.integer
|
data_val = int(self.data.value)
|
||||||
ctrl_val = self.ctrl.value.integer
|
ctrl_val = int(self.ctrl.value)
|
||||||
for offset in range(self.byte_lanes):
|
for offset in range(self.byte_lanes):
|
||||||
d_val = (data_val >> (offset*8)) & 0xff
|
d_val = (data_val >> (offset*8)) & 0xff
|
||||||
c_val = (ctrl_val >> offset) & 1
|
c_val = (ctrl_val >> offset) & 1
|
||||||
|
|||||||
18
setup.cfg
18
setup.cfg
@@ -13,7 +13,7 @@ project_urls =
|
|||||||
Source Code = https://github.com/alexforencich/cocotbext-eth
|
Source Code = https://github.com/alexforencich/cocotbext-eth
|
||||||
download_url = https://github.com/alexforencich/cocotbext-eth/tarball/master
|
download_url = https://github.com/alexforencich/cocotbext-eth/tarball/master
|
||||||
long_description = file: README.md
|
long_description = file: README.md
|
||||||
long-description-content-type = text/markdown
|
long_description_content_type = text/markdown
|
||||||
platforms = any
|
platforms = any
|
||||||
classifiers =
|
classifiers =
|
||||||
Development Status :: 3 - Alpha
|
Development Status :: 3 - Alpha
|
||||||
@@ -47,17 +47,19 @@ addopts =
|
|||||||
|
|
||||||
# tox configuration
|
# tox configuration
|
||||||
[tox:tox]
|
[tox:tox]
|
||||||
envlist = py37, py38, py39, py310
|
envlist = py38, py39, py310, py311, py312, py313
|
||||||
skip_missing_interpreters = true
|
skip_missing_interpreters = true
|
||||||
minversion = 3.18.0
|
minversion = 3.18.0
|
||||||
requires = virtualenv >= 16.1
|
requires = virtualenv >= 16.1
|
||||||
|
|
||||||
[gh-actions]
|
[gh-actions]
|
||||||
python =
|
python =
|
||||||
3.7: py37
|
|
||||||
3.8: py38
|
3.8: py38
|
||||||
3.9: py39
|
3.9: py39
|
||||||
3.10: py310
|
3.10: py310
|
||||||
|
3.11: py311
|
||||||
|
3.12: py312
|
||||||
|
3.13: py313
|
||||||
|
|
||||||
[testenv]
|
[testenv]
|
||||||
setenv =
|
setenv =
|
||||||
@@ -65,12 +67,12 @@ setenv =
|
|||||||
usedevelop = True
|
usedevelop = True
|
||||||
|
|
||||||
deps =
|
deps =
|
||||||
pytest == 7.2.1
|
pytest == 8.3.4
|
||||||
pytest-xdist == 3.1.0
|
pytest-xdist == 3.6.1
|
||||||
cocotb == 1.7.2
|
cocotb == 1.9.2
|
||||||
cocotb-bus == 0.2.1
|
cocotb-bus == 0.2.1
|
||||||
cocotb-test == 0.2.4
|
cocotb-test == 0.2.6
|
||||||
cocotbext-axi == 0.1.20
|
cocotbext-axi == 0.1.26
|
||||||
coverage == 7.0.5
|
coverage == 7.0.5
|
||||||
pytest-cov == 4.0.0
|
pytest-cov == 4.0.0
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
# Copyright (c) 2021 Alex Forencich
|
# Copyright (c) 2021-2025 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
|
||||||
@@ -27,35 +27,31 @@ COCOTB_HDL_TIMEUNIT = 1ns
|
|||||||
COCOTB_HDL_TIMEPRECISION = 1ns
|
COCOTB_HDL_TIMEPRECISION = 1ns
|
||||||
|
|
||||||
DUT = test_eth_mac
|
DUT = test_eth_mac
|
||||||
TOPLEVEL = $(DUT)
|
COCOTB_TEST_MODULES = $(DUT)
|
||||||
MODULE = $(DUT)
|
COCOTB_TOPLEVEL = $(DUT)
|
||||||
|
MODULE = $(COCOTB_TEST_MODULES)
|
||||||
|
TOPLEVEL = $(COCOTB_TOPLEVEL)
|
||||||
VERILOG_SOURCES += $(DUT).v
|
VERILOG_SOURCES += $(DUT).v
|
||||||
|
|
||||||
|
# module parameters
|
||||||
|
export PARAM_PTP_TS_WIDTH := 96
|
||||||
|
export PARAM_PTP_TAG_WIDTH := 16
|
||||||
|
export PARAM_AXIS_DATA_WIDTH := 64
|
||||||
|
export PARAM_AXIS_KEEP_WIDTH := $(shell expr $(PARAM_AXIS_DATA_WIDTH) / 8 )
|
||||||
|
export PARAM_AXIS_TX_USER_WIDTH := $(shell expr $(PARAM_PTP_TAG_WIDTH) + 1 )
|
||||||
|
export PARAM_AXIS_RX_USER_WIDTH := $(shell expr $(PARAM_PTP_TS_WIDTH) + 1 )
|
||||||
|
|
||||||
ifeq ($(SIM), icarus)
|
ifeq ($(SIM), icarus)
|
||||||
PLUSARGS += -fst
|
PLUSARGS += -fst
|
||||||
|
|
||||||
ifeq ($(WAVES), 1)
|
COMPILE_ARGS += $(foreach v,$(filter PARAM_%,$(.VARIABLES)),-P $(COCOTB_TOPLEVEL).$(subst PARAM_,,$(v))=$($(v)))
|
||||||
VERILOG_SOURCES += iverilog_dump.v
|
|
||||||
COMPILE_ARGS += -s iverilog_dump
|
|
||||||
endif
|
|
||||||
else ifeq ($(SIM), verilator)
|
else ifeq ($(SIM), verilator)
|
||||||
COMPILE_ARGS += -Wno-SELRANGE -Wno-WIDTH
|
COMPILE_ARGS += $(foreach v,$(filter PARAM_%,$(.VARIABLES)),-G$(subst PARAM_,,$(v))=$($(v)))
|
||||||
|
|
||||||
ifeq ($(WAVES), 1)
|
ifeq ($(WAVES), 1)
|
||||||
COMPILE_ARGS += --trace-fst
|
COMPILE_ARGS += --trace-fst
|
||||||
|
VERILATOR_TRACE = 1
|
||||||
endif
|
endif
|
||||||
endif
|
endif
|
||||||
|
|
||||||
include $(shell cocotb-config --makefiles)/Makefile.sim
|
include $(shell cocotb-config --makefiles)/Makefile.sim
|
||||||
|
|
||||||
iverilog_dump.v:
|
|
||||||
echo 'module iverilog_dump();' > $@
|
|
||||||
echo 'initial begin' >> $@
|
|
||||||
echo ' $$dumpfile("$(TOPLEVEL).fst");' >> $@
|
|
||||||
echo ' $$dumpvars(0, $(TOPLEVEL));' >> $@
|
|
||||||
echo 'end' >> $@
|
|
||||||
echo 'endmodule' >> $@
|
|
||||||
|
|
||||||
clean::
|
|
||||||
@rm -rf iverilog_dump.v
|
|
||||||
@rm -rf dump.fst $(TOPLEVEL).fst
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
"""
|
"""
|
||||||
|
|
||||||
Copyright (c) 2021 Alex Forencich
|
Copyright (c) 2021-2025 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
|
||||||
@@ -28,6 +28,7 @@ import logging
|
|||||||
import os
|
import os
|
||||||
|
|
||||||
import cocotb_test.simulator
|
import cocotb_test.simulator
|
||||||
|
import pytest
|
||||||
|
|
||||||
import cocotb
|
import cocotb
|
||||||
from cocotb.clock import Clock
|
from cocotb.clock import Clock
|
||||||
@@ -45,8 +46,20 @@ 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.tx_clk, 6.4, units="ns").start())
|
if len(dut.tx_axis_tdata) == 8:
|
||||||
cocotb.start_soon(Clock(dut.rx_clk, 6.4, units="ns").start())
|
clk_period = 8
|
||||||
|
elif len(dut.tx_axis_tdata) == 32:
|
||||||
|
clk_period = 3.102
|
||||||
|
elif len(dut.tx_axis_tdata) == 64:
|
||||||
|
if speed == 25e9:
|
||||||
|
clk_period = 2.56
|
||||||
|
else:
|
||||||
|
clk_period = 6.206
|
||||||
|
elif len(dut.tx_axis_tdata) == 512:
|
||||||
|
clk_period = 3.102
|
||||||
|
|
||||||
|
cocotb.start_soon(Clock(dut.tx_clk, clk_period, units="ns").start())
|
||||||
|
cocotb.start_soon(Clock(dut.rx_clk, clk_period, units="ns").start())
|
||||||
|
|
||||||
self.mac = EthMac(
|
self.mac = EthMac(
|
||||||
tx_clk=dut.tx_clk,
|
tx_clk=dut.tx_clk,
|
||||||
@@ -64,12 +77,12 @@ class TB:
|
|||||||
)
|
)
|
||||||
|
|
||||||
self.tx_ptp = PtpClockSimTime(
|
self.tx_ptp = PtpClockSimTime(
|
||||||
ts_96=dut.tx_ptp_time,
|
ts_tod=dut.tx_ptp_time,
|
||||||
clock=dut.tx_clk
|
clock=dut.tx_clk
|
||||||
)
|
)
|
||||||
|
|
||||||
self.rx_ptp = PtpClockSimTime(
|
self.rx_ptp = PtpClockSimTime(
|
||||||
ts_96=dut.rx_ptp_time,
|
ts_tod=dut.rx_ptp_time,
|
||||||
clock=dut.rx_clk
|
clock=dut.rx_clk
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -155,14 +168,23 @@ def incrementing_payload(length):
|
|||||||
return bytearray(itertools.islice(itertools.cycle(range(256)), length))
|
return bytearray(itertools.islice(itertools.cycle(range(256)), length))
|
||||||
|
|
||||||
|
|
||||||
if cocotb.SIM_NAME:
|
if getattr(cocotb, 'top', None) is not None:
|
||||||
|
|
||||||
|
if len(cocotb.top.tx_axis_tdata) == 8:
|
||||||
|
speed = [100e6, 1e9]
|
||||||
|
elif len(cocotb.top.tx_axis_tdata) == 32:
|
||||||
|
speed = [10e9]
|
||||||
|
elif len(cocotb.top.tx_axis_tdata) == 64:
|
||||||
|
speed = [10e9, 25e9]
|
||||||
|
elif len(cocotb.top.tx_axis_tdata) == 512:
|
||||||
|
speed = [100e9]
|
||||||
|
|
||||||
for test in [run_test_tx, run_test_rx]:
|
for test in [run_test_tx, run_test_rx]:
|
||||||
|
|
||||||
factory = TestFactory(test)
|
factory = TestFactory(test)
|
||||||
factory.add_option("payload_lengths", [size_list])
|
factory.add_option("payload_lengths", [size_list])
|
||||||
factory.add_option("payload_data", [incrementing_payload])
|
factory.add_option("payload_data", [incrementing_payload])
|
||||||
factory.add_option("speed", [10e9, 1e9])
|
factory.add_option("speed", speed)
|
||||||
factory.generate_tests()
|
factory.generate_tests()
|
||||||
|
|
||||||
|
|
||||||
@@ -171,7 +193,8 @@ if cocotb.SIM_NAME:
|
|||||||
tests_dir = os.path.dirname(__file__)
|
tests_dir = os.path.dirname(__file__)
|
||||||
|
|
||||||
|
|
||||||
def test_eth_mac(request):
|
@pytest.mark.parametrize("data_width", [8, 32, 64, 512])
|
||||||
|
def test_eth_mac(request, data_width):
|
||||||
dut = "test_eth_mac"
|
dut = "test_eth_mac"
|
||||||
module = os.path.splitext(os.path.basename(__file__))[0]
|
module = os.path.splitext(os.path.basename(__file__))[0]
|
||||||
toplevel = dut
|
toplevel = dut
|
||||||
@@ -182,6 +205,13 @@ def test_eth_mac(request):
|
|||||||
|
|
||||||
parameters = {}
|
parameters = {}
|
||||||
|
|
||||||
|
parameters['PTP_TS_WIDTH'] = 96
|
||||||
|
parameters['PTP_TAG_WIDTH'] = 16
|
||||||
|
parameters['AXIS_DATA_WIDTH'] = data_width
|
||||||
|
parameters['AXIS_KEEP_WIDTH'] = parameters['AXIS_DATA_WIDTH'] // 8
|
||||||
|
parameters['AXIS_TX_USER_WIDTH'] = parameters['PTP_TAG_WIDTH']+1
|
||||||
|
parameters['AXIS_RX_USER_WIDTH'] = parameters['PTP_TS_WIDTH']+1
|
||||||
|
|
||||||
extra_env = {f'PARAM_{k}': str(v) for k, v in parameters.items()}
|
extra_env = {f'PARAM_{k}': str(v) for k, v in parameters.items()}
|
||||||
|
|
||||||
sim_build = os.path.join(tests_dir, "sim_build",
|
sim_build = os.path.join(tests_dir, "sim_build",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
|
|
||||||
Copyright (c) 2021 Alex Forencich
|
Copyright (c) 2021-2025 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
|
||||||
@@ -29,29 +29,37 @@ THE SOFTWARE.
|
|||||||
/*
|
/*
|
||||||
* Ethernet MAC model test
|
* Ethernet MAC model test
|
||||||
*/
|
*/
|
||||||
module test_eth_mac
|
module test_eth_mac #
|
||||||
|
(
|
||||||
|
parameter PTP_TS_WIDTH = 96,
|
||||||
|
parameter PTP_TAG_WIDTH = 16,
|
||||||
|
parameter AXIS_DATA_WIDTH = 64,
|
||||||
|
parameter AXIS_KEEP_WIDTH = (AXIS_DATA_WIDTH/8),
|
||||||
|
parameter AXIS_TX_USER_WIDTH = PTP_TAG_WIDTH+1,
|
||||||
|
parameter AXIS_RX_USER_WIDTH = PTP_TS_WIDTH+1
|
||||||
|
)
|
||||||
(
|
(
|
||||||
inout wire tx_clk,
|
inout wire tx_clk,
|
||||||
inout wire tx_rst,
|
inout wire tx_rst,
|
||||||
inout wire [63:0] tx_axis_tdata,
|
inout wire [AXIS_DATA_WIDTH-1:0] tx_axis_tdata,
|
||||||
inout wire [7:0] tx_axis_tkeep,
|
inout wire [AXIS_KEEP_WIDTH-1:0] tx_axis_tkeep,
|
||||||
inout wire tx_axis_tlast,
|
inout wire tx_axis_tlast,
|
||||||
inout wire [16:0] tx_axis_tuser,
|
inout wire [AXIS_TX_USER_WIDTH-1:0] tx_axis_tuser,
|
||||||
inout wire tx_axis_tvalid,
|
inout wire tx_axis_tvalid,
|
||||||
inout wire tx_axis_tready,
|
inout wire tx_axis_tready,
|
||||||
inout wire [95:0] tx_ptp_time,
|
inout wire [PTP_TS_WIDTH-1:0] tx_ptp_time,
|
||||||
inout wire [95:0] tx_ptp_ts,
|
inout wire [PTP_TS_WIDTH-1:0] tx_ptp_ts,
|
||||||
inout wire [15:0] tx_ptp_ts_tag,
|
inout wire [PTP_TAG_WIDTH-1:0] tx_ptp_ts_tag,
|
||||||
inout wire tx_ptp_ts_valid,
|
inout wire tx_ptp_ts_valid,
|
||||||
|
|
||||||
inout wire rx_clk,
|
inout wire rx_clk,
|
||||||
inout wire rx_rst,
|
inout wire rx_rst,
|
||||||
inout wire [63:0] rx_axis_tdata,
|
inout wire [AXIS_DATA_WIDTH-1:0] rx_axis_tdata,
|
||||||
inout wire [7:0] rx_axis_tkeep,
|
inout wire [AXIS_KEEP_WIDTH-1:0] rx_axis_tkeep,
|
||||||
inout wire rx_axis_tlast,
|
inout wire rx_axis_tlast,
|
||||||
inout wire [96:0] rx_axis_tuser,
|
inout wire [AXIS_RX_USER_WIDTH-1:0] rx_axis_tuser,
|
||||||
inout wire rx_axis_tvalid,
|
inout wire rx_axis_tvalid,
|
||||||
inout wire [95:0] rx_ptp_time
|
inout wire [PTP_TS_WIDTH-1:0] rx_ptp_time
|
||||||
);
|
);
|
||||||
|
|
||||||
endmodule
|
endmodule
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
# Copyright (c) 2020 Alex Forencich
|
# Copyright (c) 2020-2025 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
|
||||||
@@ -27,35 +27,23 @@ COCOTB_HDL_TIMEUNIT = 1ns
|
|||||||
COCOTB_HDL_TIMEPRECISION = 1ns
|
COCOTB_HDL_TIMEPRECISION = 1ns
|
||||||
|
|
||||||
DUT = test_gmii
|
DUT = test_gmii
|
||||||
TOPLEVEL = $(DUT)
|
COCOTB_TEST_MODULES = $(DUT)
|
||||||
MODULE = $(DUT)
|
COCOTB_TOPLEVEL = $(DUT)
|
||||||
|
MODULE = $(COCOTB_TEST_MODULES)
|
||||||
|
TOPLEVEL = $(COCOTB_TOPLEVEL)
|
||||||
VERILOG_SOURCES += $(DUT).v
|
VERILOG_SOURCES += $(DUT).v
|
||||||
|
|
||||||
ifeq ($(SIM), icarus)
|
ifeq ($(SIM), icarus)
|
||||||
PLUSARGS += -fst
|
PLUSARGS += -fst
|
||||||
|
|
||||||
ifeq ($(WAVES), 1)
|
COMPILE_ARGS += $(foreach v,$(filter PARAM_%,$(.VARIABLES)),-P $(COCOTB_TOPLEVEL).$(subst PARAM_,,$(v))=$($(v)))
|
||||||
VERILOG_SOURCES += iverilog_dump.v
|
|
||||||
COMPILE_ARGS += -s iverilog_dump
|
|
||||||
endif
|
|
||||||
else ifeq ($(SIM), verilator)
|
else ifeq ($(SIM), verilator)
|
||||||
COMPILE_ARGS += -Wno-SELRANGE -Wno-WIDTH
|
COMPILE_ARGS += $(foreach v,$(filter PARAM_%,$(.VARIABLES)),-G$(subst PARAM_,,$(v))=$($(v)))
|
||||||
|
|
||||||
ifeq ($(WAVES), 1)
|
ifeq ($(WAVES), 1)
|
||||||
COMPILE_ARGS += --trace-fst
|
COMPILE_ARGS += --trace-fst
|
||||||
|
VERILATOR_TRACE = 1
|
||||||
endif
|
endif
|
||||||
endif
|
endif
|
||||||
|
|
||||||
include $(shell cocotb-config --makefiles)/Makefile.sim
|
include $(shell cocotb-config --makefiles)/Makefile.sim
|
||||||
|
|
||||||
iverilog_dump.v:
|
|
||||||
echo 'module iverilog_dump();' > $@
|
|
||||||
echo 'initial begin' >> $@
|
|
||||||
echo ' $$dumpfile("$(TOPLEVEL).fst");' >> $@
|
|
||||||
echo ' $$dumpvars(0, $(TOPLEVEL));' >> $@
|
|
||||||
echo 'end' >> $@
|
|
||||||
echo 'endmodule' >> $@
|
|
||||||
|
|
||||||
clean::
|
|
||||||
@rm -rf iverilog_dump.v
|
|
||||||
@rm -rf dump.fst $(TOPLEVEL).fst
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
"""
|
"""
|
||||||
|
|
||||||
Copyright (c) 2020 Alex Forencich
|
Copyright (c) 2020-2025 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
|
||||||
@@ -70,7 +70,7 @@ class TB:
|
|||||||
|
|
||||||
def set_enable_generator(self, generator=None):
|
def set_enable_generator(self, generator=None):
|
||||||
if self._enable_cr is not None:
|
if self._enable_cr is not None:
|
||||||
self._enable_cr.kill()
|
self._enable_cr.cancel()
|
||||||
self._enable_cr = None
|
self._enable_cr = None
|
||||||
|
|
||||||
self._enable_generator = generator
|
self._enable_generator = generator
|
||||||
@@ -132,7 +132,7 @@ def cycle_en():
|
|||||||
return itertools.cycle([0, 0, 0, 1])
|
return itertools.cycle([0, 0, 0, 1])
|
||||||
|
|
||||||
|
|
||||||
if cocotb.SIM_NAME:
|
if getattr(cocotb, 'top', None) is not None:
|
||||||
|
|
||||||
factory = TestFactory(run_test)
|
factory = TestFactory(run_test)
|
||||||
factory.add_option("payload_lengths", [size_list])
|
factory.add_option("payload_lengths", [size_list])
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
|
|
||||||
Copyright (c) 2020 Alex Forencich
|
Copyright (c) 2020-2025 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
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
# Copyright (c) 2020 Alex Forencich
|
# Copyright (c) 2020-2025 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
|
||||||
@@ -27,35 +27,23 @@ COCOTB_HDL_TIMEUNIT = 1ns
|
|||||||
COCOTB_HDL_TIMEPRECISION = 1ns
|
COCOTB_HDL_TIMEPRECISION = 1ns
|
||||||
|
|
||||||
DUT = test_gmii_phy
|
DUT = test_gmii_phy
|
||||||
TOPLEVEL = $(DUT)
|
COCOTB_TEST_MODULES = $(DUT)
|
||||||
MODULE = $(DUT)
|
COCOTB_TOPLEVEL = $(DUT)
|
||||||
|
MODULE = $(COCOTB_TEST_MODULES)
|
||||||
|
TOPLEVEL = $(COCOTB_TOPLEVEL)
|
||||||
VERILOG_SOURCES += $(DUT).v
|
VERILOG_SOURCES += $(DUT).v
|
||||||
|
|
||||||
ifeq ($(SIM), icarus)
|
ifeq ($(SIM), icarus)
|
||||||
PLUSARGS += -fst
|
PLUSARGS += -fst
|
||||||
|
|
||||||
ifeq ($(WAVES), 1)
|
COMPILE_ARGS += $(foreach v,$(filter PARAM_%,$(.VARIABLES)),-P $(COCOTB_TOPLEVEL).$(subst PARAM_,,$(v))=$($(v)))
|
||||||
VERILOG_SOURCES += iverilog_dump.v
|
|
||||||
COMPILE_ARGS += -s iverilog_dump
|
|
||||||
endif
|
|
||||||
else ifeq ($(SIM), verilator)
|
else ifeq ($(SIM), verilator)
|
||||||
COMPILE_ARGS += -Wno-SELRANGE -Wno-WIDTH
|
COMPILE_ARGS += $(foreach v,$(filter PARAM_%,$(.VARIABLES)),-G$(subst PARAM_,,$(v))=$($(v)))
|
||||||
|
|
||||||
ifeq ($(WAVES), 1)
|
ifeq ($(WAVES), 1)
|
||||||
COMPILE_ARGS += --trace-fst
|
COMPILE_ARGS += --trace-fst
|
||||||
|
VERILATOR_TRACE = 1
|
||||||
endif
|
endif
|
||||||
endif
|
endif
|
||||||
|
|
||||||
include $(shell cocotb-config --makefiles)/Makefile.sim
|
include $(shell cocotb-config --makefiles)/Makefile.sim
|
||||||
|
|
||||||
iverilog_dump.v:
|
|
||||||
echo 'module iverilog_dump();' > $@
|
|
||||||
echo 'initial begin' >> $@
|
|
||||||
echo ' $$dumpfile("$(TOPLEVEL).fst");' >> $@
|
|
||||||
echo ' $$dumpvars(0, $(TOPLEVEL));' >> $@
|
|
||||||
echo 'end' >> $@
|
|
||||||
echo 'endmodule' >> $@
|
|
||||||
|
|
||||||
clean::
|
|
||||||
@rm -rf iverilog_dump.v
|
|
||||||
@rm -rf dump.fst $(TOPLEVEL).fst
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
"""
|
"""
|
||||||
|
|
||||||
Copyright (c) 2020 Alex Forencich
|
Copyright (c) 2020-2025 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
|
||||||
@@ -140,7 +140,7 @@ def cycle_en():
|
|||||||
return itertools.cycle([0, 0, 0, 1])
|
return itertools.cycle([0, 0, 0, 1])
|
||||||
|
|
||||||
|
|
||||||
if cocotb.SIM_NAME:
|
if getattr(cocotb, 'top', None) is not None:
|
||||||
|
|
||||||
for test in [run_test_tx, run_test_rx]:
|
for test in [run_test_tx, run_test_rx]:
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
|
|
||||||
Copyright (c) 2020 Alex Forencich
|
Copyright (c) 2020-2025 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
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
# Copyright (c) 2020 Alex Forencich
|
# Copyright (c) 2020-2025 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
|
||||||
@@ -27,35 +27,23 @@ COCOTB_HDL_TIMEUNIT = 1ns
|
|||||||
COCOTB_HDL_TIMEPRECISION = 1ns
|
COCOTB_HDL_TIMEPRECISION = 1ns
|
||||||
|
|
||||||
DUT = test_mii
|
DUT = test_mii
|
||||||
TOPLEVEL = $(DUT)
|
COCOTB_TEST_MODULES = $(DUT)
|
||||||
MODULE = $(DUT)
|
COCOTB_TOPLEVEL = $(DUT)
|
||||||
|
MODULE = $(COCOTB_TEST_MODULES)
|
||||||
|
TOPLEVEL = $(COCOTB_TOPLEVEL)
|
||||||
VERILOG_SOURCES += $(DUT).v
|
VERILOG_SOURCES += $(DUT).v
|
||||||
|
|
||||||
ifeq ($(SIM), icarus)
|
ifeq ($(SIM), icarus)
|
||||||
PLUSARGS += -fst
|
PLUSARGS += -fst
|
||||||
|
|
||||||
ifeq ($(WAVES), 1)
|
COMPILE_ARGS += $(foreach v,$(filter PARAM_%,$(.VARIABLES)),-P $(COCOTB_TOPLEVEL).$(subst PARAM_,,$(v))=$($(v)))
|
||||||
VERILOG_SOURCES += iverilog_dump.v
|
|
||||||
COMPILE_ARGS += -s iverilog_dump
|
|
||||||
endif
|
|
||||||
else ifeq ($(SIM), verilator)
|
else ifeq ($(SIM), verilator)
|
||||||
COMPILE_ARGS += -Wno-SELRANGE -Wno-WIDTH
|
COMPILE_ARGS += $(foreach v,$(filter PARAM_%,$(.VARIABLES)),-G$(subst PARAM_,,$(v))=$($(v)))
|
||||||
|
|
||||||
ifeq ($(WAVES), 1)
|
ifeq ($(WAVES), 1)
|
||||||
COMPILE_ARGS += --trace-fst
|
COMPILE_ARGS += --trace-fst
|
||||||
|
VERILATOR_TRACE = 1
|
||||||
endif
|
endif
|
||||||
endif
|
endif
|
||||||
|
|
||||||
include $(shell cocotb-config --makefiles)/Makefile.sim
|
include $(shell cocotb-config --makefiles)/Makefile.sim
|
||||||
|
|
||||||
iverilog_dump.v:
|
|
||||||
echo 'module iverilog_dump();' > $@
|
|
||||||
echo 'initial begin' >> $@
|
|
||||||
echo ' $$dumpfile("$(TOPLEVEL).fst");' >> $@
|
|
||||||
echo ' $$dumpvars(0, $(TOPLEVEL));' >> $@
|
|
||||||
echo 'end' >> $@
|
|
||||||
echo 'endmodule' >> $@
|
|
||||||
|
|
||||||
clean::
|
|
||||||
@rm -rf iverilog_dump.v
|
|
||||||
@rm -rf dump.fst $(TOPLEVEL).fst
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
"""
|
"""
|
||||||
|
|
||||||
Copyright (c) 2020 Alex Forencich
|
Copyright (c) 2020-2025 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
|
||||||
@@ -69,7 +69,7 @@ class TB:
|
|||||||
|
|
||||||
def set_enable_generator(self, generator=None):
|
def set_enable_generator(self, generator=None):
|
||||||
if self._enable_cr is not None:
|
if self._enable_cr is not None:
|
||||||
self._enable_cr.kill()
|
self._enable_cr.cancel()
|
||||||
self._enable_cr = None
|
self._enable_cr = None
|
||||||
|
|
||||||
self._enable_generator = generator
|
self._enable_generator = generator
|
||||||
@@ -130,7 +130,7 @@ def cycle_en():
|
|||||||
return itertools.cycle([0, 0, 0, 1])
|
return itertools.cycle([0, 0, 0, 1])
|
||||||
|
|
||||||
|
|
||||||
if cocotb.SIM_NAME:
|
if getattr(cocotb, 'top', None) is not None:
|
||||||
|
|
||||||
factory = TestFactory(run_test)
|
factory = TestFactory(run_test)
|
||||||
factory.add_option("payload_lengths", [size_list])
|
factory.add_option("payload_lengths", [size_list])
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
|
|
||||||
Copyright (c) 2020 Alex Forencich
|
Copyright (c) 2020-2025 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
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
# Copyright (c) 2020 Alex Forencich
|
# Copyright (c) 2020 Alex-2025 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
|
||||||
@@ -27,35 +27,23 @@ COCOTB_HDL_TIMEUNIT = 1ns
|
|||||||
COCOTB_HDL_TIMEPRECISION = 1ns
|
COCOTB_HDL_TIMEPRECISION = 1ns
|
||||||
|
|
||||||
DUT = test_mii_phy
|
DUT = test_mii_phy
|
||||||
TOPLEVEL = $(DUT)
|
COCOTB_TEST_MODULES = $(DUT)
|
||||||
MODULE = $(DUT)
|
COCOTB_TOPLEVEL = $(DUT)
|
||||||
|
MODULE = $(COCOTB_TEST_MODULES)
|
||||||
|
TOPLEVEL = $(COCOTB_TOPLEVEL)
|
||||||
VERILOG_SOURCES += $(DUT).v
|
VERILOG_SOURCES += $(DUT).v
|
||||||
|
|
||||||
ifeq ($(SIM), icarus)
|
ifeq ($(SIM), icarus)
|
||||||
PLUSARGS += -fst
|
PLUSARGS += -fst
|
||||||
|
|
||||||
ifeq ($(WAVES), 1)
|
COMPILE_ARGS += $(foreach v,$(filter PARAM_%,$(.VARIABLES)),-P $(COCOTB_TOPLEVEL).$(subst PARAM_,,$(v))=$($(v)))
|
||||||
VERILOG_SOURCES += iverilog_dump.v
|
|
||||||
COMPILE_ARGS += -s iverilog_dump
|
|
||||||
endif
|
|
||||||
else ifeq ($(SIM), verilator)
|
else ifeq ($(SIM), verilator)
|
||||||
COMPILE_ARGS += -Wno-SELRANGE -Wno-WIDTH
|
COMPILE_ARGS += $(foreach v,$(filter PARAM_%,$(.VARIABLES)),-G$(subst PARAM_,,$(v))=$($(v)))
|
||||||
|
|
||||||
ifeq ($(WAVES), 1)
|
ifeq ($(WAVES), 1)
|
||||||
COMPILE_ARGS += --trace-fst
|
COMPILE_ARGS += --trace-fst
|
||||||
|
VERILATOR_TRACE = 1
|
||||||
endif
|
endif
|
||||||
endif
|
endif
|
||||||
|
|
||||||
include $(shell cocotb-config --makefiles)/Makefile.sim
|
include $(shell cocotb-config --makefiles)/Makefile.sim
|
||||||
|
|
||||||
iverilog_dump.v:
|
|
||||||
echo 'module iverilog_dump();' > $@
|
|
||||||
echo 'initial begin' >> $@
|
|
||||||
echo ' $$dumpfile("$(TOPLEVEL).fst");' >> $@
|
|
||||||
echo ' $$dumpvars(0, $(TOPLEVEL));' >> $@
|
|
||||||
echo 'end' >> $@
|
|
||||||
echo 'endmodule' >> $@
|
|
||||||
|
|
||||||
clean::
|
|
||||||
@rm -rf iverilog_dump.v
|
|
||||||
@rm -rf dump.fst $(TOPLEVEL).fst
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
"""
|
"""
|
||||||
|
|
||||||
Copyright (c) 2020 Alex Forencich
|
Copyright (c) 2020-2025 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
|
||||||
@@ -131,7 +131,7 @@ def cycle_en():
|
|||||||
return itertools.cycle([0, 0, 0, 1])
|
return itertools.cycle([0, 0, 0, 1])
|
||||||
|
|
||||||
|
|
||||||
if cocotb.SIM_NAME:
|
if getattr(cocotb, 'top', None) is not None:
|
||||||
|
|
||||||
for test in [run_test_tx, run_test_rx]:
|
for test in [run_test_tx, run_test_rx]:
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
|
|
||||||
Copyright (c) 2020 Alex Forencich
|
Copyright (c) 2020-2025 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
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
# Copyright (c) 2020 Alex Forencich
|
# Copyright (c) 2020-2025 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
|
||||||
@@ -27,35 +27,23 @@ COCOTB_HDL_TIMEUNIT = 1ns
|
|||||||
COCOTB_HDL_TIMEPRECISION = 1ps
|
COCOTB_HDL_TIMEPRECISION = 1ps
|
||||||
|
|
||||||
DUT = test_ptp_clock
|
DUT = test_ptp_clock
|
||||||
TOPLEVEL = $(DUT)
|
COCOTB_TEST_MODULES = $(DUT)
|
||||||
MODULE = $(DUT)
|
COCOTB_TOPLEVEL = $(DUT)
|
||||||
|
MODULE = $(COCOTB_TEST_MODULES)
|
||||||
|
TOPLEVEL = $(COCOTB_TOPLEVEL)
|
||||||
VERILOG_SOURCES += $(DUT).v
|
VERILOG_SOURCES += $(DUT).v
|
||||||
|
|
||||||
ifeq ($(SIM), icarus)
|
ifeq ($(SIM), icarus)
|
||||||
PLUSARGS += -fst
|
PLUSARGS += -fst
|
||||||
|
|
||||||
ifeq ($(WAVES), 1)
|
COMPILE_ARGS += $(foreach v,$(filter PARAM_%,$(.VARIABLES)),-P $(COCOTB_TOPLEVEL).$(subst PARAM_,,$(v))=$($(v)))
|
||||||
VERILOG_SOURCES += iverilog_dump.v
|
|
||||||
COMPILE_ARGS += -s iverilog_dump
|
|
||||||
endif
|
|
||||||
else ifeq ($(SIM), verilator)
|
else ifeq ($(SIM), verilator)
|
||||||
COMPILE_ARGS += -Wno-SELRANGE -Wno-WIDTH
|
COMPILE_ARGS += $(foreach v,$(filter PARAM_%,$(.VARIABLES)),-G$(subst PARAM_,,$(v))=$($(v)))
|
||||||
|
|
||||||
ifeq ($(WAVES), 1)
|
ifeq ($(WAVES), 1)
|
||||||
COMPILE_ARGS += --trace-fst
|
COMPILE_ARGS += --trace-fst
|
||||||
|
VERILATOR_TRACE = 1
|
||||||
endif
|
endif
|
||||||
endif
|
endif
|
||||||
|
|
||||||
include $(shell cocotb-config --makefiles)/Makefile.sim
|
include $(shell cocotb-config --makefiles)/Makefile.sim
|
||||||
|
|
||||||
iverilog_dump.v:
|
|
||||||
echo 'module iverilog_dump();' > $@
|
|
||||||
echo 'initial begin' >> $@
|
|
||||||
echo ' $$dumpfile("$(TOPLEVEL).fst");' >> $@
|
|
||||||
echo ' $$dumpvars(0, $(TOPLEVEL));' >> $@
|
|
||||||
echo 'end' >> $@
|
|
||||||
echo 'endmodule' >> $@
|
|
||||||
|
|
||||||
clean::
|
|
||||||
@rm -rf iverilog_dump.v
|
|
||||||
@rm -rf dump.fst $(TOPLEVEL).fst
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
"""
|
"""
|
||||||
|
|
||||||
Copyright (c) 2020 Alex Forencich
|
Copyright (c) 2020-2025 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 decimal import Decimal
|
||||||
|
|
||||||
import cocotb_test.simulator
|
import cocotb_test.simulator
|
||||||
|
|
||||||
@@ -46,8 +47,8 @@ class TB:
|
|||||||
cocotb.start_soon(Clock(dut.clk, 6.4, units="ns").start())
|
cocotb.start_soon(Clock(dut.clk, 6.4, units="ns").start())
|
||||||
|
|
||||||
self.ptp_clock = PtpClock(
|
self.ptp_clock = PtpClock(
|
||||||
ts_96=dut.ts_96,
|
ts_tod=dut.ts_tod,
|
||||||
ts_64=dut.ts_64,
|
ts_rel=dut.ts_rel,
|
||||||
ts_step=dut.ts_step,
|
ts_step=dut.ts_step,
|
||||||
pps=dut.pps,
|
pps=dut.pps,
|
||||||
clock=dut.clk,
|
clock=dut.clk,
|
||||||
@@ -66,6 +67,14 @@ class TB:
|
|||||||
await RisingEdge(self.dut.clk)
|
await RisingEdge(self.dut.clk)
|
||||||
await RisingEdge(self.dut.clk)
|
await RisingEdge(self.dut.clk)
|
||||||
|
|
||||||
|
def get_ts_tod_ns(self):
|
||||||
|
ts = int(self.dut.ts_tod.value)
|
||||||
|
return Decimal(ts >> 48).scaleb(9) + (Decimal(ts & 0xffffffffffff) / Decimal(2**16))
|
||||||
|
|
||||||
|
def get_ts_rel_ns(self):
|
||||||
|
ts = int(self.dut.ts_rel.value)
|
||||||
|
return Decimal(ts) / Decimal(2**16)
|
||||||
|
|
||||||
|
|
||||||
@cocotb.test()
|
@cocotb.test()
|
||||||
async def run_default_rate(dut):
|
async def run_default_rate(dut):
|
||||||
@@ -75,31 +84,32 @@ async def run_default_rate(dut):
|
|||||||
await tb.reset()
|
await tb.reset()
|
||||||
|
|
||||||
await RisingEdge(dut.clk)
|
await RisingEdge(dut.clk)
|
||||||
start_time = get_sim_time('sec')
|
start_time = Decimal(get_sim_time('fs')).scaleb(-6)
|
||||||
start_ts_96 = (dut.ts_96.value.integer >> 48) + ((dut.ts_96.value.integer & 0xffffffffffff)/2**16*1e-9)
|
start_ts_tod = tb.get_ts_tod_ns()
|
||||||
start_ts_64 = dut.ts_64.value.integer/2**16*1e-9
|
start_ts_rel = tb.get_ts_rel_ns()
|
||||||
|
|
||||||
await ClockCycles(dut.clk, 10000)
|
await ClockCycles(dut.clk, 10000)
|
||||||
|
|
||||||
stop_time = get_sim_time('sec')
|
stop_time = Decimal(get_sim_time('fs')).scaleb(-6)
|
||||||
stop_ts_96 = (dut.ts_96.value.integer >> 48) + ((dut.ts_96.value.integer & 0xffffffffffff)/2**16*1e-9)
|
stop_ts_tod = tb.get_ts_tod_ns()
|
||||||
stop_ts_64 = dut.ts_64.value.integer/2**16*1e-9
|
stop_ts_rel = tb.get_ts_rel_ns()
|
||||||
|
|
||||||
time_delta = stop_time-start_time
|
time_delta = stop_time-start_time
|
||||||
ts_96_delta = stop_ts_96-start_ts_96
|
ts_tod_delta = stop_ts_tod-start_ts_tod
|
||||||
ts_64_delta = stop_ts_64-start_ts_64
|
ts_rel_delta = stop_ts_rel-start_ts_rel
|
||||||
|
|
||||||
ts_96_diff = time_delta - ts_96_delta
|
tb.log.info("sim time delta : %s ns", time_delta)
|
||||||
ts_64_diff = time_delta - ts_64_delta
|
tb.log.info("ToD ts delta : %s ns", ts_tod_delta)
|
||||||
|
tb.log.info("rel ts delta : %s ns", ts_rel_delta)
|
||||||
|
|
||||||
tb.log.info("sim time delta : %g s", time_delta)
|
ts_tod_diff = time_delta - ts_tod_delta
|
||||||
tb.log.info("96 bit ts delta : %g s", ts_96_delta)
|
ts_rel_diff = time_delta - ts_rel_delta
|
||||||
tb.log.info("64 bit ts delta : %g s", ts_64_delta)
|
|
||||||
tb.log.info("96 bit ts diff : %g s", ts_96_diff)
|
|
||||||
tb.log.info("64 bit ts diff : %g s", ts_64_diff)
|
|
||||||
|
|
||||||
assert abs(ts_96_diff) < 1e-12
|
tb.log.info("ToD ts diff : %s ns", ts_tod_diff)
|
||||||
assert abs(ts_64_diff) < 1e-12
|
tb.log.info("rel ts diff : %s ns", ts_rel_diff)
|
||||||
|
|
||||||
|
assert abs(ts_tod_diff) < 1e-3
|
||||||
|
assert abs(ts_rel_diff) < 1e-3
|
||||||
|
|
||||||
await RisingEdge(dut.clk)
|
await RisingEdge(dut.clk)
|
||||||
await RisingEdge(dut.clk)
|
await RisingEdge(dut.clk)
|
||||||
@@ -112,40 +122,41 @@ async def run_load_timestamps(dut):
|
|||||||
|
|
||||||
await tb.reset()
|
await tb.reset()
|
||||||
|
|
||||||
tb.ptp_clock.set_ts_96(12345678)
|
tb.ptp_clock.set_ts_tod_ns(12345678)
|
||||||
tb.ptp_clock.set_ts_64(12345678)
|
tb.ptp_clock.set_ts_rel_ns(12345678)
|
||||||
|
|
||||||
await RisingEdge(dut.clk)
|
await RisingEdge(dut.clk)
|
||||||
|
|
||||||
assert dut.ts_96.value.integer == 12345678+((tb.ptp_clock.period_ns << 16) + tb.ptp_clock.period_fns)
|
assert int(dut.ts_tod.value) == (12345678 << 16) + (tb.ptp_clock.period_ns << 16) + (tb.ptp_clock.period_fns >> 16)
|
||||||
assert dut.ts_64.value.integer == 12345678+((tb.ptp_clock.period_ns << 16) + tb.ptp_clock.period_fns)
|
assert int(dut.ts_rel.value) == (12345678 << 16) + (tb.ptp_clock.period_ns << 16) + (tb.ptp_clock.period_fns >> 16)
|
||||||
assert dut.ts_step.value.integer == 1
|
assert int(dut.ts_step.value) == 1
|
||||||
|
|
||||||
start_time = get_sim_time('sec')
|
start_time = Decimal(get_sim_time('fs')).scaleb(-6)
|
||||||
start_ts_96 = (dut.ts_96.value.integer >> 48) + ((dut.ts_96.value.integer & 0xffffffffffff)/2**16*1e-9)
|
start_ts_tod = tb.get_ts_tod_ns()
|
||||||
start_ts_64 = dut.ts_64.value.integer/2**16*1e-9
|
start_ts_rel = tb.get_ts_rel_ns()
|
||||||
|
|
||||||
await ClockCycles(dut.clk, 2000)
|
await ClockCycles(dut.clk, 2000)
|
||||||
|
|
||||||
stop_time = get_sim_time('sec')
|
stop_time = Decimal(get_sim_time('fs')).scaleb(-6)
|
||||||
stop_ts_96 = (dut.ts_96.value.integer >> 48) + ((dut.ts_96.value.integer & 0xffffffffffff)/2**16*1e-9)
|
stop_ts_tod = tb.get_ts_tod_ns()
|
||||||
stop_ts_64 = dut.ts_64.value.integer/2**16*1e-9
|
stop_ts_rel = tb.get_ts_rel_ns()
|
||||||
|
|
||||||
time_delta = stop_time-start_time
|
time_delta = stop_time-start_time
|
||||||
ts_96_delta = stop_ts_96-start_ts_96
|
ts_tod_delta = stop_ts_tod-start_ts_tod
|
||||||
ts_64_delta = stop_ts_64-start_ts_64
|
ts_rel_delta = stop_ts_rel-start_ts_rel
|
||||||
|
|
||||||
ts_96_diff = time_delta - ts_96_delta
|
tb.log.info("sim time delta : %s ns", time_delta)
|
||||||
ts_64_diff = time_delta - ts_64_delta
|
tb.log.info("ToD ts delta : %s ns", ts_tod_delta)
|
||||||
|
tb.log.info("rel ts delta : %s ns", ts_rel_delta)
|
||||||
|
|
||||||
tb.log.info("sim time delta : %g s", time_delta)
|
ts_tod_diff = time_delta - ts_tod_delta
|
||||||
tb.log.info("96 bit ts delta : %g s", ts_96_delta)
|
ts_rel_diff = time_delta - ts_rel_delta
|
||||||
tb.log.info("64 bit ts delta : %g s", ts_64_delta)
|
|
||||||
tb.log.info("96 bit ts diff : %g s", ts_96_diff)
|
|
||||||
tb.log.info("64 bit ts diff : %g s", ts_64_diff)
|
|
||||||
|
|
||||||
assert abs(ts_96_diff) < 1e-12
|
tb.log.info("ToD ts diff : %s ns", ts_tod_diff)
|
||||||
assert abs(ts_64_diff) < 1e-12
|
tb.log.info("rel ts diff : %s ns", ts_rel_diff)
|
||||||
|
|
||||||
|
assert abs(ts_tod_diff) < 1e-3
|
||||||
|
assert abs(ts_rel_diff) < 1e-3
|
||||||
|
|
||||||
await RisingEdge(dut.clk)
|
await RisingEdge(dut.clk)
|
||||||
await RisingEdge(dut.clk)
|
await RisingEdge(dut.clk)
|
||||||
@@ -158,47 +169,48 @@ async def run_seconds_increment(dut):
|
|||||||
|
|
||||||
await tb.reset()
|
await tb.reset()
|
||||||
|
|
||||||
tb.ptp_clock.set_ts_96(999990000*2**16)
|
tb.ptp_clock.set_ts_tod_ns(999990000)
|
||||||
tb.ptp_clock.set_ts_64(999990000*2**16)
|
tb.ptp_clock.set_ts_rel_ns(999990000)
|
||||||
|
|
||||||
await RisingEdge(dut.clk)
|
await RisingEdge(dut.clk)
|
||||||
await RisingEdge(dut.clk)
|
await RisingEdge(dut.clk)
|
||||||
|
|
||||||
start_time = get_sim_time('sec')
|
start_time = Decimal(get_sim_time('fs')).scaleb(-6)
|
||||||
start_ts_96 = (dut.ts_96.value.integer >> 48) + ((dut.ts_96.value.integer & 0xffffffffffff)/2**16*1e-9)
|
start_ts_tod = tb.get_ts_tod_ns()
|
||||||
start_ts_64 = dut.ts_64.value.integer/2**16*1e-9
|
start_ts_rel = tb.get_ts_rel_ns()
|
||||||
|
|
||||||
saw_pps = False
|
saw_pps = False
|
||||||
|
|
||||||
for k in range(3000):
|
for k in range(3000):
|
||||||
await RisingEdge(dut.clk)
|
await RisingEdge(dut.clk)
|
||||||
|
|
||||||
if dut.pps.value.integer:
|
if int(dut.pps.value):
|
||||||
saw_pps = True
|
saw_pps = True
|
||||||
assert dut.ts_96.value.integer >> 48 == 1
|
assert int(dut.ts_tod.value) >> 48 == 1
|
||||||
assert dut.ts_96.value.integer & 0xffffffffffff < 10*2**16
|
assert int(dut.ts_tod.value) & 0xffffffffffff < 10*2**16
|
||||||
|
|
||||||
assert saw_pps
|
assert saw_pps
|
||||||
|
|
||||||
stop_time = get_sim_time('sec')
|
stop_time = Decimal(get_sim_time('fs')).scaleb(-6)
|
||||||
stop_ts_96 = (dut.ts_96.value.integer >> 48) + ((dut.ts_96.value.integer & 0xffffffffffff)/2**16*1e-9)
|
stop_ts_tod = tb.get_ts_tod_ns()
|
||||||
stop_ts_64 = dut.ts_64.value.integer/2**16*1e-9
|
stop_ts_rel = tb.get_ts_rel_ns()
|
||||||
|
|
||||||
time_delta = stop_time-start_time
|
time_delta = stop_time-start_time
|
||||||
ts_96_delta = stop_ts_96-start_ts_96
|
ts_tod_delta = stop_ts_tod-start_ts_tod
|
||||||
ts_64_delta = stop_ts_64-start_ts_64
|
ts_rel_delta = stop_ts_rel-start_ts_rel
|
||||||
|
|
||||||
ts_96_diff = time_delta - ts_96_delta
|
tb.log.info("sim time delta : %s ns", time_delta)
|
||||||
ts_64_diff = time_delta - ts_64_delta
|
tb.log.info("ToD ts delta : %s ns", ts_tod_delta)
|
||||||
|
tb.log.info("rel ts delta : %s ns", ts_rel_delta)
|
||||||
|
|
||||||
tb.log.info("sim time delta : %g s", time_delta)
|
ts_tod_diff = time_delta - ts_tod_delta
|
||||||
tb.log.info("96 bit ts delta : %g s", ts_96_delta)
|
ts_rel_diff = time_delta - ts_rel_delta
|
||||||
tb.log.info("64 bit ts delta : %g s", ts_64_delta)
|
|
||||||
tb.log.info("96 bit ts diff : %g s", ts_96_diff)
|
|
||||||
tb.log.info("64 bit ts diff : %g s", ts_64_diff)
|
|
||||||
|
|
||||||
assert abs(ts_96_diff) < 1e-12
|
tb.log.info("ToD ts diff : %s ns", ts_tod_diff)
|
||||||
assert abs(ts_64_diff) < 1e-12
|
tb.log.info("rel ts diff : %s ns", ts_rel_diff)
|
||||||
|
|
||||||
|
assert abs(ts_tod_diff) < 1e-3
|
||||||
|
assert abs(ts_rel_diff) < 1e-3
|
||||||
|
|
||||||
await RisingEdge(dut.clk)
|
await RisingEdge(dut.clk)
|
||||||
await RisingEdge(dut.clk)
|
await RisingEdge(dut.clk)
|
||||||
@@ -211,35 +223,35 @@ async def run_frequency_adjustment(dut):
|
|||||||
|
|
||||||
await tb.reset()
|
await tb.reset()
|
||||||
|
|
||||||
tb.ptp_clock.period_ns = 0x6
|
tb.ptp_clock.set_period(0x6, 0x66240000)
|
||||||
tb.ptp_clock.period_fns = 0x6624
|
|
||||||
|
|
||||||
await RisingEdge(dut.clk)
|
await RisingEdge(dut.clk)
|
||||||
start_time = get_sim_time('sec')
|
start_time = Decimal(get_sim_time('fs')).scaleb(-6)
|
||||||
start_ts_96 = (dut.ts_96.value.integer >> 48) + ((dut.ts_96.value.integer & 0xffffffffffff)/2**16*1e-9)
|
start_ts_tod = tb.get_ts_tod_ns()
|
||||||
start_ts_64 = dut.ts_64.value.integer/2**16*1e-9
|
start_ts_rel = tb.get_ts_rel_ns()
|
||||||
|
|
||||||
await ClockCycles(dut.clk, 10000)
|
await ClockCycles(dut.clk, 10000)
|
||||||
|
|
||||||
stop_time = get_sim_time('sec')
|
stop_time = Decimal(get_sim_time('fs')).scaleb(-6)
|
||||||
stop_ts_96 = (dut.ts_96.value.integer >> 48) + ((dut.ts_96.value.integer & 0xffffffffffff)/2**16*1e-9)
|
stop_ts_tod = tb.get_ts_tod_ns()
|
||||||
stop_ts_64 = dut.ts_64.value.integer/2**16*1e-9
|
stop_ts_rel = tb.get_ts_rel_ns()
|
||||||
|
|
||||||
time_delta = stop_time-start_time
|
time_delta = stop_time-start_time
|
||||||
ts_96_delta = stop_ts_96-start_ts_96
|
ts_tod_delta = stop_ts_tod-start_ts_tod
|
||||||
ts_64_delta = stop_ts_64-start_ts_64
|
ts_rel_delta = stop_ts_rel-start_ts_rel
|
||||||
|
|
||||||
ts_96_diff = time_delta - ts_96_delta * 6.4/(6+(0x6624+2/5)/2**16)
|
tb.log.info("sim time delta : %s ns", time_delta)
|
||||||
ts_64_diff = time_delta - ts_64_delta * 6.4/(6+(0x6624+2/5)/2**16)
|
tb.log.info("ToD ts delta : %s ns", ts_tod_delta)
|
||||||
|
tb.log.info("rel ts delta : %s ns", ts_rel_delta)
|
||||||
|
|
||||||
tb.log.info("sim time delta : %g s", time_delta)
|
ts_tod_diff = time_delta - ts_tod_delta * Decimal(6.4)/tb.ptp_clock.get_period_ns()
|
||||||
tb.log.info("96 bit ts delta : %g s", ts_96_delta)
|
ts_rel_diff = time_delta - ts_rel_delta * Decimal(6.4)/tb.ptp_clock.get_period_ns()
|
||||||
tb.log.info("64 bit ts delta : %g s", ts_64_delta)
|
|
||||||
tb.log.info("96 bit ts diff : %g s", ts_96_diff)
|
|
||||||
tb.log.info("64 bit ts diff : %g s", ts_64_diff)
|
|
||||||
|
|
||||||
assert abs(ts_96_diff) < 1e-12
|
tb.log.info("ToD ts diff : %s ns", ts_tod_diff)
|
||||||
assert abs(ts_64_diff) < 1e-12
|
tb.log.info("rel ts diff : %s ns", ts_rel_diff)
|
||||||
|
|
||||||
|
assert abs(ts_tod_diff) < 1e-3
|
||||||
|
assert abs(ts_rel_diff) < 1e-3
|
||||||
|
|
||||||
await RisingEdge(dut.clk)
|
await RisingEdge(dut.clk)
|
||||||
await RisingEdge(dut.clk)
|
await RisingEdge(dut.clk)
|
||||||
@@ -252,36 +264,35 @@ async def run_drift_adjustment(dut):
|
|||||||
|
|
||||||
await tb.reset()
|
await tb.reset()
|
||||||
|
|
||||||
tb.ptp_clock.drift_ns = 0
|
tb.ptp_clock.set_drift(20000, 5)
|
||||||
tb.ptp_clock.drift_fns = 20
|
|
||||||
tb.ptp_clock.drift_rate = 5
|
|
||||||
|
|
||||||
await RisingEdge(dut.clk)
|
await RisingEdge(dut.clk)
|
||||||
start_time = get_sim_time('sec')
|
start_time = Decimal(get_sim_time('fs')).scaleb(-6)
|
||||||
start_ts_96 = (dut.ts_96.value.integer >> 48) + ((dut.ts_96.value.integer & 0xffffffffffff)/2**16*1e-9)
|
start_ts_tod = tb.get_ts_tod_ns()
|
||||||
start_ts_64 = dut.ts_64.value.integer/2**16*1e-9
|
start_ts_rel = tb.get_ts_rel_ns()
|
||||||
|
|
||||||
await ClockCycles(dut.clk, 10000)
|
await ClockCycles(dut.clk, 10000)
|
||||||
|
|
||||||
stop_time = get_sim_time('sec')
|
stop_time = Decimal(get_sim_time('fs')).scaleb(-6)
|
||||||
stop_ts_96 = (dut.ts_96.value.integer >> 48) + ((dut.ts_96.value.integer & 0xffffffffffff)/2**16*1e-9)
|
stop_ts_tod = tb.get_ts_tod_ns()
|
||||||
stop_ts_64 = dut.ts_64.value.integer/2**16*1e-9
|
stop_ts_rel = tb.get_ts_rel_ns()
|
||||||
|
|
||||||
time_delta = stop_time-start_time
|
time_delta = stop_time-start_time
|
||||||
ts_96_delta = stop_ts_96-start_ts_96
|
ts_tod_delta = stop_ts_tod-start_ts_tod
|
||||||
ts_64_delta = stop_ts_64-start_ts_64
|
ts_rel_delta = stop_ts_rel-start_ts_rel
|
||||||
|
|
||||||
ts_96_diff = time_delta - ts_96_delta * 6.4/(6+(0x6666+20/5)/2**16)
|
tb.log.info("sim time delta : %s ns", time_delta)
|
||||||
ts_64_diff = time_delta - ts_64_delta * 6.4/(6+(0x6666+20/5)/2**16)
|
tb.log.info("ToD ts delta : %s ns", ts_tod_delta)
|
||||||
|
tb.log.info("rel ts delta : %s ns", ts_rel_delta)
|
||||||
|
|
||||||
tb.log.info("sim time delta : %g s", time_delta)
|
ts_tod_diff = time_delta - ts_tod_delta * Decimal(6.4)/tb.ptp_clock.get_period_ns()
|
||||||
tb.log.info("96 bit ts delta : %g s", ts_96_delta)
|
ts_rel_diff = time_delta - ts_rel_delta * Decimal(6.4)/tb.ptp_clock.get_period_ns()
|
||||||
tb.log.info("64 bit ts delta : %g s", ts_64_delta)
|
|
||||||
tb.log.info("96 bit ts diff : %g s", ts_96_diff)
|
|
||||||
tb.log.info("64 bit ts diff : %g s", ts_64_diff)
|
|
||||||
|
|
||||||
assert abs(ts_96_diff) < 1e-12
|
tb.log.info("ToD ts diff : %s ns", ts_tod_diff)
|
||||||
assert abs(ts_64_diff) < 1e-12
|
tb.log.info("rel ts diff : %s ns", ts_rel_diff)
|
||||||
|
|
||||||
|
assert abs(ts_tod_diff) < 1e-3
|
||||||
|
assert abs(ts_rel_diff) < 1e-3
|
||||||
|
|
||||||
await RisingEdge(dut.clk)
|
await RisingEdge(dut.clk)
|
||||||
await RisingEdge(dut.clk)
|
await RisingEdge(dut.clk)
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
|
|
||||||
Copyright (c) 2020 Alex Forencich
|
Copyright (c) 2020-2025 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
|
||||||
@@ -34,8 +34,8 @@ module test_ptp_clock
|
|||||||
input wire clk,
|
input wire clk,
|
||||||
input wire rst,
|
input wire rst,
|
||||||
|
|
||||||
inout wire [95:0] ts_96,
|
inout wire [95:0] ts_tod,
|
||||||
inout wire [63:0] ts_64,
|
inout wire [63:0] ts_rel,
|
||||||
inout wire ts_step,
|
inout wire ts_step,
|
||||||
inout wire pps
|
inout wire pps
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
# Copyright (c) 2020 Alex Forencich
|
# Copyright (c) 2020-2025 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
|
||||||
@@ -27,35 +27,23 @@ COCOTB_HDL_TIMEUNIT = 1ns
|
|||||||
COCOTB_HDL_TIMEPRECISION = 1ps
|
COCOTB_HDL_TIMEPRECISION = 1ps
|
||||||
|
|
||||||
DUT = test_ptp_clock_sim_time
|
DUT = test_ptp_clock_sim_time
|
||||||
TOPLEVEL = $(DUT)
|
COCOTB_TEST_MODULES = $(DUT)
|
||||||
MODULE = $(DUT)
|
COCOTB_TOPLEVEL = $(DUT)
|
||||||
|
MODULE = $(COCOTB_TEST_MODULES)
|
||||||
|
TOPLEVEL = $(COCOTB_TOPLEVEL)
|
||||||
VERILOG_SOURCES += $(DUT).v
|
VERILOG_SOURCES += $(DUT).v
|
||||||
|
|
||||||
ifeq ($(SIM), icarus)
|
ifeq ($(SIM), icarus)
|
||||||
PLUSARGS += -fst
|
PLUSARGS += -fst
|
||||||
|
|
||||||
ifeq ($(WAVES), 1)
|
COMPILE_ARGS += $(foreach v,$(filter PARAM_%,$(.VARIABLES)),-P $(COCOTB_TOPLEVEL).$(subst PARAM_,,$(v))=$($(v)))
|
||||||
VERILOG_SOURCES += iverilog_dump.v
|
|
||||||
COMPILE_ARGS += -s iverilog_dump
|
|
||||||
endif
|
|
||||||
else ifeq ($(SIM), verilator)
|
else ifeq ($(SIM), verilator)
|
||||||
COMPILE_ARGS += -Wno-SELRANGE -Wno-WIDTH
|
COMPILE_ARGS += $(foreach v,$(filter PARAM_%,$(.VARIABLES)),-G$(subst PARAM_,,$(v))=$($(v)))
|
||||||
|
|
||||||
ifeq ($(WAVES), 1)
|
ifeq ($(WAVES), 1)
|
||||||
COMPILE_ARGS += --trace-fst
|
COMPILE_ARGS += --trace-fst
|
||||||
|
VERILATOR_TRACE = 1
|
||||||
endif
|
endif
|
||||||
endif
|
endif
|
||||||
|
|
||||||
include $(shell cocotb-config --makefiles)/Makefile.sim
|
include $(shell cocotb-config --makefiles)/Makefile.sim
|
||||||
|
|
||||||
iverilog_dump.v:
|
|
||||||
echo 'module iverilog_dump();' > $@
|
|
||||||
echo 'initial begin' >> $@
|
|
||||||
echo ' $$dumpfile("$(TOPLEVEL).fst");' >> $@
|
|
||||||
echo ' $$dumpvars(0, $(TOPLEVEL));' >> $@
|
|
||||||
echo 'end' >> $@
|
|
||||||
echo 'endmodule' >> $@
|
|
||||||
|
|
||||||
clean::
|
|
||||||
@rm -rf iverilog_dump.v
|
|
||||||
@rm -rf dump.fst $(TOPLEVEL).fst
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
"""
|
"""
|
||||||
|
|
||||||
Copyright (c) 2021 Alex Forencich
|
Copyright (c) 2021-2025 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 decimal import Decimal
|
||||||
|
|
||||||
import cocotb_test.simulator
|
import cocotb_test.simulator
|
||||||
|
|
||||||
@@ -46,12 +47,20 @@ class TB:
|
|||||||
cocotb.start_soon(Clock(dut.clk, 6.4, units="ns").start())
|
cocotb.start_soon(Clock(dut.clk, 6.4, units="ns").start())
|
||||||
|
|
||||||
self.ptp_clock = PtpClockSimTime(
|
self.ptp_clock = PtpClockSimTime(
|
||||||
ts_96=dut.ts_96,
|
ts_tod=dut.ts_tod,
|
||||||
ts_64=dut.ts_64,
|
ts_rel=dut.ts_rel,
|
||||||
pps=dut.pps,
|
pps=dut.pps,
|
||||||
clock=dut.clk
|
clock=dut.clk
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def get_ts_tod_ns(self):
|
||||||
|
ts = int(self.dut.ts_tod.value)
|
||||||
|
return Decimal(ts >> 48).scaleb(9) + (Decimal(ts & 0xffffffffffff) / Decimal(2**16))
|
||||||
|
|
||||||
|
def get_ts_rel_ns(self):
|
||||||
|
ts = int(self.dut.ts_rel.value)
|
||||||
|
return Decimal(ts) / Decimal(2**16)
|
||||||
|
|
||||||
|
|
||||||
@cocotb.test()
|
@cocotb.test()
|
||||||
async def run_test(dut):
|
async def run_test(dut):
|
||||||
@@ -62,31 +71,32 @@ async def run_test(dut):
|
|||||||
await RisingEdge(dut.clk)
|
await RisingEdge(dut.clk)
|
||||||
|
|
||||||
await RisingEdge(dut.clk)
|
await RisingEdge(dut.clk)
|
||||||
start_time = get_sim_time('sec')
|
start_time = Decimal(get_sim_time('fs')).scaleb(-6)
|
||||||
start_ts_96 = (dut.ts_96.value.integer >> 48) + ((dut.ts_96.value.integer & 0xffffffffffff)/2**16*1e-9)
|
start_ts_tod = tb.get_ts_tod_ns()
|
||||||
start_ts_64 = dut.ts_64.value.integer/2**16*1e-9
|
start_ts_rel = tb.get_ts_rel_ns()
|
||||||
|
|
||||||
await ClockCycles(dut.clk, 10000)
|
await ClockCycles(dut.clk, 10000)
|
||||||
|
|
||||||
stop_time = get_sim_time('sec')
|
stop_time = Decimal(get_sim_time('fs')).scaleb(-6)
|
||||||
stop_ts_96 = (dut.ts_96.value.integer >> 48) + ((dut.ts_96.value.integer & 0xffffffffffff)/2**16*1e-9)
|
stop_ts_tod = tb.get_ts_tod_ns()
|
||||||
stop_ts_64 = dut.ts_64.value.integer/2**16*1e-9
|
stop_ts_rel = tb.get_ts_rel_ns()
|
||||||
|
|
||||||
time_delta = stop_time-start_time
|
time_delta = stop_time-start_time
|
||||||
ts_96_delta = stop_ts_96-start_ts_96
|
ts_tod_delta = stop_ts_tod-start_ts_tod
|
||||||
ts_64_delta = stop_ts_64-start_ts_64
|
ts_rel_delta = stop_ts_rel-start_ts_rel
|
||||||
|
|
||||||
ts_96_diff = time_delta - ts_96_delta
|
tb.log.info("sim time delta : %s ns", time_delta)
|
||||||
ts_64_diff = time_delta - ts_64_delta
|
tb.log.info("ToD ts delta : %s ns", ts_tod_delta)
|
||||||
|
tb.log.info("rel ts delta : %s ns", ts_rel_delta)
|
||||||
|
|
||||||
tb.log.info("sim time delta : %g s", time_delta)
|
ts_tod_diff = time_delta - ts_tod_delta
|
||||||
tb.log.info("96 bit ts delta : %g s", ts_96_delta)
|
ts_rel_diff = time_delta - ts_rel_delta
|
||||||
tb.log.info("64 bit ts delta : %g s", ts_64_delta)
|
|
||||||
tb.log.info("96 bit ts diff : %g s", ts_96_diff)
|
|
||||||
tb.log.info("64 bit ts diff : %g s", ts_64_diff)
|
|
||||||
|
|
||||||
assert abs(ts_96_diff) < 1e-12
|
tb.log.info("ToD ts diff : %s ns", ts_tod_diff)
|
||||||
assert abs(ts_64_diff) < 1e-12
|
tb.log.info("rel ts diff : %s ns", ts_rel_diff)
|
||||||
|
|
||||||
|
assert abs(ts_tod_diff) < 1e-3
|
||||||
|
assert abs(ts_rel_diff) < 1e-3
|
||||||
|
|
||||||
await RisingEdge(dut.clk)
|
await RisingEdge(dut.clk)
|
||||||
await RisingEdge(dut.clk)
|
await RisingEdge(dut.clk)
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
|
|
||||||
Copyright (c) 2021 Alex Forencich
|
Copyright (c) 2021-2025 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
|
||||||
@@ -33,8 +33,8 @@ module test_ptp_clock_sim_time
|
|||||||
(
|
(
|
||||||
input wire clk,
|
input wire clk,
|
||||||
|
|
||||||
inout wire [95:0] ts_96,
|
inout wire [95:0] ts_tod,
|
||||||
inout wire [63:0] ts_64,
|
inout wire [63:0] ts_rel,
|
||||||
inout wire pps
|
inout wire pps
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
# Copyright (c) 2020 Alex Forencich
|
# Copyright (c) 2020-2025 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
|
||||||
@@ -27,35 +27,23 @@ COCOTB_HDL_TIMEUNIT = 1ns
|
|||||||
COCOTB_HDL_TIMEPRECISION = 1ns
|
COCOTB_HDL_TIMEPRECISION = 1ns
|
||||||
|
|
||||||
DUT = test_rgmii
|
DUT = test_rgmii
|
||||||
TOPLEVEL = $(DUT)
|
COCOTB_TEST_MODULES = $(DUT)
|
||||||
MODULE = $(DUT)
|
COCOTB_TOPLEVEL = $(DUT)
|
||||||
|
MODULE = $(COCOTB_TEST_MODULES)
|
||||||
|
TOPLEVEL = $(COCOTB_TOPLEVEL)
|
||||||
VERILOG_SOURCES += $(DUT).v
|
VERILOG_SOURCES += $(DUT).v
|
||||||
|
|
||||||
ifeq ($(SIM), icarus)
|
ifeq ($(SIM), icarus)
|
||||||
PLUSARGS += -fst
|
PLUSARGS += -fst
|
||||||
|
|
||||||
ifeq ($(WAVES), 1)
|
COMPILE_ARGS += $(foreach v,$(filter PARAM_%,$(.VARIABLES)),-P $(COCOTB_TOPLEVEL).$(subst PARAM_,,$(v))=$($(v)))
|
||||||
VERILOG_SOURCES += iverilog_dump.v
|
|
||||||
COMPILE_ARGS += -s iverilog_dump
|
|
||||||
endif
|
|
||||||
else ifeq ($(SIM), verilator)
|
else ifeq ($(SIM), verilator)
|
||||||
COMPILE_ARGS += -Wno-SELRANGE -Wno-WIDTH
|
COMPILE_ARGS += $(foreach v,$(filter PARAM_%,$(.VARIABLES)),-G$(subst PARAM_,,$(v))=$($(v)))
|
||||||
|
|
||||||
ifeq ($(WAVES), 1)
|
ifeq ($(WAVES), 1)
|
||||||
COMPILE_ARGS += --trace-fst
|
COMPILE_ARGS += --trace-fst
|
||||||
|
VERILATOR_TRACE = 1
|
||||||
endif
|
endif
|
||||||
endif
|
endif
|
||||||
|
|
||||||
include $(shell cocotb-config --makefiles)/Makefile.sim
|
include $(shell cocotb-config --makefiles)/Makefile.sim
|
||||||
|
|
||||||
iverilog_dump.v:
|
|
||||||
echo 'module iverilog_dump();' > $@
|
|
||||||
echo 'initial begin' >> $@
|
|
||||||
echo ' $$dumpfile("$(TOPLEVEL).fst");' >> $@
|
|
||||||
echo ' $$dumpvars(0, $(TOPLEVEL));' >> $@
|
|
||||||
echo 'end' >> $@
|
|
||||||
echo 'endmodule' >> $@
|
|
||||||
|
|
||||||
clean::
|
|
||||||
@rm -rf iverilog_dump.v
|
|
||||||
@rm -rf dump.fst $(TOPLEVEL).fst
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
"""
|
"""
|
||||||
|
|
||||||
Copyright (c) 2020 Alex Forencich
|
Copyright (c) 2020-2025 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
|
||||||
@@ -68,7 +68,7 @@ class TB:
|
|||||||
|
|
||||||
def set_enable_generator(self, generator=None):
|
def set_enable_generator(self, generator=None):
|
||||||
if self._enable_cr is not None:
|
if self._enable_cr is not None:
|
||||||
self._enable_cr.kill()
|
self._enable_cr.cancel()
|
||||||
self._enable_cr = None
|
self._enable_cr = None
|
||||||
|
|
||||||
self._enable_generator = generator
|
self._enable_generator = generator
|
||||||
@@ -130,7 +130,7 @@ def cycle_en():
|
|||||||
return itertools.cycle([0, 0, 0, 1])
|
return itertools.cycle([0, 0, 0, 1])
|
||||||
|
|
||||||
|
|
||||||
if cocotb.SIM_NAME:
|
if getattr(cocotb, 'top', None) is not None:
|
||||||
|
|
||||||
factory = TestFactory(run_test)
|
factory = TestFactory(run_test)
|
||||||
factory.add_option("payload_lengths", [size_list])
|
factory.add_option("payload_lengths", [size_list])
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
|
|
||||||
Copyright (c) 2020 Alex Forencich
|
Copyright (c) 2020-2025 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
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
# Copyright (c) 2020 Alex Forencich
|
# Copyright (c) 2020-2025 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
|
||||||
@@ -27,35 +27,23 @@ COCOTB_HDL_TIMEUNIT = 1ns
|
|||||||
COCOTB_HDL_TIMEPRECISION = 1ns
|
COCOTB_HDL_TIMEPRECISION = 1ns
|
||||||
|
|
||||||
DUT = test_rgmii_phy
|
DUT = test_rgmii_phy
|
||||||
TOPLEVEL = $(DUT)
|
COCOTB_TEST_MODULES = $(DUT)
|
||||||
MODULE = $(DUT)
|
COCOTB_TOPLEVEL = $(DUT)
|
||||||
|
MODULE = $(COCOTB_TEST_MODULES)
|
||||||
|
TOPLEVEL = $(COCOTB_TOPLEVEL)
|
||||||
VERILOG_SOURCES += $(DUT).v
|
VERILOG_SOURCES += $(DUT).v
|
||||||
|
|
||||||
ifeq ($(SIM), icarus)
|
ifeq ($(SIM), icarus)
|
||||||
PLUSARGS += -fst
|
PLUSARGS += -fst
|
||||||
|
|
||||||
ifeq ($(WAVES), 1)
|
COMPILE_ARGS += $(foreach v,$(filter PARAM_%,$(.VARIABLES)),-P $(COCOTB_TOPLEVEL).$(subst PARAM_,,$(v))=$($(v)))
|
||||||
VERILOG_SOURCES += iverilog_dump.v
|
|
||||||
COMPILE_ARGS += -s iverilog_dump
|
|
||||||
endif
|
|
||||||
else ifeq ($(SIM), verilator)
|
else ifeq ($(SIM), verilator)
|
||||||
COMPILE_ARGS += -Wno-SELRANGE -Wno-WIDTH
|
COMPILE_ARGS += $(foreach v,$(filter PARAM_%,$(.VARIABLES)),-G$(subst PARAM_,,$(v))=$($(v)))
|
||||||
|
|
||||||
ifeq ($(WAVES), 1)
|
ifeq ($(WAVES), 1)
|
||||||
COMPILE_ARGS += --trace-fst
|
COMPILE_ARGS += --trace-fst
|
||||||
|
VERILATOR_TRACE = 1
|
||||||
endif
|
endif
|
||||||
endif
|
endif
|
||||||
|
|
||||||
include $(shell cocotb-config --makefiles)/Makefile.sim
|
include $(shell cocotb-config --makefiles)/Makefile.sim
|
||||||
|
|
||||||
iverilog_dump.v:
|
|
||||||
echo 'module iverilog_dump();' > $@
|
|
||||||
echo 'initial begin' >> $@
|
|
||||||
echo ' $$dumpfile("$(TOPLEVEL).fst");' >> $@
|
|
||||||
echo ' $$dumpvars(0, $(TOPLEVEL));' >> $@
|
|
||||||
echo 'end' >> $@
|
|
||||||
echo 'endmodule' >> $@
|
|
||||||
|
|
||||||
clean::
|
|
||||||
@rm -rf iverilog_dump.v
|
|
||||||
@rm -rf dump.fst $(TOPLEVEL).fst
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
"""
|
"""
|
||||||
|
|
||||||
Copyright (c) 2020 Alex Forencich
|
Copyright (c) 2020-2025 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
|
||||||
@@ -144,7 +144,7 @@ def cycle_en():
|
|||||||
return itertools.cycle([0, 0, 0, 1])
|
return itertools.cycle([0, 0, 0, 1])
|
||||||
|
|
||||||
|
|
||||||
if cocotb.SIM_NAME:
|
if getattr(cocotb, 'top', None) is not None:
|
||||||
|
|
||||||
for test in [run_test_tx, run_test_rx]:
|
for test in [run_test_tx, run_test_rx]:
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
|
|
||||||
Copyright (c) 2020 Alex Forencich
|
Copyright (c) 2020-2025 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
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
# Copyright (c) 2020 Alex Forencich
|
# Copyright (c) 2020-2025 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
|
||||||
@@ -27,45 +27,27 @@ COCOTB_HDL_TIMEUNIT = 1ns
|
|||||||
COCOTB_HDL_TIMEPRECISION = 1ns
|
COCOTB_HDL_TIMEPRECISION = 1ns
|
||||||
|
|
||||||
DUT = test_xgmii
|
DUT = test_xgmii
|
||||||
TOPLEVEL = $(DUT)
|
COCOTB_TEST_MODULES = $(DUT)
|
||||||
MODULE = $(DUT)
|
COCOTB_TOPLEVEL = $(DUT)
|
||||||
|
MODULE = $(COCOTB_TEST_MODULES)
|
||||||
|
TOPLEVEL = $(COCOTB_TOPLEVEL)
|
||||||
VERILOG_SOURCES += $(DUT).v
|
VERILOG_SOURCES += $(DUT).v
|
||||||
|
|
||||||
# module parameters
|
# module parameters
|
||||||
export PARAM_DATA_WIDTH ?= 64
|
export PARAM_DATA_WIDTH := 64
|
||||||
export PARAM_CTRL_WIDTH ?= $(shell expr $(PARAM_DATA_WIDTH) / 8 )
|
export PARAM_CTRL_WIDTH := $(shell expr $(PARAM_DATA_WIDTH) / 8 )
|
||||||
|
|
||||||
ifeq ($(SIM), icarus)
|
ifeq ($(SIM), icarus)
|
||||||
PLUSARGS += -fst
|
PLUSARGS += -fst
|
||||||
|
|
||||||
COMPILE_ARGS += -P $(TOPLEVEL).DATA_WIDTH=$(PARAM_DATA_WIDTH)
|
COMPILE_ARGS += $(foreach v,$(filter PARAM_%,$(.VARIABLES)),-P $(COCOTB_TOPLEVEL).$(subst PARAM_,,$(v))=$($(v)))
|
||||||
COMPILE_ARGS += -P $(TOPLEVEL).CTRL_WIDTH=$(PARAM_CTRL_WIDTH)
|
|
||||||
|
|
||||||
ifeq ($(WAVES), 1)
|
|
||||||
VERILOG_SOURCES += iverilog_dump.v
|
|
||||||
COMPILE_ARGS += -s iverilog_dump
|
|
||||||
endif
|
|
||||||
else ifeq ($(SIM), verilator)
|
else ifeq ($(SIM), verilator)
|
||||||
COMPILE_ARGS += -Wno-SELRANGE -Wno-WIDTH
|
COMPILE_ARGS += $(foreach v,$(filter PARAM_%,$(.VARIABLES)),-G$(subst PARAM_,,$(v))=$($(v)))
|
||||||
|
|
||||||
COMPILE_ARGS += -GDATA_WIDTH=$(PARAM_DATA_WIDTH)
|
|
||||||
COMPILE_ARGS += -GCTRL_WIDTH=$(PARAM_CTRL_WIDTH)
|
|
||||||
|
|
||||||
ifeq ($(WAVES), 1)
|
ifeq ($(WAVES), 1)
|
||||||
COMPILE_ARGS += --trace-fst
|
COMPILE_ARGS += --trace-fst
|
||||||
|
VERILATOR_TRACE = 1
|
||||||
endif
|
endif
|
||||||
endif
|
endif
|
||||||
|
|
||||||
include $(shell cocotb-config --makefiles)/Makefile.sim
|
include $(shell cocotb-config --makefiles)/Makefile.sim
|
||||||
|
|
||||||
iverilog_dump.v:
|
|
||||||
echo 'module iverilog_dump();' > $@
|
|
||||||
echo 'initial begin' >> $@
|
|
||||||
echo ' $$dumpfile("$(TOPLEVEL).fst");' >> $@
|
|
||||||
echo ' $$dumpvars(0, $(TOPLEVEL));' >> $@
|
|
||||||
echo 'end' >> $@
|
|
||||||
echo 'endmodule' >> $@
|
|
||||||
|
|
||||||
clean::
|
|
||||||
@rm -rf iverilog_dump.v
|
|
||||||
@rm -rf dump.fst $(TOPLEVEL).fst
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
"""
|
"""
|
||||||
|
|
||||||
Copyright (c) 2020 Alex Forencich
|
Copyright (c) 2020-2025 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
|
||||||
@@ -68,7 +68,7 @@ class TB:
|
|||||||
|
|
||||||
def set_enable_generator(self, generator=None):
|
def set_enable_generator(self, generator=None):
|
||||||
if self._enable_cr is not None:
|
if self._enable_cr is not None:
|
||||||
self._enable_cr.kill()
|
self._enable_cr.cancel()
|
||||||
self._enable_cr = None
|
self._enable_cr = None
|
||||||
|
|
||||||
self._enable_generator = generator
|
self._enable_generator = generator
|
||||||
@@ -208,7 +208,7 @@ def cycle_en():
|
|||||||
return itertools.cycle([0, 0, 0, 1])
|
return itertools.cycle([0, 0, 0, 1])
|
||||||
|
|
||||||
|
|
||||||
if cocotb.SIM_NAME:
|
if getattr(cocotb, 'top', None) is not None:
|
||||||
|
|
||||||
factory = TestFactory(run_test)
|
factory = TestFactory(run_test)
|
||||||
factory.add_option("payload_lengths", [size_list])
|
factory.add_option("payload_lengths", [size_list])
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
|
|
||||||
Copyright (c) 2020 Alex Forencich
|
Copyright (c) 2020-2025 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
|
||||||
|
|||||||
Reference in New Issue
Block a user