Skip to content

Conversation

@enathang
Copy link

Adds orangecrab starter project based on Felix's original orangecrab project.

One choice I'm not 100% on yet is the choice to include both Blink and accum as functionality in src/Blink.hs. I currently include both so that users can both make upload to see a change in their orangecrab (blink) and also have Hedgehog tests pre-setup for them (accum). However, this might confuse the user and blink and accum are separate functions that have no relation to one another. Thoughts welcome.

@enathang enathang requested a review from lmbollen October 28, 2025 12:56
@DigitalBrains1
Copy link
Member

One choice I'm not 100% on yet is the choice to include both Blink and accum as functionality in src/Blink.hs. I currently include both so that users can both make upload to see a change in their orangecrab (blink) and also have Hedgehog tests pre-setup for them (accum). However, this might confuse the user and blink and accum are separate functions that have no relation to one another. Thoughts welcome.

You could do it similarly to how Christiaan did it with the DECA starter: Tests.DECA

Copy link
Member

@DigitalBrains1 DigitalBrains1 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for adding another nice target!

This is quite a different setup than the other starter projects. I'd rather have more uniformity; I feel it should either be similar to simple or to simple-nix. If you feel those are inadequate, let's upgrade them all. I think it's kinda hard to document or support the starter projects if they each use a different project setup and even enable different extensions in the cabal file. It's also confusing to people when they switch dev boards.

import Clash.Prelude

-- | 48 MHz oscillator clock of the OrangeCrab board.
createDomain vSystem
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think synchronous resets are usually used on Lattice ECP5? This domain has async resets.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, it also suddenly popped in my mind the ECP5 doesn't have defined initial values, right? That's another thing not reflected in this domain.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Honestly, I have no idea. So I'll look into it :)

"BTN" ::: Reset Dom48 ->
"rgb_led0" ::: Signal Dom48 RGB
topEntity clk rst = withClockResetEnable clk rst enableGen
$ driveRGB $ mealy (~~>) (0 :: Index 48_000_000, 0 :: Index 9) $ pure ()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would prefer code more accessible to beginners. This has an infix function, a section and a use of the $ operator.

If you just bind a name to the vector of colours, you can replace the latter two in a visually clear way.

One thing that you could use here is the Counter instance for (Index 9, Index 48_000_000).

where
c ~~> Color{..} =
( satSucc SatWrap c
, RGB (c >= r) (c >= g) (c >= b)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe add a comment that "always on" isn't actually possible with this setup, but this is nicely succinct and visually indistinguishable? Explain to the user what's going on.

Alternatively, cycling through 0 to 254 would actually allow you to do both always-off and always-on, wouldn't it?

@enathang
Copy link
Author

I've gone ahead and simplified the example code.

A couple opinionated choices I made:

  1. The starter project does not automatically include Clash.Protocols or Clash.Cores. This might be a mistake, as we want to encourage new users to use these features.
  2. I tried to balance both use cases of: orange crab by itself, and orange crab with breakout board. I wanted to show off a topEntity that had both input and output, because it avoids a pure () in the topEntity, which I think is confusing. I also think an interactive example is more fun. However, the only builtin input the orangecrab has is the reset button, which I can't map to anything.

My solution was to make the example work without needing input (just cycle LEDs), but if pin B8 is driven high (corresponding to breakout PMOD 1_4), then the user can play with the orangecrab example. That way, the example still works without the breakout board, but it becomes interactive with the breakout board (or the user can drive the pin themselves).
3) The nix flake can optionally be used to pull in the tools required by makefile. However, I think it still also accidentally pulls in Haskell stuff. So, I still have to further trim down the flake.nix.

I'm still working on writing a few more comments and updating README, but feedback welcome.

@DigitalBrains1
Copy link
Member

DigitalBrains1 commented Oct 30, 2025

[edit] I think this post I made is too unnuanced, please read my next message for a more nuanced take [/edit]

The starter project does not automatically include Clash.Protocols or Clash.Cores. This might be a mistake, as we want to encourage new users to use these features.

I really think we should first make a release of those packages before we encourage new users to use those features... they're welcome to them, but they are also experimental and without any API guarantees. Is this something that was discussed? I don't remember hearing this before.

I hope I'll have time to do a bit more review sometime soon.

@enathang
Copy link
Author

No, it wasn't discussed or anything. Personally, I'm a strong proponent of pushing them even while they're in the experimental phase.

But, leaving it out makes this PR a bit easier, so no complaints on my end.

@DigitalBrains1
Copy link
Member

DigitalBrains1 commented Oct 30, 2025

I was actually rephrasing my post because I was unhappy with it. But now that you answered, I'll put it here.

I'm a bit conflicted, since we haven't released these packages and they are still experimental and without API guarantees.

There's definitely something to say for just offering it to users with a bunch of caveats, even before a release. We do do this in a sense, as they're right at the top of our Ecosystem page, including instructions for how to install them in the starter project.

