Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: azonenberg/openfpga
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: b8dcb589dfdc
Choose a base ref
...
head repository: azonenberg/openfpga
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: 6049fa6f3971
Choose a head ref
  • 16 commits
  • 9 files changed
  • 1 contributor

Commits on Jun 8, 2017

  1. Copy the full SHA
    ae2cf4f View commit details
  2. Copy the full SHA
    368ae67 View commit details
  3. Copy the full SHA
    ea22558 View commit details
  4. Copy the full SHA
    d5c27d1 View commit details
  5. Copy the full SHA
    b5dcbb7 View commit details
  6. xc2bit: Rename XC2MCOBufMode to XC2IOBOBufMode

    This is more related to the IO pin rather than the macrocell
    ArcaneNibble committed Jun 8, 2017
    Copy the full SHA
    01a51f5 View commit details
  7. xc2bit: Move special product terms back to FB again

    They belong with the function block, not a particular macrocell
    ArcaneNibble committed Jun 8, 2017
    Copy the full SHA
    f228f3d View commit details
  8. xc2bit: Start documenting macrocell stuff; rename XC2MCFF -> XC2MCReg

    A latch isn't a flip-flop, so this new name should be more appropriate
    ArcaneNibble committed Jun 8, 2017
    Copy the full SHA
    0ba5b51 View commit details
  9. Copy the full SHA
    990b1c3 View commit details
  10. xc2bit: More macrocell documentation and another rename

    This struct contains information about the entire macrocell, not just
    the register.
    ArcaneNibble committed Jun 8, 2017
    Copy the full SHA
    d94e125 View commit details
  11. Copy the full SHA
    3101eb9 View commit details
  12. Copy the full SHA
    227e526 View commit details
  13. Copy the full SHA
    034e57b View commit details
  14. Copy the full SHA
    362ff0b View commit details
  15. Copy the full SHA
    36d0d5e View commit details
  16. Copy the full SHA
    6049fa6 View commit details
Showing with 573 additions and 364 deletions.
  1. +6 −0 src/xc2bit/Cargo.toml
  2. +98 −55 src/xc2bit/src/bitstream.rs
  3. +49 −42 src/xc2bit/src/fb.rs
  4. +225 −0 src/xc2bit/src/iob.rs
  5. +3 −3 src/xc2bit/src/jed.rs
  6. +36 −6 src/xc2bit/src/lib.rs
  7. +104 −234 src/xc2bit/src/mc.rs
  8. +34 −17 src/xc2bit/src/pla.rs
  9. +18 −7 src/xc2bit/src/zia.rs
6 changes: 6 additions & 0 deletions src/xc2bit/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
[package]
name = "xc2bit"
version = "0.0.1"
authors = ["Robert Ou <rqou@robertou.com>"]
license = "BSD-2-Clause"
description = "A library for working with Xilinx Coolrunner-II bitstreams"
repository = "https://github.com/azonenberg/openfpga/tree/master/src/xc2bit"
keywords = ["coolrunner", "cpld", "xilinx", "bitstream", "eda"]
categories = ["command-line-utilities", "parser-implementations"]

[profile.release]
lto = true
153 changes: 98 additions & 55 deletions src/xc2bit/src/bitstream.rs
Original file line number Diff line number Diff line change
@@ -29,23 +29,26 @@ use std::io::Write;

use *;
use fb::{read_32_fb_logical};
use mc::{read_32_iob_logical, read_32_extra_ibuf_logical, fb_ff_num_to_iob_num_32};
use iob::{read_32_iob_logical, read_32_extra_ibuf_logical};
use zia::{encode_32_zia_choice};

/// Toplevel struct representing an entire Coolrunner-II bitstream
pub struct XC2Bitstream {
pub speed_grade: String,
pub package: String,
pub bits: XC2BitstreamBits,
}

