extract lowering into frontend
This commit is contained in:
parent
79e570dc60
commit
5b4f378526
|
@ -0,0 +1,108 @@
|
|||
use crate::parser;
|
||||
use crate::rtlil;
|
||||
|
||||
fn builtin_binop_cell(celltype: &str, id: &str, a: &str, b: &str, y: &str) -> rtlil::Cell {
|
||||
let mut cell = rtlil::Cell::new(id, celltype);
|
||||
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 builtin_unop_cell(celltype: &str, id: &str, a: &str, y: &str) -> rtlil::Cell {
|
||||
let mut cell = rtlil::Cell::new(id, celltype);
|
||||
cell.add_param("\\A_SIGNED", "0");
|
||||
cell.add_param("\\A_WIDTH", "1");
|
||||
cell.add_param("\\Y_WIDTH", "1");
|
||||
cell.add_connection("\\A", a);
|
||||
cell.add_connection("\\Y", y);
|
||||
cell
|
||||
}
|
||||
|
||||
// the hacky way
|
||||
|
||||
fn make_pubid(id: &str) -> String {
|
||||
"\\".to_owned() + id
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct CompileError;
|
||||
|
||||
fn lower_expression(module: &mut rtlil::Module, expr: &parser::Expression) -> Result<String, CompileError> {
|
||||
match expr {
|
||||
parser::Expression::Ident(ident) => Ok(make_pubid(&ident)),
|
||||
parser::Expression::Call(call) => {
|
||||
let output_gen_id = module.make_genid("cell");
|
||||
module.add_wire(rtlil::Wire::new(&output_gen_id, 1, None));
|
||||
|
||||
let mut args_resolved = call.args.iter().map(|expr| lower_expression(module, expr));
|
||||
|
||||
// TODO: make this sensible
|
||||
let cell = match call.name.as_str() {
|
||||
"and" => {
|
||||
let arg_a = args_resolved.next().unwrap()?;
|
||||
let arg_b = args_resolved.next().unwrap()?;
|
||||
let cell_id = module.make_genid("and");
|
||||
builtin_binop_cell("$and", &cell_id, &arg_a, &arg_b, &output_gen_id)
|
||||
}
|
||||
"xor" => {
|
||||
let arg_a = args_resolved.next().unwrap()?;
|
||||
let arg_b = args_resolved.next().unwrap()?;
|
||||
let cell_id = module.make_genid("xor");
|
||||
builtin_binop_cell("$xor", &cell_id, &arg_a, &arg_b, &output_gen_id)
|
||||
}
|
||||
"not" => {
|
||||
let arg_a = args_resolved.next().unwrap()?;
|
||||
let cell_id = module.make_genid("not");
|
||||
builtin_unop_cell("$not", &cell_id, &arg_a, &output_gen_id)
|
||||
}
|
||||
"reduce_or" => {
|
||||
let arg_a = args_resolved.next().unwrap()?;
|
||||
let cell_id = module.make_genid("reduce_or");
|
||||
builtin_unop_cell("$reduce_or", &cell_id, &arg_a, &output_gen_id)
|
||||
}
|
||||
_ => return Err(CompileError {}),
|
||||
};
|
||||
module.add_cell(cell);
|
||||
Ok(output_gen_id)
|
||||
}
|
||||
parser::Expression::Operation(op) => todo!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn lower_assignment(module: &mut rtlil::Module, assignment: parser::Assign) -> Result<(), CompileError> {
|
||||
let target_id = make_pubid(&assignment.lhs);
|
||||
let return_wire = lower_expression(module, &assignment.expr)?;
|
||||
module.add_connection(target_id, return_wire);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn lower_module(pa_module: parser::Module) -> Result<String, CompileError> {
|
||||
let mut writer = rtlil::ILWriter::new();
|
||||
let mut ir_module = rtlil::Module::new(make_pubid(&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 => rtlil::PortOption::Input(idx as i32 + 1),
|
||||
parser::PortDirection::Output => rtlil::PortOption::Output(idx as i32 + 1),
|
||||
};
|
||||
let wire = rtlil::Wire::new(
|
||||
make_pubid(&port.net.name),
|
||||
port.net.width.unwrap_or(1) as u32,
|
||||
Some(dir_option)
|
||||
);
|
||||
ir_module.add_wire(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);
|
||||
Ok(writer.finish())
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
mod literals;
|
||||
mod parser;
|
||||
mod rtlil;
|
||||
mod frontend;
|
||||
|
||||
use nom::error::convert_error;
|
||||
use std::fs::File;
|
||||
|
@ -34,8 +35,11 @@ fn main() {
|
|||
Err(_) => (),
|
||||
Ok(res) => {
|
||||
println!("{:#?}", res);
|
||||
let lowered = crate::rtlil::lower_module(res.1);
|
||||
println!("{}", lowered);
|
||||
let lowered = crate::frontend::lower_module(res.1);
|
||||
match lowered {
|
||||
Ok(res) => println!("{}", res),
|
||||
Err(err) => println!("{:#?}", err),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
168
src/rtlil.rs
168
src/rtlil.rs
|
@ -1,6 +1,3 @@
|
|||
use crate::parser;
|
||||
use std::collections::HashMap;
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct ILWriter {
|
||||
data: String,
|
||||
|
@ -10,65 +7,71 @@ pub struct ILWriter {
|
|||
// this would be much nicer if indent gave you a new writer
|
||||
// which would indent things
|
||||
impl ILWriter {
|
||||
fn new() -> Self {
|
||||
pub fn new() -> Self {
|
||||
Default::default()
|
||||
}
|
||||
|
||||
fn write_line(&mut self, line: &str) {
|
||||
pub fn write_line(&mut self, line: &str) {
|
||||
self.data += &"\t".repeat(self.indent);
|
||||
self.data += line;
|
||||
self.data += "\n";
|
||||
}
|
||||
|
||||
fn indent(&mut self) {
|
||||
pub fn indent(&mut self) {
|
||||
self.indent += 1
|
||||
}
|
||||
|
||||
fn dedent(&mut self) {
|
||||
pub fn dedent(&mut self) {
|
||||
self.indent = self
|
||||
.indent
|
||||
.checked_sub(1)
|
||||
.expect("tried to dedent negative amount")
|
||||
}
|
||||
|
||||
fn finish(self) -> String {
|
||||
pub fn finish(self) -> String {
|
||||
self.data
|
||||
}
|
||||
}
|
||||
|
||||
// the proper way
|
||||
#[derive(Debug)]
|
||||
pub enum WireOption {
|
||||
pub enum PortOption {
|
||||
Input(i32),
|
||||
Output(i32),
|
||||
Width(i32),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Wire {
|
||||
/// rtlil ID
|
||||
id: String,
|
||||
options: Vec<WireOption>,
|
||||
/// width in bits
|
||||
width: u32,
|
||||
/// Port info if this is a port
|
||||
port_info: Option<PortOption>
|
||||
}
|
||||
|
||||
impl Wire {
|
||||
fn new(id: impl Into<String>) -> Self {
|
||||
pub fn new(id: impl Into<String>, width: u32, port_info: Option<PortOption>) -> Self {
|
||||
Wire {
|
||||
id: id.into(),
|
||||
options: Default::default(),
|
||||
width,
|
||||
port_info
|
||||
}
|
||||
}
|
||||
|
||||
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),
|
||||
WireOption::Width(num) => format!("width {} ", num),
|
||||
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;
|
||||
};
|
||||
|
||||
line += &option_str;
|
||||
if self.width > 1 {
|
||||
line += &format!("width {} ", self.width);
|
||||
}
|
||||
|
||||
line += &self.id;
|
||||
|
@ -77,7 +80,7 @@ impl Wire {
|
|||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Module {
|
||||
pub struct Module {
|
||||
name: String,
|
||||
wires: Vec<Wire>,
|
||||
cells: Vec<Cell>,
|
||||
|
@ -86,7 +89,7 @@ struct Module {
|
|||
}
|
||||
|
||||
impl Module {
|
||||
fn new(name: impl Into<String>) -> Self {
|
||||
pub fn new(name: impl Into<String>) -> Self {
|
||||
Module {
|
||||
name: name.into(),
|
||||
wires: Default::default(),
|
||||
|
@ -96,11 +99,19 @@ impl Module {
|
|||
}
|
||||
}
|
||||
|
||||
fn add_wire(&mut self, wire: Wire) {
|
||||
pub fn add_wire(&mut self, wire: Wire) {
|
||||
self.wires.push(wire)
|
||||
}
|
||||
|
||||
fn write_rtlil(&self, writer: &mut ILWriter) {
|
||||
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 {
|
||||
|
@ -116,7 +127,7 @@ impl Module {
|
|||
writer.write_line("end");
|
||||
}
|
||||
|
||||
fn make_genid(&mut self, stem: &str) -> String {
|
||||
pub fn make_genid(&mut self, stem: &str) -> String {
|
||||
let res = format!("${}${}", stem, self.gen_id);
|
||||
self.gen_id += 1;
|
||||
res
|
||||
|
@ -124,7 +135,7 @@ impl Module {
|
|||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Cell {
|
||||
pub struct Cell {
|
||||
id: String,
|
||||
celltype: String,
|
||||
parameters: Vec<(String, String)>,
|
||||
|
@ -132,7 +143,7 @@ struct Cell {
|
|||
}
|
||||
|
||||
impl Cell {
|
||||
fn new(id: &str, celltype: &str) -> Self {
|
||||
pub fn new(id: &str, celltype: &str) -> Self {
|
||||
Cell {
|
||||
id: id.into(),
|
||||
celltype: celltype.into(),
|
||||
|
@ -141,11 +152,11 @@ impl Cell {
|
|||
}
|
||||
}
|
||||
|
||||
fn add_param(&mut self, name: &str, value: &str) {
|
||||
pub 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) {
|
||||
pub fn add_connection(&mut self, from: &str, to: &str) {
|
||||
self.connections.push((from.into(), to.into()))
|
||||
}
|
||||
|
||||
|
@ -162,104 +173,3 @@ impl Cell {
|
|||
writer.write_line("end");
|
||||
}
|
||||
}
|
||||
|
||||
fn builtin_binop_cell(celltype: &str, id: &str, a: &str, b: &str, y: &str) -> Cell {
|
||||
let mut cell = Cell::new(id, celltype);
|
||||
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 builtin_unop_cell(celltype: &str, id: &str, a: &str, y: &str) -> Cell {
|
||||
let mut cell = Cell::new(id, celltype);
|
||||
cell.add_param("\\A_SIGNED", "0");
|
||||
cell.add_param("\\A_WIDTH", "1");
|
||||
cell.add_param("\\Y_WIDTH", "1");
|
||||
cell.add_connection("\\A", a);
|
||||
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));
|
||||
|
||||
// TODO: make this sensible
|
||||
let cell = match call.name.as_str() {
|
||||
"and" => {
|
||||
let arg_a = args_resolved.next().unwrap();
|
||||
let arg_b = args_resolved.next().unwrap();
|
||||
let cell_id = module.make_genid("and");
|
||||
builtin_binop_cell("$and", &cell_id, &arg_a, &arg_b, &output_gen_id)
|
||||
}
|
||||
"xor" => {
|
||||
let arg_a = args_resolved.next().unwrap();
|
||||
let arg_b = args_resolved.next().unwrap();
|
||||
let cell_id = module.make_genid("xor");
|
||||
builtin_binop_cell("$xor", &cell_id, &arg_a, &arg_b, &output_gen_id)
|
||||
}
|
||||
"not" => {
|
||||
let arg_a = args_resolved.next().unwrap();
|
||||
let cell_id = module.make_genid("not");
|
||||
builtin_unop_cell("$not", &cell_id, &arg_a, &output_gen_id)
|
||||
}
|
||||
"reduce_or" => {
|
||||
let arg_a = args_resolved.next().unwrap();
|
||||
let cell_id = module.make_genid("reduce_or");
|
||||
builtin_unop_cell("$reduce_or", &cell_id, &arg_a, &output_gen_id)
|
||||
}
|
||||
_ => todo!("unknown function"),
|
||||
};
|
||||
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()
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue