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
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
|
// SPDX-License-Identifier: Apache-2.0
package chisel3
import scala.language.experimental.macros
import firrtl.{ir => fir}
import chisel3.internal._
import chisel3.internal.Builder.pushCommand
import chisel3.internal.firrtl._
import chisel3.internal.sourceinfo.{MemTransform, SourceInfo, SourceInfoTransform, SourceLine, UnlocatableSourceInfo}
object Mem {
/** Creates a combinational/asynchronous-read, sequential/synchronous-write [[Mem]].
*
* @param size number of elements in the memory
* @param t data type of memory element
*/
def apply[T <: Data](size: BigInt, t: T): Mem[T] = macro MemTransform.apply[T]
/** Creates a combinational/asynchronous-read, sequential/synchronous-write [[Mem]].
*
* @param size number of elements in the memory
* @param t data type of memory element
*/
def apply[T <: Data](size: Int, t: T): Mem[T] = macro MemTransform.apply[T]
/** @group SourceInfoTransformMacro */
def do_apply[T <: Data](
size: BigInt,
t: T
)(
implicit sourceInfo: SourceInfo,
compileOptions: CompileOptions
): Mem[T] = {
if (compileOptions.declaredTypeMustBeUnbound) {
requireIsChiselType(t, "memory type")
}
val mt = t.cloneTypeFull
val mem = new Mem(mt, size)
mt.bind(MemTypeBinding(mem))
pushCommand(DefMemory(sourceInfo, mem, mt, size))
mem
}
/** @group SourceInfoTransformMacro */
def do_apply[T <: Data](size: Int, t: T)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Mem[T] =
do_apply(BigInt(size), t)(sourceInfo, compileOptions)
}
sealed abstract class MemBase[T <: Data](val t: T, val length: BigInt)
extends HasId
with NamedComponent
with SourceInfoDoc {
_parent.foreach(_.addId(this))
// if the memory is created in a scope with an implicit clock (-> clockInst is defined), we will perform checks that
// ensure memory ports are created with the same clock unless explicitly specified to use a different clock
private val clockInst: Option[Clock] = Builder.currentClock
// Only kept for binary compatibility reasons, impossible for users to call
protected def clockWarning(sourceInfo: Option[SourceInfo]): Unit = clockWarning(sourceInfo, MemPortDirection.INFER)
protected def clockWarning(sourceInfo: Option[SourceInfo], dir: MemPortDirection): Unit = {
// Turn into pretty String if possible, if not, Builder.deprecated will find one via stack trace
val infoStr = sourceInfo.collect { case SourceLine(file, line, col) => s"$file:$line:$col" }
Builder.deprecated(
"The clock used to initialize the memory is different than the one used to initialize the port. " +
"If this is intentional, please pass the clock explicitly when creating the port. This behavior will be an error in 3.6.0",
infoStr
)
}
// REVIEW TODO: make accessors (static/dynamic, read/write) combinations consistent.
/** Creates a read accessor into the memory with static addressing. See the
* class documentation of the memory for more detailed information.
*/
def apply(x: BigInt): T = macro SourceInfoTransform.xArg
/** @group SourceInfoTransformMacro */
def do_apply(idx: BigInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): T = {
require(idx >= 0 && idx < length)
apply(idx.asUInt, Builder.forcedClock)
}
/** Creates a read accessor into the memory with static addressing. See the
* class documentation of the memory for more detailed information.
*/
def apply(x: Int): T = macro SourceInfoTransform.xArg
/** @group SourceInfoTransformMacro */
def do_apply(idx: Int)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): T =
do_apply(BigInt(idx))(sourceInfo, compileOptions)
/** Creates a read/write accessor into the memory with dynamic addressing.
* See the class documentation of the memory for more detailed information.
*/
def apply(x: UInt): T = macro SourceInfoTransform.xArg
/** @group SourceInfoTransformMacro */
def do_apply(idx: UInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): T =
do_apply_impl(idx, Builder.forcedClock, MemPortDirection.INFER, true)
def apply(x: UInt, y: Clock): T = macro SourceInfoTransform.xyArg
def do_apply(idx: UInt, clock: Clock)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): T =
do_apply_impl(idx, clock, MemPortDirection.INFER, false)
/** Creates a read accessor into the memory with dynamic addressing. See the
* class documentation of the memory for more detailed information.
*/
def read(x: UInt): T = macro SourceInfoTransform.xArg
/** @group SourceInfoTransformMacro */
def do_read(idx: UInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): T =
do_apply_impl(idx, Builder.forcedClock, MemPortDirection.READ, true)
/** Creates a read accessor into the memory with dynamic addressing.
* Takes a clock parameter to bind a clock that may be different
* from the implicit clock. See the class documentation of the memory
* for more detailed information.
*/
def read(x: UInt, y: Clock): T = macro SourceInfoTransform.xyArg
/** @group SourceInfoTransformMacro */
def do_read(idx: UInt, clock: Clock)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): T =
do_apply_impl(idx, clock, MemPortDirection.READ, false)
protected def do_apply_impl(
idx: UInt,
clock: Clock,
dir: MemPortDirection,
warn: Boolean
)(
implicit sourceInfo: SourceInfo,
compileOptions: CompileOptions
): T = {
if (warn && clockInst.isDefined && clock != clockInst.get) {
clockWarning(Some(sourceInfo), dir)
}
makePort(sourceInfo, idx, dir, clock)
}
/** Creates a write accessor into the memory.
*
* @param idx memory element index to write into
* @param data new data to write
*/
def write(idx: UInt, data: T)(implicit compileOptions: CompileOptions): Unit =
write_impl(idx, data, Builder.forcedClock, true)
/** Creates a write accessor into the memory with a clock
* that may be different from the implicit clock.
*
* @param idx memory element index to write into
* @param data new data to write
* @param clock clock to bind to this accessor
*/
def write(idx: UInt, data: T, clock: Clock)(implicit compileOptions: CompileOptions): Unit =
write_impl(idx, data, clock, false)
private def write_impl(
idx: UInt,
data: T,
clock: Clock,
warn: Boolean
)(
implicit compileOptions: CompileOptions
): Unit = {
if (warn && clockInst.isDefined && clock != clockInst.get) {
clockWarning(None, MemPortDirection.WRITE)
}
implicit val sourceInfo = UnlocatableSourceInfo
makePort(UnlocatableSourceInfo, idx, MemPortDirection.WRITE, clock) := data
}
/** Creates a masked write accessor into the memory.
*
* @param idx memory element index to write into
* @param data new data to write
* @param mask write mask as a Seq of Bool: a write to the Vec element in
* memory is only performed if the corresponding mask index is true.
*
* @note this is only allowed if the memory's element data type is a Vec
*/
def write(
idx: UInt,
data: T,
mask: Seq[Bool]
)(
implicit evidence: T <:< Vec[_],
compileOptions: CompileOptions
): Unit =
masked_write_impl(idx, data, mask, Builder.forcedClock, true)
/** Creates a masked write accessor into the memory with a clock
* that may be different from the implicit clock.
*
* @param idx memory element index to write into
* @param data new data to write
* @param mask write mask as a Seq of Bool: a write to the Vec element in
* memory is only performed if the corresponding mask index is true.
* @param clock clock to bind to this accessor
*
* @note this is only allowed if the memory's element data type is a Vec
*/
def write(
idx: UInt,
data: T,
mask: Seq[Bool],
clock: Clock
)(
implicit evidence: T <:< Vec[_],
compileOptions: CompileOptions
): Unit =
masked_write_impl(idx, data, mask, clock, false)
private def masked_write_impl(
idx: UInt,
data: T,
mask: Seq[Bool],
clock: Clock,
warn: Boolean
)(
implicit evidence: T <:< Vec[_],
compileOptions: CompileOptions
): Unit = {
implicit val sourceInfo = UnlocatableSourceInfo
if (warn && clockInst.isDefined && clock != clockInst.get) {
clockWarning(None, MemPortDirection.WRITE)
}
val accessor = makePort(sourceInfo, idx, MemPortDirection.WRITE, clock).asInstanceOf[Vec[Data]]
val dataVec = data.asInstanceOf[Vec[Data]]
if (accessor.length != dataVec.length) {
Builder.error(s"Mem write data must contain ${accessor.length} elements (found ${dataVec.length})")
}
if (accessor.length != mask.length) {
Builder.error(s"Mem write mask must contain ${accessor.length} elements (found ${mask.length})")
}
for (((cond, port), datum) <- mask.zip(accessor).zip(dataVec))
when(cond) { port := datum }
}
private def makePort(
sourceInfo: SourceInfo,
idx: UInt,
dir: MemPortDirection,
clock: Clock
)(
implicit compileOptions: CompileOptions
): T = {
if (Builder.currentModule != _parent) {
throwException(
s"Cannot create a memory port in a different module (${Builder.currentModule.get.name}) than where the memory is (${_parent.get.name})."
)
}
requireIsHardware(idx, "memory port index")
val i = Vec.truncateIndex(idx, length)(sourceInfo, compileOptions)
val port = pushCommand(
DefMemPort(sourceInfo, t.cloneTypeFull, Node(this), dir, i.ref, clock.ref)
).id
// Bind each element of port to being a MemoryPort
port.bind(MemoryPortBinding(Builder.forcedUserModule, Builder.currentWhen))
port
}
}
/** A combinational/asynchronous-read, sequential/synchronous-write memory.
*
* Writes take effect on the rising clock edge after the request. Reads are
* combinational (requests will return data on the same cycle).
* Read-after-write hazards are not an issue.
*
* @note when multiple conflicting writes are performed on a Mem element, the
* result is undefined (unlike Vec, where the last assignment wins)
*/
sealed class Mem[T <: Data] private[chisel3] (t: T, length: BigInt) extends MemBase(t, length) {
override protected def clockWarning(sourceInfo: Option[SourceInfo], dir: MemPortDirection): Unit = {
// Do not issue clock warnings on reads, since they are not clocked
if (dir != MemPortDirection.READ)
super.clockWarning(sourceInfo, dir)
}
}
object SyncReadMem {
type ReadUnderWrite = fir.ReadUnderWrite.Value
val Undefined = fir.ReadUnderWrite.Undefined
val ReadFirst = fir.ReadUnderWrite.Old
val WriteFirst = fir.ReadUnderWrite.New
/** Creates a sequential/synchronous-read, sequential/synchronous-write [[SyncReadMem]].
*
* @param size number of elements in the memory
* @param t data type of memory element
*/
def apply[T <: Data](size: BigInt, t: T): SyncReadMem[T] = macro MemTransform.apply[T]
def apply[T <: Data](size: BigInt, t: T, ruw: ReadUnderWrite): SyncReadMem[T] = macro MemTransform.apply_ruw[T]
/** Creates a sequential/synchronous-read, sequential/synchronous-write [[SyncReadMem]].
*
* @param size number of elements in the memory
* @param t data type of memory element
*/
def apply[T <: Data](size: Int, t: T): SyncReadMem[T] = macro MemTransform.apply[T]
def apply[T <: Data](size: Int, t: T, ruw: ReadUnderWrite): SyncReadMem[T] = macro MemTransform.apply_ruw[T]
/** @group SourceInfoTransformMacro */
def do_apply[T <: Data](
size: BigInt,
t: T,
ruw: ReadUnderWrite = Undefined
)(
implicit sourceInfo: SourceInfo,
compileOptions: CompileOptions
): SyncReadMem[T] = {
if (compileOptions.declaredTypeMustBeUnbound) {
requireIsChiselType(t, "memory type")
}
val mt = t.cloneTypeFull
val mem = new SyncReadMem(mt, size, ruw)
mt.bind(MemTypeBinding(mem))
pushCommand(DefSeqMemory(sourceInfo, mem, mt, size, ruw))
mem
}
/** @group SourceInfoTransformMacro */
// Alternate signatures can't use default parameter values
def do_apply[T <: Data](
size: Int,
t: T
)(
implicit sourceInfo: SourceInfo,
compileOptions: CompileOptions
): SyncReadMem[T] =
do_apply(BigInt(size), t)(sourceInfo, compileOptions)
/** @group SourceInfoTransformMacro */
// Alternate signatures can't use default parameter values
def do_apply[T <: Data](
size: Int,
t: T,
ruw: ReadUnderWrite
)(
implicit sourceInfo: SourceInfo,
compileOptions: CompileOptions
): SyncReadMem[T] =
do_apply(BigInt(size), t, ruw)(sourceInfo, compileOptions)
}
/** A sequential/synchronous-read, sequential/synchronous-write memory.
*
* Writes take effect on the rising clock edge after the request. Reads return
* data on the rising edge after the request. Read-after-write behavior (when
* a read and write to the same address are requested on the same cycle) is
* undefined.
*
* @note when multiple conflicting writes are performed on a Mem element, the
* result is undefined (unlike Vec, where the last assignment wins)
*/
sealed class SyncReadMem[T <: Data] private[chisel3] (t: T, n: BigInt, val readUnderWrite: SyncReadMem.ReadUnderWrite)
extends MemBase[T](t, n) {
override def read(x: UInt): T = macro SourceInfoTransform.xArg
/** @group SourceInfoTransformMacro */
override def do_read(idx: UInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): T =
do_read(idx = idx, en = true.B)
def read(x: UInt, en: Bool): T = macro SourceInfoTransform.xEnArg
/** @group SourceInfoTransformMacro */
def do_read(idx: UInt, en: Bool)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): T =
_read_impl(idx, en, Builder.forcedClock, true)
def read(idx: UInt, en: Bool, clock: Clock): T = macro SourceInfoTransform.xyzArg
/** @group SourceInfoTransformMacro */
def do_read(idx: UInt, en: Bool, clock: Clock)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): T =
_read_impl(idx, en, clock, false)
/** @group SourceInfoTransformMacro */
private def _read_impl(
addr: UInt,
enable: Bool,
clock: Clock,
warn: Boolean
)(
implicit sourceInfo: SourceInfo,
compileOptions: CompileOptions
): T = {
val a = Wire(UInt())
a := DontCare
var port: Option[T] = None
when(enable) {
a := addr
port = Some(super.do_apply_impl(a, clock, MemPortDirection.READ, warn))
}
port.get
}
// note: we implement do_read(addr) for SyncReadMem in terms of do_read(addr, en) in order to ensure that
// `mem.read(addr)` will always behave the same as `mem.read(addr, true.B)`
}
|