-
Notifications
You must be signed in to change notification settings - Fork 147
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
Fixed convertReset #1567
Fixed convertReset #1567
Conversation
Thanks for the changes @rowanG077! And thanks for digging in, because that made me think: I believe we also need to synchronize whenever the target domain is asynchronous, as Clash assumes resets asynchronous resets are deasserted synchronously. (Not doing this might lead to meta-stability.) Note that calling In the future we'd like to make this more explicit by allowing something like:
..but this would require |
Yes clear. If asynchronous resets are a precondition for clash we always need a synchronizer. The implementation now uses the |
Well, to be clear: Clash can cannot model the asynchronous deassertion. So while the |
Christaan, don't you think the negatives outweigh the positives in this case? In the worst case, you'll add two superfluous 1-bit flipflops to your design, and in the best case you prevent (I assume extremely hard to debug) meta stability. In any case, if we do decide to add |
Rowan, Martijn: I can see Martijn's point, but then I would like the reset assertion and de-assertion delay documented. Something along the lines:
In addition, this change cannot be backported to 1.2, as it is an API change. |
Alright, thanks for the discussion, I think @rowanG077 can make an informed decision now :-). When both synchronization cases would use
|
But |
Jep, makes sense @christiaanb |
Yes that's why I already had a normal flip-flop when going to a synchronous domain. I have updated the documentation to reflect this. |
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.
Looks good @rowanG077.
Seems like I made a mistake while implementing resetSynchronizer
: it should basically do exactly what convertReset
does, except that it would always insert a synchronizer (due to the cumbersome nature of creating domains). Then convertReset
would become:
convertReset clkA clkB rstA0 =
case sameDomain @domA @domB of
Just Refl -> rstA1
_ -> resetSynchronizer clkB rstA1 enableGen
where
rstA1 = unsafeToReset (unsafeSynchronizer clkA clkB (unsafeFromReset rstA0))
and resetSynchronizer
:
resetSynchronizer clk rst ena =
unsafeToReset
$ register clk rst ena isActiveHigh
$ register clk rst ena isActiveHigh
$ pure (not isActiveHigh)
where
isActiveHigh = case resetPolarity @dom of { SActiveHigh -> True; _ -> False }
This PR looks good though, so I wouldn't want to burden you with any more work :). Are you happy with it? If so, this can be merged.
edit: resetSynchronizer
should use dflipflop clk (dflipflop clk rst)
for synchronous target domains, like you did for this PR.
@martijnbastiaan I rather fix it now then run the risk of this conversation being forgotten :). I have applied the changes you suggested. I updated the docs for |
@rowanG077 I think @martijnbastiaan copied that from an internal discussion: please do use We intended to get rid of that Enable argument anyhow, as you can see from that haddock comment. |
b56e3e3
to
8e86308
Compare
@christiaanb Clear! I have pushed the changes! |
Not sure if you can see the CircleCI logs, but the test
You can replace VHDL with Verilog or SystemVerilog. Simulators needed:
In case you can figure it out from the error message alone:
|
And yeah, forgot about the initial value 😓 |
@rowanG077 I had some train time so I looked in to it :-), this should do: diff --git a/tests/shouldwork/Signal/ResetSynchronizer.hs b/tests/shouldwork/Signal/ResetSynchronizer.hs
index aa2af3de..400fe19c 100644
--- a/tests/shouldwork/Signal/ResetSynchronizer.hs
+++ b/tests/shouldwork/Signal/ResetSynchronizer.hs
@@ -28,24 +28,25 @@ testReset tbClk tbRst cClk =
$ stimuliGenerator tbClk tbRst
( True
- -- Reset synchronizer introduces a delay of two, but lets asynchronous
- -- asserts through untouched. This means that for 'topEntity' we'll see
- -- [R, R, R, R] for these cycles. Where the first two zeroes are caused by
- -- 'resetSynchronizer's induced latency, the next zero by the first cycle
- -- not in reset, and the last zero because we're in reset again (async
- -- behavior).
+ -- Asynchronous resets:
+ --
+ -- Resets are asserted asynchronously and deasserted synchronously with
+ -- a delay of two cycles. This means that for 'topEntity' we'll see
+ -- [R, R, R, R] for the first batch of reset cycles. The first two Rs
+ -- are caused by 'resetSynchronizer's induced latency, the next zero
+ -- by the first cycle not in reset, and the last zero because we're in
+ -- reset again (async assertion behavior).
+ --
+ -- The second batch of resets is similar to ^, but the reset is held
+ -- a cycle longer so we should be able to see a count output.
+ --
+ -- Synchronous resets:
+ --
+ -- Synchronous resets are simply delayed for two cycles. Hence, we
+ -- should count up to the number of deasserted cycles.
--
- -- We should be able to see a difference between asynchronous and synchronous
- -- resets here: the counter is driven by a register whose synchronicity
- -- is imposed by the 'KnownDomain' constraint. Synchronous counters will
- -- only see the reset asserted on the /next/ cycle, hence outputting
- -- [R, R, R, 0] instead.
:> False :> False :> False :> True
-
- -- Similar to ^, but now we're out of of reset one cycle longer so we should
- -- start seeing [R, R, R, 0, R] for asynchronous counters, and
- -- [R, R, R, 0, 1] for synchronous ones.
- :> False :> False :> False :> False :> True
+ :> False :> False :> False :> False :> True
:> replicate d20 False )
polyTopEntity
@@ -76,10 +77,12 @@ polyTestBench ::
polyTestBench Proxy = (done, sampleN 20 (polyTopEntity cClk cRst))
where
rKind = resetKind @circuitDom
+ rOr' = flip rOr rKind
+
expectedOutput = outputVerifier tbClk tbRst
- ( RRRRRRR :> RRRRRRR :> RRRRRRR :> RRRRRRR :> rOr 0 rKind
- :> RRRRRRR :> RRRRRRR :> RRRRRRR :> Count 0 :> rOr 1 rKind
- :> RRRRRRR :> RRRRRRR :> RRRRRRR :> Count 0 :> Count 1 :> Count 2
+ ( RRRRRRR :> RRRRRRR :> RRRRRRR :> RRRRRRR :> rOr' 0
+ :> rOr' 1 :> rOr' 2 :> RRRRRRR :> Count 0 :> rOr' 1
+ :> rOr' 2 :> rOr' 3 :> RRRRRRR :> Count 0 :> Count 1 :> Count 2
:> Nil )
done = expectedOutput (polyTopEntity cClk cRst)
(tbClk, cClk) = biTbClockGen @System @circuitDom (not <$> done)
|
@martijnbastiaan Thanks! I have some issues getting GHDL working on NixOS :(... I will add your changes today. |
Great :). Just ping me when it's done, I'll pack the changes in 1.2.5 (#1585). |
…ns are equal. Fixed `resetSynchronizer` not synchronizing the reset correctly for synchronous domains. Fixed `resetConvert` not correctly converting between synchronous and asynchronous domains.
@martijnbastiaan Once CI finishes succesfully this can be merged. |
Forgot that this is an API change, so it won't up in 1.2.5. I'll merge it to master when the CI succeeds :) |
convertReset
was not converting synchronous signals correctly. I added an additional check to never apply a synchronizer if the domains are equal. I also exposed the newly createdsameDomain
for users to use.