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

Flow

Specification

The Flow interface is a simple valid/payload protocol which means the slave can’t halt the bus. It could be used to represent data coming from an UART controller, requests to write an on-chip memory, etc.

Signal

Type

Driver

Description

Don’t care when

valid

Bool

Master

When high => payload present on the interface

payload

T

Master

Content of the transaction

valid is low

Functions

Syntax

Description

Return

Latency

Flow(type : Data)

Create a Flow of a given type

Flow[T]

master/slave Flow(type : Data)

Create a Flow of a given type
Initialized with corresponding in/out setup

Flow[T]

x.m2sPipe()

Return a Flow driven by x
through a register stage that cut valid/payload paths

Flow[T]

1

x.stage()

Equivalent to x.m2sPipe()

Flow[T]

1

x << y
y >> x

Connect y to x

0

x <-< y
y >-> x

Connect y to x through a m2sPipe

1

x.throwWhen(cond : Bool)

Return a Flow connected to x
When cond is high, transaction are dropped

Flow[T]

0

x.toReg()

Return a register which is loaded with payload when valid is high

T

x.setIdle()

Set the Flow in an Idle state: valid is False and don’t care about payload.

x.push(newPayload: T)

Assign a new valid payload to the Flow. valid is set to True.

Code example

case class FlowExample() extends Component {
  val io = new Bundle {
    val request = slave(Flow(Bits(8 bit)))
    val answer = master(Flow(Bits(8 bit)))
  }
  val storage = Reg(Bits(8 bit))

  val fsm = new StateMachine {
    io.answer.setIdle()

    val idle: State = new State with EntryPoint {
      whenIsActive {
        when(io.request.valid) {
          storage := io.request.payload
          goto(sendEcho)
        }
      }
    }

    val sendEcho: State = new State {
      whenIsActive {
        io.answer.push(storage)
        goto(idle)
      }
    }
  }

  // This StateMachine behaves equivalently to
  // io.answer <-< io.request
}

Simulation Support

Class

Usage

FlowMonitor

Used for both master and slave sides, calls function with payload if Flow transmits data.

FlowDriver

Testbench master side, drives values by calling function to apply value (if available). Function must return if value was available. Supports random delays.

ScoreboardInOrder

Often used to compare reference/dut data

package spinaldoc.libraries.flow

import spinal.core._
import spinal.core.sim._
import spinal.lib._
import spinal.lib.sim.{FlowDriver, FlowMonitor, ScoreboardInOrder}

import scala.language.postfixOps

case class SomeDUT() extends Component {
  val io = new Bundle {
    val input = slave(Flow(UInt(8 bit)))
    val output = master(Flow(UInt(8 bit)))
  }
  io.output <-< io.input
}

object Example extends App {
  val dut = SimConfig.withWave.compile(SomeDUT())

  dut.doSim("simple test") { dut =>
    SimTimeout(10000)

    val scoreboard = ScoreboardInOrder[Int]()

    // drive random data at random intervals, and add inputted data to scoreboard
    FlowDriver(dut.io.input, dut.clockDomain) { payload =>
      payload.randomize()
      true
    }
    FlowMonitor(dut.io.input, dut.clockDomain) { payload =>
      scoreboard.pushRef(payload.toInt)
    }

    // add all data coming out of DUT to scoreboard
    FlowMonitor(dut.io.output, dut.clockDomain) { payload =>
      scoreboard.pushDut(payload.toInt)
    }

    dut.clockDomain.forkStimulus(10)
    dut.clockDomain.waitActiveEdgeWhere(scoreboard.matches == 100)
  }
}