Skip to content

Commit

Permalink
Templates and generative parameters in Flattening
Browse files Browse the repository at this point in the history
  • Loading branch information
VonTum committed Jun 23, 2024
1 parent f7e61b0 commit f78e092
Show file tree
Hide file tree
Showing 10 changed files with 153 additions and 47 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ The one restriction SUS does impose over Verilog and VHDL is that it requires th

There are three main features that set SUS apart from the rest:
- Generative Variables and Types can be freely combined. Any "Dependent Types" headaches that are caused by this are sidestepped by doing the main type checking after instantiation.
- Easy Pipelining through an orthogonal language construct called "Latency Counting". This means that adding pipeline registers does not interfere with other language features such as generative or conditional code.
- Easy Pipelining through an orthogonal language construct called "Latency Counting". 'Orthogonal' means that adding pipeline registers does not interfere with other language features such as generative or conditional code.
- Separation of pipelines with interfaces. This keeps the user from accidentally crossing signals that have no logical relationship. At this level Clock Domain Crossings are implemented.

Finally, an important consideration of SUS is the user interface. SUS comes with a VSCode IDE plugin that allows the copiler to be used fully in-IDE. Compiling, typechecking and instantiation is done as the user writes code, leading to a very tight development feedback loop.
Expand All @@ -33,7 +33,7 @@ Finally, an important consideration of SUS is the user interface. SUS comes with
- Runtime Iteration Constructs
- Automatic Pipelining & Retiming

Of course, while the language does not support such protocols directly, they can be provided as libraries.
Of course, while the language does not support such protocols directly in the syntax, as this would put unneccesary extra constraints on the output hardware, modules for handling them are[^todo] provided in the standard library.

#### Example of some SUS code in the SUS VSCode Language Server.
![SUS LSP Example](philosophy/images/susLSPExample.png)
Expand Down
4 changes: 2 additions & 2 deletions src/codegen_fallback.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use std::ops::Deref;

use crate::{concrete_type::ConcreteType, flattening::{Instruction, Module}, instantiation::{InstantiatedModule, RealWire, RealWireDataSource, RealWirePathElem, WireID, CALCULATE_LATENCY_LATER}, linker::{get_builtin_type, TypeUUID}, value::Value};
use crate::{concrete_type::ConcreteType, flattening::{DeclarationPortInfo, Instruction, Module}, instantiation::{InstantiatedModule, RealWire, RealWireDataSource, RealWirePathElem, WireID, CALCULATE_LATENCY_LATER}, linker::{get_builtin_type, TypeUUID}, value::Value};

