#!/usr/bin/perl

#########################################
# makepr.pl
######################################### 
#
# by Navtej Sadhal
# cs61c-td@imail.eecs.berkeley.edu (through Summer '04)
# EECS Department
# University of California Berkeley
# July 2004
#
#########################################
# Last Update: 17-Jul-2004
#########################################
#
# This script is used to generate behavioral registers in Verilog.
# It was specifically written to generate pipeline registers for a processor
# and thus has certain resulting quirks (or "features").
#
# Usage:
# This script takes input from STDIN and produces Verilog code on STDOUT.
# With UNIX redirects you can use files instead:
#     $prompt> ./makepr.pl < input.txt > output.v
# Make note of the directions of the angle brackets.
# makepr.pl expects the input to be of the form:
#    ID:EX
#    32:regWriteData, instruction, foo, baz
#    8: bar, bang, snoz
#    1: dingle, snozzle, snaz
#    #
#    EX:MEM
#    32:regWriteData, boz, biz, ban
#    4:some, useless, bus
# 
# In general you may insert spaces freely between delimiters (: and ,) and 
# data, but not newlines. Random newlines (even at the end of the file, after 
# the last regsiter) may confuse the script, so every
# line should have data on it. The first line specifies the name of the 
# pipeline register (i.e. fromThisStage:toThisStage). It may contain no 
# numbers. The following lines contain a bit width followed by field names.
# In the example above, there are 2 registers being created. It will contain
# 4 32 bit internal registers as well as 3 8 bit and 1 bit registers. To 
# signify the end of a register and the start of a new one you put a single
# hash sign (#) (or basically any line containing a hash sign). Then you 
# may begin the next register. At the end of your input file, you may or 
# may not put a # sign. The script will keep looking for data until it
# it reaches EOF. 
#
# makepr.pl enforces a naming convention on your pipeline registers and their
# ports. All inputs will be named <fieldName>_<fromThisStage> and outputs
# will be named <fieldName>_<toThisStage>. In the above example, we would have
# regWriteData_ID and regWriteData_EX as input and output respectively. 
# The pipeline register itself is named pr_<fromThisStage>_<toThisStage>. 
# In the above example, we would have pr_ID_EX and pr_EX_MEM.
#
# This script is currently not very robust and may crash or hang depending
# on the input given. It should work fine for input that follows the convention
# described above, however. I make no guarantees about the correctness
# or usefulness of this script. This script is currently not very well written
# or documented, so if you plan to do anything besides generate verilog
# pipeline registers for the CS61C MIPS processor project, don't expect too
# much. 


use strict;

my @regs;
my @regsandwidth;
my @regsW;

my $reglist;
my $regW;

my $decltype;
my $inline;

while ($inline = <STDIN>) {
    if ($inline !~ /[0-9]/) { 

	chomp($inline);
	$inline =~s/ //g;
	my @name = split(/:/, $inline);

	my $line = "module pr_".$name[0]."_".$name[1]." (CLK, RST, ";

	my $i =0;

	@regs = ();
	while (($inline = <STDIN>) !~ /\#/ && $inline) {
	    chomp($inline);
	    $inline =~ s/ //g;
	    $regs[$i++] = $inline;
	}
	
# make module header
	for $reglist (@regs) {
	    @regsandwidth = split(/:/, $reglist);
	    @regsW = split(/\,/, $regsandwidth[1]);
	    for $regW (@regsW) {
		$line.= $regW."_".$name[0].", ".$regW."_".$name[1].", ";
	    }
	}
	
	chop ($line);
	chop ($line);
	
	$line.= ");\n";
	
	print $line;
	
# port declarations
	
	print "\n";
	print "\tinput\tCLK, RST\;\n";
	for $decltype (("input", "output",  "reg")) {

	    for $reglist (@regs) {
		@regsandwidth = split(/:/, $reglist);
		@regsW = split(/\,/,$regsandwidth[1]);
		
		$line = "\t$decltype\t".(($regsandwidth[0] == 1)?"":
					 ("\[".($regsandwidth[0]-1).":0\]"))."\t";
		
		for $regW (@regsW) {
		    $line .= $regW . "_" . (($decltype eq "input")?$name[0]:$name[1]) .
			", ";
		}
		
		chop ($line);
		chop ($line);
		
		$line .= ";\n";
		
		print $line;
	    }
	    print "\n";
	}

	print "\talways \@ \(posedge CLK\) \n";
	print "\t\tif \(RST\) \n\t\t\tbegin\n";
	for $reglist (@regs) {
	    @regsandwidth = split(/:/, $reglist);
	    @regsW = split(/\,/,$regsandwidth[1]);
	    
	    for $regW (@regsW) {
		print "\t\t\t\t${regW}_$name[1] <= $regsandwidth[0]\'b0\;\n";
	    }
	}
	print "\t\t\tend\n\t\telse\n\t\t\tbegin\n";
	for $reglist (@regs) {
	    @regsandwidth = split(/:/, $reglist);
	    @regsW = split(/\,/,$regsandwidth[1]);
	    
	    for $regW (@regsW) {
		print "\t\t\t\t${regW}_$name[1] <= ${regW}_$name[0]\;\n";
	    }
	}
	print "\t\t\tend\n";

	print "endmodule\n\n"

	}
}
