<commit_before>package fringe

import util._
import chisel3._
import chisel3.util._
import templates.SRFF
import templates.Utils.log2Up
import scala.language.reflectiveCalls

class MAGCore(
  val w: Int,
  val d: Int,
  val v: Int,
  val loadStreamInfo: List[StreamParInfo],
  val storeStreamInfo: List[StreamParInfo],
  val numOutstandingBursts: Int,
  val burstSizeBytes: Int,
  val blockingDRAMIssue: Boolean = false
) extends Module { // AbstractMAG(w, d, v, numOutstandingBursts, burstSizeBytes) {

  // The data bus width to DRAM is 1-burst wide
  // While it should be possible in the future to add a write combining buffer
  // and decouple Plasticine's data bus width from the DRAM bus width,
  // this is currently left out as a wishful todo.
  // Check and error out if the data bus width does not match DRAM burst size
  Predef.assert(w/8*v == 64,
  s"Unsupported combination of w=$w and v=$v; data bus width must equal DRAM burst size ($burstSizeBytes bytes)")

  val numStreams = loadStreamInfo.size + storeStreamInfo.size
  val wordSizeBytes = w/8
  val burstSizeWords = burstSizeBytes / wordSizeBytes
  val tagWidth = log2Up(numStreams)

  val io = IO(new Bundle {
    // Debug
    val enable = Input(Bool())

    val dbg = new DebugSignals

//    val app = Vec(numStreams, new MemoryStream(w, v))
    val app = new AppStreams(loadStreamInfo, storeStreamInfo)
    val dram = new DRAMStream(w, v) // Should not have to pass vector width; must be DRAM burst width
    val config = Input(new MAGOpcode())
  })

  def extractBurstAddr(addr: UInt) = {
    val burstOffset = log2Up(burstSizeBytes)
    addr(addr.getWidth-1, burstOffset)
  }

  def extractBurstOffset(addr: UInt) = {
    val burstOffset = log2Up(burstSizeBytes)
    addr(burstOffset-1, 0)
  }

  def extractWordOffset(addr: UInt) = {
    val burstOffset = log2Up(burstSizeBytes)
    val wordOffset = log2Up(w)
    addr(burstOffset, wordOffset)
  }

  val addrWidth = 64
  // Addr FIFO
  val addrFifo = Module(new FIFOArbiter(addrWidth, d, v, numStreams))
  val addrFifoConfig = Wire(new FIFOOpcode(d, v))
  addrFifoConfig.chainRead := 1.U
  addrFifoConfig.chainWrite := 1.U
  addrFifo.io.config := addrFifoConfig

  addrFifo.io.forceTag.valid := 0.U
  val cmds = io.app.loads.map { _.cmd} ++ io.app.stores.map {_.cmd}
  addrFifo.io.enq.zip(cmds) foreach {case (enq, cmd) => enq(0) := cmd.bits.addr }
  addrFifo.io.enqVld.zip(cmds) foreach {case (enqVld, cmd) => enqVld := cmd.valid }


  // Debug assertions
  for (i <- 0 until numStreams) {
    val str3 = s"ERROR: addrFifo $i enqVld is high when not enabled!"
    assert((io.enable & cmds(i).valid) | (io.enable & ~cmds(i).valid) | (~io.enable & ~cmds(i).valid), str3)
  }

  val burstAddrs = addrFifo.io.deq map { extractBurstAddr(_) }
  val wordOffsets = addrFifo.io.deq map { extractWordOffset(_) }

  // isWr FIFO: Currently a 1-bit FIFO. Ideally we would have a single FIFO
  // for the entire 'cmd' struct, which would require changes to the FIFO and SRAM templates.
  val isWrFifo = Module(new FIFOArbiter(1, d, v, numStreams))
  val isWrFifoConfig = Wire(new FIFOOpcode(d, v))
  isWrFifoConfig.chainRead := 1.U
  isWrFifoConfig.chainWrite := 1.U
  isWrFifo.io.config := isWrFifoConfig

  isWrFifo.io.forceTag.valid := 0.U
  isWrFifo.io.enq.zip(cmds) foreach { case (enq, cmd) => enq(0) := cmd.bits.isWr }
  isWrFifo.io.enqVld.zip(cmds) foreach {case (enqVld, cmd) => enqVld := cmd.valid }

  // isSparse FIFO: Currently a 1-bit FIFO. Ideally we would have a single FIFO
  // for the entire 'cmd' struct, which would require changes to the FIFO and SRAM templates.
  val isSparseFifo = Module(new FIFOArbiter(1, d, v, numStreams))
  val isSparseFifoConfig = Wire(new FIFOOpcode(d, v))
  isSparseFifoConfig.chainRead := 1.U
  isSparseFifoConfig.chainWrite := 1.U
  isSparseFifo.io.config := isSparseFifoConfig

  isSparseFifo.io.forceTag.valid := 0.U
  isSparseFifo.io.enq.zip(cmds) foreach { case (enq, cmd) => enq(0) := cmd.bits.isSparse }
  isSparseFifo.io.enqVld.zip(cmds) foreach {case (enqVld, cmd) => enqVld := cmd.valid }

  val isSparse = isSparseFifo.io.deq(0)(0) & ~isSparseFifo.io.empty
  io.dram.cmd.bits.isSparse := isSparse

  val scatterGather = Wire(Bool())
  scatterGather := io.config.scatterGather
//  val sgPulse = Wire(Bool())
//  scatterGather := isSparseFifo.io.deq(0)(0) & ~isSparseFifo.io.empty
//  val sgPulser = Module(new Pulser())
//  sgPulser.io.in := scatterGather
//  sgPulse := sgPulser.io.out

  // Size FIFO
  val sizeFifo = Module(new FIFOArbiter(w, d, v, numStreams))
  val sizeFifoConfig = Wire(new FIFOOpcode(d, v))
  sizeFifoConfig.chainRead := 1.U
  sizeFifoConfig.chainWrite := 1.U
  sizeFifo.io.config := sizeFifoConfig
  sizeFifo.io.forceTag.valid := 0.U
  sizeFifo.io.enq.zip(cmds) foreach { case (enq, cmd) => enq(0) := cmd.bits.size }
  sizeFifo.io.enqVld.zip(cmds) foreach {case (enqVld, cmd) => enqVld := cmd.valid & ~scatterGather }

  val sizeTop = sizeFifo.io.deq(0)
  val sizeInBursts = extractBurstAddr(sizeTop) + (extractBurstOffset(sizeTop) != 0.U)
  io.dram.cmd.bits.size := sizeInBursts

  // WData FIFO
  // Deriving the 'enqVld' signal from the other FIFO states is potentially
  // dangerous because there is no guarantee that the data input pins actually contain
  // valid data. The safest approach is to have separate enables for command (addr, size, rdwr)
  // and data.<commit_msg>Use single, large SRAMs for address and size FIFO<commit_after>package fringe

import util._
import chisel3._
import chisel3.util._
import templates.SRFF
import templates.Utils.log2Up
import scala.language.reflectiveCalls

class MAGCore(
  val w: Int,
  val d: Int,
  val v: Int,
  val loadStreamInfo: List[StreamParInfo],
  val storeStreamInfo: List[StreamParInfo],
  val numOutstandingBursts: Int,
  val burstSizeBytes: Int,
  val blockingDRAMIssue: Boolean = false
) extends Module { // AbstractMAG(w, d, v, numOutstandingBursts, burstSizeBytes) {

  // The data bus width to DRAM is 1-burst wide
  // While it should be possible in the future to add a write combining buffer
  // and decouple Plasticine's data bus width from the DRAM bus width,
  // this is currently left out as a wishful todo.
  // Check and error out if the data bus width does not match DRAM burst size
  Predef.assert(w/8*v == 64,
  s"Unsupported combination of w=$w and v=$v; data bus width must equal DRAM burst size ($burstSizeBytes bytes)")

  val numStreams = loadStreamInfo.size + storeStreamInfo.size
  val wordSizeBytes = w/8
  val burstSizeWords = burstSizeBytes / wordSizeBytes
  val tagWidth = log2Up(numStreams)

  val io = IO(new Bundle {
    // Debug
    val enable = Input(Bool())

    val dbg = new DebugSignals

//    val app = Vec(numStreams, new MemoryStream(w, v))
    val app = new AppStreams(loadStreamInfo, storeStreamInfo)
    val dram = new DRAMStream(w, v) // Should not have to pass vector width; must be DRAM burst width
    val config = Input(new MAGOpcode())
  })

  def extractBurstAddr(addr: UInt) = {
    val burstOffset = log2Up(burstSizeBytes)
    addr(addr.getWidth-1, burstOffset)
  }

  def extractBurstOffset(addr: UInt) = {
    val burstOffset = log2Up(burstSizeBytes)
    addr(burstOffset-1, 0)
  }

  def extractWordOffset(addr: UInt) = {
    val burstOffset = log2Up(burstSizeBytes)
    val wordOffset = log2Up(w)
    addr(burstOffset, wordOffset)
  }

  val addrWidth = 64
  // Addr FIFO
  val addrFifo = Module(new FIFOArbiter(addrWidth, d, 1, numStreams))
  val addrFifoConfig = Wire(new FIFOOpcode(d, 1))
  addrFifoConfig.chainRead := 1.U
  addrFifoConfig.chainWrite := 1.U
  addrFifo.io.config := addrFifoConfig

  addrFifo.io.forceTag.valid := 0.U
  val cmds = io.app.loads.map { _.cmd} ++ io.app.stores.map {_.cmd}
  addrFifo.io.enq.zip(cmds) foreach {case (enq, cmd) => enq(0) := cmd.bits.addr }
  addrFifo.io.enqVld.zip(cmds) foreach {case (enqVld, cmd) => enqVld := cmd.valid }


  // Debug assertions
  for (i <- 0 until numStreams) {
    val str3 = s"ERROR: addrFifo $i enqVld is high when not enabled!"
    assert((io.enable & cmds(i).valid) | (io.enable & ~cmds(i).valid) | (~io.enable & ~cmds(i).valid), str3)
  }

  val burstAddrs = addrFifo.io.deq map { extractBurstAddr(_) }
  val wordOffsets = addrFifo.io.deq map { extractWordOffset(_) }

  // isWr FIFO: Currently a 1-bit FIFO. Ideally we would have a single FIFO
  // for the entire 'cmd' struct, which would require changes to the FIFO and SRAM templates.
  val isWrFifo = Module(new FIFOArbiter(1, d, v, numStreams))
  val isWrFifoConfig = Wire(new FIFOOpcode(d, v))
  isWrFifoConfig.chainRead := 1.U
  isWrFifoConfig.chainWrite := 1.U
  isWrFifo.io.config := isWrFifoConfig

  isWrFifo.io.forceTag.valid := 0.U
  isWrFifo.io.enq.zip(cmds) foreach { case (enq, cmd) => enq(0) := cmd.bits.isWr }
  isWrFifo.io.enqVld.zip(cmds) foreach {case (enqVld, cmd) => enqVld := cmd.valid }

  // isSparse FIFO: Currently a 1-bit FIFO. Ideally we would have a single FIFO
  // for the entire 'cmd' struct, which would require changes to the FIFO and SRAM templates.
  val isSparseFifo = Module(new FIFOArbiter(1, d, v, numStreams))
  val isSparseFifoConfig = Wire(new FIFOOpcode(d, v))
  isSparseFifoConfig.chainRead := 1.U
  isSparseFifoConfig.chainWrite := 1.U
  isSparseFifo.io.config := isSparseFifoConfig

  isSparseFifo.io.forceTag.valid := 0.U
  isSparseFifo.io.enq.zip(cmds) foreach { case (enq, cmd) => enq(0) := cmd.bits.isSparse }
  isSparseFifo.io.enqVld.zip(cmds) foreach {case (enqVld, cmd) => enqVld := cmd.valid }

  val isSparse = isSparseFifo.io.deq(0)(0) & ~isSparseFifo.io.empty
  io.dram.cmd.bits.isSparse := isSparse

  val scatterGather = Wire(Bool())
  scatterGather := io.config.scatterGather
//  val sgPulse = Wire(Bool())
//  scatterGather := isSparseFifo.io.deq(0)(0) & ~isSparseFifo.io.empty
//  val sgPulser = Module(new Pulser())
//  sgPulser.io.in := scatterGather
//  sgPulse := sgPulser.io.out

  // Size FIFO
  val sizeFifo = Module(new FIFOArbiter(w, d, 1, numStreams))
  val sizeFifoConfig = Wire(new FIFOOpcode(d, 1))
  sizeFifoConfig.chainRead := 1.U
  sizeFifoConfig.chainWrite := 1.U
  sizeFifo.io.config := sizeFifoConfig
  sizeFifo.io.forceTag.valid := 0.U
  sizeFifo.io.enq.zip(cmds) foreach { case (enq, cmd) => enq(0) := cmd.bits.size }
  sizeFifo.io.enqVld.zip(cmds) foreach {case (enqVld, cmd) => enqVld := cmd.valid & ~scatterGather }

  val sizeTop = sizeFifo.io.deq(0)
  val sizeInBursts = extractBurstAddr(sizeTop) + (extractBurstOffset(sizeTop) != 0.U)
  io.dram.cmd.bits.size := sizeInBursts

  // WData FIFO
  // Deriving the 'enqVld' signal from the other FIFO states is potentially
  // dangerous because there is no guarantee that the data input pins actually contain
  // valid data. The safest approach is to have separate enables for command (addr, size, rdwr)
  // and data.
{'fraction_non_alphanumeric': 0.1175, 'fraction_numerical': 0.009074074074074075, 'mean_word_length': 3.747692307692308, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 19, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1794029', 'n_tokens_mistral': 4199, 'n_tokens_neox': 3943, 'n_words': 1063}