Skip to content

Compilation


Elaboration

Wildcard Arithmetic Value Checking

When arithmetic operations involve wildcard Int values (Scala Int or DFHDL Int parameters), the wildcard Int value adapts to the bit-accurate value's sign and width. The value is then checked to ensure it fits. This check occurs at three levels, depending on when the value becomes known:

  1. Scala compile-time: literal Scala integers have known values, and therefore known minimum widths, at compile time. In +, - and * that minimum counts as an actual width when the other operand's width is also known at compile time, so the result is the wider of the two and a literal that does not fit widens the operation rather than failing (u8 + 1000 is UInt[10]). The Scala compiler still reports an error where widening cannot express the result, namely a negative literal on the left of -, / or % with an unsigned operand, which those LHS-dominant operations cannot represent. Elsewhere the literal adapts, and the compiler reports a value that does not fit.

  2. DFHDL elaboration-time: non-literal Scala integers (e.g., val x: Int = computeValue(); u8 + x) and DFHDL Int constants whose values are resolved during elaboration. These have no statically known width, so they always adapt, as does any literal meeting a parametric width. A DFHDL elaboration error (Scala runtime error) is generated if the value does not fit.

  3. Synthesis/simulation-time: DFHDL Int parameters that are set externally or computed in complex generation loops may not be known until synthesis or simulation. Assertions must be added to verify these values at the target platform level. This is a planned future feature (TODO).


Generated Files

Compiling a design emits one file per design, plus up to two shared files.

File Contents
<Design>.sv One file per design in the hierarchy
dfhdl_defs.svh DFHDL's own macros and helper definitions. Always emitted, and `included by every design. Public domain, so it can be redistributed with generated output
<Top>_defs.svh Your design's global declarations, named after the top design. Emitted only when something needs to be shared across designs
File Contents
<Design>.vhd One file per design in the hierarchy
dfhdl_pkg.vhd DFHDL's own helper package. Always emitted
<Top>_pkg.vhd Your design's global package, named after the top design. Emitted only when something needs to be shared across designs

The global definitions file

A declaration goes into <Top>_defs.svh / <Top>_pkg.vhd when more than one design must name it. The common case is an enum appearing in a port type, since the two modules on either side of the connection have to agree on the type:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
enum State extends Encoded:
  case IDLE, RUN

class lane extends EDDesign:
  val s = State <> IN
  // ...

class Foo extends EDDesign:
  val s = State <> IN
  val u = new lane
  u.s <> s
Foo_defs.svh
1
2
3
4
5
6
7
`ifndef FOO_DEFS
`define FOO_DEFS
typedef enum logic [0:0] {
  State_IDLE = 0,
  State_RUN  = 1
} t_enum_State;
`endif

Foo.sv
1
2
3
4
5
6
7
8
`include "Foo_defs.svh"

module Foo(
  input  wire t_enum_State s,
  output logic             o
);
  `include "dfhdl_defs.svh"
  // ...
The file is include-guarded, so every design that needs it can include it unconditionally.

Foo_pkg.vhd
1
2
3
4
5
6
7
8
9
package Foo_pkg is
type t_enum_State is (
  State_IDLE, State_RUN
);
function bitWidth(A: t_enum_State) return integer;
function to_slv(A: t_enum_State) return std_logic_vector;
function to_t_enum_State(A: std_logic_vector) return t_enum_State;
-- ...
end package Foo_pkg;
The VHDL package carries the conversion and helper functions for the type alongside its declaration.

Had State been used only inside a single design, no <Top>_defs file would be emitted at all and the typedef would sit inside that one module. The placement follows the usage, not the Scala declaration site.