/*
This file is part of JOP, the Java Optimized Processor
see <http://www.jopdesign.com/>
Copyright (C) 2004, Ed Anuff (ed@anuff.com)
Copyright (C) 2007, Wolfgang Puffitsch (hausen@gmx.at)
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 3 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 General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import java.io.*;
import java.util.*;
/**
* Generate an asynchronous ROM module.
*
* Can use either a text file with hex values or Altera-style MIF file for
* initialization data.
*
* @author Wolfgang Puffitsch (hausen@gmx.at), based on BlockGen by Ed Anuff (ed@anuff.com)
*
*/
public class GenAsynROM {
static String build_date = "Aug 7 2007 19:11 PM PST";
int depth = 256;
int width = 32;
String src_filename = "";
String dst_filename = "";
File src_file;
String module_name = "";
String dst_dir = "";
public GenAsynROM(String[] args) {
processOptions(args);
}
public void process() {
try {
if (!stringEmpty(src_filename)) {
src_file = new File(src_filename);
dst_dir = src_file.getParent();
}
if (stringEmpty(module_name) && (src_file.getName().indexOf('.') > 0)) {
module_name = src_file.getName().substring(0, src_file.getName().indexOf('.'));
}
if (stringEmpty(module_name)) module_name = "ram";
if (stringEmpty(dst_filename)) dst_filename = dst_dir + File.separator + module_name + ".vhd";
System.out.println("Asynchronous ROM Generator");
System.out.println("Module: " + module_name);
String binary_string = "";
if (!stringEmpty(src_filename)) {
File file = new File(src_filename);
if (!file.exists()) {
System.out.println("Input file not found: " + src_filename);
System.exit(-1);
}
if (src_filename.endsWith(".mif"))
binary_string = parseMifFile();
else
binary_string = parseTextFile();
}
System.out.println("Input file: " + src_filename);
System.out.println("Output file: " + dst_filename);
PrintWriter pw = new PrintWriter(new FileWriter(dst_filename));
int bitcount = binary_string.length();
if ((bitcount % width) != 0) {
System.out.println("Input data not bit aligned to the specified width.");
System.exit(1);
}
pw.println("--");
pw.println("-- " + (new File(dst_filename)).getName());
pw.println("--");
pw.println("-- Generated by GenAsynROM");
pw.println("-- " + (new Date()));
pw.println("--");
pw.println("-- This module will synthesize on ProASIC3 devices.");
pw.println("--");
pw.println();
pw.println("library IEEE;");
pw.println("use IEEE.std_logic_1164.all;");
pw.println("use IEEE.numeric_std.all;");
pw.println();
pw.println("entity " + module_name + " is");
pw.println(" generic (");
pw.println(" width : integer := "+width+";");
pw.println(" addr_width : integer := "+(int)Math.ceil(Math.log(depth)/Math.log(2.0)));
pw.println(" );");
pw.println(" port (");
pw.println(" address : in std_logic_vector(addr_width-1 downto 0);");
pw.println(" data : out std_logic_vector(width-1 downto 0)");
pw.println(" );");
pw.println("end " + module_name + ";");
pw.println();
pw.println("architecture rtl of " + module_name + " is");
pw.println();
pw.println(" subtype word is std_logic_vector(width-1 downto 0);");
pw.println(" type initrom_type is array(0 to 2**addr_width-1) of word;");
pw.println();
pw.println(" constant initrom : initrom_type := (");
String [] words = split(binary_string, width);
for (int i = 0; i < words.length; i++) {
if (i > 0) pw.println(",");
pw.print(" \""+words[i]+"\"");
}
pw.println(");");
pw.println();
pw.println("begin");
pw.println();
pw.println(" data <= initrom(to_integer(unsigned(address)));");
pw.println();
pw.println("end rtl;");
pw.close();
System.out.println("Done.");
} catch (IOException e) {
System.out.println(e.getMessage());
System.exit(-1);
}
}
private static final int SEEK_GENERAL = 0;
private static final int SEEK_ADDR = 1;
private static final int SEEK_DATA = 2;
/**
* Parse a file in Altera MIF file format.
*
* @return String containing binary data
* @throws IOException
*/
public String parseMifFile() throws IOException {
FileReader fr = new FileReader(src_filename);
StreamTokenizer st = new StreamTokenizer(fr);
st.resetSyntax();
st.eolIsSignificant(true);
st.slashStarComments(true);
st.slashSlashComments(true);
st.lowerCaseMode(true);
st.commentChar('%');
st.commentChar('-');
st.wordChars('0', '9');
st.wordChars('a', 'z');
st.wordChars('A', 'Z');
st.wordChars('_', '_');
st.wordChars('[', '[');
st.wordChars(']', ']');
st.wordChars('.', '.');
st.whitespaceChars(0, ' ');
int state = SEEK_GENERAL;
int address_radix = 16;
int data_radix = 16;
int addr = 0;
int addr_range_lo = 0;
int addr_range_hi = 0;
boolean is_addr_range = false;
long[] mem = null;
while (st.nextToken() != StreamTokenizer.TT_EOF)
{
if (state == SEEK_GENERAL) {
if ("width".equalsIgnoreCase(st.sval)) {
st.nextToken();
st.nextToken();
if (!stringEmpty(st.sval)) {
width = Integer.parseInt(st.sval);
}
}
else if ("depth".equalsIgnoreCase(st.sval)) {
st.nextToken();
st.nextToken();
if (!stringEmpty(st.sval)) {
depth = Integer.parseInt(st.sval);
mem = new long[depth];
}
}
else if ("address_radix".equalsIgnoreCase(st.sval)) {
st.nextToken();
st.nextToken();
if ("bin".equalsIgnoreCase(st.sval)) address_radix = 2;
else if ("dec".equalsIgnoreCase(st.sval)) address_radix = 10;
else if ("hex".equalsIgnoreCase(st.sval)) address_radix = 16;
else if ("oct".equalsIgnoreCase(st.sval)) address_radix = 8;
}
else if ("data_radix".equalsIgnoreCase(st.sval)) {
st.nextToken();
st.nextToken();
if ("bin".equalsIgnoreCase(st.sval)) data_radix = 2;
else if ("dec".equalsIgnoreCase(st.sval)) data_radix = 10;
else if ("hex".equalsIgnoreCase(st.sval)) data_radix = 16;
else if ("oct".equalsIgnoreCase(st.sval)) data_radix = 8;
}
else if ("begin".equalsIgnoreCase(st.sval)) {
state = SEEK_ADDR;
}
}
else if ((state == SEEK_ADDR) && (st.ttype == StreamTokenizer.TT_WORD)) {
if (!stringEmpty(st.sval)) {
if ("end".equalsIgnoreCase(st.sval)) {
break;
}
else if (st.sval.startsWith("[")) {
is_addr_range = true;
addr_range_lo = Integer.parseInt(st.sval.substring(1, st.sval.indexOf('.')), address_radix);
addr_range_hi = Integer.parseInt(st.sval.substring(st.sval.lastIndexOf('.') + 1, st.sval.length() - 1), address_radix);
}
else {
is_addr_range = false;
addr = Integer.parseInt(st.sval, address_radix);
}
state = SEEK_DATA;
}
}
else if ((state == SEEK_DATA) && (st.ttype == StreamTokenizer.TT_EOL)) {
state = SEEK_ADDR;
}
else if ((state == SEEK_DATA) && (st.ttype == StreamTokenizer.TT_WORD)) {
if (!stringEmpty(st.sval)) {
long data = Long.parseLong(st.sval, data_radix);
if (is_addr_range) {
for (addr = addr_range_lo; addr <= addr_range_hi; addr++) {
if (addr >= 0 && addr < mem.length) mem[addr] = data;
else {
System.out.println("Warning: Data at address " + addr + " is outside the range of the specified depth.");
}
}
}
else {
if (addr >= 0 && addr < mem.length) mem[addr] = data;
else {
System.out.println("Warning: Data at address " + addr + " is outside the range of the specified depth.");
}
}
addr++;
}
}
}
fr.close();
StringBuffer binary_buffer = new StringBuffer();
for (int i = 0; i < mem.length; i ++) {
binary_buffer.append(toBinary(mem[i], width));
}
return binary_buffer.toString();
}
/**
* Brute force parse of a text file to extract hex data.
* Good for files that only contain the desired hex data and whitespace or
* delimeters such as commas or semicolons.
*
* @return String containing binary data.
* @throws IOException
*/
public String parseTextFile() throws IOException {
FileReader fr = new FileReader(src_filename);
BufferedReader br = new BufferedReader(fr);
StringBuffer hex_buffer = new StringBuffer();
String line = br.readLine();
while (line != null) {
line = line.toUpperCase();
line = findAndReplace(line, "0X", "");
line = endBefore(line, "/");
line = filterNonHexDigits(line);
if (!stringEmpty(line)) {
hex_buffer.append(line);
}
line = br.readLine();
}
br.close();
return longHexStringToBinary(hex_buffer.toString());
}
/**
* Test for whether a string is null or of zero length.
*
* @param str string to test
* @return true if string is empty or null
*/
public static boolean stringEmpty(String str) {
if (str == null) return true;
if (str.length() == 0) return true;
return false;
}
/**
* Reverse the input string by extracting blocks of length len and reordering them.
* For example, if <code>len</code> equals <code>2</code> and <code>input</code> is
* <code>A1B2C3D4E5</code>, then the return value will be <code>E5D4C3B2A1</code>.
* @param input string to reverse
* @param len length of blocks within string
* @return reversed string
*/
public static String reverse(String input, int len) {
StringBuffer output = new StringBuffer();
int i = input.length() - len;
while (i >= 0) {
output.append(input.substring(i, i + len));
i -= len;
}
return output.toString();
}
/**
* Split the input string into an array of strings of length <code>count</code>
* @param input string to split
* @param count character count to split at
* @return array of strings
*/
public static String[] split(String input, int count) {
ArrayList list = new ArrayList();
int i = 0;
while (i < input.length()) {
list.add(input.substring(i, Math.min(i + count, input.length())));
i += count;
}
return (String[])list.toArray(new String[]{});
}
/**
* Replace all instances of string <code>find</code> in the input string with string
* <code>replace</code>.
* @param input string to search
* @param find string to search for
* @param replace string to replace with
* @return processed string
*/
public static String findAndReplace(String input, String find, String replace) {
String output = input;
int idx;
while ((idx = output.indexOf(find)) != -1) {
output = output.substring(0, idx) + replace + output.substring(idx+find.length());
}
return output;
}
/**
* Input parameter <code>terms<code> containst a set of <code>find</code> and <code>replace</code> pairs to
* apply to the input string.
* @param input string to search
* @param terms hashmap of find and replace pairs
* @return processed string
*/
public static String findAndReplace(String input, HashMap terms) {
String output = input;
for (Iterator i = terms.keySet().iterator(); i.hasNext();) {
String find = i.next().toString();
String replace = terms.get(find).toString();
int idx;
while ((idx = output.indexOf(find)) != -1) {
output = output.substring(0, idx) +
replace +
output.substring(idx+find.length());
}
//Pattern p = Pattern.compile(find, Pattern.CASE_INSENSITIVE);
//Matcher m = p.matcher(output);
//output = m.replaceAll(replace);
}
return output;
}
/**
* Terminate the input string at the first instance of the string passed as <code>terminator</code>.
* @param input string to terminate
* @param terminator string to find in the input string and to terminate input before
* @return terminated string
*/
public static String endBefore(String input, String terminator) {
String output = input;
int idx;
if ((idx = output.indexOf(terminator)) != -1) {
output = output.substring(0, idx);
}
return output;
}
/**
* Remove any characters that are not 0 to 9 or A to F (case sensitive).
*
* @param input string to process for non-hex characters
* @return string with non-hex characters removed
*/
public static String filterNonHexDigits(String input) {
StringBuffer output = new StringBuffer();
for (int i = 0; i < input.length(); i ++) {
char c = input.charAt(i);
if (Character.isDigit(c) || ((c >= 'A') && (c <= 'F'))) {
output.append(c);
}
}
return output.toString();
}
/**
* Read a file into a string.
*
* @param file file to read
* @return contents of file
*/
public static String readFileContents(File file) {
String contents = null;
try {
FileInputStream fis = new FileInputStream(file);
byte[] data = new byte[(int)file.length()];
int r = fis.read(data);
contents = new String(data, 0, r);
}
catch (Exception e) {
}
return contents;
}
/**
* Read a file into a string.
*
* @param file_name filename of file to read
* @return contents of file
*/
public static String readFileContents(String file_name) {
File file = new File(file_name);
return readFileContents(file);
}
/**
* Convert an integer into a hex string with correct zero padding for word aligment.
*
* @param i integer value to convert
* @param word word size to align hex return value to
* @return hex return value
*/
public static String toHex(int i, int word) {
String result = Integer.toHexString(i);
if ((result.length() % word) != 0) result = zero(word - (result.length() % word)) + result;
return result;
}
/**
* Convert an long integer into a binary string with correct zero padding for word aligment.
*
* @param n long integer value to convert
* @param word word size to align hex return value to
* @return hex return value
*/
public static String toBinary(long n, int word) {
String result = Long.toBinaryString(n);
if ((result.length() % word) != 0) result = zero(word - (result.length() % word)) + result;
return result;
}
/**
* Generate a string of zeros of the specified count
* @param count number of zeroes to generate
* @return string with the specified number of zeros
*/
public static String zero(int count) {
StringBuffer result = new StringBuffer();
while (result.length() < count) result.append('0');
return result.toString();
}
/**
* Convert a hex string into a binary string with correct zero padding for word aligment.
* Use for hex string of less than 8 characters.
* @param hex input hex string
* @param word word size to align binary return value to
* @return binary return value
*/
public static String hexToBinary(String hex, int word) {
int i = Integer.parseInt(hex, 16);
return toBinary(i, word);
}
/**
* Convert a hex string into a binary string with correct zero padding for word aligment.
* Use for hex string of 8 or more characters.
* @param hex input hex string
* @return binary return value
*/
public static String longHexStringToBinary(String hex) {
StringBuffer output = new StringBuffer();
for (int i = 0; i < hex.length(); i++) {
String h = hex.substring(i, i + 1);
output.append(hexToBinary(h, 4));
}
return output.toString();
}
/**
* Convert a binary string into a hex string.
*
* @param input binary string
* @return hex string
*/
public static String longBinaryStringToHex(String input) {
StringBuffer output = new StringBuffer();
for (int i = 0; i < input.length(); i += 4) {
String nibble = input.substring(i, i + 4);
int n = 0;
if (nibble.charAt(3) == '1') n += 1;
if (nibble.charAt(2) == '1') n += 2;
if (nibble.charAt(1) == '1') n += 4;
if (nibble.charAt(0) == '1') n += 8;
output.append(Integer.toHexString(n));
}
return output.toString();
}
/**
* Process the command line parameters
*
* @param clist command line parameters passed to main() method
* @return true if options processed correctly
*/
public boolean processOptions( String clist[] ){
boolean success = true;
for( int i = 0; i < clist.length; i++ ){
if ( clist[i].equals("-w") ){
width = Integer.parseInt(clist[ ++i ]);
} else if ( clist[i].equals("-d") ){
depth = Integer.parseInt(clist[ ++i ]);
} else if ( clist[i].equals("-m") ){
module_name = clist[ ++i ];
} else if ( clist[i].equals("-o") ){
dst_dir = clist[ ++i ];
} else if ( clist[i].startsWith("-") ){
printUsage();
System.out.println("Unknown parameter: " + clist[i]);
System.exit(-1);
} else {
if (stringEmpty(src_filename)) src_filename = clist[i];
else dst_filename = clist[i];
}
}
return success;
}
public static void printUsage() {
System.out.println("GenAsynROM VHDL Module Generator");
System.out.println("By Wolfgang Puffitsch, based on BlockGen by Ed Anuff <ed@anuff.com>");
System.out.println("Version built: " + build_date);
System.out.println();
System.out.println("Usage:");
System.out.println("java GenAsynROM [-w #] [-d #] [-m name] [-o dir] [src [dst]]");
System.out.println();
System.out.println("Generates an asynchronous ROM module in VHDL with the given parameters.");
System.out.println();
System.out.println("Input file should be in Altera MIF (.mif) format or be a text file with");
System.out.println("hex data that can be extracted.");
System.out.println();
System.out.println("Parameters:");
System.out.println(" -w : RAM data width in # of bits to a data word");
System.out.println(" -d : RAM address depth in # of words");
System.out.println(" -m : Name of module generated");
System.out.println(" -o : Output directory");
System.out.println();
}
public static void main(String[] args) {
if (args.length < 1) {
printUsage();
System.exit(-1);
}
GenAsynROM bgen = new GenAsynROM(args);
bgen.process();
}
}