futilehdl/src/rtlil.rs

225 lines
5.2 KiB
Rust

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<PortOption>,
}
#[derive(Debug)]
pub enum PortOption {
Input(i32),
Output(i32),
}
impl Wire {
pub fn new(id: impl Into<String>, width: u32, port_info: Option<PortOption>) -> 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<Wire>,
cells: Vec<Cell>,
processes: Vec<Process>,
connections: Vec<(SigSpec, SigSpec)>,
gen_id: i32,
}
impl Module {
pub fn new(name: impl Into<String>) -> 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");
}
}