Skip to content

Commit

Permalink
Add template parameters for 'extern' modules (#42)
Browse files Browse the repository at this point in the history
* Add template parameters for 'extern' modules

Resolves #25
---------

Co-authored-by: Tobias Tom Hodapp <[email protected]>
  • Loading branch information
VonTum and IBims1NicerTobi authored Dec 10, 2024
1 parent a806ce1 commit 79895dc
Show file tree
Hide file tree
Showing 11 changed files with 150 additions and 59 deletions.
1 change: 0 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ members = [
# chumsky = "0.9.2"
ariadne = "0.4.1" # for nice errors
num = "0.4"
# itertools = "0.13.0"
clap = { version = "4.5.21", features = ["derive", "wrap_help"] }
arrayvec = "0.7.6"

Expand Down
12 changes: 6 additions & 6 deletions src/codegen/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ pub trait CodeGenBackend {
fn file_extension(&self) -> &str;
fn output_dir_name(&self) -> &str;
fn comment(&self) -> &str;
fn codegen(&self, md: &Module, instance: &InstantiatedModule, use_latency: bool) -> String;
fn codegen(&self, md: &Module, instance: &InstantiatedModule, linker: &Linker, use_latency: bool) -> String;

fn make_output_file(&self, name: &str) -> File {
let mut path = PathBuf::with_capacity(name.len() + self.output_dir_name().len() + self.file_extension().len() + 2);
Expand All @@ -34,21 +34,21 @@ pub trait CodeGenBackend {
file
}

fn codegen_instance(&self, inst: &InstantiatedModule, md: &Module, out_file: &mut File) {
fn codegen_instance(&self, inst: &InstantiatedModule, md: &Module, linker: &Linker, out_file: &mut File) {
let inst_name = &inst.name;
if inst.errors.did_error {
println!("Instantiating error: {inst_name}");
return; // Continue
}
println!("Instantiating success: {inst_name}");
let code = self.codegen(md, &inst, true); // hardcode use_latency = true for now. Maybe forever, we'll see
let code = self.codegen(md, &inst, linker, true); // hardcode use_latency = true for now. Maybe forever, we'll see
write!(out_file, "{} {inst_name}\n{code}", self.comment()).unwrap();
}

fn codegen_to_file(&self, md: &Module) {
fn codegen_to_file(&self, md: &Module, linker: &Linker) {
let mut out_file = self.make_output_file(&md.link_info.name);
md.instantiations.for_each_instance(|_template_args, inst| {
self.codegen_instance(inst.as_ref(), md, &mut out_file)
self.codegen_instance(inst.as_ref(), md, linker, &mut out_file)
});
}

Expand Down Expand Up @@ -83,7 +83,7 @@ pub trait CodeGenBackend {
to_process_queue.push((new_inst, &linker.modules[sub_mod.module_uuid]));
}

self.codegen_instance(cur_instance, cur_md, &mut out_file);
self.codegen_instance(cur_instance, cur_md, linker, &mut out_file);

cur_idx += 1;
}
Expand Down
68 changes: 60 additions & 8 deletions src/codegen/system_verilog.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
use std::borrow::Cow;
use std::ops::Deref;

use crate::linker::IsExtern;
use crate::linker::{IsExtern, LinkInfo};
use crate::prelude::*;

use crate::flattening::{DeclarationPortInfo, Instruction, Module, Port};
use crate::instantiation::{
InstantiatedModule, RealWire, RealWireDataSource, RealWirePathElem, CALCULATE_LATENCY_LATER,
};
use crate::typing::template::{ConcreteTemplateArg, ConcreteTemplateArgs};
use crate::{typing::concrete_type::ConcreteType, value::Value};

use super::shared::*;
Expand All @@ -25,8 +26,14 @@ impl super::CodeGenBackend for VerilogCodegenBackend {
fn comment(&self) -> &str {
"//"
}
fn codegen(&self, md: &Module, instance: &InstantiatedModule, use_latency: bool) -> String {
gen_verilog_code(md, instance, use_latency)
fn codegen(
&self,
md: &Module,
instance: &InstantiatedModule,
linker: &Linker,
use_latency: bool,
) -> String {
gen_verilog_code(md, instance, linker, use_latency)
}
}

Expand Down Expand Up @@ -59,6 +66,7 @@ fn typ_to_declaration(mut typ: &ConcreteType, var_name: &str) -> String {
struct CodeGenerationContext<'g, 'out, Stream: std::fmt::Write> {
md: &'g Module,
instance: &'g InstantiatedModule,
linker: &'g Linker,
program_text: &'out mut Stream,

use_latency: bool,
Expand Down Expand Up @@ -274,13 +282,18 @@ impl<'g, 'out, Stream: std::fmt::Write> CodeGenerationContext<'g, 'out, Stream>

fn write_submodules(&mut self) {
for (_id, sm) in &self.instance.submodules {
let sm_md = &self.linker.modules[sm.module_uuid];
let sm_inst: &InstantiatedModule = sm
.instance
.get()
.expect("Invalid submodules are impossible to remain by the time codegen happens");
let sm_instance_name = mangle(&sm_inst.name);
if sm_md.link_info.is_extern == IsExtern::Extern {
self.write_template_args(&sm_md.link_info, &sm.template_args);
} else {
self.program_text.write_str(&sm_inst.name).unwrap();
};
let sm_name = &sm.name;
writeln!(self.program_text, "{sm_instance_name} {sm_name}(").unwrap();
writeln!(self.program_text, " {sm_name}(").unwrap();
write!(self.program_text, "\t.clk(clk)").unwrap();
for (port_id, iport) in sm_inst.interface_ports.iter_valids() {
let port_name =
Expand All @@ -300,6 +313,39 @@ impl<'g, 'out, Stream: std::fmt::Write> CodeGenerationContext<'g, 'out, Stream>
}
}

fn write_template_args(
&mut self,
link_info: &LinkInfo,
concrete_template_args: &ConcreteTemplateArgs,
) {
self.program_text.write_str(&link_info.name).unwrap();
self.program_text.write_str(" #(").unwrap();
let mut first = true;
concrete_template_args.iter().for_each(|(arg_id, arg)| {
let arg_name = &link_info.template_arguments[arg_id].name;
let arg_value = match arg {
ConcreteTemplateArg::Type(..) => {
unreachable!("No extern module type arguments. Should have been caught by Lint")
}
ConcreteTemplateArg::Value(typed_value, _) => {
typed_value.value.inline_constant_to_string()
}
ConcreteTemplateArg::NotProvided => unreachable!("All args are known at codegen"),
};
if first {
self.program_text.write_char(',').unwrap();
} else {
first = false;
}
self.program_text.write_char('.').unwrap();
self.program_text.write_str(&arg_name).unwrap();
self.program_text.write_char('(').unwrap();
self.program_text.write_str(&arg_value).unwrap();
self.program_text.write_char(')').unwrap();
});
self.program_text.write_char(')').unwrap();
}

fn write_multiplexers(&mut self) {
for (_id, w) in &self.instance.wires {
match &w.source {
Expand All @@ -309,7 +355,7 @@ impl<'g, 'out, Stream: std::fmt::Write> CodeGenerationContext<'g, 'out, Stream>
writeln!(self.program_text, "always_ff @(posedge clk) begin").unwrap();
"<="
} else {
writeln!(self.program_text, "always_comb begin\n\t// Combinatorial wires are not defined when not valid. This is just so that the synthesys tool doesn't generate latches").unwrap();
writeln!(self.program_text, "always_comb begin\n\t// Combinatorial wires are not defined when not valid. This is just so that the synthesis tool doesn't generate latches").unwrap();
let invalid_val = w.typ.get_initial_val();
let tabbed_name = format!("\t{output_name}");
self.write_constant(&tabbed_name, &invalid_val);
Expand Down Expand Up @@ -404,7 +450,7 @@ impl Value {
fn inline_constant_to_string(&self) -> Cow<str> {
match self {
Value::Bool(b) => Cow::Borrowed(if *b { "1'b1" } else { "1'b0" }),
Value::Integer(v) => Cow::Owned(format!("{v}")),
Value::Integer(v) => Cow::Owned(v.to_string()),
Value::Unset => Cow::Borrowed("'x"),
Value::Array(_) => unreachable!("Not an inline constant!"),
Value::Error => unreachable!("Error values should never have reached codegen!"),
Expand Down Expand Up @@ -439,12 +485,18 @@ impl RealWireDataSource {
}
}

fn gen_verilog_code(md: &Module, instance: &InstantiatedModule, use_latency: bool) -> String {
fn gen_verilog_code(
md: &Module,
instance: &InstantiatedModule,
linker: &Linker,
use_latency: bool,
) -> String {
let mut program_text = String::new();

let mut ctx = CodeGenerationContext {
md,
instance,
linker,
program_text: &mut program_text,
use_latency,
needed_untils: instance.compute_needed_untils(),
Expand Down
7 changes: 2 additions & 5 deletions src/codegen/vhdl.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@

use crate::{
flattening::{DeclarationPortInfo, Instruction},
linker::IsExtern,
typing::concrete_type::ConcreteType,
FlatAlloc, InstantiatedModule, Module, WireIDMarker,
flattening::{DeclarationPortInfo, Instruction}, linker::IsExtern, typing::concrete_type::ConcreteType, FlatAlloc, InstantiatedModule, Linker, Module, WireIDMarker
};
use std::ops::Deref;
use std::fmt::Write;
Expand All @@ -23,7 +20,7 @@ impl super::CodeGenBackend for VHDLCodegenBackend {
fn comment(&self) -> &str {
"--"
}
fn codegen(&self, md: &Module, instance: &InstantiatedModule, use_latency: bool) -> String {
fn codegen(&self, md: &Module, instance: &InstantiatedModule, linker: &Linker, use_latency: bool) -> String {
gen_vhdl_code(md, instance, use_latency)
}
}
Expand Down
8 changes: 6 additions & 2 deletions src/compiler_top.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,12 @@ impl Linker {
}

pub fn add_all_files_in_directory<ExtraInfoManager : LinkerExtraFileInfoManager>(&mut self, directory : &PathBuf, info_mngr : &mut ExtraInfoManager) {
for file in std::fs::read_dir(directory).unwrap() {
let file_path = file.unwrap().path().canonicalize().unwrap();
let mut files = std::fs::read_dir(directory).unwrap()
.map(|res| res.map(|e| e.path()))
.collect::<Result<Vec<_>, std::io::Error>>().unwrap();
files.sort();
for file in files {
let file_path = file.canonicalize().unwrap();
if file_path.is_file() && file_path.extension() == Some(OsStr::new("sus")) {
let file_text = std::fs::read_to_string(&file_path).unwrap();
let file_identifier : String = info_mngr.convert_filename(&file_path);
Expand Down
21 changes: 20 additions & 1 deletion src/flattening/lints.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use crate::linker::AFTER_LINTS_CP;
use crate::linker::{IsExtern, LinkInfo, AFTER_LINTS_CP};
use crate::prelude::*;
use crate::typing::template::TemplateInputKind;

use super::walk::for_each_generative_input_in_template_args;

Expand All @@ -11,15 +12,33 @@ pub fn perform_lints(linker: &mut Linker) {
let errors = ErrorCollector::from_storage(md.link_info.errors.take(), md.link_info.file, &linker.files);
let resolved_globals = md.link_info.resolved_globals.take();
find_unused_variables(md, &errors);
extern_objects_may_not_have_type_template_args(&md.link_info, &errors);
md.link_info.reabsorb_errors_globals((errors, resolved_globals), AFTER_LINTS_CP);
}
}

/*
==== Additional Errors ====
*/
fn extern_objects_may_not_have_type_template_args(link_info: &LinkInfo, errors: &ErrorCollector) {
if link_info.is_extern == IsExtern::Extern {
for (_id, arg) in &link_info.template_arguments {
if let TemplateInputKind::Type(..) = &arg.kind {
errors.error(arg.name_span, "'extern' modules may not have 'type' arguments. Convert to bool[] first");
}
}
}
}

/*
==== Additional Warnings ====
*/
fn find_unused_variables(md: &Module, errors: &ErrorCollector) {
match md.link_info.is_extern {
IsExtern::Normal => {}
IsExtern::Extern | IsExtern::Builtin => {return} // Don't report unused variables for extern modules.
}

let instruction_fanins = make_fanins(&md.link_info.instructions);

let mut is_instance_used_map: FlatAlloc<bool, FlatIDMarker> =
Expand Down
2 changes: 1 addition & 1 deletion src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ fn main() -> Result<(), Box<dyn Error + Sync + Send>> {

if config.codegen {
for (_id, md) in &linker.modules {
codegen_backend.codegen_to_file(md);
codegen_backend.codegen_to_file(md, &linker);
}
}

Expand Down
25 changes: 25 additions & 0 deletions test.sus
Original file line number Diff line number Diff line change
Expand Up @@ -985,4 +985,29 @@ module numbersToAddUp {
}


/// Test parametrized extern modules
// Expects a SV module of the form:
/*
```sv
module sized_int_add #(
parameter int LEFT_SIZE,
parameter int RIGHT_SIZE,
parameter int OUTPUT_SIZE
) (
input clk,
input[LEFT_SIZE-1:0] a,
input[RIGHT_SIZE-1:0] b,
// c is output 1 cycle after a and b are provided
output[OUTPUT_SIZE-1:0] c
);
```
*/
extern module sized_int_add #(int LEFT_SIZE, int RIGHT_SIZE, int OUTPUT_SIZE) {
interface sized_int_add : bool[LEFT_SIZE] a'0, bool[RIGHT_SIZE] b'0 -> bool[OUTPUT_SIZE] c'1
}

module use_sized_int_add {
interface use_sized_int_add : bool[4] a, bool[3] b -> bool[5] c

c = sized_int_add(a, b)
}
35 changes: 0 additions & 35 deletions test.sus_errors.txt
Original file line number Diff line number Diff line change
@@ -1,38 +1,3 @@
Warning: Unused Variable: This variable does not affect the output ports of this module
╭─[core.sus:3:43]
3 │ __builtin__ module LatencyOffset #(T, int OFFSET) {
│ ───┬──
│ ╰──── Unused Variable: This variable does not affect the output ports of this module
───╯
Warning: Unused Variable: This variable does not affect the output ports of this module
╭─[core.sus:4:33]
4 │ interface LatencyOffset : T in'0 -> T out'OFFSET
│ ─┬
│ ╰── Unused Variable: This variable does not affect the output ports of this module
───╯
Warning: Unused Variable: This variable does not affect the output ports of this module
╭─[core.sus:9:29]
9 │ interface in_domain : T in'0
│ ─┬
│ ╰── Unused Variable: This variable does not affect the output ports of this module
───╯
Warning: Unused Variable: This variable does not affect the output ports of this module
╭─[core.sus:15:31]
15 │ interface IntToBits : int value'0 -> bool[32] bits'0
│ ──┬──
│ ╰──── Unused Variable: This variable does not affect the output ports of this module
────╯
Warning: Unused Variable: This variable does not affect the output ports of this module
╭─[core.sus:19:36]
19 │ interface IntToBits : bool[32] bits'0 -> int value'0
│ ──┬─
│ ╰─── Unused Variable: This variable does not affect the output ports of this module
────╯
Warning: Unused port 'ready'
╭─[util.sus:87:2]
Expand Down
10 changes: 10 additions & 0 deletions test.sus_output.txt
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,8 @@ TREE SITTER module! dont_care
TREE SITTER module! m
TREE SITTER module! xyz
TREE SITTER module! numbersToAddUp
TREE SITTER module! sized_int_add
TREE SITTER module! use_sized_int_add
Typechecking LatencyOffset
Typechecking CrossDomain
Typechecking IntToBits
Expand Down Expand Up @@ -213,6 +215,8 @@ Typechecking useModuleWithBadInterface
Typechecking m
Typechecking xyz
Typechecking numbersToAddUp
Typechecking sized_int_add
Typechecking use_sized_int_add
Instantiating IntToBits
Concrete Typechecking IntToBits
Latency Counting IntToBits
Expand Down Expand Up @@ -531,3 +535,9 @@ Latency Counting SplitAt
Latency Counting TreeAdd
Latency Counting TreeAdd
Latency Counting numbersToAddUp
Instantiating use_sized_int_add
Concrete Typechecking use_sized_int_add
Instantiating sized_int_add
Concrete Typechecking sized_int_add
Latency Counting sized_int_add
Latency Counting use_sized_int_add
20 changes: 20 additions & 0 deletions test_native.sv
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// Extern module for test.sus:sized_int_add

module sized_int_add #(
parameter int LEFT_SIZE,
parameter int RIGHT_SIZE,
parameter int OUTPUT_SIZE
) (
input clk,
input[LEFT_SIZE-1:0] a,
input[RIGHT_SIZE-1:0] b,
// c is output 1 cycle after a and b are provided
output[OUTPUT_SIZE-1:0] c
);

reg[OUTPUT_SIZE-1:0] c_reg;

always_ff @(posedge clk) c_reg = a + b;
assign c = c_reg;

endmodule

0 comments on commit 79895dc

Please sign in to comment.