255 lines
6.7 KiB
Rust
255 lines
6.7 KiB
Rust
use crate::parser;
|
|
use std::collections::HashMap;
|
|
|
|
#[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 {
|
|
fn new() -> Self {
|
|
Default::default()
|
|
}
|
|
|
|
fn write_line(&mut self, line: &str) {
|
|
self.data += &"\t".repeat(self.indent);
|
|
self.data += line;
|
|
self.data += "\n";
|
|
}
|
|
|
|
fn indent(&mut self) {
|
|
self.indent += 1
|
|
}
|
|
|
|
fn dedent(&mut self) {
|
|
self.indent = self
|
|
.indent
|
|
.checked_sub(1)
|
|
.expect("tried to dedent negative amount")
|
|
}
|
|
|
|
fn finish(self) -> String {
|
|
self.data
|
|
}
|
|
}
|
|
|
|
// the proper way
|
|
#[derive(Debug)]
|
|
pub enum WireOption {
|
|
Input(i32),
|
|
Output(i32),
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub struct Wire {
|
|
id: String,
|
|
options: Vec<WireOption>,
|
|
}
|
|
|
|
impl Wire {
|
|
fn new(id: impl Into<String>) -> Self {
|
|
Wire {
|
|
id: id.into(),
|
|
options: Default::default(),
|
|
}
|
|
}
|
|
|
|
fn write_rtlil(&self, writer: &mut ILWriter) {
|
|
let mut line = String::from("wire ");
|
|
|
|
for option in &self.options {
|
|
let option_str = match option {
|
|
WireOption::Input(num) => format!("input {} ", num),
|
|
WireOption::Output(num) => format!("output {} ", num),
|
|
};
|
|
|
|
line += &option_str;
|
|
}
|
|
|
|
line += &self.id;
|
|
writer.write_line(&line);
|
|
}
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
struct Module {
|
|
name: String,
|
|
wires: Vec<Wire>,
|
|
cells: Vec<Cell>,
|
|
connections: Vec<(String, String)>,
|
|
gen_id: i32,
|
|
}
|
|
|
|
impl Module {
|
|
fn new(name: impl Into<String>) -> Self {
|
|
Module {
|
|
name: name.into(),
|
|
wires: Default::default(),
|
|
cells: Default::default(),
|
|
connections: Default::default(),
|
|
gen_id: 0,
|
|
}
|
|
}
|
|
|
|
fn add_wire(&mut self, wire: Wire) {
|
|
self.wires.push(wire)
|
|
}
|
|
|
|
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");
|
|
}
|
|
|
|
fn make_genid(&mut self, stem: &str) -> String {
|
|
let res = format!("${}${}", stem, self.gen_id);
|
|
self.gen_id += 1;
|
|
res
|
|
}
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
struct Cell {
|
|
id: String,
|
|
celltype: String,
|
|
parameters: Vec<(String, String)>,
|
|
connections: Vec<(String, String)>,
|
|
}
|
|
|
|
impl Cell {
|
|
fn new(id: &str, celltype: &str) -> Self {
|
|
Cell {
|
|
id: id.into(),
|
|
celltype: celltype.into(),
|
|
parameters: Vec::new(),
|
|
connections: Vec::new(),
|
|
}
|
|
}
|
|
|
|
fn add_param(&mut self, name: &str, value: &str) {
|
|
self.parameters.push((name.into(), value.into()))
|
|
}
|
|
|
|
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");
|
|
}
|
|
}
|
|
|
|
fn xor_cell(id: &str, a: &str, b: &str, y: &str) -> Cell {
|
|
let mut cell = Cell::new(id, "$xor");
|
|
cell.add_param("\\A_SIGNED", "0");
|
|
cell.add_param("\\A_WIDTH", "1");
|
|
cell.add_param("\\B_SIGNED", "0");
|
|
cell.add_param("\\B_WIDTH", "1");
|
|
cell.add_param("\\Y_WIDTH", "1");
|
|
cell.add_connection("\\A", a);
|
|
cell.add_connection("\\B", b);
|
|
cell.add_connection("\\Y", y);
|
|
cell
|
|
}
|
|
|
|
fn adder_cell(id: &str, a: &str, b: &str, y: &str) -> Cell {
|
|
let mut cell = Cell::new(id, "$and");
|
|
cell.add_param("\\A_SIGNED", "0");
|
|
cell.add_param("\\A_WIDTH", "1");
|
|
cell.add_param("\\B_SIGNED", "0");
|
|
cell.add_param("\\B_WIDTH", "1");
|
|
cell.add_param("\\Y_WIDTH", "1");
|
|
cell.add_connection("\\A", a);
|
|
cell.add_connection("\\B", b);
|
|
cell.add_connection("\\Y", y);
|
|
cell
|
|
}
|
|
|
|
// the hacky way
|
|
|
|
fn make_pubid(id: &str) -> String {
|
|
"\\".to_owned() + id
|
|
}
|
|
|
|
fn lower_expression(module: &mut Module, expr: &parser::Expression) -> String {
|
|
match expr {
|
|
parser::Expression::Ident(ident) => make_pubid(&ident),
|
|
parser::Expression::Call(call) => {
|
|
let output_gen_id = module.make_genid("cell");
|
|
module.add_wire(Wire::new(&output_gen_id));
|
|
|
|
let mut args_resolved = call.args.iter().map(|expr| lower_expression(module, expr));
|
|
|
|
let arg_a = args_resolved.next().unwrap();
|
|
let arg_b = args_resolved.next().unwrap();
|
|
|
|
let cell = match call.name.as_str() {
|
|
"and" => {
|
|
let cell_id = module.make_genid("and");
|
|
adder_cell(&cell_id, &arg_a, &arg_b, &output_gen_id)
|
|
},
|
|
"xor" => {
|
|
let cell_id = module.make_genid("xor");
|
|
xor_cell(&cell_id, &arg_a, &arg_b, &output_gen_id)
|
|
},
|
|
_ => todo!(),
|
|
};
|
|
module.cells.push(cell);
|
|
output_gen_id
|
|
}
|
|
parser::Expression::Operation(op) => todo!(),
|
|
}
|
|
}
|
|
|
|
fn lower_assignment(module: &mut Module, assignment: parser::Assign) {
|
|
let target_id = make_pubid(&assignment.lhs);
|
|
let return_wire = lower_expression(module, &assignment.expr);
|
|
module.connections.push((target_id, return_wire))
|
|
}
|
|
|
|
pub fn lower_module(pa_module: parser::Module) -> String {
|
|
let mut writer = ILWriter::new();
|
|
let mut ir_module = Module::new(format!("\\{}", pa_module.name));
|
|
writer.write_line("autoidx 1");
|
|
for (idx, port) in pa_module.ports.iter().enumerate() {
|
|
let dir_option = match port.direction {
|
|
parser::PortDirection::Input => WireOption::Input(idx as i32 + 1),
|
|
parser::PortDirection::Output => WireOption::Output(idx as i32 + 1),
|
|
};
|
|
let wire = Wire {
|
|
id: make_pubid(&port.net.name),
|
|
options: vec![dir_option],
|
|
};
|
|
ir_module.wires.push(wire);
|
|
}
|
|
for stmt in pa_module.statements {
|
|
match stmt {
|
|
parser::Statement::Assign(assignment) => lower_assignment(&mut ir_module, assignment),
|
|
}
|
|
}
|
|
ir_module.write_rtlil(&mut writer);
|
|
writer.finish()
|
|
}
|