-
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
Upstreaming clash-port-name
?
#2650
Comments
I like the elegant and principled approach here! But I do have one main criticism. But first, some simple observations. You write module Lib where
foozle = ...
(foozleTop, foozleAnn) = mkEntity "foozle" foozle
-- N.B. Sadly, we must place the top-level entity and its annotation in a separate
-- module due to GHC's staging restriction.
module Top where
import Lib
{-# ANN top foozleAnn #-}
top = foozleTop The staging restriction is very annoying, but I do think the usability can be increased a bunch by defining module Lib where
foozle = ...
-- N.B. Sadly, we must place the top-level entity and its annotation in a separate
-- module due to GHC's staging restriction.
module Top where
import Lib
mkEntity "top" "foozle" 'foozle with the following: module Clash.PortRep.Class.Def where
mkEntity0 :: HasEntityPorts a => String -> a -> (EntityPortRep a, TopEntity)
mkEntity0 name f = ... what mkEntity currently is ...
clashOpaquePragma ::
Quote m =>
Name ->
m Dec
#if MIN_VERSION_template_haskell(2,19,0)
clashOpaquePragma = pure . PragmaD . OpaqueP
#else
clashOpaquePragma name = pragInlD name NoInline FunLike AllPhases
#endif
clashOpaque ::
Quote m =>
Name ->
m [Dec]
clashOpaque = fmap pure . clashOpaquePragma
mkEntity ::
String ->
String ->
Name ->
DecsQ
mkEntity topFunName0 topName entity =
funDef
<> funAnn
<> clashOpaque topFunName1
where
funDef = [d| $(varP topFunName1) = fst (mkEntity0 $topNameE $entityE) |]
funAnn = fmap pure $
pragAnnD (valueAnnotation topFunName1)
[| snd (mkEntity0 $topNameE $entityE) |]
topFunName1 = mkName topFunName0
topNameE = litE $ stringL topName
entityE = varE entity The functions We should annotate everything that Clash might need to look up the name of (primitives, top entities, perhaps more) with And then hopefully you can also plug in a type declaration for the top function through Template Haskell, because it is not very pretty to have to do Now regarding the naming part of But Right now, a top entity will often contain some conversion code to massage the data representations as they are used internally in the circuit into a representation that is suitable for the outside. And often, when you connect stuff to the FPGA pins, you want the first thing connected to the I/O buffer of the pin to be a register. If you're writing ad-hoc conversions, you'll just see where that register end up, and you can trivially put it on the "outside". But your Therefore, I think it would be better if you would either make the port conversion explicit, or find some other way where the user will not easily make this mistake. |
One of the facets of Clash that I have long struggled with is the treatment of naming top-level ports. Ensuring that the port-naming of a top-level entity remains consistent with the entity's actual type is annoying at best and a horrible source of bugs at worst. For this reason, I quickly find myself yearning for better tools for treating the representations of top-level entities' ports as composable, first-class objects.
To this end I have written
clash-port-name
(which, in hindsight, would probably be better calledclash-port-rep
). While the newly-addedREADME
and Haddocks should hopefully start to explain the "why" and "how", what I would like to discuss here is the future of this package. While I am happy to continue maintaining this as an external package, I suspect that the the problem that it solves is universal enough to potentially warrant eventual upstreaming of it or something like it. I am opening this ticket as a forum to have this conversation.Things I would do differently
clash-port-name
is just one point in the design space. There are various things that I think could be improved in it:Clash.PortRep.TH.genHasPortRep
could likely be cleanergenerics-sop
. Sadly, I have found that in practice this mechanism tends to be a very good trigger ofclash
compiler bugs and consequently I generally don't rely on it. This is sad because in general I think it is much more composable than the TH-based deriving mechanism.Port
) and the typeclass is helpful, use of the typeclass is a bit less convenient as a result. Adding a few convenient class-centric accessors (e.g.toPortName :: forall a. HasPortRep a => a -> PortRep a
) may be a good idea.Port
is both (a) an isomorphism between a Haskell type and a generic product, (b) an isomorphism between each of the factors and its external bit encoding, and (c) a naming for the factors. Ideally, you wouldn't need to rewrite (a) in order to affect (b) and (c). Thegenerics-sop
deriving mechanism is IMHO much better than TH in this regard.The text was updated successfully, but these errors were encountered: