RegisterFileSpec.scala 8.45 KB
Newer Older
Jens Korinth's avatar
Jens Korinth committed
1
2
3
package chisel.axiutils.axi4lite
import  chisel.axi._
import  chisel.miscutils.Logging
Jens Korinth's avatar
Jens Korinth committed
4
5
6
import  chisel3._
import  chisel3.util._
import  chisel3.iotesters.{ChiselFlatSpec, Driver, PeekPokeTester}
7
8

/**
Jens Korinth's avatar
Jens Korinth committed
9
10
 * Harness for Axi4Lite RegisterFile:
 * Creates a register file using the specified register map, connects an ProgrammableMaster
11
12
13
14
15
16
17
18
 * 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
19
class RegFileTest(val size: Int, val off: Int, regs: Map[Int, ControlRegister], actions: Seq[MasterAction])
Jens Korinth's avatar
Jens Korinth committed
20
                 (implicit axi: Axi4Lite.Configuration, logLevel: Logging.Level) extends Module {
Jens Korinth's avatar
Jens Korinth committed
21
22
23
24
25
26
  val io = IO(new Bundle {
    val out = Decoupled(UInt(axi.dataWidth))
    val finished = Output(Bool())
    val rresp = Output(UInt(2.W))
    val wresp = Output(UInt(2.W))
  })
Jens Korinth's avatar
Jens Korinth committed
27
28
29
30
31
  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
32
33
34
35
  io.finished       := m.io.finished
  m.io.w_resp.ready := true.B
  io.rresp          := m.io.maxi.readData.bits.resp
  io.wresp          := m.io.maxi.writeResp.bits
36
37
38
39
40
41
42
}

/**
 * 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
43
class ReadTester(m: RegFileTest) extends PeekPokeTester(m) {
44
45
46
47
48
49
50
51
52
53
54
  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
55
    val resp = peek(m.io.rresp)
56
57
58
59
60
61
62
63
64
65
66
67
    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
68
class WriteTester(m: RegFileTest) extends PeekPokeTester(m) {
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
  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
92
class InvalidReadTester(m: RegFileTest, reads: Int) extends PeekPokeTester(m) {
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
  reset(10)
  println("performing %d invalid reads ...")
  var steps = reads * 10
  for (i <- 1 until reads + 1 if steps > 0) {
    while (steps > 0 && (peek(m.m.io.maxi.readData.valid) == 0 || peek(m.m.io.maxi.readData.ready) == 0)) {
      steps -= 1
      step(1)
    }
    val resp = peek(m.m.io.maxi.readData.bits.resp)
    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
114
class InvalidWriteTester(m: RegFileTest, writes: Int) extends PeekPokeTester(m) {
115
  reset(10)
Jens Korinth's avatar
Jens Korinth committed
116
  println("performing %d invalid writes ...".format(writes))
117
118
119
120
121
122
  var steps = writes * 10
  for (i <- 1 until writes + 1 if steps > 0) {
    while (steps > 0 && peek(m.m.io.w_resp.valid) == 0) {
      steps -= 1
      step(1)
    }
Jens Korinth's avatar
Jens Korinth committed
123
    val resp = peek(m.io.wresp)
124
125
126
127
128
129
130
    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. **/
Jens Korinth's avatar
Jens Korinth committed
131
132
class RegisterFileSuite extends ChiselFlatSpec {
  implicit val logLevel = Logging.Level.Info
133
  // basic Chisel arguments
Jens Korinth's avatar
Jens Korinth committed
134
  val chiselArgs = Array("--fint-write-vcd")
135
  // implicit AXI configuration
Jens Korinth's avatar
Jens Korinth committed
136
  implicit val axi = Axi4Lite.Configuration(dataWidth = Axi4Lite.Width32, addrWidth = AddrWidth(32))
137
138
139

