add hspice block analyze
This commit is contained in:
parent
9319508ff4
commit
cfbc71f303
@ -0,0 +1,167 @@
|
|||||||
|
use super::shared::{self, Endian};
|
||||||
|
use byteorder::{BigEndian, LittleEndian, NativeEndian, ReadBytesExt};
|
||||||
|
use std::io::{Read, Seek, SeekFrom};
|
||||||
|
use thiserror::Error as TeError;
|
||||||
|
|
||||||
|
// Reference:
|
||||||
|
//
|
||||||
|
// If these URLs are unreachable, use Internet Archive instead.
|
||||||
|
//
|
||||||
|
// https://github.com/HMC-ACE/hspiceParser/blob/main/hSpice_output.md
|
||||||
|
// https://www.rvq.fr/linux/gawfmt.php
|
||||||
|
|
||||||
|
#[derive(Debug, TeError)]
|
||||||
|
pub enum Error {
|
||||||
|
#[error("IO error {0}")]
|
||||||
|
Io(#[from] std::io::Error),
|
||||||
|
#[error("fail to cast value into integral value")]
|
||||||
|
CastToInt(#[from] std::num::TryFromIntError),
|
||||||
|
#[error("wrong endian integer {0:#X} in block head. it must be one of 0x4 or 0x4000000")]
|
||||||
|
WrongEndianInt(u32),
|
||||||
|
#[error("wrong block head value {0:#X}. it must be 0x4")]
|
||||||
|
WrongBlockHead(u32),
|
||||||
|
#[error("wrong block tail value {0:#X}. it must be equal to head given value {1:#X}")]
|
||||||
|
WrongBlockTail(u32, u32),
|
||||||
|
}
|
||||||
|
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)]
|
||||||
|
pub enum SimSource {
|
||||||
|
/// For HSPICE, read as big endian.
|
||||||
|
Hspice,
|
||||||
|
/// For HSPICE on Windows, read as little endian.
|
||||||
|
HspiceWin,
|
||||||
|
}
|
||||||
|
impl SimSource {
|
||||||
|
fn to_endian(&self) -> Endian {
|
||||||
|
match *self {
|
||||||
|
SimSource::Hspice => Endian::BigEndian,
|
||||||
|
SimSource::HspiceWin => Endian::LittleEndian,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct HspiceBlock {
|
||||||
|
pub block_offset: u64,
|
||||||
|
pub block_len: usize,
|
||||||
|
}
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct HspiceBlocks {
|
||||||
|
pub sim_source: SimSource,
|
||||||
|
pub blocks: Vec<HspiceBlock>,
|
||||||
|
pub header_blk_cnt: usize,
|
||||||
|
pub data_blk_cnt: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Guess whether given reader is a legal HSPICE signal file.
|
||||||
|
///
|
||||||
|
/// Please note that this is a vauge check.
|
||||||
|
/// There is no guarantee that the file with `true` return value of this function,
|
||||||
|
/// can be loaded successfully.
|
||||||
|
pub fn guess_signal<T>(reader: &mut T) -> bool
|
||||||
|
where
|
||||||
|
T: ReadBytesExt + Seek,
|
||||||
|
{
|
||||||
|
// Back to the head of file.
|
||||||
|
if let Err(_) = reader.seek(SeekFrom::Start(0)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check whether first u32 is 4 (in little or big endian)
|
||||||
|
match reader.read_u32::<NativeEndian>() {
|
||||||
|
Ok(val) => match val {
|
||||||
|
LITTLE_ENDIAN_INT | BIG_ENDIAN_INT => true,
|
||||||
|
_ => false,
|
||||||
|
},
|
||||||
|
Err(_) => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Load given reader as HSPICE signal file.
|
||||||
|
pub fn load_signal<T>(reader: &mut T) -> Result<()>
|
||||||
|
where
|
||||||
|
T: ReadBytesExt + Seek,
|
||||||
|
{
|
||||||
|
// Fetch blocks list first
|
||||||
|
let blocks = analyze_hspice_block(reader)?;
|
||||||
|
println!("{:#?}", blocks);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn analyze_hspice_block<T>(reader: &mut T) -> Result<HspiceBlocks>
|
||||||
|
where
|
||||||
|
T: ReadBytesExt + Seek,
|
||||||
|
{
|
||||||
|
// Back to the file head
|
||||||
|
reader.seek(SeekFrom::Start(0))?;
|
||||||
|
|
||||||
|
// Fetch the first u32 as little endian to decide the sim source
|
||||||
|
let endian_u32 = reader.read_u32::<LittleEndian>()?;
|
||||||
|
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.seek(SeekFrom::Start(0))?;
|
||||||
|
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 raw_block_len = block_header[3];
|
||||||
|
let block_offset = reader.seek(SeekFrom::Current(0))?;
|
||||||
|
let block_len = usize::try_from(raw_block_len).unwrap();
|
||||||
|
|
||||||
|
// Skip block body to check block tail
|
||||||
|
reader.seek(SeekFrom::Current(i64::try_from(raw_block_len).unwrap()))?;
|
||||||
|
let block_tail = shared::read_int_value(endian, reader)?;
|
||||||
|
if block_tail != raw_block_len {
|
||||||
|
return Err(Error::WrongBlockTail(block_tail, raw_block_len));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Push result
|
||||||
|
blocks.push(HspiceBlock {
|
||||||
|
block_offset,
|
||||||
|
block_len,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Cursor now at the beginning of next block.
|
||||||
|
// We can continue analyze next blocks safely.
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO:
|
||||||
|
// Analyze header block count
|
||||||
|
|
||||||
|
// Build result and return.
|
||||||
|
Ok(HspiceBlocks {
|
||||||
|
sim_source,
|
||||||
|
blocks,
|
||||||
|
header_blk_cnt: 0,
|
||||||
|
data_blk_cnt: 0,
|
||||||
|
})
|
||||||
|
}
|
@ -1,3 +1,52 @@
|
|||||||
|
/*
|
||||||
|
* loadsig.c: retrieval of HSPICE data into MATLAB
|
||||||
|
* written by Michael Perrott while at Silicon
|
||||||
|
* Laboratories. This code leveraged the work of
|
||||||
|
* Stephen G. Tell (i.e., his Gwave viewer) to
|
||||||
|
* develop the Hspice loading functions. At this
|
||||||
|
* point, the code here has little similarity to the
|
||||||
|
* routines he provided, but many thanks to Stephen
|
||||||
|
* for providing enough info to get started.
|
||||||
|
* For those wishing to modify this code - good luck!
|
||||||
|
* Unfortunatley, it is quite "hacked" due
|
||||||
|
* to the fact that the actual binary format of Hspice output
|
||||||
|
* was never provided (i.e., I simply updated the
|
||||||
|
* code each time a new issue was found), and due to the
|
||||||
|
* fact that I had little time to develop it. However,
|
||||||
|
* after years of use, it's pretty solid now.
|
||||||
|
*
|
||||||
|
* To compile this code into a mex file for Matlab, simply
|
||||||
|
* run Matlab in the directory that loadsig.c is contained
|
||||||
|
* and then type:
|
||||||
|
* mex loadsig.c
|
||||||
|
* You'll then have a loadsig mex function for whatever
|
||||||
|
* computer (i.e., Sun, Linux, Windows) that you're running
|
||||||
|
* on at the time.
|
||||||
|
*
|
||||||
|
* I do ask for one thing for those that use this code - please
|
||||||
|
* keep my name and Silicon Labs attached to it when the user
|
||||||
|
* first executes it (as currently done). I am not particularly
|
||||||
|
* interested in getting anything for this package other then
|
||||||
|
* recognition, but I do want that since this was a nontrivial
|
||||||
|
* amount of work. Of course, if you want to throw money at me,
|
||||||
|
* I'm happy to accomodate you. :) - Michael Perrott 6/3/03
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Library General Public
|
||||||
|
* License along with this library; if not, write to the MxFree
|
||||||
|
* Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
pub mod shared;
|
pub mod shared;
|
||||||
pub mod hspice;
|
pub mod hspice;
|
||||||
pub mod ngspice;
|
pub mod ngspice;
|
||||||
|
7
loadsig/loadsig/src/main.rs
Normal file
7
loadsig/loadsig/src/main.rs
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
fn main() {
|
||||||
|
let args: Vec<_> = std::env::args().collect();
|
||||||
|
let mut reader = std::fs::File::open(&args[1]).unwrap();
|
||||||
|
loadsig::hspice::load_signal(&mut reader).unwrap_or_else(|e| {
|
||||||
|
eprintln!("{}", e);
|
||||||
|
});
|
||||||
|
}
|
@ -0,0 +1,78 @@
|
|||||||
|
use byteorder::{BigEndian, LittleEndian, NativeEndian, ReadBytesExt};
|
||||||
|
use std::io::Result;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
pub enum Endian {
|
||||||
|
NativeEndian,
|
||||||
|
BigEndian,
|
||||||
|
LittleEndian,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read_int_values<T>(endian: Endian, buf: &mut [u32], reader: &mut T) -> Result<()>
|
||||||
|
where
|
||||||
|
T: ReadBytesExt,
|
||||||
|
{
|
||||||
|
match endian {
|
||||||
|
Endian::NativeEndian => reader.read_u32_into::<NativeEndian>(buf)?,
|
||||||
|
Endian::BigEndian => reader.read_u32_into::<BigEndian>(buf)?,
|
||||||
|
Endian::LittleEndian => reader.read_u32_into::<LittleEndian>(buf)?,
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read_int_value<T>(endian: Endian, reader: &mut T) -> Result<u32>
|
||||||
|
where
|
||||||
|
T: ReadBytesExt,
|
||||||
|
{
|
||||||
|
Ok(match endian {
|
||||||
|
Endian::NativeEndian => reader.read_u32::<NativeEndian>()?,
|
||||||
|
Endian::BigEndian => reader.read_u32::<BigEndian>()?,
|
||||||
|
Endian::LittleEndian => reader.read_u32::<LittleEndian>()?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read_float_values<T>(endian: Endian, buf: &mut [f32], reader: &mut T) -> Result<()>
|
||||||
|
where
|
||||||
|
T: ReadBytesExt,
|
||||||
|
{
|
||||||
|
match endian {
|
||||||
|
Endian::NativeEndian => reader.read_f32_into::<NativeEndian>(buf)?,
|
||||||
|
Endian::BigEndian => reader.read_f32_into::<BigEndian>(buf)?,
|
||||||
|
Endian::LittleEndian => reader.read_f32_into::<LittleEndian>(buf)?,
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read_float_value<T>(endian: Endian, reader: &mut T) -> Result<f32>
|
||||||
|
where
|
||||||
|
T: ReadBytesExt,
|
||||||
|
{
|
||||||
|
Ok(match endian {
|
||||||
|
Endian::NativeEndian => reader.read_f32::<NativeEndian>()?,
|
||||||
|
Endian::BigEndian => reader.read_f32::<BigEndian>()?,
|
||||||
|
Endian::LittleEndian => reader.read_f32::<LittleEndian>()?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read_double_values<T>(endian: Endian, buf: &mut [f64], reader: &mut T) -> Result<()>
|
||||||
|
where
|
||||||
|
T: ReadBytesExt,
|
||||||
|
{
|
||||||
|
match endian {
|
||||||
|
Endian::NativeEndian => reader.read_f64_into::<NativeEndian>(buf)?,
|
||||||
|
Endian::BigEndian => reader.read_f64_into::<BigEndian>(buf)?,
|
||||||
|
Endian::LittleEndian => reader.read_f64_into::<LittleEndian>(buf)?,
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read_double_value<T>(endian: Endian, reader: &mut T) -> Result<f64>
|
||||||
|
where
|
||||||
|
T: ReadBytesExt,
|
||||||
|
{
|
||||||
|
Ok(match endian {
|
||||||
|
Endian::NativeEndian => reader.read_f64::<NativeEndian>()?,
|
||||||
|
Endian::BigEndian => reader.read_f64::<BigEndian>()?,
|
||||||
|
Endian::LittleEndian => reader.read_f64::<LittleEndian>()?,
|
||||||
|
})
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user