From 6b39db8ce67cf6edded4a87d29a6b669af9c7f6a Mon Sep 17 00:00:00 2001 From: Palmer Dabbelt Date: Tue, 16 Feb 2016 23:50:23 -0800 Subject: [PATCH] Add "NastiMemorySelector", a memory interconnect On Hurricane we want to be able to support multiple memory channels but have a fallback to fewer, since the full configuration is going to require a complicated FPGA setup. This adds another sort of interconnect that can switch between having different numbers of top-level memory channels active at chip boot time. This interconnect is a bit funny: changing the select input when there is memory traffic is a bad idea. This is fine for this use case, since we really only care about changing the memory configuration at boot time -- since it'll scramble the memory of the machine it's not so useful, anyway. The advantage is that we don't have to have a full 8x8 Nasti crossbar in our chip, which would be fairly expensive. Changing the crossbar would garble memory as well, so it's not like it would add any extra functionality. --- junctions/src/main/scala/nasti.scala | 79 ++++++++++++++++++++++++++++ 1 file changed, 79 insertions(+) diff --git a/junctions/src/main/scala/nasti.scala b/junctions/src/main/scala/nasti.scala index 57c12a69..9567cfb4 100644 --- a/junctions/src/main/scala/nasti.scala +++ b/junctions/src/main/scala/nasti.scala @@ -596,3 +596,82 @@ class NastiMemoryInterconnect( connectChannel(io.slaves(i), channelArb.io.slave) } } + +/** Allows users to switch between various memory configurations. Note that + * this is a dangerous operation: not only does switching the select input to + * this module violate Nasti, it also causes the memory of the machine to + * become garbled. It's expected that select only changes at boot time, as + * part of the memory controller configuration. */ +class NastiMemorySelector(nBanks: Int, maxMemChannels: Int, configs: Seq[Int])(implicit p: Parameters) extends NastiModule()(p) { + val nMasters = nBanks + val nSlaves = maxMemChannels + val nConfigs = configs.size + + val io = new Bundle { + val masters = Vec(nMasters, new NastiIO).flip + val slaves = Vec(nSlaves, new NastiIO) + val select = UInt(INPUT, width = log2Up(nConfigs)) + } + + def muxOnSelect(up: DecoupledIO[Bundle], dn: DecoupledIO[Bundle], active: Bool): Unit = { + when (active) { dn.bits := up.bits } + when (active) { up.ready := dn.ready } + when (active) { dn.valid := up.valid } + } + + def muxOnSelect(up: NastiIO, dn: NastiIO, active: Bool): Unit = { + muxOnSelect(up.aw, dn.aw, active) + muxOnSelect(up.w, dn.w, active) + muxOnSelect(dn.b, up.b, active) + muxOnSelect(up.ar, dn.ar, active) + muxOnSelect(dn.r, up.r, active) + } + + def muxOnSelect(up: Vec[NastiIO], dn: Vec[NastiIO], active: Bool) : Unit = { + for (i <- 0 until up.size) + muxOnSelect(up(i), dn(i), active) + } + + /* Disconnects a vector of Nasti ports, which involves setting them to + * invalid. Due to Chisel reasons, we need to also set the bits to 0 (since + * there can't be any unconnected inputs). */ + def disconnectSlave(slave: Vec[NastiIO]) = { + slave.foreach{ m => + m.aw.valid := Bool(false) + m.aw.bits := m.aw.bits.fromBits( UInt(0) ) + m.w.valid := Bool(false) + m.w.bits := m.w.bits.fromBits( UInt(0) ) + m.b.ready := Bool(false) + m.ar.valid := Bool(false) + m.ar.bits := m.ar.bits.fromBits( UInt(0) ) + m.r.ready := Bool(false) + } + } + + def disconnectMaster(master: Vec[NastiIO]) = { + master.foreach{ m => + m.aw.ready := Bool(false) + m.w.ready := Bool(false) + m.b.valid := Bool(false) + m.b.bits := m.b.bits.fromBits( UInt(0) ) + m.ar.ready := Bool(false) + m.r.valid := Bool(false) + m.r.bits := m.r.bits.fromBits( UInt(0) ) + } + } + + /* Provides default wires on all our outputs. */ + disconnectMaster(io.masters) + disconnectSlave(io.slaves) + + /* Constructs interconnects for each of the layouts suggested by the + * configuration and switches between them based on the select input. */ + configs.zipWithIndex.foreach{ case (nChannels, select) => + val nBanksPerChannel = nBanks / nChannels + val ic = Module(new NastiMemoryInterconnect(nBanksPerChannel, nChannels)) + disconnectMaster(ic.io.slaves) + disconnectSlave(ic.io.masters) + muxOnSelect( io.masters, ic.io.masters, io.select === UInt(select)) + muxOnSelect(ic.io.slaves, io.slaves, io.select === UInt(select)) + } +}