# How to use a BlackBox to specify a predefined type in existing HDL

**URL:** <https://clash-lang.discourse.group/t/how-to-use-a-blackbox-to-specify-a-predefined-type-in-existing-hdl/60>\
**Category:** Clash Help\
**Created:** [December 17, 2024, 10:41pm UTC](https://clash-lang.discourse.group/t/how-to-use-a-blackbox-to-specify-a-predefined-type-in-existing-hdl/60 "2024-12-17T22:41:58Z")\
**Posts on this page:** 3\
**Page:** 1

<div class="post-metadata">

**Author:** ![tich](https://avatars.discourse-cdn.com/v4/letter/t/838e76/32.png) [@tich](https://clash-lang.discourse.group/u/tich)\
**Post date:** [December 17, 2024, 10:41pm UTC](https://clash-lang.discourse.group/t/how-to-use-a-blackbox-to-specify-a-predefined-type-in-existing-hdl/60/1 "2024-12-17T22:41:58Z")

</div>

I would like Synthesize topEntity to generate the following code Note packet\_control\_t is predefined type:

```auto
library nuand;
    use nuand.fifo_readwrite_p.all;

entity gnodeb_top is
  port (
    tx_clock : in std_logic ;
    tx_reset : in std_logic ;
    tx_packet_control : in packet_control_t;
    tx_packet_empty : in std_logic;
    tx_packet_ready : out std_logic;
    tx_leds : out std_logic_vector( 2 downto 0)
  ) ;
end gnodeb_top ;

```

Instead the generated code looks as follows:

```auto
entity gnodeb_top is
 port(-- clock
      tx_clock : in gnodeb_top_types.clk_System;
      -- reset
      tx_reset : in gnodeb_top_types.rst_System;
      tx_packet_control : in std_logic_vector(50 downto 0);
      tx_packet_empty : in boolean;
      tx_packet_ready : out boolean;
      tx_leds : out std_logic_vector(2 downto 0));
end;

```

My Clash code looks as follows:

```auto
import Clash.Prelude
import Clash.Signal
import Clash.Annotations.TopEntity
import Clash.Annotations.Primitive
import Data.String.Interpolate (__i)
-- PacketControl type matching VHDL packet_control_t record
data PacketControl = PacketControl
  { pktCoreId :: Vec 8 Bit -- std_logic_vector(7 downto 0)
  , pktFlags :: Vec 8 Bit -- std_logic_vector(7 downto 0)
  , pktSop :: Bool -- std_logic
  , pktEop :: Bool -- std_logic
  , pktData :: Vec 32 Bit -- std_logic_vector(31 downto 0)
  , dataValid :: Bool -- std_logic
  } deriving (Show)

{-# ANN type PacketControl (InlinePrimitive [VHDL] [__i|
  BlackBox:
    kind: Declaration
    name: GNodeBTop.PacketControl
    template: |-
      packet_control_t
   |] ) #-}

-- Top entity definition
topEntity
  :: "tx_clock" ::: Clock System
  -> "tx_reset" ::: Reset System
  -> "tx_packet_control" ::: PacketControl
  -> "tx_packet_empty" ::: Signal System Bool
  -> ( "tx_packet_ready" ::: Signal System Bool
     , "tx_leds" ::: Signal System (Vec 3 Bit)
     )
topEntity txClock txReset txPacketControl txPacketEmpty =
  ( txPacketReady
  , txLeds
  )
  where
    -- These are placeholder implementations
    txPacketReady = pure False
    txLeds = pure (repeat low)

{-# ANN topEntity
  (Synthesize
    { t_name = "gnodeb_top"
    , t_inputs = [ PortName "tx_clock"
                 , PortName "tx_reset"
                 , PortName "tx_packet_control"
                 , PortName "tx_packet_empty"
                 ]
    , t_output = PortProduct ""
                   [ PortName "tx_packet_ready"
                   , PortName "tx_leds"
                   ]
    }) #-}

```

How can I define my BlackBox to achieve the desired VHDL?

---

<div class="post-metadata">

**Author:** ![DigitalBrains](https://yyz2.discourse-cdn.com/free1/user_avatar/clash-lang.discourse.group/digitalbrains/32/28_2.png) [@DigitalBrains](https://clash-lang.discourse.group/u/DigitalBrains)\
**Post date:** [December 20, 2024, 12:42pm UTC](https://clash-lang.discourse.group/t/how-to-use-a-blackbox-to-specify-a-predefined-type-in-existing-hdl/60/2 "2024-12-20T12:42:27Z")

</div>

Hello tich and welcome!

I’m afraid the user cannot control the types of ports in a generated entity like that. By far the simplest solution would be to write a wrapper in VHDL that simply converts the port types of this entity to what you want to have. If your Clash-generated entity isn’t the true top entity of the design and you combine it with stuff that is not part of what Clash generates, that might be a pretty natural solution.

But while custom primitives generally only control part of the architecture of a generated VHDL entity rather than the whole entity, you can actually have Clash generate additional files where you do have full control. The following example combines two features:

```haskell
module IncludeFile where

import Clash.Annotations.Primitive
import Clash.Prelude
import Data.String.Interpolate (__i)

topEntity ::
  Unsigned 8 ->
  Unsigned 8
topEntity x = includeFile `hwSeqX` x
{-# OPAQUE topEntity #-}

includeFile :: ()
includeFile = ()
{-# OPAQUE includeFile #-}
{-# ANN includeFile hasBlackBox #-}
{-# ANN includeFile (
   let
     primName = 'includeFile
   in InlineYamlPrimitive [VHDL] [__i|
      BlackBox:
        name: #{primName}
        kind: Declaration
        renderVoid: RenderVoid
        template: |-
          -- You could reference ~INCLUDENAME[0].txt
        includes:
          - name: pickaname
            extension: txt
            template: |-
              Hello world!

              We have control over the contents of the complete file.
   |]) #-}

```

With the `includes` key, we can list one or several files to be generated whenever this primitive is referenced. Here, I’ve just created a literal text file, but it could be a VHDL file.

Using `clash-lib`’s `BlackBoxFunction`, you could even generate the actual text using Haskell code, which can reference arguments you give to the primitive to control the result. But this requires a deeper dive into `clash-lib` I won’t do right now, and also, I’d like to note that not only is it hardly documented, it is also not a stable API, so we can change `clash-lib` between minor releases. But it’s out there, and if you want more info we can provide it.

Anyway, back to what I’ve done here. If your code references `includeFile` (correctly…), Clash will generate a file with a name like `topEntity_pickaname_6367058288311B08.txt`, where it prefixes the name with the synthesis entity and the hexadecimal number is a hash of the context, such that if you reference it multiple times but the context for the primitive is identical, it will only generate one such file and reference that one file multiple times instead of creating a new file for each reference. The template for the include file has the exact same syntax and substitution features as the template for the primitive itself, but now we control the whole generated file instead of just a part of the architecture.

I use another feature: the combination of `RenderVoid` and `hwSeqX`. Normally, the output of your primitive needs to be _used_ before it will be rendered. These two together allow us to generate HDL even when the output of the primitive is unused. This might be a useful trick if you want to try and use this approach with a separate file to glue things together in your design.

The `hasBlackBox` annotation here prevents us from accidentally generating Verilog or SystemVerilog for which we did not define a black box. HDL generation will only look for primitive annotations for the language being targetted, so for Verilog and SystemVerilog it would not even notice the `InlineYamlPrimitive [VHDL]` and happily render the body of the function instead of the black box. `hasBlackBox` turns this case into an error.

All functions annotated with primitive or top entity annotations need to also have an `OPAQUE` pragma. Or if you’re using a GHC older than 9.4, `NOINLINE`, since `OPAQUE` doesn’t exist there.

You can get `library` and `use` calls in your generated VHDL with the `libraries` and `imports` keys of the primitive YAML. For instance, [this primitive](https://github.com/clash-lang/clash-compiler/blob/8a827e1e232b795a92da584c3e9cd51a46315680/clash-lib/prims/vhdl/Clash_Explicit_Testbench.primitives.yaml#L1-L63) generates a VHDL package with a function definition it needs and then adds a `use ~INCLUDENAME[0].all` line to every Clash-generated VHDL file that needs that package (with the proper name substituted).

I will finally note that if you replace `PortName "tx_packet_control"` in your original code by

```haskell
                 , PortProduct "tx_packet_control"
                   [ PortName "core_id"
                   , PortName "flags"
                   , PortName "sop"
                   , PortName "eop"
                   , PortName "data"
                   , PortName "data_valid"
                   ]

```

you’ll at least get this entity from Clash:

```vhdl
entity gnodeb_top is
  port(-- clock
       tx_clock : in gnodeb_top_types.clk_System;
       -- reset
       tx_reset : in gnodeb_top_types.rst_System;
       tx_packet_control_core_id : in std_logic_vector(7 downto 0);
       tx_packet_control_flags : in std_logic_vector(7 downto 0);
       tx_packet_control_sop : in boolean;
       tx_packet_control_eop : in boolean;
       tx_packet_control_data : in std_logic_vector(31 downto 0);
       tx_packet_control_data_valid : in boolean;
       tx_packet_empty : in boolean;
       tx_packet_ready : out boolean;
       tx_leds : out std_logic_vector(2 downto 0));
end;

```

---

<div class="post-metadata">

**Author:** ![tich](https://avatars.discourse-cdn.com/v4/letter/t/838e76/32.png) [@tich](https://clash-lang.discourse.group/u/tich)\
**Post date:** [December 21, 2024, 9:11pm UTC](https://clash-lang.discourse.group/t/how-to-use-a-blackbox-to-specify-a-predefined-type-in-existing-hdl/60/3 "2024-12-21T21:11:54Z")

</div>

Thanks DigitalBrains, I appreciate your explanation. My clash generated entity is the true top entity so I will follow your advice to write a VHDL wrapper.

Thank you once again.
