You're reading an old version of this documentation.
For the latest stable release version, please have a look at master.

VHDL equivalences

Entity and architecture

In SpinalHDL, a VHDL entity and architecture are both defined inside a Component.

Here is an example of a component which has 3 inputs (a, b, c) and an output (result). This component also has an offset construction parameter (like a VHDL generic).

case class MyComponent(offset: Int) extends Component {
  val io = new Bundle{
    val a, b, c = in UInt(8 bits)
    val result  = out UInt(8 bits)
  }
  io.result := a + b + c + offset
}

Then to instantiate that component, you don’t need to bind it:

case class TopLevel extends Component {
  ...
  val mySubComponent = MyComponent(offset = 5)

  ...

  mySubComponent.io.a := 1
  mySubComponent.io.b := 2
  mySubComponent.io.c := 3
  ??? := mySubComponent.io.result

  ...
}

Data types

SpinalHDL data types are similar to the VHDL ones:

VHDL

SpinalHDL

std_logic

Bool

std_logic_vector

Bits

unsigned

UInt

signed

SInt

In VHDL, to define an 8 bit unsigned you have to give the range of bits unsigned(7 downto 0),
whereas in SpinalHDL you simply supply the number of bits UInt(8 bits).

VHDL

SpinalHDL

records

Bundle

array

Vec

enum

SpinalEnum

Here is an example of the SpinalHDL Bundle definition. channelWidth is a construction parameter, like VHDL generics, but for data structures:

case class RGB(channelWidth: Int) extends Bundle {
  val r, g, b = UInt(channelWidth bits)
}

Then for example, to instantiate a Bundle, you need to write val myColor = RGB(channelWidth=8).

Signal

Here is an example about signal instantiations:

case class MyComponent(offset: Int) extends Component {
  val io = new Bundle {
    val a, b, c = UInt(8 bits)
    val result  = UInt(8 bits)
  }
  val ab = UInt(8 bits)
  ab := a + b

  val abc = ab + c            // You can define a signal directly with its value
  io.result := abc + offset
}

Assignments

In SpinalHDL, the := assignment operator is equivalent to the VHDL signal assignment (<=):

val myUInt = UInt(8 bits)
myUInt := 6

Conditional assignments are done like in VHDL by using if/case statements:

val clear   = Bool()
val counter = Reg(UInt(8 bits))

when(clear) {
  counter := 0
}.elsewhen(counter === 76) {
  counter := 79
}.otherwise {
  counter(7) := ! counter(7)
}

switch(counter) {
  is(42) {
    counter := 65
  }
  default {
    counter := counter + 1
  }
}

Literals

Literals are a little bit different than in VHDL:

val myBool = Bool()
myBool := False
myBool := True
myBool := Bool(4 > 7)

val myUInt = UInt(8 bits)
myUInt := "0001_1100"
myUInt := "xEE"
myUInt := 42
myUInt := U(54,8 bits)
myUInt := ((3 downto 0) -> myBool, default -> true)
when(myUInt === U(myUInt.range -> true)) {
  myUInt(3) := False
}

Registers

In SpinalHDL, registers are explicitly specified while in VHDL registers are inferred. Here is an example of SpinalHDL registers:

val counter = Reg(UInt(8 bits))  init(0)
counter := counter + 1   // Count up each cycle

// init(0) means that the register should be initialized to zero when a reset occurs

Process blocks

Process blocks are a simulation feature that is unnecessary to design RTL. It’s why SpinalHDL doesn’t contain any feature analogous to process blocks, and you can assign what you want, where you want.

val cond = Bool()
val myCombinatorial = Bool()
val myRegister = UInt(8 bits)

myCombinatorial := False
when(cond) {
  myCombinatorial := True
  myRegister = myRegister + 1
}