mod sync; pub use sync::{CaseRule, Process, SwitchRule, SyncCond, SyncRule}; #[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"; } // TODO: make this actually take an iterator pub fn write_iter(&mut self, iter: &[impl RtlilWrite]) { for item in iter { item.write_rtlil(self) } } 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 } } pub trait RtlilWrite { fn write_rtlil(&self, writer: &mut ILWriter); } #[derive(Debug, Clone)] pub enum SigSpec { Const(i64, u32), Wire(String), } impl SigSpec { pub fn wire(id: &str) -> Self { Self::Wire(id.to_owned()) } } impl std::fmt::Display for SigSpec { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { SigSpec::Const(val, width) => write!(f, "{0}'{2:01$b}", width, *width as usize, val)?, SigSpec::Wire(id) => write!(f, "{}", id)?, }; Ok(()) } } #[derive(Debug)] pub struct Wire { /// rtlil ID id: String, /// width in bits width: u32, /// Port info if this is a port port_info: Option, } #[derive(Debug)] pub enum PortOption { Input(i32), Output(i32), } impl Wire { pub fn new(id: impl Into, width: u32, port_info: Option) -> Self { Self { id: id.into(), width, port_info, } } pub fn id(&self) -> &str { &self.id } } impl RtlilWrite for Wire { 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, processes: Vec, connections: Vec<(SigSpec, SigSpec)>, gen_id: i32, } impl Module { pub fn new(name: impl Into) -> Self { Self { name: name.into(), wires: Default::default(), cells: Default::default(), connections: Default::default(), processes: Default::default(), gen_id: 0, } } pub fn add_wire(&mut self, wire: Wire) { self.wires.push(wire) } pub fn add_connection(&mut self, target: &SigSpec, source: &SigSpec) { self.connections.push((target.clone(), source.clone())) } pub fn add_cell(&mut self, cell: Cell) { self.cells.push(cell) } pub fn add_process(&mut self, proc: Process) { self.processes.push(proc) } pub fn make_genid(&mut self, stem: &str) -> String { let res = format!("${}${}", stem, self.gen_id); self.gen_id += 1; res } } impl RtlilWrite for Module { fn write_rtlil(&self, writer: &mut ILWriter) { writer.write_line(&format!("module {}", self.name)); writer.indent(); writer.write_iter(&self.wires); writer.write_iter(&self.cells); writer.write_iter(&self.processes); for conn in &self.connections { writer.write_line(&format!("connect {} {}", conn.0, conn.1)) } writer.dedent(); writer.write_line("end"); } } #[derive(Debug)] pub struct Cell { id: String, celltype: String, parameters: Vec<(String, String)>, connections: Vec<(SigSpec, SigSpec)>, } impl Cell { pub fn new(id: &str, celltype: &str) -> Self { Self { 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: &SigSpec) { self.connections.push((SigSpec::wire(from), to.clone())) } } impl RtlilWrite for Cell { 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"); } }