Skip to content
This repository has been archived by the owner on Jun 16, 2024. It is now read-only.

Commit

Permalink
verilog: add a beautiful regex-based "parser" for Verilog module defi…
Browse files Browse the repository at this point in the history
…nitions.
  • Loading branch information
kivikakk committed May 22, 2024
1 parent 22ca7e7 commit 9616d00
Show file tree
Hide file tree
Showing 4 changed files with 157 additions and 15 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ class ICE40Top[Top <: Module](
}
}

lastPCF = Some(PCF(ios.iterator.to(Map), freqs.iterator.to(Map)))
lastPCF = Some(PCF(ios.to(Map), freqs.to(Map)))
}

object ICE40Top {
Expand Down
66 changes: 66 additions & 0 deletions src/main/scala/ee/hrzn/chryse/verilog/InterfaceExtractor.scala
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)
}
}
25 changes: 11 additions & 14 deletions src/test/scala/ee/hrzn/chryse/platform/BoardResourcesSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import ee.hrzn.chryse.platform.ice40.ICE40Top
import ee.hrzn.chryse.platform.ice40.IceBreakerPlatform
import ee.hrzn.chryse.platform.ice40.PCF
import ee.hrzn.chryse.platform.resource.implicits._
import ee.hrzn.chryse.verilog
import org.scalatest.flatspec.AnyFlatSpec
import org.scalatest.matchers.should._

Expand Down Expand Up @@ -68,10 +69,11 @@ class BoardResourcesSpec extends AnyFlatSpec with Matchers {
rtl should include("ledg_int = view__ubtn_int")
rtl should include("uart_tx_int = ~view__ubtn_int")

// HACK: this is brittle. Parse the Verilog or something.
"\\s+".r
.replaceAllIn(rtl, " ") should include(
"module chrysetop( input clock, ubtn, output uart_tx, ledg );",
verilog.InterfaceExtractor(rtl) should contain(
"chrysetop" -> verilog.InterfaceExtractor.Module(
inputs = List("clock", "ubtn"),
outputs = List("uart_tx", "ledg"),
),
)
}

Expand Down Expand Up @@ -101,16 +103,11 @@ class BoardResourcesSpec extends AnyFlatSpec with Matchers {
rtl should include("pmod1b1_int = ~view__ubtn_int")
rtl should include("ledr_int = ~view__pmod1b2_int")

// HACK: this is _extra_ brittle.
"\\s+".r
.replaceAllIn(rtl, " ") should include(
"module chrysetop( " +
"input clock, ubtn, uart_rx, " +
"output uart_tx, ledr, pmod1a1, " +
"input pmod1a2, " +
"output pmod1b1, " +
"input pmod1b2 " +
");",
verilog.InterfaceExtractor(rtl) should contain(
"chrysetop" -> verilog.InterfaceExtractor.Module(
inputs = List("clock", "ubtn", "uart_rx", "pmod1a2", "pmod1b2"),
outputs = List("uart_tx", "ledr", "pmod1a1", "pmod1b1"),
),
)
}
}
Expand Down
79 changes: 79 additions & 0 deletions src/test/scala/ee/hrzn/chryse/verilog/InterfaceExtractorSpec.scala
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"),
),
),
)
}
}

0 comments on commit 9616d00

Please sign in to comment.