RegisterFileSpec.scala 9.7 KB
Newer Older
Jens Korinth's avatar
Jens Korinth committed
1
2
package chisel.axiutils.axi4lite
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
                 (implicit axi: Axi4Lite.Configuration, logLevel: Logging.Level) extends Module {
Jens Korinth's avatar
Jens Korinth committed
25
26
27
  val io = IO(new Bundle {
    val out = Decoupled(UInt(axi.dataWidth))
    val finished = Output(Bool())
28
29
    val rresp = Output(chisel.axi.Response.okay.cloneType)
    val wresp = Irrevocable(new chisel.axi.Axi4Lite.WriteResponse)
Jens Korinth's avatar
Jens Korinth committed
30
  })
Jens Korinth's avatar
Jens Korinth committed
31
32
33
34
35
  val cfg = new RegisterFile.Configuration(regs = regs)
  val saxi = Module(new RegisterFile(cfg))
  val m = Module(new ProgrammableMaster(actions))
  m.io.maxi         <> saxi.io.saxi
  io.out            <> m.io.out
Jens Korinth's avatar
Jens Korinth committed
36
37
38
  io.finished       := m.io.finished
  m.io.w_resp.ready := true.B
  io.rresp          := m.io.maxi.readData.bits.resp
39
  io.wresp          <> saxi.io.saxi.writeResp
40
41
42
43
44
45
46
}

/**
 * ReadTester checks attempts to read from all registers.
 * @param m configured RegFileTest module
 * @param isTrace turns on debug output (default: true)
 **/
Jens Korinth's avatar
Jens Korinth committed
47
class ReadTester(m: RegFileTest) extends PeekPokeTester(m) {
48
49
50
51
52
53
54
55
56
57
58
  reset(10)
  poke(m.io.out.ready, true)
  var steps = m.size * 10 // no more than 10 clock cycles per read
  for (i <- 1 until m.size + 1 if steps > 0) {
    // wait til output queue is ready
    while (steps > 0 && peek(m.io.out.ready) == 0 || peek(m.io.out.valid) == 0) {
      steps -= 1
      step(1)
    }
    val v = peek(m.io.out.bits)
    val e = BigInt("%02x".format(i) * 4, 16)
Jens Korinth's avatar
Jens Korinth committed
59
    val resp = peek(m.io.rresp)
60
61
62
63
64
65
66
67
68
69
70
71
    expect (resp == 0, "read #%d: resp is 0x%x (%d), should be 0 (OKAY)".format(i, resp, resp))
    expect(v == e, "at action #%d, expected: 0x%x (%d) but found %x (%d)".format(i, e, e, v, v))
    step(1)
  }
  expect(peek(m.io.finished) != 0, "finished signal should be true at end of test")
}

/**
 * WriteTester checks attempts to write to all registers.
 * @param m configured RegFileTest module
 * @param isTrace turns on debug output (default: true)
 **/
Jens Korinth's avatar
Jens Korinth committed
72
class WriteTester(m: RegFileTest) extends PeekPokeTester(m) {
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
  reset(10)
  poke(m.io.out.ready, true)
  println("running for a total of %d steps max ...".format(m.size * 20))
  var steps = m.size * 20 // no more than 10 clock cycles per read+write
  for (i <- 1 until m.size + 1 if steps > 0) {
    while (steps > 0 && (peek(m.io.out.ready) == 0 || peek(m.io.out.valid) == 0)) {
      steps -= 1
      step(1)
    }
    val v = peek(m.io.out.bits)
    val e = BigInt("%02x".format(i) * 4, 16)
    expect(v == e, "at output #%d, expected: 0x%x (%d), found %x (%d)".format(i, e, e, v, v))
    step(1)
  }
  expect(peek(m.io.finished) != 0, "finished signal should be true at end of test")
}

/**
 * InvalidReadTester checks invalid read attempts for proper return code.
 * @param m configured RegFileTest module
 * @param reads number of invalid reads to perform
 * @param isTrace turns on debug output (default: true)
 **/
