<commit_before>//! ROM image loading code

use std::str;

/// The (decoded) SNES header
#[derive(Debug)]
pub struct RomHeader {
    /// ASCII title, filled with spaces to 21 Bytes
    title: [u8; 21],
    rom_size: u32,
    ram_size: u32,

    // (type is ignored, FastROM and HiROM are not yet supported)
}

impl RomHeader {
    /// Loads the ROM header from the given byte slice (must be exactly 64 bytes large). `checksum`
    /// is the sum of all bytes in the ROM image (truncated to a `u16`).
    fn load(bytes: &[u8], checksum: u16) -> Result<RomHeader, ()> {
        // The header size must be correct (the ROM loader won't pass a wrong size)
        assert_eq!(bytes.len(), 64);

        debug!("{:?}", bytes);

        // First check: Is the title valid ASCII?
        let mut title = [0; 21];
        for (i, c) in bytes[0..21].iter().enumerate() {
            match *c {
                0x20 ... 0x7E => {
                    title[i] = *c;
                }
                _ => {
                    debug!("title contains non-ascii bytes");
                    return Err(())
                }
            }
        }

        debug!("'{}'", str::from_utf8(&title).unwrap().trim_right());

        // "ROM makeup byte"
        let makeup = bytes[21];
        if makeup & 0b00110000 == 0b00110000 {
            error!("FastROM unsupported!");
            return Err(())
        }

        if makeup & 1 == 1 {
            error!("HiROM unsupported");
            return Err(())
        }

        // bytes[22] is the ROM type. I don't care.
        debug!("type: 0x{:02X}", bytes[22]);

        let rom_size = 0x400 << bytes[23] as u32;
        let ram_size = 0x400 << bytes[24] as u32;
        debug!("ROM/RAM size values: {:02X} {:02X}", bytes[23], bytes[24]);
        debug!("{} bytes of ROM, {} bytes of RAM", rom_size, ram_size);

        // bytes[25-26] is a vendor code (doesn't matter)
        debug!("vendor code: 0x{:02X}{:02X}", bytes[25], bytes[26]);
        // 27 = version (also doesn't matter for us)
        debug!("version: 0x{:02X}", bytes[27]);

        // Now it's getting a bit more interesting: The next byte is the checksum's complement
        // followed by the checksum itself. The checksum is the sum of all bytes in the ROM
        // (truncated to 16 bits).
        // 16 bit, little-endian.
        let check_inv = (bytes[29] as u16) << 8 | bytes[28] as u16;
        let check = (bytes[31] as u16) << 8 | bytes[30] as u16;
        if check_inv != !check {
            error!("checksum invalid: stored complement is {:04X}, stored checksum is {:04X}",
                check_inv, check);
            return Err(())
        }

        if check != checksum {
            error!("checksum invalid: stored checksum is {:04X}, computed checksum is {:04X}",
                check, checksum);
            return Err(())
        }

        Ok(RomHeader {
            title: title,
            rom_size: rom_size,
            ram_size: ram_size,
        })
    }

    pub fn title(&self) -> &str {
        // Trim the padding
        str::from_utf8(&self.title).unwrap().trim_right()
    }
}

/// A ROM image
#[derive(Debug)]
pub struct Rom {
    header: RomHeader,
    ram: Vec<u8>,
    rom: Vec<u8>,
}

impl Rom {
    /// Loads a ROM from raw data.
    pub fn from_bytes(mut bytes: &[u8]) -> Result<Rom, ()> {
        // FIXME Return a proper error!

        debug!("raw size: {} bytes ({:#X})", bytes.len(), bytes.len());

        // ROMs may begin with a 512 Bytes SMC header. It needs to go.
        match bytes.len() % 1024 {
            512 => {
                debug!("stripping SMC header");
                bytes = &bytes[512..];
            }
            0 => {},
            n => panic!("len() % 1024 == {} (expected 512 or 0)", n),
        }

        // Calculate the ROM's checksum (the header loader validates it)
        let mut checksum: u16 = 0;
        for &byte in bytes {
            checksum = checksum.wrapping_add(byte as u16);
        }

        debug!("computed checksum: {:04X}", checksum);

        // Read the SNES header. It is located in a really stupid location which depends on whether
        // the ROM is HiROM or LoROM, so we must check both locations and pick the right one.
        // LoROM header is at 0x7FFF - 63, HiROM is at 0xFFFF - 63 (it is 64 Bytes large)

        // Test LoROM first (it's more common)
        let header = try!(RomHeader::load(&bytes[0x7FFF - 63..0x7FFF + 1], checksum)
            .or_else(|_| RomHeader::load(&bytes[0xFFFF - 63..0xFFFF + 1], checksum)));

        // Create the right amount of RAM...
        let ram = vec![0; header.ram_size as usize];
        // ...and copy the ROM
        let mut rom = Vec::with_capacity(header.rom_size as usize);
        for &b in bytes.iter().take(header.rom_size as usize) {
            rom.push(b);
        }
        if rom.len() != header.rom_size as usize { panic!("ROM size mismatch") }

        Ok(Rom {
            header: header,
            ram: ram,
            rom: rom,
        })
    }

    fn resolve_addr(&mut self, bank: u8, addr: u16) -> &mut u8 {
        // FIXME this assumes LoROM, implement HiROM!

        // XXX I fear that this can cause the worst bugs, so make sure this is 100% correct!!!

        match addr {
            0x0000 ... 0x7fff => {
                // Cartridge RAM mapped to the low 8 pages
                // (there's other stuff here, but that's handled much earlier than we are called)
                match bank {
                    0x70 ... 0x7d => {
                        let a = (bank as u32 - 0x70) * 0x8000 + addr as u32;
                        self.ram.get_mut(a as usize).unwrap_or_else(|| out_of_ram_bounds(bank, addr, a))
                    }
                    0xfe ... 0xff => {
                        // last 64k of RAM
                        let start = self.ram.len() as u32 - 64 * 1024;
                        let a = start + (bank - 0xfe) as u32 * 0x8000 + addr as u32;
                        self.ram.get_mut(a as usize).unwrap_or_else(|| out_of_ram_bounds(bank, addr, a))
                    }
                    _ => {
                        // 0x40 ... 0x6f | 0x7e ... 0xfd
                        panic!("attempted to access unmapped address: ${:02X}:{:04X}", bank, addr);
                    }
                }
            },
            0x8000 ... 0xffff => match bank {
                // LoROM is mapped to the higher 8 pages
                0xfe => {
                    let a = 0x3f0000 + addr as u32 - 0x8000;
                    self.rom.get_mut(a as usize).unwrap_or_else(|| out_of_rom_bounds(bank, addr, a))
                }
                0xff => {
                    let a = 0x3f8000 + addr as u32 - 0x8000;
                    self.rom.get_mut(a as usize).unwrap_or_else(|| out_of_rom_bounds(bank, addr, a))
                }
                0x80 ... 0xfd | 0x00 ... 0x7d => {
                    // `& !0x80` because 0x80-0xFD mirrors 0x00-0x7D
                    let a = (bank as u32 & !0x80) * 0x8000 + addr as u32 - 0x8000;
                    self.rom.get_mut(a as usize).unwrap_or_else(|| out_of_rom_bounds(bank, addr, a))
                }
                _ => panic!("attempted to access unmapped address: ${:02X}:{:04X}", bank, addr)
            },
            _ => unreachable!()
        }
    }
}

impl Rom {
    pub fn loadb(&mut self, bank: u8, addr: u16) -> u8 {
        *self.resolve_addr(bank, addr)
    }

    pub fn storeb(&mut self, bank: u8, addr: u16, value: u8) {
        if addr >= 0x8000 {
            panic!("attempted to write ${:02X} to ROM (${:02X}:{:04X})", value, bank, addr);
        }
        *self.resolve_addr(bank, addr) = value;
    }
}

fn out_of_ram_bounds(bank: u8, addr: u16, abs: u32) -> ! {
    panic!("RAM access out of bounds at {:02X}:{:04X} -> {:04X}",
        bank, addr, abs)
}

fn out_of_rom_bounds(bank: u8, addr: u16, abs: u32) -> ! {
    panic!("LoROM access out of bounds at {:02X}:{:04X} -> {:06X}",
        bank, addr, abs)
}
<commit_msg>adjust a few rom loading log levels and messages<commit_after>//! ROM image loading code

use std::str;

/// The (decoded) SNES header
#[derive(Debug)]
pub struct RomHeader {
    /// ASCII title, filled with spaces to 21 Bytes
    title: [u8; 21],
    rom_size: u32,
    ram_size: u32,

    // (type is ignored, FastROM and HiROM are not yet supported)
}

