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 support for queue overflow hook methods #492

Merged
merged 11 commits into from
Aug 16, 2024
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
1,630 changes: 825 additions & 805 deletions compiler/lib/src/main/resources/META-INF/native-image/reflect-config.json

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions compiler/lib/src/main/scala/ast/Ast.scala
Original file line number Diff line number Diff line change
Expand Up @@ -299,6 +299,9 @@ object Ast {
case object Drop extends QueueFull {
override def toString = "drop"
}
case object Hook extends QueueFull {
override def toString = "hook"
}
}

/** Command specifier */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,8 @@ case class ComponentCommands (
List(
getHandlers,
getHandlerBases,
getPreMsgHooks
getPreMsgHooks,
getOverflowHooks,
).flatten
}

Expand Down Expand Up @@ -185,7 +186,7 @@ case class ComponentCommands (
)
)
),
writeSendMessageLogic("msg", queueFull, priority)
writeSendMessageLogic("msg", queueFull, priority, MessageType.Command, cmd.getName, cmdParamTypeMap(opcode).map((n, _, _) => n))
)
)
case _ => intersperseBlankLines(
Expand Down Expand Up @@ -318,4 +319,26 @@ case class ComponentCommands (
)
}

private def getOverflowHooks: List[CppDoc.Class.Member] = {
addAccessTagAndComment(
"PROTECTED",
"""|Overflow hooks for async commands marked 'hook'
|
|Each of these functions is invoked after an overflow event
|on a queue when the command is marked with 'hook' overflow
|behavior.
|""",
hookCmds.map((opcode, cmd) =>
functionClassMember(
Some(s"Overflow hook for command ${cmd.getName}"),
inputOverflowHookName(cmd.getName, MessageType.Command),
opcodeParam :: cmdSeqParam :: cmdParamMap(opcode),
CppDoc.Type("void"),
Nil,
CppDoc.Function.PureVirtual
)
)
)
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,13 @@ import fpp.compiler.analysis._
import fpp.compiler.ast._
import fpp.compiler.codegen._

/** Message type for message send logic */
sealed trait MessageType
object MessageType {
case object Command extends MessageType
case object Port extends MessageType
}

/** Utilities for writing C++ component definitions */
abstract class ComponentCppWriterUtils(
s: CppWriterState,
Expand Down Expand Up @@ -103,6 +110,21 @@ abstract class ComponentCppWriterUtils(

/** List of serial async input ports */
val serialAsyncInputPorts: List[PortInstance.General] = filterAsyncInputPorts(serialInputPorts)

/** List of typed overflow hook ports */
val typedHookPorts: List[PortInstance.General] = filterOverflowHookPorts(typedAsyncInputPorts)

/** List of serial overflow hook ports */
val serialHookPorts: List[PortInstance.General] = filterOverflowHookPorts(serialInputPorts)

/** List of serial overflow hook ports */
val dataProductHookPorts: List[PortInstance.Special] =
dataProductAsyncInputPorts.filter(p =>
p.queueFull match {
case Some(Ast.QueueFull.Hook) => true
case _ => false
}
)

/** List of typed output ports */
val typedOutputPorts: List[PortInstance.General] = filterTypedPorts(outputPorts)
Expand All @@ -116,6 +138,15 @@ abstract class ComponentCppWriterUtils(
case _ => None
}).filter(_.isDefined).map(_.get).sortBy(_.getUnqualifiedName)

/** List of internal overflow hook ports */
val internalHookPorts: List[PortInstance.Internal] =
internalPorts.filter(p =>
p.queueFull match {
case Ast.QueueFull.Hook => true
case _ => false
}
)

/** List of commands sorted by opcode */
val sortedCmds: List[(Command.Opcode, Command)] = component.commandMap.toList.sortBy(_._1)

Expand All @@ -131,6 +162,12 @@ abstract class ComponentCppWriterUtils(
case _ => false
})

/** List of async commands */
val hookCmds: List[(Command.Opcode, Command.NonParam)] = nonParamCmds.filter((_, cmd) => cmd.kind match {
case Command.NonParam.Async(_, Ast.QueueFull.Hook) => true
case _ => false
})

/** List of guarded commands */
val guardedCmds: List[(Command.Opcode, Command.NonParam)] = nonParamCmds.filter((_, cmd) => cmd.kind match {
case Command.NonParam.Guarded => true
Expand Down Expand Up @@ -445,6 +482,15 @@ abstract class ComponentCppWriterUtils(
)
case _ => portParamTypeMap(p.getUnqualifiedName).map((n, tn, t) => (n, tn, Some(t)))
}

/** Get port params as a list of names */
def getPortParamNames(p: PortInstance): List[String] =
p.getType match {
case Some(PortInstance.Type.Serial) => List(
"buffer"
LeStarch marked this conversation as resolved.
Show resolved Hide resolved
)
case _ => portParamTypeMap(p.getUnqualifiedName).map((n, _, _) => n)
}

/** Get port params as CppDoc Function Params */
def getPortFunctionParams(p: PortInstance): List[CppDoc.Function.Param] =
Expand Down Expand Up @@ -563,7 +609,10 @@ abstract class ComponentCppWriterUtils(
def writeSendMessageLogic(
bufferName: String,
queueFull: Ast.QueueFull,
priority: Option[BigInt]
priority: Option[BigInt],
messageType: MessageType,
name: String,
arguments: List[String]
): List[Line] = {
val queueBlocking = queueFull match {
case Ast.QueueFull.Block => "QUEUE_BLOCKING"
Expand Down Expand Up @@ -591,6 +640,13 @@ abstract class ComponentCppWriterUtils(
|}
|"""
)
case Ast.QueueFull.Hook => lines(
s"""|if (qStatus == Os::Queue::QUEUE_FULL) {
| this->${inputOverflowHookName(name, messageType)}(${arguments.mkString(",")});
| return;
|}
|"""
)
case _ => Nil
}
,
Expand Down Expand Up @@ -671,6 +727,13 @@ abstract class ComponentCppWriterUtils(
/** Get the name for an async input port pre-message hook function */
def inputPortHookName(name: String) =
s"${name}_preMsgHook"

/** Get the name for an async input port overflow hook function */
def inputOverflowHookName(name: String, messageType: MessageType) =
messageType match {
case MessageType.Port => s"${name}_overflowHook"
case MessageType.Command => s"${name}_cmdOverflowHook"
}

// Get the name for an output port connector function
def outputPortConnectorName(name: String) =
Expand Down Expand Up @@ -822,6 +885,14 @@ abstract class ComponentCppWriterUtils(
case _ => false
}
)

private def filterOverflowHookPorts(ports: List[PortInstance.General]) =
ports.filter(p =>
p.kind match {
case PortInstance.General.Kind.AsyncInput(_, Ast.QueueFull.Hook) => true
case _ => false
}
)

private def filterAsyncSpecialPorts(ports: List[PortInstance.Special]) =
ports.filter(p =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,8 @@ case class ComponentImplWriter(
private def getClassMembers: List[CppDoc.Class.Member] = {
List.concat(
getPublicMembers,
getHandlers
getHandlers,
getOverflowHooks
)
}

Expand Down Expand Up @@ -178,5 +179,48 @@ case class ComponentImplWriter(
})
)
}
private def getOverflowHooks: List[CppDoc.Class.Member] = {
List.concat(
getPortOverflowHooks(
List.concat(
typedHookPorts,
serialHookPorts,
dataProductHookPorts,
internalHookPorts
)
),
addAccessTagAndComment(
"PRIVATE",
s"Overflow hook implementations for 'hook' commands",
hookCmds.map((opcode, cmd) => {
functionClassMember(
Some(s"Overflow hook implementation for ${cmd.getName}"),
inputOverflowHookName(cmd.getName, MessageType.Command),
opcodeParam :: cmdSeqParam :: cmdParamMap(opcode),
CppDoc.Type("void"),
lines("// TODO"),
CppDoc.Function.Override
)
})
)
)
}

private def getPortOverflowHooks(ports: List[PortInstance]): List[CppDoc.Class.Member] = {
addAccessTagAndComment(
"PRIVATE",
s"Overflow hook implementations for 'hook' input ports",
ports.map(p => {
functionClassMember(
Some(s"Overflow hook implementation for ${p.getUnqualifiedName}"),
inputOverflowHookName(p.getUnqualifiedName, MessageType.Port),
portNumParam :: getPortFunctionParams(p),
CppDoc.Type("void"),
lines("// TODO"),
CppDoc.Function.Override
)
})
)
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ case class ComponentInputPorts(
)
})
),
writeSendMessageLogic(bufferName, queueFull, priority)
writeSendMessageLogic(bufferName, queueFull, priority, MessageType.Port, p.getUnqualifiedName, getPortParamNames(p))
)
)
}
Expand Down Expand Up @@ -408,6 +408,28 @@ case class ComponentInputPorts(
)
)
}

def getDropHooks(ports: List[PortInstance]): List[CppDoc.Class.Member] = {
addAccessTagAndComment(
"PROTECTED",
s"""|Hooks for ${getPortListTypeString(ports)} async input ports
|
|Each of these functions is invoked just before dropping a message
|on the corresponding port. You should override them to provide
|specific drop behavior.
|""",
ports.map(p =>
functionClassMember(
Some(s"Overflow hook for async input port ${p.getUnqualifiedName}"),
inputOverflowHookName(p.getUnqualifiedName, MessageType.Port),
portNumParam :: getPortFunctionParams(p),
CppDoc.Type("void"),
Nil,
CppDoc.Function.PureVirtual
)
)
)
}

// Get the name for a param command handler function
private def paramCmdHandlerName(cmd: Command.Param) =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,10 @@ case class ComponentInternalPort (
s: CppWriterState,
aNode: Ast.Annotated[AstNode[Ast.DefComponent]]
) extends ComponentCppWriterUtils(s, aNode) {

private val inputPortWriter = ComponentInputPorts(s, aNode)
def getFunctionMembers: List[CppDoc.Class.Member] = {
List(
inputPortWriter.getDropHooks(internalHookPorts),
getHandlers,
getHandlerBases
).flatten
Expand Down Expand Up @@ -82,7 +83,7 @@ case class ComponentInternalPort (
)
)
),
writeSendMessageLogic("msg", p.queueFull, p.priority)
writeSendMessageLogic("msg", p.queueFull, p.priority, MessageType.Port, p.getUnqualifiedName, getPortParams(p).map((n, _, _) => n))
)
)
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,9 @@ case class ComponentPorts(
inputPortWriter.getPreMsgHooks(dataProductInputPorts),
inputPortWriter.getPreMsgHooks(typedAsyncInputPorts),
inputPortWriter.getPreMsgHooks(serialAsyncInputPorts),
inputPortWriter.getDropHooks(dataProductHookPorts),
inputPortWriter.getDropHooks(typedHookPorts),
inputPortWriter.getDropHooks(serialHookPorts),
outputPortWriter.getInvokers(dataProductOutputPorts),
outputPortWriter.getInvokers(typedOutputPorts),
outputPortWriter.getInvokers(serialOutputPorts),
Expand Down
1 change: 1 addition & 0 deletions compiler/lib/src/main/scala/codegen/FppWriter.scala
Original file line number Diff line number Diff line change
Expand Up @@ -665,6 +665,7 @@ object FppWriter extends AstVisitor with LineUtils {
"guarded",
"health",
"high",
"hook",
"id",
"import",
"include",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,9 @@ object ComponentXmlWriter extends AstVisitor with LineUtils {
case _ => Nil
}
val queueFull = general.kind match {
// Hook queue full option becomes drop in XML
case Kind.AsyncInput(_, Ast.QueueFull.Hook) =>
List(("full", Ast.QueueFull.Drop.toString))
case Kind.AsyncInput(_, queueFull) =>
List(("full", queueFull.toString))
case _ => Nil
Expand Down
1 change: 1 addition & 0 deletions compiler/lib/src/main/scala/syntax/Lexer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,7 @@ object Lexer extends RegexParsers {
("guarded", (u: Unit) => Token.GUARDED()),
("health", (u: Unit) => Token.HEALTH()),
("high", (u: Unit) => Token.HIGH()),
("hook", (u: Unit) => Token.HOOK()),
("id", (u: Unit) => Token.ID()),
("import", (u: Unit) => Token.IMPORT()),
("include", (u: Unit) => Token.INCLUDE()),
Expand Down
3 changes: 3 additions & 0 deletions compiler/lib/src/main/scala/syntax/Parser.scala
Original file line number Diff line number Diff line change
Expand Up @@ -349,6 +349,7 @@ object Parser extends Parsers {
assert ^^ { case _ => Ast.QueueFull.Assert } |
block ^^ { case _ => Ast.QueueFull.Block } |
drop ^^ { case _ => Ast.QueueFull.Drop } |
hook ^^ { case _ => Ast.QueueFull.Hook } |
failure("queue full expected")
}

Expand Down Expand Up @@ -776,6 +777,8 @@ object Parser extends Parsers {

private def high = accept("high", { case t : Token.HIGH => t })

private def hook = accept("hook", { case t : Token.HOOK => t })

private def id = accept("id", { case t : Token.ID => t })

private def ident: Parser[Ast.Ident] =
Expand Down
1 change: 1 addition & 0 deletions compiler/lib/src/main/scala/syntax/Token.scala
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ object Token {
final case class GUARDED() extends Token
final case class HEALTH() extends Token
final case class HIGH() extends Token
final case class HOOK() extends Token
final case class I16() extends Token
final case class I32() extends Token
final case class I64() extends Token
Expand Down
10 changes: 10 additions & 0 deletions compiler/tools/fpp-to-cpp/test/component/active.fpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,16 @@ module M {

}

@ An active component with overflow behavior
active component ActiveOverflow {
include "include/special_ports.fppi"
include "include/overflow_commands.fppi"
include "include/overflow_typed_ports.fppi"
include "include/overflow_product_ports.fppi"
include "include/overflow_serial_ports.fppi"
include "include/overflow_internal_ports.fppi"
}

@ An active component with serial ports
active component ActiveSerial {

Expand Down
Loading
Loading