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

add transforms to lint module name conflicts and override module names #2452

Merged
merged 50 commits into from
Jun 11, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
50 commits
Select commit Hold shift + click to select a range
5bed0b5
StabilizeModuleNames: add basic transform, tests
albertchen-sifive May 6, 2020
aa1cf66
add compare_names.py script
albertchen-sifive May 6, 2020
6f42818
cleanup
albertchen-sifive May 6, 2020
a9185d5
more cleanup
albertchen-sifive May 6, 2020
ee4c11d
StabilizeModuelNames: prepend code to hash
albertchen-sifive May 6, 2020
11aa648
fix makefiles/scripts
albertchen-sifive May 6, 2020
bb29565
add NamingStrategyAnnotation
albertchen-sifive May 8, 2020
e6becb7
fix Makefrag, scripts/sampler.sh
albertchen-sifive May 8, 2020
538c521
cleanup
albertchen-sifive May 8, 2020
c170a59
update tests, fix bugs
albertchen-sifive May 8, 2020
ba2920a
update prereqs
albertchen-sifive May 8, 2020
01c05d4
fix typo
albertchen-sifive May 8, 2020
403f204
travis.yml: require sudo
albertchen-sifive May 8, 2020
b40fbfb
remove verbose plusarg
albertchen-sifive May 10, 2020
d13983d
more makefile updates
albertchen-sifive May 11, 2020
94b17c2
move scala compile earlier
albertchen-sifive May 11, 2020
87f5c41
more Makefile updates
albertchen-sifive May 12, 2020
fd87227
refactor StabilizeModuleNames as LintAmbiguousModuleNames
albertchen-sifive May 13, 2020
98ad330
get rid of trailing space
albertchen-sifive May 13, 2020
40ba474
revert make/travis changes
albertchen-sifive May 13, 2020
e0544be
add scaladoc, cleanup
albertchen-sifive May 13, 2020
0c51208
improve aspect, add more comments
albertchen-sifive May 14, 2020
012c765
respond to feedback
albertchen-sifive May 18, 2020
c7ee967
add multiple desiredName check
albertchen-sifive May 18, 2020
7b868e9
more updates
albertchen-sifive May 18, 2020
aa79772
fix typo
albertchen-sifive May 27, 2020
dedc363
split LintConflictingModuleNames into two
albertchen-sifive May 29, 2020
aa4e345
update tests
albertchen-sifive May 29, 2020
2953537
remove compare_names.py and sampler.sh
albertchen-sifive May 29, 2020
0749c37
revert Makefrag changes
albertchen-sifive May 29, 2020
12a7cd1
fix typo
albertchen-sifive May 29, 2020
4f71d6b
fix typo
albertchen-sifive May 29, 2020
6f9ca9a
remove unused naming strategies
albertchen-sifive May 29, 2020
0881c42
update tests
albertchen-sifive May 29, 2020
86312ba
add final name assert
albertchen-sifive May 29, 2020
5536bcd
update scaladoc
albertchen-sifive May 29, 2020
2e83fd9
Apply suggestions from code review
albertchen-sifive May 29, 2020
896ca0e
match on RawModule instead of chisel3.Module
albertchen-sifive May 29, 2020
28e0b33
add another test
albertchen-sifive May 29, 2020
b97d66f
RenameDesiredNamesSpec: add annos check
albertchen-sifive May 29, 2020
be3a6dc
move around files
albertchen-sifive Jun 1, 2020
26f206c
add test for lint pass, move rename tests
albertchen-sifive Jun 1, 2020
07564fb
Apply suggestions from code review
albertchen-sifive Jun 4, 2020
b55e593
Apply suggestions from code review
albertchen-sifive Jun 4, 2020
66326bb
update scaladoc with suggestions from code review
albertchen-sifive Jun 4, 2020
ab1c355
add combined rename + lint tests
albertchen-sifive Jun 4, 2020
637107d
fix typos
albertchen-sifive Jun 5, 2020
0216873
RenameDesiredNames: move package
albertchen-sifive Jun 5, 2020
5d70580
RenameDesireNames: remove naming strategy
albertchen-sifive Jun 5, 2020
e6a6a88
update recommended fix, add comment
albertchen-sifive Jun 8, 2020
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ project/target
*~
.addons-dont-touch
/lib/
/test_lib/
8 changes: 7 additions & 1 deletion Makefrag
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ space := $() $()
splitConfigs := $(subst $(comma), ,$(CONFIG))
configBases := $(foreach config,$(splitConfigs),$(lastword $(subst ., ,$(config))))
CONFIG_STR := $(subst $(space),_,$(configBases))
long_name = $(PROJECT).$(CONFIG_STR)
long_name ?= $(PROJECT).$(CONFIG_STR)

VLSI_MEM_GEN ?= $(base_dir)/scripts/vlsi_mem_gen

