Fix DDR primitives - #2833
Conversation
Of course, while we didn't have Vivado back when the bug was introduced, we do now. Maybe it's even worth it to make a combined test with IDDR and ODDR prims, but just a "does this generate HDL" test is much more quickly written. Anyway, the names in HDL are a bit weird: D1 => dataout_l(i), -- 1-bit data input (positive edge)
D2 => dataout_h(i), -- 1-bit data input (negative edge)Why is the data for the positive edge labeled L and the data for the negative edge labeled H? Note that |
|
I think they're called like that due to copy/paste from the Intel primitives where the output ports of the Intel primivite are called clash-compiler/clash-lib/prims/vhdl/Clash_Intel_DDR.primitives.yaml Lines 43 to 44 in 5706eaf |
|
Shall I name them more obviously in this PR as well? I'm thinking |
christiaanb
left a comment
There was a problem hiding this comment.
Yes, please clean up the names, and also it would be nice if we tested these primitives on Vivado at least in nightly builds. I mean... we already do that for the generic DDRout primitives:
Lines 517 to 526 in 10f26ff
56f9f38 to
e77b004
Compare
This comment was marked as resolved.
This comment was marked as resolved.
e77b004 to
c6172d0
Compare
oddrAll arguments passing in a DDR primitive have also gained an `Enable`, and the DDR output primitive gets its inputs as a tuple rather than as two arguments. This matches the type of the DDR primitives in `clash-prelude` (especially after clash-lang/clash-compiler#2833 is merged) `ddrForwardClock` is only in Clash master, we no longer support Clash 1.8 in this core.
All arguments passing in a DDR primitive have also gained an `Enable`, and the DDR output primitive gets its inputs as a tuple rather than as two arguments. This matches the type of the DDR primitives in `clash-prelude` (especially after clash-lang/clash-compiler#2833 is merged) `ddrForwardClock` is only in Clash master, we no longer support Clash 1.8 in this core.
All arguments passing in a DDR primitive have also gained an `Enable`, and the DDR output primitive gets its inputs as a tuple rather than as two arguments. This matches the type of the DDR primitives in `clash-prelude` (especially after clash-lang/clash-compiler#2833 is merged) `ddrForwardClock` is only in Clash master, we no longer support Clash 1.8 in this core.
All arguments passing in a DDR primitive have also gained an `Enable`, and the DDR output primitive gets its inputs as a tuple rather than as two arguments. This matches the type of the DDR primitives in `clash-prelude` (especially after clash-lang/clash-compiler#2833 is merged) `ddrForwardClock` is only in Clash master, we no longer support Clash 1.8 in this core.
All arguments passing in a DDR primitive have also gained an `Enable`, and the DDR output primitive gets its inputs as a tuple rather than as two arguments. This matches the type of the DDR primitives in `clash-prelude` (especially after clash-lang/clash-compiler#2833 is merged) `ddrForwardClock` is only in Clash master, we no longer support Clash 1.8 in this core.
All arguments passing in a DDR primitive have also gained an `Enable`, and the DDR output primitive gets its inputs as a tuple rather than as two arguments. This matches the type of the DDR primitives in `clash-prelude` (especially after clash-lang/clash-compiler#2833 is merged) `ddrForwardClock` is only in Clash master, we no longer support Clash 1.8 in this core.
| es0 = P.take 69 es P.++ P.take 8 (cycle [133, 233]) P.++ P.drop 77 es | ||
| in P.take 31 es0 P.++ P.take 8 (cycle [114, 214]) P.++ P.drop 39 es0 |
There was a problem hiding this comment.
Wow that's a lot of magic numbers! How did they come to be?
There was a problem hiding this comment.
Because I definitely did not feel like writing this in a comprehensible way which would be way much more work! :-D
They come from looking at waveforms, understanding them, agreeing with them, and stuffing them in a list of expected values here.
This kind of code would be inexcusible in src/ but we have more instances of magic numbers in tests/.
Also, don't bother running the Haskell simulation against them. I still need to file some bugs that our simulation doesn't agree with HDL simulation. Although I have to say the Xilinx DDR primitive has extremely surprising timing regarding resets and enables, I actually wonder whether their simulation matches hardware.
There was a problem hiding this comment.
(also, note that 69 plus 8 equals 77 and 31 plus 8 equals 39, these do not actually change the length of the list of samples, they overwrite samples that were affected by a deasserted enable)
All arguments passing in a DDR primitive have also gained an `Enable`, and the DDR output primitive gets its inputs as a tuple rather than as two arguments. This matches the type of the DDR primitives in `clash-prelude` (especially after clash-lang/clash-compiler#2833 is merged) `ddrForwardClock` is only in Clash master, we no longer support Clash 1.8 in this core.
`Clash.Explicit.DDR`:
- `ddrOut`: VHDL: Fix incorrect usage of `Enable` signal. With
asynchronous resets, the `Enable` was not connected to the
registers. Furthermore, the `Enable` was combinatorially(!)
connected to the mux that selects the output. The mux should not be
affected by the `Enable` at all (with the current Haskell simulation
model).
- `ddrIn`: VHDL: Remove bogus sensitivity list. In the initial version
of this primitive, the version for asynchronous resets included the
data input in its sensitivity lists for the registers. A later
commit broke this, making it render the empty string for `~VARS`.
But the input should not be in the sensitivity list anyway, so let's
just remove the `~VARS` without actually changing the rendering in
HDL.
Additionally, the register outputting the data clocked in on the
edge other than the active edge (`ddrIn_neg_latch`) also had its
data input in its sensitivity list like the other two registers.
This is also bogus and has been removed.
`Clash.Xilinx.DDR`:
- `oddr`: Verilog correctly picked one of the possible arguments to
inspect for `ResetKind`, but the others picked the `KnownNat`
constraint. This caused Clash to error out during HDL generation.
- Both in and out prims: Renamed symbols that were incorrectly
copy-pasted from the Intel prims.
`Clash.Intel.DDR`:
- `altddioIn`:
- VHDL: Fix `~ISACTIVEENABLE` parameter reference that caused HDL
generation to error out.
- Verilog: Fix width parameter; it accidentally always rendered as
1.
- SystemVerilog: Accidental comma generated invalid HDL.
- `altddioOut`: VHDL: Several numeric identifiers were incorrect,
referring to wrong arguments or symbols. HDL generation completed
without error, but the resulting VHDL was completely broken.
- Both prims: evaluate the `SSymbol` in Haskell to fix HDL generation
warning about using an argument that is unused in Haskell.
c6172d0 to
eb13594
Compare
A variant for Intel is also included, but we don't have a simulator in CI that supports Intel IP, so it can only be run manually for now.
The existing constraint can be redefined in terms of `DomainPeriod`. Since the virtual DDR domain only exists in simulation, it really doesn't matter if it doesn't match with the real domain in all its parameters, so we can drop those and make the constraints more intuitive to read. The primitives for Xilinx and Intel only support the rising edge as the active edge of the domain. The real domain of the functions was the `slow` domain, and the `fast` domain was the virtual domain. The new names `dom` and `domDDR` reinforce the fact that `dom` is the real domain.
eb13594 to
c6bda10
Compare
All of our DDR primitives turned out to have some issues. This PR initially started as fixing some issues in Xilinx
oddrbut quickly turned into fixing more bugs and extending the Haddock documentation.Clash.Explicit.DDR:ddrOut: VHDL: Fix incorrect usage ofEnablesignal. With asynchronous resets, theEnablewas not connected to the registers. Furthermore, theEnablewas combinatorially(!) connected to the mux that selects the output. The mux should not be affected by theEnableat all (with the current Haskell simulation model).ddrIn: VHDL: Remove bogus sensitivity list. In the initial version of this primitive, the version for asynchronous resets included the data input in its sensitivity lists for the registers. A later commit broke this, making it render the empty string for~VARS. But the input should not be in the sensitivity list anyway, so let's just remove the~VARSwithout actually changing the rendering in HDL.Additionally, the register outputting the data clocked in on the edge other than the active edge (
ddrIn_neg_latch) also had its data input in its sensitivity list like the other two registers. This is also bogus and has been removed.Clash.Xilinx.DDR:oddr: Verilog correctly picked one of the possible arguments to inspect forResetKind, but the others picked theKnownNatconstraint. This caused Clash to error out during HDL generation.Clash.Intel.DDR:altddioIn:~ISACTIVEENABLEparameter reference that caused HDL generation to error out.altddioOut: VHDL: Several numeric identifiers were incorrect, referring to wrong arguments or symbols. HDL generation completed without error, but the resulting VHDL was completely broken.SSymbolin Haskell to fix HDL generation warning about using an argument that is unused in Haskell.A variant for Intel is also included, but we don't have a simulator in CI that supports Intel IP, so it can only be run manually for now. If the Verilog code is to be tested in Questa Intel FPGA Edition, one needs to define the preprocessor macro
INTEL_VERILOG, which will skip verifying the output of theClash.Explicit.DDR.ddrOutprimitive as Questa trips on this:VendorDDR.hscomment. SystemVerilog doesn't need this. Vivado is also fine with the Verilog code, so we actually test the Verilog variant of the vendor-neutral output DDR primitive in our nightlies now.The new test bench for vendor primitives tests the vendor-neutral primitives more extensively than the existing test bench did. For this reason, we also no longer run the existing test bench in Vivado, as it would not increase test coverage.
The new test bench is very slow in generating HDL; this is due to some unfortunate GHC transformations that are hard on Clash and not easily fixed. Compensating a bit: Vivado runs all test bench configurations in parallel, saving a lot of time spent in Vivado.
DomainPeriod. Since the virtual DDR domain only exists in simulation, it really doesn't matter if it doesn't match with the real domain in all its parameters, so we can drop those and make the constraints more intuitive to read.BitPackinstance, instead of being restricted toBitVector.I have written a variant for Clash 1.8 that does not change the API, but uses
clashCompileErrorto prevent people instantiating the vendor primitives using a domain where the falling edge is the active edge. Once this PR has passed review, I'll open a new PR for that one (incorporating changes that are the result of review here).Several of the bugs were introduced in 2019 in commit 0c38d65 part of PR #527. We could not test the vendor primitives, so it went unnoticed. Now we at least test automatically in Vivado, and manually testing in Questa Intel FPGA Edition is straightforward.
Still TODO: