use crate::SignalRef;
use num_enum::TryFromPrimitive;
use std::io::BufRead;
use std::num::NonZeroU32;
use thiserror::Error;
#[derive(Debug, Error)]
pub enum GhwParseError {
#[error("[ghw] unsupported compression: {0}")]
UnsupportedCompression(String),
#[error("[ghw] unexpected header magic: {0}")]
UnexpectedHeaderMagic(String),
#[error("[ghw] unexpected value in header: {0}")]
UnexpectedHeader(String),
#[error("[ghw] unexpected section start: {0}")]
UnexpectedSection(String),
#[error("[ghw] unexpected type: {0}, {1}")]
UnexpectedType(String, &'static str),
#[error("[ghw] failed to parse a {0} section: {1}")]
FailedToParseSection(&'static str, String),
#[error("[ghw] expected positive integer, not: {0}")]
ExpectedPositiveInteger(i64),
#[error("[ghw] float range has no length: {0} .. {1}")]
#[allow(dead_code)]
FloatRangeLen(f64, f64),
#[error("[ghw] failed to parse GHDL RTIK: {0}.")]
FailedToParseGhdlRtik(#[from] num_enum::TryFromPrimitiveError<GhwRtik>),
#[error("[ghw] failed to parse well known type: {0}.")]
FailedToParseWellKnownType(#[from] num_enum::TryFromPrimitiveError<GhwWellKnownType>),
#[error("[ghw] failed to parse hierarchy kind: {0}.")]
FailedToParseHierarchyKind(#[from] num_enum::TryFromPrimitiveError<GhwHierarchyKind>),
#[error("[ghw] failed to parse a leb128 encoded number: {0}.")]
FailedToParsLeb128(#[from] leb128::read::Error),
#[error("[ghw] failed to decode string: {0}.")]
Utf8(#[from] std::str::Utf8Error),
#[error("[ghw] failed to parse an integer: {0}.")]
ParseInt(#[from] std::num::ParseIntError),
#[error("[ghw] I/O operation failed: {0}.")]
Io(#[from] std::io::Error),
}
pub const GHW_STRING_SECTION: &[u8; 4] = b"STR\x00";
pub const GHW_HIERARCHY_SECTION: &[u8; 4] = b"HIE\x00";
pub const GHW_TYPE_SECTION: &[u8; 4] = b"TYP\x00";
pub const GHW_WK_TYPE_SECTION: &[u8; 4] = b"WKT\x00";
pub const GHW_END_OF_HEADER_SECTION: &[u8; 4] = b"EOH\x00";
pub const GHW_DIRECTORY_SECTION: &[u8; 4] = b"DIR\x00";
pub const GHW_TAILER_SECTION: &[u8; 4] = b"TAI\x00";
pub const GHW_END_DIRECTORY_SECTION: &[u8; 4] = b"EOD\x00";
pub const GHW_SNAPSHOT_SECTION: &[u8; 4] = b"SNP\x00";
pub const GHW_CYCLE_SECTION: &[u8; 4] = b"CYC\x00";
pub const GHW_END_SNAPSHOT_SECTION: &[u8; 4] = b"ESN\x00";
pub const GHW_END_CYCLE_SECTION: &[u8; 4] = b"ECY\x00";
pub const GHW_TAILER_LEN: usize = 12;
pub type Result<T> = std::result::Result<T, GhwParseError>;
pub fn read_directory(header: &HeaderData, input: &mut impl BufRead) -> Result<Vec<SectionPos>> {
let mut h = [0u8; 8];
input.read_exact(&mut h)?;
let num_entries = header.read_u32(&mut &h[4..8])?;
let mut sections = Vec::new();
for _ in 0..num_entries {
let mut id = [0u8; 4];
input.read_exact(&mut id)?;
let mut buf = [0u8; 4];
input.read_exact(&mut buf)?;
let pos = header.read_u32(&mut &buf[..])?;
sections.push(SectionPos { id, pos });
}
check_magic_end(input, "directory", GHW_END_DIRECTORY_SECTION)?;
Ok(sections)
}
#[derive(Debug)]
pub struct SectionPos {
#[allow(dead_code)]
pub id: [u8; 4],
#[allow(dead_code)]
pub pos: u32,
}
pub fn check_header_zeros(section: &'static str, header: &[u8]) -> Result<()> {
if header.len() < 4 {
return Err(GhwParseError::FailedToParseSection(
section,
"first four bytes should be zero".to_string(),
));
}
let zeros = &header[..4];
if zeros == b"\x00\x00\x00\x00" {
Ok(())
} else {
Err(GhwParseError::FailedToParseSection(
section,
format!(
"first four bytes should be zero and not {}",
String::from_utf8_lossy(zeros)
),
))
}
}
pub fn check_magic_end(
input: &mut impl BufRead,
section: &'static str,
expected: &[u8],
) -> Result<()> {
let mut end_magic = [0u8; 4];
input.read_exact(&mut end_magic)?;
if end_magic == expected {
Ok(())
} else {
Err(GhwParseError::UnexpectedSection(format!(
"expected {section} section to end in {}, not {}",
String::from_utf8_lossy(expected),
String::from_utf8_lossy(&end_magic)
)))
}
}
#[inline]
pub fn read_u8(input: &mut impl BufRead) -> Result<u8> {
let mut buf = [0u8];
input.read_exact(&mut buf)?;
Ok(buf[0])
}
#[inline]
pub fn read_f64_le(input: &mut impl BufRead) -> Result<f64> {
let mut buf = [0u8; 8];
input.read_exact(&mut buf)?;
Ok(f64::from_le_bytes(buf))
}
#[derive(Debug)]
pub struct HeaderData {
pub version: u8,
pub big_endian: bool,
#[allow(dead_code)]
pub word_len: u8,
#[allow(dead_code)]
pub word_offset: u8,
}
impl HeaderData {
#[inline]
pub fn read_i32(&self, input: &mut impl BufRead) -> Result<i32> {
let mut b = [0u8; 4];
input.read_exact(&mut b)?;
if self.big_endian {
Ok(i32::from_be_bytes(b))
} else {
Ok(i32::from_le_bytes(b))
}
}
#[inline]
pub fn read_u32(&self, input: &mut impl BufRead) -> Result<u32> {
let ii = self.read_i32(input)?;
if ii >= 0 {
Ok(ii as u32)
} else {
Err(GhwParseError::ExpectedPositiveInteger(i64::from(ii)))
}
}
#[inline]
pub fn read_i64(&self, input: &mut impl BufRead) -> Result<i64> {
let mut b = [0u8; 8];
input.read_exact(&mut b)?;
if self.big_endian {
Ok(i64::from_be_bytes(b))
} else {
Ok(i64::from_le_bytes(b))
}
}
}
#[derive(Debug, Clone, Copy)]
pub struct GhwSignalInfo {
tpe_and_vec: NonZeroU32,
signal_ref: SignalRef,
}
impl GhwSignalInfo {
pub fn new(tpe: SignalType, signal_ref: SignalRef, vector: Option<usize>) -> Self {
let raw_tpe = u32::from(tpe as u8) + 1;
debug_assert_eq!(raw_tpe & 0x7, raw_tpe);
let tpe_and_vec = if let Some(vector) = vector {
let raw_vector_id = (vector as u32) + 1;
debug_assert_eq!((raw_vector_id << 3) >> 3, raw_vector_id);
NonZeroU32::new((raw_vector_id << 3) | raw_tpe).unwrap()
} else {
NonZeroU32::new(raw_tpe).unwrap()
};
Self {
tpe_and_vec,
signal_ref,
}
}
pub fn signal_ref(&self) -> SignalRef {
self.signal_ref
}
pub fn vec_id(&self) -> Option<GhwVecId> {
let vec_id = self.tpe_and_vec.get() >> 3;
if vec_id == 0 {
None
} else {
Some(GhwVecId::new(vec_id as usize))
}
}
pub fn tpe(&self) -> SignalType {
let value = self.tpe_and_vec.get();
let raw_tpe = (value & 0x7) as u8;
SignalType::try_from_primitive(raw_tpe - 1).unwrap()
}
}
pub type GhwDecodeInfo = (GhwSignals, Vec<GhwVecInfo>);
#[derive(Debug, Default)]
pub struct GhwSignals {
signals: Vec<GhwSignalInfo>,
}
impl GhwSignals {
pub fn new(signals: Vec<GhwSignalInfo>) -> Self {
Self { signals }
}
pub fn get_info(&self, signal_id: GhwSignalId) -> &GhwSignalInfo {
&self.signals[signal_id.index()]
}
pub fn signal_len(&self) -> usize {
self.signals.len()
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct GhwVecId(NonZeroU32);
impl GhwVecId {
pub fn new(pos_id: usize) -> Self {
Self(NonZeroU32::new(pos_id as u32).unwrap())
}
pub fn index(self) -> usize {
(self.0.get() - 1) as usize
}
}
#[derive(Debug, Copy, Clone, PartialEq)]
pub struct GhwSignalId(NonZeroU32);
impl GhwSignalId {
#[inline]
pub fn index(self) -> usize {
(self.0.get() - 1) as usize
}
pub fn new(pos_id: u32) -> Self {
Self(NonZeroU32::new(pos_id).unwrap())
}
}
#[derive(Debug, Clone, Copy)]
pub struct GhwVecInfo {
max: GhwSignalId,
min: GhwSignalId,
two_state: bool,
signal_ref: SignalRef,
alias: Option<NonZeroU32>,
}
impl GhwVecInfo {
pub fn new(min: GhwSignalId, max: GhwSignalId, two_state: bool, signal_ref: SignalRef) -> Self {
Self {
min,
max,
two_state,
signal_ref,
alias: None,
}
}
pub fn bits(&self) -> u32 {
(self.max.index() - self.min.index() + 1) as u32
}
pub fn is_two_state(&self) -> bool {
self.two_state
}
pub fn min(&self) -> GhwSignalId {
self.min
}
pub fn max(&self) -> GhwSignalId {
self.max
}
pub fn signal_ref(&self) -> SignalRef {
self.signal_ref
}
pub fn alias(&self) -> Option<NonZeroU32> {
self.alias
}
pub fn set_alias(&mut self, alias: NonZeroU32) {
self.alias = Some(alias);
}
}
#[repr(u8)]
#[derive(Debug, PartialEq, Copy, Clone, TryFromPrimitive)]
pub enum SignalType {
NineState,
NineStateVec,
TwoState,
TwoStateVec,
U8,
Leb128Signed,
F64,
}
pub const GHW_GZIP_HEADER: &[u8; 2] = &[0x1f, 0x8b];
pub const GHW_BZIP2_HEADER: &[u8; 2] = b"BZ";
pub const GHW_HEADER_START: &[u8] = b"GHDLwave\n";
#[repr(u8)]
#[derive(Debug, PartialEq, Copy, Clone, TryFromPrimitive)]
pub enum GhwWellKnownType {
Unknown = 0,
Boolean = 1,
Bit = 2,
StdULogic = 3,
}
#[repr(u8)]
#[derive(Debug, PartialEq, Copy, Clone, TryFromPrimitive)]
pub enum GhwRtik {
Error = 0,
EndOfScope = 15,
Signal = 16,
PortIn = 17,
PortOut = 18,
PortInOut = 19,
PortBuffer = 20,
PortLinkage = 21,
TypeB2 = 22,
TypeE8 = 23,
TypeI32 = 25,
TypeI64 = 26,
TypeF64 = 27,
TypeP32 = 28,
TypeP64 = 29,
TypeArray = 31,
TypeRecord = 32,
SubtypeScalar = 34,
SubtypeArray = 35,
SubtypeUnboundedArray = 37,
SubtypeRecord = 38,
SubtypeUnboundedRecord = 39,
}
#[repr(u8)]
#[derive(Debug, PartialEq, Copy, Clone, TryFromPrimitive)]
pub enum GhwHierarchyKind {
End = 0,
Design = 1,
Block = 3,
GenerateIf = 4,
GenerateFor = 5,
Instance = 6,
Package = 7,
Process = 13,
Generic = 14,
EndOfScope = 15,
Signal = 16,
PortIn = 17,
PortOut = 18,
PortInOut = 19,
Buffer = 20,
Linkage = 21,
}
pub const STD_LOGIC_VALUES: [u8; 9] = [b'u', b'x', b'0', b'1', b'z', b'w', b'l', b'h', b'-'];
pub const STD_LOGIC_LUT: [u8; 9] = [5, 2, 0, 1, 3, 6, 7, 4, 8];
pub const VHDL_BIT_VALUES: [u8; 2] = [b'0', b'1'];
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_sizes() {
assert_eq!(std::mem::size_of::<GhwSignalInfo>(), 8);
assert_eq!(std::mem::size_of::<GhwVecInfo>(), 20);
}
}