#!/usr/bin/perl

#----------------------------------------------------------
#	A Verilog-stimulus-generator.
#	Given an assembly language program with a pre-defined 
#	instruction set of size 7, this generates an equivalent 
#	Verilog stimulus code.
#----------------------------------------------------------

#---------------------------------------------------------
# Caution : Give only even integer values for CLOCK_PERIOD.
# CLOCK_PERIOD / 2 is used in stimulus block to generate a 
# clock waveform.Rounding errors will cause a problem if
# odd integers are used.
#--------------------------------------------------------

$CLOCK_PERIOD = 10;
$outfile = "stimulus.v";

open(VSM,"stimulus.vsm") or 
			die "Can't open stimulus.vsm $!\n";
open(OUT,">$outfile")	or 
			die "Can't write to output file $!\n";

$time = 0;

$lineno = 0;
$errors = 0;
$commentline = 0;

#-----------------------------------------------------------
# Adding comment lines to the generated file
# BTW, in VSM, comments are perl-style-beginning
# with a # and spanning whatever is there after it
# on the same line.
#----------------------------------------------------------

print OUT "//---------------------------------------------",
				"------\n";
print OUT "// Verilog stimulus code for a 4-bit processor\n";
print OUT "// Machine-generated : Don't edit\n";
print OUT "// Created with vsm-to-verilog converter v0.0,\n";
print OUT "// written in perl.\n";
print OUT "//---------------------------------------------",
				"------\n\n";

#----------------------------------------------------------
# Adding the net-definitions and the initial block
# for the clock.Also instantiating a decoder module.
#---------------------------------------------------------

print OUT "`include \"processor.v\"\n\n"; 
print OUT "module stimulus;\n\n";
print OUT "reg [3:0] op1;\n";
print OUT "reg [3:0] op2;\n";
print OUT "reg [2:0] opcode;\n\n";

print OUT "reg clk;\n";
print OUT "reg reset;\n";
print OUT "reg next_inst;\n\n";

print OUT "Processor proc",
			"(.opcode(opcode),.op1(op1),.op2(op2),
			.clk(clk),.reset(reset), .next_inst(next_inst));\n\n";

print OUT "initial\nbegin\nclk = 1'b0;\nnext_inst=1'b0;\n";
print OUT "reset = 1'b1;\n";
print OUT "forever #",$CLOCK_PERIOD / 2," clk = ~clk;\nend\n\n";

print OUT "initial\n\$dumpfile(\"proc.dmp\");\n\n";
print OUT "initial\n\$dumpvars(3,stimulus);\n\n";
print OUT "initial\nbegin\n#200;";

#---------------------------------------------------------
# Read line-by-line of the VSM(Verilog assembly)
# code and convert them to proper stimulus code.
# Warnings : 1) When operands are not numbers
#			 2) When operands are out of range
# Errors : 1)When an unknown instruction is
#			 encountered.
#---------------------------------------------------------

