diff options
| author | Jack | 2022-07-30 22:41:15 +0000 |
|---|---|---|
| committer | Jack | 2022-07-30 22:41:15 +0000 |
| commit | 4cd44fa4dab370fcc5c20bcacc1fa0ee02327252 (patch) | |
| tree | 05730be260feca0d2a870c4bb88325d36631a8fc /core/src/main/scala/chisel3/package.scala | |
| parent | fe9635ef21bad233945617a24ab16cfa4055f2d1 (diff) | |
| parent | bced77045c8fc5db37e40b159c49220929e15d46 (diff) | |
Merge branch '3.5.x' into 3.5-release
Diffstat (limited to 'core/src/main/scala/chisel3/package.scala')
| -rw-r--r-- | core/src/main/scala/chisel3/package.scala | 165 |
1 files changed, 140 insertions, 25 deletions
diff --git a/core/src/main/scala/chisel3/package.scala b/core/src/main/scala/chisel3/package.scala index bd088e21..87024683 100644 --- a/core/src/main/scala/chisel3/package.scala +++ b/core/src/main/scala/chisel3/package.scala @@ -1,10 +1,14 @@ // SPDX-License-Identifier: Apache-2.0 import chisel3.internal.firrtl.BinaryPoint +import java.util.{MissingFormatArgumentException, UnknownFormatConversionException} +import scala.collection.mutable /** This package contains the main chisel3 API. */ package object chisel3 { + import internal.chiselRuntimeDeprecated + import internal.sourceinfo.DeprecatedSourceInfo import internal.firrtl.{Port, Width} import internal.Builder @@ -37,13 +41,11 @@ package object chisel3 { case bigint => Builder.error(s"Cannot convert $bigint to Bool, must be 0 or 1"); Bool.Lit(false) } - /** Int to UInt conversion, recommended style for constants. - */ - def U: UInt = UInt.Lit(bigint, Width()) + /** Int to UInt conversion, recommended style for constants. */ + def U: UInt = UInt.Lit(bigint, Width()) // scalastyle:ignore method.name - /** Int to SInt conversion, recommended style for constants. - */ - def S: SInt = SInt.Lit(bigint, Width()) + /** Int to SInt conversion, recommended style for constants. */ + def S: SInt = SInt.Lit(bigint, Width()) // scalastyle:ignore method.name /** Int to UInt conversion with specified width, recommended style for constants. */ @@ -210,29 +212,142 @@ package object chisel3 { implicit class PrintableHelper(val sc: StringContext) extends AnyVal { /** Custom string interpolator for generating Printables: p"..." - * Will call .toString on any non-Printable arguments (mimicking s"...") + * mimicks s"..." for non-Printable data) */ def p(args: Any*): Printable = { - sc.checkLengths(args) // Enforce sc.parts.size == pargs.size + 1 - val pargs: Seq[Option[Printable]] = args.map { - case p: Printable => Some(p) - case d: Data => Some(d.toPrintable) - case any => - for { - v <- Option(any) // Handle null inputs - str = v.toString - if !str.isEmpty // Handle empty Strings - } yield PString(str) + // P interpolator does not treat % differently - hence need to add % before sending to cf. + val t = sc.parts.map(_.replaceAll("%", "%%")) + StringContext(t: _*).cf(args: _*) + } + + /** Custom string interpolator for generating formatted Printables : cf"..." + * + * Enhanced version of scala's `f` interpolator. + * Each expression (argument) referenced within the string is + * converted to a particular Printable depending + * on the format specifier and type. + * + * ==== For Chisel types referenced within the String ==== + * + * - <code>%n</code> - Returns [[Name]] Printable. + * - <code>%N</code> - Returns [[FullName]] Printable. + * - <code>%b,%d,%x,%c</code> - Only applicable for types of [[Bits]] or dreived from it. - returns ([[Binary]],[[Decimal]], + * [[Hexadecimal]],[[Character]]) Printable respectively. + * - Default - If no specifier given call [[Data.toPrintable]] on the Chisel Type. + * + * ==== For [[Printable]] type: ==== + * No explicit format specifier supported - just return the Printable. + * + * ==== For regular scala types ==== + * Call String.format with the argument and specifier. + * Default is %s if no specifier is given. + * Wrap the result in [[PString]] Printable. + * + * ==== For the parts of the StringContext ==== + * Remove format specifiers and if literal percents (need to be escaped with %) + * are present convert them into [[Percent]] Printable. + * Rest of the string will be wrapped in [[PString]] Printable. + * + * @example + * {{{ + * + * val w1 = 20.U // Chisel UInt type (which extends Bits) + * val f1 = 30.2 // Scala float type. + * val pable = cf"w1 = $w1%x f1 = $f1%2.2f. This is 100%% clear" + * + * // pable is as follows + * // Printables(List(PString(w1 = ), Hexadecimal(UInt<5>(20)), PString( f1 = ), PString(30.20), PString(. This is 100), Percent, PString( clear))) + * }}} + * + * @throws UnknownFormatConversionException + * if literal percent not escaped with % or if the format specifier is not supported + * for the specific type + * + * @throws StringContext.InvalidEscapeException + * if a `parts` string contains a backslash (`\`) character + * that does not start a valid escape sequence. + * + * @throws IllegalArgumentException + * if the number of `parts` in the enclosing `StringContext` does not exceed + * the number of arguments `arg` by exactly 1. + */ + def cf(args: Any*): Printable = { + + // Handle literal % + // Takes the part string - + // - this is assumed to not have any format specifiers - already handled / removed before calling this function. + // Only thing present is literal % if any which should ideally be with %%. + // If not - then flag an error. + // Return seq of Printables (either PString or Percent or both - nothing else + def percentSplitter(s: String): Seq[Printable] = { + if (s.isEmpty) Seq(PString("")) + else { + val pieces = s.split("%%").toList.flatMap { p => + if (p.contains('%')) throw new UnknownFormatConversionException("Un-escaped % found") + // Wrap in PString and intersperse the escaped percentages + Seq(Percent, PString(p)) + } + if (pieces.isEmpty) Seq(Percent) + else pieces.tail // Don't forget to drop the extra percent we put at the beginning + } } + + def extractFormatSpecifier(part: String): (Option[String], String) = { + // Check if part starts with a format specifier (with % - disambiguate with literal % checking the next character if needed to be %) + // In the case of %f specifier there is a chance that we need more information - so capture till the 1st letter (a-zA-Z). + // Example cf"This is $val%2.2f here" - parts - Seq("This is ","%2.2f here") - the format specifier here is %2.2f. + val endFmtIdx = + if (part.length > 1 && part(0) == '%' && part(1) != '%') part.indexWhere(_.isLetter) + else -1 + val (fmt, rest) = part.splitAt(endFmtIdx + 1) + + val fmtOpt = if (fmt.nonEmpty) Some(fmt) else None + (fmtOpt, rest) + + } + + sc.checkLengths(args) // Enforce sc.parts.size == pargs.size + 1 val parts = sc.parts.map(StringContext.treatEscapes) - // Zip sc.parts and pargs together ito flat Seq - // eg. Seq(sc.parts(0), pargs(0), sc.parts(1), pargs(1), ...) - val seq = for { // append None because sc.parts.size == pargs.size + 1 - (literal, arg) <- parts.zip(pargs :+ None) - optPable <- Seq(Some(PString(literal)), arg) - pable <- optPable // Remove Option[_] - } yield pable - Printables(seq) + // The 1st part is assumed never to contain a format specifier. + // If the 1st part of a string is an argument - then the 1st part will be an empty String. + // So we need to parse parts following the 1st one to get the format specifiers if any + val partsAfterFirst = parts.tail + + // Align parts to their potential specifiers + val pables = partsAfterFirst.zip(args).flatMap { + case (part, arg) => { + val (fmt, modP) = extractFormatSpecifier(part) + val fmtArg: Printable = arg match { + case d: Data => { + fmt match { + case Some("%n") => Name(d) + case Some("%N") => FullName(d) + case Some(fForm) if d.isInstanceOf[Bits] => FirrtlFormat(fForm.substring(1, 2), d) + case Some(x) => { + val msg = s"Illegal format specifier '$x' for Chisel Data type!\n" + throw new UnknownFormatConversionException(msg) + } + case None => d.toPrintable + } + } + case p: Printable => { + fmt match { + case Some(x) => { + val msg = s"Illegal format specifier '$x' for Chisel Printable type!\n" + throw new UnknownFormatConversionException(msg) + } + case None => p + } + } + + // Generic case - use String.format (for example %d,%2.2f etc on regular Scala types) + case t => PString(fmt.getOrElse("%s").format(t)) + + } + Seq(fmtArg) ++ percentSplitter(modP) + } + } + Printables(percentSplitter(parts.head) ++ pables) } } |
