From c5f9ea3133ef363ff8944e17d94fea79767b6bed Mon Sep 17 00:00:00 2001 From: Jim Lawson Date: Wed, 6 Jul 2016 10:01:23 -0700 Subject: Rename "Chisel" to "chisel3" (only git mv). --- .../src/main/scala/chisel3/core/Binding.scala | 179 +++++++++++++++++++++ 1 file changed, 179 insertions(+) create mode 100644 chiselFrontend/src/main/scala/chisel3/core/Binding.scala (limited to 'chiselFrontend/src/main/scala/chisel3/core/Binding.scala') diff --git a/chiselFrontend/src/main/scala/chisel3/core/Binding.scala b/chiselFrontend/src/main/scala/chisel3/core/Binding.scala new file mode 100644 index 00000000..949ac9cd --- /dev/null +++ b/chiselFrontend/src/main/scala/chisel3/core/Binding.scala @@ -0,0 +1,179 @@ +package Chisel + +/** + * The purpose of a Binding is to indicate what type of hardware 'entity' a + * specific Data's leaf Elements is actually bound to. All Data starts as being + * Unbound (and the whole point of cloneType is to return an unbound version). + * Then, specific API calls take a Data, and return a bound version (either by + * binding the original model or cloneType then binding the clone). For example, + * Reg[T<:Data](...) returns a T bound to RegBinding. + * + * It is considered invariant that all Elements of a single Data are bound to + * the same concrete type of Binding. + * + * These bindings can be checked (e.g. checkSynthesizable) to make sure certain + * operations are valid. For example, arithemetic operations or connections can + * only be executed between synthesizable nodes. These checks are to avoid + * undefined reference errors. + * + * Bindings can carry information about the particular element in the graph it + * represents like: + * - For ports (and unbound), the 'direction' + * - For (relevant) synthesizable nodes, the enclosing Module + * + * TODO(twigg): Enrich the bindings to carry more information like the hosting + * module (when applicable), direction (when applicable), literal info (when + * applicable). Can ensure applicable data only stored on relevant nodes. e.g. + * literal info on LitBinding, direction info on UnboundBinding and PortBinding, + * etc. + * + * TODO(twigg): Currently, bindings only apply at the Element level and an + * Aggregate is considered bound via its elements. May be appropriate to allow + * Aggregates to be bound along with the Elements. However, certain literal and + * port direction information doesn't quite make sense in aggregates. This would + * elegantly handle the empty Vec or Bundle problem though. + * + * TODO(twigg): Binding is currently done via allElements. It may be more + * elegant if this was instead done as a more explicit tree walk as that allows + * for better errors. + */ + +object Binding { + // Two bindings are 'compatible' if they are the same type. + // Check currently kind of weird: just ensures same class + private def compatible(a: Binding, b: Binding): Boolean = a.getClass == b.getClass + private def compatible(nodes: Seq[Binding]): Boolean = + if(nodes.size > 1) + (for((a,b) <- nodes zip nodes.tail) yield compatible(a,b)) + .fold(true)(_&&_) + else true + + case class BindingException(message: String) extends Exception(message) + def AlreadyBoundException(binding: String) = BindingException(s": Already bound to $binding") + def NotSynthesizableException = BindingException(s": Not bound to synthesizable node, currently only Type description") + + // This recursively walks down the Data tree to look at all the leaf 'Element's + // Will build up an error string in case something goes wrong + // TODO(twigg): Make member function of Data. + // Allows oddities like sample_element to be better hidden + private def walkToBinding(target: Data, checker: Element=>Unit): Unit = target match { + case (element: Element) => checker(element) + case (vec: Vec[Data @unchecked]) => { + try walkToBinding(vec.sample_element, checker) + catch { + case BindingException(message) => throw BindingException(s"(*)$message") + } + for(idx <- 0 until vec.length) { + try walkToBinding(vec(idx), checker) + catch { + case BindingException(message) => throw BindingException(s"($idx)$message") + } + } + } + case (bundle: Bundle) => { + for((field, subelem) <- bundle.elements) { + try walkToBinding(subelem, checker) + catch { + case BindingException(message) => throw BindingException(s".$field$message") + } + } + } + } + + // Use walkToBinding to actually rebind the node type + def bind[T<:Data](target: T, binder: Binder[_<:Binding], error_prelude: String): target.type = { + try walkToBinding( + target, + element => element.binding match { + case unbound @ UnboundBinding(_) => { + element.binding = binder(unbound) + } + case binding => throw AlreadyBoundException(binding.toString) + } + ) + catch { + case BindingException(message) => throw BindingException(s"$error_prelude$message") + } + target + } + + // Excepts if any root element is already bound + def checkUnbound(target: Data, error_prelude: String): Unit = { + try walkToBinding( + target, + element => element.binding match { + case unbound @ UnboundBinding(_) => {} + case binding => throw AlreadyBoundException(binding.toString) + } + ) + catch { + case BindingException(message) => throw BindingException(s"$error_prelude$message") + } + } + + // Excepts if any root element is unbound and thus not on the hardware graph + def checkSynthesizable(target: Data, error_prelude: String): Unit = + try walkToBinding( + target, + element => element.binding match { + case SynthesizableBinding() => {} // OK + case binding => throw NotSynthesizableException + } + ) + catch { + case BindingException(message) => throw BindingException(s"$error_prelude$message") + } +} + +// Location refers to 'where' in the Module hierarchy this lives +sealed trait Binding { + def location: Option[Module] + def direction: Option[Direction] +} + +// Constrained-ness refers to whether 'bound by Module boundaries' +// An unconstrained binding, like a literal, can be read by everyone +sealed trait UnconstrainedBinding extends Binding { + def location = None +} +// A constrained binding can only be read/written by specific modules +// Location will track where this Module is +sealed trait ConstrainedBinding extends Binding { + def enclosure: Module + def location = Some(enclosure) +} + +// An undirectioned binding means the element represents an internal node +// with no meaningful concept of a direction +sealed trait UndirectionedBinding extends Binding { def direction = None } + +// This is the default binding, represents data not yet positioned in the graph +case class UnboundBinding(direction: Option[Direction]) + extends Binding with UnconstrainedBinding + + +// A synthesizable binding is 'bound into' the hardware graph +object SynthesizableBinding { + def unapply(target: Binding): Boolean = target.isInstanceOf[SynthesizableBinding] + // Type check OK because Binding and SynthesizableBinding is sealed +} +sealed trait SynthesizableBinding extends Binding +case class LitBinding() // will eventually have literal info + extends SynthesizableBinding with UnconstrainedBinding with UndirectionedBinding + +case class MemoryPortBinding(enclosure: Module) + extends SynthesizableBinding with ConstrainedBinding with UndirectionedBinding + +// TODO(twigg): Ops between unenclosed nodes can also be unenclosed +// However, Chisel currently binds all op results to a module +case class OpBinding(enclosure: Module) + extends SynthesizableBinding with ConstrainedBinding with UndirectionedBinding + +case class PortBinding(enclosure: Module, direction: Option[Direction]) + extends SynthesizableBinding with ConstrainedBinding + +case class RegBinding(enclosure: Module) + extends SynthesizableBinding with ConstrainedBinding with UndirectionedBinding + +case class WireBinding(enclosure: Module) + extends SynthesizableBinding with ConstrainedBinding with UndirectionedBinding -- cgit v1.2.3 From 12810b5efe6a8f872fbc1c63cdfb835ca354624f Mon Sep 17 00:00:00 2001 From: Jim Lawson Date: Wed, 6 Jul 2016 09:31:47 -0700 Subject: Update Chisel -> chisel3 references. --- chiselFrontend/src/main/scala/chisel3/core/Binding.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'chiselFrontend/src/main/scala/chisel3/core/Binding.scala') diff --git a/chiselFrontend/src/main/scala/chisel3/core/Binding.scala b/chiselFrontend/src/main/scala/chisel3/core/Binding.scala index 949ac9cd..d8d9ebd2 100644 --- a/chiselFrontend/src/main/scala/chisel3/core/Binding.scala +++ b/chiselFrontend/src/main/scala/chisel3/core/Binding.scala @@ -1,4 +1,4 @@ -package Chisel +package chisel3.core /** * The purpose of a Binding is to indicate what type of hardware 'entity' a -- cgit v1.2.3 From e09a09e3f4e5d6d8650b1db4add96c0a5b09e8ca Mon Sep 17 00:00:00 2001 From: Jim Lawson Date: Mon, 25 Jul 2016 16:36:06 -0700 Subject: Enable current (chisel2-style) compatibility mode. --- .../src/main/scala/chisel3/core/Binding.scala | 25 ++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) (limited to 'chiselFrontend/src/main/scala/chisel3/core/Binding.scala') diff --git a/chiselFrontend/src/main/scala/chisel3/core/Binding.scala b/chiselFrontend/src/main/scala/chisel3/core/Binding.scala index d8d9ebd2..75a80e4f 100644 --- a/chiselFrontend/src/main/scala/chisel3/core/Binding.scala +++ b/chiselFrontend/src/main/scala/chisel3/core/Binding.scala @@ -112,17 +112,38 @@ object Binding { } // Excepts if any root element is unbound and thus not on the hardware graph - def checkSynthesizable(target: Data, error_prelude: String): Unit = + def checkSynthesizable(target: Data, error_prelude: String): Unit = { + // This is called is we support autoIOWrap + def elementOfIO(element: Data): Boolean = { + element._parent match { + case None => false + case Some(x: Module) => { + // io.flatten eliminates Clock elements, so we need to use io.allElements + val ports = x.io.allElements + val isIOElement = ports.contains(element) || element == x.clock || element == x.reset + isIOElement + } + } + } try walkToBinding( target, element => element.binding match { case SynthesizableBinding() => {} // OK - case binding => throw NotSynthesizableException + case binding => + // The following kludge is an attempt to provide backward compatibility + // It should be done at at higher level. + if (!(autoIOWrap && elementOfIO(element))) + throw NotSynthesizableException + else + Binding.bind(element, PortBinder(element._parent.get), "Error: IO") } ) catch { case BindingException(message) => throw BindingException(s"$error_prelude$message") } + } + // This should be configure by options in Driver. + private[chisel3] var autoIOWrap = true } // Location refers to 'where' in the Module hierarchy this lives -- cgit v1.2.3 From f41f2533c55e506f7d5bf2ee0198de4d9a3dbea3 Mon Sep 17 00:00:00 2001 From: Jim Lawson Date: Tue, 16 Aug 2016 11:59:20 -0700 Subject: Reduce rocket-chip elaboration errors. --- .../src/main/scala/chisel3/core/Binding.scala | 33 ++++++++++++++-------- 1 file changed, 22 insertions(+), 11 deletions(-) (limited to 'chiselFrontend/src/main/scala/chisel3/core/Binding.scala') diff --git a/chiselFrontend/src/main/scala/chisel3/core/Binding.scala b/chiselFrontend/src/main/scala/chisel3/core/Binding.scala index 75a80e4f..555ba4d5 100644 --- a/chiselFrontend/src/main/scala/chisel3/core/Binding.scala +++ b/chiselFrontend/src/main/scala/chisel3/core/Binding.scala @@ -1,5 +1,7 @@ package chisel3.core +import chisel3.internal.Builder.compileOptions + /** * The purpose of a Binding is to indicate what type of hardware 'entity' a * specific Data's leaf Elements is actually bound to. All Data starts as being @@ -88,6 +90,8 @@ object Binding { case unbound @ UnboundBinding(_) => { element.binding = binder(unbound) } + // If autoIOWrap is enabled and we're rebinding a PortBinding, just ignore the rebinding. + case portBound @ PortBinding(_, _) if (compileOptions.autoIOWrap && binder.isInstanceOf[PortBinder]) => case binding => throw AlreadyBoundException(binding.toString) } ) @@ -113,15 +117,24 @@ object Binding { // Excepts if any root element is unbound and thus not on the hardware graph def checkSynthesizable(target: Data, error_prelude: String): Unit = { - // This is called is we support autoIOWrap - def elementOfIO(element: Data): Boolean = { + // This is called if we support autoIOWrap + def elementOfIO(element: Element): Boolean = { element._parent match { case None => false case Some(x: Module) => { - // io.flatten eliminates Clock elements, so we need to use io.allElements - val ports = x.io.allElements - val isIOElement = ports.contains(element) || element == x.clock || element == x.reset - isIOElement + // Have we defined the IO ports for this module? If not, do so now. + if (!x.ioDefined) { + x.computePorts + element.binding match { + case SynthesizableBinding() => true + case _ => false + } + } else { + // io.flatten eliminates Clock elements, so we need to use io.allElements + val ports = x.io.allElements + val isIOElement = ports.contains(element) || element == x.clock || element == x.reset + isIOElement + } } } } @@ -132,7 +145,7 @@ object Binding { case binding => // The following kludge is an attempt to provide backward compatibility // It should be done at at higher level. - if (!(autoIOWrap && elementOfIO(element))) + if (!(compileOptions.autoIOWrap && elementOfIO(element))) throw NotSynthesizableException else Binding.bind(element, PortBinder(element._parent.get), "Error: IO") @@ -142,8 +155,6 @@ object Binding { case BindingException(message) => throw BindingException(s"$error_prelude$message") } } - // This should be configure by options in Driver. - private[chisel3] var autoIOWrap = true } // Location refers to 'where' in the Module hierarchy this lives @@ -196,5 +207,5 @@ case class PortBinding(enclosure: Module, direction: Option[Direction]) case class RegBinding(enclosure: Module) extends SynthesizableBinding with ConstrainedBinding with UndirectionedBinding -case class WireBinding(enclosure: Module) - extends SynthesizableBinding with ConstrainedBinding with UndirectionedBinding +case class WireBinding(enclosure: Module, direction: Option[Direction]) + extends SynthesizableBinding with ConstrainedBinding -- cgit v1.2.3 From 7922f8d4998dd902ee18a6e85e4a404a1f29eb3f Mon Sep 17 00:00:00 2001 From: Jim Lawson Date: Wed, 17 Aug 2016 13:30:05 -0700 Subject: Rocket-chip updates. Assume LHSItOutput if neither side is driving. Restore Wire()'s removal of direction in binding. --- chiselFrontend/src/main/scala/chisel3/core/Binding.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'chiselFrontend/src/main/scala/chisel3/core/Binding.scala') diff --git a/chiselFrontend/src/main/scala/chisel3/core/Binding.scala b/chiselFrontend/src/main/scala/chisel3/core/Binding.scala index 555ba4d5..da678fed 100644 --- a/chiselFrontend/src/main/scala/chisel3/core/Binding.scala +++ b/chiselFrontend/src/main/scala/chisel3/core/Binding.scala @@ -207,5 +207,5 @@ case class PortBinding(enclosure: Module, direction: Option[Direction]) case class RegBinding(enclosure: Module) extends SynthesizableBinding with ConstrainedBinding with UndirectionedBinding -case class WireBinding(enclosure: Module, direction: Option[Direction]) - extends SynthesizableBinding with ConstrainedBinding +case class WireBinding(enclosure: Module) + extends SynthesizableBinding with ConstrainedBinding with UndirectionedBinding -- cgit v1.2.3 From 5fcdd12fe92bd22f9cdfb8f5e39e510684b709bf Mon Sep 17 00:00:00 2001 From: Jim Lawson Date: Mon, 29 Aug 2016 09:33:51 -0700 Subject: Rename individual compile options. Stricter values are "true". Current default (not strict) values are "false". --- chiselFrontend/src/main/scala/chisel3/core/Binding.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'chiselFrontend/src/main/scala/chisel3/core/Binding.scala') diff --git a/chiselFrontend/src/main/scala/chisel3/core/Binding.scala b/chiselFrontend/src/main/scala/chisel3/core/Binding.scala index da678fed..a32d3ade 100644 --- a/chiselFrontend/src/main/scala/chisel3/core/Binding.scala +++ b/chiselFrontend/src/main/scala/chisel3/core/Binding.scala @@ -91,7 +91,7 @@ object Binding { element.binding = binder(unbound) } // If autoIOWrap is enabled and we're rebinding a PortBinding, just ignore the rebinding. - case portBound @ PortBinding(_, _) if (compileOptions.autoIOWrap && binder.isInstanceOf[PortBinder]) => + case portBound @ PortBinding(_, _) if (!compileOptions.requireIOWrap && binder.isInstanceOf[PortBinder]) => case binding => throw AlreadyBoundException(binding.toString) } ) @@ -145,7 +145,7 @@ object Binding { case binding => // The following kludge is an attempt to provide backward compatibility // It should be done at at higher level. - if (!(compileOptions.autoIOWrap && elementOfIO(element))) + if ((compileOptions.requireIOWrap || !elementOfIO(element))) throw NotSynthesizableException else Binding.bind(element, PortBinder(element._parent.get), "Error: IO") -- cgit v1.2.3 From 1973e4d7333e2c57be4bcb7204210ecafdacab93 Mon Sep 17 00:00:00 2001 From: Jim Lawson Date: Mon, 29 Aug 2016 17:04:51 -0700 Subject: Check module-specific compile options. Import chisel3.NotStrict.CompileOptions in Chisel package. Add CompileOptions tests. --- chiselFrontend/src/main/scala/chisel3/core/Binding.scala | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'chiselFrontend/src/main/scala/chisel3/core/Binding.scala') diff --git a/chiselFrontend/src/main/scala/chisel3/core/Binding.scala b/chiselFrontend/src/main/scala/chisel3/core/Binding.scala index a32d3ade..b36794f1 100644 --- a/chiselFrontend/src/main/scala/chisel3/core/Binding.scala +++ b/chiselFrontend/src/main/scala/chisel3/core/Binding.scala @@ -1,6 +1,6 @@ package chisel3.core -import chisel3.internal.Builder.compileOptions +import chisel3.internal.Builder.{compileOptions, forcedModule} /** * The purpose of a Binding is to indicate what type of hardware 'entity' a @@ -91,7 +91,7 @@ object Binding { element.binding = binder(unbound) } // If autoIOWrap is enabled and we're rebinding a PortBinding, just ignore the rebinding. - case portBound @ PortBinding(_, _) if (!compileOptions.requireIOWrap && binder.isInstanceOf[PortBinder]) => + case portBound @ PortBinding(_, _) if (!(compileOptions.requireIOWrap || forcedModule.compileOptions.requireIOWrap)&& binder.isInstanceOf[PortBinder]) => case binding => throw AlreadyBoundException(binding.toString) } ) @@ -145,7 +145,7 @@ object Binding { case binding => // The following kludge is an attempt to provide backward compatibility // It should be done at at higher level. - if ((compileOptions.requireIOWrap || !elementOfIO(element))) + if ((compileOptions.requireIOWrap || forcedModule.compileOptions.requireIOWrap || !elementOfIO(element))) throw NotSynthesizableException else Binding.bind(element, PortBinder(element._parent.get), "Error: IO") -- cgit v1.2.3 From 4b88a5dd45337fa88178fe17324eef3661daf1b3 Mon Sep 17 00:00:00 2001 From: Jim Lawson Date: Thu, 1 Sep 2016 10:21:57 -0700 Subject: Move connection implicits from Module constructor to connection methods. Eliminate builder compileOptions. --- chiselFrontend/src/main/scala/chisel3/core/Binding.scala | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'chiselFrontend/src/main/scala/chisel3/core/Binding.scala') diff --git a/chiselFrontend/src/main/scala/chisel3/core/Binding.scala b/chiselFrontend/src/main/scala/chisel3/core/Binding.scala index b36794f1..5378f3ae 100644 --- a/chiselFrontend/src/main/scala/chisel3/core/Binding.scala +++ b/chiselFrontend/src/main/scala/chisel3/core/Binding.scala @@ -1,6 +1,6 @@ package chisel3.core -import chisel3.internal.Builder.{compileOptions, forcedModule} +import chisel3.internal.Builder.{forcedModule} /** * The purpose of a Binding is to indicate what type of hardware 'entity' a @@ -91,7 +91,7 @@ object Binding { element.binding = binder(unbound) } // If autoIOWrap is enabled and we're rebinding a PortBinding, just ignore the rebinding. - case portBound @ PortBinding(_, _) if (!(compileOptions.requireIOWrap || forcedModule.compileOptions.requireIOWrap)&& binder.isInstanceOf[PortBinder]) => + case portBound @ PortBinding(_, _) if (!(forcedModule.compileOptions.requireIOWrap) && binder.isInstanceOf[PortBinder]) => case binding => throw AlreadyBoundException(binding.toString) } ) @@ -145,7 +145,7 @@ object Binding { case binding => // The following kludge is an attempt to provide backward compatibility // It should be done at at higher level. - if ((compileOptions.requireIOWrap || forcedModule.compileOptions.requireIOWrap || !elementOfIO(element))) + if ((forcedModule.compileOptions.requireIOWrap || !elementOfIO(element))) throw NotSynthesizableException else Binding.bind(element, PortBinder(element._parent.get), "Error: IO") -- cgit v1.2.3