#![doc = include_str!("../README.md")]
#![doc(html_logo_url = "https://github.com/DMoore12/sv-sim/blob/main/sv-sim-logo.png?raw=true")]
use log::{debug, error, trace, warn};
use logos::{Lexer, Logos};
use std::num::ParseIntError;
use std::fs;
use std::fmt;
pub mod var_types;
pub mod sim_time;
use sim_time::*;
pub mod module;
use module::*;
#[derive(Default, Debug, Clone, PartialEq)]
pub enum LexingError {
InvalidInteger(String),
#[default]
UnexpectedToken,
ImproperTimeFormatting,
NonAsciiCharacter,
IncompleteWidth,
NegativeBitWidth,
}
impl Into<String> for LexingError {
fn into(self) -> String {
match self {
Self::InvalidInteger(error) => format!("invalid integer encountered: {error:}"),
Self::UnexpectedToken => "unexpected token encountered".to_owned(),
Self::ImproperTimeFormatting => "improper time format encountered".to_owned(),
Self::IncompleteWidth => "incomplete width encountered".to_owned(),
Self::NegativeBitWidth => "negative bit width encountered".to_owned(),
_ => "generic/unknown error encountered".to_owned(),
}
}
}
impl From<ParseIntError> for LexingError {
fn from(err: ParseIntError) -> Self {
use std::num::IntErrorKind::*;
match err.kind() {
PosOverflow | NegOverflow => LexingError::InvalidInteger("overflow error".to_owned()),
_ => LexingError::InvalidInteger("unknown error".to_owned()),
}
}
}
#[derive(Logos, Debug, PartialEq)]
#[logos(error = LexingError)]
#[logos(skip r"[\r\f]+")]
pub enum Token {
#[token("module")]
Module,
#[token("endmodule")]
EndModule,
#[token("parameter")]
Parameter,
#[token("inout")]
Inout,
#[token("input")]
Input,
#[token("output")]
Output,
#[token("reg")]
Reg,
#[token("wire")]
Wire,
#[token("assign")]
Assign,
#[token("always_comb")]
Comb,
#[token("if")]
If,
#[token("else")]
Else,
#[token("begin")]
Begin,
#[token("end")]
End,
#[token("posedge")]
Posedge,
#[token("negedge")]
Negedge,
#[token("timescale")]
Timescale,
#[regex(r"\d+ns", nanosecond)]
#[regex(r"\d+ps", picosecond)]
Time(f64),
#[token("#")]
Pound,
#[token("(")]
OpenParen,
#[token(")")]
CloseParen,
#[token("[")]
OpenBracket,
#[token("]")]
CloseBracket,
#[token("{")]
OpenBrace,
#[token("}")]
CloseBrace,
#[token("==")]
BEQ,
#[token("<")]
BLT,
#[token(">")]
BGT,
#[token("<=")]
BLTE,
#[token(">=")]
BGTE,
#[token("=")]
Equals,
#[token("-")]
Subtract,
#[token("+")]
Add,
#[token("*")]
Multiply,
#[token("/")]
Divide,
#[token("?")]
QMark,
#[token("!")]
EMark,
#[token(":")]
Colon,
#[token(";")]
Semicolon,
#[token(",")]
Comma,
#[token("`")]
BTick,
#[token("_")]
Underscore,
#[token("@")]
At,
#[token("\n")]
Newline,
#[regex(r"[ ]+")]
#[regex(r"\t")]
WhiteSpace,
#[regex(r"\d+'b\d+")]
BinaryValue,
#[regex(r"\d+'bz")]
HiZValue,
#[regex(r"//")]
Comment,
#[regex(r"[a-zA-Z]+")]
Word,
#[regex(r"[0-9]+", |lex| lex.slice().parse())]
Integer(u64),
}
pub fn read_sv_file(path: &std::path::PathBuf) -> Result<String, std::io::Error> {
trace!("reading sv file {:?}", path);
Ok(fs::read_to_string(path)?)
}
#[derive(Default)]
pub struct SimObject {
pub sim_time: SimTime,
pub mods: Vec<Module>,
}
impl fmt::Debug for SimObject {
fn fmt(&self, _: &mut std::fmt::Formatter) -> fmt::Result {
debug!("{:?}", self.sim_time);
for module in &self.mods {
format!("{module:?}");
}
Ok(())
}
}
pub fn parse_sv_file(file_contents: String) -> Result<SimObject, LexingError> {
let mut lexer = Token::lexer(file_contents.as_str());
let mut sim_time = SimTime::default();
let mut mods: Vec<Module> = Vec::new();
let mut errors: Vec<LexingError> = Vec::new();
trace!("parsing sv file");
while let Some(token) = lexer.next() {
match token {
Ok(Token::Module) => match parse_module(&mut lexer) {
Ok(module) => mods.push(module),
Err(e) => errors.push(e),
},
Ok(Token::BTick) => match parse_sim_time(&mut lexer) {
Ok(val) => sim_time = val,
Err(e) => errors.push(e),
},
Ok(Token::Comment) => match parse_comment(&mut lexer) {
Ok(_) => (),
Err(e) => errors.push(e),
},
Ok(Token::Newline) | Ok(Token::WhiteSpace) => (),
Err(e) => {
error!(
"unexpected error occurred parsing sv file: '{}'",
lexer.slice()
);
return Err(e);
}
_ => warn!("{:?} not implemented", token.unwrap()),
}
}
for error in errors {
error!(
"lexing error parsing sv file: {}",
<LexingError as Into<String>>::into(error)
);
}
Ok(SimObject { sim_time, mods })
}
fn parse_comment<'source>(lexer: &mut Lexer<'source, Token>) -> Result<(), LexingError> {
trace!("parsing comment");
while let Some(token) = lexer.next() {
match token {
Ok(Token::Newline) => return Ok(()),
Err(e) => {
error!(
"unexpected error occurred parsing comment: '{}'",
lexer.slice()
);
return Err(e);
}
_ => (),
};
}
Ok(())
}