impl RomHeader {
    /// Loads the ROM header from the given byte slice (must be exactly 64 bytes large). `checksum`
    /// is the sum of all bytes in the ROM image (truncated to a `u16`).
    fn load(bytes: &[u8], checksum: u16) -> Result<RomHeader, ()> {
        // The header size must be correct (the ROM loader won't pass a wrong size)
        assert_eq!(bytes.len(), 64);

        debug!("raw rom header: {:?}", bytes);

        // First check: Is the title valid ASCII?
        let mut title = [0; 21];
        for (i, c) in bytes[0..21].iter().enumerate() {
            match *c {
                0x20 ... 0x7E => {
                    title[i] = *c;
                }
                _ => {
                    debug!("title contains non-ascii bytes");
                    return Err(())
                }
            }
        }

        info!("loading '{}'", str::from_utf8(&title).unwrap().trim_right());

        // "ROM makeup byte"
        let makeup = bytes[21];
        if makeup & 0b00110000 == 0b00110000 {
            error!("FastROM unsupported!");
            return Err(())
        }

        if makeup & 1 == 1 {
            error!("HiROM unsupported");
            return Err(())
        }

        // bytes[22] is the ROM type. I don't care.
        debug!("type: 0x{:02X}", bytes[22]);

        let rom_size = 0x400 << bytes[23] as u32;
        let ram_size = 0x400 << bytes[24] as u32;
        debug!("ROM/RAM size values: {:02X} {:02X}", bytes[23], bytes[24]);
        info!("{} bytes of ROM, {} bytes of RAM", rom_size, ram_size);

        // bytes[25-26] is a vendor code (doesn't matter)
        debug!("vendor code: 0x{:02X}{:02X}", bytes[25], bytes[26]);
        // 27 = version (also doesn't matter for us)
        debug!("version: 0x{:02X}", bytes[27]);

        // Now it's getting a bit more interesting: The next byte is the checksum's complement
        // followed by the checksum itself. The checksum is the sum of all bytes in the ROM
        // (truncated to 16 bits).
        // 16 bit, little-endian.
        let check_inv = (bytes[29] as u16) << 8 | bytes[28] as u16;
        let check = (bytes[31] as u16) << 8 | bytes[30] as u16;
        if check_inv != !check {
            error!("checksum invalid: stored complement is {:04X}, stored checksum is {:04X}",
                check_inv, check);
            return Err(())
        }

        if check != checksum {
            error!("checksum invalid: stored checksum is {:04X}, computed checksum is {:04X}",
                check, checksum);
            return Err(())
        }

        Ok(RomHeader {
            title: title,
            rom_size: rom_size,
            ram_size: ram_size,
        })
    }

    pub fn title(&self) -> &str {
        // Trim the padding
        str::from_utf8(&self.title).unwrap().trim_right()
    }
}

/// A ROM image
#[derive(Debug)]
pub struct Rom {
    header: RomHeader,
    ram: Vec<u8>,
    rom: Vec<u8>,
}

impl Rom {
    /// Loads a ROM from raw data.
    pub fn from_bytes(mut bytes: &[u8]) -> Result<Rom, ()> {
        // FIXME Return a proper error!

        debug!("raw size: {} bytes ({:#X})", bytes.len(), bytes.len());

        // ROMs may begin with a 512 Bytes SMC header. It needs to go.
        match bytes.len() % 1024 {
            512 => {
                debug!("stripping SMC header");
                bytes = &bytes[512..];
            }
            0 => {},
            n => panic!("len() % 1024 == {} (expected 512 or 0)", n),
        }

        // Calculate the ROM's checksum (the header loader validates it)
        let mut checksum: u16 = 0;
        for &byte in bytes {
            checksum = checksum.wrapping_add(byte as u16);
        }

        debug!("computed checksum: {:04X}", checksum);

        // Read the SNES header. It is located in a really stupid location which depends on whether
        // the ROM is HiROM or LoROM, so we must check both locations and pick the right one.
        // LoROM header is at 0x7FFF - 63, HiROM is at 0xFFFF - 63 (it is 64 Bytes large)

        // Test LoROM first (it's more common)
        let header = try!(RomHeader::load(&bytes[0x7FFF - 63..0x7FFF + 1], checksum)
            .or_else(|_| RomHeader::load(&bytes[0xFFFF - 63..0xFFFF + 1], checksum)));

        // Create the right amount of RAM...
        let ram = vec![0; header.ram_size as usize];
        // ...and copy the ROM
        let mut rom = Vec::with_capacity(header.rom_size as usize);
        for &b in bytes.iter().take(header.rom_size as usize) {
            rom.push(b);
        }
        if rom.len() != header.rom_size as usize { panic!("ROM size mismatch") }

        Ok(Rom {
            header: header,
            ram: ram,
            rom: rom,
        })
    }

    fn resolve_addr(&mut self, bank: u8, addr: u16) -> &mut u8 {
        // FIXME this assumes LoROM, implement HiROM!

        // XXX I fear that this can cause the worst bugs, so make sure this is 100% correct!!!

        match addr {
            0x0000 ... 0x7fff => {
                // Cartridge RAM mapped to the low 8 pages
                // (there's other stuff here, but that's handled much earlier than we are called)
                match bank {
                    0x70 ... 0x7d => {
                        let a = (bank as u32 - 0x70) * 0x8000 + addr as u32;
                        self.ram.get_mut(a as usize).unwrap_or_else(|| out_of_ram_bounds(bank, addr, a))
                    }
                    0xfe ... 0xff => {
                        // last 64k of RAM
                        let start = self.ram.len() as u32 - 64 * 1024;
                        let a = start + (bank - 0xfe) as u32 * 0x8000 + addr as u32;
                        self.ram.get_mut(a as usize).unwrap_or_else(|| out_of_ram_bounds(bank, addr, a))
                    }
                    _ => {
                        // 0x40 ... 0x6f | 0x7e ... 0xfd
                        panic!("attempted to access unmapped address: ${:02X}:{:04X}", bank, addr);
                    }
                }
            },
            0x8000 ... 0xffff => match bank {
                // LoROM is mapped to the higher 8 pages
                0xfe => {
                    let a = 0x3f0000 + addr as u32 - 0x8000;
                    self.rom.get_mut(a as usize).unwrap_or_else(|| out_of_rom_bounds(bank, addr, a))
                }
                0xff => {
                    let a = 0x3f8000 + addr as u32 - 0x8000;
                    self.rom.get_mut(a as usize).unwrap_or_else(|| out_of_rom_bounds(bank, addr, a))
                }
                0x80 ... 0xfd | 0x00 ... 0x7d => {
                    // `& !0x80` because 0x80-0xFD mirrors 0x00-0x7D
                    let a = (bank as u32 & !0x80) * 0x8000 + addr as u32 - 0x8000;
                    self.rom.get_mut(a as usize).unwrap_or_else(|| out_of_rom_bounds(bank, addr, a))
                }
                _ => panic!("attempted to access unmapped address: ${:02X}:{:04X}", bank, addr)
            },
            _ => unreachable!()
        }
    }
}

impl Rom {
    pub fn loadb(&mut self, bank: u8, addr: u16) -> u8 {
        *self.resolve_addr(bank, addr)
    }

    pub fn storeb(&mut self, bank: u8, addr: u16, value: u8) {
        if addr >= 0x8000 {
            panic!("attempted to write ${:02X} to ROM (${:02X}:{:04X})", value, bank, addr);
        }
        *self.resolve_addr(bank, addr) = value;
    }
}

fn out_of_ram_bounds(bank: u8, addr: u16, abs: u32) -> ! {
    panic!("RAM access out of bounds at {:02X}:{:04X} -> {:04X}",
        bank, addr, abs)
}

fn out_of_rom_bounds(bank: u8, addr: u16, abs: u32) -> ! {
    panic!("LoROM access out of bounds at {:02X}:{:04X} -> {:06X}",
        bank, addr, abs)
}
<|endoftext|>


