Custom *internal* bit representation for types?

53 views
Skip to first unread message

ÉRDI Gergő

unread,
Apr 4, 2021, 8:15:16 AM4/4/21
to clash-l...@googlegroups.com
Hi,

I have three examples of types where I can shave off one bit from each,
and I would like to tell Clash about it. I have the elements stored in an
`asyncRom`, and I would like to make the `asyncRom` size smaller. IIUC,
adding a custom `BitPack` instance wouldn't change the internal
representation, if I am not manually `pack`/`unpack`ing values at edges of
my circuit.

The three types in question, just in case it matters:

1. An ADT with six constructors, but eight possible values:

data ALU = Add Bool | Sub Bool | And | Or | XOr | BCD

This takes 3 + 1 bits by default, but could be stored on 3 bits.

2. `Wedge A B`, where `A` and `B` are enum types with 4 and 3 construcors.

This takes 2 bits to store the decision of `Here`/`There`/`Nowhere`, and
then max(2,2)=2 bits to store the `A` or the `B`, for a total of 4 bits.
We could store it on 3 bits, if we stored it as `Either A B'`, where `B'`
is `B` with a new `NotReallyB` constructor added.

3. `Maybe (Index 547)`

This takes 11 bits by default, but of course could be stored on just 10.


So, what are my options?

Thanks,
Gergo

Rowan Goemans

unread,
Apr 4, 2021, 1:42:29 PM4/4/21
to clash-l...@googlegroups.com
Happy easter :)!

Do you know about `BitRepresentation` annotations? See here:
http://hackage.haskell.org/package/clash-prelude-1.4.0/docs/Clash-Annotations-BitRepresentation.html

It's a bit limited but from what I understand from your mail you should
be able to do what you want. I only ever used it for very simple types
so I'm no expert.

Rowan

Pat the Builder

unread,
Apr 5, 2021, 3:41:18 AM4/5/21
to Clash - Hardware Description Language
Hi Grego,

Howsabout using a `newtype`? It still lets you add the extra compression but also lets you fall back on the underlying representation.

Regards,

Paddy

Martijn Bastiaan

unread,
Apr 5, 2021, 10:45:40 AM4/5/21
to clash-l...@googlegroups.com
Rowan's suggestion is probably your best bet. There's also TH code to generate various representations:


(1) could be derived with the packed derivator I think.

Op ma 5 apr. 2021 om 09:41 schreef Pat the Builder <paddythep...@gmail.com>:
--
You received this message because you are subscribed to the Google Groups "Clash - Hardware Description Language" group.
To unsubscribe from this group and stop receiving emails from it, send an email to clash-languag...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/clash-language/bfa00bd2-17c8-40b5-a82d-6a9777c2a00bn%40googlegroups.com.

ÉRDI Gergő

unread,
Apr 6, 2021, 7:50:03 AM4/6/21
to clash-l...@googlegroups.com
On Sun, 4 Apr 2021, Rowan Goemans wrote:

> Do you know about `BitRepresentation` annotations? See here:
> http://hackage.haskell.org/package/clash-prelude-1.4.0/docs/Clash-Annotations-BitRepresentation.html

No, I did not. Looks like it's exactly what I was looking for! I
implemented the first two:

1. 8 possible values, organized into 6 constructors:

```
data ALU = Add Bool | Sub Bool | And | XOr | Or | BCD
deriving (Eq, Ord, Show, Generic, NFDataX, Lift)

{-# ANN module (DataReprAnn
$(liftQ [t|ALU|])
3
[ ConstrRepr 'Add 0b110 0b000 [0b001]
, ConstrRepr 'Sub 0b110 0b010 [0b001]
, ConstrRepr 'And 0b111 0b100 []
, ConstrRepr 'XOr 0b111 0b101 []
, ConstrRepr 'Or 0b111 0b110 []
, ConstrRepr 'BCD 0b111 0b111 []
]) #-}
```

2. `Wedge A B`, where `A` has 3 values, and `B` 4:

```
data InAddr
= FromPtr
| FromPort
| IncrPC
| IncrSP
deriving (Show, Eq, Ord, Enum, Bounded, Generic, NFDataX, Lift)

data OutAddr
= ToPtr
| ToPort
| DecrSP
deriving (Show, Eq, Ord, Enum, Bounded, Generic, NFDataX, Lift)

{-# ANN module (DataReprAnn
$(liftQ [t|Wedge OutAddr InAddr|])
3
[ ConstrRepr 'Here 0b100 0b000 [0b011]
, ConstrRepr 'There 0b100 0b100 [0b011]
, ConstrRepr 'Nowhere 0b111 0b011 []
]) #-}
```

3. `Maybe (Index 548)`

Now this one is trickier... I tried the following, to no avail:

```
type MicroSize = $(microSize)
type MicroPtr = Index MicroSize

{-# ANN module (DataReprAnn
$(liftQ [t|Maybe MicroPtr|])
10
[ ConstrRepr 'Nothing 0b1111111111 0b1111111111 []
, ConstrRepr 'Just 0b0000000000 0b0000000000 [0b1111111111]
]) #-}
```

I don't get any error messages, but in the generated Verilog code, I can
see that `Maybe MicroPtr` is still stored as 11 bits; for example, `Just
257` is `{1'b1,10'd257}`.

ÉRDI Gergő

