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
|
// 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.{SourceInfo, SourceInfoTransform, UnlocatableSourceInfo, MemTransform}
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))
// 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
/** 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: BigInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): T = {
require(idx >= 0 && idx < length)
apply(idx.asUInt)
}
/** @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 =
makePort(sourceInfo, idx, MemPortDirection.INFER)
/** 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 =
makePort(sourceInfo, idx, MemPortDirection.READ)
/** 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 = {
implicit val sourceInfo = UnlocatableSourceInfo
makePort(UnlocatableSourceInfo, idx, MemPortDirection.WRITE) := 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 = {
implicit val sourceInfo = UnlocatableSourceInfo
val accessor = makePort(sourceInfo, idx, MemPortDirection.WRITE).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)(implicit compileOptions: CompileOptions): T = {
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, Builder.forcedClock.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 (t: T, length: BigInt) extends MemBase(t, length)
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 (t: T, n: BigInt, val readUnderWrite: SyncReadMem.ReadUnderWrite) extends MemBase[T](t, n) {
def read(x: UInt, en: Bool): T = macro SourceInfoTransform.xEnArg
/** @group SourceInfoTransformMacro */
def do_read(addr: UInt, enable: Bool)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): T = {
val a = Wire(UInt())
a := DontCare
var port: Option[T] = None
when (enable) {
a := addr
port = Some(read(a))
}
port.get
}
}
|