impl XC2Bitstream {
/// Dump a human-readable explanation of the bitstream to the given `writer` object.
pub fn dump_human_readable(&self, writer: &mut Write) {
write!(writer, "xc2bit dump\n").unwrap();
write!(writer, "device speed grade: {}\n", self.speed_grade).unwrap();
write!(writer, "device package: {}\n", self.package).unwrap();
self.bits.dump_human_readable(writer);
}

/// Write a .jed representation of the bitstream to the given `writer` object.
pub fn write_jed(&self, writer: &mut Write) {
write!(writer, ".JED fuse map written by xc2bit\n").unwrap();
write!(writer, "https://github.com/azonenberg/openfpga\n\n").unwrap();
@@ -67,6 +70,7 @@ impl XC2Bitstream {
write!(writer, "\x030000\n").unwrap();
}

/// Construct a new blank bitstream of the given part
pub fn blank_bitstream(device: &str, speed_grade: &str, package: &str) -> Result<XC2Bitstream, &'static str> {
// TODO: Validate speed_grade and package

@@ -76,7 +80,7 @@ impl XC2Bitstream {
speed_grade: speed_grade.to_owned(),
package: package.to_owned(),
bits: XC2BitstreamBits::XC2C32 {
fb: [XC2BistreamFB::default(); 2],
fb: [XC2BitstreamFB::default(); 2],
iobs: [XC2MCSmallIOB::default(); 32],
inpin: XC2ExtraIBuf::default(),
global_nets: XC2GlobalNets::default(),
@@ -90,7 +94,7 @@ impl XC2Bitstream {
speed_grade: speed_grade.to_owned(),
package: package.to_owned(),
bits: XC2BitstreamBits::XC2C32A {
fb: [XC2BistreamFB::default(); 2],
fb: [XC2BitstreamFB::default(); 2],
iobs: [XC2MCSmallIOB::default(); 32],
inpin: XC2ExtraIBuf::default(),
global_nets: XC2GlobalNets::default(),
@@ -106,19 +110,31 @@ impl XC2Bitstream {
}
}

/// Represents the configuration of the global nets. Coolrunner-II parts have various global control signals that have
/// dedicated low-skew paths.
pub struct XC2GlobalNets {
/// Controls whether the three global clock nets are enabled or not
pub gck_enable: [bool; 3],
/// Controls whether the global set/reset net is enabled or not
pub gsr_enable: bool,
// false = active low, true = active high
/// Controls the polarity of the global set/reset signal
///
/// `false` = active low, `true` = active high
pub gsr_invert: bool,
/// Controls whether the four global tristate nets are enabled or not
pub gts_enable: [bool; 4],
// false = used as T, true = used as !T
/// Controls the polarity of the global tristate signal
///
/// `false` = used as T, `true` = used as !T
pub gts_invert: [bool; 4],
// false = keeper, true = pull-up
/// Controls the mode of the global termination
///
/// `false` = keeper, `true` = pull-up
pub global_pu: bool,
}

impl Default for XC2GlobalNets {
/// Returns a "default" global net configuration which has everything disabled.
fn default() -> XC2GlobalNets {
XC2GlobalNets {
gck_enable: [false; 3],
@@ -132,6 +148,7 @@ impl Default for XC2GlobalNets {
}

impl XC2GlobalNets {
/// Dump a human-readable explanation of the global net configuration to the given `writer` object.
pub fn dump_human_readable(&self, writer: &mut Write) {
write!(writer, "\n").unwrap();
write!(writer, "GCK0 {}\n", if self.gck_enable[0] {"enabled"} else {"disabled"}).unwrap();
@@ -159,6 +176,7 @@ impl XC2GlobalNets {
}
}

/// Internal function to read the global nets from a 32-macrocell part
fn read_32_global_nets_logical(fuses: &[bool]) -> XC2GlobalNets {
XC2GlobalNets {
gck_enable: [
@@ -184,30 +202,48 @@ fn read_32_global_nets_logical(fuses: &[bool]) -> XC2GlobalNets {
}
}

/// The actual bitstream bits for each possible Coolrunner-II part
pub enum XC2BitstreamBits {
XC2C32 {
fb: [XC2BistreamFB; 2],
fb: [XC2BitstreamFB; 2],
iobs: [XC2MCSmallIOB; 32],
inpin: XC2ExtraIBuf,
global_nets: XC2GlobalNets,
// false = low, true = high
/// Voltage level control
///
/// `false` = low, `true` = high
ivoltage: bool,
/// Voltage level control
///
/// `false` = low, `true` = high
ovoltage: bool,
},
XC2C32A {
fb: [XC2BistreamFB; 2],
fb: [XC2BitstreamFB; 2],
iobs: [XC2MCSmallIOB; 32],
inpin: XC2ExtraIBuf,
global_nets: XC2GlobalNets,
// false = low, true = high
/// Legacy voltage level control, should almost always be set to `false`
///
/// `false` = low, `true` = high
legacy_ivoltage: bool,
/// Legacy voltage level control, should almost always be set to `false`
///
/// `false` = low, `true` = high
legacy_ovoltage: bool,
/// Voltage level control for each I/O bank
///
/// `false` = low, `true` = high
ivoltage: [bool; 2],
/// Voltage level control for each I/O bank
///
/// `false` = low, `true` = high
ovoltage: [bool; 2],
},
}

impl XC2BitstreamBits {
/// Dump a human-readable explanation of the bitstream to the given `writer` object.
pub fn dump_human_readable(&self, writer: &mut Write) {
match self {
&XC2BitstreamBits::XC2C32 {
@@ -252,6 +288,7 @@ impl XC2BitstreamBits {
}
}

/// Write a .jed representation of the bitstream to the given `writer` object.
pub fn write_jed(&self, writer: &mut Write) {
match self {
&XC2BitstreamBits::XC2C32 {
@@ -266,7 +303,7 @@ impl XC2BitstreamBits {
let fuse_base = if fb_i == 0 {0} else {6128};

// ZIA
for i in 0..40 {
for i in 0..INPUTS_PER_ANDTERM {
write!(writer, "L{:06} ", fuse_base + i * 8).unwrap();
let zia_choice_bits =
encode_32_zia_choice(i as u32, fb[fb_i].zia_bits[i].selected)
@@ -285,9 +322,10 @@ impl XC2BitstreamBits {
write!(writer, "\n").unwrap();

// AND terms
for i in 0..56 {
write!(writer, "L{:06} ", fuse_base + 8 * 40 + i * 80).unwrap();
for j in 0..40 {
for i in 0..ANDTERMS_PER_FB {
write!(writer, "L{:06} ",
fuse_base + 8 * INPUTS_PER_ANDTERM + i * INPUTS_PER_ANDTERM * 2).unwrap();
for j in 0..INPUTS_PER_ANDTERM {
if fb[fb_i].and_terms[i].input[j] {
write!(writer, "0").unwrap();
} else {
@@ -304,9 +342,11 @@ impl XC2BitstreamBits {
write!(writer, "\n").unwrap();

// OR terms
for i in 0..56 {
write!(writer, "L{:06} ", fuse_base + 8 * 40 + 56 * 80 + i * 16).unwrap();
for j in 0..16 {
for i in 0..ANDTERMS_PER_FB {
write!(writer, "L{:06} ",
fuse_base + 8 * INPUTS_PER_ANDTERM +
ANDTERMS_PER_FB * INPUTS_PER_ANDTERM * 2 + i * MCS_PER_FB).unwrap();
for j in 0..MCS_PER_FB {
if fb[fb_i].or_terms[j].input[i] {
write!(writer, "0").unwrap();
} else {
@@ -318,53 +358,55 @@ impl XC2BitstreamBits {
write!(writer, "\n").unwrap();

// Macrocells
for i in 0..16 {
write!(writer, "L{:06} ", fuse_base + 8 * 40 + 56 * 80 + 56 * 16 + i * 27).unwrap();
for i in 0..MCS_PER_FB {
write!(writer, "L{:06} ",
fuse_base + 8 * INPUTS_PER_ANDTERM +
ANDTERMS_PER_FB * INPUTS_PER_ANDTERM * 2 + ANDTERMS_PER_FB * MCS_PER_FB + i * 27).unwrap();

let iob = fb_ff_num_to_iob_num_32(fb_i as u32, i as u32).unwrap() as usize;

// aclk
write!(writer, "{}", match fb[fb_i].ffs[i].clk_src {
XC2MCFFClkSrc::CTC => "1",
XC2MCRegClkSrc::CTC => "1",
_ => "0",
}).unwrap();

// clkop
write!(writer, "{}", if fb[fb_i].ffs[i].falling_edge {"1"} else {"0"}).unwrap();
write!(writer, "{}", if fb[fb_i].ffs[i].clk_invert_pol {"1"} else {"0"}).unwrap();

// clk
write!(writer, "{}", match fb[fb_i].ffs[i].clk_src {
XC2MCFFClkSrc::GCK0 => "00",
XC2MCFFClkSrc::GCK1 => "01",
XC2MCFFClkSrc::GCK2 => "10",
XC2MCFFClkSrc::PTC | XC2MCFFClkSrc::CTC => "11",
XC2MCRegClkSrc::GCK0 => "00",
XC2MCRegClkSrc::GCK1 => "01",
XC2MCRegClkSrc::GCK2 => "10",
XC2MCRegClkSrc::PTC | XC2MCRegClkSrc::CTC => "11",
}).unwrap();

// clkfreq
write!(writer, "{}", if fb[fb_i].ffs[i].is_ddr {"1"} else {"0"}).unwrap();

// r
write!(writer, "{}", match fb[fb_i].ffs[i].r_src {
XC2MCFFResetSrc::PTA => "00",
XC2MCFFResetSrc::GSR => "01",
XC2MCFFResetSrc::CTR => "10",
XC2MCFFResetSrc::Disabled => "11",
XC2MCRegResetSrc::PTA => "00",
XC2MCRegResetSrc::GSR => "01",
XC2MCRegResetSrc::CTR => "10",
XC2MCRegResetSrc::Disabled => "11",
}).unwrap();

// p
write!(writer, "{}", match fb[fb_i].ffs[i].s_src {
XC2MCFFSetSrc::PTA => "00",
XC2MCFFSetSrc::GSR => "01",
XC2MCFFSetSrc::CTS => "10",
XC2MCFFSetSrc::Disabled => "11",
XC2MCRegSetSrc::PTA => "00",
XC2MCRegSetSrc::GSR => "01",
XC2MCRegSetSrc::CTS => "10",
XC2MCRegSetSrc::Disabled => "11",
}).unwrap();

// regmod
write!(writer, "{}", match fb[fb_i].ffs[i].ff_mode {
XC2MCFFMode::DFF => "00",
XC2MCFFMode::LATCH => "01",
XC2MCFFMode::TFF => "10",
XC2MCFFMode::DFFCE => "11",
write!(writer, "{}", match fb[fb_i].ffs[i].reg_mode {
XC2MCRegMode::DFF => "00",
XC2MCRegMode::LATCH => "01",
XC2MCRegMode::TFF => "10",
XC2MCRegMode::DFFCE => "11",
}).unwrap();

// inz
@@ -400,16 +442,16 @@ impl XC2BitstreamBits {

// oe
write!(writer, "{}", match iobs[iob].obuf_mode {
XC2MCOBufMode::PushPull => "0000",
XC2MCOBufMode::OpenDrain => "0001",
XC2MCOBufMode::TriStateGTS1 => "0010",
XC2MCOBufMode::TriStatePTB => "0100",
XC2MCOBufMode::TriStateGTS3 => "0110",
XC2MCOBufMode::TriStateCTE => "1000",
XC2MCOBufMode::TriStateGTS2 => "1010",
XC2MCOBufMode::TriStateGTS0 => "1100",
XC2MCOBufMode::CGND => "1110",
XC2MCOBufMode::Disabled => "1111",
XC2IOBOBufMode::PushPull => "0000",
XC2IOBOBufMode::OpenDrain => "0001",
XC2IOBOBufMode::TriStateGTS1 => "0010",
XC2IOBOBufMode::TriStatePTB => "0100",
XC2IOBOBufMode::TriStateGTS3 => "0110",
XC2IOBOBufMode::TriStateCTE => "1000",
XC2IOBOBufMode::TriStateGTS2 => "1010",
XC2IOBOBufMode::TriStateGTS0 => "1100",
XC2IOBOBufMode::CGND => "1110",
XC2IOBOBufMode::Disabled => "1111",
}).unwrap();

// tm
@@ -470,8 +512,9 @@ impl XC2BitstreamBits {
}
}

/// Internal function for parsing an XC2C32 bitstream
pub fn read_32_bitstream_logical(fuses: &[bool]) -> Result<XC2BitstreamBits, &'static str> {
let mut fb = [XC2BistreamFB::default(); 2];
let mut fb = [XC2BitstreamFB::default(); 2];
for i in 0..fb.len() {
let res = read_32_fb_logical(fuses, i);
if let Err(err) = res {
@@ -482,12 +525,12 @@ pub fn read_32_bitstream_logical(fuses: &[bool]) -> Result<XC2BitstreamBits, &'s

let mut iobs = [XC2MCSmallIOB::default(); 32];
for i in 0..iobs.len() {
let base_fuse = if i < 16 {
let base_fuse = if i < MCS_PER_FB {
5696
} else {
11824
};
let res = read_32_iob_logical(fuses, base_fuse, i % 16);
let res = read_32_iob_logical(fuses, base_fuse, i % MCS_PER_FB);
if let Err(err) = res {
return Err(err);
}
@@ -508,9 +551,9 @@ pub fn read_32_bitstream_logical(fuses: &[bool]) -> Result<XC2BitstreamBits, &'s
})
}


/// Internal function for parsing an XC2C32A bitstream
pub fn read_32a_bitstream_logical(fuses: &[bool]) -> Result<XC2BitstreamBits, &'static str> {
let mut fb = [XC2BistreamFB::default(); 2];
let mut fb = [XC2BitstreamFB::default(); 2];
for i in 0..fb.len() {
let res = read_32_fb_logical(fuses, i);
if let Err(err) = res {
@@ -521,12 +564,12 @@ pub fn read_32a_bitstream_logical(fuses: &[bool]) -> Result<XC2BitstreamBits, &'

let mut iobs = [XC2MCSmallIOB::default(); 32];
for i in 0..iobs.len() {
let base_fuse = if i < 16 {
let base_fuse = if i < MCS_PER_FB {
5696
} else {
11824
};
let res = read_32_iob_logical(fuses, base_fuse, i % 16);
let res = read_32_iob_logical(fuses, base_fuse, i % MCS_PER_FB);
if let Err(err) = res {
return Err(err);
}
91 changes: 49 additions & 42 deletions src/xc2bit/src/fb.rs
Original file line number Diff line number Diff line change
@@ -23,48 +23,55 @@ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

// Function block
//! Contains functions pertaining to function blocks
use std::io::Write;

use *;
use pla::{read_and_term_logical, read_or_term_logical};
use mc::{read_32_ff_logical, iob_num_to_fb_ff_num_32};
use mc::{read_32_ff_logical};
use zia::{read_32_zia_fb_row_logical};

/// Represents a collection of all the parts that make up one function block
#[derive(Copy)]
pub struct XC2BistreamFB {
pub and_terms: [XC2PLAAndTerm; 56],
pub or_terms: [XC2PLAOrTerm; 16],
pub zia_bits: [XC2ZIARowPiece; 40],
pub ffs: [XC2MCFF; 16],
pub struct XC2BitstreamFB {
/// The AND terms of the PLA part of the function block
pub and_terms: [XC2PLAAndTerm; ANDTERMS_PER_FB],
/// The OR terms of the PLA part of the function block
pub or_terms: [XC2PLAOrTerm; MCS_PER_FB],
/// The inputs to the function block from the ZIA
pub zia_bits: [XC2ZIARowPiece; INPUTS_PER_ANDTERM],
/// The macrocells of the function block
pub ffs: [XC2Macrocell; MCS_PER_FB],
}

impl Clone for XC2BistreamFB {
fn clone(&self) -> XC2BistreamFB {*self}
impl Clone for XC2BitstreamFB {
fn clone(&self) -> XC2BitstreamFB {*self}
}

impl Default for XC2BistreamFB {
fn default() -> XC2BistreamFB {
XC2BistreamFB {
and_terms: [XC2PLAAndTerm::default(); 56],
or_terms: [XC2PLAOrTerm::default(); 16],
zia_bits: [XC2ZIARowPiece::default(); 40],
ffs: [XC2MCFF::default(); 16],
impl Default for XC2BitstreamFB {
fn default() -> XC2BitstreamFB {
XC2BitstreamFB {
and_terms: [XC2PLAAndTerm::default(); ANDTERMS_PER_FB],
or_terms: [XC2PLAOrTerm::default(); MCS_PER_FB],
zia_bits: [XC2ZIARowPiece::default(); INPUTS_PER_ANDTERM],
ffs: [XC2Macrocell::default(); MCS_PER_FB],
}
}
}

impl XC2BistreamFB {
impl XC2BitstreamFB {
/// Dump a human-readable explanation of the settings for this pin to the given `writer` object.
/// `fb` must be the index of this function block.
pub fn dump_human_readable(&self, fb: u32, writer: &mut Write) {
for i in 0..16 {
for i in 0..MCS_PER_FB {
self.ffs[i].dump_human_readable(fb, i as u32, writer);
}

// FIXME: Move this somewhere else?
write!(writer, "\n").unwrap();
write!(writer, "ZIA inputs for FB{}\n", fb + 1).unwrap();
for i in 0..40 {
for i in 0..INPUTS_PER_ANDTERM {
write!(writer, "{:2}: ", i).unwrap();
match self.zia_bits[i].selected {
XC2ZIAInput::Zero => write!(writer, "0\n").unwrap(),
@@ -87,9 +94,9 @@ impl XC2BistreamFB {
21|~21| 22|~22| 23|~23| 24|~24| 25|~25| 26|~26| 27|~27| 28|~28| 29|~29| 30|~30| \
31|~31| 32|~32| 33|~33| 34|~34| 35|~35| 36|~36| 37|~37| 38|~38| 39|~39\
\n").unwrap();
for i in 0..56 {
for i in 0..ANDTERMS_PER_FB {
write!(writer, "{:2}:", i).unwrap();
for j in 0..40 {
for j in 0..INPUTS_PER_ANDTERM {
if self.and_terms[i].input[j] {
write!(writer, "|XXX").unwrap();
} else {
@@ -111,9 +118,9 @@ impl XC2BistreamFB {
write!(writer, " | 0| 1| 2| 3| 4| 5| 6| 7| 8| 9|10|11|12|13|14|15|16|17|18|19|20|\
21|22|23|24|25|26|27|28|29|30|31|32|33|34|35|36|37|38|39|40|\
41|42|43|44|45|46|47|48|49|50|51|52|53|54|55\n").unwrap();
for i in 0..16 {
for i in 0..MCS_PER_FB {
write!(writer, "{:2}:", i).unwrap();
for j in 0..56 {
for j in 0..ANDTERMS_PER_FB {
if self.or_terms[i].input[j] {
write!(writer, "|XX").unwrap();
} else {
@@ -125,9 +132,9 @@ impl XC2BistreamFB {
}
}


pub fn read_32_fb_logical(fuses: &[bool], block_idx: usize) -> Result<XC2BistreamFB, &'static str> {
let mut and_terms = [XC2PLAAndTerm::default(); 56];
/// Internal function that reads a function block for 32-macrocell devices
pub fn read_32_fb_logical(fuses: &[bool], block_idx: usize) -> Result<XC2BitstreamFB, &'static str> {
let mut and_terms = [XC2PLAAndTerm::default(); ANDTERMS_PER_FB];
let and_block_idx = match block_idx {
0 => 320,
1 => 6448,
@@ -137,7 +144,7 @@ pub fn read_32_fb_logical(fuses: &[bool], block_idx: usize) -> Result<XC2Bistrea
and_terms[i] = read_and_term_logical(fuses, and_block_idx, i);
}

let mut or_terms = [XC2PLAOrTerm::default(); 16];
let mut or_terms = [XC2PLAOrTerm::default(); MCS_PER_FB];
let or_block_idx = match block_idx {
0 => 4800,
1 => 10928,
@@ -147,7 +154,7 @@ pub fn read_32_fb_logical(fuses: &[bool], block_idx: usize) -> Result<XC2Bistrea
or_terms[i] = read_or_term_logical(fuses, or_block_idx, i);
}

let mut zia_bits = [XC2ZIARowPiece::default(); 40];
let mut zia_bits = [XC2ZIARowPiece::default(); INPUTS_PER_ANDTERM];
let zia_block_idx = match block_idx {
0 => 0,
1 => 6128,
@@ -161,7 +168,7 @@ pub fn read_32_fb_logical(fuses: &[bool], block_idx: usize) -> Result<XC2Bistrea
zia_bits[i] = result.unwrap();
}

let mut ff_bits = [XC2MCFF::default(); 16];
let mut ff_bits = [XC2Macrocell::default(); MCS_PER_FB];
let ff_block_idx = match block_idx {
0 => 5696,
1 => 11824,
@@ -172,7 +179,7 @@ pub fn read_32_fb_logical(fuses: &[bool], block_idx: usize) -> Result<XC2Bistrea
}


Ok(XC2BistreamFB {
Ok(XC2BitstreamFB {
and_terms: and_terms,
or_terms: or_terms,
zia_bits: zia_bits,
@@ -181,30 +188,30 @@ pub fn read_32_fb_logical(fuses: &[bool], block_idx: usize) -> Result<XC2Bistrea
}

// TODO: This is the same across all sizes, right?
pub fn get_ctc() -> u32 {
4
}

pub fn get_ctr() -> u32 {
5
}
/// The index of the special CTC product term
pub const CTC: u32 = 4;

pub fn get_cts() -> u32 {
6
}
/// The index of the special CTR product term
pub const CTR: u32 = 5;

pub fn get_cte() -> u32 {
7
}
/// The index of the special CTS product term
pub const CTS: u32 = 6;

/// The index of the special CTE product term
pub const CTE: u32 = 7;

/// Returns the special PTA product term given a macrocell index
pub fn get_pta(mc: u32) -> u32 {
3 * mc + 8
}

/// Returns the special PTB product term given a macrocell index
pub fn get_ptb(mc: u32) -> u32 {
3 * mc + 9
}

/// Returns the special PTC product term given a macrocell index
pub fn get_ptc(mc: u32) -> u32 {
3 * mc + 10
}
225 changes: 225 additions & 0 deletions src/xc2bit/src/iob.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,225 @@
/*
Copyright (c) 2016-2017, Robert Ou <rqou@robertou.com> and contributors
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

//! Contains functions pertaining to the I/O pins
use std::io::Write;

use *;

/// Mux selection for the ZIA input from this I/O pin's input. The ZIA input can be chosen to come from either the
/// input pin directly or from the output of the register in the macrocell corresponding to this I/O pin. The latter
/// is used to allow for buried combinatorial feedback in a macrocell without "wasting" the register.
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
pub enum XC2IOBZIAMode {
Disabled,
PAD,
REG,
}

/// Mode selection for the I/O pin's output buffer. See the Xilinx Coolrunner-II documentation for more information.
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
pub enum XC2IOBOBufMode {
Disabled,
PushPull,
OpenDrain,
TriStateGTS0,
TriStateGTS1,
TriStateGTS2,
TriStateGTS3,
TriStatePTB,
TriStateCTE,
CGND,
}

/// Represents an I/O pin on "small" (32 and 64 macrocell) devices.
#[derive(Copy, Clone)]
pub struct XC2MCSmallIOB {
/// Mux selection for the ZIA input for this pin
pub zia_mode: XC2IOBZIAMode,
/// Whether the Schmitt trigger is being used on this pin's input
pub schmitt_trigger: bool,
/// Selects the source used to drive this pin's output (if the output is enabled).
/// `false` selects the XOR gate in the macrocell (combinatorial output), and `true` selects the register output
/// (registered output).
pub obuf_uses_ff: bool,
/// Selects the output mode for this pin
pub obuf_mode: XC2IOBOBufMode,
/// Selects if the global termination (bus hold or pull-up) is enabled on this pin
pub termination_enabled: bool,
/// Selects if fast slew rate is used on this pin
pub slew_is_fast: bool,
}

impl Default for XC2MCSmallIOB {
/// Returns a "default" I/O pin configuration. The default state is for the output and the input into the ZIA
/// to be disabled.
// FIXME: Do the other defaults come from the particular way I invoked the Xilinx tools??
fn default() -> XC2MCSmallIOB {
XC2MCSmallIOB {
zia_mode: XC2IOBZIAMode::Disabled,
schmitt_trigger: true,
obuf_uses_ff: false,
obuf_mode: XC2IOBOBufMode::Disabled,
termination_enabled: true,
slew_is_fast: true,
}
}
}

impl XC2MCSmallIOB {
/// Dump a human-readable explanation of the settings for this pin to the given `writer` object.
/// `my_idx` must be the index of this I/O pin in the internal numbering scheme.
pub fn dump_human_readable(&self, my_idx: u32, writer: &mut Write) {
write!(writer, "\n").unwrap();
let (fb, ff) = iob_num_to_fb_ff_num_32(my_idx).unwrap();
write!(writer, "I/O configuration for FB{}_{}\n", fb + 1, ff + 1).unwrap();
write!(writer, "output mode: {}\n", match self.obuf_mode {
XC2IOBOBufMode::Disabled => "disabled",
XC2IOBOBufMode::PushPull => "push-pull",
XC2IOBOBufMode::OpenDrain => "open-drain",
XC2IOBOBufMode::TriStateGTS0 => "GTS0-controlled tri-state",
XC2IOBOBufMode::TriStateGTS1 => "GTS1-controlled tri-state",
XC2IOBOBufMode::TriStateGTS2 => "GTS2-controlled tri-state",
XC2IOBOBufMode::TriStateGTS3 => "GTS3-controlled tri-state",
XC2IOBOBufMode::TriStatePTB => "PTB-controlled tri-state",
XC2IOBOBufMode::TriStateCTE => "CTE-controlled tri-state",
XC2IOBOBufMode::CGND => "CGND",
}).unwrap();
write!(writer, "output comes from {}\n", if self.obuf_uses_ff {"FF"} else {"XOR gate"}).unwrap();
write!(writer, "slew rate: {}\n", if self.slew_is_fast {"fast"} else {"slow"}).unwrap();
write!(writer, "ZIA driven from: {}\n", match self.zia_mode {
XC2IOBZIAMode::Disabled => "disabled",
XC2IOBZIAMode::PAD => "input pad",
XC2IOBZIAMode::REG => "register",
}).unwrap();
write!(writer, "Schmitt trigger input: {}\n", if self.schmitt_trigger {"yes"} else {"no"}).unwrap();
write!(writer, "termination: {}\n", if self.termination_enabled {"yes"} else {"no"}).unwrap();
}
}

/// Represents the one additional special input-only pin on 32-macrocell devices.
pub struct XC2ExtraIBuf {
pub schmitt_trigger: bool,
pub termination_enabled: bool,
}

impl Default for XC2ExtraIBuf {
/// Returns a "default" pin configuration.
// FIXME: Do the other defaults come from the particular way I invoked the Xilinx tools??
fn default() -> XC2ExtraIBuf {
XC2ExtraIBuf {
schmitt_trigger: true,
termination_enabled: true,
}
}
}

impl XC2ExtraIBuf {
/// Dump a human-readable explanation of the settings for this pin to the given `writer` object.
pub fn dump_human_readable(&self, writer: &mut Write) {
write!(writer, "\n").unwrap();
write!(writer, "I/O configuration for input-only pin\n").unwrap();
write!(writer, "Schmitt trigger input: {}\n", if self.schmitt_trigger {"yes"} else {"no"}).unwrap();
write!(writer, "termination: {}\n", if self.termination_enabled {"yes"} else {"no"}).unwrap();
}
}

/// Function to map from the internal numbering scheme for I/O pins to a function block and macrocell number.
/// This function is for the 32-macrocell devices.
pub fn iob_num_to_fb_ff_num_32(iob: u32) -> Option<(u32, u32)> {
if iob >= 32 {
None
} else {
Some((iob / MCS_PER_FB as u32, iob % MCS_PER_FB as u32))
}
}

/// Function to map from a function block and macrocell number to the internal numbering scheme for I/O pins.
/// This function is for the 32-macrocell devices.
pub fn fb_ff_num_to_iob_num_32(fb: u32, ff: u32) -> Option<u32> {
if fb >= 2 || ff >= MCS_PER_FB as u32 {
None
} else {
Some(fb * MCS_PER_FB as u32 + ff)
}
}

/// Internal function that reads only the IO-related bits from the macrocell configuration
pub fn read_32_iob_logical(fuses: &[bool], block_idx: usize, io_idx: usize) -> Result<XC2MCSmallIOB, &'static str> {
let inz = (fuses[block_idx + io_idx * 27 + 11],
fuses[block_idx + io_idx * 27 + 12]);
let input_to_zia = match inz {
(false, false) => XC2IOBZIAMode::PAD,
(true, false) => XC2IOBZIAMode::REG,
(_, true) => XC2IOBZIAMode::Disabled,
};

let st = fuses[block_idx + io_idx * 27 + 16];
let regcom = fuses[block_idx + io_idx * 27 + 19];

let oe = (fuses[block_idx + io_idx * 27 + 20],
fuses[block_idx + io_idx * 27 + 21],
fuses[block_idx + io_idx * 27 + 22],
fuses[block_idx + io_idx * 27 + 23]);
let output_mode = match oe {
(false, false, false, false) => XC2IOBOBufMode::PushPull,
(false, false, false, true) => XC2IOBOBufMode::OpenDrain,
(false, false, true, false) => XC2IOBOBufMode::TriStateGTS1,
(false, true, false, false) => XC2IOBOBufMode::TriStatePTB,
(false, true, true, false) => XC2IOBOBufMode::TriStateGTS3,
(true, false, false, false) => XC2IOBOBufMode::TriStateCTE,
(true, false, true, false) => XC2IOBOBufMode::TriStateGTS2,
(true, true, false, false) => XC2IOBOBufMode::TriStateGTS0,
(true, true, true, false) => XC2IOBOBufMode::CGND,
(true, true, true, true) => XC2IOBOBufMode::Disabled,
_ => return Err("unknown Oe mode used"),
};

let tm = fuses[block_idx + io_idx * 27 + 24];
let slw = fuses[block_idx + io_idx * 27 + 25];

Ok(XC2MCSmallIOB {
zia_mode: input_to_zia,
schmitt_trigger: st,
obuf_uses_ff: !regcom,
obuf_mode: output_mode,
termination_enabled: tm,
slew_is_fast: !slw,
})
}

/// Internal function that reads only the input-only pin configuration
pub fn read_32_extra_ibuf_logical(fuses: &[bool]) -> XC2ExtraIBuf {
let st = fuses[12272];
let tm = fuses[12273];

XC2ExtraIBuf {
schmitt_trigger: st,
termination_enabled: tm,
}
}
6 changes: 3 additions & 3 deletions src/xc2bit/src/jed.rs
Original file line number Diff line number Diff line change
@@ -23,7 +23,7 @@ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

// Xilinx JED file I/O
//! Xilinx JED file I/O
use std::num::Wrapping;
use std::str;
@@ -41,7 +41,7 @@ enum Ternary {
const STX: u8 = 0x02;
const ETX: u8 = 0x03;

// Reads .jed file and outputs the fuses as an array of booleans and optional device name
/// Reads .jed file and outputs the fuses as an array of booleans and optional device name
pub fn read_jed(in_bytes: &[u8]) -> Result<(Vec<bool>, Option<String>), &'static str> {
let mut fuse_csum = Wrapping(0u16);
let mut fuse_expected_csum = None;
@@ -234,7 +234,7 @@ pub fn read_jed(in_bytes: &[u8]) -> Result<(Vec<bool>, Option<String>), &'static
Ok((fuses, device))
}

// Processes a fuse array into a bitstream object
/// Processes a fuse array into a bitstream object
pub fn process_jed(fuses: &[bool], device: &str) -> Result<XC2Bitstream, &'static str> {
let device_split = device.split('-').collect::<Vec<_>>();

42 changes: 36 additions & 6 deletions src/xc2bit/src/lib.rs
Original file line number Diff line number Diff line change
@@ -23,24 +23,54 @@ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

// Public interface
//! # xc2bit: A library for working with Xilinx Coolrunner-II bitstreams
//!
//! xc2bit is a library for reading and writing bitstreams for the Xilinx Coolrunner-II family of CPLD devices.
//!
//! This project is the result of a reverse-engineering effort involving a combination of [imaging physical
//! CPLD devices](http://siliconexposed.blogspot.com/2014/03/getting-my-feet-wet-with-invasive.html) and black-box
//! reverse-engineering of generated .jed files. It is not an official project of Xilinx, Inc. and is not
//! affiliated or endorsed by Xilinx, Inc.
//!
//! Logically, a Coolrunner-II CPLD contains the following major blocks: function blocks (occasionally abbreviated
//! to FBs), a global interconnect (occasionally referred to as the ZIA or the AIM), and input/output blocks
//! (occasionally abbreviated to IOBs). Function blocks are further divided into the PLA (programmable logic array,
//! a matrix of AND and OR gates) and macrocells. In the Coolrunner-II architecture, macrocells also contain
//! an XOR gate and a register. The global interconnect accepts inputs from IOBs and function blocks and connects these
//! inputs into the PLA of each function block. IOBs also have direct connections to a corresponding macrocell in
//! a function block. (The reverse is not always true - on larger devices, there are macrocells that are not connected
//! to IOBs.) As a special exception, the smallest 32-macrocell devices also have one single input-only pin that is
//! connected directly into the global interconnect and does not have a corresponding macrocell.
/// The number of inputs from the ZIA interconnect into the AND gate section of each PLA.
/// This is an unchangeable property of the architecture of the CPLD.
pub const INPUTS_PER_ANDTERM: usize = 40;
/// The number of AND gates in each PLA. This is also the number of inputs into each OR gate in the PLA.
/// This is an unchangeable property of the architecture of the CPLD.
pub const ANDTERMS_PER_FB: usize = 56;
/// The number of macrocells in each function block. This is also the number of OR gates in each PLA.
/// This is an unchangeable property of the architecture of the CPLD.
pub const MCS_PER_FB: usize = 16;

mod bitstream;
pub use bitstream::{XC2Bitstream, XC2BitstreamBits, XC2GlobalNets};

mod fb;
pub use fb::{XC2BistreamFB, get_ctc, get_ctr, get_cts, get_cte, get_pta, get_ptb, get_ptc};
pub use fb::{XC2BitstreamFB, CTC, CTR, CTS, CTE, get_pta, get_ptb, get_ptc};

mod mc;
pub use mc::{XC2MCFF, XC2MCSmallIOB, XC2MCFFClkSrc, XC2MCFFResetSrc, XC2MCFFSetSrc, XC2MCFFMode, XC2MCFeedbackMode,
XC2IOBZIAMode, XC2MCXorMode, XC2MCOBufMode, XC2ExtraIBuf, iob_num_to_fb_ff_num_32,
mod iob;
pub use iob::{XC2MCSmallIOB, XC2IOBZIAMode, XC2IOBOBufMode, XC2ExtraIBuf, iob_num_to_fb_ff_num_32,
fb_ff_num_to_iob_num_32};

mod mc;
pub use mc::{XC2Macrocell, XC2MCRegClkSrc, XC2MCRegResetSrc, XC2MCRegSetSrc, XC2MCRegMode, XC2MCFeedbackMode,
XC2MCXorMode};

mod pla;
pub use pla::{XC2PLAAndTerm, XC2PLAOrTerm};

mod zia;
pub use zia::{XC2ZIARowPiece, XC2ZIAInput, ZIA_BIT_TO_CHOICE_32};
pub use zia::{XC2ZIARowPiece, XC2ZIAInput, ZIA_MAP_32};

mod jed;
pub use jed::{read_jed, process_jed};
338 changes: 104 additions & 234 deletions src/xc2bit/src/mc.rs

Large diffs are not rendered by default.

51 changes: 34 additions & 17 deletions src/xc2bit/src/pla.rs
Original file line number Diff line number Diff line change
@@ -23,53 +23,69 @@ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

// PLA stuff
//! Contains functions pertaining to the PLA
use *;

/// Represents one single AND term in the PLA. Each AND term can perform an AND function on any subset of its inputs
/// and the complement of those inputs. The index for each input is the corresponding ZIA row.
#[derive(Copy)]
pub struct XC2PLAAndTerm {
// true = part of and, false = not part of and
pub input: [bool; 40],
pub input_b: [bool; 40],
/// Indicates whether a particular ZIA row output is a part of this AND term.
///
/// `true` = part of and, `false` = not part of and
pub input: [bool; INPUTS_PER_ANDTERM],
/// Indicates whether the complement of a particular ZIA row output is a part of this AND term.
///
/// `true` = part of and, `false` = not part of and
pub input_b: [bool; INPUTS_PER_ANDTERM],
}

impl Clone for XC2PLAAndTerm {
fn clone(&self) -> XC2PLAAndTerm {*self}
}

impl Default for XC2PLAAndTerm {
/// Returns a "default" AND term. The default state is for none of the inputs to be selected.
fn default() -> XC2PLAAndTerm {
XC2PLAAndTerm {
input: [false; 40],
input_b: [false; 40],
input: [false; INPUTS_PER_ANDTERM],
input_b: [false; INPUTS_PER_ANDTERM],
}
}
}

/// Represents one single OR term in the PLA. Each OR term can perform an OR function on any subset of its inputs.
/// The index for each input is the index of the corresponding AND term in the same PLA.
#[derive(Copy)]
pub struct XC2PLAOrTerm {
// true = part of or, false = not part of or
pub input: [bool; 56],
/// Indicates whether a particular PLA AND term is a part of this OR term.
///
/// `true` = part of or, `false` = not part of or
pub input: [bool; ANDTERMS_PER_FB],
}

impl Clone for XC2PLAOrTerm {
fn clone(&self) -> XC2PLAOrTerm {*self}
}

impl Default for XC2PLAOrTerm {
/// Returns a "default" OR term. The default state is for none of the inputs to be selected.
fn default() -> XC2PLAOrTerm {
XC2PLAOrTerm {
input: [false; 56],
input: [false; ANDTERMS_PER_FB],
}
}
}

/// Internal function that reads one single AND term from a block of fuses using logical fuse indexing
pub fn read_and_term_logical(fuses: &[bool], block_idx: usize, term_idx: usize) -> XC2PLAAndTerm {
let mut input = [false; 40];
let mut input_b = [false; 40];
let mut input = [false; INPUTS_PER_ANDTERM];
let mut input_b = [false; INPUTS_PER_ANDTERM];

for i in 0..40 {
input[i] = !fuses[block_idx + term_idx * 80 + i * 2 + 0];
input_b[i] = !fuses[block_idx + term_idx * 80 + i * 2 + 1];
for i in 0..INPUTS_PER_ANDTERM {
input[i] = !fuses[block_idx + term_idx * INPUTS_PER_ANDTERM * 2 + i * 2 + 0];
input_b[i] = !fuses[block_idx + term_idx * INPUTS_PER_ANDTERM * 2 + i * 2 + 1];
}

XC2PLAAndTerm {
@@ -78,11 +94,12 @@ pub fn read_and_term_logical(fuses: &[bool], block_idx: usize, term_idx: usize)
}
}

/// Internal function that reads one single OR term from a block of fuses using logical fuse indexing
pub fn read_or_term_logical(fuses: &[bool], block_idx: usize, term_idx: usize) -> XC2PLAOrTerm {
let mut input = [false; 56];
let mut input = [false; ANDTERMS_PER_FB];

for i in 0..56 {
input[i] = !fuses[block_idx + term_idx +i * 16];
for i in 0..ANDTERMS_PER_FB {
input[i] = !fuses[block_idx + term_idx +i * MCS_PER_FB];
}

XC2PLAOrTerm {
25 changes: 18 additions & 7 deletions src/xc2bit/src/zia.rs
Original file line number Diff line number Diff line change
@@ -23,13 +23,19 @@ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

// ZIA
//! Contains functions pertaining to the ZIA
use *;

/// Represents one output of the ZIA. The ZIA is divided into rows, and each row can independently select a choice
/// to connect to each function block. This represents one such output (as opposed to all outputs in a given row)
#[derive(Copy, Clone, Default)]
pub struct XC2ZIARowPiece {
pub selected: XC2ZIAInput,
}

/// Represents one input to the ZIA. The ZIA has inputs from every part of the chip and can additionally output a
/// constant zero or one.
#[derive(Copy, Clone, Eq, PartialEq)]
pub enum XC2ZIAInput {
Macrocell {
@@ -45,10 +51,12 @@ pub enum XC2ZIAInput {
}

impl Default for XC2ZIAInput {
/// Returns a "default" ZIA selection, which is a constant one.
fn default() -> XC2ZIAInput { XC2ZIAInput::One }
}

pub static ZIA_BIT_TO_CHOICE_32: [[XC2ZIAInput; 6]; 40] = [
/// A map of the connections that exist within the ZIA for 32-macrocell parts
pub static ZIA_MAP_32: [[XC2ZIAInput; 6]; INPUTS_PER_ANDTERM] = [
// Row 0
[XC2ZIAInput::IBuf{ibuf: 0},
XC2ZIAInput::IBuf{ibuf: 10},
@@ -331,8 +339,10 @@ pub static ZIA_BIT_TO_CHOICE_32: [[XC2ZIAInput; 6]; 40] = [
XC2ZIAInput::Macrocell{fb: 1, ff: 9}],
];

// Read a piece of the ZIA corresponding to one FB and one row
pub fn read_32_zia_fb_row_logical(fuses: &[bool], block_idx: usize, row_idx: usize) -> Result<XC2ZIARowPiece, &'static str> {
/// Internal function that reads a piece of the ZIA corresponding to one FB and one row
pub fn read_32_zia_fb_row_logical(fuses: &[bool], block_idx: usize, row_idx: usize)
-> Result<XC2ZIARowPiece, &'static str> {

let mut zia_row_fuses = [false; 8];

for i in 0..8 {
@@ -365,11 +375,12 @@ pub fn read_32_zia_fb_row_logical(fuses: &[bool], block_idx: usize, row_idx: usi
} else if active_bit == 7 {
XC2ZIAInput::One
} else {
ZIA_BIT_TO_CHOICE_32[row_idx][active_bit]
ZIA_MAP_32[row_idx][active_bit]
}
})
}

/// Internal function that takes a ZIA row and choice and returns the bit encoding for it
pub fn encode_32_zia_choice(row: u32, choice: XC2ZIAInput) -> Option<[bool; 8]> {
if choice == XC2ZIAInput::One {
Some([true, true, true, true, true, true, true, true])
@@ -381,8 +392,8 @@ pub fn encode_32_zia_choice(row: u32, choice: XC2ZIAInput) -> Option<[bool; 8]>
ret[7] = false;

let mut found_bit = 8;
for i in 0..ZIA_BIT_TO_CHOICE_32[row as usize].len() {
if choice == ZIA_BIT_TO_CHOICE_32[row as usize][i] {
for i in 0..ZIA_MAP_32[row as usize].len() {
if choice == ZIA_MAP_32[row as usize][i] {
found_bit = i;
break;
}