Expand All @@ -39,14 +39,20 @@ ROCKET_CLASS_DIRS ?= \

ROCKET_CLASSES ?= $(subst $(SPACE),:,$(ROCKET_CLASS_DIRS))
FIRRTL_JAR ?= $(base_dir)/firrtl/utils/bin/firrtl.jar
FIRRTL_TEST_JAR ?= $(base_dir)/firrtl/utils/bin/firrtl-test.jar
FIRRTL ?= java -Xmx$(JVM_MEMORY) -Xss8M -XX:MaxPermSize=256M -cp "$(FIRRTL_JAR)":"$(ROCKET_CLASSES)" firrtl.Driver

# Build firrtl.jar and put it where chisel3 can find it.
$(FIRRTL_JAR): $(shell find $(base_dir)/firrtl/src/main/scala -iname "*.scala")
$(MAKE) -C $(base_dir)/firrtl SBT="$(SBT)" root_dir=$(base_dir)/firrtl build-scala
cd $(base_dir)/firrtl && $(SBT) "Test / assembly"
touch $(FIRRTL_JAR)
mkdir -p $(base_dir)/lib
cp -p $(FIRRTL_JAR) $(base_dir)/lib

mkdir -p $(base_dir)/test_lib
cp -p $(FIRRTL_JAR) $(base_dir)/test_lib
cp -p $(FIRRTL_TEST_JAR) $(base_dir)/test_lib
# When chisel3 pr 448 is merged, the following extraneous copy may be removed.
mkdir -p $(base_dir)/chisel3/lib
cp -p $(FIRRTL_JAR) $(base_dir)/chisel3/lib
Expand Down
3 changes: 2 additions & 1 deletion build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,8 @@ lazy val rocketchip = dependOnChisel(project in file("."))
mappings in (Compile, packageSrc) ++= (mappings in (hardfloat, Compile, packageSrc)).value,
mappings in (Compile, packageBin) ++= (mappings in (`rocket-macros`, Compile, packageBin)).value,
mappings in (Compile, packageSrc) ++= (mappings in (`rocket-macros`, Compile, packageSrc)).value,
exportJars := true
exportJars := true,
Test / unmanagedBase := baseDirectory.value / "test_lib"
)


Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
freechips.rocketchip.linting.LintReporter
freechips.rocketchip.linting.rule.LintAnonymousRegisters
freechips.rocketchip.linting.rule.LintTruncatingWidths
freechips.rocketchip.linting.rule.LintConflictingModuleNames
46 changes: 46 additions & 0 deletions src/main/scala/aspects/RenameModulesAspect.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// See LICENSE.SiFive for license details.

package freechips.rocketchip.aspects

import chisel3.aop.Aspect
import chisel3.RawModule

import firrtl.AnnotationSeq
import firrtl.stage.RunFirrtlTransformAnnotation

import freechips.rocketchip.transforms.naming.{OverrideDesiredNameAnnotation, RenameDesiredNames}