fn get_type_name_size(id : TypeUUID) -> u64 {
if id == get_builtin_type("int") {
Expand Down Expand Up @@ -136,7 +136,7 @@ impl<'g, 'out, Stream : std::fmt::Write> CodeGenerationContext<'g, 'out, Stream>

if let Instruction::Declaration(wire_decl) = &self.md.instructions[w.original_instruction] {
// Don't print named inputs and outputs, already did that in interface
if wire_decl.is_input_port.is_some() {
if let DeclarationPortInfo::RegularPort { is_input: _ } = wire_decl.is_port {
continue;
}
}
Expand Down
8 changes: 5 additions & 3 deletions src/dev_aid/lsp/hover_info.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use std::borrow::Cow;
use lsp_types::{LanguageString, MarkedString};

use crate::{
abstract_type::DomainType, flattening::{FlatID, IdentifierType, InterfaceToDomainMap, Module}, instantiation::{SubModuleOrWire, CALCULATE_LATENCY_LATER}, linker::{FileData, LinkInfo, Linker, NameElem}, parser::Documentation
abstract_type::DomainType, flattening::{DeclarationPortInfo, FlatID, IdentifierType, InterfaceToDomainMap, Module}, instantiation::{SubModuleOrWire, CALCULATE_LATENCY_LATER}, linker::{FileData, LinkInfo, Linker, NameElem}, parser::Documentation
};

use super::tree_walk::{InModule, LocationInfo};
Expand Down Expand Up @@ -77,8 +77,10 @@ pub fn hover(info: LocationInfo, linker: &Linker, file_data: &FileData) -> Vec<M
if let Some(ds) = &domain_str {
details_vec.push(ds);
}
if let Some(is_input) = decl.is_input_port {
details_vec.push(if is_input {"input"} else {"output"});
match decl.is_port {
DeclarationPortInfo::RegularPort { is_input } => details_vec.push(if is_input {"input"} else {"output"}),
DeclarationPortInfo::NotPort => {},
DeclarationPortInfo::GenerativeInput => details_vec.push("input") // "gen" in "input gen" is covered by decl.identifier_type
}

match decl.identifier_type {
Expand Down
2 changes: 1 addition & 1 deletion src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ impl<'linker> ErrorCollector<'linker> {
}

pub fn todo<S : Into<String>>(&self, position : Span, reason : S)-> ErrorReference<'_> {
self.push_diagnostic(position, format!("TODO: {}", reason.into()), ErrorLevel::Warning)
self.push_diagnostic(position, format!("TODO: {}", reason.into()), ErrorLevel::Error)
}

pub fn did_error(&self) -> bool {
Expand Down
17 changes: 10 additions & 7 deletions src/flattening/initialization.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ impl<'linker> ModuleInitializationContext<'linker> {
self.template_inputs.alloc(TemplateInput{
name,
name_span,
kind: TemplateInputKind::Type
kind: TemplateInputKind::Type{default_value : None} // UNSET, gets overwritten in Flattening
});
});
});
Expand Down Expand Up @@ -66,6 +66,7 @@ impl<'linker> ModuleInitializationContext<'linker> {
cursor.field(field!("expr_or_decl"));

if cursor.kind() == kind!("declaration") {
let whole_decl_span = cursor.span();
cursor.go_down_no_check(|cursor| {
if cursor.optional_field(field!("io_port_modifiers")) {
let is_input = match cursor.kind() {
Expand All @@ -74,7 +75,7 @@ impl<'linker> ModuleInitializationContext<'linker> {
_ => cursor.could_not_match()
};

self.finish_gather_decl(is_input, cursor);
self.finish_gather_decl(is_input, whole_decl_span, cursor);
}
});
}
Expand Down Expand Up @@ -149,36 +150,38 @@ impl<'linker> ModuleInitializationContext<'linker> {
fn gather_decl_names_in_list(&mut self, is_input : bool, cursor : &mut Cursor) -> PortIDRange {
let list_start_at = self.ports.get_next_alloc_id();
cursor.list(kind!("declaration_list"), |cursor| {
let whole_decl_span = cursor.span();
cursor.go_down(kind!("declaration"), |cursor| {
// Skip fields if they exist
let _ = cursor.optional_field(field!("io_port_modifiers"));
self.finish_gather_decl(is_input, cursor);
self.finish_gather_decl(is_input, whole_decl_span, cursor);
});
});
self.ports.range_since(list_start_at)
}

fn finish_gather_decl(&mut self, is_input: bool, cursor: &mut Cursor) {
fn finish_gather_decl(&mut self, is_input: bool, whole_decl_span : Span, cursor: &mut Cursor) {
// If generative input it's a template arg
let is_gen = if cursor.optional_field(field!("declaration_modifiers")) {
cursor.kind() == kw!("gen")
} else {false};

cursor.field(field!("type"));
let type_span = cursor.span();
let decl_span = Span::new_overarching(type_span, whole_decl_span.empty_span_at_end());
let name_span = cursor.field_span(field!("name"), kind!("identifier"));
let name = self.file_text[name_span].to_owned();
if is_gen {
self.template_inputs.alloc(TemplateInput {
name,
name_span,
kind: TemplateInputKind::Generative{declaration_instruction : UUID::PLACEHOLDER}
kind: TemplateInputKind::Generative{decl_span, declaration_instruction : UUID::PLACEHOLDER}
});
} else {
self.ports.alloc(Port{
name,
name_span,
decl_span : Span::new_overarching(type_span, name_span),
decl_span,
is_input,
interface : self.current_interface,
declaration_instruction : UUID::PLACEHOLDER
Expand Down Expand Up @@ -223,7 +226,7 @@ pub fn gather_initial_file_data(mut builder : FileBuilder) {
span,
errors,
resolved_globals,
template_arguments : FlatAlloc::new(), // TODO
template_arguments : ctx.template_inputs,
after_initial_parse_cp,
after_flatten_cp : None
},
Expand Down
57 changes: 48 additions & 9 deletions src/flattening/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ pub struct Module {
pub link_info : LinkInfo,

/// Created by Stage 1: Initialization
///
/// [Port::declaration_instruction] are set in Stage 2: Flattening
pub ports : FlatAlloc<Port, PortIDMarker>,

/// Created by Stage 1: Initialization
Expand Down Expand Up @@ -106,24 +108,36 @@ impl Module {
let mut r = String::new(); self.make_interface_info_fmt(interface_id, file_text, &mut r); r
}

pub fn make_all_ports_info_fmt(&self, file_text : &FileText, local_domains : Option<InterfaceToDomainMap>, result : &mut String) {
pub fn make_all_ports_info_string(&self, file_text : &FileText, local_domains : Option<InterfaceToDomainMap>) -> String {
use std::fmt::Write;

let mut interface_iter = self.interfaces.iter();
if !self.main_interface_used {
interface_iter.next();
}

let mut type_args : Vec<&str> = Vec::new();
let mut temporary_gen_input_builder = String::new();
for (_id, t) in &self.link_info.template_arguments {
match &t.kind {
TemplateInputKind::Type { default_value:_ } => type_args.push(&t.name),
TemplateInputKind::Generative { decl_span, declaration_instruction:_ } => writeln!(temporary_gen_input_builder, "input gen {}", &file_text[*decl_span]).unwrap(),
}
}

let mut result = format!("module {}<{}>:\n", self.link_info.get_full_name(), type_args.join(", "));
result.push_str(&temporary_gen_input_builder);

for (interface_id, interface) in interface_iter {
use std::fmt::Write;
if let Some(domain_map) = &local_domains {
writeln!(result, "{}: {{{}}}", &interface.name, domain_map.get_submodule_interface_domain(interface_id).name).unwrap();
} else {
writeln!(result, "{}:", &interface.name).unwrap();
}
self.make_interface_info_fmt(interface_id, file_text, result);
self.make_interface_info_fmt(interface_id, file_text, &mut result);
}
}
pub fn make_all_ports_info_string(&self, file_text : &FileText, local_domains : Option<InterfaceToDomainMap>) -> String {
let mut r = String::new(); self.make_all_ports_info_fmt(file_text, local_domains, &mut r); r

result
}

pub fn print_flattened_module(&self, file_text : &FileText) {
Expand Down Expand Up @@ -449,6 +463,30 @@ impl WrittenType {

const DECL_DEPTH_LATER : usize = usize::MAX;

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum DeclarationPortInfo {
NotPort,
RegularPort{is_input : bool},
GenerativeInput
}

impl DeclarationPortInfo {
pub fn as_regular_port(&self) -> Option<bool> {
if let DeclarationPortInfo::RegularPort{is_input} = self {
Some(*is_input)
} else {
None
}
}
pub fn implies_read_only(&self) -> bool {
match self {
DeclarationPortInfo::NotPort => false,
DeclarationPortInfo::RegularPort { is_input } => *is_input,
DeclarationPortInfo::GenerativeInput => true,
}
}
}

#[derive(Debug)]
pub struct Declaration {
pub typ_expr : WrittenType,
Expand All @@ -463,7 +501,7 @@ pub struct Declaration {
pub read_only : bool,
/// If the program text already covers the write, then lsp stuff on this declaration shouldn't use it.
pub declaration_itself_is_not_written_to : bool,
pub is_input_port : Option<bool>,
pub is_port : DeclarationPortInfo,
pub identifier_type : IdentifierType,
pub latency_specifier : Option<FlatID>,
pub documentation : Documentation
Expand All @@ -479,8 +517,9 @@ pub struct TemplateInput {

#[derive(Debug)]
pub enum TemplateInputKind {
Type,
Generative{declaration_instruction : FlatID}
/// TODO this isn't quite right, because WrittenType requires access to the instructions, and ostensibly types get executed beforehand. Look into it
Type{default_value : Option<WrittenType>},
Generative{decl_span : Span, declaration_instruction : FlatID}
}

pub struct TemplateArg {
Expand Down
Loading

0 comments on commit f78e092

Please sign in to comment.