From 87f887bbfe03ea710aa16e820a9e4ce14fd449cb Mon Sep 17 00:00:00 2001 From: Griffin Berlstein Date: Mon, 29 Jul 2024 14:32:26 -0400 Subject: [PATCH] [Cider2] Fixed point data initialization and serialization (#2232) --- Cargo.lock | 19 +- .../src/flatten/structures/environment/env.rs | 6 +- interp/src/flatten/structures/indexed_map.rs | 2 +- interp/src/serialization/data_dump.rs | 28 +- tools/cider-data-converter/Cargo.toml | 2 + tools/cider-data-converter/src/converter.rs | 334 ++++++++++++++---- tools/cider-data-converter/src/json_data.rs | 36 +- tools/cider-data-converter/src/main.rs | 7 +- 8 files changed, 334 insertions(+), 100 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a435422145..28e4cef6f0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -623,6 +623,8 @@ dependencies = [ "interp", "itertools 0.11.0", "num-bigint", + "num-rational", + "num-traits", "proptest", "serde", "serde_json", @@ -1881,11 +1883,10 @@ dependencies = [ [[package]] name = "num-rational" -version = "0.4.1" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0" +checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824" dependencies = [ - "autocfg", "num-bigint", "num-integer", "num-traits", @@ -1894,9 +1895,9 @@ dependencies = [ [[package]] name = "num-traits" -version = "0.2.18" +version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ "autocfg", "libm", @@ -2945,9 +2946,9 @@ dependencies = [ [[package]] name = "time" -version = "0.3.34" +version = "0.3.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8248b6521bb14bc45b4067159b9b6ad792e2d6d754d6c41fb50e29fefe38749" +checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" dependencies = [ "deranged", "itoa", @@ -2966,9 +2967,9 @@ checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" [[package]] name = "time-macros" -version = "0.2.17" +version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ba3a3ef41e6672a2f0f001392bb5dcd3ff0a9992d618ca761a11c3121547774" +checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf" dependencies = [ "num-conv", "time-core", diff --git a/interp/src/flatten/structures/environment/env.rs b/interp/src/flatten/structures/environment/env.rs index bb7303277e..092735b96a 100644 --- a/interp/src/flatten/structures/environment/env.rs +++ b/interp/src/flatten/structures/environment/env.rs @@ -1892,7 +1892,7 @@ impl + Clone> Simulator { } else { MemoryDeclaration::new_bitnum( name, - *width as usize, + *width, dims.as_serializing_dim(), false, ) @@ -1900,7 +1900,7 @@ impl + Clone> Simulator { } else { MemoryDeclaration::new_bitnum( name, - *width as usize, + *width, dims.as_serializing_dim(), false, ) @@ -1921,7 +1921,7 @@ impl + Clone> Simulator { if dump_registers { dump.push_reg( name, - *width as usize, + *width, self.env.cells[cell_index] .unwrap_primitive() .dump_memory_state() diff --git a/interp/src/flatten/structures/indexed_map.rs b/interp/src/flatten/structures/indexed_map.rs index 9a66a90319..f744799345 100644 --- a/interp/src/flatten/structures/indexed_map.rs +++ b/interp/src/flatten/structures/indexed_map.rs @@ -143,7 +143,7 @@ where Self::new() } } - +#[allow(dead_code)] pub struct IndexedMapRangeIterator<'range, 'data, K, D> where K: IndexRef + PartialOrd, diff --git a/interp/src/serialization/data_dump.rs b/interp/src/serialization/data_dump.rs index bc117e2d6e..acc4577e54 100644 --- a/interp/src/serialization/data_dump.rs +++ b/interp/src/serialization/data_dump.rs @@ -48,12 +48,12 @@ impl From<(usize, usize, usize, usize)> for Dimensions { pub enum FormatInfo { Bitnum { signed: bool, - width: usize, + width: u32, }, Fixed { signed: bool, - int_width: usize, - frac_width: usize, + int_width: u32, + frac_width: u32, }, } @@ -65,7 +65,7 @@ impl FormatInfo { } } - pub fn width(&self) -> usize { + pub fn width(&self) -> u32 { match self { FormatInfo::Bitnum { width, .. } => *width, FormatInfo::Fixed { @@ -87,7 +87,7 @@ pub struct MemoryDeclaration { impl MemoryDeclaration { pub fn new_bitnum( name: String, - width: usize, + width: u32, dimensions: Dimensions, signed: bool, ) -> Self { @@ -102,8 +102,8 @@ impl MemoryDeclaration { name: String, dimensions: Dimensions, signed: bool, - int_width: usize, - frac_width: usize, + int_width: u32, + frac_width: u32, ) -> Self { assert!(int_width + frac_width > 0, "width must be greater than 0"); @@ -135,10 +135,10 @@ impl MemoryDeclaration { } pub fn byte_count(&self) -> usize { - self.format.width().div_ceil(8) * self.dimensions.size() + self.format.width().div_ceil(8) as usize * self.dimensions.size() } - pub fn width(&self) -> usize { + pub fn width(&self) -> u32 { self.format.width() } @@ -208,7 +208,7 @@ impl DataDump { pub fn push_reg>( &mut self, name: String, - width: usize, + width: u32, data: T, ) { let declaration = MemoryDeclaration::new_bitnum( @@ -389,7 +389,7 @@ mod tests { use proptest::prelude::*; prop_compose! { - fn arb_memory_declaration()(name in any::(), signed in any::(), width in 1_usize..=256, size in 1_usize..=500) -> MemoryDeclaration { + fn arb_memory_declaration()(name in any::(), signed in any::(), width in 1_u32..=256, size in 1_usize..=500) -> MemoryDeclaration { MemoryDeclaration::new_bitnum(name.to_string(), width, Dimensions::D1(size), signed) } } @@ -429,7 +429,7 @@ mod tests { // produced from the memory primitive to not match the one // serialized into it in the first place for mem in &header.memories { - let bytes_per_val = mem.width().div_ceil(8); + let bytes_per_val = mem.width().div_ceil(8) as usize; let rem = mem.width() % 8; let mask = if rem != 0 { 255u8 >> (8 - rem) } else { 255_u8 }; @@ -475,7 +475,7 @@ mod tests { #[test] fn comb_roundtrip(dump in arb_data_dump()) { for mem in &dump.header.memories { - let memory_prim = CombMemD1::new_with_init(GlobalPortIdx::new(0), mem.width() as u32, false, mem.size(), dump.get_data(&mem.name).unwrap()); + let memory_prim = CombMemD1::new_with_init(GlobalPortIdx::new(0), mem.width(), false, mem.size(), dump.get_data(&mem.name).unwrap()); let data = memory_prim.dump_data(); prop_assert_eq!(dump.get_data(&mem.name).unwrap(), data); } @@ -484,7 +484,7 @@ mod tests { #[test] fn seq_roundtrip(dump in arb_data_dump()) { for mem in &dump.header.memories { - let memory_prim = SeqMemD1::new_with_init(GlobalPortIdx::new(0), mem.width() as u32, false, mem.size(), dump.get_data(&mem.name).unwrap()); + let memory_prim = SeqMemD1::new_with_init(GlobalPortIdx::new(0), mem.width(), false, mem.size(), dump.get_data(&mem.name).unwrap()); let data = memory_prim.dump_data(); prop_assert_eq!(dump.get_data(&mem.name).unwrap(), data); } diff --git a/tools/cider-data-converter/Cargo.toml b/tools/cider-data-converter/Cargo.toml index 835f25be48..a5755b4066 100644 --- a/tools/cider-data-converter/Cargo.toml +++ b/tools/cider-data-converter/Cargo.toml @@ -15,6 +15,8 @@ itertools = { workspace = true } argh = { workspace = true } thiserror = "1.0.59" num-bigint = { version = "0.4.6" } +num-rational = { version = "0.4.2" } +num-traits = { version = "0.2.19" } [dev-dependencies] proptest = "1.0.0" diff --git a/tools/cider-data-converter/src/converter.rs b/tools/cider-data-converter/src/converter.rs index 2cdf47ee99..9ce57f4e89 100644 --- a/tools/cider-data-converter/src/converter.rs +++ b/tools/cider-data-converter/src/converter.rs @@ -1,18 +1,20 @@ use itertools::Itertools; -use num_bigint::BigInt; +use num_bigint::{BigInt, BigUint, ToBigInt}; +use num_rational::BigRational; +use num_traits::{sign::Signed, Num, ToPrimitive}; use serde_json::Number; use std::{collections::HashMap, iter::repeat, str::FromStr}; use super::json_data::*; use interp::serialization::*; -fn msb(width: usize) -> u8 { +fn msb(width: u32) -> u8 { let rem = width % 8; 1u8 << (if rem != 0 { rem - 1 } else { 7 }) // shift to the right by between 0 and 7 } -fn sign_extend_vec(mut vec: Vec, width: usize, signed: bool) -> Vec { - let byte_count = width.div_ceil(8); +fn sign_extend_vec(mut vec: Vec, width: u32, signed: bool) -> Vec { + let byte_count = width.div_ceil(8) as usize; let msb = if vec.len() < byte_count { 0b1000_0000u8 } else { @@ -20,7 +22,7 @@ fn sign_extend_vec(mut vec: Vec, width: usize, signed: bool) -> Vec { }; if signed && vec.last().unwrap() & msb != 0 { - match vec.len().cmp(&byte_count) { + match vec.len().cmp(&(byte_count)) { std::cmp::Ordering::Less => { vec.extend( repeat(0b1111_1111).take(byte_count - vec.len() - 1), @@ -49,15 +51,16 @@ fn sign_extend_vec(mut vec: Vec, width: usize, signed: bool) -> Vec { vec } -pub fn convert_to_data_dump(json: &JsonData) -> DataDump { +pub fn convert_to_data_dump(json: &JsonData, round_float: bool) -> DataDump { let mut data_dump = DataDump::new_empty(); for (name, entry) in json.0.iter() { let data_vec = entry.data.parse(&entry.format).unwrap(); + let format = entry.format.as_data_dump_format(); let dec: MemoryDeclaration = MemoryDeclaration::new( name.clone(), data_vec.dimensions(), - entry.format.as_data_dump_format(), + format.clone(), ); let width = dec.width(); @@ -66,57 +69,48 @@ pub fn convert_to_data_dump(json: &JsonData) -> DataDump { let data: Box> = match &data_vec { DataVec::Id1(v1) => Box::new(v1.iter().flat_map(|val| { // chopping off the upper bits - sign_extend_vec( - val.to_signed_bytes_le(), - width, - signed && val < &BigInt::ZERO, - ) - .into_iter() - .take(width.div_ceil(8)) + unroll_bigint(val, width, signed) })), DataVec::Id2(v1) => Box::new(v1.iter().flat_map(|v2| { - v2.iter().flat_map(|val| { - sign_extend_vec( - val.to_signed_bytes_le(), - width, - signed && val < &BigInt::ZERO, - ) - .into_iter() - .take(width.div_ceil(8)) - }) + v2.iter().flat_map(|val| unroll_bigint(val, width, signed)) })), DataVec::Id3(v1) => Box::new(v1.iter().flat_map(|v2| { + v2.iter().flat_map(|v3| { + v3.iter().flat_map(|val| unroll_bigint(val, width, signed)) + }) + })), + DataVec::Id4(v1) => Box::new(v1.iter().flat_map(|v2| { + v2.iter().flat_map(|v3| { + v3.iter().flat_map(|v4| { + v4.iter() + .flat_map(|val| unroll_bigint(val, width, signed)) + }) + }) + })), + DataVec::Fd1(v1) => Box::new( + v1.iter() + .flat_map(|val| unroll_float(*val, &format, round_float)), + ), + DataVec::Fd2(v1) => Box::new(v1.iter().flat_map(|v2| { + v2.iter() + .flat_map(|val| unroll_float(*val, &format, round_float)) + })), + DataVec::Fd3(v1) => Box::new(v1.iter().flat_map(|v2| { v2.iter().flat_map(|v3| { v3.iter().flat_map(|val| { - sign_extend_vec( - val.to_signed_bytes_le(), - width, - signed && val < &BigInt::ZERO, - ) - .into_iter() - .take(width.div_ceil(8)) + unroll_float(*val, &format, round_float) }) }) })), - DataVec::Id4(v1) => Box::new(v1.iter().flat_map(|v2| { + DataVec::Fd4(v1) => Box::new(v1.iter().flat_map(|v2| { v2.iter().flat_map(|v3| { v3.iter().flat_map(|v4| { v4.iter().flat_map(|val| { - sign_extend_vec( - val.to_signed_bytes_le(), - width, - signed && val < &BigInt::ZERO, - ) - .into_iter() - .take(width.div_ceil(8)) + unroll_float(*val, &format, round_float) }) }) }) })), - DataVec::Fd1(_) => todo!("implement fixed-point"), - DataVec::Fd2(_) => todo!("implement fixed-point"), - DataVec::Fd3(_) => todo!("implement fixed-point"), - DataVec::Fd4(_) => todo!("implement fixed-point"), }; data_dump.push_memory(dec, data) @@ -125,7 +119,116 @@ pub fn convert_to_data_dump(json: &JsonData) -> DataDump { data_dump } -fn parse_bytes(bytes: &[u8], width: usize, signed: bool) -> BigInt { +#[inline] +fn unroll_bigint( + val: &BigInt, + width: u32, + signed: bool, +) -> std::iter::Take> { + sign_extend_vec(val.to_signed_bytes_le(), width, signed) + .into_iter() + .take(width.div_ceil(8) as usize) +} + +/// This is so so so stupid. unfortunately, using the `BigRational` type's +/// to_f64 method results in some rounding behavior which creates very confusing +/// errors (demanding more precision) so this is a workaround. +fn float_to_rational(float: f64) -> BigRational { + let string = format!("{:.}", float); + let string = string.split('.').collect_vec(); + + if string.len() == 1 { + return BigRational::from_integer( + BigInt::from_str_radix(string[0], 10).unwrap(), + ); + } + + let is_neg = string[0].starts_with('-'); + + let int = BigInt::from_str_radix( + string[0].strip_prefix('-').unwrap_or(string[0]), + 10, + ) + .unwrap(); + let frac = BigInt::from_str_radix(string[1], 10).unwrap(); + let denom = BigInt::from(10).pow(string[1].len() as u32); + + let result = BigRational::from_integer(int) + BigRational::new(frac, denom); + if is_neg { + -result + } else { + result + } +} + +fn unroll_float( + val: f64, + format: &interp::serialization::FormatInfo, + round_float: bool, +) -> impl Iterator { + if let &interp::serialization::FormatInfo::Fixed { + signed, + int_width, + frac_width, + } = format + { + let rational = float_to_rational(val); + + let frac_part = rational.fract().abs(); + let frac_log = log2_exact(&frac_part.denom().to_biguint().unwrap()); + + let number = if frac_log.is_none() && round_float { + let w = BigInt::from(1) << frac_width; + let new = (val * w.to_f64().unwrap()).round(); + new.to_bigint().unwrap() + } else if frac_log.is_none() { + panic!("Number {val} cannot be represented as a fixed-point number. If you want to approximate the number, set the `round_float` flag to true."); + } else { + let int_part = rational.to_integer(); + + let frac_log = frac_log.unwrap_or_else(|| panic!("unable to round the given value to a value representable with {frac_width} fractional bits")); + if frac_log > frac_width { + panic!("cannot represent value with {frac_width} fractional bits, requires at least {frac_log} bits"); + } + + let mut int_log = + log2_round_down(&int_part.abs().to_biguint().unwrap()); + if (BigInt::from(1) << int_log) <= int_part.abs() { + int_log += 1; + } + if signed { + int_log += 1; + } + + if int_log > int_width { + let signed_str = if signed { "signed " } else { "" }; + + panic!("cannot represent {signed_str}value of {val} with {int_width} integer bits, requires at least {int_log} bits"); + } + + rational.numer() << (frac_width - frac_log) + }; + + let bit_count = number.bits() + if signed { 1 } else { 0 }; + + if bit_count > (frac_width + int_width) as u64 { + let difference = bit_count - frac_width as u64; + panic!("The approximation of the number {val} cannot be represented with {frac_width} fractional bits and {int_width} integer bits. Requires at least {difference} integer bits."); + } + + sign_extend_vec( + number.to_signed_bytes_le(), + frac_width + int_width, + signed, + ) + .into_iter() + .take((frac_width + int_width).div_ceil(8) as usize) + } else { + panic!("Called unroll_float on a non-fixed point type"); + } +} + +fn parse_bytes(bytes: &[u8], width: u32, signed: bool) -> BigInt { if signed { let msb = msb(width); @@ -144,20 +247,44 @@ fn parse_bytes(bytes: &[u8], width: usize, signed: bool) -> BigInt { } } +fn parse_bytes_fixed( + bytes: &[u8], + int_width: u32, + frac_width: u32, + signed: bool, +) -> BigRational { + let int = parse_bytes(bytes, int_width + frac_width, signed); + + BigRational::new(int.clone(), BigInt::from(1) << frac_width) +} + fn format_data(declaration: &MemoryDeclaration, data: &[u8]) -> ParseVec { let width = declaration.width(); - let chunk_stream = data.chunks_exact(width.div_ceil(8)).map(|chunk| { - match declaration.format { - interp::serialization::FormatInfo::Bitnum { signed, .. } => { - let int = parse_bytes(chunk, width, signed); - Number::from_str(&int.to_str_radix(10)).unwrap() + let chunk_stream = + data.chunks_exact(width.div_ceil(8) as usize).map(|chunk| { + match declaration.format { + interp::serialization::FormatInfo::Bitnum { + signed, .. + } => { + let int = parse_bytes(chunk, width, signed); + Number::from_str(&int.to_str_radix(10)).unwrap() + } + interp::serialization::FormatInfo::Fixed { + signed, + int_width, + frac_width, + } => { + let int = + parse_bytes_fixed(chunk, int_width, frac_width, signed); + let float = int.to_f64().unwrap(); + + Number::from_f64(float).unwrap() + } } - interp::serialization::FormatInfo::Fixed { .. } => todo!(), - } - }); + }); // sanity check - assert!(data.len() % width.div_ceil(8) == 0); + assert!(data.len() % (width.div_ceil(8) as usize) == 0); match &declaration.dimensions { Dimensions::D1(_) => chunk_stream.collect_vec().into(), @@ -209,13 +336,68 @@ pub fn convert_from_data_dump(dump: &DataDump) -> JsonPrintDump { JsonPrintDump(map) } +/// This is catastrophically stupid. +fn log2_round_down(x: &BigUint) -> u32 { + if *x == BigUint::ZERO { + return 0; + } + + let mut count = 0_u32; + while *x > BigUint::from(2_u32).pow(count) { + count += 1; + } + + if BigUint::from(2_u32).pow(count) == *x { + count + } else { + count - 1 + } +} + +fn log2_exact(x: &BigUint) -> Option { + let log_round_down = log2_round_down(x); + if *x == BigUint::from(2_u32).pow(log_round_down) { + Some(log_round_down) + } else { + None + } +} + #[cfg(test)] mod tests { use super::*; use proptest::prelude::*; + #[test] + fn test_unroll_float() { + let float = -0.5; + + let signed = true; + let int_width = 16; + let frac_width = 16; + + let format = interp::serialization::FormatInfo::Fixed { + signed, + int_width, + frac_width, + }; + + let result = unroll_float(float, &format, true); + let result = result.collect_vec(); + BigInt::from_signed_bytes_le(&result); + let parsed_res = + parse_bytes_fixed(&result, int_width, frac_width, signed); + println!( + " exact {}\n approx {}", + parsed_res, + parsed_res.to_f64().unwrap() + ); + + assert_eq!(parsed_res.to_f64().unwrap(), float) + } + prop_compose! { - fn arb_format_info()(width in 1_u64..=128, signed in any::()) -> crate::json_data::FormatInfo { + fn arb_format_info_bitnum()(width in 1_u32..=128, signed in any::()) -> crate::json_data::FormatInfo { crate::json_data::FormatInfo { width: Some(width), is_signed: signed, @@ -226,6 +408,23 @@ mod tests { } } + prop_compose! { + fn arb_format_info_fixed()(int_width in 1_u32..=128, frac_width in 1_u32..=128, signed in any::()) -> crate::json_data::FormatInfo { + crate::json_data::FormatInfo { + width: None, + is_signed: signed, + numeric_type: NumericType::Fixed, + int_width: Some(int_width), + frac_width: Some(frac_width), + } + } + } + + fn format_info_generator( + ) -> impl Strategy { + prop_oneof![arb_format_info_bitnum(), arb_format_info_fixed()] + } + fn dim_generator() -> impl Strategy { prop_oneof![ (1_usize..=32).prop_map(Dimensions::D1), @@ -239,14 +438,14 @@ mod tests { } prop_compose! { - fn arb_bigint(width: u64, signed: bool)(mut data in prop::collection::vec(any::(), width.div_ceil(8) as usize)) -> BigInt { + fn arb_bigint(width: u32, signed: bool)(mut data in prop::collection::vec(any::(), width.div_ceil(8) as usize)) -> BigInt { let last = data.last_mut().unwrap(); let mask = 0b1111_1111u8 >> (if width % 8 == 0 { 0 } else { 8 - width % 8 }); *last &= mask; if signed { - parse_bytes(data.as_slice(), width as usize, signed) + parse_bytes(data.as_slice(), width, signed) } else { BigInt::from_bytes_le(num_bigint::Sign::Plus, data.as_slice()) @@ -256,8 +455,15 @@ mod tests { } prop_compose! { - fn arb_data(width: u64, dimensions: Dimensions, signed: bool)(data in prop::collection::vec(arb_bigint(width, signed), dimensions.size())) -> ParseVec { - let data = data.into_iter().map(|x| Number::from_str(&x.to_str_radix(10)).unwrap()); + fn arb_data(format: crate::json_data::FormatInfo, dimensions: Dimensions, signed: bool)(data in prop::collection::vec(arb_bigint(format.get_width(), signed), dimensions.size())) -> ParseVec { + let data = data.into_iter().map(|x| { + if format.is_fixedpt() { + let rat = BigRational::new(x.clone(), BigInt::from(1) << format.frac_width().unwrap()); + Number::from_f64(rat.to_f64().unwrap()).unwrap() + } else { + Number::from_str(&x.to_str_radix(10)).unwrap() + } + }); match dimensions { Dimensions::D1(_) => data.collect_vec().into(), @@ -269,10 +475,10 @@ mod tests { } fn arb_json_entry() -> impl Strategy { - let arb_format_info = arb_format_info(); + let arb_format_info = format_info_generator(); let dim = dim_generator(); (arb_format_info, dim).prop_flat_map(|(format, dimensions)| { - arb_data(format.get_width(), dimensions, format.is_signed).prop_map( + arb_data(format.clone(), dimensions, format.is_signed).prop_map( move |x| JsonDataEntry { data: x, format: format.clone(), @@ -281,8 +487,8 @@ mod tests { }) } - fn arb_bigint_with_info() -> impl Strategy { - let width = prop_oneof![1..=128_u64]; + fn arb_bigint_with_info() -> impl Strategy { + let width = prop_oneof![1..=128_u32]; let signed = any::(); (width, signed).prop_flat_map(|(width, signed)| { @@ -296,7 +502,7 @@ mod tests { fn test_json_roundtrip(map in prop::collection::hash_map(any::(), arb_json_entry(), 1..4)) { let json_data = JsonData(map); - let dump = convert_to_data_dump(&json_data); + let dump = convert_to_data_dump(&json_data, true); let json_print_dump = convert_from_data_dump(&dump); @@ -308,9 +514,9 @@ mod tests { #[test] fn sign_extend(data in arb_bigint_with_info()) { let (data, width, signed) = data; - let vec = sign_extend_vec(data.to_signed_bytes_le(), width as usize, signed); + let vec = sign_extend_vec(data.to_signed_bytes_le(), width, signed); - let parsed_back = parse_bytes(&vec, width as usize, signed); + let parsed_back = parse_bytes(&vec, width, signed); prop_assert_eq!(data, parsed_back); } diff --git a/tools/cider-data-converter/src/json_data.rs b/tools/cider-data-converter/src/json_data.rs index 395847a9fa..c4589a9296 100644 --- a/tools/cider-data-converter/src/json_data.rs +++ b/tools/cider-data-converter/src/json_data.rs @@ -20,17 +20,17 @@ pub struct FormatInfo { pub is_signed: bool, #[serde(default)] #[serde(skip_serializing_if = "Option::is_none")] - pub width: Option, + pub width: Option, #[serde(default)] #[serde(skip_serializing_if = "Option::is_none")] - pub int_width: Option, + pub int_width: Option, #[serde(default)] #[serde(skip_serializing_if = "Option::is_none")] - pub frac_width: Option, + pub frac_width: Option, } impl FormatInfo { - pub fn get_width(&self) -> u64 { + pub fn get_width(&self) -> u32 { if let Some(w) = self.width { w } else if self.int_width.is_some() && self.frac_width.is_some() { @@ -40,17 +40,37 @@ impl FormatInfo { } } - fn is_fixedpt(&self) -> bool { + pub fn is_fixedpt(&self) -> bool { self.int_width.is_some() && self.frac_width.is_some() || self.width.is_some() && self.frac_width.is_some() || self.width.is_some() && self.int_width.is_some() } + pub fn int_width(&self) -> Option { + if self.int_width.is_some() { + self.int_width + } else if self.width.is_some() && self.frac_width.is_some() { + Some(self.width.unwrap() - self.frac_width.unwrap()) + } else { + None + } + } + + pub fn frac_width(&self) -> Option { + if self.frac_width.is_some() { + self.frac_width + } else if self.int_width.is_some() && self.width.is_some() { + Some(self.width.unwrap() - self.int_width.unwrap()) + } else { + None + } + } + pub fn as_data_dump_format(&self) -> interp::serialization::FormatInfo { match &self.numeric_type { NumericType::Bitnum => interp::serialization::FormatInfo::Bitnum { signed: self.is_signed, - width: self.width.unwrap() as usize, + width: self.width.unwrap(), }, NumericType::Fixed => { let (int_width, frac_width) = if self.int_width.is_some() @@ -75,8 +95,8 @@ impl FormatInfo { interp::serialization::FormatInfo::Fixed { signed: self.is_signed, - int_width: int_width as usize, - frac_width: frac_width as usize, + int_width, + frac_width, } } } diff --git a/tools/cider-data-converter/src/main.rs b/tools/cider-data-converter/src/main.rs index 9db19f1e4f..91359e6f4e 100644 --- a/tools/cider-data-converter/src/main.rs +++ b/tools/cider-data-converter/src/main.rs @@ -63,6 +63,11 @@ struct Opts { #[argh(option, short = 'o')] output_path: Option, + /// whether to round un-representable floating point instantiations rather than + /// throwing an error + #[argh(switch, short = 'r', long = "round-float")] + round_float: bool, + /// optional specification of what action to perform. Can be "cider" or /// "json". If not provided, the converter will try to guess based on file names #[argh(option, short = 't', long = "to")] @@ -108,7 +113,7 @@ fn main() -> Result<(), CiderDataConverterError> { Action::ToDataDump => { let parsed_json: JsonData = serde_json::from_reader(&mut input)?; - converter::convert_to_data_dump(&parsed_json) + converter::convert_to_data_dump(&parsed_json, opts.round_float) .serialize(&mut output)?; } Action::ToJson => {