From 8e5f52c48ef5d2f657a5143561f7a50cad95f630 Mon Sep 17 00:00:00 2001 From: Lennart Van Hirtum Date: Fri, 15 Dec 2023 17:50:32 +0100 Subject: [PATCH] Added basic fallback Verilog generation --- src/arena_alloc.rs | 7 ++ src/ast.rs | 3 +- src/block_vector.rs | 4 + src/codegen.rs | 56 +----------- src/codegen_fallback.rs | 141 +++++++++++++++++++++++++++++ src/dev_aid/syntax_highlighting.rs | 2 +- src/flattening.rs | 45 +++++---- src/instantiation/mod.rs | 100 ++++++++++++-------- src/linker.rs | 3 + src/main.rs | 51 ++++++++--- src/typing.rs | 1 + verilog_output/.gitignore | 1 + 12 files changed, 291 insertions(+), 123 deletions(-) create mode 100644 src/codegen_fallback.rs create mode 100644 verilog_output/.gitignore diff --git a/src/arena_alloc.rs b/src/arena_alloc.rs index 4e63dbc..a0c78d7 100644 --- a/src/arena_alloc.rs +++ b/src/arena_alloc.rs @@ -254,6 +254,9 @@ impl ListAllocator { pub fn alloc(&self, v : T) -> UUID { UUID(self.data.alloc(v), PhantomData) } + pub fn get_next_alloc_id(&self) -> UUID { + UUID(self.data.len(), PhantomData) + } pub fn iter<'a>(&'a self) -> ListAllocIterator<'a, T, IndexMarker> { self.into_iter() } @@ -356,6 +359,10 @@ impl FlatAlloc { pub fn new() -> Self { Self{data : Vec::new(), _ph : PhantomData} } + pub fn get_next_alloc_id(&self) -> UUID { + let uuid = self.data.len(); + UUID(uuid, PhantomData) + } pub fn alloc(&mut self, value : T) -> UUID { let uuid = self.data.len(); self.data.push(value); diff --git a/src/ast.rs b/src/ast.rs index c9921b9..0a5fb36 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -86,7 +86,8 @@ impl Display for Operator { #[derive(Debug,Clone)] pub enum Value { Bool(bool), - Integer(BigUint) + Integer(BigUint), + Invalid } #[derive(Debug,Clone)] diff --git a/src/block_vector.rs b/src/block_vector.rs index 69f8d4f..c359734 100644 --- a/src/block_vector.rs +++ b/src/block_vector.rs @@ -50,6 +50,10 @@ impl BlockVec { allocated_id } + pub fn len(&self) -> usize { + self.length.get() + } + pub fn iter<'s>(&'s self) -> BlockVecIter<'s, T, BLOCK_SIZE> { self.into_iter() } diff --git a/src/codegen.rs b/src/codegen.rs index b6a26de..1e3541f 100644 --- a/src/codegen.rs +++ b/src/codegen.rs @@ -19,7 +19,7 @@ impl GenerationContext { Self{global_ctx} } - pub fn to_circt(&self, instance : &InstantiatedModule) { + pub fn to_circt(&self) { let ctx = *self.global_ctx.deref(); //moore_circt::hw:: let module = moore_circt::ModuleOp::new(ctx); @@ -29,11 +29,6 @@ impl GenerationContext { let mut builder = Builder::new(mod_ctx); builder.set_insertion_point_to_start(module.block()); - //let mut wire_names = instance.wires.iter().map(|a| builder.) - for (id, w) in &instance.wires { - - } - //mlir_builder.set_loc(span_to_loc(mod_ctx, hir.span())); //mlir_builder.set_insertion_point_to_end(self.into_mlir.block()); @@ -80,53 +75,4 @@ pub fn to_calyx(md : &Module, flattened : &FlattenedModule, linker : &Linker) { let res : CalyxResult<()> = backend.emit(ctx: &ir::Context, file: &mut OutputFile) } -pub fn gen_verilog_code(md : &Module, flattened : &FlattenedModule, linker : &Linker) { - let mut cur_wire_id : usize = 0; - let mut wire_names = flattened.wires.map(&mut |_id, _| { - let name = format!("w_{cur_wire_id}"); - cur_wire_id += 1; - name - }); - - let mut cur_inst_id : usize = 0; - let mut instance_names = flattened.instantiations.map(&mut |_id, _| { - let name = format!("inst_{cur_inst_id}"); - cur_inst_id += 1; - name - }); - - let file = &linker.files[md.link_info.file]; - for (idx, v) in md.declarations.iter().enumerate() { - let name = file.file_text[v.name.clone()].to_owned(); - match &flattened.local_map[idx].wire_or_instance { - crate::flattening::WireOrInstantiation::Wire(w_idx) => { - wire_names[*w_idx] = name; - }, - crate::flattening::WireOrInstantiation::Instantiation(i_idx) => { - instance_names[*i_idx] = name; - }, - crate::flattening::WireOrInstantiation::Other(_) => {}, - } - } - - println!("Module {} {{", &file.file_text[file.tokens[md.link_info.name_token].get_range()]); - for (id, w) in &flattened.wires { - println!("\twire {:?} {};", w.typ, &wire_names[id]); - } - println!(); - for (id, i) in &flattened.instantiations { - println!("\tinstantiation {:?} {};", i, &instance_names[id]); - } - println!(); - for conn in &flattened.connections { - let regs = "reg ".repeat(conn.num_regs as usize); - - - if conn.condition != WireID::INVALID { - - } - println!("\t·{regs}{:?} {};", i, &instance_names[id]); - } - println!("}}") -} */ diff --git a/src/codegen_fallback.rs b/src/codegen_fallback.rs new file mode 100644 index 0000000..8679c80 --- /dev/null +++ b/src/codegen_fallback.rs @@ -0,0 +1,141 @@ +use std::{iter::zip, ops::Deref}; + +use crate::{ast::{Module, Value}, instantiation::{InstantiatedModule, RealWireDataSource, StateInitialValue, ConnectToPathElem}, linker::{Linker, NamedUUID, get_builtin_uuid}, arena_alloc::UUID, typing::ConcreteType, tokenizer::get_token_type_name}; + +fn get_type_name_size(id : NamedUUID) -> u64 { + if id == get_builtin_uuid("int") { + 32 // TODO concrete int sizes + } else if id == get_builtin_uuid("bool") { + 1 // TODO concrete int sizes + } else { + println!("TODO Named Structs Size"); + 1 // todo!() // Named structs are not implemented yet + } +} + +fn arr_str(sz : u64) -> String { + format!("[{}:0]", sz - 1) +} + +fn typ_to_verilog_array(typ : &ConcreteType) -> String { + match typ { + ConcreteType::Named(id) => { + let sz = get_type_name_size(*id); + if sz == 1 { + String::new() + } else { + arr_str(sz) + } + } + ConcreteType::Array(arr) => { + let (sub_typ, size) = arr.deref(); + typ_to_verilog_array(sub_typ) + &arr_str(*size) + } + } +} + +pub fn value_to_str(value : &Value) -> String { + match value { + Value::Bool(b) => if *b {"1'b1"} else {"1'b0"}.to_owned(), + Value::Integer(v) => v.to_string(), + Value::Invalid => "INVALID".to_owned() + } +} + +pub fn gen_verilog_code(md : &Module, instance : &InstantiatedModule, linker : &Linker) -> Option { + let mut program_text : String = format!("module {}(\n\tinput clk, \n", md.link_info.name); + for (port, real_port) in zip(&md.interface.interface_wires, &instance.interface) { + if real_port.id == UUID::INVALID {return None;} + let wire = &instance.wires[real_port.id]; + program_text.push_str(if port.is_input {"\tinput"} else {"\toutput"}); + program_text.push_str(&typ_to_verilog_array(&wire.typ)); + program_text.push(' '); + program_text.push_str(&wire.name); + program_text.push_str(",\n"); + } + program_text.push_str(");\n"); + + for (id, w) in &instance.wires { + let wire_or_reg = if let RealWireDataSource::Multiplexer{is_state: initial_value, sources} = &w.source { + if let StateInitialValue::NotState = initial_value { + "/*mux_wire*/ reg" + } else { + "reg" + } + } else {"wire"}; + + program_text.push_str(wire_or_reg); + program_text.push_str(&typ_to_verilog_array(&w.typ)); + program_text.push(' '); + program_text.push_str(&w.name); + program_text.push_str(";\n"); + } + + for (id, sm) in &instance.submodules { + program_text.push_str(&sm.instance.name); + program_text.push(' '); + program_text.push_str(&sm.name); + program_text.push_str("(\n.clk(clk)"); + for (port, wire) in zip(&sm.instance.interface, &sm.wires) { + program_text.push_str(",\n."); + program_text.push_str(&sm.instance.wires[port.id].name); + program_text.push('('); + program_text.push_str(&instance.wires[*wire].name); + program_text.push_str(")"); + } + program_text.push_str("\n);\n"); + } + + for (id, w) in &instance.wires { + match &w.source { + RealWireDataSource::ReadOnly => {} + RealWireDataSource::Multiplexer { is_state, sources } => { + let output_name = w.name.deref(); + match is_state { + StateInitialValue::NotState => { + program_text.push_str(&format!("/*always_comb*/ always @(*) begin\n\t{output_name} <= 1'bX; // Not defined when not valid\n")); + } + StateInitialValue::State { initial_value } => { + program_text.push_str(&format!("/*always_ff*/ always @(posedge clk) begin\n")); + } + } + for s in sources { + let mut path = String::new(); + for path_elem in &s.path { + match path_elem { + ConnectToPathElem::ArrayConnection { idx_wire } => { + path.push('['); + path.push_str(&instance.wires[*idx_wire].name); + path.push(']'); + } + } + } + let from_name = instance.wires[s.from.from].name.deref(); + if s.from.condition != UUID::INVALID { + let cond = instance.wires[s.from.condition].name.deref(); + program_text.push_str(&format!("\tif({cond}) begin {output_name}{path} <= {from_name}; end\n")); + } else { + program_text.push_str(&format!("\t{output_name}{path} <= {from_name};\n")); + } + } + program_text.push_str("end\n"); + } + RealWireDataSource::UnaryOp { op, right } => { + program_text.push_str(&format!("assign {} = {}{};\n", w.name, get_token_type_name(op.op_typ), instance.wires[*right].name)); + } + RealWireDataSource::BinaryOp { op, left, right } => { + program_text.push_str(&format!("assign {} = {} {} {};\n", w.name, instance.wires[*left].name, get_token_type_name(op.op_typ), instance.wires[*right].name)); + } + RealWireDataSource::ArrayAccess { arr, arr_idx } => { + program_text.push_str(&format!("assign {} = {}[{}];\n", w.name, instance.wires[*arr].name, instance.wires[*arr_idx].name)); + } + RealWireDataSource::Constant { value } => { + program_text.push_str(&format!("assign {} = {};\n", w.name, value_to_str(value))); + } + } + } + + program_text.push_str("endmodule\n"); + + Some(program_text) +} diff --git a/src/dev_aid/syntax_highlighting.rs b/src/dev_aid/syntax_highlighting.rs index 36cff87..a34e774 100644 --- a/src/dev_aid/syntax_highlighting.rs +++ b/src/dev_aid/syntax_highlighting.rs @@ -1,5 +1,5 @@ -use std::{ops::Range, path::{PathBuf, Path}}; +use std::{ops::Range, path::PathBuf}; use crate::{ast::*, tokenizer::*, parser::*, linker::{PreLinker, FileData, Links, NamedUUID, Named, Linkable, Linker, FileUUIDMarker, FileUUID}, arena_alloc::ArenaVector}; diff --git a/src/flattening.rs b/src/flattening.rs index 92f0db3..14890a2 100644 --- a/src/flattening.rs +++ b/src/flattening.rs @@ -43,8 +43,8 @@ pub struct InterfacePort { #[derive(Debug)] pub enum Instantiation { - SubModule{module_uuid : NamedUUID, typ_span : Span, interface_wires : Vec}, - PlainWire{read_only : bool, typ : Type, decl_id : Option}, + SubModule{module_uuid : NamedUUID, name : Box, typ_span : Span, interface_wires : Vec}, + PlainWire{read_only : bool, identifier_type : IdentifierType, typ : Type, decl_id : Option}, UnaryOp{typ : Type, op : Operator, right : SpanFlatID}, BinaryOp{typ : Type, op : Operator, left : SpanFlatID, right : SpanFlatID}, ArrayAccess{typ : Type, arr : SpanFlatID, arr_idx : SpanFlatID}, @@ -55,8 +55,8 @@ pub enum Instantiation { impl Instantiation { pub fn get_type(&self) -> &Type { match self { - Instantiation::SubModule{module_uuid : _, typ_span : _, interface_wires : _} => panic!("This is not a struct!"), - Instantiation::PlainWire{read_only: _, typ, decl_id : _} => typ, + Instantiation::SubModule{module_uuid : _, name : _, typ_span : _, interface_wires : _} => panic!("This is not a struct!"), + Instantiation::PlainWire{read_only: _, identifier_type : _, typ, decl_id : _} => typ, Instantiation::UnaryOp{typ, op : _, right : _} => typ, Instantiation::BinaryOp{typ, op : _, left : _, right : _} => typ, Instantiation::ArrayAccess{typ, arr : _, arr_idx : _} => typ, @@ -67,14 +67,14 @@ impl Instantiation { pub fn iter_sources ()>(&self, mut f : F) { match self { - Instantiation::SubModule { module_uuid : _, typ_span : _, interface_wires } => { + Instantiation::SubModule { module_uuid : _, name : _, typ_span : _, interface_wires } => { for port in interface_wires { if port.is_input { f(port.id); } } } - Instantiation::PlainWire { read_only : _, typ : _, decl_id : _ } => {} + Instantiation::PlainWire { read_only : _, identifier_type : _, typ : _, decl_id : _ } => {} Instantiation::UnaryOp { typ : _, op : _, right } => {f(right.0);} Instantiation::BinaryOp { typ : _, op : _, left, right } => {f(left.0); f(right.0);} Instantiation::ArrayAccess { typ : _, arr, arr_idx } => {f(arr.0); f(arr_idx.0)} @@ -141,12 +141,17 @@ impl<'l, 'm, 'fl> FlatteningContext<'l, 'm, 'fl> { Some(()) } - fn alloc_module_interface(&self, module : &Module, module_uuid : NamedUUID, typ_span : Span) -> Instantiation { + fn alloc_module_interface(&self, name : Box, module : &Module, module_uuid : NamedUUID, typ_span : Span) -> Instantiation { let interface_wires = module.interface.interface_wires.iter().map(|port| { - InterfacePort{is_input : port.is_input, id : self.instantiations.alloc(Instantiation::PlainWire { read_only : !port.is_input, typ: port.typ.clone(), decl_id : None })} + let identifier_type = if port.is_input { + IdentifierType::Input + } else { + IdentifierType::Output + }; + InterfacePort{is_input : port.is_input, id : self.instantiations.alloc(Instantiation::PlainWire { read_only : !port.is_input, identifier_type, typ: port.typ.clone(), decl_id : None })} }).collect(); - Instantiation::SubModule{module_uuid, typ_span, interface_wires} + Instantiation::SubModule{name, module_uuid, typ_span, interface_wires} } fn desugar_func_call(&self, func_and_args : &[SpanExpression], closing_bracket_pos : usize, condition : FlatID) -> Option<(&Module, &[InterfacePort])> { let (name_expr, name_expr_span) = &func_and_args[0]; // Function name is always there @@ -158,7 +163,7 @@ impl<'l, 'm, 'fl> FlatteningContext<'l, 'm, 'fl> { let module_ref = self.module.link_info.global_references[*g]; let dependency = self.linker.try_get_module(module_ref, &self.errors)?; - let new_module_interface = self.alloc_module_interface(dependency, module_ref.1, *name_expr_span); + let new_module_interface = self.alloc_module_interface(dependency.link_info.name.clone(), dependency, module_ref.1, *name_expr_span); self.instantiations.alloc(new_module_interface) } _other => { @@ -167,7 +172,7 @@ impl<'l, 'm, 'fl> FlatteningContext<'l, 'm, 'fl> { } }; let func_instantiation = &self.instantiations[func_instantiation_id]; - let Instantiation::SubModule{module_uuid, typ_span, interface_wires} = func_instantiation else {unreachable!("It should be proven {func_instantiation:?} was a Module!");}; + let Instantiation::SubModule{module_uuid, name, typ_span, interface_wires} = func_instantiation else {unreachable!("It should be proven {func_instantiation:?} was a Module!");}; let Named::Module(md) = &self.linker.links.globals[*module_uuid] else {unreachable!("UUID Should be a module!");}; let (inputs, output_range) = md.interface.get_function_sugar_inputs_outputs(); @@ -260,13 +265,15 @@ impl<'l, 'm, 'fl> FlatteningContext<'l, 'm, 'fl> { Some(match expr { AssignableExpression::Named{local_idx} => { let root = self.decl_to_flat_map[*local_idx]; - if let Instantiation::PlainWire { read_only : false, typ : _, decl_id } = &self.instantiations[root] { - ConnectionWrite{root, path : Vec::new(), span : *span} - } else { + let Instantiation::PlainWire { read_only, identifier_type : _, typ : _, decl_id } = &self.instantiations[root] else { + unreachable!("Attempting to assign to a Instantiation::PlainWire") + }; + if *read_only { let decl_info = error_info(self.module.declarations[*local_idx].span, self.errors.file, "Declared here"); self.errors.error_with_info(*span, "Cannot Assign to Read-Only value", vec![decl_info]); return None } + ConnectionWrite{root, path : Vec::new(), span : *span} } AssignableExpression::ArrayIndex(arr_box) => { let (arr, idx, bracket_span) = arr_box.deref(); @@ -311,14 +318,16 @@ impl<'l, 'm, 'fl> FlatteningContext<'l, 'm, 'fl> { } Named::Module(md) => { if let Type::Named(name) = typ { - self.alloc_module_interface(md, name, typ_span) + self.alloc_module_interface(decl.name.clone(), md, name, typ_span) } else { todo!("Implement submodule arrays"); //Instantiation::Error } } Named::Type(_) => { - Instantiation::PlainWire{read_only : decl.identifier_type == IdentifierType::Input, typ, decl_id : Some(*decl_id)} + assert!(decl.identifier_type != IdentifierType::Input); + assert!(decl.identifier_type != IdentifierType::Output); + Instantiation::PlainWire{read_only : false, identifier_type : decl.identifier_type, typ, decl_id : Some(*decl_id)} } } }; @@ -467,7 +476,7 @@ impl FlattenedModule { }; let (wire_id, typ) = if let Some(typ) = context.map_to_type(&decl.typ.0, &module.link_info.global_references) { - let wire_id = context.instantiations.alloc(Instantiation::PlainWire { read_only: is_input, typ : typ.clone(), decl_id : Some(decl_id)}); + let wire_id = context.instantiations.alloc(Instantiation::PlainWire { read_only: is_input, identifier_type : decl.identifier_type, typ : typ.clone(), decl_id : Some(decl_id)}); (wire_id, typ) } else { (UUID::INVALID, Type::Named(UUID::INVALID)) @@ -549,7 +558,7 @@ impl FlattenedModule { // Now produce warnings from the unused list for (id, inst) in &self.instantiations { if !is_instance_used_map[id] { - if let Instantiation::PlainWire { read_only : _, typ : _, decl_id : Some(decl_id) } = inst { + if let Instantiation::PlainWire { read_only : _, identifier_type, typ : _, decl_id : Some(decl_id) } = inst { self.errors.warn_basic(Span::from(md.declarations[*decl_id].name_token), "Unused Variable: This variable does not affect the output ports of this module"); } } diff --git a/src/instantiation/mod.rs b/src/instantiation/mod.rs index 4874f8e..4f73f97 100644 --- a/src/instantiation/mod.rs +++ b/src/instantiation/mod.rs @@ -1,6 +1,8 @@ use std::{rc::Rc, ops::Deref, cell::RefCell}; -use crate::{arena_alloc::{UUID, UUIDMarker, FlatAlloc}, ast::{Value, Operator, Module}, typing::{ConcreteType, Type}, flattening::{FlatID, Instantiation, FlatIDMarker, ConnectionWrite, ConnectionWritePathElement, InterfacePort}, errors::ErrorCollector, linker::{Linker, get_builtin_uuid, NamedUUID}}; +use num::{BigUint, FromPrimitive}; + +use crate::{arena_alloc::{UUID, UUIDMarker, FlatAlloc}, ast::{Value, Operator, Module, IdentifierType}, typing::{ConcreteType, Type}, flattening::{FlatID, Instantiation, FlatIDMarker, ConnectionWrite, ConnectionWritePathElement}, errors::ErrorCollector, linker::{Linker, get_builtin_uuid}}; pub mod latency; @@ -16,9 +18,9 @@ pub type SubModuleID = UUID; #[derive(Debug)] pub struct ConnectFrom { - num_regs : i64, - from : WireID, - condition : WireID + pub num_regs : i64, + pub from : WireID, + pub condition : WireID } #[derive(Debug)] @@ -32,10 +34,16 @@ pub struct MultiplexerSource { pub from : ConnectFrom } +#[derive(Debug)] +pub enum StateInitialValue { + NotState, + State{initial_value : Option} +} + #[derive(Debug)] pub enum RealWireDataSource { ReadOnly, - Multiplexer{sources : Vec}, + Multiplexer{is_state : StateInitialValue, sources : Vec}, UnaryOp{op : Operator, right : WireID}, BinaryOp{op : Operator, left : WireID, right : WireID}, ArrayAccess{arr : WireID, arr_idx : WireID}, @@ -46,7 +54,7 @@ impl RealWireDataSource { fn iter_sources_with_min_latency ()>(&self, mut f : F) { match self { RealWireDataSource::ReadOnly => {} - RealWireDataSource::Multiplexer { sources } => { + RealWireDataSource::Multiplexer { is_state, sources } => { for s in sources { f(s.from.from, s.from.num_regs); f(s.from.condition, 0); @@ -70,10 +78,11 @@ impl RealWireDataSource { #[derive(Debug)] pub struct RealWire { - source : RealWireDataSource, - original_wire : FlatID, - typ : ConcreteType, - latency : i64 + pub source : RealWireDataSource, + pub original_wire : FlatID, + pub typ : ConcreteType, + pub latency : i64, + pub name : Box } #[derive(Debug,Clone,Copy)] @@ -85,13 +94,15 @@ pub struct InstantiatedInterfacePort { #[derive(Debug)] pub struct SubModule { - original_flat : FlatID, - instance : Rc, - wires : Vec + pub original_flat : FlatID, + pub instance : Rc, + pub wires : Vec, + pub name : Box } #[derive(Debug)] pub struct InstantiatedModule { + pub name : Box, // Unique name involving all template arguments pub interface : Vec, pub wires : FlatAlloc, pub submodules : FlatAlloc, @@ -120,7 +131,6 @@ struct InstantiationContext<'fl, 'l> { instance_map : FlatAlloc, wires : FlatAlloc, submodules : FlatAlloc, - interface : Vec, errors : ErrorCollector, module : &'fl Module, @@ -129,8 +139,12 @@ struct InstantiationContext<'fl, 'l> { impl<'fl, 'l> InstantiationContext<'fl, 'l> { fn compute_constant(&self, wire : FlatID) -> Value { - let Instantiation::Constant { typ : _, value } = &self.module.flattened.instantiations[wire] else {todo!()}; - value.clone() + if let Instantiation::Constant { typ : _, value } = &self.module.flattened.instantiations[wire] { + value.clone() + } else { + println!("TODO HIT"); + Value::Integer(BigUint::from_u64(3333333).unwrap()) + } } fn concretize_type(&self, typ : &Type) -> ConcreteType { match typ { @@ -171,50 +185,46 @@ impl<'fl, 'l> InstantiationContext<'fl, 'l> { todo!(); } - let RealWire{latency : _, typ : _, original_wire: _, source : RealWireDataSource::Multiplexer { sources }} = &mut self.wires[self.instance_map[to.root].extract_wire()] else {unreachable!("Should only be a writeable wire here")}; + let RealWire{name : _, latency : _, typ : _, original_wire: _, source : RealWireDataSource::Multiplexer { is_state: initial_value, sources }} = &mut self.wires[self.instance_map[to.root].extract_wire()] else {unreachable!("Should only be a writeable wire here")}; sources.push(MultiplexerSource{from, path : new_path}) } - fn add_wire(&mut self, typ : &Type, original_wire : FlatID, source : RealWireDataSource) -> SubModuleOrWire { - SubModuleOrWire::Wire(self.wires.alloc(RealWire{ latency : i64::MIN /* Invalid */, typ : self.concretize_type(typ), original_wire, source})) + fn add_wire(&mut self, name : Option>, typ : &Type, original_wire : FlatID, source : RealWireDataSource) -> SubModuleOrWire { + let name = name.unwrap_or_else(|| {format!("_{}", self.wires.get_next_alloc_id().get_hidden_value()).into_boxed_str()}); + SubModuleOrWire::Wire(self.wires.alloc(RealWire{ name, latency : i64::MIN /* Invalid */, typ : self.concretize_type(typ), original_wire, source})) } - fn compute_absolute_latencies(&mut self) { - - - - - } - fn instantiate_flattened_module(&mut self) { for (original_wire, inst) in &self.module.flattened.instantiations { let instance_to_add : SubModuleOrWire = match inst { - Instantiation::SubModule{module_uuid, typ_span, interface_wires} => { + Instantiation::SubModule{module_uuid, name, typ_span, interface_wires} => { let instance = self.linker.instantiate(*module_uuid); let interface_real_wires = interface_wires.iter().map(|port| { self.instance_map[port.id].extract_wire() }).collect(); - SubModuleOrWire::SubModule(self.submodules.alloc(SubModule { original_flat: original_wire, instance, wires : interface_real_wires})) + SubModuleOrWire::SubModule(self.submodules.alloc(SubModule { original_flat: original_wire, instance, wires : interface_real_wires, name : name.clone()})) }, - Instantiation::PlainWire{read_only, typ, decl_id} => { + Instantiation::PlainWire{read_only, identifier_type, typ, decl_id} => { let source = if *read_only { RealWireDataSource::ReadOnly } else { - RealWireDataSource::Multiplexer {sources : Vec::new()} + // TODO initial value + let is_state = if *identifier_type == IdentifierType::State {StateInitialValue::State { initial_value: None }} else {StateInitialValue::NotState}; + RealWireDataSource::Multiplexer {is_state, sources : Vec::new()} }; - self.add_wire(typ, original_wire, source) + self.add_wire(decl_id.map(|id| self.module.declarations[id].name.clone()), typ, original_wire, source) }, Instantiation::UnaryOp{typ, op, right} => { - self.add_wire(typ, original_wire, RealWireDataSource::UnaryOp{op: *op, right: self.instance_map[right.0].extract_wire() }) + self.add_wire(None, typ, original_wire, RealWireDataSource::UnaryOp{op: *op, right: self.instance_map[right.0].extract_wire() }) }, Instantiation::BinaryOp{typ, op, left, right} => { - self.add_wire(typ, original_wire, RealWireDataSource::BinaryOp{op: *op, left: self.instance_map[left.0].extract_wire(), right: self.instance_map[right.0].extract_wire() }) + self.add_wire(None, typ, original_wire, RealWireDataSource::BinaryOp{op: *op, left: self.instance_map[left.0].extract_wire(), right: self.instance_map[right.0].extract_wire() }) }, Instantiation::ArrayAccess{typ, arr, arr_idx} => { - self.add_wire(typ, original_wire, RealWireDataSource::ArrayAccess{arr: self.instance_map[arr.0].extract_wire(), arr_idx: self.instance_map[arr_idx.0].extract_wire() }) + self.add_wire(None, typ, original_wire, RealWireDataSource::ArrayAccess{arr: self.instance_map[arr.0].extract_wire(), arr_idx: self.instance_map[arr_idx.0].extract_wire() }) }, Instantiation::Constant{typ, value} => { - self.add_wire(typ, original_wire, RealWireDataSource::Constant{value : value.clone() }) + self.add_wire(None, typ, original_wire, RealWireDataSource::Constant{value : value.clone() }) }, Instantiation::Error => {unreachable!()}, }; @@ -235,6 +245,21 @@ impl<'fl, 'l> InstantiationContext<'fl, 'l> { self.process_connection(&conn.to, conn_from); } } + + fn make_interface(&self) -> Vec { + self.module.interface.interface_wires.iter().map(|port| { + let real_interface_wire = if port.wire_id != UUID::INVALID { + self.instance_map[port.wire_id].extract_wire() + } else { + UUID::INVALID + }; + InstantiatedInterfacePort { + id: real_interface_wire, + is_input: port.is_input, + absolute_latency: i64::MIN, // INVALID + } + }).collect() + } } @@ -258,18 +283,19 @@ impl InstantiationList { instance_map : module.flattened.instantiations.iter().map(|(_, _)| SubModuleOrWire::Unnasigned).collect(), wires : FlatAlloc::new(), submodules : FlatAlloc::new(), - interface : Vec::new(), module : module, linker : linker, errors : ErrorCollector::new(module.flattened.errors.file) }; context.instantiate_flattened_module(); + let interface = context.make_interface(); cache_borrow.push(Rc::new(InstantiatedModule{ + name : module.link_info.name.clone(), wires : context.wires, submodules : context.submodules, - interface : context.interface, + interface, errors : context.errors })); } diff --git a/src/linker.rs b/src/linker.rs index aa0e21e..48d01f6 100644 --- a/src/linker.rs +++ b/src/linker.rs @@ -179,6 +179,9 @@ impl Links { pub fn get_obj_by_name(&self, name : &str) -> Option<&Named> { self.global_namespace.get(name).map(|id| &self.globals[*id]) } + pub fn get_obj_id(&self, name : &str) -> Option { + self.global_namespace.get(name).map(|id| *id) + } } // Represents the fully linked set of all files. Incremental operations such as adding and removing files can be performed diff --git a/src/main.rs b/src/main.rs index 66bf466..8d80daa 100644 --- a/src/main.rs +++ b/src/main.rs @@ -11,17 +11,34 @@ mod instantiation; #[cfg(feature = "codegen")] mod codegen; +mod codegen_fallback; + mod typing; mod dev_aid; mod linker; -use std::env; +use std::{env, ops::Deref}; use std::error::Error; +use std::fs::File; +use std::io::Write; use std::path::PathBuf; +use ast::Module; +use codegen_fallback::gen_verilog_code; use dev_aid::syntax_highlighting::*; -use linker::Named; +use linker::{Named, Linker, NamedUUID}; + +fn codegen_to_file(linker : &Linker, id : NamedUUID, md : &Module) { + let inst = linker.instantiate(id); + + let module_name = md.link_info.name.deref(); + println!("Generating Verilog for {module_name}:"); + // gen_ctx.to_circt(); + let Some(code) = gen_verilog_code(md, &inst, &linker) else {println!("Error"); return;}; + let mut out_file = File::create(format!("verilog_output/{module_name}.v")).unwrap(); + write!(out_file, "{}", code).unwrap() +} fn main() -> Result<(), Box> { let mut args = env::args(); @@ -31,6 +48,7 @@ fn main() -> Result<(), Box> { let mut file_paths : Vec = Vec::new(); let mut is_lsp = false; let mut codegen = None; + let mut codegen_all = false; let mut settings = SyntaxHighlightSettings{ show_tokens : false }; @@ -43,6 +61,9 @@ fn main() -> Result<(), Box> { "--codegen" => { codegen = Some(args.next().expect("Expected a module name after --codegen")); } + "--codegen-all" => { + codegen_all = true; + } "--tokens" => { settings.show_tokens = true; } @@ -57,9 +78,11 @@ fn main() -> Result<(), Box> { return dev_aid::lsp::lsp_main(25000); } if file_paths.len() == 0 { - // Quick debug file + // Quick debugging file_paths.push(PathBuf::from("resetNormalizer.sus")); - //file_paths.push(PathBuf::from("multiply_add.sus")); + file_paths.push(PathBuf::from("multiply_add.sus")); + codegen_all = true; + //codegen = Some("first_bit_idx_6".to_owned()); } let (linker, paths_arena) = compile_all(file_paths); @@ -69,21 +92,27 @@ fn main() -> Result<(), Box> { syntax_highlight_file(&linker, id, &settings); } - #[cfg(feature = "codegen")] + // #[cfg(feature = "codegen")] if let Some(module_name) = codegen { - let gen_ctx = codegen::GenerationContext::new(); - - let Some(named_obj) = linker.links.get_obj_by_name(&module_name) else { + //let gen_ctx = codegen::GenerationContext::new(); + + let Some(id) = linker.links.get_obj_id(&module_name) else { panic!("Module {module_name} does not exist!"); }; - let Named::Module(md) = named_obj else { + let Named::Module(md) = &linker.links.globals[id] else { panic!("{module_name} is not a Module!"); }; - + codegen_to_file(&linker, id, md); + } - //gen_ctx.to_circt(); + if codegen_all { + for (id, obj) in &linker.links.globals { + if let Named::Module(md) = obj { + codegen_to_file(&linker, id, md); + } + } } Ok(()) diff --git a/src/typing.rs b/src/typing.rs index a9698c1..760cebf 100644 --- a/src/typing.rs +++ b/src/typing.rs @@ -39,6 +39,7 @@ impl Value { match self { Value::Bool(_) => Type::Named(get_builtin_uuid("bool")), Value::Integer(_) => Type::Named(get_builtin_uuid("int")), + Value::Invalid => Type::Named(UUID::INVALID), } } } diff --git a/verilog_output/.gitignore b/verilog_output/.gitignore new file mode 100644 index 0000000..39c60c5 --- /dev/null +++ b/verilog_output/.gitignore @@ -0,0 +1 @@ +*.v