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 hspice;
|
||||
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