Finite-State Machines

47
Finite-State Machines Martin Schoeberl Technical University of Denmark Embedded Systems Engineering March 11, 2021 1 / 47

Transcript of Finite-State Machines

Page 1: Finite-State Machines

Finite-State Machines

Martin Schoeberl

Technical University of DenmarkEmbedded Systems Engineering

March 11, 2021

1 / 47

Page 2: Finite-State Machines

Overview

I Debugging with waveformsI Fun with countersI Finite-state machinesI Collection with Vec

2 / 47

Page 3: Finite-State Machines

Organization and Lab Work

I Look into files at DTU LearnI Looks like it the 7-segment decoder was fine last weekI This week it will be counters to test the 7-segment displayI I am aware of that not everyone has an FPGA boardI A lot can be done with simulation and testingI I do provide some FPGA board simulationI But, at the end of the day I want to see a vending machine

in an FPGA

3 / 47

Page 4: Finite-State Machines

Testing with Chisel

I Set input values with pokeI Advance the simulation with stepI Read the output values with peekI Compare the values with expectI Import following packages:

import chisel3._

import chisel3.iotesters._

4 / 47

Page 5: Finite-State Machines

Using peek, poke, and expect

// Set input values

poke(dut.io.a, 3)

poke(dut.io.b, 4)

// Execute one iteration

step(1)

// Print the result

val res = peek(dut.io.result)

println(res)

// Or compare against expected value

expect(dut.io.result, 7)

5 / 47

Page 6: Finite-State Machines

A Chisel Tester

I Extends class PeekPokeTesterI Has the device-under test (DUT) as parameterI Testing code can use all features of Scala

class CounterTester(dut: Counter) extends

PeekPokeTester(dut) {

// Here comes the Chisel/Scala code

// for the testing

}

6 / 47

Page 7: Finite-State Machines

Using ScalaTest for our Tester

I Little verbose syntaxI Copy example code to start with

class SimpleSpec extends FlatSpec with Matchers {

"Tester" should "pass" in {

chisel3.iotesters.Driver(() => new

DeviceUnderTest()) { c =>

new Tester(c)

} should be (true)

}

}

7 / 47

Page 8: Finite-State Machines

Generating Waveforms

I Waveforms are timing diagramsI Good to see many parallel signals and registersI Additional parameters: "--generate-vcd-output", "on"I IO signals and registers are dumpedI Generates a .vcd fileI Viewing with GTKWave or ModelSim

8 / 47

Page 9: Finite-State Machines

A Self-Running Circuit

I SevenSegTest is a self-running circuitI Needs no stimuli (poke)I Just run for a few cyclesI Tester for today’s labI This tester does NOT test the circuit

I You are not finished when this test does not show an error

class SevenSegTest(dut: CountSevenSeg) extends

PeekPokeTester(dut) {

step(100)

}

9 / 47

Page 10: Finite-State Machines

Display Waveform with GTKWave

I Run the tester: sbt testI Locate the .vcd file in test run dir/SevenSegCountSpecnnnI Start GTKWaveI Open the .vcd file with

I File – Open New TabI Select the circuitI Drag and drop the interesting signals

10 / 47

Page 11: Finite-State Machines

Call the Tester for Waveform Generation

I Using here ScalaTestI Note Driver.executeI Note Array("--generate-vcd-output", "on")

class SevenSegCountSpec extends

FlatSpec with Matchers {

"SevenSegTest " should "pass" in {

chisel3.iotesters.Driver

.execute(Array("--generate-vcd-output", "on"),

() => new CountSevenSeg)

{ c => new SevenSegTest(c)} should be (true) }

}

11 / 47

Page 12: Finite-State Machines

Counters as Building Blocks

I Counters are versatile toolsI Count eventsI Generate timing ticksI Generate a one-shot timer

12 / 47

Page 13: Finite-State Machines

Counting Up and Down

I Up:

val cntReg = RegInit(0.U(8.W))

cntReg := cntReg + 1.U

when(cntReg === N) {

cntReg := 0.U

}

I Down:

val cntReg = RegInit(N)

cntReg := cntReg - 1.U

when(cntReg === 0.U) {

cntReg := N

}

13 / 47

Page 14: Finite-State Machines

Generating Timing with Counters

I Generate a tick at a lower frequencyI We used it in Lab 1 for the blinking LEDI Use it for today’s labI Use it for driving the display multiplexing at 1 kHz

