#[derive(Debug, Default)] pub struct ILWriter { data: String, indent: usize, } // this would be much nicer if indent gave you a new writer // which would indent things impl ILWriter { pub fn new() -> Self { Default::default() } pub fn write_line(&mut self, line: &str) { self.data += &"\t".repeat(self.indent); self.data += line; self.data += "\n"; } pub fn indent(&mut self) { self.indent += 1 } pub fn dedent(&mut self) { self.indent = self .indent .checked_sub(1) .expect("tried to dedent negative amount") } pub fn finish(self) -> String { self.data } } // the proper way #[derive(Debug)] pub enum PortOption { Input(i32), Output(i32), } #[derive(Debug)] pub struct Wire { /// rtlil ID id: String, /// width in bits width: u32, /// Port info if this is a port port_info: Option, } impl Wire { pub fn new(id: impl Into, width: u32, port_info: Option) -> Self { Wire { id: id.into(), width, port_info, } } fn write_rtlil(&self, writer: &mut ILWriter) { let mut line = String::from("wire "); if let Some(option) = &self.port_info { let port_str = match option { PortOption::Input(num) => format!("input {} ", num), PortOption::Output(num) => format!("output {} ", num), }; line += &port_str; }; if self.width > 1 { line += &format!("width {} ", self.width); } line += &self.id; writer.write_line(&line); } } #[derive(Debug)] pub struct Module { name: String, wires: Vec, cells: Vec, connections: Vec<(String, String)>, gen_id: i32, } impl Module { pub fn new(name: impl Into) -> Self { Module { name: name.into(), wires: Default::default(), cells: Default::default(), connections: Default::default(), gen_id: 0, } } pub fn add_wire(&mut self, wire: Wire) { self.wires.push(wire) } pub fn add_connection(&mut self, target: String, source: String) { self.connections.push((target, source)) } pub fn add_cell(&mut self, cell: Cell) { self.cells.push(cell) } pub fn write_rtlil(&self, writer: &mut ILWriter) { writer.write_line(&format!("module {}", self.name)); writer.indent(); for wire in &self.wires { wire.write_rtlil(writer); } for cell in &self.cells { cell.write_rtlil(writer); } for conn in &self.connections { writer.write_line(&format!("connect {} {}", conn.0, conn.1)) } writer.dedent(); writer.write_line("end"); } pub fn make_genid(&mut self, stem: &str) -> String { let res = format!("${}${}", stem, self.gen_id); self.gen_id += 1; res } } #[derive(Debug)] pub struct Cell { id: String, celltype: String, parameters: Vec<(String, String)>, connections: Vec<(String, String)>, } impl Cell { pub fn new(id: &str, celltype: &str) -> Self { Cell { id: id.into(), celltype: celltype.into(), parameters: Vec::new(), connections: Vec::new(), } } pub fn add_param(&mut self, name: &str, value: &str) { self.parameters.push((name.into(), value.into())) } pub fn add_connection(&mut self, from: &str, to: &str) { self.connections.push((from.into(), to.into())) } fn write_rtlil(&self, writer: &mut ILWriter) { writer.write_line(&format!("cell {} {}", self.celltype, self.id)); writer.indent(); for param in &self.parameters { writer.write_line(&format!("parameter {} {}", param.0, param.1)) } for conn in &self.connections { writer.write_line(&format!("connect {} {}", conn.0, conn.1)) } writer.dedent(); writer.write_line("end"); } }