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

Fiber framework


This framework is not expected to be used for general RTL generation and targets large system design management and code generation. It is currently used as toplevel integration tool in SaxonSoC.

Currently in developpement.

The Fiber to run the hardware elaboration in a out of order manner, a bit similarly to Makefile, where you can define rules and dependencies which will then be solved when you run a make command. It is very similar to the Scala Future feature.

Using this framework can complicate simple things but provide some strong features for complex cases :

  • You can define things before even knowing all their requirements, ex : instantiating a interruption controller, before knowing how many interrupt signal lines you need

  • Abstract/lazy/partial SoC architecture definition allowing the creation of SoC template for further specialisations

  • Automatic requirement negotiation between multiple agents in a decentralized way, ex : between masters and slaves of a memory bus

The framework is mainly composed of :

  • Handle[T], which can be used later to store a value of type T.

  • handle.load which allow to set the value of a handle (will reschedule all tasks waiting on it)

  • handle.get, which return the value of the given handle. Will block the task execution if that handle isn’t loaded yet

  • Handle{ /*code*/ }, which fork a new task which will execute the given code. The result of that code will be loaded into the Handle

  • soon(handle), which allows the current task to announce that it will load handle with a value (used for scheduling)

Simple dummy example

There is a simple example :

import spinal.core.fiber._

// Create two empty Handles
val a, b = Handle[Int]

// Create a Handle which will be loaded asynchronously by the given body result
val calculator = Handle {
    a.get + b.get // .get will block until they are loaded

// Same as above
val printer = Handle {
    println(s"a + b = ${calculator.get}") // .get is blocking until the calculator body is done

// Synchronously load a and b, this will unblock a.get and b.get

Its runtime will be :

  • create a and b

  • fork the calculator task, but is blocked when executing a.get

  • fork the printer task, but is blocked when executing calculator.get

  • load a and b, which reschedule the calculator task (as it was waiting on a)

  • calculator do its a + b sum, and load its Handle with that result, which reschedule the printer task

  • printer task print its stuff

  • everything done

So, the main point of that example is to show that we kind of overcome the sequential execution of things, as a and b are loaded after the definition of the calculator.


Handle[T] are a bit like scala’s Future[T], they allow to talk about something before it is even existing, and wait on it.

val x,y = Handle[Int]
val xPlus2 : Handle[Int] = x.produce(x.get + 2) //x.produce can be used to generate a new Handle when x is loaded
val xPlus3 : Handle[Int] = x.derivate(_ + 3)    //x.derivate is as x.produce, but also provide the x.get as argument of the lambda function
x.load(3) //x will now contain the value 3


In order to maintain a proper graph of dependencies between tasks and Handle, a task can specify in advance that it will load a given handle. This is very usefull in case of a generation starvation/deadlock for SpinalHDL to report accuratly where is the issue.