|
|
|
@ -1,6 +1,7 @@
|
|
|
|
|
use super::shared::{self, Endian};
|
|
|
|
|
use byteorder::ReadBytesExt;
|
|
|
|
|
use std::io::{Read, Seek, SeekFrom};
|
|
|
|
|
use std::str::FromStr;
|
|
|
|
|
use thiserror::Error as TeError;
|
|
|
|
|
|
|
|
|
|
// Reference:
|
|
|
|
@ -14,13 +15,11 @@ use thiserror::Error as TeError;
|
|
|
|
|
pub enum Error {
|
|
|
|
|
#[error("IO error {0}")]
|
|
|
|
|
Io(#[from] std::io::Error),
|
|
|
|
|
#[error(
|
|
|
|
|
"fail to cast value into integral value. this usually caused by some value out of range"
|
|
|
|
|
)]
|
|
|
|
|
#[error("fail to cast value into integral value. maybe some values out of range")]
|
|
|
|
|
CastToInt(#[from] std::num::TryFromIntError),
|
|
|
|
|
#[error("something was overflow")]
|
|
|
|
|
Overflow,
|
|
|
|
|
#[error("wrong endian integer {0:#X} in block head. it must be one of 0x4 or 0x4000000")]
|
|
|
|
|
#[error("wrong endian integer {0:#X} in block head. it must be one of 0x4 or 0x04000000")]
|
|
|
|
|
WrongEndianInt(u32),
|
|
|
|
|
#[error("wrong block head value {0:#X}. it must be 0x4")]
|
|
|
|
|
WrongBlockHead(u32),
|
|
|
|
@ -31,10 +30,6 @@ pub enum Error {
|
|
|
|
|
}
|
|
|
|
|
type Result<T> = std::result::Result<T, Error>;
|
|
|
|
|
|
|
|
|
|
const ENDIAN_INT: u32 = 0x4;
|
|
|
|
|
const LITTLE_ENDIAN_INT: u32 = 0x4;
|
|
|
|
|
const BIG_ENDIAN_INT: u32 = 0x4000000;
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
|
|
|
enum SimSource {
|
|
|
|
|
/// For HSPICE, read as big endian.
|
|
|
|
@ -51,14 +46,16 @@ impl SimSource {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub struct HSpiceData {}
|
|
|
|
|
const ENDIAN_INT: u32 = 0x4;
|
|
|
|
|
const LITTLE_ENDIAN_INT: u32 = 0x4;
|
|
|
|
|
const BIG_ENDIAN_INT: u32 = 0x04000000;
|
|
|
|
|
|
|
|
|
|
#[derive(Debug)]
|
|
|
|
|
struct HspiceBlock {
|
|
|
|
|
struct HSpiceBlock {
|
|
|
|
|
block_offset: u64,
|
|
|
|
|
block_len: u32,
|
|
|
|
|
}
|
|
|
|
|
impl HspiceBlock {
|
|
|
|
|
impl HSpiceBlock {
|
|
|
|
|
pub fn new(block_offset: u64, block_len: u32) -> Self {
|
|
|
|
|
Self {
|
|
|
|
|
block_offset,
|
|
|
|
@ -74,46 +71,86 @@ impl HspiceBlock {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
#[derive(Debug)]
|
|
|
|
|
struct HspiceBlocks {
|
|
|
|
|
struct HSpiceBlocks {
|
|
|
|
|
sim_source: SimSource,
|
|
|
|
|
blocks: Vec<HspiceBlock>,
|
|
|
|
|
header_blk_cnt: usize,
|
|
|
|
|
blocks: Vec<HSpiceBlock>,
|
|
|
|
|
}
|
|
|
|
|
impl HspiceBlocks {
|
|
|
|
|
pub fn new(sim_source: SimSource, blocks: Vec<HspiceBlock>, header_blk_cnt: usize) -> Self {
|
|
|
|
|
Self {
|
|
|
|
|
sim_source,
|
|
|
|
|
blocks,
|
|
|
|
|
header_blk_cnt,
|
|
|
|
|
impl HSpiceBlocks {
|
|
|
|
|
pub fn new<T>(reader: &mut T) -> Result<Self>
|
|
|
|
|
where
|
|
|
|
|
T: ReadBytesExt + Seek,
|
|
|
|
|
{
|
|
|
|
|
// Back to the file head
|
|
|
|
|
reader.rewind()?;
|
|
|
|
|
|
|
|
|
|
// Fetch the first u32 as little endian to decide the sim source
|
|
|
|
|
let endian_u32 = shared::read_int_value(Endian::LittleEndian, reader)?;
|
|
|
|
|
let sim_source = match endian_u32 {
|
|
|
|
|
LITTLE_ENDIAN_INT => SimSource::HspiceWin,
|
|
|
|
|
BIG_ENDIAN_INT => SimSource::Hspice,
|
|
|
|
|
other => return Err(Error::WrongEndianInt(other)),
|
|
|
|
|
};
|
|
|
|
|
let endian = sim_source.to_endian();
|
|
|
|
|
|
|
|
|
|
// Iterate each block
|
|
|
|
|
let mut blocks: Vec<HSpiceBlock> = Vec::new();
|
|
|
|
|
reader.rewind()?;
|
|
|
|
|
loop {
|
|
|
|
|
// Read block header.
|
|
|
|
|
// If there is no more data, break this loop.
|
|
|
|
|
let mut block_header = [0u32; 4];
|
|
|
|
|
if let Err(e) = shared::read_int_values(endian, &mut block_header, reader) {
|
|
|
|
|
match e.kind() {
|
|
|
|
|
std::io::ErrorKind::UnexpectedEof => break,
|
|
|
|
|
_ => return Err(e.into()),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Check endian u32
|
|
|
|
|
if block_header[0] != ENDIAN_INT {
|
|
|
|
|
return Err(Error::WrongBlockHead(block_header[0]));
|
|
|
|
|
}
|
|
|
|
|
if block_header[2] != ENDIAN_INT {
|
|
|
|
|
return Err(Error::WrongBlockHead(block_header[2]));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Get the offset and length of block body
|
|
|
|
|
let block_offset = reader.stream_position()?;
|
|
|
|
|
let block_len = block_header[3];
|
|
|
|
|
|
|
|
|
|
// Skip block body to check block tail
|
|
|
|
|
reader.seek(SeekFrom::Current(i64::try_from(block_len).unwrap()))?;
|
|
|
|
|
let block_tail = shared::read_int_value(endian, reader)?;
|
|
|
|
|
if block_tail != block_len {
|
|
|
|
|
return Err(Error::WrongBlockTail(block_tail, block_len));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Push result
|
|
|
|
|
blocks.push(HSpiceBlock::new(block_offset, block_len));
|
|
|
|
|
|
|
|
|
|
// Cursor now at the beginning of next block.
|
|
|
|
|
// We can continue analyze next blocks safely.
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Build result and return.
|
|
|
|
|
Ok(Self { sim_source, blocks })
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
impl<'a> HspiceBlocks {
|
|
|
|
|
pub fn to_reader<T>(&'a self, reader: T) -> HspiceBlocksReader<'a, T>
|
|
|
|
|
impl<'a> HSpiceBlocks {
|
|
|
|
|
pub fn to_reader<T>(&'a self, reader: T) -> HSpiceBlocksReader<'a, T>
|
|
|
|
|
where
|
|
|
|
|
T: Read + Seek,
|
|
|
|
|
{
|
|
|
|
|
HspiceBlocksReader::new(reader, &self.blocks)
|
|
|
|
|
}
|
|
|
|
|
pub fn to_header_reader<T>(&'a self, reader: T) -> HspiceBlocksReader<'a, T>
|
|
|
|
|
where
|
|
|
|
|
T: Read + Seek,
|
|
|
|
|
{
|
|
|
|
|
HspiceBlocksReader::new(reader, &self.blocks[..self.header_blk_cnt])
|
|
|
|
|
}
|
|
|
|
|
pub fn to_data_reader<T>(&'a self, reader: T) -> HspiceBlocksReader<'a, T>
|
|
|
|
|
where
|
|
|
|
|
T: Read + Seek,
|
|
|
|
|
{
|
|
|
|
|
HspiceBlocksReader::new(reader, &self.blocks[self.header_blk_cnt..])
|
|
|
|
|
HSpiceBlocksReader::new(reader, &self.blocks)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
struct HspiceBlocksReader<'a, T>
|
|
|
|
|
|
|
|
|
|
struct HSpiceBlocksReader<'a, T>
|
|
|
|
|
where
|
|
|
|
|
T: Read + Seek,
|
|
|
|
|
{
|
|
|
|
|
reader: T,
|
|
|
|
|
blocks: &'a [HspiceBlock],
|
|
|
|
|
blocks: &'a [HSpiceBlock],
|
|
|
|
|
|
|
|
|
|
/// The full size of this reader
|
|
|
|
|
total_len: u64,
|
|
|
|
@ -126,11 +163,11 @@ where
|
|
|
|
|
/// The cursor inside block.
|
|
|
|
|
cursor: u32,
|
|
|
|
|
}
|
|
|
|
|
impl<'a, T> HspiceBlocksReader<'a, T>
|
|
|
|
|
impl<'a, T> HSpiceBlocksReader<'a, T>
|
|
|
|
|
where
|
|
|
|
|
T: Read + Seek,
|
|
|
|
|
{
|
|
|
|
|
pub fn new(reader: T, blocks: &'a [HspiceBlock]) -> Self {
|
|
|
|
|
pub fn new(reader: T, blocks: &'a [HSpiceBlock]) -> Self {
|
|
|
|
|
// Build total length and offset table.
|
|
|
|
|
let mut total_len: u64 = 0;
|
|
|
|
|
let mut offset_table: Vec<u64> = Vec::with_capacity(blocks.len());
|
|
|
|
@ -142,7 +179,7 @@ where
|
|
|
|
|
// otherwise Seek impl will panic when SeekFrom::Current.
|
|
|
|
|
offset_table.push(total_len);
|
|
|
|
|
|
|
|
|
|
HspiceBlocksReader {
|
|
|
|
|
HSpiceBlocksReader {
|
|
|
|
|
reader,
|
|
|
|
|
blocks,
|
|
|
|
|
total_len,
|
|
|
|
@ -155,7 +192,7 @@ where
|
|
|
|
|
self.reader
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
impl<'a, T> Read for HspiceBlocksReader<'a, T>
|
|
|
|
|
impl<'a, T> Read for HSpiceBlocksReader<'a, T>
|
|
|
|
|
where
|
|
|
|
|
T: Read + Seek,
|
|
|
|
|
{
|
|
|
|
@ -213,7 +250,7 @@ where
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
impl<'a, T> Seek for HspiceBlocksReader<'a, T>
|
|
|
|
|
impl<'a, T> Seek for HSpiceBlocksReader<'a, T>
|
|
|
|
|
where
|
|
|
|
|
T: Read + Seek,
|
|
|
|
|
{
|
|
|
|
@ -269,6 +306,46 @@ where
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const SIGNAL_NAME_LEN: usize = 8;
|
|
|
|
|
const SIGNAL_NAME_MAGIC_TAIL: &[u8; 4] = b"$&%#";
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
|
|
|
pub enum HSpiceVersion {
|
|
|
|
|
V9007,
|
|
|
|
|
V9601,
|
|
|
|
|
V2001,
|
|
|
|
|
}
|
|
|
|
|
#[derive(Debug, TeError)]
|
|
|
|
|
#[error("can not parse given string into valid HSpice version")]
|
|
|
|
|
pub struct ParseHSpiceVersionError {}
|
|
|
|
|
impl FromStr for HSpiceVersion {
|
|
|
|
|
type Err = ParseHSpiceVersionError;
|
|
|
|
|
|
|
|
|
|
fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
|
|
|
|
|
match s {
|
|
|
|
|
"9007" => Ok(Self::V9007),
|
|
|
|
|
"9601" => Ok(Self::V9601),
|
|
|
|
|
"2001" => Ok(Self::V2001),
|
|
|
|
|
_ => Err(ParseHSpiceVersionError {}),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Debug)]
|
|
|
|
|
struct HSpiceDigest {
|
|
|
|
|
version: HSpiceVersion,
|
|
|
|
|
file_name: String,
|
|
|
|
|
timestamp: String,
|
|
|
|
|
copyright: String,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Debug)]
|
|
|
|
|
pub struct HSpiceContent {
|
|
|
|
|
digest: HSpiceDigest,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub type HSpiceData = HSpiceContent;
|
|
|
|
|
|
|
|
|
|
/// Guess whether given reader is a legal HSPICE signal file.
|
|
|
|
|
///
|
|
|
|
|
/// Please note that this is a vauge check.
|
|
|
|
@ -294,102 +371,9 @@ where
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Load given reader as HSPICE signal file.
|
|
|
|
|
pub fn load_signal<T>(reader: &mut T) -> Result<HSpiceData>
|
|
|
|
|
pub fn load_signal<T>(reader: T) -> Result<HSpiceData>
|
|
|
|
|
where
|
|
|
|
|
T: ReadBytesExt + Seek,
|
|
|
|
|
{
|
|
|
|
|
// Fetch blocks list first
|
|
|
|
|
let blocks = analyze_hspice_block(reader)?;
|
|
|
|
|
println!("{:#?}", blocks);
|
|
|
|
|
|
|
|
|
|
Ok(HSpiceData {})
|
|
|
|
|
Err(Error::Overflow)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn analyze_hspice_block<T>(reader: &mut T) -> Result<HspiceBlocks>
|
|
|
|
|
where
|
|
|
|
|
T: ReadBytesExt + Seek,
|
|
|
|
|
{
|
|
|
|
|
// Back to the file head
|
|
|
|
|
reader.rewind()?;
|
|
|
|
|
|
|
|
|
|
// Fetch the first u32 as little endian to decide the sim source
|
|
|
|
|
let endian_u32 = shared::read_int_value(Endian::LittleEndian, reader)?;
|
|
|
|
|
let sim_source = match endian_u32 {
|
|
|
|
|
LITTLE_ENDIAN_INT => SimSource::HspiceWin,
|
|
|
|
|
BIG_ENDIAN_INT => SimSource::Hspice,
|
|
|
|
|
other => return Err(Error::WrongEndianInt(other)),
|
|
|
|
|
};
|
|
|
|
|
let endian = sim_source.to_endian();
|
|
|
|
|
|
|
|
|
|
// Iterate each block
|
|
|
|
|
let mut blocks: Vec<HspiceBlock> = Vec::new();
|
|
|
|
|
reader.rewind()?;
|
|
|
|
|
loop {
|
|
|
|
|
// Read block header.
|
|
|
|
|
// If there is no more data, break this loop.
|
|
|
|
|
let mut block_header = [0u32; 4];
|
|
|
|
|
if let Err(e) = shared::read_int_values(endian, &mut block_header, reader) {
|
|
|
|
|
match e.kind() {
|
|
|
|
|
std::io::ErrorKind::UnexpectedEof => break,
|
|
|
|
|
_ => return Err(e.into()),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Check endian u32
|
|
|
|
|
if block_header[0] != ENDIAN_INT {
|
|
|
|
|
return Err(Error::WrongBlockHead(block_header[0]));
|
|
|
|
|
}
|
|
|
|
|
if block_header[2] != ENDIAN_INT {
|
|
|
|
|
return Err(Error::WrongBlockHead(block_header[2]));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Get the offset and length of block body
|
|
|
|
|
let block_offset = reader.stream_position()?;
|
|
|
|
|
let block_len = block_header[3];
|
|
|
|
|
|
|
|
|
|
// Skip block body to check block tail
|
|
|
|
|
reader.seek(SeekFrom::Current(i64::try_from(block_len).unwrap()))?;
|
|
|
|
|
let block_tail = shared::read_int_value(endian, reader)?;
|
|
|
|
|
if block_tail != block_len {
|
|
|
|
|
return Err(Error::WrongBlockTail(block_tail, block_len));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Push result
|
|
|
|
|
blocks.push(HspiceBlock::new(block_offset, block_len));
|
|
|
|
|
|
|
|
|
|
// Cursor now at the beginning of next block.
|
|
|
|
|
// We can continue analyze next blocks safely.
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Analyze header block count
|
|
|
|
|
let mut header_blk_cnt: Option<usize> = None;
|
|
|
|
|
for (i, block) in blocks.iter().enumerate() {
|
|
|
|
|
let signal_name_len = u32::try_from(SIGNAL_NAME_LEN).unwrap();
|
|
|
|
|
let offset = if block.block_len < signal_name_len {
|
|
|
|
|
continue;
|
|
|
|
|
} else {
|
|
|
|
|
block.compute_offset(block.block_len - signal_name_len)
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
reader.seek(SeekFrom::Start(offset))?;
|
|
|
|
|
let mut magic_word = [0u8; MAGIC_WORDS.len()];
|
|
|
|
|
reader.read_exact(&mut magic_word)?;
|
|
|
|
|
if &magic_word == MAGIC_WORDS {
|
|
|
|
|
match i.checked_add(1) {
|
|
|
|
|
Some(v) => header_blk_cnt = Some(v),
|
|
|
|
|
None => return Err(Error::Overflow),
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
let header_blk_cnt = match header_blk_cnt {
|
|
|
|
|
Some(v) => v,
|
|
|
|
|
None => return Err(Error::NoMagicWords),
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Build result and return.
|
|
|
|
|
Ok(HspiceBlocks::new(sim_source, blocks, header_blk_cnt))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const SIGNAL_NAME_LEN: usize = 8;
|
|
|
|
|
const MAGIC_WORDS: &[u8; 4] = b"$&%#";
|
|
|
|
|