unread,
Apr 6, 2021, 8:38:18 AM4/6/21
to clash-l...@googlegroups.com
On Tue, 6 Apr 2021, ÉRDI Gergő wrote:

> No, I did not. Looks like it's exactly what I was looking for! I implemented
> the first two:

Actually, it seems they don't work as expected, because when I run the
simulation via Clashilator, the result is a mess... I'll work on isolating
a small self-contained example.

ÉRDI Gergő

unread,
Apr 6, 2021, 8:42:12 AM4/6/21
to clash-l...@googlegroups.com
To be more precise, the ALU annotation works, the Wedge one breaks things.

Peter Lebbing

unread,
Apr 6, 2021, 10:36:03 AM4/6/21
to clash-l...@googlegroups.com
Hi Gergő,

On Tue, 6 Apr 2021, ÉRDI Gergő wrote:
> To be more precise, the ALU annotation works, the Wedge one breaks things.

When matching, `DataReprAnn` is tried in order. So the Wedge one falls
into There before it reaches Nowhere. I tried this:

--8<---------------cut here---------------start------------->8---
{-# ANN module (DataReprAnn
$(liftQ [t|Wedge OutAddr InAddr|])
3
, ConstrRepr 'Nowhere 0b111 0b011 []
[ ConstrRepr 'Here 0b100 0b000 [0b011]
, ConstrRepr 'There 0b100 0b100 [0b011]
]) #-}

deriveBitPack [t|Wedge OutAddr InAddr|]
--8<---------------cut here---------------end--------------->8---

(also deriving BitPack for OutAddr/InAddr) and it's `pack` and `unpack`
seem to behave better. Now `unpack 0b111` comes out Nowhere. With the
old order, it came out `There DecrSP`.

HTH,

Peter.

Peter Lebbing

unread,
Apr 6, 2021, 11:24:09 AM4/6/21
to ÉRDI Gergő, clash-l...@googlegroups.com
On Tue, 6 Apr 2021, ÉRDI Gergő wrote:
> ```
> type MicroSize = $(microSize)
> type MicroPtr = Index MicroSize
>
> {-# ANN module (DataReprAnn
> $(liftQ [t|Maybe MicroPtr|])
> 10
> [ ConstrRepr 'Nothing 0b1111111111 0b1111111111 []
> , ConstrRepr 'Just 0b0000000000 0b0000000000
> [0b1111111111]
> ]) #-}
> ```

Because `MicroPtr` is just a type alias, the `DataReprAnn` simply never
fires! It needs to be written as:

```
{-# ANN module (DataReprAnn
$(liftQ [t|Maybe (Index $(microSize))|])
10
[ ConstrRepr 'Nothing 0b1111111111 0b1111111111 []
, ConstrRepr 'Just 0b0000000000 0b0000000000 [0b1111111111]
]) #-}
```

then it works.

HTH,

Peter.

Christiaan Baaij

unread,
Apr 6, 2021, 11:37:53 AM4/6/21
to clash-l...@googlegroups.com
type aliases not working in that particular way seems like a bug; or at least being able to use type aliases like that would be a desirable feature.
I can have a few guess as to why it's broken: Clash throws away type aliases, but the data-repr map might still use the type alias as a key
But it could be something else entirely

--
You received this message because you are subscribed to the Google Groups "Clash - Hardware Description Language" group.
To unsubscribe from this group and stop receiving emails from it, send an email to clash-languag...@googlegroups.com.

Martijn Bastiaan

unread,
Apr 6, 2021, 11:38:30 AM4/6/21
to clash-l...@googlegroups.com
Thanks Gergo, I've created an issue on the tracker:


Op di 6 apr. 2021 om 17:37 schreef Christiaan Baaij <christia...@gmail.com>:

Martijn Bastiaan

unread,
Apr 6, 2021, 11:45:54 AM4/6/21
to clash-l...@googlegroups.com
That's exactly it Christiaan. `liftQ` should probably erase type aliases (if it can).

Op di 6 apr. 2021 om 17:37 schreef Christiaan Baaij <christia...@gmail.com>:
type aliases not working in that particular way seems like a bug; or at least being able to use type aliases like that would be a desirable feature.

ÉRDI Gergő

unread,
Apr 7, 2021, 6:12:30 AM4/7/21
to clash-l...@googlegroups.com
On Tue, 6 Apr 2021, Peter Lebbing wrote:

> When matching, `DataReprAnn` is tried in order. So the Wedge one falls
> into There before it reaches Nowhere.

Thanks, that fixed it.

ÉRDI Gergő

unread,
Apr 7, 2021, 6:28:54 AM4/7/21
to Peter Lebbing, clash-l...@googlegroups.com
On Tue, 6 Apr 2021, Peter Lebbing wrote:

> Because `MicroPtr` is just a type alias, the `DataReprAnn` simply never
> fires! It needs to be written as:

Thanks.

peter.t...@gmail.com

unread,
May 22, 2021, 7:03:38 AM5/22/21
to Clash - Hardware Description Language
I would wonder about defining MaybeB x = NothingB | JustB x

with instance BitPack x => BitPack(MaybeB x) where
         pack NothingB = 0; pack (JustB x) = 1+pack x  

etc. And good luck! (yes, I know it goes wrong if there are 1023 valid codes in type x already - you might be able to insert a runtime check for that, if you ever care).

PTB
Reply all
Reply to author
Forward
0 new messages