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 |
While for defining an 8 bit unsigned
in VHDL you have to give the range of bits unsigned(7 downto 0)
,
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
}
Assignements
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 it’s 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 analog 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
}