New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix Xilinx clock wizard and add Tcl IP generation #2427
Conversation
56cd335
to
cb4fa36
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice changes, this definitely makes the primitive more usable.
Two remarks on the output Signal domPllLock Bool
:
- The domain
domPllLock
isn't bound by an argument, so any domain can be instantiated here. This is incredibly unsafe. - We never produce asynchronous signals in Clash, we shouldn't break this pattern.
So I propose:
- Renaming the current functions to
unsafeClockWizard[Differential]
. - Rename
domPllLock
todomOut
again. It should be documented that this an asynchronous signal and the user should think twice about using it. - Create a new function
clockWizard[Differential]
which callsunsafeClockWizard[Differential]
and synchronizes the signal using adualFlipFlopSynchronizer
.
clash-lib/prims/commonverilog/Clash_Xilinx_ClockGen.primitives.yaml
Outdated
Show resolved
Hide resolved
I explicitly requested Hidde to write it like this. This is how we do it and I think have always done it in The reason I have requested it is precisely because I'm working on a class to make these PLL's more user friendly. Currently we always have to write (clk100, pllStable) = clockWizard (SSymbol @"clkWizard50to100") clk50 rstIn
rstSync = resetSynchronizer clk $ unsafeFromLowPolarity pllStable which means we're forcing the user to remember this and teaching them to use functions that begin with the word (clk100, rstSync) = clocksSynchronizedReset (clockWizard (SSymbol @"clkWizard50to100")) clk50 rstIn but this implementation absolutely requires that By the way, for multiple clocks, instead of (clk100, clk150, pllStable :: Signal DomIn Bool) = alteraPll (SSymbol @"alterapllmulti") clk50 rst50
rst100 = resetSynchronizer clk100 $ unsafeFromLowPolarity $ unsafeSynchronizer clk50 clk100 pllStable
rst150 = resetSynchronizer clk150 $ unsafeFromLowPolarity $ unsafeSynchronizer clk50 clk150 pllStable you write (clk100, rst100, clk150, rst150) = clocksSynchronizedReset (alteraPll (SSymbol @"alterapllmulti")) clk50 rst50 You might actually need to do something more clever with multiple domains (i.e., a reset manager), but in this simple case, you can use the simple implementation. |
By the way, along with my (Nitpick: you wrote synchronizes the signal using a |
Agreed on keeping the scope of this PR limited.
I get what you're saying, but this just hides the fact that
|
We could make it safe by adding This would make it safe as it prevents accidental synchronization. It would make it inconvenient to use though, as you have to create a |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, nice work! I just have a few little things.
This has surprising simulation results in Clash simulation, though. In HDL [edit] |
This, by the way, is inaccurate. It always generates an MMCM. The automatic mode which picks the one the wizard likes best is only supported on UltraScale and UltraScale+, so we stuck to the default MMCM so it also works on at least 7 series (I don't think anything older, but not sure). |
I fully support this (in a new PR). |
Since we support Vivado in CI, we could (in a future PR) add a simple test that verifies we can instantiate and simulate a |
I've completed a basic functionality test bench; it is in the branch peter/xilinx-clockwiz. It adds Now that I see the commit summary, I realise I did not review the commit message. I'd like it to be more explicit. After all, you fix the API, you fix everything else about it (broken Haskell simulation, wrong port names, was there more?), and you add Tcl instantiation support. But the only thing you mention is that last bit :-D. Could you rephrase it to something like "Fix Xilinx clock wizard, add Tcl IP generation"? And also change the title and cover letter of this PR accordingly? [edit] |
bd11d65
to
c64c656
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you reorder the commits? If you put Generalize periodToHz
first, you don't need Change frequency calculation to use periodToHz
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@DigitalBrains1's suggestions look good. I'm fine with merging after applying them.
I have already applied all Peters changes, with the exception of two:
I think I have only one more thing to do for this PR, which is removing my periodToHz changes, rebase those from Peter and then remove those commits without breaking other things. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Whoops, hit "Approve" too soon.
c64c656
to
d3c47bc
Compare
d3c47bc
to
d541461
Compare
After much internal discussion we decided to leave this part alone in this PR, i.e., leave it at |
dfe248b
to
447101f
Compare
To create a differential clock signal in a test bench. It is not suitable for synthesising a differential output in hardware.
6a99bf2
to
30c5370
Compare
Fixes the Xilinx clock generation primitives and adds support for clock primitive instantiation on Xilinx without manually going through the Vivado Clocking Wizard. The updated black boxes generate Tcl code to instantiate an MMCM through the Clocking Wizard. The clock primitives are limited to 1 output clock, and either a single-ended or a differential input clock. The generated Tcl script conforms with the Clash<->Tcl API. Fixes the following: - Broken Haskell simulation The Haskell simulation of the Xilinx clock primitives was incorrect due to a polarity mismatch in the lock output of Xilinx `clockWizard` and `clockWizardDifferential`: Haskell simulation was the inverse of HDL simulation and hardware. In Haskell simulation, the primitives had an `Enable` as their output, which was _asserted_ when the PLL lock was _deasserted_. This corresponds to an _asserted_ reset input to the primitive in the simulation model, meaning the `Enable` output was asserted iff the PLL reset input was asserted. HDL simulation and hardware meanwhile had an active high lock signal output (which is usually used as an active low reset signal to the circuit). Finally, the simulation model did not account for different frequencies of the input and output domains, instead treating individual input samples as individual output samples without resampling. This has been fixed by changing the type to `Signal Bool` and asserting it when the PLL has locked. - Wrong port names of blackbox primitives The blackbox primitives for `clockWizard` and `clockWizardDifferential` had capitalized port names. Also, the `clockWizardDifferential` primitive had 2 identical port names for the differential input clock signals. - Remove `Asynchronous` constraint from Xilinx `clockWizard` and `clockWizardDifferential`. Originally intended to signal that these functions react synchronously to the incoming reset and that the outgoing lock signal is an asynchronous signal. Since synchronous reset signals are a subset of asynchronous reset signals this constraint on the input is vacuous. The constraint on the lock output does not convey this information at all and is wrong. Note that it is still necessary to synchronize the lock output in your design. Known problems: - The `locked` signal is asynchronous. Therefore, this function is unsafe. - The Clocking Wizard uses input and output frequencies to determine the multiplier and divider for the MMCM, but Clash uses periods instead. The conversion from period (picoseconds, Natural) to frequency (MHz, Double) is not exact. This is already not exact when using `hzToPeriod`, e.g. `hzToPeriod 300e6` translates to a period of `3333` ps, which becomes `300.03000300030004` in Tcl.
30c5370
to
95a9df0
Compare
This PR fixes the Xilinx clock generation primitives and adds support for clock primitive instantiation on Xilinx without manually going through the Vivado Clocking Wizard. The updated black boxes generate Tcl code to instantiate an MMCM through the Clocking Wizard. The clock primitives are limited to 1 output clock, and either a single-ended or a differential input clock. The generated Tcl script conforms with the Clash<->Tcl API.
Fixed in this PR:
The Haskell simulation of the Xilinx clock primitives was incorrect due to a polarity mismatch in the lock output of Xilinx
clockWizard
andclockWizardDifferential
: Haskell simulation was the inverse of HDL simulation and hardware. In Haskell simulation, the primitives had anEnable
as their output, which was asserted when the PLL lock was deasserted. This corresponds to an asserted reset input to the primitive in the simulation model, meaning theEnable
output was asserted iff the PLL reset input was asserted. HDL simulation and hardware meanwhile had an active high lock signal output (which is usually used as an active low reset signal to the circuit). Finally, the simulation model did not account for different frequencies of the input and output domains, instead treating individual input samples as individual output samples without resampling.This has been fixed by changing the type to
Signal Bool
and asserting it when the PLL has locked.The blackbox primitives for
clockWizard
andclockWizardDifferential
had capitalized port names. Also, theclockWizardDifferential
primitive had 2 identical port names for the differential input clock signals.Asynchronous
constraint from XilinxclockWizard
andclockWizardDifferential
Originally intended to signal that these functions react synchronously to the incoming reset and that the outgoing lock signal is an asynchronous signal. Since synchronous reset signals are a subset of asynchronous reset signals this constraint on the input is vacuous. The constraint on the lock output does not convey this information at all and is wrong. Note that it is still necessary to synchronize the lock output in your design.
Known problems:
locked
signal is asynchronous. Therefore, this function is unsafe.hzToPeriod
, e.g.hzToPeriod 300e6
translates to a period of3333
ps, which becomes300.03000300030004
in Tcl.
Still TODO: