RegisterFileSpec.scala 6.72 KB
Newer Older
Jens Korinth's avatar
Jens Korinth committed
1
package chisel.axi.axi4lite
Jens Korinth's avatar
Jens Korinth committed
2
import  chisel.axi._
3
4
import  chisel.axi.Axi4Lite, chisel.axi.Axi4Lite._
import  chisel.axi.generators.Axi4Lite._
Jens Korinth's avatar
Jens Korinth committed
5
import  chisel.miscutils.Logging
Jens Korinth's avatar
Jens Korinth committed
6
7
8
import  chisel3._
import  chisel3.util._
import  chisel3.iotesters.{ChiselFlatSpec, Driver, PeekPokeTester}
9
10
import  org.scalacheck._, org.scalacheck.Prop._
import  org.scalatest.prop.Checkers
11
12

/**
Jens Korinth's avatar
Jens Korinth committed
13
14
 * Harness for Axi4Lite RegisterFile:
 * Creates a register file using the specified register map, connects an ProgrammableMaster
15
16
17
18
19
20
21
22
 * to the register file and programs it with the specified actions.
 * Read data is queued in a FIFO and can be accessed from the outside.
 * When all actions are processed, the `finished` flag is driven high.
 * @param size number of registers
 * @param distance byte distance of registers
 * @param regs register map for register file
 * @param actions master actions to perform
 **/
Jens Korinth's avatar
Jens Korinth committed
23
class RegFileTest(val size: Int, val off: Int, regs: Map[Int, ControlRegister], actions: Seq[MasterAction])
Jens Korinth's avatar
Jens Korinth committed
24
25
26
27
                 (implicit axi: Axi4Lite.Configuration, logLevel: Logging.Level) extends Module {
  val cfg = new RegisterFile.Configuration(regs = regs)
  val saxi = Module(new RegisterFile(cfg))
  val m = Module(new ProgrammableMaster(actions))
28
29
30
31
32
  val io = IO(new Bundle {
    val rdata = Irrevocable(saxi.io.saxi.readData.bits.cloneType)
    val wresp = Irrevocable(new chisel.axi.Axi4Lite.WriteResponse)
    val finished = Output(Bool())
  })
Jens Korinth's avatar
Jens Korinth committed
33
  m.io.maxi         <> saxi.io.saxi
Jens Korinth's avatar
Jens Korinth committed
34
  io.finished       := m.io.finished
35
  io.wresp          <> saxi.io.saxi.writeResp
36
  io.rdata          <> saxi.io.saxi.readData
37
38
39
}

/** Unit test suite for Axi4LiteRegisterFile module. **/
40
class RegisterFileSpec extends ChiselFlatSpec with Checkers {
Jens Korinth's avatar
Jens Korinth committed
41
  implicit val logLevel = Logging.Level.Info
42
  // basic Chisel arguments
Jens Korinth's avatar
Jens Korinth committed
43
  val chiselArgs = Array("--fint-write-vcd")
44

45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
  private def generateActionsFromRegMap(regs: Map[Int, Option[ControlRegister]]): Seq[MasterAction] =
    regs.toSeq.sortBy(_._1) map { _ match {
      case (i, Some(r)) => r match {
        case c: Register         => Seq(MasterWrite(i, i), MasterRead(i))
        case c: ConstantRegister => Seq(MasterRead(i))
        case _                   => Seq()
      }
      case (i, None) => Seq(MasterRead(i), MasterWrite(i, i))
    }} reduce (_ ++ _)

