diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c35d7c8 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +/out/ +/.idea/ +/.bsp/ diff --git a/tilelink/src/bundle/MessageTypes.scala b/tilelink/src/bundle/MessageTypes.scala new file mode 100644 index 0000000..989e541 --- /dev/null +++ b/tilelink/src/bundle/MessageTypes.scala @@ -0,0 +1,117 @@ +// SPDX-License-Identifier: Apache-2.0 + +package org.chipsalliance.tilelink +package bundle + +import chisel3._ +import chisel3.internal.firrtl.Width + +/** opcode field of TileLink messages + * + * cf. TileLink Spec 1.9.3 Table 12 + */ +object OpCode { + protected[tilelink] val width: Width = 3.W + + val PutFullData: UInt = 0.U(width) + val PutPartialData: UInt = 1.U(width) + val ArithmeticData: UInt = 2.U(width) + val LogicalData: UInt = 3.U(width) + val Get: UInt = 4.U(width) + val Intent: UInt = 5.U(width) + val AcquireBlock: UInt = 6.U(width) + val AcquirePerm: UInt = 7.U(width) + val ProbePerm: UInt = 7.U(width) + val AccessAck: UInt = 0.U(width) + val AccessAckData: UInt = 1.U(width) + val HintAck: UInt = 2.U(width) + val ProbeAck: UInt = 4.U(width) + val ProbeAckData: UInt = 5.U(width) + val Release: UInt = 6.U(width) + val ReleaseData: UInt = 7.U(width) + val Grant: UInt = 4.U(width) + val GrantData: UInt = 5.U(width) + val ProbeBlock: UInt = 6.U(width) + val ReleaseAck: UInt = 6.U(width) + // GrantAck has no Opcode +} + +/** param field of TileLink messages + * + * Allowed values are dependent on opcode of the message. + */ +object Param { + protected[tilelink] val width: Width = 3.W + + /** Reserved param fields (e.g. a_param in Get message) should be tied to zero + */ + def tieZero: UInt = 0.U(width) + + /** param field for ArithmeticData + * TileLink Spec 1.9.3 Table 23 + */ + object Arithmetic { + val MIN: UInt = 0.U(width) + val MAX: UInt = 1.U(width) + val MINU: UInt = 2.U(width) + val MAXU: UInt = 3.U(width) + val ADD: UInt = 4.U(width) + } + + /** param field for LogicalData + * TileLink Spec 1.9.3 Table 25 + */ + object Logical { + val XOR: UInt = 0.U(width) + val OR: UInt = 1.U(width) + val AND: UInt = 2.U(width) + val SWAP: UInt = 3.U(width) + } + + /** param field for Intent + * TileLink Spec 1.9.3 Table 27 + */ + object Intent { + val PrefetchRead: UInt = 0.U(width) + val PrefetchWrite: UInt = 1.U(width) + val CBOInval: UInt = 5.U(width) + val CBOClean: UInt = 6.U(width) + val CBOFlush: UInt = 7.U(width) + } + + /** permissions transitions encodings for Cap + * TileLink Spec 1.9.3 Table 31 + */ + object Cap { + val toT: UInt = 0.U(width) + val toB: UInt = 1.U(width) + val toN: UInt = 2.U(width) + } + + /** permissions transitions encodings for Grow + * TileLink Spec 1.9.3 Table 31 + */ + object Grow { + val NtoB: UInt = 0.U(width) + val NtoT: UInt = 1.U(width) + val BtoT: UInt = 2.U(width) + } + + /** permissions transitions encodings for Prune + * TileLink Spec 1.9.3 Table 31 + */ + object Prune { + val TtoB: UInt = 0.U(width) + val TtoN: UInt = 1.U(width) + val BtoN: UInt = 2.U(width) + } + + /** permissions transitions encodings for Report + * TileLink Spec 1.9.3 Table 31 + */ + object Report { + val TtoT: UInt = 3.U(width) + val BtoB: UInt = 4.U(width) + val NtoN: UInt = 5.U(width) + } +} diff --git a/tilelink/src/bundle/NoTLCException.scala b/tilelink/src/bundle/NoTLCException.scala new file mode 100644 index 0000000..2664447 --- /dev/null +++ b/tilelink/src/bundle/NoTLCException.scala @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: Apache-2.0 + +package org.chipsalliance.tilelink +package bundle + +import chisel3.ChiselException +private class NoTLCException( + channel: String, + linkParameter: TLLinkParameter) + extends ChiselException( + s"call $channel in TLLink is not present in a TL-UL or TL-UH bus:\n $linkParameter" + ) diff --git a/tilelink/src/bundle/TLChannel.scala b/tilelink/src/bundle/TLChannel.scala new file mode 100644 index 0000000..05e4e89 --- /dev/null +++ b/tilelink/src/bundle/TLChannel.scala @@ -0,0 +1,60 @@ +// SPDX-License-Identifier: Apache-2.0 + +package org.chipsalliance.tilelink +package bundle + +import chisel3._ + +trait TLChannel extends Bundle { + val parameter: TLChannelParameter +} + +class TLChannelA(val parameter: TileLinkChannelAParameter) extends TLChannel { + private val maskWidth = parameter.dataWidth / 8 + // NOTE: this field is called a_code in TileLink spec version 1.9.3 p. 16, which is probably a typo + val opcode: UInt = UInt(OpCode.width) + val param: UInt = UInt(Param.width) + val size: UInt = UInt(parameter.sizeWidth.W) + val source: UInt = UInt(parameter.sourceWidth.W) + val address: UInt = UInt(parameter.addressWidth.W) + val mask: UInt = UInt(maskWidth.W) + val data: UInt = UInt(parameter.dataWidth.W) + val corrupt: Bool = Bool() +} + +class TLChannelB(val parameter: TileLinkChannelBParameter) extends TLChannel { + private val maskWidth = parameter.dataWidth / 8 + val opcode: UInt = UInt(OpCode.width) + val param: UInt = UInt(Param.width) + val size: UInt = UInt(parameter.sizeWidth.W) + val source: UInt = UInt(parameter.sourceWidth.W) + val address: UInt = UInt(parameter.addressWidth.W) + val mask: UInt = UInt(maskWidth.W) + val data: UInt = UInt(parameter.dataWidth.W) + val corrupt: Bool = Bool() +} + +class TLChannelC(val parameter: TileLinkChannelCParameter) extends TLChannel { + val opcode: UInt = UInt(OpCode.width) + val param: UInt = UInt(Param.width) + val size: UInt = UInt(parameter.sizeWidth.W) + val source: UInt = UInt(parameter.sourceWidth.W) + val address: UInt = UInt(parameter.addressWidth.W) + val data: UInt = UInt(parameter.dataWidth.W) + val corrupt: Bool = Bool() +} + +class TLChannelD(val parameter: TileLinkChannelDParameter) extends TLChannel { + val opcode: UInt = UInt(OpCode.width) + val param: UInt = UInt(Param.width) + val size: UInt = UInt(parameter.sizeWidth.W) + val source: UInt = UInt(parameter.sourceWidth.W) + val sink: UInt = UInt(parameter.sinkWidth.W) + val denied: Bool = Bool() + val data: UInt = UInt(parameter.dataWidth.W) + val corrupt: Bool = Bool() +} + +class TLChannelE(val parameter: TileLinkChannelEParameter) extends TLChannel { + val sink: UInt = UInt(parameter.sinkWidth.W) +} diff --git a/tilelink/src/bundle/TLChannelParameter.scala b/tilelink/src/bundle/TLChannelParameter.scala new file mode 100644 index 0000000..4d25b68 --- /dev/null +++ b/tilelink/src/bundle/TLChannelParameter.scala @@ -0,0 +1,117 @@ +// SPDX-License-Identifier: Apache-2.0 + +package org.chipsalliance.tilelink +package bundle + +import chisel3.util.isPow2 + +import upickle.default.{macroRW, ReadWriter => RW} + +sealed trait TLChannelParameter + +object TLChannelParameter { + implicit val rw: RW[TLChannelParameter] = RW.merge( + TileLinkChannelAParameter.rw, + TileLinkChannelBParameter.rw, + TileLinkChannelCParameter.rw, + TileLinkChannelDParameter.rw, + TileLinkChannelEParameter.rw + ) + + def bundle(p: TLChannelParameter): TLChannel = { + p match { + case a: TileLinkChannelAParameter => new TLChannelA(a) + case b: TileLinkChannelBParameter => new TLChannelB(b) + case c: TileLinkChannelCParameter => new TLChannelC(c) + case d: TileLinkChannelDParameter => new TLChannelD(d) + case e: TileLinkChannelEParameter => new TLChannelE(e) + } + } +} + +case class TileLinkChannelAParameter( + addressWidth: Int, + sourceWidth: Int, + dataWidth: Int, + sizeWidth: Int) + extends TLChannelParameter { + require(addressWidth > 0) + require(sourceWidth > 0) + require(dataWidth > 0) + require(sizeWidth > 0) + require(dataWidth % 8 == 0, "Width of data field must be multiples of 8") + require( + isPow2(dataWidth / 8), + "Width of data field in bytes must be power of 2" + ) +} +object TileLinkChannelAParameter { + implicit val rw: RW[TileLinkChannelAParameter] = macroRW +} + +case class TileLinkChannelBParameter( + addressWidth: Int, + sourceWidth: Int, + dataWidth: Int, + sizeWidth: Int) + extends TLChannelParameter { + require(addressWidth > 0) + require(sourceWidth > 0) + require(dataWidth > 0) + require(sizeWidth > 0) + require(dataWidth % 8 == 0, "Width of data field must be multiples of 8") + require( + isPow2(dataWidth / 8), + "Width of data field in bytes must be power of 2" + ) +} +object TileLinkChannelBParameter { + implicit val rw: RW[TileLinkChannelBParameter] = macroRW +} + +case class TileLinkChannelCParameter( + addressWidth: Int, + sourceWidth: Int, + dataWidth: Int, + sizeWidth: Int) + extends TLChannelParameter { + require(addressWidth > 0) + require(sourceWidth > 0) + require(dataWidth > 0) + require(sizeWidth > 0) + require(dataWidth % 8 == 0, "Width of data field must be multiples of 8") + require( + isPow2(dataWidth / 8), + "Width of data field in bytes must be power of 2" + ) +} +object TileLinkChannelCParameter { + implicit val rw: RW[TileLinkChannelCParameter] = macroRW +} + +case class TileLinkChannelDParameter( + sourceWidth: Int, + sinkWidth: Int, + dataWidth: Int, + sizeWidth: Int) + extends TLChannelParameter { + require(sourceWidth > 0) + require(sinkWidth > 0) + require(dataWidth > 0) + require(sizeWidth > 0) + require(dataWidth % 8 == 0, "Width of data field must be multiples of 8") + require( + isPow2(dataWidth / 8), + "Width of data field in bytes must be power of 2" + ) +} +object TileLinkChannelDParameter { + implicit val rw: RW[TileLinkChannelDParameter] = macroRW +} + +case class TileLinkChannelEParameter(sinkWidth: Int) extends TLChannelParameter { + require(sinkWidth > 0) +} +object TileLinkChannelEParameter { + implicit val rw: RW[TileLinkChannelEParameter] = macroRW +} diff --git a/tilelink/src/bundle/TLLink.scala b/tilelink/src/bundle/TLLink.scala new file mode 100644 index 0000000..5ceccde --- /dev/null +++ b/tilelink/src/bundle/TLLink.scala @@ -0,0 +1,98 @@ +// SPDX-License-Identifier: Apache-2.0 + +package org.chipsalliance.tilelink +package bundle + +import chisel3._ +import chisel3.util._ +import utils.OH1._ + +import scala.collection.immutable.SeqMap + +object TLLink { + def hasData(x: TLChannel): Bool = { + x match { + case a: TLChannelA => !a.opcode(2) + // opcode === PutFullData || + // opcode === PutPartialData || + // opcode === ArithmeticData || + // opcode === LogicalData + case b: TLChannelB => !b.opcode(2) + // opcode === PutFullData || + // opcode === PutPartialData || + // opcode === ArithmeticData || + // opcode === LogicalData + case c: TLChannelC => c.opcode(0) + // opcode === AccessAckData || + // opcode === ProbeAckData || + // opcode === ReleaseData + case d: TLChannelD => d.opcode(0) + // opcode === AccessAckData || + // opcode === GrantData + case e: TLChannelE => false.B + } + } + + def opcode(x: TLChannel): Option[UInt] = { + x.elements.get("opcode").map(_.asUInt) + } + + def size(x: TLChannel): Option[UInt] = { + x.elements.get("size").map(_.asUInt) + } + + /** + * Circuit generating total number of data beats in current transaction minus 1. + */ + def numBeatsMinus1(x: TLChannel): UInt = { + // exploit the fact that the OH1 encoding of size is exactly size of transaction in bytes minus 1 (2^size - 1) + x match { + case a: TLChannelA => (UIntToOH1(a.size, a.parameter.sizeWidth) >> log2Ceil(a.parameter.dataWidth / 8)).asUInt + case a: TLChannelB => (UIntToOH1(a.size, a.parameter.sizeWidth) >> log2Ceil(a.parameter.dataWidth / 8)).asUInt + case a: TLChannelC => (UIntToOH1(a.size, a.parameter.sizeWidth) >> log2Ceil(a.parameter.dataWidth / 8)).asUInt + case a: TLChannelD => (UIntToOH1(a.size, a.parameter.sizeWidth) >> log2Ceil(a.parameter.dataWidth / 8)).asUInt + case _: TLChannelE => 0.U + } + } +} + +class TLLink(val parameter: TLLinkParameter) extends Record { + def a: DecoupledIO[TLChannelA] = + elements("a").asInstanceOf[DecoupledIO[TLChannelA]] + + def b: DecoupledIO[TLChannelB] = + elements + .getOrElse("b", throw new NoTLCException("b", parameter)) + .asInstanceOf[DecoupledIO[TLChannelB]] + + def c: DecoupledIO[TLChannelC] = + elements + .getOrElse("c", throw new NoTLCException("c", parameter)) + .asInstanceOf[DecoupledIO[TLChannelC]] + + def d: DecoupledIO[TLChannelD] = + elements("d").asInstanceOf[DecoupledIO[TLChannelD]] + + def e: DecoupledIO[TLChannelE] = + elements + .getOrElse("e", throw new NoTLCException("e", parameter)) + .asInstanceOf[DecoupledIO[TLChannelE]] + + val elements: SeqMap[String, DecoupledIO[Bundle]] = + SeqMap[String, DecoupledIO[Bundle]]( + "a" -> DecoupledIO(new TLChannelA(parameter.channelAParameter)), + "d" -> Flipped(DecoupledIO(new TLChannelD(parameter.channelDParameter))) + ) ++ ( + if (parameter.hasBCEChannels) + Seq( + "b" -> Flipped( + DecoupledIO(new TLChannelB(parameter.channelBParameter.get)) + ), + "c" -> DecoupledIO(new TLChannelC(parameter.channelCParameter.get)), + "e" -> DecoupledIO(new TLChannelE(parameter.channelEParameter.get)) + ) + else + Seq() + ) + +} diff --git a/tilelink/src/bundle/TLLinkParameter.scala b/tilelink/src/bundle/TLLinkParameter.scala new file mode 100644 index 0000000..caabe20 --- /dev/null +++ b/tilelink/src/bundle/TLLinkParameter.scala @@ -0,0 +1,59 @@ +// SPDX-License-Identifier: Apache-2.0 + +package org.chipsalliance.tilelink +package bundle + +import chisel3.util.isPow2 + +import upickle.default.{macroRW, ReadWriter => RW} + +/** Parameter of a TileLink link bundle + * + * All width values are specified in bits + * + * @param hasBCEChannels + * whether the link has channel B, C and E (i.e. is a TL-C link) + */ +case class TLLinkParameter( + addressWidth: Int, + sourceWidth: Int, + sinkWidth: Int, + dataWidth: Int, + sizeWidth: Int, + hasBCEChannels: Boolean) { + require(addressWidth > 0) + require(sourceWidth > 0) + require(sinkWidth > 0) + require(dataWidth > 0) + require(sizeWidth > 0) + require(dataWidth % 8 == 0, "Width of data field must be multiples of 8") + require( + isPow2(dataWidth / 8), + "Width of data field in bytes must be power of 2" + ) + + def channelAParameter: TileLinkChannelAParameter = + TileLinkChannelAParameter(addressWidth, sourceWidth, dataWidth, sizeWidth) + def channelBParameter: Option[TileLinkChannelBParameter] = + Option.when(hasBCEChannels)(TileLinkChannelBParameter(addressWidth, sourceWidth, dataWidth, sizeWidth)) + def channelCParameter: Option[TileLinkChannelCParameter] = + Option.when(hasBCEChannels)(TileLinkChannelCParameter(addressWidth, sourceWidth, dataWidth, sizeWidth)) + def channelDParameter: TileLinkChannelDParameter = + TileLinkChannelDParameter(sourceWidth, sinkWidth, dataWidth, sizeWidth) + def channelEParameter: Option[TileLinkChannelEParameter] = + Option.when(hasBCEChannels)(TileLinkChannelEParameter(sinkWidth)) +} + +object TLLinkParameter { + def union(x: TLLinkParameter*): TLLinkParameter = + TLLinkParameter( + addressWidth = x.map(_.addressWidth).max, + sourceWidth = x.map(_.sourceWidth).max, + sinkWidth = x.map(_.sinkWidth).max, + dataWidth = x.map(_.dataWidth).max, + sizeWidth = x.map(_.sizeWidth).max, + hasBCEChannels = x.map(_.hasBCEChannels).fold(false)(_ || _) + ) + + implicit val rw: RW[TLLinkParameter] = macroRW +} diff --git a/tilelink/src/utils/OH1.scala b/tilelink/src/utils/OH1.scala new file mode 100644 index 0000000..06b3e26 --- /dev/null +++ b/tilelink/src/utils/OH1.scala @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2016-2017 SiFive, Inc. + +package org.chipsalliance.tilelink +package utils + +import chisel3._ +import chisel3.util.{Cat, OHToUInt} + +/** + * Utilities for one-hot-1s encoding. + * + * For a value x, one-hot encoding puts a single 1 on the x-th bit, while one-hot-1s encoding puts x consecutive 1's + * on the least-significant x bits in the encoded value. + * + * Example: UIntToOH1(4.U, 8) => b00001111 + */ +object OH1 { + def OH1ToOH(x: UInt): UInt = + ((x << 1).asUInt | 1.U) & (~Cat(0.U(1.W), x)).asUInt + + def OH1ToUInt(x: UInt): UInt = OHToUInt(OH1ToOH(x)) + + def UIntToOH1(x: UInt, width: Int): UInt = + (~((-1).S(width.W).asUInt << x)(width - 1, 0)).asUInt + + def UIntToOH1(x: UInt): UInt = UIntToOH1(x, (1 << x.getWidth) - 1) +}