Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion compiler/ir/src/main/scala/dfhdl/compiler/ir/DB.scala
Original file line number Diff line number Diff line change
Expand Up @@ -308,7 +308,7 @@ final case class DB(
}
// 2. Build port copies (reusing origToDupMap from step 1)
val pbnsTypes = pbnsByDesign.getOrElse(dupDesign, Map.empty)
portEntries += dupDesign -> ListMap.from(origPortMap(origDesign).view.map { (name, dcl) =>
portEntries += dupDesign -> ListMap.from(origPortMap.getOrElse(origDesign, ListMap.empty).view.map { (name, dcl) =>
val dfType = pbnsTypes.getOrElse(name, dcl.dfType)
val dupOwnerDomain = origToDupMap(dcl.getOwnerDomain)
name -> dcl.copy(ownerRef = DFRef.DuplicationRef(dupOwnerDomain), dfType = dfType)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ case object BackendPrepStage
ConnectUnused,
VHDLProcToVerilog,
ExplicitNamedVars,
ExplicitCondExprAssign,
DropLocalDcls,
DropOutportRead,
GlobalizePortVectorParams,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,44 @@ import dfhdl.compiler.ir.*
import dfhdl.compiler.patching.*
import dfhdl.internals.*
import dfhdl.options.CompilerOptions
import dfhdl.core.DomainType.ED

/* This stage transforms an assignment from a conditional expression to a statement.*/
//format: off
/** This stage transforms an assignment from a conditional expression to a statement.
*
* ==Rule 1: Conditional expression to statement==
*
* Converts assignments from conditional expressions into conditional statements.
* {{{
* // Before
* val z = SInt(16) <> VAR
* z := (if (x > 0) 5 else x)
*
* // After
* val z = SInt(16) <> VAR
* if (x > sd"16'0") z := sd"16'5"
* else z := x
* }}}
*
* ==Rule 2: ED domain process wrapping==
*
* When the conditional statement is in an ED domain and outside a process,
* it is wrapped in a `process(all)` block.
* {{{
* // Before (EDDesign, outside process)
* val y = SInt(16) <> OUT
* y <>(if (x > sd"16'0") sd"16'5" else x)
*
* // After
* val y = SInt(16) <> OUT
* process(all):
* if (x > sd"16'0") y := sd"16'5"
* else y := x
* }}}
*/
//format: on
case object ExplicitCondExprAssign extends Stage:
def dependencies: List[Stage] = List()
def dependencies: List[Stage] = List(ExplicitNamedVars)
def nullifies: Set[Stage] = Set(DropUnreferencedAnons)

def transform(designDB: DB)(using MemberGetSet, CompilerOptions): DB =
Expand Down Expand Up @@ -53,6 +87,7 @@ case object ExplicitCondExprAssign extends Stage:
val removeNetPatch = net -> Patch.Remove()
removeNetPatch :: ch.patchChains(headerVar, op)
end extension
// Phase 1: transform conditional expressions to statements
val patchList = designDB.members.view
// collect all the assignments from anonymous conditionals
.flatMap {
Expand All @@ -63,7 +98,32 @@ case object ExplicitCondExprAssign extends Stage:
header.patchChainsNet(toVal, net, DFNet.Op.Assignment)
case _ => Nil
}.toList
designDB.patch(patchList)
val phase1DB = designDB.patch(patchList)
// Phase 2: wrap ED-domain non-process conditional statements in process(all)
locally {
given MemberGetSet = phase1DB.getSet
val wrapPatches: List[(DFMember, Patch)] = phase1DB.members.view.flatMap {
case ch: DFConditional.Header
if ch.dfType == DFUnit && ch.isInEDDomain && !ch.isInProcess
&& !ch.getOwnerBlock.isInstanceOf[DFConditional.Block] =>
val chain = phase1DB.conditionalChainTable(ch)
val chainBlocksAndMembers: List[DFMember] =
chain.flatMap(cb => cb :: cb.members(MemberView.Flattened))
val allToWrap: List[DFMember] = ch :: chainBlocksAndMembers
val owner = ch.getOwnerBlock
val wrapDsn = new MetaDesign(
ch,
Patch.Add.Config.Before,
domainType = ED
):
process(all):
plantMembers(owner, allToWrap)
val removals = allToWrap.map(_ -> Patch.Remove(isMoved = true))
wrapDsn.patch :: removals
case _ => None
}.toList
phase1DB.patch(wrapPatches)
}
end transform
end ExplicitCondExprAssign

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -177,16 +177,25 @@ case object NamedAnonMultiref extends NamedAliases, NoCheckStage:
else Nil

//Names anonymous conditional expressions, as long as they are not referenced by an ident which indicates that
//they are themselves inside another conditional expression
//they are themselves inside another conditional expression, and as long as they are not directly assigned to
//a declaration or connected to an output port
case object NamedAnonCondExpr extends NamedAliases:
override def dependencies: List[Stage] = List(ExplicitCondExprAssign)
override def dependencies: List[Stage] = List()
def criteria(dfVal: DFVal)(using MemberGetSet, CompilerOptions): List[DFVal] = dfVal match
case dfVal: DFConditional.Header if dfVal.isAnonymous && dfVal.dfType != DFUnit =>
val isReferencedByIdent =
dfVal.getReadDeps.collectFirst { case Ident(_) => true }.getOrElse(false)
if (isReferencedByIdent) Nil
else List(dfVal)
val nameIt =
dfVal.getReadDeps.collectFirst {
// directly assigned to a declaration (variable or output port)
case DFNet.Assignment(toVal = _: DFVal.Dcl) => false
// directly connected to an output port
case DFNet.Connection(toVal = DclOut()) => false
// is referenced by an ident, which means it is used in another conditional expression
case Ident(_) => false
}.getOrElse(true)
if (nameIt) List(dfVal)
else Nil
case dfVal => Nil
end NamedAnonCondExpr

extension [T: HasDB](t: T)
def namedAnonMultiref(using CompilerOptions): DB =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ import scala.annotation.tailrec
import scala.collection.mutable
case object ToED extends Stage:
def dependencies: List[Stage] =
List(DropUnreferencedAnons, ToRT, DropRTProcess, NameRegAliases, ExplicitNamedVars, AddClkRst,
SimpleOrderMembers)
List(DropUnreferencedAnons, ToRT, DropRTProcess, NameRegAliases, ExplicitNamedVars,
ExplicitCondExprAssign, AddClkRst, SimpleOrderMembers)
def nullifies: Set[Stage] = Set(DropUnreferencedAnons)
def transform(designDB: DB)(using getSet: MemberGetSet, co: CompilerOptions): DB =
given RefGen = RefGen.fromGetSet
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,8 @@ class ExplicitCondExprAssignSpec extends StageSpec(stageCreatesUnrefAnons = true
val z2 = SInt(16) <> VAR
z2 := z match
case 1 | 2 =>
val zz: SInt[4] <> VAL = z match
val zz = SInt(4) <> VAR
zz := z match
case 1 => 5
case 2 => 3
if (x < 11) zz + 3
Expand All @@ -79,11 +80,11 @@ class ExplicitCondExprAssignSpec extends StageSpec(stageCreatesUnrefAnons = true
| val z2 = SInt(16) <> VAR
| z match
| case sd"16'1" | sd"16'2" =>
| val zz: SInt[4] <> VAL =
| z match
| case sd"16'1" => sd"4'5"
| case sd"16'2" => sd"4'3"
| end match
| val zz = SInt(4) <> VAR
| z match
| case sd"16'1" => zz := sd"4'5"
| case sd"16'2" => zz := sd"4'3"
| end match
| if (x < sd"16'11") z2 := (zz +^ sd"4'3").resize(16)
| else z2 := zz.resize(16)
| case _ => z2 := z + sd"16'12"
Expand All @@ -96,19 +97,22 @@ class ExplicitCondExprAssignSpec extends StageSpec(stageCreatesUnrefAnons = true
test("AES xtime example") {
class xtime extends DFDesign:
val lhs = Bits(8) <> IN
val shifted = lhs << 1
val o = Bits(8) <> OUT
o <> ((
if (lhs(7)) shifted ^ h"1b"
else shifted
): Bits[8] <> VAL)
val shifted = Bits(8) <> VAR
shifted := lhs << 1
val o = Bits(8) <> OUT
o <>
((
if (lhs(7)) shifted ^ h"1b"
else shifted
): Bits[8] <> VAL)
end xtime
val id = (new xtime).explicitCondExprAssign
assertCodeString(
id,
"""|class xtime extends DFDesign:
| val lhs = Bits(8) <> IN
| val shifted = lhs << 1
| val shifted = Bits(8) <> VAR
| shifted := lhs << 1
| val o = Bits(8) <> OUT
| if (lhs(7)) o := shifted ^ h"1b"
| else o := shifted
Expand Down Expand Up @@ -150,4 +154,68 @@ class ExplicitCondExprAssignSpec extends StageSpec(stageCreatesUnrefAnons = true
|end LRShiftFlat""".stripMargin
)
}
test("ED domain conditional expression connection outside process") {
class Top extends EDDesign:
val x = Bits(8) <> IN
val y = Bits(8) <> OUT
y <>
((
if (x(7)) x ^ h"1b"
else x
): Bits[8] <> VAL)
end Top
val result = (new Top).explicitCondExprAssign
assertCodeString(
result,
"""|class Top extends EDDesign:
| val x = Bits(8) <> IN
| val y = Bits(8) <> OUT
| process(all):
| if (x(7)) y := x ^ h"1b"
| else y := x
|end Top
|""".stripMargin
)
}

test("ED domain conditional expression connection to child input port") {
class Child extends EDDesign:
val x = Bits(8) <> IN
val y = Bits(8) <> OUT
y <> x
class Top extends EDDesign:
val a = Bits(8) <> IN
val b = Bits(8) <> OUT
val child_inst = Child()
child_inst.x <>
((
if (a(7)) a ^ h"1b"
else a
): Bits[8] <> VAL)
b <> child_inst.y
end Top
val result = (new Top).explicitCondExprAssign
assertCodeString(
result,
"""|class Child extends EDDesign:
| val x = Bits(8) <> IN
| val y = Bits(8) <> OUT
| y <> x
|end Child
|
|class Top extends EDDesign:
| val a = Bits(8) <> IN
| val b = Bits(8) <> OUT
| val child_inst = Child()
| val x_part = Bits(8) <> VAR
| process(all):
| if (a(7)) x_part := a ^ h"1b"
| else x_part := a
| child_inst.x <> x_part
| b <> child_inst.y
|end Top
|""".stripMargin
)
}

end ExplicitCondExprAssignSpec
Original file line number Diff line number Diff line change
Expand Up @@ -131,8 +131,10 @@ class ExplicitNamedVarsSpec extends StageSpec:
| val shifted = Bits(8) <> VAR
| shifted := lhs << 1
| val o = Bits(8) <> OUT
| if (lhs(7)) o := shifted ^ h"1b"
| else o := shifted
| o <> ((
| if (lhs(7)) shifted ^ h"1b"
| else shifted
| ): Bits[8] <> VAL)
|end xtime""".stripMargin
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1733,4 +1733,78 @@ class PrintCodeStringSpec extends StageSpec:
|end Foo""".stripMargin
)
}

test("empty deduplicated designs") {
class Mid() extends EDDesign
class Foo extends EDDesign:
val inst_a = Mid()
val inst_b = Mid()
val top = (new Foo).getCodeString
assertNoDiff(
top,
"""|class Mid extends EDDesign
|
|class Foo extends EDDesign:
| val inst_a = Mid()
| val inst_b = Mid()
|end Foo""".stripMargin
)
}

test("default design parameter value leak regression") {
class child(
val A: Int <> CONST = 8,
val B: Int <> CONST = 0,
val C: Int <> CONST = 5
) extends EDDesign:
val din = Bits(A) <> IN
val dout = Bits(A) <> OUT
dout <> din

class Foo(
val W: Int <> CONST = 8
) extends EDDesign:
val din = Bits(W) <> IN
val dout = Bits(W) <> OUT

// Instance 1: all params explicit, B = 1
val c1 = child(A = W, B = 1, C = 7)
c1.din <> din
c1.dout <> dout

// Instance 2: B omitted (uses default 0), C explicit
val c2 = child(A = W, C = 7)
c2.din <> din
val top = (new Foo).getCodeString
assertNoDiff(
top,
"""|class child(
| val A: Int <> CONST = 8,
| val B: Int <> CONST = 0,
| val C: Int <> CONST = 5
|) extends EDDesign:
| val din = Bits(A) <> IN
| val dout = Bits(A) <> OUT
| dout <> din
|end child
|
|class Foo(val W: Int <> CONST = 8) extends EDDesign:
| val din = Bits(W) <> IN
| val dout = Bits(W) <> OUT
| val c1 = child(
| A = W,
| B = 1,
| C = 7
| )
| c1.din <> din
| dout <> c1.dout
| val c2 = child(
| A = W,
| B = 0,
| C = 7
| )
| c2.din <> din
|end Foo""".stripMargin
)
}
end PrintCodeStringSpec
Loading
Loading