How do enable signals propagate through a function?

23 views
Skip to first unread message

L Bollen

unread,
Jul 21, 2021, 4:24:07 PM7/21/21
to Clash - Hardware Description Language
Hello group,

I've been struggling with controlling the enable signals in my clash code to stall logic to prevent data loss.

In short, I've got functions the operate on DSignal dom functions and want to be able to control the enable signals of based on other signals within the system.
Consider a simplified version of the function  I'm implementing:
getAlphaLoop pipeIn readEnable = (alphaResultPipe <$> pipeOut, (&&) <$> enable <*> preEnable, pipeOut)
    where
        (selectedPipe, enable) = priorityMux reIterate pipeIn
        (pipeA, preEnable) = withEnable (toEnable getLikEnable) getAlphaLoopPreLik selectedPipe
        (likelihoodOutput, getLikEnable) = withEnable (toEnable likEnable) getLikelihood pipeA
        (pipeOut, reIterate, reachedLimit) = withEnable (toEnable postEnable) getAlphaLoopPostLik likelihoodOutpu
        postEnable = branchEnables <$> reachedLimit <*> readEnable <*> preEnable
        likEnable = branchEnables <$> reachedLimit <*> readEnable' <*> (pure True)

The concept of these data types is that each function returns its output and an enable signal that indicates whether or not it consumes an input at the next clock edge (e.g. selectedPipe is an output and enable indicates whether the function consumes pipeIn).  While the enable is low, all logic that produces pipeIn must be stalled, so this should be handled by the function that calls getAlphaLoop. 

In a nutshell, priorityMux is a multiplexer that prioritizes valid data at reIterate over valid data at pipeIn (which is why pipeIn should be stalled).

getAlphaLoopPreLik is a linear pipeline that transforms selectedPipe in pipeA.

getLikelihood is a pipeline with a feedback loop that introduces stalling, it intermittently consumes and produces valid data.

getAlphaLoopPostLik is a linear pipeline that transforms likelihoodOutput in pipeOut and reIterate, reIterate is a feedback signal that is the prioritized input of priorityMux. 

branchEnables returns one of two enable signals (argument two and three) based on its first argument.

What I'm trying to achieve is that the enable signal of every function is controlled by whether or not the next function consumes data at the next clock edge. 

However, I can not figure out how to properly control the enable signals or how they are routed. in the where clause.

Hopefully you can offer me some insights.

Kind regards,
Lucas 

Peter Lebbing

unread,
Jul 22, 2021, 6:41:57 AM7/22/21
to clash-l...@googlegroups.com

Hello Lucas,

The really short answer is that when you're only passing clocks, resets and enables down to functions, implicit parameters save you a lot of boilerplate; but when actually doing things with clocks, resets and/or enables, it's usually better to switch to explicit functions from Clash.Explicit. Clocks, resets and enables are just arguments to a function, and enables are really not that different from a Signal dom Bool.

How enable signals propagate is clearly apparent when using Clash.Explicit. And the implicit version is mainly meant to save you from constantly passing arguments around without operating on those arguments directly. Due to technicalities regarding the monomorphism restriction among others, it can even lead to unintended results when there are multiple different Enable signals in one implicit function definition; Clash might route the wrong enable to your function as it "monomorphises" the HiddenEnable constraint.

Finally, I should note that I expect your getAlphaLoop function to actually have a hidden enable, but since you're using withEnable, that "incoming" hidden enable is ignored for those functions invoked with withEnable. So if the whole getAlphaLoop has its enable de-asserted, that does not propagate to the subfunctions with withEnable. You probably want Clash.Explicit.Signal.enable to AND the enables together. Or if you're going explicit anyway, drop the incoming enable if you know it won't be used. If the incoming enable is in practice always asserted, the ANDing will not introduce additonal logic.

By coincidence, I intend to continue on PR#1849 today, giving people a bit more options to work with enables in implicit notation. I will rename the fuction from mergeEnable to enable though, to fall in with the existing explicit function. Still, my gut feeling is that when you're dealing so extensively with enables as you are here, it's better to go explicit. My intention for the new function is to avoid all the explicit/implicit conversions I had to do here (variant1 under The variants of the pipeline from my second mail:, you will have to click the three dots to expand, Google just keeps messing up my mails and hiding the interesting parts. Thank you, Google!).

I also slightly improved some documentation in that PR; for a while, the improved documentation of hidden arguments is available as a CI artifact for that PR at https://clash-lang.gitlab.io/-/clash-compiler/-/jobs/1336109331/artifacts/hadocs/clash-prelude/Clash-Signal.html.

HTH,

Peter.

Reply all
Reply to author
Forward
0 new messages