Jens Korinth's avatar
Jens Korinth committed
96
class InvalidReadTester(m: RegFileTest, reads: Int) extends PeekPokeTester(m) {
97
98
99
100
  reset(10)
  println("performing %d invalid reads ...")
  var steps = reads * 10
  for (i <- 1 until reads + 1 if steps > 0) {
101
    while (steps > 0 && (peek(m.io.out.ready) == 0 || peek(m.io.out.valid) == 0)) {
102
103
104
      steps -= 1
      step(1)
    }
105
    val resp = peek(m.io.out.bits)
106
107
108
109
110
111
112
113
114
115
116
117
    expect (resp == 2, "read #%d: resp is 0x%x (%d), should be 2 (SLVERR)".format(i, resp, resp))
    step(1)
  }
  expect(peek(m.io.finished) != 0, "finished signal should be true at end of test")
}

/**
 * InvalidWriteTester checks invalid write attempts for proper return code.
 * @param m configured RegFileTest module
 * @param writes number of invalid writes to perform
 * @param isTrace turns on debug output (default: true)
 **/
Jens Korinth's avatar
Jens Korinth committed
118
class InvalidWriteTester(m: RegFileTest, writes: Int) extends PeekPokeTester(m) {
119
  reset(10)
Jens Korinth's avatar
Jens Korinth committed
120
  println("performing %d invalid writes ...".format(writes))
121
122
  var steps = writes * 10
  for (i <- 1 until writes + 1 if steps > 0) {
123
    while (steps > 0 && peek(m.io.wresp.valid) == 0) {
124
125
126
      steps -= 1
      step(1)
    }
127
    val resp = peek(m.io.wresp.bits.bresp)
128
129
130
131
132
133
134
    expect(resp == 2, "write #%d: resp is 0x%x (%d), should be 2 (SLVERR)".format(i, resp, resp))
    step(1)
  }
  expect(peek(m.io.finished) != 0, "finished signal should be true at end of test")
}

/** Unit test suite for Axi4LiteRegisterFile module. **/
135
class RegisterFileSpec extends ChiselFlatSpec with Checkers {
Jens Korinth's avatar
Jens Korinth committed
136
  implicit val logLevel = Logging.Level.Info
137
  // basic Chisel arguments
Jens Korinth's avatar
Jens Korinth committed
138
  val chiselArgs = Array("--fint-write-vcd")
139
  // implicit AXI configuration
Jens Korinth's avatar
Jens Korinth committed
140
  implicit val axi = Axi4Lite.Configuration(dataWidth = Axi4Lite.Width32, addrWidth = AddrWidth(32))
141
142
143

  /** Attempts to read from all registers. **/
  private def readTest(size: Int, off: Int) =  {
Jens Korinth's avatar
Jens Korinth committed
144
    val args = chiselArgs ++ Array("--target-dir", "test/Axi4RegisterFileSuite/read/size_%d_off_%d".format(size, off))
145
146
147
    // fill constant registers with pattern
    val regs = (for (i <- 1 until size + 1) yield
      off * i -> new ConstantRegister(value = BigInt("%02x".format(i) * 4, 16))
Jens Korinth's avatar
Jens Korinth committed
148
    ).toMap
149
    // read each of the registers in sequence
150
    val actions = for (i <- 1 until size + 1) yield MasterRead(off * i)
151
    // run test
Jens Korinth's avatar
Jens Korinth committed
152
153
    Driver.execute(args, () => new RegFileTest(size, off, regs, actions))
      { m => new ReadTester(m) }
154
155
156
157
  }