clock

reset

tick

counter 0 1 2 0 1 2 0 1

14 / 47

Page 15: Finite-State Machines

The Tick Generation

val tickCounterReg = RegInit(0.U(4.W))

val tick = tickCounterReg === (N-1).U

tickCounterReg := tickCounterReg + 1.U

when (tick) {

tickCounterReg := 0.U

}

15 / 47

Page 16: Finite-State Machines

Using the Tick

I A counter running at a slower frequencyI By using the tick as an enable signal

val lowFrequCntReg = RegInit(0.U(4.W))

when (tick) {

lowFrequCntReg := lowFrequCntReg + 1.U

}

16 / 47

Page 17: Finite-State Machines

The Slow Counter

I Incremented every tick

clock

reset

tick

slow cnt 0 1 2

17 / 47

Page 18: Finite-State Machines

A Timer

I Like a kitchen timerI Start by loading a timeout valueI Count down till 0I Assert done when finished

18 / 47

Page 19: Finite-State Machines

One-Shot Timer

D Q

+

=0 done

load

din

-10

Select

cntnext

19 / 47

Page 20: Finite-State Machines

One-Shot Timer

val cntReg = RegInit(0.U(8.W))

val done = cntReg === 0.U

val next = WireDefault(0.U)

when (load) {

next := din

} .elsewhen (!done) {

next := cntReg - 1.U

}

cntReg := next

20 / 47

Page 21: Finite-State Machines

A 4 Stage Shift Register

din dout

val shiftReg = Reg(UInt(4.W))

shiftReg := Cat(shiftReg(2, 0), din)

val dout = shiftReg(3)

21 / 47

Page 22: Finite-State Machines

A Shift Register with Parallel Output

serIn

q3 q2 q1 q0

val outReg = RegInit(0.U(4.W))

outReg := Cat(serIn, outReg(3, 1))

val q = outReg

22 / 47

Page 23: Finite-State Machines

A Shift Register with Parallel Load

d3

load

d2

load

d1

load

d0

load

serOut0

val loadReg = RegInit(0.U(4.W))

when (load) {

loadReg := d

} otherwise {

loadReg := Cat(0.U, loadReg(3, 1))

}

val serOut = loadReg(0)

23 / 47

Page 24: Finite-State Machines

A Simple Circuit

I What does the following circuit?I Is this related to a finite-state machine?

ANDNOTdin

risingEdge

24 / 47

Page 25: Finite-State Machines

Finite-State Machine (FSM)

I Has a register that contains the stateI Has a function to computer the next state

I Depending on current state and inputI Has an output depending on the state

I And maybe on the input as wellI Every synchronous circuit can be considered a finite state

machineI However, sometimes the state space is a little bit too large

25 / 47

Page 26: Finite-State Machines

Basic Finite-State Machine

I A state registerI Two combinational blocks

in

state

nextStateNext statelogic

Ouputlogic out

26 / 47

Page 27: Finite-State Machines

State Diagram

bad event

green orange red/ring bell

bad event

clear

reset

clear

I States and transitions depending on input valuesI Example is a simple alarm FSMI Nice visualizationI Will not work for large FSMsI Complete code in the Chisel book

27 / 47

Page 28: Finite-State Machines

State Table for the Alarm FSM

Input

State Bad event Clear Next state Ring bell

green 0 0 green 0green 1 - orange 0

orange 0 0 orange 0orange 1 - red 0orange 0 1 green 0

red 0 0 red 1red 0 1 green 1

28 / 47

Page 29: Finite-State Machines

The Input and Output of the Alarm FSM

I Two inputs and one output

val io = IO(new Bundle{

val badEvent = Input(Bool())

val clear = Input(Bool())

val ringBell = Output(Bool())

})

29 / 47

Page 30: Finite-State Machines

Encoding the State

I We can optimize state encodingI Two common encodings are: binary and one-hotI We leave it to the synthesize toolI Use symbolic names with an EnumI Note the number of states in the Enum constructI We use a Scala list with the :: operator

val green :: orange :: red :: Nil = Enum(3)

30 / 47

Page 31: Finite-State Machines

Start the FSM

I We have a starting state on reset

val stateReg = RegInit(green)

31 / 47

Page 32: Finite-State Machines

