add hspice block analyze

This commit is contained in:
yyc12345 2025-06-04 22:02:26 +08:00
parent 9319508ff4
commit cfbc71f303
4 changed files with 301 additions and 0 deletions

View File

@ -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,
})
}

View File

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

View 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);
});
}

View File

@ -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>()?,
})
}