Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SRAM API: Add a parameter to initialize the memory #3364

Merged
merged 8 commits into from
Jun 22, 2023
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
105 changes: 101 additions & 4 deletions src/main/scala/chisel3/util/SRAM.scala
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import chisel3._
import chisel3.internal.Builder
import chisel3.experimental.SourceInfo
import chisel3.internal.sourceinfo.{MemTransform, SourceInfoTransform}
import chisel3.util.experimental.loadMemoryFromFileInline
import firrtl.annotations.MemoryLoadFileType
import scala.language.reflectiveCalls
import scala.language.experimental.macros

Expand Down Expand Up @@ -104,6 +106,30 @@ class SRAMInterface[T <: Data](
Vec(numReadwritePorts, new MemoryReadWritePort(tpe, addrWidth, masked))
}

/** A memory file with which to preload an [[SRAM]]
*
* See concrete subclasses [[BinaryMemoryFile]] and [[HexMemoryFile]]
*/
sealed abstract class MemoryFile(private[chisel3] val fileType: MemoryLoadFileType) {

/** The path to the memory contents file */
val path: String
}

/** A binary memory file to preload an [[SRAM]] with, represented by a filesystem path. This will annotate
* the inner [[SyncReadMem]] with `loadMemoryFromFile` using `MemoryLoadFileType.Binary` as the file type.
*
* @param path The path to the binary file
*/
case class BinaryMemoryFile(path: String) extends MemoryFile(MemoryLoadFileType.Binary)

/** A hex memory file to preload an [[SRAM]] with, represented by a filesystem path. This will annotate
* the inner [[SyncReadMem]] with `loadMemoryFromFile` using `MemoryLoadFileType.Hex` as the file type.
*
* @param path The path to the hex file
*/
case class HexMemoryFile(path: String) extends MemoryFile(MemoryLoadFileType.Hex)

object SRAM {

/** Generates a [[SyncReadMem]] within the current module, connected to an explicit number
Expand All @@ -130,7 +156,35 @@ object SRAM {
)(
implicit sourceInfo: SourceInfo
): SRAMInterface[T] =
memInterface_impl(size, tpe)(numReadPorts, numWritePorts, numReadwritePorts, Builder.forcedClock)
memInterface_impl(size, tpe)(numReadPorts, numWritePorts, numReadwritePorts, Builder.forcedClock, None)

/** Generates a [[SyncReadMem]] within the current module, connected to an explicit number
* of read, write, and read/write ports. This SRAM abstraction has both read and write capabilities: that is,
* it contains at least one read accessor (a read-only or read-write port), and at least one write accessor
* (a write-only or read-write port).
*
* @param size The desired size of the inner `SyncReadMem`
* @tparam T The data type of the memory element
* @param numReadPorts The number of desired read ports >= 0, and (numReadPorts + numReadwritePorts) > 0
* @param numWritePorts The number of desired write ports >= 0, and (numWritePorts + numReadwritePorts) > 0
* @param numReadwritePorts The number of desired read/write ports >= 0, and the above two conditions must hold
* @param memoryFile A memory file whose path is emitted as Verilog directives to initialize the inner `SyncReadMem`
*
* @return A new `SRAMInterface` wire containing the control signals for each instantiated port
* @note This does *not* return the `SyncReadMem` itself, you must interact with it using the returned bundle
* @note Read-only memories (R >= 1, W === 0, RW === 0) and write-only memories (R === 0, W >= 1, RW === 0) are not supported by this API, and will result in an error if declared.
*/
def apply[T <: Data](
size: BigInt,
tpe: T,
numReadPorts: Int,
numWritePorts: Int,
numReadwritePorts: Int,
memoryFile: MemoryFile
)(
implicit sourceInfo: SourceInfo
): SRAMInterface[T] =
memInterface_impl(size, tpe)(numReadPorts, numWritePorts, numReadwritePorts, Builder.forcedClock, Some(memoryFile))

/** Generates a [[SyncReadMem]] within the current module, connected to an explicit number
* of read, write, and read/write ports, with masking capability on all write and read/write ports.
Expand All @@ -157,15 +211,51 @@ object SRAM {
implicit evidence: T <:< Vec[_],
sourceInfo: SourceInfo
): SRAMInterface[T] =
masked_memInterface_impl(size, tpe)(numReadPorts, numWritePorts, numReadwritePorts, Builder.forcedClock)
masked_memInterface_impl(size, tpe)(numReadPorts, numWritePorts, numReadwritePorts, Builder.forcedClock, None)

/** Generates a [[SyncReadMem]] within the current module, connected to an explicit number
* of read, write, and read/write ports, with masking capability on all write and read/write ports.
* This SRAM abstraction has both read and write capabilities: that is, it contains at least one read
* accessor (a read-only or read-write port), and at least one write accessor (a write-only or read-write port).
*
* @param size The desired size of the inner `SyncReadMem`
* @tparam T The data type of the memory element
* @param numReadPorts The number of desired read ports >= 0, and (numReadPorts + numReadwritePorts) > 0
* @param numWritePorts The number of desired write ports >= 0, and (numWritePorts + numReadwritePorts) > 0
* @param numReadwritePorts The number of desired read/write ports >= 0, and the above two conditions must hold
* @param memoryFile A memory file whose path is emitted as Verilog directives to initialize the inner `SyncReadMem`
*
* @return A new `SRAMInterface` wire containing the control signals for each instantiated port
* @note This does *not* return the `SyncReadMem` itself, you must interact with it using the returned bundle
* @note Read-only memories (R >= 1, W === 0, RW === 0) and write-only memories (R === 0, W >= 1, RW === 0) are not supported by this API, and will result in an error if declared.
*/
def masked[T <: Data](
size: BigInt,
tpe: T,
numReadPorts: Int,
numWritePorts: Int,
numReadwritePorts: Int,
memoryFile: MemoryFile
)(
implicit evidence: T <:< Vec[_],
sourceInfo: SourceInfo
): SRAMInterface[T] =
masked_memInterface_impl(size, tpe)(
numReadPorts,
numWritePorts,
numReadwritePorts,
Builder.forcedClock,
Some(memoryFile)
)

private def memInterface_impl[T <: Data](
size: BigInt,
tpe: T
)(numReadPorts: Int,
numWritePorts: Int,
numReadwritePorts: Int,
clock: Clock
clock: Clock,
memoryFile: Option[MemoryFile]
)(
implicit sourceInfo: SourceInfo
): SRAMInterface[T] = {
Expand Down Expand Up @@ -205,6 +295,9 @@ object SRAM {
)
}

// Emit Verilog for preloading the memory from a file if requested
memoryFile.foreach { file: MemoryFile => loadMemoryFromFileInline(mem, file.path, file.fileType) }

_out
}

Expand All @@ -214,7 +307,8 @@ object SRAM {
)(numReadPorts: Int,
numWritePorts: Int,
numReadwritePorts: Int,
clock: Clock
clock: Clock,
memoryFile: Option[MemoryFile]
)(
implicit sourceInfo: SourceInfo,
evidence: T <:< Vec[_]
Expand Down Expand Up @@ -261,6 +355,9 @@ object SRAM {
)
}

// Emit Verilog for preloading the memory from a file if requested
memoryFile.foreach { file: MemoryFile => loadMemoryFromFileInline(mem, file.path, file.fileType) }

_out
}

Expand Down