/* 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(); } }