<commit_before>pub mod log_entry {
    use chrono::prelude::*;
    use std::net::Ipv4Addr;
    use std::time::Duration;
    use std::default::Default;
    use operation::operation::Operation;
    use tokenize::tokenize::Tokenizer;

    static SEPARATORS: &'static [char] = &[' ', ' ', '[', ']', ' ', ' ', ' ', ' ', ' ', ' ',
        '"', '"', ' ', ' ', ' ', ' ', ' ', ' ', ' ', '"', '"', ' ', '"', '"'];

    #[derive(Debug)]
    pub struct LogEntry {
        owner_id: Option<String>,
        bucket: Option<String>,
        timestamp: DateTime<UTC>,
        ip_address: Ipv4Addr,
        request_id: Option<String>,
        requestor_id: Option<String>,
        operation: Operation,
        path: Option<String>,
        request_uri: Option<String>,
        http_status: u16,
        error_message: Option<String>,
        // TODO: Replace with a list of error codes
        bytes_sent: u64,
        object_size: u64,
        total_time: Duration,
        processing_time: Duration,
        referrer: Option<String>,
        user_agent: Option<String>,
        version_id: Option<String>
    }

    impl Default for LogEntry {
        fn default() -> LogEntry {
            LogEntry {
                owner_id: None,
                bucket: None,
                timestamp: Local::now().with_timezone(&UTC),
                ip_address: "127.0.0.1".parse().unwrap(),
                requestor_id: None,
                request_id: None,
                operation: "UNKNOWN.UNKNOWN.UNKNOWN".parse().unwrap(),
                path: None,
                request_uri: None,
                http_status: 0,
                error_message: None,
                bytes_sent: 0,
                object_size: 0,
                total_time: Duration::new(0, 0),
                processing_time: Duration::new(0, 0),
                referrer: None,
                user_agent: None,
                version_id: None
            }
        }
    }

    fn parse_int(data: &str) -> u64 {
        match data.parse::<u64>() {
            Ok(x) => x,
            _ => 0
        }
    }

    macro_rules! get_next_token {
        ($a:ident) => (Some($a.next().unwrap().to_string()))
    }

    macro_rules! get_next_parsed_token {
        ($a:ident) => ($a.next().unwrap().parse().unwrap())
    }

    impl LogEntry {
        pub fn was_complete_download(&self) -> bool {
            self.bytes_sent == self.object_size
        }

        pub fn from_str<'a>(line: &'a str) -> LogEntry {
            let mut new_entry = LogEntry::default();
            let mut tokenizer = Tokenizer::new(SEPARATORS, line);
            new_entry.owner_id = get_next_token!(tokenizer);
            new_entry.bucket = get_next_token!(tokenizer);
            tokenizer.next(); // Skip open '['
            new_entry.timestamp = DateTime::parse_from_str(tokenizer.next().unwrap(), "%d/%b/%Y:%H:%M:%S %z").unwrap().with_timezone(&UTC);
            tokenizer.next(); // Skip ending space
            new_entry.ip_address = get_next_parsed_token!(tokenizer);
            new_entry.requestor_id = get_next_token!(tokenizer);
            new_entry.request_id = get_next_token!(tokenizer);
            new_entry.operation = get_next_parsed_token!(tokenizer);
            new_entry.path = get_next_token!(tokenizer);
            tokenizer.next(); // Skip open '"'
            new_entry.request_uri = get_next_token!(tokenizer);
            tokenizer.next(); // Skip ending space
            new_entry.http_status = get_next_parsed_token!(tokenizer);
            new_entry.error_message = get_next_token!(tokenizer);
            new_entry.bytes_sent = parse_int(tokenizer.next().unwrap());
            new_entry.object_size = parse_int(tokenizer.next().unwrap());

            new_entry.total_time = Duration::from_millis(parse_int(tokenizer.next().unwrap()));
            new_entry.processing_time = Duration::from_millis(parse_int(tokenizer.next().unwrap()));
            tokenizer.next(); // Skip open '"'
            new_entry.referrer = get_next_token!(tokenizer);
            tokenizer.next(); // Skip ending space
            tokenizer.next(); // Skip open '"'
            new_entry.user_agent = get_next_token!(tokenizer);
            new_entry.version_id = get_next_token!(tokenizer);

            new_entry
        }
    }
}
<commit_msg>setting things to public<commit_after>pub mod log_entry {
    use chrono::prelude::*;
    use std::net::Ipv4Addr;
    use std::time::Duration;
    use std::default::Default;
    use operation::operation::Operation;
    use tokenize::tokenize::Tokenizer;

    static SEPARATORS: &'static [char] = &[' ', ' ', '[', ']', ' ', ' ', ' ', ' ', ' ', ' ',
        '"', '"', ' ', ' ', ' ', ' ', ' ', ' ', ' ', '"', '"', ' ', '"', '"'];

    #[derive(Debug)]
    pub struct LogEntry {
        pub owner_id: Option<String>,
        pub bucket: Option<String>,
        pub timestamp: DateTime<UTC>,
        pub ip_address: Ipv4Addr,
        pub request_id: Option<String>,
        pub requestor_id: Option<String>,
        pub operation: Operation,
        pub path: Option<String>,
        pub request_uri: Option<String>,
        pub http_status: u16,
        pub error_message: Option<String>,
        // TODO: Replace with a list of error codes
        pub bytes_sent: u64,
        pub object_size: u64,
        pub total_time: Duration,
        pub processing_time: Duration,
        pub referrer: Option<String>,
        pub user_agent: Option<String>,
        pub version_id: Option<String>
    }

    impl Default for LogEntry {
        fn default() -> LogEntry {
            LogEntry {
                owner_id: None,
                bucket: None,
                timestamp: Local::now().with_timezone(&UTC),
                ip_address: "127.0.0.1".parse().unwrap(),
                requestor_id: None,
                request_id: None,
                operation: "UNKNOWN.UNKNOWN.UNKNOWN".parse().unwrap(),
                path: None,
                request_uri: None,
                http_status: 0,
                error_message: None,
                bytes_sent: 0,
                object_size: 0,
                total_time: Duration::new(0, 0),
                processing_time: Duration::new(0, 0),
                referrer: None,
                user_agent: None,
                version_id: None
            }
        }
    }

    fn parse_int(data: &str) -> u64 {
        match data.parse::<u64>() {
            Ok(x) => x,
            _ => 0
        }
    }

    macro_rules! get_next_token {
        ($a:ident) => (Some($a.next().unwrap().to_string()))
    }

    macro_rules! get_next_parsed_token {
        ($a:ident) => ($a.next().unwrap().parse().unwrap())
    }

    impl LogEntry {
        pub fn was_complete_download(&self) -> bool {
            self.bytes_sent == self.object_size
        }

        pub fn from_str<'a>(line: &'a str) -> LogEntry {
            let mut new_entry = LogEntry::default();
            let mut tokenizer = Tokenizer::new(SEPARATORS, line);
            new_entry.owner_id = get_next_token!(tokenizer);
            new_entry.bucket = get_next_token!(tokenizer);
            tokenizer.next(); // Skip open '['
            new_entry.timestamp = DateTime::parse_from_str(tokenizer.next().unwrap(), "%d/%b/%Y:%H:%M:%S %z").unwrap().with_timezone(&UTC);
            tokenizer.next(); // Skip ending space
            new_entry.ip_address = get_next_parsed_token!(tokenizer);
            new_entry.requestor_id = get_next_token!(tokenizer);
            new_entry.request_id = get_next_token!(tokenizer);
            new_entry.operation = get_next_parsed_token!(tokenizer);
            new_entry.path = get_next_token!(tokenizer);
            tokenizer.next(); // Skip open '"'
            new_entry.request_uri = get_next_token!(tokenizer);
            tokenizer.next(); // Skip ending space
            new_entry.http_status = get_next_parsed_token!(tokenizer);
            new_entry.error_message = get_next_token!(tokenizer);
            new_entry.bytes_sent = parse_int(tokenizer.next().unwrap());
            new_entry.object_size = parse_int(tokenizer.next().unwrap());

            new_entry.total_time = Duration::from_millis(parse_int(tokenizer.next().unwrap()));
            new_entry.processing_time = Duration::from_millis(parse_int(tokenizer.next().unwrap()));
            tokenizer.next(); // Skip open '"'
            new_entry.referrer = get_next_token!(tokenizer);
            tokenizer.next(); // Skip ending space
            tokenizer.next(); // Skip open '"'
            new_entry.user_agent = get_next_token!(tokenizer);
            new_entry.version_id = get_next_token!(tokenizer);

            new_entry
        }
    }
}
<|endoftext|>


<commit_before>// A reference to a wire in a relatively located tile
#[derive(Clone)]
pub struct RelWire {
    pub rel_x: i32,   // (bel.x + rel_x == tile.x)
    pub rel_y: i32,   // (bel.y + rel_y == tile.y)
    pub name: String, // wire name in tile
}

impl RelWire {
    pub fn rel_name(&self) -> String {
        let mut name = String::new();
        if self.rel_y < 0 {
            name.push_str(&format!("N{}", -self.rel_y));
        }
        if self.rel_y > 0 {
            name.push_str(&format!("S{}", self.rel_y));
        }
        if self.rel_x < 0 {
            name.push_str(&format!("W{}", -self.rel_x));
        }
        if self.rel_x > 0 {
            name.push_str(&format!("E{}", self.rel_x));
        }
        if !name.is_empty() {
            name.push(':');
        }
        name.push_str(&self.name);
        name
    }
}

#[derive(Eq, PartialEq, Clone)]
pub enum PinDir {
    INPUT = 0,
    OUTPUT = 1,
    INOUT = 2,
}

#[derive(Clone)]
pub struct BelPin {
    pub name: String,  // name of pin on bel
    pub desc: String,  // description for documentation
    pub dir: PinDir,   // direction
    pub wire: RelWire, // reference to wire in tile
}

impl BelPin {
    pub fn new(
        name: &str,
        desc: &str,
        dir: PinDir,
        wirename: &str,
        rel_x: i32,
        rel_y: i32,
    ) -> BelPin {
        BelPin {
            name: name.to_string(),
            desc: desc.to_string(),
            dir: dir,
            wire: RelWire {
                rel_x: rel_x,
                rel_y: rel_y,
                name: wirename.to_string(),
            },
        }
    }

    // Standard postfixed wirename scheme
    pub fn new_p(
        name: &str,
        desc: &str,
        dir: PinDir,
        postfix: &str,
        rel_x: i32,
        rel_y: i32,
    ) -> BelPin {
        BelPin {
            name: name.to_string(),
            desc: desc.to_string(),
            dir: dir,
            wire: RelWire {
                rel_x: rel_x,
                rel_y: rel_y,
                name: format!("J{}_{}", name, postfix),
            },
        }
    }
    // Logical->physical mapped postfixed wirename scheme
    pub fn new_mapped(
        name: &str,
        desc: &str,
        dir: PinDir,
        physpin: &str,
        postfix: &str,
        rel_x: i32,
        rel_y: i32,
    ) -> BelPin {
        BelPin {
            name: name.to_string(),
            desc: desc.to_string(),
            dir: dir,
            wire: RelWire {
                rel_x: rel_x,
                rel_y: rel_y,
                name: format!("J{}_{}", physpin, postfix),
            },
        }
    }
}