/** An aspect that renames modules
*
* @example renaming all Queues in a design to "Queue_$bundleType_entries_$numEntries"
* {{{
* case object StabilizeQueueNames extends RenameModulesAspect({ top: RawModule =>
* chisel3.aop.Select.collectDeep(top) {
* case m: Queue[_] =>
* m -> s"Queue_${m.genType.getClass.getSimpleName}_entries_${m.entries}"
* }.toSeq
* })
* }}}
*
* @example renaming a specific instance of RocketCore in the design
* {{{
* case object RenameRocketCore0 extends RenameModulesAspect({ top: RawModule =>
* chisel3.aop.Select.collectDeep(top) {
* case th: TestHarness =>
* val core = th.ldut.rocketTiles.head.module.core
* core -> "Rocket_Core_0"
* }.toSeq
* })
* }}}
*
* @param collectNameOverrides a function that takes the design top module and returns pairs of name overrides for the modules under the top module in the hierarchy
*/
abstract class RenameModulesAspect(
collectNameOverrides: RawModule => Seq[(RawModule, String)]
) extends Aspect[RawModule] {
final def toAnnotation(top: RawModule): AnnotationSeq = {
RunFirrtlTransformAnnotation(new RenameDesiredNames) +: collectNameOverrides(top).map {
azidar marked this conversation as resolved.
Show resolved Hide resolved
case (m, nameOverride) => OverrideDesiredNameAnnotation(nameOverride, m.toTarget)
}
}
}
3 changes: 2 additions & 1 deletion src/main/scala/linting/Linter.scala
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ object Linter {
// Update list for any new lint rule
private[linting] lazy val linters = Seq(
new rule.LintAnonymousRegisters,
new rule.LintTruncatingWidths
new rule.LintTruncatingWidths,
new rule.LintConflictingModuleNames
)

private [linting] lazy val lintMap = linters.flatMap {
Expand Down
104 changes: 104 additions & 0 deletions src/main/scala/linting/rule/LintConflictingModuleNames.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
// See LICENSE.SiFive for license details.

package freechips.rocketchip.linting
package rule

import firrtl._
import firrtl.annotations.{IsModule, SingleTargetAnnotation, Target}
import firrtl.ir._
import firrtl.options.Dependency

import freechips.rocketchip.transforms.naming.RenameDesiredNames

import chisel3.aop.{Aspect, Select}
import chisel3.RawModule

import scala.collection.mutable

/** Captures the original desired name for a module
*/
case class DesiredNameAnnotation(
desiredName: String,
target: IsModule
) extends SingleTargetAnnotation[IsModule] {
def duplicate(newTarget: IsModule): DesiredNameAnnotation = {
this.copy(target = newTarget)
}
}

/** Collects [[DesiredNameAnnotation]]s for [[LintConflictingModuleNames]] to lint
*/
case object LintConflictingModuleNamesAspect extends Aspect[RawModule] {
def toAnnotation(top: RawModule): AnnotationSeq = {
Select.collectDeep(top) {
case m: RawModule => DesiredNameAnnotation(m.desiredName, m.toTarget)
}.toSeq
}
}

/** This LintRule checks for module name collisions
*
* Module name collisions occur when different [[Module]]s are annotated with
* [[DesiredNameAnnotation]]s that have the same desiredName. Module name
* conflicts will cause a [[LintViolation]].
*/
final class LintConflictingModuleNames extends LintRule {
val recommendedFix: String = "override desiredName based on module parameters ('override def desiredName = \"...\"') or use RenameModulesAspect"

val lintName: String = "conflicting-module-names"

// depends on DedupModules which comes from super[LintRule].optionalPrerequisites
override def optionalPrerequisites: Seq[Dependency[Transform]] =
Dependency[RenameDesiredNames] +: super.optionalPrerequisites
mwachs5 marked this conversation as resolved.
Show resolved Hide resolved

override def execute(state: CircuitState): CircuitState = {
val violations = new Violations()

val modMap = state.circuit.modules.collect {
case m: Module => m.name -> m
}.toMap

val desiredNameAnnos = state.annotations.collect {
case a: DesiredNameAnnotation if a.target.circuit == state.circuit.main => a
}

val moduleToDesiredName: mutable.Map[String, mutable.Set[String]] = mutable.Map()

val nameMap = desiredNameAnnos.groupBy(_.desiredName).mapValues { annos =>
annos.map(a => Target.referringModule(a.target).module).distinct.map { referringModule =>
require(modMap.contains(referringModule), s"ModuleNameAnnotations may not refer to blackboxes: $referringModule")
val desiredNames = moduleToDesiredName.getOrElseUpdate(referringModule, mutable.Set())
desiredNames += annos.head.desiredName
modMap(referringModule)
}
}

val conflictingDesiredNames = moduleToDesiredName.collect {
case kv@ (moduleName, desiredName) if desiredName.size > 1 => kv
}

require(conflictingDesiredNames.size == 0, {
val explanation = conflictingDesiredNames.map {
case (modName, desiredNames) => s" ${modName}: ${desiredNames.mkString(", ")}"
}.mkString("\n")
s"Modules may not have more than one desiredName:\n${explanation}"
})

nameMap.foreach {
case (desiredName, modules) if modules.size > 1 =>
val msg = s"Module conflicts for desired name $desiredName: ${modules.map(_.name).mkString(", ")}"
val info = MultiInfo(modules.map(_.info))
val mods = violations.getOrElse((info, msg), Set.empty)
violations((info, msg)) = mods ++ modules.map(_.name)
case _ =>
}

val whitelist = collectWhitelist(state.annotations)
val errorList = violations.collect {
case ((info, message), mods) if !isWhitelisted(info, whitelist) => Violation(this, info, message, mods)
}.toSeq.sortBy { _.toString }
val newAnnos = errorList ++ state.annotations

state.copy(annotations = newAnnos)
}
}
2 changes: 1 addition & 1 deletion src/main/scala/stage/RocketChipCli.scala
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ trait RocketChipCli { this: Shell =>
Seq(
TopModuleAnnotation,
ConfigsAnnotation,
OutputBaseNameAnnotation,
OutputBaseNameAnnotation
)
.foreach(_.addOptions(parser))

Expand Down
131 changes: 131 additions & 0 deletions src/main/scala/transforms/naming/RenameDesiredNames.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
// See LICENSE.SiFive for license details.

package freechips.rocketchip.transforms.naming

import firrtl._
import firrtl.annotations.{CircuitTarget, HasSerializationHints, IsModule, SingleTargetAnnotation, Target}
import firrtl.ir._
import firrtl.options.Dependency
import firrtl.transforms.DedupModules

import freechips.rocketchip.linting.rule.DesiredNameAnnotation

import scala.collection.mutable

/** A helper to rename modules in a [[Circuit]]
*/
object RenameModules {
def onStmt(moduleNameMap: Map[String, String])(stmt: Statement): Statement = stmt match {
case inst: WDefInstance if moduleNameMap.contains(inst.module) => inst.copy(module = moduleNameMap(inst.module))
case inst: DefInstance if moduleNameMap.contains(inst.module) => inst.copy(module = moduleNameMap(inst.module))
case other => other.mapStmt(onStmt(moduleNameMap))
}

/** Renames the modules in a circuit given a mapping of old names to new names
*
* @param nameMappings mapping of old to new names
* @param circuit the circuit to rename
*/
def apply(nameMappings: Map[String, String], circuit: Circuit): Circuit = {
val modules = circuit.modules.map {
case mod: Module => mod.mapStmt(onStmt(nameMappings)).mapString(m => nameMappings.getOrElse(m, m))
case ext: ExtModule => ext
}
val main = nameMappings.getOrElse(circuit.main, circuit.main)
circuit.copy(main = main, modules = modules)
}
}

/** Specifies the desired name to rename the module to, overriding the Module.desiredName
*/
case class OverrideDesiredNameAnnotation(
desiredName: String,
target: IsModule
) extends SingleTargetAnnotation[IsModule] {
def duplicate(newTarget: IsModule): OverrideDesiredNameAnnotation = {
this.copy(target = newTarget)
}
}

/** Renames modules based on their overridden desired names
*
* Desired module name overrides are specified by
* [[OverrideDesiredNameAnnotation]].
*/
class RenameDesiredNames extends Transform with DependencyAPIMigration {

override def prerequisites = Seq(Dependency[DedupModules])
override def optionalPrerequisiteOf = Seq(Dependency[VerilogEmitter])

override def invalidates(transform: Transform) = false

def execute(state: CircuitState): CircuitState = {
val modMap = state.circuit.modules.collect {
case m: Module => m.name -> m
}.toMap

val overrideDesiredNameAnnos = state.annotations.collect {
case a: OverrideDesiredNameAnnotation if a.target.circuit == state.circuit.main => a
}

val moduleToDesiredName: mutable.Map[String, mutable.Set[String]] = mutable.Map()

val nameMap = overrideDesiredNameAnnos.groupBy(_.desiredName).mapValues { annos =>
annos.map(a => Target.referringModule(a.target).module).distinct.map { referringModule =>
require(modMap.contains(referringModule), "ModuleNameAnnotations may not refer to blackboxes")
val desiredNames = moduleToDesiredName.getOrElseUpdate(referringModule, mutable.Set())
desiredNames += annos.head.desiredName
modMap(referringModule)
}
}

val conflictingDesiredNames = moduleToDesiredName.collect {
case kv@ (moduleName, desiredName) if desiredName.size > 1 => kv
}

require(conflictingDesiredNames.size == 0, {
val explanation = conflictingDesiredNames.map {
case (modName, desiredNames) => s" ${modName}: ${desiredNames.mkString(", ")}"
}.mkString("\n")
s"Modules may not have more than one desiredName:\n${explanation}"
})

val renamedDesiredNames = mutable.Set[String]()
val nameMappings = nameMap.flatMap { case (desiredName, modules) =>
if (modules.size == 1) {
renamedDesiredNames += desiredName
Some(modules.head.name -> desiredName)
} else {
None
}
}.toMap

val finalNames = state.circuit.modules.map {
case m: Module if nameMappings.contains(m.name) => nameMappings(m.name)
case m: DefModule => m.name
}
val renameConflicts = finalNames.groupBy(identity).collect {
case (_, conflicts) if conflicts.size > 1 => conflicts.head
}
require(renameConflicts.size == 0, s"desired names conflict with pre-existing module names: ${renameConflicts.mkString(", ")}")

val circuit = RenameModules(nameMappings, state.circuit)

val newMain = CircuitTarget(circuit.main)
val oldMain = CircuitTarget(state.circuit.main)
val renames = RenameMap()
nameMappings.foreach { case (from, to) =>
renames.record(oldMain.module(from), newMain.module(to))
}

// delete override annotations and rename desired name annotations for ones that were renamed
val newAnnos = state.annotations.flatMap {
case a: OverrideDesiredNameAnnotation if renamedDesiredNames(a.desiredName) => None
case a: DesiredNameAnnotation if nameMappings.contains(Target.referringModule(a.target).module) =>
Some(a.copy(desiredName = nameMappings(Target.referringModule(a.target).module)))
case a => Some(a)
}

state.copy(circuit = circuit, annotations = newAnnos, renames = Some(renames))
}
}
Loading