Coder Social home page Coder Social logo

j1clash's Introduction

j1clash

This is an implementation of the J1 stack-machine CPU in clash-lang. I did this largely as an excuse to learn Clash, and I'm not quite there yet, so please excuse the poor code quality.

The implementation currently only consists of the ALU and control unit. Interfacing (an abstract model of) memory should be next step, and shouldn't be too hard with Clash's memory primitives.

The hard part is getting all of this to run on an actual FPGA for testing. I can't for the life of me find a nice way to run it on my FPGA. If you have any experience with this sort of thing at all (especially clash!) do feel free to reach out.

(The rest of this README is autogenerated by stack, there's nothing interesting below this line)


Table of Contents

Getting this project

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

Building and testing this project

There's a number of ways to build this project on your machine. The recommended way of doing so is using Stack, whose instructions will follow directly after this section.

Stack (Windows, Linux, MacOS) [recommended]

Install Stack using your package manager or refer to the How to install section of the Stack manual.

Build the project with:

stack build

To run the tests defined in tests/, use:

stack test

To compile the project to VHDL, run:

stack run clash -- Example.Project --vhdl

You can find the HDL files in vhdl/. The source can be found in src/Example/Project.hs.

Cabal (Linux, MacOS)

The following instructions only work for Cabal >=3.0 and GHC >=8.4.

First, update your cabal package database:

cabal update

You only have to run the update command once. After that, you can keep rebuilding your project by running the build command:

cabal build

To run the tests defined in tests/, use:

cabal run test-library --enable-tests
cabal run doctests --enable-tests

To compile the project to VHDL, run:

cabal run clash -- Example.Project --vhdl

You can find the HDL files in vhdl/. The source can be found in src/Example/Project.hs.

REPL

Clash offers a REPL as a quick way to try things, similar to Python's python or Ruby's irb. Stack users can open the REPL by invoking:

stack run clashi

Cabal users use:

cabal run clashi

IDE support

We currently recommend Visual Studio Code in combination with the Haskell plugin. All you need to do is open this folder in VSCode; it will prompt you to install the plugin.

Project overview

This section will give a tour of all the files present in this starter project. It's also a general introduction into Clash dependency management. It's not an introduction to Clash itself though. If you're looking for an introduction to Clash, read "Clash.Tutorial" on Hackage.

test
├── bin
│   ├── Clash.hs
│   └── Clashi.hs
├── src
│   └── Example
│       └── Project.hs
├─── tests
│   ├── Tests
│   │   └── Example
│   │       └── Project.hs
│   ├── doctests.hs
│   └── unittests.hs
├── cabal.project
├── test.cabal
└── stack.yaml

test.cabal

This is the most important file in your project. It describes how to build your project. Even though it ends in .cabal, Stack will use this file too. It starts of with meta-information:

cabal-version:       2.4
name:                test
version:             0.1
license:             BSD-2-Clause
author:              John Smith <[email protected]>
maintainer:          John Smith <[email protected]>

If you decide to publish your code on Hackage, this will show up on your package's front page. Take note of the license, it's set to BSD-2-Clause by default, but this might bee too liberal for your project. You can use any of the licenses on spdx.org/licenses. If none of those suit, remove the license line, add license-file: LICENSE, and add a LICENSE file of your choice to the root of this project. Moving on:

common common-options
  default-extensions:
    BangPatterns
    BinaryLiterals
    ConstraintKinds
    [..]
    QuasiQuotes

    -- Prelude isn't imported by default as Clash offers Clash.Prelude
    NoImplicitPrelude

Clash's parent language is Haskell and its de-facto compiler, GHC, does a lot of heavy lifting before Clash gets to see anything. Because using Clash's Prelude requires a lot of extensions to be enabled to be used, we enable them here for all files in the project. Alternatively, you could add them where needed using {-# LANGUAGE SomeExtension #-} at the top of a .hs file instead. The next section, ghc-options, sets warning flags (-Wall -Wcompat) and flags that make GHC generate code Clash can handle.

Note that this whole section is a common "stanza". We'll use it as a template for any other definitions (more on those later). The last thing we add to the common section is some build dependencies:

  build-depends:
    base,
    Cabal,

    -- clash-prelude will set suitable version bounds for the plugins
    clash-prelude >= 1.2.4 && < 1.6,
    ghc-typelits-natnormalise,
    ghc-typelits-extra,
    ghc-typelits-knownnat

These dependencies are fetched from Hackage, Haskell's store for packages. Next up is a library stanza. It defines where the source is located, in our case src/, and what modules can be found there. In our case that's just a single module, Example.Project.

library
  import: common-options
  hs-source-dirs: src
  exposed-modules:
    Example.Project
  default-language: Haskell2010

Note that extra dependencies could be added by adding a build-depends line to this section. The following section defines a testsuite called doctests. Doctests are tests that are defined in the documentation of your project. We'll see this in action in src/.

test-suite doctests
  import:           common-options
  type:             exitcode-stdio-1.0
  default-language: Haskell2010
  main-is:          doctests.hs
  hs-source-dirs:   tests

  build-depends:
    base,
    doctest >= 0.16.1 && < 0.18,
    clash-prelude

Last but not least, another testsuite stanza is defined:

test-suite test-library
  import: common-options
  default-language: Haskell2010
  hs-source-dirs: tests
  type: exitcode-stdio-1.0
  ghc-options: -threaded
  main-is: unittests.hs
  other-modules:
    Tests.Example.Project
  build-depends:
    test,
    QuickCheck,
    hedgehog,
    tasty >= 1.2 && < 1.3,
    tasty-hedgehog,
    tasty-th

These testsuites are executed when using stack test or cabal test --enable-tests. Note that Cabal swallows the output if more than one testsuite is defined, as is the case here. You might want to consider running the testsuites separately. More on tests in /tests.

cabal.project

A cabal.project file is used to configure details of the build, more info can be found in the Cabal user documentation. We use it to make Cabal always generate GHC environment files, which is a feature Clash needs when using Cabal. It also sets a flag for older versions of Clash, massively speeding up compilation. It is ignored by Stack.

packages:
  test.cabal

package clash-prelude
  -- 'large-tuples' generates tuple instances for various classes up to the
  -- GHC imposed maximum of 62 elements. This severely slows down compiling
  -- Clash, and triggers Template Haskell bugs on Windows. Hence, we disable
  -- it by default. This will be the default for Clash >=1.4.
  flags: -large-tuples

write-ghc-environment-files: always

cabal.project can be used to build multi-package projects, by extending packages.

stack.yaml

While Cabal fetches packages straight from Hackage (with a bias towards the latest versions), Stack works through snapshots. Snapshots are an index of packages from Hackage know to work well with each other. In addition to that, they specify a GHC version. These snapshots are curated by the community and FP Complete and can be found on stackage.org.

resolver: lts-17.9

extra-deps:
  # At the time of writing, no snapshot includes Clash 1.4 yet so we add it - and
  # its dependencies - manually.
  - lazysmallcheck-0.6
  - Stream-0.4.7.2
  - arrows-0.4.4.2
  - clash-prelude-1.4.1
  - clash-lib-1.4.1
  - clash-ghc-1.4.1

This project uses lts-17.9, which includes Clash 1.2.5. We've added the extra-deps section to make sure Stack fetches the latest version of Clash, 1.4.1, instead. The point of this exercise is to make reproducible builds. Or in other words, if a stack build works now, it will work in 10 years too.

Note: If you need a newer Clash version, simply change the version bounds in test.cabal and follow the hints given by Stack.

src/

This is where the source code of the project lives, as specified in test.cabal. It contains a single file, Example/Project.hs which starts with:

module Example.Project (topEntity, plus) where

import Clash.Prelude

-- | Add two numbers. Example:
--
-- >>> plus 3 5
-- 8
plus :: Signed 8 -> Signed 8 -> Signed 8
plus a b = a + b

test.cabal enabled NoImplicitPrelude which enables the use of Clash.Prelude here. Next, a function plus is defined. It simply adds two numbers. Note that the example (>>> plus 3 5) gets executed by the doctests defined for this project and checked for consistency with the result in the documentation (8).

-- | 'topEntity' is Clash's equivalent of 'main' in other programming
-- languages. Clash will look for it when compiling 'Example.Project'
-- and translate it to HDL. While polymorphism can be used freely in
-- Clash projects, a 'topEntity' must be monomorphic and must use non-
-- recursive types. Or, to put it hand-wavily, a 'topEntity' must be
-- translatable to a static number of wires.
topEntity :: Signed 8 -> Signed 8 -> Signed 8
topEntity = plus

as the comment says topEntity will get compiled by Clash if we ask it to compile this module:

stack run clash -- Example.Project --vhdl

or

cabal run clash -- Example.Project --vhdl

We could instead ask it to synthesize plus instead:

stack run clash -- Example.Project --vhdl -main-is plus

If you want to play around with Clash, this is probably where you would put all the definitions mentioned in "Clash.Tutorial" on Hackage.

tests/

Most of this directory is scaffolding, with the meat of the tests being defined in tests/Tests/Example/Project.hs. Writing good test cases is pretty hard: edge cases are easy to forget both in the implementation and tests. To this end, it's a good idea to use fuzz testing. In this project we use Hedgehog:

import Example.Project (plus)

prop_plusIsCommutative :: H.Property
prop_plusIsCommutative = H.property $ do
  a <- H.forAll (Gen.integral (Range.linear minBound maxBound))
  b <- H.forAll (Gen.integral (Range.linear minBound maxBound))
  plus a b === plus b a

This test generates two numbers a and b that fit neatly into domain of Signed 8, thanks to the use of minBound and maxBound. It then tests whether the plus operation commutes. I.e., whether a + b ~ b + a. All functions called prop_* are collected automatically:

tests :: TestTree
tests = $(testGroupGenerator)

We can run the tests using stack test or cabal run test-library --enable-tests:

.
  Tests.Example.Project
    plusIsCommutative: OK
        ✓ plusIsCommutative passed 100 tests.

All 1 tests passed (0.00s)

Change the license

By default test.cabal sets its license field to BSD-2-Clause. You might want to change this.

j1clash's People

Contributors

jacobjwalters avatar

Watchers

 avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.