///|
fn width_of_range(range : @syntax.Range?) -> Width {
match range {
Some(value) =>
match value.width() {
Some(width) => Known(width)
None => Unknown
}
None => Known(1)
}
}
///|
fn expression_name(expression : @syntax.Expression) -> String? {
match expression {
@syntax.Name(name, _) => Some(name)
@syntax.Index(base, _, _) => expression_name(base)
_ => None
}
}
///|
/// Infer widths for the small expression subset understood by the analyzer.
fn expression_width(
modules : Array[ModuleInfo],
module_name : String,
expression : @syntax.Expression,
) -> Width {
match expression {
@syntax.Name(name, _) =>
match modules.search_by(fn(mod) { mod.name == module_name }) {
Some(index) =>
match modules[index].signal(name) {
Some(signal) => signal.width
None => Unknown
}
None => Unknown
}
@syntax.Number(value, _) =>
if value == "0" || value == "1" {
Known(1)
} else {
Unknown
}
@syntax.Index(base, _, _) => expression_width(modules, module_name, base)
@syntax.Unary(_, operand, _) =>
expression_width(modules, module_name, operand)
@syntax.Binary(left, _, right, _) => {
let left_width = expression_width(modules, module_name, left)
let right_width = expression_width(modules, module_name, right)
match (left_width, right_width) {
(Known(left), Known(right)) =>
Known(if left >= right { left } else { right })
_ => Unknown
}
}
@syntax.StringLiteral(_, _) | @syntax.Unknown(_) => Unknown
}
}
///|
fn width_mismatch(
modules : Array[ModuleInfo],
module_name : String,
target : @syntax.Expression,
value : @syntax.Expression,
) -> Bool {
match
(
expression_width(modules, module_name, target),
expression_width(modules, module_name, value),
) {
(Known(target_width), Known(value_width)) => target_width != value_width
_ => false
}
}
///|
fn signal_index(signals : Array[SignalInfo], name : String) -> Int? {
for i, signal in signals {
if signal.name == name {
return Some(i)
}
}
None
}
///|
fn add_driver(
modules : Array[ModuleInfo],
module_name : String,
expression : @syntax.Expression,
kind : DriveKind,
) -> Unit {
match expression_name(expression) {
Some(name) =>
for mod in modules {
if mod.name == module_name {
match signal_index(mod.signals, name) {
Some(index) => {
let span = expression.span()
mod.signals[index].drivers.push({ kind, span })
}
None => ()
}
}
}
None => ()
}
}
///|
/// Add one procedural driver per signal for a single always block.
fn add_procedural_driver_once(
modules : Array[ModuleInfo],
module_name : String,
expression : @syntax.Expression,
seen : Array[String],
) -> Unit {
match expression_name(expression) {
Some(name) =>
if !seen.contains(name) {
seen.push(name)
add_driver(modules, module_name, expression, Procedural)
}
None => ()
}
}
///|
fn collect_statement_drivers(
modules : Array[ModuleInfo],
module_name : String,
statement : @syntax.Statement,
seen : Array[String],
) -> Unit {
match statement {
@syntax.Assignment(target, _, _, _) =>
add_procedural_driver_once(modules, module_name, target, seen)
@syntax.Block(statements, _) =>
for statement in statements {
collect_statement_drivers(modules, module_name, statement, seen)
}
@syntax.If(_, then_branch, else_branch, _) => {
collect_statement_drivers(modules, module_name, then_branch, seen)
match else_branch {
Some(branch) =>
collect_statement_drivers(modules, module_name, branch, seen)
None => ()
}
}
@syntax.Case(_, arms, _) =>
for arm in arms {
collect_statement_drivers(modules, module_name, arm.body, seen)
}
@syntax.Empty(_) | @syntax.Unknown(_) => ()
}
}
///|
fn make_module_info(mod : @syntax.Module) -> ModuleInfo {
let signals : Array[SignalInfo] = []
for port in mod.ports {
signals.push({
name: port.name,
net_type: @syntax.Unspecified,
width: width_of_range(port.width),
declaration_span: port.span,
from_port: true,
drivers: [],
})
}
for signal in mod.signals {
signals.push({
name: signal.name,
net_type: signal.net_type,
width: width_of_range(signal.width),
declaration_span: signal.span,
from_port: false,
drivers: [],
})
}
let instances : Array[InstanceInfo] = []
for item in mod.items {
match item {
@syntax.Instance(instance) =>
instances.push({
module_name: instance.module_name,
instance_name: instance.instance_name,
span: instance.span,
})
_ => ()
}
}
{ name: mod.name, ports: mod.ports, signals, instances, span: mod.span }
}
///|
pub fn index(design : @syntax.Design) -> SemanticIndex {
let modules : Array[ModuleInfo] = []
let issues : Array[SemanticIssue] = []
for mod in design.modules {
if modules.any(fn(existing) { existing.name == mod.name }) {
issues.push({
code: "RTL101",
message: "duplicate module declaration",
span: mod.span,
})
}
modules.push(make_module_info(mod))
}
let edges : Array[(String, String)] = []
for mod in design.modules {
for item in mod.items {
match item {
@syntax.Assign(assign) => {
add_driver(modules, mod.name, assign.target, Continuous)
if width_mismatch(modules, mod.name, assign.target, assign.value) {
issues.push({
code: "RTL204",
message: "assignment widths do not match",
span: assign.span,
})
}
}
@syntax.Always(always) =>
collect_statement_drivers(modules, mod.name, always.body, [])
@syntax.Instance(instance) => {
edges.push((mod.name, instance.module_name))
if !modules.any(fn(candidate) {
candidate.name == instance.module_name
}) {
issues.push({
code: "RTL102",
message: "unresolved module instance",
span: instance.span,
})
}
for connection in instance.connections {
add_driver(modules, mod.name, connection.expression, InstanceOutput)
}
}
@syntax.Statement(statement) =>
collect_statement_drivers(modules, mod.name, statement, [])
@syntax.Unknown(_) => ()
}
}
}
{
modules,
graph: { edge_list: edges },
issues,
syntax_modules: design.modules,
}
}