pub struct Bel {
    pub name: String,
    pub beltype: String,
    pub pins: Vec<BelPin>,
    pub z: u32,
}

// Macros for common cases
macro_rules! input {
	($($rest:expr),*) =>
		( def_pin!(PinDir::INPUT, $($rest),*) );
}

macro_rules! output {
	($($rest:expr),*) =>
		( def_pin!(PinDir::OUTPUT, $($rest),*) );
}

macro_rules! def_pin {
    ($dir: expr, $postfix: expr, $name: expr) => {
        BelPin::new_p($name, "", $dir, $postfix, 0, 0)
    };

    ($dir: expr,$postfix: expr, $name: expr, $desc: expr) => {
        BelPin::new_p($name, $desc, $dir, $postfix, 0, 0)
    };
    ($dir: expr,$postfix: expr, $name: expr, $desc: expr, $rel_x: expr, $rel_y: expr) => {
        BelPin::new_p($name, $desc, $dir, $postfix, $rel_x, $rel_y)
    };
}

macro_rules! input_m {
    ($($rest:expr),*) =>
        ( def_pin_mapped!(PinDir::INPUT, $($rest),*) );
}

macro_rules! output_m {
    ($($rest:expr),*) =>
        ( def_pin_mapped!(PinDir::OUTPUT, $($rest),*) );
}

macro_rules! def_pin_mapped {
    ($dir: expr, $postfix: expr, $name: expr) => {
        BelPin::new_p($name, "", $dir, $physpin, $postfix, 0, 0)
    };

    ($dir: expr,$postfix: expr, $name: expr, $physpin: expr, $desc: expr) => {
        BelPin::new_mapped($name, $desc, $dir, $physpin, $postfix, 0, 0)
    };
}

const Z_TO_CHAR: [char; 4] = ['A', 'B', 'C', 'D'];

impl Bel {
    pub fn make_slice(z: usize) -> Bel {
        let ch = Z_TO_CHAR[z];
        let postfix = format!("SLICE{}", ch);
        let mut pins = vec![
            input!(&postfix, "A0", "LUT0 A input"),
            input!(&postfix, "A1", "LUT1 A input"),
            input!(&postfix, "B0", "LUT0 B input"),
            input!(&postfix, "B1", "LUT1 B input"),
            input!(&postfix, "C0", "LUT0 C input"),
            input!(&postfix, "C1", "LUT1 C input"),
            input!(&postfix, "D0", "LUT0 D input"),
            input!(&postfix, "D1", "LUT1 D input"),
            input!(&postfix, "M0", "FF0 M (direct) input"),
            input!(&postfix, "M1", "FF1 M (direct) input"),
            input!(&postfix, "DI0", "FF0 DI (LUT/mux output) input"),
            input!(&postfix, "DI1", "FF1 DI (LUT output) input"),
            input!(&postfix, "SEL", "LUT MUX2 select input"),
            input!(&postfix, "FCI", "fast carry input"),
            input!(&postfix, "CLK", "FF clock"),
            input!(&postfix, "CE", "FF clock enable"),
            input!(&postfix, "LSR", "FF local set/reset"),
            output!(&postfix, "F0", "LUT0 output"),
            output!(&postfix, "F1", "LUT1 output"),
            output!(&postfix, "Q0", "FF0 output"),
            output!(&postfix, "Q1", "FF1 output"),
            output!(&postfix, "OFX0", "MUX2 output"),
            output!(&postfix, "FCO", "fast carry out"),
        ];
        match z {
            0 | 1 => {
                pins.append(&mut vec![
                    input!(&postfix, "WAD0", "LUTRAM write address 0 (from SLICEC)"),
                    input!(&postfix, "WAD1", "LUTRAM write address 1 (from SLICEC)"),
                    input!(&postfix, "WAD2", "LUTRAM write address 2 (from SLICEC)"),
                    input!(&postfix, "WAD3", "LUTRAM write address 3 (from SLICEC)"),
                    input!(&postfix, "WCK", "LUTRAM write clock (from SLICEC)"),
                    input!(&postfix, "WRE", "LUTRAM write enable (from SLICEC)"),
                    input!(&postfix, "WDI0", "LUTRAM write data 0"),
                    input!(&postfix, "WDI1", "LUTRAM write data 1"),
                ]);
            }
            2 => {
                pins.append(&mut vec![
                    output!(&postfix, "WADO0", "LUTRAM write address 0 (to SLICEA/B)"),
                    output!(&postfix, "WADO1", "LUTRAM write address 1 (to SLICEA/B)"),
                    output!(&postfix, "WADO2", "LUTRAM write address 2 (to SLICEA/B)"),
                    output!(&postfix, "WADO3", "LUTRAM write address 3 (to SLICEA/B)"),
                    output!(&postfix, "WCKO", "LUTRAM write clock (to SLICEA/B)"),
                    output!(&postfix, "WREO", "LUTRAM write enable (to SLICEA/B)"),
                    output!(&postfix, "WDO0", "LUTRAM write data 0 (to SLICEA)"),
                    output!(&postfix, "WDO1", "LUTRAM write data 1 (to SLICEA)"),
                    output!(&postfix, "WDO2", "LUTRAM write data 2 (to SLICEB)"),
                    output!(&postfix, "WDO3", "LUTRAM write data 3 (to SLICEB)"),
                ]);
            }
            _ => {}
        }
        Bel {
            name: postfix.clone(),
            beltype: String::from("SLICE"),
            pins: pins,
            z: z as u32,
        }
    }

    pub fn make_oxide_ff(slice: usize, ff: usize) -> Bel {
        let ch = Z_TO_CHAR[slice];
        let postfix = format!("SLICE{}", ch);
        let pins = vec![
            input!(&postfix, "CLK", "FF clock"),
            input!(&postfix, "CE", "FF clock enable"),
            input!(&postfix, "LSR", "FF local set/reset"),
            input_m!(
                &postfix,
                "DI",
                &format!("DI{}", ff),
                "FF input from LUT/MUX output"
            ),
            input_m!(
                &postfix,
                "M",
                &format!("M{}", ff),
                "FF direct input from fabric M signal"
            ),
            output_m!(&postfix, "Q", &format!("Q{}", ff), "FF output"),
        ];
        Bel {
            name: format!("{}_FF{}", &postfix, ff),
            beltype: String::from("OXIDE_FF"),
            pins: pins,
            z: (slice << 3 | (ff + 2)) as u32,
        }
    }

    pub fn make_oxide_comb(slice: usize, lut: usize) -> Bel {
        let ch = Z_TO_CHAR[slice];
        let postfix = format!("SLICE{}", ch);
        let mut pins = vec![
            input_m!(&postfix, "A", &format!("A{}", lut), "LUT A input"),
            input_m!(&postfix, "B", &format!("B{}", lut), "LUT B input"),
            input_m!(&postfix, "C", &format!("C{}", lut), "LUT C input"),
            input_m!(&postfix, "D", &format!("D{}", lut), "LUT D input"),
            input_m!(
                &postfix,
                "FCI",
                if lut == 0 { "FCI" } else { "INT_CARRY" },
                "CCU2 fast carry input"
            ),
            output_m!(&postfix, "F", &format!("F{}", lut), "LUT/sum output"),
            output_m!(
                &postfix,
                "FCO",
                if lut == 1 { "FCO" } else { "INT_CARRY" },
                "CCU2 fast carry output"
            ),
        ];
        if lut == 0 {
            // MUX2 in LUT0 COMB only
            pins.append(&mut vec![
                input!(&postfix, "SEL", "MUX2 select input"),
                input!(&postfix, "F1", "input from second LUT to MUX2"),
                output_m!(&postfix, "OFX", "OFX0", "MUX2 output"),
            ]);
        }
        if slice == 0 || slice == 1 {
            // DPRAM in lower two SLICEs only
            pins.append(&mut vec![
                input!(&postfix, "WAD0", "LUTRAM write address 0 (from RAMW)"),
                input!(&postfix, "WAD1", "LUTRAM write address 1 (from RAMW)"),
                input!(&postfix, "WAD2", "LUTRAM write address 2 (from RAMW)"),
                input!(&postfix, "WAD3", "LUTRAM write address 3 (from RAMW)"),
                input_m!(
                    &postfix,
                    "WD",
                    &format!("WD{}", lut),
                    "LUTRAM write data (from RAMW)"
                ),
                input!(&postfix, "WCK", "LUTRAM write clock (from RAMW)"),
                input!(&postfix, "WRE", "LUTRAM write enable (from RAMW)"),
            ]);
        }
        Bel {
            name: format!("{}_LUT{}", &postfix, lut),
            beltype: String::from("OXIDE_COMB"),
            pins: pins,
            z: (slice << 3 | lut) as u32,
        }
    }