while($line = <VSM>)
{
	$lineno++;
	if ($line =~ /^\s+#.*/)
	{
		$commentline = 1;
		# do nothing.It's a comment.
	}
	else
	{
		($line) = split /#/,$line;
		$line =~ s/^\s+//g;
		if ($line)
		{
			#if($line =~ /\s.*/)
			#	$line = 
			($opcode,$op1,$op2) = split /\s+/,$line;
			
			$oneop = ($opcode =~ /\bONEOP\b/i);
			#	$oneop = 1;
	
			if ($op1 !~ /^\d+$/ && !$oneop) 
			{
				print "Warning: line $lineno: ",
				"operand 1 \"$op1\" is not a number\n";
				$warns ++;
				$op1 = 0;
			}

			if ($op2 !~ /^\d+$/) 
			{
				print "Warning: line $lineno: ",
				"operand 2 \"$op2\" is not a number\n";
				$warns ++;
				$op2 = 0;
			}
	
			$m1 = $op1 % 16;
			$m2 = $op2 % 16;
	
			if ($m1 != $op1)
			{
				print "Warning: line $lineno: ",
				"operand 1 out of range\n";
				$warns++;
			}

			if ($m2 != $op2)
			{
				print "Warning: line $lineno: ",
				"operand 2 out of range\n";
				$warns++;
			}

			if ($opcode =~ /\bSTO\b/i) 
			{	
				print OUT "// STO instruction\n";
				print OUT "#",$time," ";
				print OUT "opcode = 3'b001;\n";
				$time = $CLOCK_PERIOD;
			}
			elsif ($opcode =~ /\bADD\b/i) 
			{
				print OUT "// ADD instruction\n";
				print OUT "#",$time," ";
				print OUT "opcode = 3'b010;\n";
				$time = $CLOCK_PERIOD * 6;
			}
			elsif ($opcode =~ /\bSUB\b/i) 
			{ 
				print OUT "// SUB instruction\n";
				print OUT "#",$time," ";
				print OUT "opcode = 3'b011;\n";
				$time = $CLOCK_PERIOD * 6;
			}
			elsif ($opcode =~ /\bOR\b/i) 
			{ 
				print OUT "// OR instruction\n";
				print OUT "#",$time," ";
				print OUT "opcode = 3'b101;\n";
				$time = $CLOCK_PERIOD * 6;
			}
			elsif ($opcode =~ /\bXOR\b/i) 
			{ 
				print OUT "// XOR instruction\n";
				print OUT "#",$time," ";
				print OUT "opcode = 3'b110;\n";
				$time = $CLOCK_PERIOD * 6;
			}
			elsif ($opcode =~ /\bAND\b/i) 
			{ 	
				print OUT "// AND instruction\n";
				print OUT "#",$time," ";
				print OUT "opcode = 3'b100;\n";
				$time = $CLOCK_PERIOD * 6;
			}	
			elsif ($opcode =~ /\bNOT\b/i) 
			{ 
				print OUT "// NOT instruction\n";
				print OUT "#",$time," ";
				print OUT "opcode = 3'b111;\n";
				$time = $CLOCK_PERIOD * 6;
			}
			elsif ($opcode =~ /\bONEOP\b/i) 
			{ 
				print OUT "// ONEOP instruction\n";
				print OUT "#",$time," ";
				print OUT "opcode = 3'b000;\n";
				if($op1 =~ /\binc\b/i)
					{$op1 = 0;}
				elsif($op1 =~ /\bdec\b/i)
					{$op1 = 3 ;}
				elsif($op1 =~ /\bnot\b/i)
					{$op1 = 7;}
				elsif($op1 =~ /\bshr\b/i)
					{$op1 = 1;}
				elsif($op1 =~ /\bshl\b/i)
					{$op1 = 2;}
				else
				{
					print "Error:line $lineno: ",
					"unknown unary operator\n";
					$errors++;
				}
				$time = $CLOCK_PERIOD * 6;
			}
			elsif ($opcode =~ /\bRESET\b/i)
			{
				$commentline = 1;
				print OUT "#",$time," ","reset = 1'b0;\n";
			}	
			else
			{
				print "Error:line $lineno: ",
				"unknown instruction\n";
				$errors++;
			}
		}
		else
		{
				#Do nothing.It's a blank line.
				$commentline = 1;
		}
	}	
		
	if($commentline <= 0) 
	{
		print OUT "op1 = $op1;\n";
		print OUT "op2 = $op2;\n";
		print OUT "next_inst = ~next_inst;\n\n";
	}
	else
	{
		$commentline = 0;
	}
}

print OUT "#100"," stimulus.proc.ram.dumpmem;\n\n";
print OUT "#",$time," \$finish;\n\n";
print OUT "end\n";

if($errors > 0)
{
	unlink $outfile;
	print "Could not create stimulus ", 
		"file.Exiting .... :-(\n";
	die "$!\n";
}
else
{
	print "Compilation Successful.:-)\n";
}

print OUT "\nendmodule\n";
