minetest_pnr/src/circuit.rs

429 lines
12 KiB
Rust

// Copyright 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
extern crate itertools;
use itertools::Itertools;
use crate::canvas::{BlockType, Canvas, CornerOrientation};
use crate::channel_router::{ChannelLayout, ChannelState};
use crate::loader::{YosysJsonCell, YosysJsonPortDirection};
use serde_json::Value;
use std::convert::TryFrom;
#[derive(PartialEq, Debug)]
pub enum IOType {
Input,
Output,
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct Position2D(pub u32, pub u32);
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub enum PortConnection {
Net(usize),
Constant(bool),
}
impl PortConnection {
pub fn is_constant(&self) -> bool {
match self {
PortConnection::Constant(_) => true,
_ => false,
}
}
pub fn get_net(&self) -> Option<usize> {
match self {
PortConnection::Net(n) => Some(*n),
_ => None,
}
}
}
impl Into<ChannelState> for PortConnection {
fn into(self) -> ChannelState {
match self {
PortConnection::Net(n) => ChannelState::Net(n),
PortConnection::Constant(false) => ChannelState::Occupied,
PortConnection::Constant(true) => ChannelState::Constant,
}
}
}
#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Eq)]
pub struct Port {
pub connection: PortConnection,
pub position: Option<Position2D>,
}
impl Port {
pub fn new_unplaced(connection: PortConnection) -> Self {
Self {
connection,
position: None,
}
}
pub fn from(s: &Value) -> Self {
let connection = match s {
Value::Number(n) => PortConnection::Net(n.as_u64().unwrap() as usize),
Value::String(ref s) => match s.as_ref() {
"0" | "x" | "y" => PortConnection::Constant(false),
"1" => PortConnection::Constant(true),
_ => panic!("Unknown constant value '{}'", s),
},
_ => unreachable!(),
};
Self {
connection,
position: None,
}
}
}
impl std::cmp::Ord for Port {
fn cmp(&self, other: &Port) -> std::cmp::Ordering {
use PortConnection::*;
match (self.connection, other.connection) {
(Net(a), Net(b)) => a.cmp(&b),
(_, _) => std::cmp::Ordering::Equal,
}
}
}
#[derive(Debug, Copy, Clone, PartialEq)]
pub enum CircuitType {
INPUT,
OUTPUT,
FORWARD,
AND,
NAND,
ANDNOT,
OR,
NOR,
ORNOT,
NOT,
XNOR,
XOR,
}
impl CircuitType {
pub fn from_verilog(s: &str) -> Self {
match s {
"$_ANDNOT_" => CircuitType::ANDNOT, // A & (~B)
"$_NAND_" => CircuitType::NAND, // and but inverted
"$_NOR_" => CircuitType::NOR, // or but inverted
"$_OR_" => CircuitType::OR,
"$_AND_" => CircuitType::AND,
"$_ORNOT_" => CircuitType::ORNOT, // A | (~B)
"$_XNOR_" => CircuitType::XNOR, // ~(XOR)
"$_XOR_" => CircuitType::XOR, // XOR
"$_NOT_" => CircuitType::NOT,
_ => panic!(
"Unsupported gate type '{}', make sure to run an abc pass",
s
),
}
}
pub fn mesecon_id(self) -> &'static str {
use self::CircuitType::*;
match self {
INPUT => "mesecons_walllever:wall_lever_off",
OUTPUT => "mesecons_lamp:lamp_off",
FORWARD => "mesecons_insulated:insulated_off",
OR => "mesecons_gates:or_off",
XOR => "mesecons_gates:xor_off",
AND => "mesecons_gates:and_off",
NAND => "mesecons_gates:nand_off",
NOR => "mesecons_gates:nor_off",
NOT => "mesecons_gates:not_off",
_ => panic!("{:?} not a mesecon thing", &self),
}
}
}
pub trait CircuitT {
fn get_type(&self) -> CircuitType;
fn width(&self) -> u32 {
use self::CircuitType::*;
match self.get_type() {
INPUT | NOT | OUTPUT | FORWARD => 1,
AND | OR | XOR | NAND | ANDNOT | NOR | ORNOT | XNOR => 2,
}
}
fn height(&self) -> u32 {
use self::CircuitType::*;
match self.get_type() {
INPUT | NOT | OUTPUT | FORWARD => 1,
AND | OR | XOR | NAND | ANDNOT | NOR | ORNOT | XNOR => {
assert!(self.inputs().len() == 2 || self.inputs().len() == 1);
3
}
}
}
fn inputs(&self) -> &Vec<Port>;
fn outputs(&self) -> &Vec<Port>;
fn input_positions(&self) -> Vec<(Position2D, Port)>;
fn reposition(&mut self, px: u32);
fn draw(&self, canvas: &mut Canvas);
fn place(&mut self, position: Position2D);
}
#[derive(Debug)]
pub struct Circuit {
pub circuit_type: CircuitType,
pub inputs: Vec<Port>,
pub outputs: Vec<Port>,
pub position: Option<Position2D>,
}
impl Circuit {
pub fn new_external_input_pin(net: Port) -> Self {
Self {
circuit_type: CircuitType::INPUT,
inputs: Vec::new(),
outputs: vec![net],
position: None,
}
}
pub fn new_external_output_pin(net: Port) -> Self {
Self {
circuit_type: CircuitType::OUTPUT,
inputs: vec![net],
outputs: Vec::new(),
position: None,
}
}
pub fn new_forwarding_pin(net: Port) -> Self {
Self {
circuit_type: CircuitType::FORWARD,
inputs: vec![net],
outputs: vec![net],
position: None,
}
}
pub fn width(&self) -> u32 {
use self::CircuitType::*;
match self.circuit_type {
INPUT | NOT | OUTPUT | FORWARD => 1,
AND | OR | XOR | NAND | ANDNOT | NOR | ORNOT | XNOR => 2,
}
}
pub fn height(&self) -> u32 {
use self::CircuitType::*;
match self.circuit_type {
INPUT | NOT | OUTPUT | FORWARD => 1,
AND | OR | XOR | NAND | ANDNOT | NOR | ORNOT | XNOR => {
assert!(self.inputs.len() == 2 || self.inputs.len() == 1);
3
}
}
}
pub fn can_swap_inputs(&self) -> bool {
use self::CircuitType::*;
match self.circuit_type {
INPUT | NOT | OUTPUT | FORWARD | ANDNOT | ORNOT => false,
AND | OR | XOR | NAND | NOR | XNOR => {
assert!(self.inputs.len() == 2);
true
}
}
}
pub fn swap_inputs(&mut self) {
assert!(self.can_swap_inputs());
self.inputs.swap(0, 1);
}
pub fn reposition(&mut self, px: u32) {
assert!(self.position.is_some());
if let Some(ref mut p) = self.position {
p.0 = px + 1;
}
for i in self.inputs.iter_mut() {
if let Some(ref mut p) = i.position {
p.0 = px;
}
}
let w = self.width();
for i in self.outputs.iter_mut() {
if let Some(ref mut p) = i.position {
p.0 = px + w + 1;
}
}
}
pub fn draw(&self, canvas: &mut Canvas) {
if self.circuit_type == CircuitType::INPUT
|| self.circuit_type == CircuitType::NOT
|| self.circuit_type == CircuitType::OUTPUT
|| self.circuit_type == CircuitType::FORWARD
{
// Nothing to do.
} else {
// We need to do the input/output wiring.
let p = self.position.unwrap();
let w = self.width();
// Input
canvas.set(
p.0 as usize,
p.1 as usize - 1,
BlockType::WireCorner(CornerOrientation::LeftDown),
);
canvas.set(
p.0 as usize,
p.1 as usize + 1,
BlockType::WireCorner(CornerOrientation::LeftUp),
);
// Output:
canvas.set(
p.0 as usize + w as usize - 1,
p.1 as usize,
BlockType::WireH,
);
// XNOR / ORNOT / ANDNOT are not mesecon things and need to be monkeypatched.
let invert_second = match self.circuit_type {
CircuitType::XNOR => {
canvas.set(
p.0 as usize,
p.1 as usize,
BlockType::Gate(CircuitType::XOR),
);
canvas.set(
p.0 as usize + w as usize - 1,
p.1 as usize,
BlockType::Gate(CircuitType::NOT),
);
false
}
CircuitType::ORNOT => {
canvas.set(p.0 as usize, p.1 as usize, BlockType::Gate(CircuitType::OR));
true
}
CircuitType::ANDNOT => {
canvas.set(
p.0 as usize,
p.1 as usize,
BlockType::Gate(CircuitType::AND),
);
true
}
_ => false,
};
if invert_second {
// Invert second input.
canvas.set(
p.0 as usize - 1,
p.1 as usize + 1,
BlockType::Gate(CircuitType::NOT),
);
}
}
}
pub fn place(&mut self, position: Position2D) {
if self.position.is_some() {
// This was already placed.
panic!("Circuit was already placed");
}
self.position = Some(Position2D(position.0 + 1, position.1 + self.height() / 2));
let mut off_y = 0;
for i in self.inputs.iter_mut() {
i.position = Some(Position2D(position.0, position.1 + off_y));
off_y += 2;
}
let w = self.width();
if !self.outputs.is_empty() {
assert!(self.outputs.len() == 1);
if self.circuit_type == CircuitType::FORWARD
|| self.circuit_type == CircuitType::INPUT
|| self.circuit_type == CircuitType::NOT
{
self.outputs[0].position = Some(Position2D(position.0 + w + 1, position.1));
} else {
self.outputs[0].position = Some(Position2D(position.0 + w + 2, position.1 + 1));
}
}
}
}
impl TryFrom<&YosysJsonCell> for Circuit {
type Error = ();
fn try_from(cell: &YosysJsonCell) -> Result<Self, Self::Error> {
for i in cell.connections.iter() {
assert!(i.1.len() == 1);
}
let inputs: Vec<_> = cell
.connections
.iter()
.filter(|(k, _)| cell.port_directions[*k] == YosysJsonPortDirection::Input)
.sorted_by(|(k0, _), (k1, _)| k0.cmp(k1))
.map(|(_, v)| Port::from(&v[0]))
.collect();
let outputs: Vec<_> = cell
.connections
.iter()
.filter(|(k, _)| cell.port_directions[*k] == YosysJsonPortDirection::Output)
.map(|(_, v)| Port::from(&v[0]))
.collect();
Ok(Self {
circuit_type: CircuitType::from_verilog(&cell.cell_type),
inputs,
outputs,
position: None,
})
}
}
// Determine the channel layout for the given list of circuits.
pub fn determine_channel_layout<'a, T: Iterator<Item = &'a Circuit>>(
circuits: T,
io: IOType,
) -> Box<ChannelLayout> {
let mut res = Vec::new();
for c in circuits {
let it = match io {
IOType::Input => c.inputs.iter(),
IOType::Output => c.outputs.iter(),
};
for i in it {
let off = i.position.unwrap().1 as usize;
while off >= res.len() {
res.push(ChannelState::Occupied);
}
res[off] = i.connection.into();
}
}
res.into_boxed_slice()
}