    pub fn make_oxide_ramw(slice: usize) -> Bel {
        assert_eq!(slice, 2);
        let ch = Z_TO_CHAR[slice];
        let postfix = format!("SLICE{}", ch);
        let pins = vec![
            input!(&postfix, "A0", "buffered to WADO3"),
            input!(&postfix, "A1", "buffered to WDO2"),
            input!(&postfix, "B0", "buffered to WADO1"),
            input!(&postfix, "B1", "buffered to WDO3"),
            input!(&postfix, "C0", "buffered to WADO2"),
            input!(&postfix, "C1", "buffered to WDO1"),
            input!(&postfix, "D0", "buffered to WADO0"),
            input!(&postfix, "D1", "buffered to WDO0"),
            input!(&postfix, "CLK", "buffered to WCKO"),
            input!(&postfix, "LSR", "buffered to WREO"),
            output!(&postfix, "WADO0", "LUTRAM write address 0 (to SLICEA/B)"),
            output!(&postfix, "WADO1", "LUTRAM write address 1 (to SLICEA/B)"),
            output!(&postfix, "WADO2", "LUTRAM write address 2 (to SLICEA/B)"),
            output!(&postfix, "WADO3", "LUTRAM write address 3 (to SLICEA/B)"),
            output!(&postfix, "WCKO", "LUTRAM write clock (to SLICEA/B)"),
            output!(&postfix, "WREO", "LUTRAM write enable (to SLICEA/B)"),
            output!(&postfix, "WDO0", "LUTRAM write data 0 (to SLICEA)"),
            output!(&postfix, "WDO1", "LUTRAM write data 1 (to SLICEA)"),
            output!(&postfix, "WDO2", "LUTRAM write data 2 (to SLICEB)"),
            output!(&postfix, "WDO3", "LUTRAM write data 3 (to SLICEB)"),
        ];
        Bel {
            name: format!("{}_RAMW", &postfix),
            beltype: String::from("RAMW"),
            pins: pins,
            z: (slice << 3 | 4) as u32,
        }
    }

    pub fn make_seio33(z: usize) -> Bel {
        let ch = Z_TO_CHAR[z];
        let postfix = format!("SEIO33_CORE_IO{}", ch);
        Bel {
            name: format!("IO{}", ch),
            beltype: String::from("SEIO33_CORE"),
            pins: vec![
                input!(&postfix, "PADDO", "output buffer input from fabric/IOLOGIC"),
                input!(
                    &postfix,
                    "PADDT",
                    "output buffer tristate (0=driven, 1=hi-z)"
                ),
                output!(&postfix, "PADDI", "input buffer output to fabric/IOLOGIC"),
                input!(&postfix, "I3CRESEN", "I3C strong pullup enable"),
                input!(&postfix, "I3CWKPU", "I3C weak pullup enable"),
            ],
            z: z as u32,
        }
    }
}

pub fn get_tile_bels(tiletype: &str) -> Vec<Bel> {
    match tiletype {
        "PLC" => (0..4)
            .map(|slice| {
                let mut bels = vec![
                    Bel::make_oxide_comb(slice, 0),
                    Bel::make_oxide_comb(slice, 1),
                    Bel::make_oxide_ff(slice, 0),
                    Bel::make_oxide_ff(slice, 1),
                ];
                if slice == 2 {
                    bels.push(Bel::make_oxide_ramw(slice));
                }
                bels
            })
            .flatten()
            .collect(),
        "SYSIO_B0_0" | "SYSIO_B1_0" | "SYSIO_B1_0_C" | "SYSIO_B2_0" | "SYSIO_B2_0_C"
        | "SYSIO_B6_0" | "SYSIO_B6_0_C" | "SYSIO_B7_0" | "SYSIO_B7_0_C" => {
            (0..2).map(Bel::make_seio33).collect()
        }
        _ => vec![],
    }
}
<commit_msg>Bel fixes<commit_after>// A reference to a wire in a relatively located tile
#[derive(Clone)]
pub struct RelWire {
    pub rel_x: i32,   // (bel.x + rel_x == tile.x)
    pub rel_y: i32,   // (bel.y + rel_y == tile.y)
    pub name: String, // wire name in tile
}

impl RelWire {
    pub fn rel_name(&self) -> String {
        let mut name = String::new();
        if self.rel_y < 0 {
            name.push_str(&format!("N{}", -self.rel_y));
        }
        if self.rel_y > 0 {
            name.push_str(&format!("S{}", self.rel_y));
        }
        if self.rel_x < 0 {
            name.push_str(&format!("W{}", -self.rel_x));
        }
        if self.rel_x > 0 {
            name.push_str(&format!("E{}", self.rel_x));
        }
        if !name.is_empty() {
            name.push(':');
        }
        name.push_str(&self.name);
        name
    }
}

#[derive(Eq, PartialEq, Clone)]
pub enum PinDir {
    INPUT = 0,
    OUTPUT = 1,
    INOUT = 2,
}

#[derive(Clone)]
pub struct BelPin {
    pub name: String,  // name of pin on bel
    pub desc: String,  // description for documentation
    pub dir: PinDir,   // direction
    pub wire: RelWire, // reference to wire in tile
}

impl BelPin {
    pub fn new(
        name: &str,
        desc: &str,
        dir: PinDir,
        wirename: &str,
        rel_x: i32,
        rel_y: i32,
    ) -> BelPin {
        BelPin {
            name: name.to_string(),
            desc: desc.to_string(),
            dir: dir,
            wire: RelWire {
                rel_x: rel_x,
                rel_y: rel_y,
                name: wirename.to_string(),
            },
        }
    }

    // Standard postfixed wirename scheme
    pub fn new_p(
        name: &str,
        desc: &str,
        dir: PinDir,
        postfix: &str,
        rel_x: i32,
        rel_y: i32,
    ) -> BelPin {
        BelPin {
            name: name.to_string(),
            desc: desc.to_string(),
            dir: dir,
            wire: RelWire {
                rel_x: rel_x,
                rel_y: rel_y,
                name: format!("J{}_{}", name, postfix),
            },
        }
    }
    // Logical->physical mapped postfixed wirename scheme
    pub fn new_mapped(
        name: &str,
        desc: &str,
        dir: PinDir,
        physpin: &str,
        postfix: &str,
        rel_x: i32,
        rel_y: i32,
    ) -> BelPin {
        BelPin {
            name: name.to_string(),
            desc: desc.to_string(),
            dir: dir,
            wire: RelWire {
                rel_x: rel_x,
                rel_y: rel_y,
                name: format!("J{}_{}", physpin, postfix),
            },
        }
    }
}

pub struct Bel {
    pub name: String,
    pub beltype: String,
    pub pins: Vec<BelPin>,
    pub z: u32,
}

// Macros for common cases
macro_rules! input {
	($($rest:expr),*) =>
		( def_pin!(PinDir::INPUT, $($rest),*) );
}

macro_rules! output {
	($($rest:expr),*) =>
		( def_pin!(PinDir::OUTPUT, $($rest),*) );
}

macro_rules! def_pin {
    ($dir: expr, $postfix: expr, $name: expr) => {
        BelPin::new_p($name, "", $dir, $postfix, 0, 0)
    };

    ($dir: expr,$postfix: expr, $name: expr, $desc: expr) => {
        BelPin::new_p($name, $desc, $dir, $postfix, 0, 0)
    };
    ($dir: expr,$postfix: expr, $name: expr, $desc: expr, $rel_x: expr, $rel_y: expr) => {
        BelPin::new_p($name, $desc, $dir, $postfix, $rel_x, $rel_y)
    };
}

macro_rules! input_m {
    ($($rest:expr),*) =>
        ( def_pin_mapped!(PinDir::INPUT, $($rest),*) );
}

macro_rules! output_m {
    ($($rest:expr),*) =>
        ( def_pin_mapped!(PinDir::OUTPUT, $($rest),*) );
}

macro_rules! inout_m {
    ($($rest:expr),*) =>
        ( def_pin_mapped!(PinDir::INOUT, $($rest),*) );
}

macro_rules! def_pin_mapped {
    ($dir: expr, $postfix: expr, $name: expr) => {
        BelPin::new_p($name, "", $dir, $physpin, $postfix, 0, 0)
    };

    ($dir: expr,$postfix: expr, $name: expr, $physpin: expr, $desc: expr) => {
        BelPin::new_mapped($name, $desc, $dir, $physpin, $postfix, 0, 0)
    };
}

const Z_TO_CHAR: [char; 4] = ['A', 'B', 'C', 'D'];

impl Bel {
    pub fn make_oxide_ff(slice: usize, ff: usize) -> Bel {
        let ch = Z_TO_CHAR[slice];
        let postfix = format!("SLICE{}", ch);
        let pins = vec![
            input!(&postfix, "CLK", "FF clock"),
            input!(&postfix, "CE", "FF clock enable"),
            input!(&postfix, "LSR", "FF local set/reset"),
            input_m!(
                &postfix,
                "DI",
                &format!("DI{}", ff),
                "FF input from LUT/MUX output"
            ),
            input_m!(
                &postfix,
                "M",
                &format!("M{}", ff),
                "FF direct input from fabric M signal"
            ),
            output_m!(&postfix, "Q", &format!("Q{}", ff), "FF output"),
        ];
        Bel {
            name: format!("{}_FF{}", &postfix, ff),
            beltype: String::from("OXIDE_FF"),
            pins: pins,
            z: (slice << 3 | (ff + 2)) as u32,
        }
    }

