Skip to content

Commit

Permalink
Added basic fallback Verilog generation
Browse files Browse the repository at this point in the history
  • Loading branch information
VonTum committed Dec 15, 2023
1 parent 04e11ae commit 8e5f52c
Show file tree
Hide file tree
Showing 12 changed files with 291 additions and 123 deletions.
7 changes: 7 additions & 0 deletions src/arena_alloc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,9 @@ impl<T, IndexMarker : UUIDMarker> ListAllocator<T, IndexMarker> {
pub fn alloc(&self, v : T) -> UUID<IndexMarker> {
UUID(self.data.alloc(v), PhantomData)
}
pub fn get_next_alloc_id(&self) -> UUID<IndexMarker> {
UUID(self.data.len(), PhantomData)
}
pub fn iter<'a>(&'a self) -> ListAllocIterator<'a, T, IndexMarker> {
self.into_iter()
}
Expand Down Expand Up @@ -356,6 +359,10 @@ impl<T, IndexMarker : UUIDMarker> FlatAlloc<T, IndexMarker> {
pub fn new() -> Self {
Self{data : Vec::new(), _ph : PhantomData}
}
pub fn get_next_alloc_id(&self) -> UUID<IndexMarker> {
let uuid = self.data.len();
UUID(uuid, PhantomData)
}
pub fn alloc(&mut self, value : T) -> UUID<IndexMarker> {
let uuid = self.data.len();
self.data.push(value);
Expand Down
3 changes: 2 additions & 1 deletion src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,8 @@ impl Display for Operator {
#[derive(Debug,Clone)]
pub enum Value {
Bool(bool),
Integer(BigUint)
Integer(BigUint),
Invalid
}

#[derive(Debug,Clone)]
Expand Down
4 changes: 4 additions & 0 deletions src/block_vector.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,10 @@ impl<T, const BLOCK_SIZE : usize> BlockVec<T, BLOCK_SIZE> {
allocated_id
}

pub fn len(&self) -> usize {
self.length.get()
}

pub fn iter<'s>(&'s self) -> BlockVecIter<'s, T, BLOCK_SIZE> {
self.into_iter()
}
Expand Down
56 changes: 1 addition & 55 deletions src/codegen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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());

Expand Down Expand Up @@ -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!("}}")
}
*/
141 changes: 141 additions & 0 deletions src/codegen_fallback.rs
Original file line number Diff line number Diff line change
@@ -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<String> {
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)
}
2 changes: 1 addition & 1 deletion src/dev_aid/syntax_highlighting.rs
Original file line number Diff line number Diff line change
@@ -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};

Expand Down
45 changes: 27 additions & 18 deletions src/flattening.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,8 @@ pub struct InterfacePort {

#[derive(Debug)]
pub enum Instantiation {
SubModule{module_uuid : NamedUUID, typ_span : Span, interface_wires : Vec<InterfacePort>},
PlainWire{read_only : bool, typ : Type, decl_id : Option<DeclID>},
SubModule{module_uuid : NamedUUID, name : Box<str>, typ_span : Span, interface_wires : Vec<InterfacePort>},
PlainWire{read_only : bool, identifier_type : IdentifierType, typ : Type, decl_id : Option<DeclID>},
UnaryOp{typ : Type, op : Operator, right : SpanFlatID},
BinaryOp{typ : Type, op : Operator, left : SpanFlatID, right : SpanFlatID},
ArrayAccess{typ : Type, arr : SpanFlatID, arr_idx : SpanFlatID},
Expand All @@ -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,
Expand All @@ -67,14 +67,14 @@ impl Instantiation {

pub fn iter_sources<F : FnMut(FlatID) -> ()>(&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)}
Expand Down Expand Up @@ -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<str>, 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
Expand All @@ -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 => {
Expand All @@ -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();

Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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)}
}
}
};
Expand Down Expand Up @@ -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))
Expand Down Expand Up @@ -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");
}
}
Expand Down
Loading

0 comments on commit 8e5f52c

Please sign in to comment.