Skip to content

Commit

Permalink
Clash.Cores: Add CRC module (#2694)
Browse files Browse the repository at this point in the history
clash-cores/crc: Init
  • Loading branch information
rowanG077 authored May 21, 2024
1 parent a245bb8 commit e6522d7
Show file tree
Hide file tree
Showing 8 changed files with 2,811 additions and 2 deletions.
1 change: 1 addition & 0 deletions clash-cores/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@
* Add SPI master and slave cores
* Add Lattice Semi SB_IO primitive
* Add UART core
* Add CRC core
1 change: 1 addition & 0 deletions clash-cores/LICENSE
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
Copyright (c) 2019, Foamspace Corp.
2023, Amaranth HDL contributors
All rights reserved.

Redistribution and use in source and binary forms, with or without
Expand Down
8 changes: 7 additions & 1 deletion clash-cores/clash-cores.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,9 @@ library
hs-source-dirs: src

exposed-modules:
Clash.Cores.Crc
Clash.Cores.Crc.Internal
Clash.Cores.Crc.Catalog
Clash.Cores.LatticeSemi.ECP5.Blackboxes.IO
Clash.Cores.LatticeSemi.ECP5.IO
Clash.Cores.LatticeSemi.ICE40.Blackboxes.IO
Expand Down Expand Up @@ -176,7 +179,9 @@ library
prettyprinter >= 1.2.0.1 && < 1.8,
prettyprinter-interp ^>= 0.2,
reducers >= 3.12.2 && < 4.0,
text >= 1.2.2 && < 2.2
text >= 1.2.2 && < 2.2,
constraints >= 0.9 && < 1.0,
template-haskell >= 2.12.0.0 && < 2.22

test-suite unittests
import: basic-config
Expand All @@ -188,6 +193,7 @@ test-suite unittests
buildable: False

other-Modules:
Test.Cores.Crc
Test.Cores.Internal.SampleSPI
Test.Cores.Internal.Signals
Test.Cores.SPI
Expand Down
100 changes: 100 additions & 0 deletions clash-cores/src/Clash/Cores/Crc.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
{-|
Copyright : (C) 2024, Rowan Goemans <[email protected]>
License : BSD2 (see the file LICENSE)
Maintainer : QBayLogic B.V. <[email protected]>
Utilities for computing cyclic redundancy checks (CRCs) in software and in
hardware.
CRCs are specified using 'CrcParams' type in conjunction with the 'KnownCrc' type class.
'CrcParams' contains settings for CRC width, polynomial, initial value,
input/output reflection, and output XOR. This fully defines a CRC computation.
Many commonly used CRC algorithms are available in the "Clash.Cores.Crc.Catalog"
module, while most other CRC designs can be accommodated by manually constructing
'CrcParams' and writing a 'KnownCrc' instance.
Call the 'mkSoftwareCrc' using a specifc CRC to create an engine to perform software
computations. It's not intended to be used on hardware.
Example usage:
>>> :set -XMultiParamTypeClasses
>>> import Clash.Prelude
>>> import Clash.Sized.Vector (unsafeFromList)
>>> import Data.Char (ord)
>>> import qualified Data.List as List
>>> import Clash.Cores.Crc
>>> import Clash.Cores.Crc.Catalog
First we convert the check characters to @BitVector 8@ for processing.
>>> charToBv = fromIntegral . ord
>>> checkInput = fmap charToBv "123456789"
>>> checkValue = 0xcbf43926
Here we instantiate a software CRC engine for the 32-bit Ethernet CRC
that can handle @BitVector 8@ input and use it to compute the CRC.
>>> softwareCrc = mkSoftwareCrc Crc32_ethernet d8
>>> crcFromSoftware = digest $ List.foldl' feed softwareCrc checkInput
>>> crcFromSoftware == checkValue
True
For a hardware implementation the first thing you have to do is use Template Haskell
to derive a 'HardwareCrc' instance using 'Clash.Cores.Crc.deriveHardwareCrc'.
Here you need to provide a concrete @dataWidth@ and @nLanes@. Up to
@dataWidth * nLanes@ of an input can be handled in a single clock cycle in
@dataWidth@ increments. See the type of 'crcEngine' to see what impact it has
on the CRC circuit.
>>> :{
deriveHardwareCrc Crc32_ethernet d8 d4
dummy = 1
:}
>>> crcEngine' = exposeClockResetEnable crcEngine systemClockGen resetGen enableGen
>>> myEngine = crcEngine' Crc32_ethernet
Note how we hinted to @clashi@ that our multi-line command was a list of
declarations by including a dummy declaration @dummy = 1@. Without this trick,
@clashi@ would expect an expression and the Template Haskell emitted by
@deriveHardwareCrc@ would not work.
We can give up to 4 bytes from @checkInput@ to our hardware CRC in the format
of @Maybe (Index 4, Vec 4 (BitVector 8))@. The @Maybe@ indicates whether
we want to run the CRC engine this clock cycle. The @Index@ inside the @Maybe@
indicates how many @BitVector 8@ are valid inside the @Vec@. 0 means 1 is valid.
3 means 4 are valid.
>>> hwInp0 = Just (True, 3, unsafeFromList $ fmap charToBv "1234" :: Vec 4 (BitVector 8))
>>> hwInp1 = Just (False, 3, unsafeFromList $ fmap charToBv "5678")
>>> hwInp2 = Just (False, 0, unsafeFromList $ fmap charToBv "9___")
>>> hwInp = [Nothing, hwInp0, hwInp1, hwInp2]
>>> hwOut = myEngine (fromList hwInp)
>>> crcFromHardware = List.last $ sampleN (1 + List.length hwInp) hwOut
>>> crcFromHardware == checkValue
True
Notice that the 'crcEngine' has a latency of one clock cycle.
-}

module Clash.Cores.Crc
( CrcParams(..)
, KnownCrc(..)
-- ** Software
, SoftwareCrc
, mkSoftwareCrc
, reset
, feed
, digest
, rawResidue
, residue
-- ** Hardware
, HardwareCrc
, deriveHardwareCrc
, crcEngine
, crcValidator
) where

import Clash.Cores.Crc.Internal
Loading

0 comments on commit e6522d7

Please sign in to comment.