    pub fn make_oxide_comb(slice: usize, lut: usize) -> Bel {
        let ch = Z_TO_CHAR[slice];
        let postfix = format!("SLICE{}", ch);
        let mut pins = vec![
            input_m!(&postfix, "A", &format!("A{}", lut), "LUT A input"),
            input_m!(&postfix, "B", &format!("B{}", lut), "LUT B input"),
            input_m!(&postfix, "C", &format!("C{}", lut), "LUT C input"),
            input_m!(&postfix, "D", &format!("D{}", lut), "LUT D input"),
            input_m!(
                &postfix,
                "FCI",
                if lut == 0 { "FCI" } else { "INT_CARRY" },
                "CCU2 fast carry input"
            ),
            output_m!(&postfix, "F", &format!("F{}", lut), "LUT/sum output"),
            output_m!(
                &postfix,
                "FCO",
                if lut == 1 { "FCO" } else { "INT_CARRY" },
                "CCU2 fast carry output"
            ),
        ];
        if lut == 0 {
            // MUX2 in LUT0 COMB only
            pins.append(&mut vec![
                input!(&postfix, "SEL", "MUX2 select input"),
                input!(&postfix, "F1", "input from second LUT to MUX2"),
                output_m!(&postfix, "OFX", "OFX0", "MUX2 output"),
            ]);
        }
        if slice == 0 || slice == 1 {
            // DPRAM in lower two SLICEs only
            pins.append(&mut vec![
                input!(&postfix, "WAD0", "LUTRAM write address 0 (from RAMW)"),
                input!(&postfix, "WAD1", "LUTRAM write address 1 (from RAMW)"),
                input!(&postfix, "WAD2", "LUTRAM write address 2 (from RAMW)"),
                input!(&postfix, "WAD3", "LUTRAM write address 3 (from RAMW)"),
                input_m!(
                    &postfix,
                    "WD",
                    &format!("WD{}", lut),
                    "LUTRAM write data (from RAMW)"
                ),
                input!(&postfix, "WCK", "LUTRAM write clock (from RAMW)"),
                input!(&postfix, "WRE", "LUTRAM write enable (from RAMW)"),
            ]);
        }
        Bel {
            name: format!("{}_LUT{}", &postfix, lut),
            beltype: String::from("OXIDE_COMB"),
            pins: pins,
            z: (slice << 3 | lut) as u32,
        }
    }

    pub fn make_oxide_ramw(slice: usize) -> Bel {
        assert_eq!(slice, 2);
        let ch = Z_TO_CHAR[slice];
        let postfix = format!("SLICE{}", ch);
        let pins = vec![
            input!(&postfix, "A0", "buffered to WADO3"),
            input!(&postfix, "A1", "buffered to WDO2"),
            input!(&postfix, "B0", "buffered to WADO1"),
            input!(&postfix, "B1", "buffered to WDO3"),
            input!(&postfix, "C0", "buffered to WADO2"),
            input!(&postfix, "C1", "buffered to WDO1"),
            input!(&postfix, "D0", "buffered to WADO0"),
            input!(&postfix, "D1", "buffered to WDO0"),
            input!(&postfix, "CLK", "buffered to WCKO"),
            input!(&postfix, "LSR", "buffered to WREO"),
            output!(&postfix, "WADO0", "LUTRAM write address 0 (to SLICEA/B)"),
            output!(&postfix, "WADO1", "LUTRAM write address 1 (to SLICEA/B)"),
            output!(&postfix, "WADO2", "LUTRAM write address 2 (to SLICEA/B)"),
            output!(&postfix, "WADO3", "LUTRAM write address 3 (to SLICEA/B)"),
            output!(&postfix, "WCKO", "LUTRAM write clock (to SLICEA/B)"),
            output!(&postfix, "WREO", "LUTRAM write enable (to SLICEA/B)"),
            output!(&postfix, "WDO0", "LUTRAM write data 0 (to SLICEA)"),
            output!(&postfix, "WDO1", "LUTRAM write data 1 (to SLICEA)"),
            output!(&postfix, "WDO2", "LUTRAM write data 2 (to SLICEB)"),
            output!(&postfix, "WDO3", "LUTRAM write data 3 (to SLICEB)"),
        ];
        Bel {
            name: format!("{}_RAMW", &postfix),
            beltype: String::from("RAMW"),
            pins: pins,
            z: (slice << 3 | 4) as u32,
        }
    }

    pub fn make_seio33(z: usize) -> Bel {
        let ch = Z_TO_CHAR[z];
        let postfix = format!("SEIO33_CORE_IO{}", ch);
        Bel {
            name: format!("IO{}", ch),
            beltype: String::from("SEIO33_CORE"),
            pins: vec![
                inout_m!(&postfix, "B", "PAD", "top level pad signal"),
                input_m!(
                    &postfix,
                    "I",
                    "PADDO",
                    "output buffer input from fabric/IOLOGIC"
                ),
                input_m!(
                    &postfix,
                    "T",
                    "PADDT",
                    "output buffer tristate (0=driven, 1=hi-z)"
                ),
                output_m!(
                    &postfix,
                    "O",
                    "PADDI",
                    "input buffer output to fabric/IOLOGIC"
                ),
                input!(&postfix, "I3CRESEN", "I3C strong pullup enable"),
                input!(&postfix, "I3CWKPU", "I3C weak pullup enable"),
            ],
            z: z as u32,
        }
    }

    pub fn make_seio18(z: usize) -> Bel {
        let ch = Z_TO_CHAR[z];
        let postfix = format!("SEIO18_CORE_IO{}", ch);
        Bel {
            name: format!("IO{}", ch),
            beltype: String::from("SEIO18_CORE"),
            pins: vec![
                inout_m!(&postfix, "B", "IOPAD", "top level pad signal"),
                input_m!(
                    &postfix,
                    "I",
                    "PADDO",
                    "output buffer input from fabric/IOLOGIC"
                ),
                input_m!(
                    &postfix,
                    "T",
                    "PADDT",
                    "output buffer tristate (0=driven, 1=hi-z)"
                ),
                output_m!(
                    &postfix,
                    "O",
                    "PADDI",
                    "input buffer output to fabric/IOLOGIC"
                ),
                input!(&postfix, "DOLP", "DPHY LP mode output buffer input"),
                output!(&postfix, "INLP", "DPHY LP mode input buffer output"),
                output!(&postfix, "INADC", "analog signal out to ADC"),
            ],
            z: z as u32,
        }
    }
}

pub fn get_tile_bels(tiletype: &str) -> Vec<Bel> {
    match tiletype {
        "PLC" => (0..4)
            .map(|slice| {
                let mut bels = vec![
                    Bel::make_oxide_comb(slice, 0),
                    Bel::make_oxide_comb(slice, 1),
                    Bel::make_oxide_ff(slice, 0),
                    Bel::make_oxide_ff(slice, 1),
                ];
                if slice == 2 {
                    bels.push(Bel::make_oxide_ramw(slice));
                }
                bels
            })
            .flatten()
            .collect(),
        "SYSIO_B0_0" | "SYSIO_B1_0" | "SYSIO_B1_0_C" | "SYSIO_B2_0" | "SYSIO_B2_0_C"
        | "SYSIO_B6_0" | "SYSIO_B6_0_C" | "SYSIO_B7_0" | "SYSIO_B7_0_C" => {
            (0..2).map(Bel::make_seio33).collect()
        }
        "SYSIO_B3_0" | "SYSIO_B4_0" | "SYSIO_B5_0" => (0..2).map(Bel::make_seio18).collect(),
        _ => vec![],
    }
}
<|endoftext|>


<commit_before>use std::collections::HashMap;
use std::str::FromStr;

/// A shader module.
///
/// A shader module is a container that associates some shading code to several identifiers.
struct ShaderModule {
  symbols: HashMap<Identifier, ShadingCode>
}

/// A semigroup version of Vec.
#[derive(Clone, Debug, Eq, PartialEq)]
struct NonEmpty<T>(Vec<T>);

/// Spectra Shading Language AST.
#[derive(Clone, Debug, Eq, PartialEq)]
enum SSL {
  /// An `export list_of_identifiers_` statement.
  Export(NonEmpty<Identifier>),
  /// A `from module use list of identifiers` statement.
  FromUse(Module, NonEmpty<Identifier>),
  /// A `pipeline { list_of_pipeline_attributes }` statement.
  Pipeline(Vec<PipelineAttribute>),
  /// A yield statement, valid in geometry shaders.
  Yield(GeometryYieldExpression),
  /// Some legacy GLSL code.
  GLSL(ShadingCode),
  /// Some more SSL code.
  SSL(Box<SSL>)
}

/// A module.
type Module = String;
/// An identifier.
type Identifier = String;
/// Some opaque shading code.
type ShadingCode = String;
/// An expression.
type Expression = String;

/// Attributes that can be set in a pipeline.
#[derive(Clone, Debug, Eq, PartialEq)]
enum PipelineAttribute {
  /// Maximum vertices that the geometry shader can output.
  GeometryShaderMaxVertices(u32),
  /// Number of times the geometry shader must be invoked.
  GeometryShaderInvokations(u32)
}

/// Expressions that can be yielded in a geometry shader.
#[derive(Clone, Debug, Eq, PartialEq)]
enum GeometryYieldExpression {
  /// Yield a primitive.
  YieldPrimitive,
  /// Yield a primitive’s vertex (fold vertex).
  YieldFoldVertex(Expression)
}

