Value circuits new#36
Conversation
f847f89 to
b2f0f98
Compare
|
RIP Fable.. I find that I typically mix and match accum :: Circuit (Signal dom Int) (Signal dom Int)
accum = circuit \(Fwd (Values i)) -> do
Fwd (Values acc) <- registerC 0 -< Fwd (Values acc')
let acc' = acc + i
idC -< Fwd (Values acc')~ accum :: Circuit (Signal dom Int) (Signal dom Int)
accum = circuit \(Fwd (Values i)) -> do
let
Values acc = register 0 (Values acc')
acc' = acc + i
idC -< Fwd (Values acc')Not sure about any of this, just floating an idea. |
|
I'll think about it but adding support in general Haskell expressions and bindings might open a whole can of worms. Right now, since it's limited to the "arrow land", it's relatively simple without much in the way of edge cases (I hope). I'm generally in favour of keeping the plugin simple and predictable at the cost of a bit of verbosity. You can always use helpers (something like |
|
Yeah, makes sense. I'd rather have less magic too. |
83565d7 to
cd19ade
Compare
Since #14 the error locations often pointed to the end of the circuit instead of the correct location. Locate each generated binding at its circuit expression so GHC blames the offending statement. The regression test is in a separate PR. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Compiles tests/fixtures/BusError.hs (a deliberate type error on a bus) with the plugin enabled and asserts that GHC reports the error on the offending line rather than at the end of the circuit block. Under cabal the plugin is found via the package environment file. Under `nix build` the package isn't registered in a package db during its own check phase, so the flake points GHC_PACKAGE_PATH (via the builder's NIX_GHC_PACKAGE_PATH_FOR_TEST hook) at the in-place db, letting the test run for real there too. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Value-group logic is lifted to the signal level with `fmap` over a
`bundle` of the group's inputs. Two strictness points there can make
combinational feedback between value groups deadlock simulation:
* `bundle` lifts its first element with `fmap` / `mapSignal#`, which
forces that element's spine. Prepend a lazy unit so no real input
sits in that spine-forcing head slot.
* The logic function matched its inputs strictly, so a constructor
pattern at the boundary (destructuring the sampled value) forced its
input to produce *any* of the group's outputs -- even outputs that do
not use it. That deadlocks when the input depends, through the
circuit, on such an output. Match each value input lazily
(irrefutable), as the bus-level plumbing already does, so an output
only forces the inputs it actually uses.
Both keep value-group feedback well founded without changing the lifted
logic. The library and error-location test suites still pass.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
cd19ade to
10ba3c1
Compare
Keep the summary and the breaking ExternalNames change; the marker semantics and multi-domain details live in the README and haddocks. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
SignalBus previously stopped at 3-tuples. Extend it (and the Fwd/Bwd, TrivialBwd and BusTagBundle instances it leans on) to 12-tuples, and exercise a tuple bus with an FwdV marker in the examples and tests. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Generated tuple patterns and expressions had no source location, so a type error on a whole bundle -- e.g. a cross-domain mismatch discovered while checking the slave pattern of a value circuit -- fell back to the head of the circuit expression. Give them the combined span of the ports they bundle, so the error points at the pattern that introduces the offending ports. The CrossDomainError fixture now puts its markers on a different line than the circuit keyword to pin this down. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
rowanG077
left a comment
There was a problem hiding this comment.
I like this a lot. It makes circuit notation fit much more naturally with standard Clash code.
Could you add write-ghc-environment-files: always to the cabal.project? This ensures that a standard cabal build + cabal test works.
| import Clash.Signal.Internal (Signal ((:-))) | ||
|
|
||
| registerC :: a -> Circuit (Signal dom a) (Signal dom a) | ||
| registerC a = Circuit $ \(s :-> ()) -> (() :-> (a :- s)) |
There was a problem hiding this comment.
Why use internals here? This seems to work just fine:
registerC :: HiddenClockResetEnable dom => a -> Circuit (Signal dom a) (Signal dom a)
registerC a = Circuit $ \(s :-> ()) -> (() :-> register a s)| import Clash.Signal.Internal (Signal ((:-))) | ||
|
|
||
| registerC :: a -> Circuit (Signal dom a) (Signal dom a) | ||
| registerC a = Circuit $ \(s :-> ()) -> (() :-> (a :- s)) |
| else lifted | ||
| outsLoc = noAnnSrcSpan (foldr (combineSrcSpans . getLocA) noSrcSpan outVarPs) | ||
| knotBind = L loc $ patBind (tupP outsLoc outVarPs) knotExpr | ||
| in if null outs then [] else [logicBind, knotBind] |
There was a problem hiding this comment.
This drops value groups with no outputs. This means code like this is accepted:
deadLet :: Circuit (Signal dom Int) (Signal dom Bool)
deadLet = circuit \(SignalV a) -> do
let bad = P.not a
idC -< SignalV (0 :: Int)Where that should be a type error imo.
|
|
||
| -- | Variable names bound by a pattern (conservative, syntactic; as-pattern | ||
| -- names are not collected). | ||
| patVarNames :: LPat GhcPs -> [String] |
There was a problem hiding this comment.
This is maybe a bit of a nit, but it would be nice if this would work:
valueAsPattern :: Circuit (Signal dom (Int, Int)) (Signal dom (Int, Int))
valueAsPattern = circuit \(SignalV p@(a, b)) -> do
idC -< SignalV pThat can also just be an issue, doesn't need to be fixed now imo.
| sigComp (L _ rdr) = case unqualName rdr of | ||
| [n] -> Map.lookup n nameComp | ||
| _ -> Nothing | ||
| splitSigs = concatMap |
There was a problem hiding this comment.
Should pragmas/fixities annotation move as well, like:
valueLocalInline :: Circuit (Signal dom Int) (Signal dom Int)
valueLocalInline = circuit \(SignalV a) -> do
let
{-# INLINE inc #-}
inc x = x + 1
idC -< SignalV (inc a)So that a plain `cabal build && cabal test` drops a .ghc.environment file, letting the error-location test's bare `ghc` find the circuit-notation plugin without the CI-only --write-ghc-environment-files flag. Addresses review feedback from @rowanG077 on #36. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The ValueExprError and CrossDomainError fixtures defined a local register circuit via Clash.Signal.Internal's (:-). Use the public `register` instead, as suggested by @rowanG077 on #36. The deliberate errors still land on their marked lines (checked by the error-location test). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Three fixes from @rowanG077's review of #36: * Type-check value groups with no outputs. Previously a value group that produced no output (e.g. an unused `let bad = not a`) was dropped entirely, so a broken circuit compiled silently. The group's logic is now still bound (to a wildcard) so its expressions are type-checked; with nothing downstream to pin the value, the mismatch surfaces where the input value enters the circuit. Regression: tests/fixtures/DeadLetError.hs. * Move INLINE/NOINLINE pragmas and fixity declarations with a value-level `let` when it is relocated into a group's generated logic function. Previously only type signatures were routed to the group, so a local pragma was orphaned in the outer let and GHC rejected it as "lacks an accompanying binding". Example: localLetPragmas in ValueCircuits.hs. * Support as-patterns on value markers: `SignalV p@(a, b)` now binds `p` as well as `a` and `b` (patVarNames collects the as-pattern binder). Example: valueAsPattern in ValueCircuits.hs. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
AsPat carries a separate `@`-token field on GHC 9.4-9.10 (4 fields) that 9.12 folded into its extension field (3 fields). CPP-guard the pattern so the as-pattern support builds on all supported GHCs, not just 9.12. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The `@`-token field was dropped from AsPat in GHC 9.10, not 9.12 as the previous fix assumed (9.10.3 then failed with the 4-field pattern). Guard on `>= 910`. Verified by building and running the test suites on all four supported GHCs (9.6.7, 9.8.4, 9.10.3, 9.12.4) via the flake. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The value level circuit syntax I originally started work on 6 years ago! master...value-circuits
This was written with the anthropic's new fable model. I also got it to add significantly more testing.
This gets you pretty close to being able to write everything with circuit syntax! Mealy machines can be done with a
registerC:It also supports multiple clock domains (although you don't get a great error when you mix them up):
and
DSignalCsupport (makes sure all groups have the same delay):I've tested this enough internally that I'm happy with it. The only issue that came up is the lazyness which has been fixed.