  /** Attempts to read from all registers. **/
  private def readTest(size: Int, off: Int) =  {
Jens Korinth's avatar
Jens Korinth committed
140
    val args = chiselArgs ++ Array("--target-dir", "test/Axi4RegisterFileSuite/read/size_%d_off_%d".format(size, off))
141
142
143
    // 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
144
    ).toMap
145
146
147
    // read each of the registers in sequence
    val actions = for (i <- 1 until size + 1) yield MasterAction(true, off * i, None)
    // run test
Jens Korinth's avatar
Jens Korinth committed
148
149
    Driver.execute(args, () => new RegFileTest(size, off, regs, actions))
      { m => new ReadTester(m) }
150
151
152
153
  }

  /** Attempts to write to all registers. **/
  private def writeTest(size: Int, off: Int) =  {
Jens Korinth's avatar
Jens Korinth committed
154
    val args = chiselArgs ++ Array("--target-dir", "test/Axi4RegisterFileSuite/write/size_%d_off_%d".format(size, off))
155
156
157
    // 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
158
    ).toMap
159
160
161
162
163
164
165
166
    // read each of the registers in sequence
    val actions = (for (i <- 1 until size + 1) yield Seq(
      // first write the register
      MasterAction(false, off * i, Some(BigInt("%02x".format(i) * (axi.dataWidth/8), 16))),
      // then read the new value
      MasterAction(true, off * i, None)
    )) reduce (_++_)
    // run test
Jens Korinth's avatar
Jens Korinth committed
167
168
    Driver.execute(args, () => new RegFileTest(size, off, regs, actions))
      { m => new WriteTester(m) }
169
170
171
172
  }

  /** Attempts to perform invalid reads and checks return code. **/
  private def invalidReads(reads: Int) = {
Jens Korinth's avatar
Jens Korinth committed
173
    val args = chiselArgs ++ Array("--target-dir", "test/Axi4RegisterFileSuite/invalidReads/%d".format(reads))
174
175
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)
    val actions = for (i <- 1 until reads + 1) yield MasterAction(true, i * (axi.dataWidth / 8), None)
    // run test
Jens Korinth's avatar
Jens Korinth committed
179
180
    Driver.execute(args, () => new RegFileTest(1, 4, regs, actions))
      { m => new InvalidReadTester(m, reads) }
181
182
183
184
  }

  /** Attempts to perform invalid writes and checks return code. **/
  private def invalidWrites(writes: Int) = {
Jens Korinth's avatar
Jens Korinth committed
185
    val args = chiselArgs ++ Array("--target-dir", "test/Axi4RegisterFileSuite/invalidWrites/%d".format(writes))
186
187
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)
    val actions = for (i <- 1 until writes + 1) yield MasterAction(false, i * (axi.dataWidth / 8), Some(42))
    // run test
Jens Korinth's avatar
Jens Korinth committed
191
192
    Driver.execute(args, () => new RegFileTest(1, 4, regs, actions))
      { m => new InvalidWriteTester(m, writes) }
193
194
195
  }

  /* READ TESTS */
Jens Korinth's avatar
Jens Korinth committed
196
197
198
199
  "read_255_4" should "be ok" in { readTest(255, 4) }
  "read_16_16" should "be ok" in { readTest(16, 16) }
  "read_4_4" should "be ok" in   { readTest(4, 4) }
  "read_7_13" should "be ok" in  { readTest(7, 13) }
200
201

  /* WRITE TESTS */
Jens Korinth's avatar
Jens Korinth committed
202
203
204
205
  "write_255_4" should "be ok" in { writeTest(255,   4) }
  "write_16_16" should "be ok" in { writeTest( 16,  16) }
  "write_4_4" should "be ok" in   { writeTest(  4,   4) }
  "write_7_13" should "be ok" in  { writeTest(  7,  13) }
206
207

  /* INVALID R/W TESTS */
Jens Korinth's avatar
Jens Korinth committed
208
209
  "invalidReads_16" should "be ok" in  { invalidReads(16) }
  "invalidWrites_16" should "be ok" in { invalidWrites(16) }
210
}