/// Error that can occur when parsing SSL code.
#[derive(Clone, Debug, Eq, PartialEq)]
enum ParseError {
  ExpressionError(String)
}

impl FromStr for PipelineAttribute {
  type Err = ParseError;

  fn from_str(s: &str) -> Result<Self, Self::Err> {
    let equal_sign_idx = s.find('=').ok_or(ParseError::ExpressionError("cannot find =".into()))?;
    let (key, value) = s.split_at(equal_sign_idx);

    if value.len() <= 1 {
      return Err(ParseError::ExpressionError("no value".into()));
    }

    match key.trim() {
      "geometry_shader_max_vertices" => {
        let value = (&value[1..]).trim();
        let max_vertices = value.parse().map_err(|_| ParseError::ExpressionError(format!("unable to parse geometry_shader_max_vertices, found {}", value)))?;
        Ok(PipelineAttribute::GeometryShaderMaxVertices(max_vertices))
      },
      "geometry_shader_invokations" => {
        let value = (&value[1..]).trim();
        let invokations = value.parse().map_err(|_| ParseError::ExpressionError(format!("unable to parse geometry_shader_invokation, found {}", value)))?;
        Ok(PipelineAttribute::GeometryShaderInvokations(invokations))
      },
      _ => Err(ParseError::ExpressionError(format!("expected a valid pipeline attribute, found {}", key)))
    }
  }
}

impl FromStr for GeometryYieldExpression {
  type Err = ParseError;

  fn from_str(s: &str) -> Result<Self, Self::Err> {
    match s {
      "yieldprim" => Ok(GeometryYieldExpression::YieldPrimitive),
      _ if s.starts_with("yield ") && s.len() > "yield ".len() => {
        let expr = &s["yield ".len() ..];
        Ok(GeometryYieldExpression::YieldFoldVertex(expr.into()))
      },
      _ => {
        Err(ParseError::ExpressionError(format!("expected yield, found {}", s)))
      }
    }
  }
}

// FIXME: move that into a specific module
#[test]
fn parse_pipeline_attribute() {
  let geo_max_vertices  = "geometry_shader_max_vertices = 3";
  let geo_max_vertices1 = "geometry_shader_max_vertices =3";
  let geo_max_vertices2 = "geometry_shader_max_vertices =";
  let geo_max_vertices3 = "geometry_shader_max_vertices = ";
  let geo_invokations  = "geometry_shader_invokations = 1";
  let geo_invokations1 = "geometry_shader_invokations =1";
  let geo_invokations2 = "geometry_shader_invokations =";
  let geo_invokations3 = "geometry_shader_invokations = ";

  assert_eq!(geo_max_vertices.parse::<PipelineAttribute>(), Ok(PipelineAttribute::GeometryShaderMaxVertices(3)));
  assert_eq!(geo_max_vertices1.parse::<PipelineAttribute>(), Ok(PipelineAttribute::GeometryShaderMaxVertices(3)));
  assert!(geo_max_vertices2.parse::<PipelineAttribute>().is_err());
  assert!(geo_max_vertices3.parse::<PipelineAttribute>().is_err());
  assert_eq!(geo_invokations.parse::<PipelineAttribute>(), Ok(PipelineAttribute::GeometryShaderInvokations(1)));
  assert_eq!(geo_invokations1.parse::<PipelineAttribute>(), Ok(PipelineAttribute::GeometryShaderInvokations(1)));
  assert!(geo_invokations2.parse::<PipelineAttribute>().is_err());
  assert!(geo_invokations3.parse::<PipelineAttribute>().is_err());
}

// FIXME: move that into a specific module
#[test]
fn parse_geometry_yield_expression() {
  let yieldprim = "yieldprim";
  let yield_1 = "yield FoldVertex(vertex[i].color)";

  assert_eq!(yieldprim.parse::<GeometryYieldExpression>(), Ok(GeometryYieldExpression::YieldPrimitive));
  assert_eq!(yield_1.parse::<GeometryYieldExpression>(), Ok(GeometryYieldExpression::YieldFoldVertex("FoldVertex(vertex[i].color)".into())));
}
<commit_msg>Refactored some code about SSL. #24<commit_after>use std::collections::{HashMap, HashSet};
use std::str::FromStr;

/// A shader module.
///
/// A shader module is a container that associates some shading code to several identifiers.
struct ShaderModule {
  symbols: HashMap<Identifier, ShadingCode>
}

/// Spectra Shading Language AST.
#[derive(Clone, Debug, Eq, PartialEq)]
enum SSL {
  /// An `export list_of_identifiers_` statement.
  Export(ExportList),
  /// A `from module use list of identifiers` statement.
  FromUse(ImportList),
  /// A `pipeline { list_of_pipeline_attributes }` statement.
  Pipeline(PipelineStatement),
  /// A yield statement, valid in geometry shaders.
  Yield(GeometryYieldExpression),
  /// Some legacy GLSL code.
  GLSL(ShadingCode),
  /// Some more SSL code.
  SSL(Box<SSL>)
}

/// A module.
type Module = String;
/// An identifier.
type Identifier = String;
/// Some opaque shading code.
type ShadingCode = String;
/// An expression.
type Expression = String;

/// An export non-empty list.
#[derive(Clone, Debug, Eq, PartialEq)]
struct ExportList {
  list: HashSet<Identifier>
}

/// An import non-empty list.
#[derive(Clone, Debug, Eq, PartialEq)]
struct ImportList {
  module: Module,
  list: HashSet<Identifier>
}

/// A pipeline statement.
#[derive(Clone, Debug, Eq, PartialEq)]
struct PipelineStatement {
  attributes: Vec<PipelineAttribute>
}

/// Attributes that can be set in a pipeline.
#[derive(Clone, Debug, Eq, PartialEq)]
enum PipelineAttribute {
  /// Maximum vertices that the geometry shader can output.
  GeometryShaderMaxVertices(u32),
  /// Number of times the geometry shader must be invoked.
  GeometryShaderInvokations(u32)
}

/// Expressions that can be yielded in a geometry shader.
#[derive(Clone, Debug, Eq, PartialEq)]
enum GeometryYieldExpression {
  /// Yield a primitive.
  YieldPrimitive,
  /// Yield a primitive’s vertex (fold vertex).
  YieldFoldVertex(Expression)
}

/// Error that can occur when parsing SSL code.
#[derive(Clone, Debug, Eq, PartialEq)]
enum ParseError {
  ExpressionError(String)
}

impl FromStr for PipelineAttribute {
  type Err = ParseError;

  fn from_str(s: &str) -> Result<Self, Self::Err> {
    let equal_sign_idx = s.find('=').ok_or(ParseError::ExpressionError("cannot find =".into()))?;
    let (key, value) = s.split_at(equal_sign_idx);

    if value.len() <= 1 {
      return Err(ParseError::ExpressionError("no value".into()));
    }

    match key.trim() {
      "geometry_shader_max_vertices" => {
        let value = (&value[1..]).trim();
        let max_vertices = value.parse().map_err(|_| ParseError::ExpressionError(format!("unable to parse geometry_shader_max_vertices, found {}", value)))?;
        Ok(PipelineAttribute::GeometryShaderMaxVertices(max_vertices))
      },
      "geometry_shader_invokations" => {
        let value = (&value[1..]).trim();
        let invokations = value.parse().map_err(|_| ParseError::ExpressionError(format!("unable to parse geometry_shader_invokation, found {}", value)))?;
        Ok(PipelineAttribute::GeometryShaderInvokations(invokations))
      },
      _ => Err(ParseError::ExpressionError(format!("expected a valid pipeline attribute, found {}", key)))
    }
  }
}

impl FromStr for GeometryYieldExpression {
  type Err = ParseError;

  fn from_str(s: &str) -> Result<Self, Self::Err> {
    match s {
      "yieldprim" => Ok(GeometryYieldExpression::YieldPrimitive),
      _ if s.starts_with("yield ") && s.len() > "yield ".len() => {
        let expr = &s["yield ".len() ..];
        Ok(GeometryYieldExpression::YieldFoldVertex(expr.into()))
      },
      _ => {
        Err(ParseError::ExpressionError(format!("expected yield, found {}", s)))
      }
    }
  }
}

// FIXME: move that into a specific module
#[test]
fn parse_pipeline_attribute() {
  let geo_max_vertices  = "geometry_shader_max_vertices = 3";
  let geo_max_vertices1 = "geometry_shader_max_vertices =3";
  let geo_max_vertices2 = "geometry_shader_max_vertices =";
  let geo_max_vertices3 = "geometry_shader_max_vertices = ";
  let geo_invokations  = "geometry_shader_invokations = 1";
  let geo_invokations1 = "geometry_shader_invokations =1";
  let geo_invokations2 = "geometry_shader_invokations =";
  let geo_invokations3 = "geometry_shader_invokations = ";

  assert_eq!(geo_max_vertices.parse::<PipelineAttribute>(), Ok(PipelineAttribute::GeometryShaderMaxVertices(3)));
  assert_eq!(geo_max_vertices1.parse::<PipelineAttribute>(), Ok(PipelineAttribute::GeometryShaderMaxVertices(3)));
  assert!(geo_max_vertices2.parse::<PipelineAttribute>().is_err());
  assert!(geo_max_vertices3.parse::<PipelineAttribute>().is_err());
  assert_eq!(geo_invokations.parse::<PipelineAttribute>(), Ok(PipelineAttribute::GeometryShaderInvokations(1)));
  assert_eq!(geo_invokations1.parse::<PipelineAttribute>(), Ok(PipelineAttribute::GeometryShaderInvokations(1)));
  assert!(geo_invokations2.parse::<PipelineAttribute>().is_err());
  assert!(geo_invokations3.parse::<PipelineAttribute>().is_err());
}

// FIXME: move that into a specific module
#[test]
fn parse_geometry_yield_expression() {
  let yieldprim = "yieldprim";
  let yield_1 = "yield FoldVertex(vertex[i].color)";

  assert_eq!(yieldprim.parse::<GeometryYieldExpression>(), Ok(GeometryYieldExpression::YieldPrimitive));
  assert_eq!(yield_1.parse::<GeometryYieldExpression>(), Ok(GeometryYieldExpression::YieldFoldVertex("FoldVertex(vertex[i].color)".into())));
}
<|endoftext|>


<commit_before>//! Code to trace out a single lane, collecting information into the
//! lane table as we go.

use collections::Set;
use grammar::repr::*;
use lr1::core::*;
use lr1::first::FirstSets;
use lr1::lookahead::*;
use lr1::state_graph::StateGraph;

use super::table::{ConflictIndex, LaneTable};

pub struct LaneTracer<'trace, 'grammar: 'trace> {
    states: &'trace [LR0State<'grammar>],
    first_sets: FirstSets,
    state_graph: StateGraph,
    table: LaneTable<'grammar>,
}

impl<'trace, 'grammar> LaneTracer<'trace, 'grammar> {
    pub fn new(grammar: &'grammar Grammar,
               states: &'trace [LR0State<'grammar>],
               conflicts: usize)
               -> Self {
        LaneTracer {
            states: states,
            first_sets: FirstSets::new(grammar),
            state_graph: StateGraph::new(states),
            table: LaneTable::new(grammar, conflicts),
        }
    }

    pub fn into_table(self) -> LaneTable<'grammar> {
        self.table
    }

    pub fn start_trace(&mut self,
                       state: StateIndex,
                       conflict: ConflictIndex,
                       item: LR0Item<'grammar>) {
        let mut visited_set = Set::default();

        // if the conflict item is a "shift" item, then the context
        // is always the terminal to shift (and conflicts only arise
        // around shifting terminal, so it must be a terminal)
        match item.shift_symbol() {
            Some((Symbol::Terminal(term), _)) => {
                let mut token_set = TokenSet::new();
                token_set.insert(Token::Terminal(term));
                self.table.add_lookahead(state, conflict, &token_set);
            }

            Some((Symbol::Nonterminal(_), _)) => {
                panic!("invalid conflict item `{:?}`: shifts nonterminal",
                       item);
            }

            None => {
                self.continue_trace(state, conflict, item, &mut visited_set);
            }
        }
    }

    fn continue_trace(&mut self,
                      state: StateIndex,
                      conflict: ConflictIndex,
                      item: LR0Item<'grammar>,
                      visited: &mut Set<(StateIndex, LR0Item<'grammar>)>) {
        if !visited.insert((state, item)) {
            return;
        }

        if item.index > 0 {
            // This item was reached by shifting some symbol.  We need
            // to unshift that symbol, which means we walk backwards
            // to predecessors of `state` in the state graph.
            //
            // Example:
            //
            //     X = ...p T (*) ...s
            //
            // Here we would be "unshifting" T, which means we will
            // walk to predecessors of the current state that were
            // reached by shifting T. Those predecessors will contain
            // an item like `X = ...p (*) T ...s`, which we will then
            // process in turn.
            let shifted_symbol = item.production.symbols[item.index - 1];
            let unshifted_item = Item { index: item.index - 1, ..item };
            let predecessors = self.state_graph.predecessors(state, shifted_symbol);
            for predecessor in predecessors {
                self.table.add_successor(predecessor, state);
                self.continue_trace(predecessor, conflict, unshifted_item, visited);
            }
            return;
        }

        // Either: we are in the start state, or this item was
        // reached by an epsilon transition. We have to
        // "unepsilon", which means that we search elsewhere in
        // the state for where the epsilon transition could have
        // come from.
        //
        // Example:
        //
        //     X = (*) ...
        //
        // We will search for other items in the same state like:
        //
        //     Y = ...p (*) X ...s
        //
        // We can then insert `FIRST(...s)` as lookahead for
        // `conflict`. If `...s` may derive epsilon, though, we
        // have to recurse and search with the previous item.

        let state_items = &self.states[state.0].items.vec;
        let nonterminal = item.production.nonterminal;
        for &pred_item in state_items.iter()
                                     .filter(|i| i.can_shift_nonterminal(nonterminal)) {
            let symbol_sets = pred_item.symbol_sets();
            let mut first = self.first_sets.first0(symbol_sets.suffix);
            let derives_epsilon = first.take_eof();
            self.table.add_lookahead(state, conflict, &first);
            if derives_epsilon {
                self.continue_trace(state, conflict, pred_item, visited);
            }
        }
    }
}
<commit_msg>make lane table construction generic in lookahead L<commit_after>//! Code to trace out a single lane, collecting information into the
//! lane table as we go.

use collections::Set;
use grammar::repr::*;
use lr1::core::*;
use lr1::first::FirstSets;
use lr1::lookahead::*;
use lr1::state_graph::StateGraph;

use super::table::{ConflictIndex, LaneTable};

pub struct LaneTracer<'trace, 'grammar: 'trace, L: Lookahead + 'trace> {
    states: &'trace [State<'grammar, L>],
    first_sets: FirstSets,
    state_graph: StateGraph,
    table: LaneTable<'grammar>,
}

impl<'trace, 'grammar, L: Lookahead> LaneTracer<'trace, 'grammar, L> {
    pub fn new(grammar: &'grammar Grammar,
               states: &'trace [State<'grammar, L>],
               conflicts: usize)
               -> Self {
        LaneTracer {
            states: states,
            first_sets: FirstSets::new(grammar),
            state_graph: StateGraph::new(states),
            table: LaneTable::new(grammar, conflicts),
        }
    }

    pub fn into_table(self) -> LaneTable<'grammar> {
        self.table
    }

    pub fn start_trace(&mut self,
                       state: StateIndex,
                       conflict: ConflictIndex,
                       item: Item<'grammar, L>) {
        let mut visited_set = Set::default();

        // if the conflict item is a "shift" item, then the context
        // is always the terminal to shift (and conflicts only arise
        // around shifting terminal, so it must be a terminal)
        match item.shift_symbol() {
            Some((Symbol::Terminal(term), _)) => {
                let mut token_set = TokenSet::new();
                token_set.insert(Token::Terminal(term));
                self.table.add_lookahead(state, conflict, &token_set);
            }

            Some((Symbol::Nonterminal(_), _)) => {
                panic!("invalid conflict item `{:?}`: shifts nonterminal",
                       item);
            }

            None => {
                self.continue_trace(state, conflict, item, &mut visited_set);
            }
        }
    }

    fn continue_trace(&mut self,
                      state: StateIndex,
                      conflict: ConflictIndex,
                      item: Item<'grammar, L>,
                      visited: &mut Set<(StateIndex, Item<'grammar, L>)>) {
        if !visited.insert((state, item.clone())) {
            return;
        }

        if item.index > 0 {
            // This item was reached by shifting some symbol.  We need
            // to unshift that symbol, which means we walk backwards
            // to predecessors of `state` in the state graph.
            //
            // Example:
            //
            //     X = ...p T (*) ...s
            //
            // Here we would be "unshifting" T, which means we will
            // walk to predecessors of the current state that were
            // reached by shifting T. Those predecessors will contain
            // an item like `X = ...p (*) T ...s`, which we will then
            // process in turn.
            let shifted_symbol = item.production.symbols[item.index - 1];
            let unshifted_item = Item { index: item.index - 1, ..item };
            let predecessors = self.state_graph.predecessors(state, shifted_symbol);
            for predecessor in predecessors {
                self.table.add_successor(predecessor, state);
                self.continue_trace(predecessor, conflict, unshifted_item.clone(), visited);
            }
            return;
        }

        // Either: we are in the start state, or this item was
        // reached by an epsilon transition. We have to
        // "unepsilon", which means that we search elsewhere in
        // the state for where the epsilon transition could have
        // come from.
        //
        // Example:
        //
        //     X = (*) ...
        //
        // We will search for other items in the same state like:
        //
        //     Y = ...p (*) X ...s
        //
        // We can then insert `FIRST(...s)` as lookahead for
        // `conflict`. If `...s` may derive epsilon, though, we
        // have to recurse and search with the previous item.

        let state_items = &self.states[state.0].items.vec;
        let nonterminal = item.production.nonterminal;
        for pred_item in state_items.iter()
                                    .filter(|i| i.can_shift_nonterminal(nonterminal)) {
            let symbol_sets = pred_item.symbol_sets();
            let mut first = self.first_sets.first0(symbol_sets.suffix);
            let derives_epsilon = first.take_eof();
            self.table.add_lookahead(state, conflict, &first);
            if derives_epsilon {
                self.continue_trace(state, conflict, pred_item.clone(), visited);
            }
        }
    }
}
<|endoftext|>