This repository has been archived by the owner on Jun 16, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
verilog: add a beautiful regex-based "parser" for Verilog module defi…
…nitions.
- Loading branch information
Showing
4 changed files
with
157 additions
and
15 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
66 changes: 66 additions & 0 deletions
66
src/main/scala/ee/hrzn/chryse/verilog/InterfaceExtractor.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
package ee.hrzn.chryse.verilog | ||
|
||
import scala.collection.mutable | ||
import scala.util.matching.Regex | ||
|
||
object InterfaceExtractor { | ||
private val reWhole: Regex = | ||
(raw"(?m)^module (\w+)\(" + | ||
raw"((?:\s*,?(?:\s*(?:input|output|inout)\s)?" + | ||
raw"(?:\s*\[\d+:\d+\]\s)?" + | ||
raw"\s*(?:\w+))*)" + | ||
raw"\s*,?\s*\);").r | ||
|
||
private val reIndividual: Regex = | ||
(raw"\A\s*(?:(input|output|inout)\s)?" + | ||
raw"(?:\s*\[\d+:\d+\]\s)?" + | ||
raw"\s*(\w+)").r | ||
|
||
case class Module( | ||
inputs: List[String] = List.empty, | ||
outputs: List[String] = List.empty, | ||
inouts: List[String] = List.empty, | ||
) | ||
|
||
sealed private trait Mode | ||
final private case object ModeNone extends Mode | ||
final private case object ModeInput extends Mode | ||
final private case object ModeOutput extends Mode | ||
final private case object ModeInout extends Mode | ||
|
||
def apply(sv: String): Map[String, Module] = { | ||
var map = mutable.Map[String, Module]() | ||
for { | ||
List(moduleName, contents) <- reWhole.findAllMatchIn(sv).map(_.subgroups) | ||
} { | ||
var mode: Mode = ModeNone | ||
var inputs = List[String]() | ||
var outputs = List[String]() | ||
var inouts = List[String]() | ||
for { el <- contents.split(",") if el.strip().length() != 0 } { | ||
val List(kind, name) = reIndividual.findAllMatchIn(el).next().subgroups | ||
kind match { | ||
case "input" => mode = ModeInput | ||
case "output" => mode = ModeOutput | ||
case "inout" => mode = ModeInout | ||
case null => () | ||
case _ => throw new Exception(s"eh? $kind") | ||
} | ||
mode match { | ||
case ModeInput => inputs :+= name | ||
case ModeOutput => outputs :+= name | ||
case ModeInout => inouts :+= name | ||
case ModeNone => | ||
throw new Exception(s"no mode for {$name}") | ||
} | ||
} | ||
|
||
map += moduleName -> Module( | ||
inputs = inputs, | ||
outputs = outputs, | ||
inouts = inouts, | ||
) | ||
} | ||
map.to(Map) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
79 changes: 79 additions & 0 deletions
79
src/test/scala/ee/hrzn/chryse/verilog/InterfaceExtractorSpec.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
package ee.hrzn.chryse.verilog | ||
|
||
import org.scalatest.flatspec.AnyFlatSpec | ||
import org.scalatest.matchers.should._ | ||
|
||
class InterfaceExtractorSpec extends AnyFlatSpec with Matchers { | ||
behavior.of("InterfaceExtractor") | ||
|
||
it should "extract inputs" in { | ||
InterfaceExtractor("""module chrysetop( | ||
| input clock, | ||
| reset | ||
|); | ||
| | ||
| Top top ( | ||
| .clock (clock), | ||
| .reset (reset) | ||
| ); | ||
|endmodule | ||
|""".stripMargin) should be( | ||
Map( | ||
"chrysetop" -> InterfaceExtractor.Module( | ||
inputs = List("clock", "reset"), | ||
), | ||
), | ||
) | ||
} | ||
|
||
it should "extract inputs, outputs and inouts from multiple modules" in { | ||
InterfaceExtractor( | ||
"""module SevSeg( | ||
| input [2:0] io_char, | ||
| output io_abcdefg_0, | ||
| io_abcdefg_1, | ||
| io_abcdefg_2, | ||
| io_abcdefg_3, | ||
| io_abcdefg_4, | ||
| io_abcdefg_5, | ||
| io_abcdefg_6 | ||
|); | ||
| | ||
| wire _GEN = io_char == 3'h0; | ||
| wire _GEN_0 = io_char == 3'h1; | ||
| wire _GEN_1 = io_char == 3'h2; | ||
| wire _GEN_2 = io_char != 3'h3; | ||
| wire _GEN_3 = _GEN_0 | _GEN_1; | ||
| wire io_abcdefg_5_0 = ~_GEN & (_GEN_3 | _GEN_2); | ||
| assign io_abcdefg_0 = io_abcdefg_5_0; | ||
| assign io_abcdefg_1 = io_abcdefg_5_0; | ||
| assign io_abcdefg_2 = _GEN | ~_GEN_3 & _GEN_2; | ||
| assign io_abcdefg_3 = _GEN | ~_GEN_0 & (_GEN_1 | _GEN_2); | ||
| assign io_abcdefg_4 = ~(_GEN | _GEN_0) & ~_GEN_1; | ||
| assign io_abcdefg_5 = io_abcdefg_5_0; | ||
| assign io_abcdefg_6 = ~(_GEN | _GEN_3) & _GEN_2; | ||
|endmodule | ||
| | ||
|module Top( | ||
| input clock, | ||
| reset, | ||
| output xyz, | ||
| inout abc, | ||
| input def | ||
|); | ||
|""".stripMargin, | ||
) should be( | ||
Map( | ||
"SevSeg" -> InterfaceExtractor.Module( | ||
inputs = List("io_char"), | ||
outputs = (for { i <- 0 until 7 } yield s"io_abcdefg_$i").toList, | ||
), | ||
"Top" -> InterfaceExtractor.Module( | ||
inputs = List("clock", "reset", "def"), | ||
outputs = List("xyz"), | ||
inouts = List("abc"), | ||
), | ||
), | ||
) | ||
} | ||
} |