  private def genericTest(width: DataWidth, regs: Map[Int, Option[ControlRegister]])
                         (implicit axi: Axi4Lite.Configuration) = {
    val testDir = "test/axi4lite/RegisterFileSpec/generic/%d/%d".format(width: Int, scala.util.Random.nextInt)
    println(s"Test results here: $testDir, width = $width")
    val args = chiselArgs ++ Array("--target-dir", testDir)
    val actions = generateActionsFromRegMap(regs)
    Driver.execute(args, () => new RegFileTest(regs.size, width / 8, regs filter { case (_, or) => or.nonEmpty } map { case (i, or) => (i, or.get) }, actions))
      { m => new GenericTester(width, regs, m) } // FIXME implement generic Tester
63
64
  }

65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
  private class GenericTester(width: DataWidth, regs: Map[Int, Option[ControlRegister]], m: RegFileTest) extends PeekPokeTester(m) {
    def waitForReadData {
      if (peek(m.io.rdata.valid) != 0) {
        println("read data is still valid at start of waitForReadData, waiting ...")
        var waitCycles = 0
        while (peek(m.io.rdata.valid) != 0) {
          waitCycles += 1
          step(1)
        }
        println(s"waited $waitCycles cycles for read data valid signal to go low")
      }
      println("waiting for read data ...")
      var steps = 100
      while (steps > 0 && peek(m.io.rdata.valid) == 0) { steps -= 1; step(1) }
      expect(m.io.rdata.valid, 1, "expected read data to arrive, but it did not")
      println(s"wait for read data took ${100 - steps} cycles")
    }
82

83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
    def waitForWriteResp {
      if (peek(m.io.wresp.valid) != 0) {
        println("write resp is still valid at start of waitForReadData, waiting ...")
        var waitCycles = 0
        while (peek(m.io.wresp.valid) != 0) {
          waitCycles += 1
          step(1)
        }
        println(s"waited $waitCycles cycles for write resp valid signal to go low")
      }
      println("waiting for write response ...")
      var steps = 100
      while (steps > 0 && peek(m.io.wresp.valid) == 0) { steps -= 1; step(1) }
      expect(m.io.wresp.valid, 1, "expected write response to arrive, but it did not")
      println(s"wait for write response took ${100 - steps} cycles")
    }
99

100
101
102
103
104
105
106
107
108
109
    def test(r: Register, off: Int) {
      waitForWriteResp
      val bresp = peek(m.io.wresp.bits.bresp)
      expect (m.io.wresp.bits.bresp, Response.okay, s"[$off] write response is 0x%x (%d), should be 0 (OKAY)".format(bresp, bresp))
      waitForReadData
      val resp = peek(m.io.rdata.bits.resp)
      expect (m.io.rdata.bits.resp, Response.okay, s"[$off] read response is 0x%x (%d), should be 0 (OKAY)".format(resp, resp))
      val data = peek(m.io.rdata.bits.data)
      expect (m.io.rdata.bits.data, off, s"[$off] read data is 0x%x (%d), should be %d".format(data, data, off))
    }
110

111
112
113
114
115
116
    def test(r: ConstantRegister, off: Int) {
      waitForReadData
      val resp = peek(m.io.rdata.bits.resp)
      val data = peek(m.io.rdata.bits.data)
      expect (m.io.rdata.bits.resp, Response.okay, s"[$off] read response is 0x%x (%d), should be 0 (OKAY)".format(resp, resp))
      expect (m.io.rdata.bits.data, r.value, s"[$off] read data is 0x%x (%d), should be %d".format(data, data, r.value))
117
118
    }

119
120
121
122
123
124
125
126
127
128
    def testInvalid(off: Int) {
      println(s"[$off] expecting an invalid read ...")
      waitForReadData
      val resp = peek(m.io.rdata.bits.resp)
      expect (m.io.rdata.bits.resp, Response.slverr, s"[$off] read resp is 0x%x (%d), should be 2 (SLVERR)".format(resp, resp))
      println(s"[$off] expecting an invalid write ...")
      waitForWriteResp
      val bresp = peek(m.io.wresp.bits.bresp)
      expect (m.io.wresp.bits.bresp, Response.slverr, s"[$off] write resp is 0x%x (%d), should be 2 (SLVERR)".format(bresp, bresp))
    }
129

130
131
132
133
    println(s"data width = $width")

    println("Register Map:")
    regs.toSeq.sortBy(_._1) foreach { _ match { case (i, or) => println(s"$i -> $or") } }
134

135
136
    poke(m.io.wresp.ready, true)
    poke(m.io.rdata.ready, true)
137

138
139
140
141
142
143
144
145
146
147
148
    regs.toSeq.sortBy(_._1) foreach { _ match {
      case (i, or) => or match {
        case Some(r) => r match {
          case cr: ConstantRegister => test(cr, i)
          case rr: Register         => test(rr, i)
          case _                    => ()
        }
        case _                      => testInvalid(i)
      }
    }}
  }
149

150
151
152
153
154
155
  "random register files" should "behave as expected at each register" in {
    check(forAll(chisel.axi.generators.Axi4Lite.configurationGen) { cfg => {
      implicit val axi = cfg
      forAll(dataWidthGen) { w => forAllNoShrink(registerMapGen(w)) { regs => genericTest(w, regs) } }
    }})
  }
156
}