All the things in the ecosystem page are interesting, so giving our ecosystem page (which contains the instructions to add the packages!) a more prominent place in the README for the starter projects seems very sensible.

We might need to be a bit more explicit about the stability of clash-cores. [edit] Ah no, wait, we have the under construction right at the top :-). I don't think my brain is firing on all cylinders to be honest. [/edit]

@jaschutte
Copy link
Contributor

  1. The nix flake can optionally be used to pull in the tools required by makefile. However, I think it still also accidentally pulls in Haskell stuff. So, I still have to further trim down the flake.nix.

So do you do not want to pull in any Haskell dependencies via the Nix flake? Why not? If someone already uses Nix, why not allow them to use the Nix cache for the Haskell dependencies?

Copy link
Member

@DigitalBrains1 DigitalBrains1 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You forgot to run render.hs.

Could you add Stack support?

I still have more feedback, but there's no more week left now. I'll continue later; this is already a lot!

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we should include a LICENSE file in our starter projects. Our users are free to choose a licence for their projects and can create their own LICENSE file if they want to.

{-|
Module : Domain
Copyright : Copyright © 2024 QBayLogic B.V.
License : MIT
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please see here:

The default license for each of the starter project is BSD2. However, this whole repository -including every starter project- is licensed under CC0. That means the authors, to the extent possible under law, have waived all copyright and related or neighboring rights to this "Clash Example Project". Feel free to choose any license for the starter projects that you want.

Would the authors of this Orangecrab code and accompanying files mind releasing it under CC0 1.0 Universal? Otherwise I think we have a bit of a problem regarding the licence. Once these files are relicensed under CC0, I'd like to suggest removing the whole licence header from all files, similar to how the other starter projects do it.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The makefile in here evolved from a long history of quick-and-dirty updates of some past projects of mine, where I mostly was interested in getting a rudimentary synthesis flow just running back then. You can still find some public origins of it at https://github.com/reactive-systems/KitchenTimer/blob/master/makefile, or a later variant that's more close to the given version at https://github.com/kleinreact/clash-demo/blob/main/makefile. Licenses apply as given by these repositories.

All other lines of Haskell code in here that carry my signature have been created as an employee for QBayLogic. Hence, QBayLogic owns the code and can always re-license it according to their demands.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Licenses apply as given by these repositories.

Are you saying you don't want to relicense your makefile to be compatible with this repository's license? If so, how do you think we should proceed?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Afaik MIT does not put any restrictions in terms of reuse or relicensing.

What I wanted to say is that everybody should be able to do with the makefile whatever they want without the need of any action of its originator. That's the joy of MIT, right?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd say this part of the MIT licence

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

prevents recipients of the licensed work from redistributing it under CC0 (or potentially other licences; the whole idea is that the user of the starter project is unencumbered in their choice of licence for their project).

But it's fairly inconsequential here; if you, the author, are okay with releasing the Makefile under CC0, that is all that needs to be established here in this PR.

Thank you!

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I notice the README does not mention how to get a starter project going, like we do for simple:

Stack users can run stack new my-clash-project clash-lang/simple. Cabal users can download a zip containing the project.

Could you make the README look more like the README for simple, just deviating where useful?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This file differs wildly from the one in simple. Please use the version of simple and only deviate where warranted.

Copy link
Member

@DigitalBrains1 DigitalBrains1 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah woops I'm requesting changes with my review immediately above but forgot to select that again after my choice got reset through a little oopsie that cost me tons of time...

@DigitalBrains1
Copy link
Member

DigitalBrains1 commented Nov 1, 2025

Thanks a lot for all your work on the nice starter project! It has a well-written instructive README, it is easy to use and it adds support for a very affordable dev board. You could say a starter dev board.

Note that all my little style suggestions to the README could quickly be applied with the "Add suggestion to batch" button and then the batch can be committed. However, GitHub has a new view for the Files tab and it doesn't seem to support that. If you want to be able to add suggestions to a batch, go to the Files tab and in the top right corner find the "Preview - Switch back - Feedback" text. Click "Switch back", and you'll get back to the old view that has "Add suggestion to batch". Of course if instead of "Preview - Switch back" it says "✨ Try the new experience", then you're already in the old view.

I wrote:

Could you add Stack support?

And I thought it'd be a matter of copying the stack.yaml from simple and add a few entries to the .gitignore. But that's not correct! We still have the Makefile then which doesn't actually use Stack. There's however two reasons I'd like to have Stack in there. First of all, currently, if you do stack new my-clash-project clash-lang/orangecrab, you get a whole bunch of warnings that are confusing to the user. Secondly, our recommended way of using Clash is with a starter project and Stack. So I'd like our actually recommended setup to work. Don't bother updating Clash 1.8.2 in stack.yaml to 1.8.3. Somehow, we forgot to do that when we released 1.8.3 even though it's one of the points in the checklist for releases. But we'll have a 1.8.4 Real Soon Now™ so let's tackle it then.

Perhaps you can add this to build.cfg:

USE_STACK=yes

and then use that var in the Makefile to switch between stack and cabal.

-l ${BUILDDIR}/log/synth.log \
-p 'read_verilog $(dir $^)/*.v; synth_ecp5 -top $(TOP) -json $@'

${BUILDDIR}/02-hdl/${TOP}.v: src/${NAME}.hs dist-newstyle/packagedb src/*
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So what this means, I think, is:

cabal run clash will only be invoked again, if:

  • src/${NAME}.hs changes
  • dist-newstyle/packagedb changes
  • any file in the directory src changes

But to take this project as an example, if the module OrangeCrab.Domain changes, and the user invokes say make or make upload, then Clash will not be invoked, because the changed file is in a subdirectory. With make, probably nothing happens as everything is up to date. With make upload, it will upload the old bitstream.

I am unsure how you'd tell make when a Haskell package has changed.

Copy link
Member

@DigitalBrains1 DigitalBrains1 Nov 2, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can think of a partial solution. It requires a two-step process. I didn't succeed in getting it to first run a recipe for a file, and only then check whether that changed the file.

For the haskell target, I'm just using the Makefile as a glorified shell script executer. It is separate from the rest. I was first going to name it cabal, but then I thought that'd be confusing when Stack is added.

${BUILDDIR}/02-hdl/${TOP}.v: ${BUILDDIR}/log/cabal-build.log
        mkdir -p ${BUILDDIR}/01-clash
        echo
        ${CABAL} v2-run clash -- \
                -package-env $(abspath).ghc.environment.* \
                --verilog \
                -outputdir ${BUILDDIR}/01-clash \
                ${NAME}
        rm -Rf ${BUILDDIR}/02-hdl
        mv ${BUILDDIR}/01-clash/${NAME}.topEntity ${BUILDDIR}/02-hdl

haskell:
        mkdir -p ${BUILDDIR}/log
        ${CABAL} v2-build --build-summary=${BUILDDIR}/log/cabal-build.log

.PHONY: haskell

Now if you invoke make haskell followed by any other target, only the necessary steps are done, because when cabal v2-build concludes that nothing needs to be done, it's log / build summary file will remain untouched. Conversely, when something Haskelly changes, that file gets a new mtime and other targets will pick up on that.

Note I also changed the way the top level Haskell file is passed to cabal run clash.

-outputdir ${BUILDDIR}/01-clash \
$<
rm -Rf ${BUILDDIR}/02-hdl
mv ${BUILDDIR}/01-clash/${NAME}.topEntity ${BUILDDIR}/02-hdl
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This both assumes that the top entity is named literally topEntity and that there are no subentities with a SYNTHESIZE annotation (they would not be copied here). I think the README should mention these requirements.

Comment on lines +54 to +58
${CABAL} run clash -- \
-package-env $(abspath).ghc.environment.* \
--verilog \
-outputdir ${BUILDDIR}/01-clash \
$<
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This invokes

cabal run clash -- src/Blink.hs

but that is not truly the correct thing for a project with the structure of our starter projects. You should just invoke

cabal run clash -- Blink

which will pick up the entity from the package built by Cabal.

Passing the source file means you again compile Blink.hs from source and only its imports come from pre-compiled stuff. This also means none of the things in orangecrab.cabal or cabal.project apply to Blink.hs, it is compiled with the builtin settings of the Clash compiler. There are situations where the two are simply not equivalent at all.

@lmbollen
Copy link
Member

lmbollen commented Nov 3, 2025

  1. The nix flake can optionally be used to pull in the tools required by makefile. However, I think it still also accidentally pulls in Haskell stuff. So, I still have to further trim down the flake.nix.

So do you do not want to pull in any Haskell dependencies via the Nix flake? Why not? If someone already uses Nix, why not allow them to use the Nix cache for the Haskell dependencies?

This was discussed offline. The main reason is that this is a stack template, so it's tailored to stack users. We don't want to run into the situation where we offer an accessible way to obtain the required synthesis tools(flake.nix) that would pull those users out of their ecosystem.

For us the alternative was not offering a flake.nix at all.

We can later still make a template for nix users similar to simple-nix.

@kleinreact
Copy link
Member

kleinreact commented Nov 6, 2025

We might need to be a bit more explicit about the stability of clash-cores. [edit] Ah no, wait, we have the under construction right at the top :-). I don't think my brain is firing on all cylinders to be honest. [/edit]

Maybe not the right place to discuss this, but we also still ship clash-cores on the 1.8 branch and publish it with the compiler, e.g. its still part of the sources recently released at https://github.com/clash-lang/clash-compiler/releases/tag/v1.8.4 (yes: there is a clash-cores-1.8.4 inside, which is not marked experimental).

NB: some of my private projects even still rely on this fact, as they only need basic stuff like the UART and pulling that from the release package is much more convenient than switching to the new repository and checking it out from there instead.

@DigitalBrains1
Copy link
Member

DigitalBrains1 commented Nov 7, 2025

Ah, yes, you should definitely not consider that a released clash-cores.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants