-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathMemorytest.scala
228 lines (183 loc) · 7.28 KB
/
Memorytest.scala
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
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
package TurboRav
import Chisel._
import Constants._
class MemoryTest(c: Memory) extends JUnitTester(c) {
val apb_address = 0x20000000
val ram_address = 0x10000000
val rom_address = 0x00000000
def reset_all() = {
step(1)
clear_all_pokes()
reset(1)
step(1)
}
def clear_all_pokes() = {
// NB: Must add all the poke values used in the tb here.
// (This really sucks, how to do better? Let us hope testing is
// better in Chisel3)
poke(c.io.exe_mem.wrb_ctrl.rd_wen, 0)
poke(c.io.exe_mem.mem_ctrl.read, 0)
poke(c.io.exe_mem.alu_result, 0)
poke(c.io.rr_io.response.valid, 0)
poke(c.io.rr_io.response.bits.word, 0)
poke (c.io.exe_mem.wrb_ctrl.sign_extend, 0)
poke(c.io.exe_mem.wrb_ctrl.rd_sel, 0)
}
println("The mem stage will pipeline control signals")
poke (c.io.exe_mem.wrb_ctrl.rd_wen, 1)
expect(c.io.mem_wrb.wrb_ctrl.rd_wen, 0) // Don't pass control signals through immediately
step(1)
expect(c.io.mem_wrb.wrb_ctrl.rd_wen, 1) // Pass control signals through after 1 clock cycle
reset_all()
reset_all()
println("Read control signals will comb. cause a memory request")
poke (c.io.exe_mem.mem_ctrl.read, 1)
poke (c.io.exe_mem.wrb_ctrl.rd_wen, 1)
expect(c.io.rr_io.request.valid, 1)
reset_all()
println("RAM read scenario")
poke (c.io.exe_mem.wrb_ctrl.rd_wen, 1)
poke (c.io.exe_mem.mem_ctrl.read, 1)
poke (c.io.exe_mem.alu_result, ram_address)
expect(c.io.rr_io.request.valid, 1)
step(1) // Roam responds 1 cycle later with a valid flag and a word
poke (c.io.rr_io.response.valid, 1)
poke (c.io.rr_io.response.bits.word, 42)
expect(c.io.mem_wrb.mem_read_data, 42)
reset_all()
println("Multi-cycle MMIO read scenario")
poke (c.io.exe_mem.wrb_ctrl.rd_wen, 1)
poke (c.io.exe_mem.mem_ctrl.read, 1)
poke (c.io.exe_mem.alu_result, apb_address)
expect(c.io.rr_io.request.valid, 1)
expect(c.io.hdu_mem.mem_busy, 0)
step(1) // Roam does not give a valid response after the first cycle
// exe sends a new instruction to mem, which should be ignored by mem
poke (c.io.exe_mem.wrb_ctrl.rd_wen, 0)
poke (c.io.exe_mem.mem_ctrl.read, 0)
poke (c.io.exe_mem.alu_result, ram_address)
expect(c.io.mem_wrb.wrb_ctrl.rd_wen, 0,
"""
mem is doing a multi-cycle memory transfer now so mem
should send bubbles down the pipeline until the operation is
done.
"""
)
expect(c.io.hdu_mem.mem_busy, 1, "Mem should signal that it is busy")
// The memory request signals are a part of the multi-cycle
// operation and should therefore not be bubbled like wrb's rd_wen
// control signal, but instead be held until the transfer is
// complete.
expect(c.io.rr_io.request.valid, 1)
step(1) // After 1 busy-cycle the APB request goes through
poke (c.io.rr_io.response.valid, 1)
poke (c.io.rr_io.response.bits.word, 21)
expect(c.io.hdu_mem.mem_busy, 1, "Mem should keep the busy signal for one last cycle")
expect(c.io.mem_wrb.wrb_ctrl.rd_wen, 1)
expect(c.io.mem_wrb.mem_read_data, 21)
step(1)
poke (c.io.rr_io.response.valid, 0)
poke (c.io.rr_io.response.bits.word, 0)
expect(c.io.hdu_mem.mem_busy, 0, "Mem should no longer stall the pipeline.")
expect(c.io.mem_wrb.wrb_ctrl.rd_wen, 0)
reset_all()
println("Mem will signal a stall when doing a multi-cycle memory transfer")
poke (c.io.exe_mem.wrb_ctrl.rd_wen, 1)
poke (c.io.exe_mem.mem_ctrl.read, 1)
poke (c.io.exe_mem.alu_result, apb_address)
expect(c.io.hdu_mem.mem_busy, 0,
"""Mem should not signal to the HDU that it is busy
comb. after getting multi-cycle memory transfer signals, it
should instead wait until the request has been written to the
pipeline registers. This is to make the stall signaling
consistent with other stages."""
)
step(1)
poke (c.io.exe_mem.wrb_ctrl.rd_wen, 0)
poke (c.io.exe_mem.mem_ctrl.read, 0)
poke (c.io.exe_mem.alu_result, 0)
expect(c.io.hdu_mem.mem_busy, 1,
"""We now have the MMIO transfer control signals in the mem
pipeline registers and can start signaling that we need to
stall."""
)
step(1)
expect(c.io.hdu_mem.mem_busy, 1)
poke (c.io.rr_io.response.valid, 1)
expect(c.io.hdu_mem.mem_busy, 1,
"""When the Mem stage recieves a valid response from APB it
should keep signaling that it is busy until the next cycle."""
)
step(1)
poke(c.io.rr_io.response.valid, 0)
expect(c.io.hdu_mem.mem_busy, 0)
reset_all()
println("Mem will hold the address signal during an MMIO request")
poke (c.io.exe_mem.wrb_ctrl.rd_wen, 1)
poke (c.io.exe_mem.mem_ctrl.read, 1)
poke (c.io.exe_mem.alu_result, apb_address)
expect(c.io.rr_io.request.bits.addr, apb_address)
step(1)
poke (c.io.exe_mem.wrb_ctrl.rd_wen, 0)
poke (c.io.exe_mem.mem_ctrl.read, 0)
poke (c.io.exe_mem.alu_result, 0)
expect(c.io.rr_io.request.valid, 1)
expect(c.io.rr_io.request.bits.addr, apb_address)
reset_all()
println(
"""Mem will not drop the second instruction when executing two
MMIO write instructions back-to-back.
The first instruction will write a 1 to apb_address, the second
instruction will write a 0 to apb_address, effectively creating a
pulse on apb_address."""
)
// exe sends the first instruction to mem
poke (c.io.exe_mem.mem_ctrl.write, 1)
poke (c.io.exe_mem.rs2, 1) // Value to be written
poke (c.io.exe_mem.alu_result, apb_address)
step(1)
// exe will be stalled by mem
expect(c.io.hdu_mem.mem_busy, 1)
// exe will send a bubble to mem
poke (c.io.exe_mem.mem_ctrl.write, 0)
poke (c.io.exe_mem.rs2, 0) // Value to be written
poke (c.io.exe_mem.alu_result, 0)
step(1)
// ROAM signals that the MMIO request went through
poke (c.io.rr_io.response.valid, 1)
// mem keeps stalling the pipeline for 1 more cycle
expect(c.io.hdu_mem.mem_busy, 1)
step(1)
// mem stops stalling the pipeline
expect(c.io.hdu_mem.mem_busy, 0)
// exe sends the second instruction to mem
poke (c.io.exe_mem.mem_ctrl.write, 1)
poke (c.io.exe_mem.rs2, 0) // Value to be written
poke (c.io.exe_mem.alu_result, apb_address)
// Mem should now issue the second write
expect(c.io.rr_io.request.valid, 1)
expect(c.io.rr_io.request.bits.wdata, 0)
expect(c.io.rr_io.request.bits.addr, apb_address)
expect(c.io.rr_io.request.bits.write, 1)
reset_all()
println("Mem should mux between pc_next and alu_result")
// Writeback can write either a memory read (mem_read_data), an alu
// result (alu_result), or PC + 4 (pc_next) to the register
// bank. Decode determines which one based on the instrucion and
// encodes this in rd_sel. Muxing between these options in writeback
// is expensive and on the critical path, so we do the mux between
// alu_result and pc_next in Memory instead.
val RD_SEL_ALU = 0
poke(c.io.exe_mem.wrb_ctrl.rd_sel, RD_SEL_ALU)
poke(c.io.exe_mem.alu_result, 4)
step(1)
expect(c.io.mem_wrb.pc_next_or_alu_result_or_mult_result, 4, "Mem should choose alu_result when rd_sel is 0")
reset_all()
val RD_SEL_PC_NEXT = 2
poke(c.io.exe_mem.wrb_ctrl.rd_sel, RD_SEL_PC_NEXT)
poke(c.io.exe_mem.alu_result, 4)
poke(c.io.exe_mem.pc_next, 8)
step(1)
expect(c.io.mem_wrb.pc_next_or_alu_result_or_mult_result, 8, "Mem should choose pc_next when rd_sel is 2")
step(1)
}