summaryrefslogtreecommitdiff
path: root/src/test/scala/examples/SimpleVendingMachine.scala
blob: dff60a4d288b3d4e1aa8cd98651fb579a0e00b8b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
// SPDX-License-Identifier: Apache-2.0

package examples

import chiselTests.ChiselFlatSpec
import chisel3.testers.{BasicTester, TesterDriver}
import chisel3._
import chisel3.util._

class SimpleVendingMachineIO extends Bundle {
  val nickel = Input(Bool())
  val dime   = Input(Bool())
  val dispense  = Output(Bool())
}

// Superclass for vending machines with very simple IO
abstract class SimpleVendingMachine extends Module {
  val io = IO(new SimpleVendingMachineIO)
  assert(!(io.nickel && io.dime), "Only one of nickel or dime can be input at a time!")
}

// Vending machine implemented with a Finite State Machine
class FSMVendingMachine extends SimpleVendingMachine {
  val sIdle :: s5 :: s10 :: s15 :: sOk :: Nil = Enum(5)
  val state = RegInit(sIdle)

  switch (state) {
    is (sIdle) {
      when (io.nickel) { state := s5 }
      when (io.dime)   { state := s10 }
    }
    is (s5) {
      when (io.nickel) { state := s10 }
      when (io.dime)   { state := s15 }
    }
    is (s10) {
      when (io.nickel) { state := s15 }
      when (io.dime)   { state := sOk }
    }
    is (s15) {
      when (io.nickel) { state := sOk }
      when (io.dime)   { state := sOk }
    }
    is (sOk) {
      state := sIdle
    }
  }
  io.dispense := (state === sOk)
}

class VerilogVendingMachine extends BlackBox {
  // Because this is a blackbox, we must explicitly add clock and reset
  val io = IO(new SimpleVendingMachineIO {
    val clock = Input(Clock())
    val reset = Input(Reset())
  })
}

// Shim because Blackbox io is slightly different than normal Chisel Modules
class VerilogVendingMachineWrapper extends SimpleVendingMachine {
  val impl = Module(new VerilogVendingMachine)
  impl.io.clock := clock
  impl.io.reset := reset
  impl.io.nickel := io.nickel
  impl.io.dime := io.dime
  io.dispense := impl.io.dispense
}

// Accept a reference to a SimpleVendingMachine so it can be constructed inside
// the tester (in a call to Module.apply as required by Chisel
class SimpleVendingMachineTester(mod: => SimpleVendingMachine) extends BasicTester {

  val dut = Module(mod)

  val (cycle, done) = Counter(true.B, 10)
  when (done) { stop(); stop() } // Stop twice because of Verilator

  val nickelInputs = VecInit(true.B, true.B, true.B, true.B, true.B, false.B, false.B, false.B, true.B, false.B)
  val dimeInputs   = VecInit(false.B, false.B, false.B, false.B, false.B, true.B, true.B, false.B, false.B, true.B)
  val expected     = VecInit(false.B, false.B, false.B, false.B, true.B , false.B, false.B, true.B, false.B, false.B)

  dut.io.nickel := nickelInputs(cycle)
  dut.io.dime := dimeInputs(cycle)
  assert(dut.io.dispense === expected(cycle))
}

class SimpleVendingMachineSpec extends ChiselFlatSpec {
  "An FSM implementation of a vending machine" should "work" in {
    assertTesterPasses { new SimpleVendingMachineTester(new FSMVendingMachine) }
  }
  "An Verilog implementation of a vending machine" should "work" in {
    assertTesterPasses(new SimpleVendingMachineTester(new VerilogVendingMachineWrapper),
                       List("/chisel3/VerilogVendingMachine.v"), annotations = TesterDriver.verilatorOnly)
  }
}