The Next State Logicswitch (stateReg) {

is (green) {

when(io.badEvent) {

stateReg := orange

}

}

is (orange) {

when(io.badEvent) {

stateReg := red

} .elsewhen(io.clear) {

stateReg := green

}

}

is (red) {

when (io.clear) {

stateReg := green

}

}

}

32 / 47

Page 33: Finite-State Machines

The Output Logic

io.ringBell := stateReg === red

33 / 47

Page 34: Finite-State Machines

Summary on the Alarm Example

I Three elements:1. State register2. Next state logic3. Output logic

I This was a so-called Moore FSMI There is also a FSM type called Mealy machine

34 / 47

Page 35: Finite-State Machines

A so-called Mealy FSM

I Similar to the former FSMI Output also depends in the inputI It is fasterI Less composable (draw it)

in

state

nextStateNext statelogic Output

logic out

35 / 47

Page 36: Finite-State Machines

The Mealy FSM for the Rising Edge

I That was our starting exampleI Output is also part of the transition arrows

zero one

1/1

reset

0/0

0/0 1/0

36 / 47

Page 37: Finite-State Machines

The Mealy Solution

I Show code from the book as it is too long for slides

37 / 47

Page 38: Finite-State Machines

State Diagram for the Moore Rising Edge Detection

I We need three states

1

zero0

puls1

one0

1

0

reset

0

38 / 47

Page 39: Finite-State Machines

Comparing with a Timing Diagram

I Moore is delayed one clock cycle compared to Mealy

clock

din

risingEdge Mealy

risingEdge Moore

39 / 47

Page 40: Finite-State Machines

What is Better?

I It depends ;-)I Moore is on the save sideI More is composableI Mealy has faster reactionI Both are tools in you toolboxI Keep it simple with your vending machine and use a Moore

FSM

40 / 47

Page 41: Finite-State Machines

Another Simple FSM

I a FSM for a single word bufferI Just two symbols for the state machine

val empty :: full :: Nil = Enum(2)

41 / 47

Page 42: Finite-State Machines

Finite State Machine for a Buffer

val empty :: full :: Nil = Enum(2)

val stateReg = RegInit(empty)

val dataReg = RegInit(0.U(size.W))

when(stateReg === empty) {

when(io.enq.write) {

stateReg := full

dataReg := io.enq.din

}

}.elsewhen(stateReg === full) {

when(io.deq.read) {

stateReg := empty

}

}

I A simple buffer for a bubble FIFO

42 / 47

Page 43: Finite-State Machines

A Collection of Signals with Vec

I Chisel Vec is a collection of signals of the same typeI The collection can be accessed by an indexI Similar to an array in other languagesI Wrap into a Wire() for combinational logicI Wrap into a Reg() for a collection of registers

val v = Wire(Vec(3, UInt(4.W)))

43 / 47

Page 44: Finite-State Machines

Using a Vec

v(0) := 1.U

v(1) := 3.U

v(2) := 5.U

val idx = 1.U(2.W)

val a = v(idx)

I Reading from an Vec is a multplexerI We can put a Vec into a Reg

val registerFile = Reg(Vec(32, UInt(32.W)))

An element of that register file is accessed with an index andused as a normal register.

registerFile(idx) := dIn

val dOut = registerFile(idx)

44 / 47

Page 45: Finite-State Machines

Mixing Vecs and Bundles

I We can freely mix bundles and vectorsI When creating a vector with a bundle type, we need to

pass a prototype for the vector fields. Using our Channel,which we defined above, we can create a vector ofchannels with:

val vecBundle = Wire(Vec(8, new Channel()))

I A bundle may as well contain a vector

class BundleVec extends Bundle {

val field = UInt(8.W)

val vector = Vec(4,UInt(8.W))

}

45 / 47

Page 46: Finite-State Machines

Today’s Lab

I Driving your 7-segment decoderI Use a counter to count from 0 to 15, driving your displayI Use another counter to generate your timing

I We talked about this todayI You clock on the board is 100 MHzI The given tester does only generate a waveform, no testingI Use a different maximum count value for waveform

debuggingI Then synthesize it for the FPGAI Show a TA your working designI Lab 6

46 / 47

Page 47: Finite-State Machines

Summary

I Waveform testing is the way to develop/debugI Counters are important tools, e.g., to generate timingI Finite-state machines are another tool of the tradeI Two types: Moore and MealyI A Chisel Vec is the hardware version of an array

47 / 47