summaryrefslogtreecommitdiff
path: root/src/main/scala/chisel3/util/experimental/decode/decoder.scala
blob: ee2ece48a7e050f720ea361fec54b2d9632db1bb (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
// SPDX-License-Identifier: Apache-2.0

package chisel3.util.experimental.decode

import chisel3._
import chisel3.experimental.{ChiselAnnotation, annotate}
import chisel3.util.{BitPat, pla}
import chisel3.util.experimental.getAnnotations
import firrtl.annotations.Annotation
import logger.LazyLogging

object decoder extends LazyLogging {
  /** Use a specific [[Minimizer]] to generated decoded signals.
    *
    * @param minimizer  specific [[Minimizer]], can be [[QMCMinimizer]] or [[EspressoMinimizer]].
    * @param input      input signal that contains decode table input
    * @param truthTable [[TruthTable]] to decode user input.
    * @return decode table output.
    */
  def apply(minimizer: Minimizer, input: UInt, truthTable: TruthTable): UInt = {
    val minimizedTable = getAnnotations().collect {
      case DecodeTableAnnotation(_, in, out) => TruthTable.fromString(in) -> TruthTable.fromString(out)
    }.toMap.getOrElse(truthTable, minimizer.minimize(truthTable))
    if (minimizedTable.table.isEmpty) {
      val outputs = Wire(UInt(minimizedTable.default.getWidth.W))
      outputs := minimizedTable.default.value.U(minimizedTable.default.getWidth.W)
      outputs
    } else {
      val (plaInput, plaOutput) =
        pla(minimizedTable.table.toSeq, BitPat(minimizedTable.default.value.U(minimizedTable.default.getWidth.W)))

      annotate(new ChiselAnnotation {
        override def toFirrtl: Annotation =
          DecodeTableAnnotation(plaOutput.toTarget, truthTable.toString, minimizedTable.toString)
      })

      plaInput := input
      plaOutput
    }
  }

  /** Use [[EspressoMinimizer]] to generated decoded signals.
    *
    * @param input      input signal that contains decode table input
    * @param truthTable [[TruthTable]] to decode user input.
    * @return decode table output.
    */
  def espresso(input: UInt, truthTable: TruthTable): UInt = apply(EspressoMinimizer, input, truthTable)

  /** Use [[QMCMinimizer]] to generated decoded signals.
    *
    * @param input      input signal that contains decode table input
    * @param truthTable [[TruthTable]] to decode user input.
    * @return decode table output.
    */
  def qmc(input: UInt, truthTable: TruthTable): UInt = apply(QMCMinimizer, input, truthTable)

  /** try to use [[EspressoMinimizer]] to decode `input` by `truthTable`
    * if `espresso` not exist in your PATH environment it will fall back to [[QMCMinimizer]], and print a warning.
    *
    * @param input      input signal that contains decode table input
    * @param truthTable [[TruthTable]] to decode user input.
    * @return decode table output.
    */
  def apply(input: UInt, truthTable: TruthTable): UInt = {
    def qmcFallBack(input: UInt, truthTable: TruthTable) = {
      """fall back to QMC.
        |Quine-McCluskey is a NP complete algorithm, may run forever or run out of memory in large decode tables.
        |To get rid of this warning, please use `decoder.qmc` directly, or add espresso to your PATH.
        |""".stripMargin
      qmc(input, truthTable)
    }

    try espresso(input, truthTable) catch {
      case EspressoNotFoundException =>
        logger.error(s"espresso is not found in your PATH:\n${sys.env("PATH").split(":").mkString("\n")}".stripMargin)
        qmcFallBack(input, truthTable)
      case e: java.io.IOException =>
        logger.error(s"espresso failed to run with ${e.getMessage}")
        qmcFallBack(input, truthTable)
    }
  }
}