  /** Attempts to write to all registers. **/
  private def writeTest(size: Int, off: Int) =  {
Jens Korinth's avatar
Jens Korinth committed
158
    val args = chiselArgs ++ Array("--target-dir", "test/Axi4RegisterFileSuite/write/size_%d_off_%d".format(size, off))
159
160
161
    // fill constant registers with pattern
    val regs = (for (i <- 1 until size + 1) yield
      off * i -> new Register(width = axi.dataWidth)
Jens Korinth's avatar
Jens Korinth committed
162
    ).toMap
163
164
    // read each of the registers in sequence
    val actions = (for (i <- 1 until size + 1) yield Seq(
165
166
      MasterWrite(off * i, BigInt("%02x".format(i) * (axi.dataWidth/8), 16)),
      MasterRead(off * i)
167
168
    )) reduce (_++_)
    // run test
Jens Korinth's avatar
Jens Korinth committed
169
170
    Driver.execute(args, () => new RegFileTest(size, off, regs, actions))
      { m => new WriteTester(m) }
171
172
173
174
  }

  /** Attempts to perform invalid reads and checks return code. **/
  private def invalidReads(reads: Int) = {
Jens Korinth's avatar
Jens Korinth committed
175
    val args = chiselArgs ++ Array("--target-dir", "test/Axi4RegisterFileSuite/invalidReads/%d".format(reads))
176
177
178
    // only zero is valid register
    val regs = Map( 0 -> new ConstantRegister(value = 0) )
    // read from increasing addresses (all above 0 are invalid)
179
    val actions = for (i <- 1 until reads + 1) yield MasterRead(i * (axi.dataWidth / 8))
180
    // run test
Jens Korinth's avatar
Jens Korinth committed
181
182
    Driver.execute(args, () => new RegFileTest(1, 4, regs, actions))
      { m => new InvalidReadTester(m, reads) }
183
184
185
186
  }

  /** Attempts to perform invalid writes and checks return code. **/
  private def invalidWrites(writes: Int) = {
Jens Korinth's avatar
Jens Korinth committed
187
    val args = chiselArgs ++ Array("--target-dir", "test/Axi4RegisterFileSuite/invalidWrites/%d".format(writes))
188
189
190
    // only zero is valid register
    val regs = Map( 0 -> new ConstantRegister(value = 0) )
    // write from increasing addresses (all above 0 are invalid)
191
    val actions = for (i <- 1 until writes + 1) yield MasterWrite(i * (axi.dataWidth / 8), 42)
192
    // run test
Jens Korinth's avatar
Jens Korinth committed
193
194
    Driver.execute(args, () => new RegFileTest(1, 4, regs, actions))
      { m => new InvalidWriteTester(m, writes) }
195
196
  }

197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
  private def generateActionsFromRegMap(regs: Map[Int, Option[ControlRegister]]): Seq[MasterAction] = regs 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]]) = {
    val args = chiselArgs ++ Array("--target-dir", "test/axi4lite/RegisterFileSpec/generic/%d/%d"
      .format(width: Int, scala.util.Random.nextInt)) // FIXME find a nice name, or output it somehow
    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 InvalidWriteTester(m, regs.size) } // FIXME implement generic Tester
  }

  "random register files" should "behave as expected at each register" in {
    check(forAll(dataWidthGen) { w => forAllNoShrink(registerMapGen(w)) { regs => genericTest(w, regs) } })
  }

218
  /* READ TESTS */
219
  /*"read_255_4" should "be ok" in { readTest(255, 4) }
Jens Korinth's avatar
Jens Korinth committed
220
221
  "read_16_16" should "be ok" in { readTest(16, 16) }
  "read_4_4" should "be ok" in   { readTest(4, 4) }
222
  "read_7_13" should "be ok" in  { readTest(7, 13) }*/
223
224

  /* WRITE TESTS */
225
  /*"write_255_4" should "be ok" in { writeTest(255,   4) }
Jens Korinth's avatar
Jens Korinth committed
226
227
  "write_16_16" should "be ok" in { writeTest( 16,  16) }
  "write_4_4" should "be ok" in   { writeTest(  4,   4) }
228
  "write_7_13" should "be ok" in  { writeTest(  7,  13) }*/
229
230

  /* INVALID R/W TESTS */
231
232
  /*"invalidReads_16" should "be ok" in  { invalidReads(16) }
  "invalidWrites_16" should "be ok" in { invalidWrites(16) }*/
233
}