/*
This file is part of JOP, the Java Optimized Processor
see <http://www.jopdesign.com/>
Copyright (C) 2001-2008, Martin Schoeberl (martin@jopdesign.com)
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/>.
*/
package com.jopdesign.tools;
/**
Assemler for JOP3
Author: Martin Schoeberl
revision:
2001-09-22 creation
2001-10-24 working version
2001-12-08 intruction set change (16->8 bit)
2005-01-17 interrupt mux in jtbl.vhd
2005-02-06 JOP version in stack RAM at address 64
2005-02-20 Generate memory data for the simulation
2006-12-29 Remove bcfetbl.vhd generation (it's part of rom.vhd/mif)
Changed rom legth to 2K
2009-09-05 Branch and jump offsets are part of the instruction (no offtbl.vhd)
*/
import com.jopdesign.tools.Instruction.JmpType;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintStream;
import java.io.Serializable;
import java.io.StreamTokenizer;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
public class Jopa {
private String fname;
static final int ADDRBITS = 11;
/** length of microcode instruction including nxt and opd */
static final int DATABITS = Instruction.INSTLEN+2;
static final int CONST_ADDR = 32;
static final int VER_ADDR = 64-2;
static final int RAM_LEN = 256;
static final int ROM_LEN = 1<<ADDRBITS;
private String srcDir;
private String dstDir;
private boolean error;
public Jopa(String fn) {
this(fn,System.getProperty("user.dir"),System.getProperty("user.dir"));
}
public Jopa(String[] clist) {
srcDir = System.getProperty("user.dir");
dstDir = System.getProperty("user.dir");
processOptions(clist);
if (!srcDir.endsWith(File.separator))
srcDir += File.separator;
if (!dstDir.endsWith(File.separator))
dstDir += File.separator;
}
/**
* Creates a new Jopa (JopAssembler) instance
* @param fn name of the assembler file
* @param src source directory for assembler files
* @param dst destination directory for ROM vhdl
*/
public Jopa(String fn, String src, String dst) {
this.fname = fn;
this.srcDir = src;
this.dstDir = dst;
if (!srcDir.endsWith(File.separator))
srcDir += File.separator;
if (!dstDir.endsWith(File.separator))
dstDir += File.separator;
}
void error(StreamTokenizer in, String s) {
System.out.println((in.lineno()-1)+" error: "+s);
error = true;
}
private StreamTokenizer getSt() {
try {
FileReader fileIn = new FileReader(srcDir + fname);
StreamTokenizer in = new StreamTokenizer(fileIn);
in.wordChars( '_', '_' );
in.wordChars( ':', ':' );
in.eolIsSignificant(true);
in.slashStarComments(true);
in.slashSlashComments(true);
in.lowerCaseMode(true);
return in;
} catch (IOException e) {
System.out.println(e.getMessage());
System.exit(-1);
return null;
}
}
public static class Line implements Serializable {
private static final long serialVersionUID = 1L;
int jinstr;
String label;
Instruction instr;
int special;
int intVal;
String symVal;
boolean nxt;
boolean opd;
public boolean hasNxtFlag() {
return nxt;
}
public boolean hasOpdFlag() {
return opd;
}
public Instruction getInstruction() {
return instr;
}
public String getSymVal() {
return symVal;
}
public Integer getIntVal() {
return intVal;
}
public Integer getSpecial() {
return special;
}
@Override
public String toString() {
StringBuffer sb = new StringBuffer();
sb.append(instr.name);
if(instr.opdSize!=0) {
sb.append(' ');
if(symVal != null) { sb.append(symVal); }
else { sb.append(special); sb.append(" / "); sb.append(intVal); }
}
if(hasOpdFlag()) sb.append(" [opd]");
if(hasNxtFlag()) sb.append(" [nxt]");
return sb.toString();
}
}
private Line getLine(StreamTokenizer in) {
Line l = new Line();
l.jinstr = -1;
try {
for (int cnt=0; in.nextToken()!=StreamTokenizer.TT_EOL; ++cnt) {
if (in.ttype == StreamTokenizer.TT_WORD) {
int pos = in.sval.indexOf(":");
if (pos!=-1) {
String s = in.sval.substring(0, pos);
l.jinstr = JopInstr.get(s);
if (l.jinstr==-1) {
l.label = s;
}
} else {
if (in.sval.equals("nxt")) {
l.nxt = true;
} else if (in.sval.equals("opd")) {
l.opd = true;
} else {
Instruction i = Instruction.get(in.sval);
if (i==null) {
l.symVal = in.sval;
} else if (l.instr==null) {
l.instr = i;
}
}
}
} else if (in.ttype == StreamTokenizer.TT_NUMBER) {
l.intVal = (int) in.nval;
} else if (in.ttype == '=') {
l.special = in.ttype;
} else if (in.ttype == '?') {
l.special = in.ttype;
} else {
error(in, "'"+(char) in.ttype+"' syntax");
}
} // EOL
} catch (IOException e) {
System.out.println(e.getMessage());
System.exit(-1);
}
return l;
}
static String bin(int val, int bits) {
String s = "";
for (int i=0; i<bits; ++i) {
s += (val & (1<<(bits-i-1))) != 0 ? "1" : "0";
}
return s;
}
private Map<String, Integer> symMap = new HashMap<String, Integer>();
private int memcnt = 0;
private List<String> varList = new LinkedList<String>();
private int version = -1;
private Map<Integer, Integer> jinstrMap = new HashMap<Integer, Integer>();
private List<Line> instructions = new LinkedList<Line>();
/**
* Parse the assembler file and build symbol table (first pass).
* During this pass, the assembler code, the symboltable and vartable are build.
* @return the map from program locations (pc) to microcode lines
*/
public void pass1() {
StreamTokenizer in = getSt();
int pc = 0;
try {
while (in.nextToken() != StreamTokenizer.TT_EOF) {
in.pushBack();
Line l = getLine(in);
//System.out.println("L"+in.lineno()+" "+l.jinstr+" "+l.label+" "+l.instr+" '"+(char) l.special+"' "+l.intVal+" "+l.symVal);
if (l.jinstr==-1) {
if (l.label!=null) {
if (symMap.containsKey(l.label)) {
error(in, "symbol "+l.label+" already defined");
} else {
symMap.put(l.label, new Integer(pc));
}
}
if (l.special=='=') {
if (l.symVal==null) {
error(in, "missing symbol for '='");
} else {
if (symMap.containsKey(l.symVal)) {
error(in, "symbol "+l.symVal+" allready defined");
} else {
symMap.put(l.symVal, new Integer(l.intVal));
}
}
} else if (l.special=='?') {
if (symMap.containsKey(l.symVal)) {
error(in, "symbol "+l.symVal+" allready defined");
} else {
symMap.put(l.symVal, new Integer(memcnt++));
varList.add(l.symVal);
}
}
} else {
jinstrMap.put(l.jinstr,pc);
}
if (l.instr!=null) {
++pc;
instructions.add(l);
}
}
} catch (IOException e) {
System.out.println(e.getMessage());
System.exit(-1);
}
//System.out.println(symMap);
}
/**
* Get the symbol table:
* Maps labels to program counter, constants to values and variables to memory
* positions
* @return
*/
public Map<String, Integer> getSymMap() {
return this.symMap;
}
/**
* Get list of variables
* @return
*/
public List<String> getVarList() {
return this.varList;
}
/**
* get table of java instructions
*/
public Map<Integer, Integer> getJavaInstructions() {
return this.jinstrMap;
}
/**
* Get instruction list
* @return
*/
public List<Line> getInstructions() {
return this.instructions;
}
/**
* @param i
* @param len
* @return
*/
static String hex(int i, int len) {
String s = Integer.toHexString(i);
int cnt = len-s.length();
for (int k=0; k<cnt; ++k) s = "0"+s;
return s;
}
private Map<Integer, Integer> constMap = new HashMap<Integer, Integer>();
private List<Integer> constList = new LinkedList<Integer>();
private int[] romData = new int[ROM_LEN];
private int romLen = 0;
private int[] ramData = new int[RAM_LEN];
/**
* second pass.
* generate code and write rom.mif and ram.mif.
*/
public void pass2() {
StreamTokenizer in = getSt();
int pc = 0;
int ji_cnt = 0;
try {
FileWriter rom = new FileWriter(dstDir + "rom.mif");
FileWriter jtbl = new FileWriter(dstDir + "jtbl.vhd");
BufferedReader inraw = new BufferedReader(new FileReader(srcDir + fname));
String line;
//
// print rom.mif head
//
line = "--\n";
line += "--\trom.mif\n";
line += "--\n";
line += "depth = "+ROM_LEN+";\n";
line += "width = "+DATABITS+";\n";
line += "\n";
line += "content\n";
line += "\n";
line += "begin\n";
line += "\n";
line += "\t[0..1ff] : 080; -- nop TODO: new instruction\n\n";
rom.write( line );
//
// print jtbl.vhd head
//
line = "--\n";
line += "--\tjtbl.vhd\n";
line += "--\n";
line += "--\tjump table for java bc to jvm address\n";
line += "--\n";
line += "--\t\tDONT edit this file!\n";
line += "--\t\tgenerated by Jopa.java\n";
line += "--\n";
line += "\n";
line += "library ieee;\n";
line += "use ieee.std_logic_1164.all;\n";
line += "use ieee.std_logic_arith.all;\n";
line += "use ieee.std_logic_unsigned.all;\n";
line += "\n";
line += "entity jtbl is\n";
line += "port (\n";
line += "\tbcode\t: in std_logic_vector(7 downto 0);\n";
line += "\tint_pend\t: in std_logic;\n";
line += "\texc_pend\t: in std_logic;\n";
line += "\tq\t\t: out std_logic_vector("+(ADDRBITS-1)+" downto 0)\n";
line += ");\n";
line += "end jtbl;\n";
line += "\n";
line += "--\n";
line += "--\tunregistered rdbcode\n";
line += "--\tunregistered dout\n";
line += "--\n";
line += "architecture rtl of jtbl is\n";
line += "\n";
line += "\tsignal\taddr\t: std_logic_vector("+(ADDRBITS-1)+" downto 0);\n";
line += "\n";
line += "begin\n";
line += "\n";
line += "process(bcode) begin\n";
line += "\n";
line += "\tcase bcode is\n";
line += "\n";
jtbl.write( line );
int noim_address = 0;
int int_address = 0;
int exc_address = 0;
while (in.nextToken() != StreamTokenizer.TT_EOF) {
in.pushBack();
Line l = getLine(in);
if (l.jinstr!=-1) {
++ji_cnt;
if (JopInstr.name(l.jinstr).equals("sys_int")) {
int_address = pc;
} else if (JopInstr.name(l.jinstr).equals("sys_exc")) {
exc_address = pc;
} else if (JopInstr.name(l.jinstr).equals("sys_noim")) {
noim_address = pc;
} else {
jtbl.write("\t\twhen \""+bin(l.jinstr, 8) +
"\" => addr <= \""+bin(pc, ADDRBITS)+"\";" +
"\t--\t"+hex(pc,4)+"\t"+JopInstr.name(l.jinstr)+"\n");
}
}
line = "\t";
if (l.instr==null) {
line += " ";
} else {
//
// do the assembling
//
int opcode = l.instr.opcode;
if (l.instr.opdSize!=0) {
int opVal = 0;
if (l.symVal!=null) {
Integer i = symMap.get(l.symVal);
if (i==null) {
error(in, "Symbol "+l.symVal+" not defined");
} else {
opVal = i.intValue();
}
} else {
opVal = l.intVal;
}
if (l.instr.name.equals("ldi")) {
Integer i = new Integer(opVal);
Integer addr;
if (constMap.containsKey(i)) {
addr = constMap.get(i);
} else {
addr = new Integer(constMap.size());
constMap.put(i, addr);
constList.add(i);
}
opVal = addr.intValue();
}
int mask = (1<<l.instr.opdSize)-1;
// for branches and jumps opVal points to the target address
if (l.instr.jType==JmpType.JMP || l.instr.jType==JmpType.BR) {
// relative address
opVal = opVal-pc-1;
// check maximum relative offset
if (opVal>(mask>>1) || opVal<(-((mask>>1)+1))) {
error(in, "jmp/br address too far: "+opVal);
}
opVal &= mask;
}
// general check
if (opVal>mask || opVal<0) {
error(in, "operand wrong: "+opVal);
}
opcode |= opVal & mask; // use operand
}
if (l.nxt) opcode |= 0x2<<Instruction.INSTLEN;
if (l.opd) opcode |= 0x1<< Instruction.INSTLEN;
romData[romLen] = opcode;
++romLen;
line += hex(pc, 4)+" : "+hex(opcode, 3)+";\t";
++pc;
}
line += "\t--\t"+inraw.readLine()+"\n";
rom.write( line );
// System.out.print(line);
}
rom.write( "\nend;\n" );
rom.close();
line = "\n";
line += "\t\twhen others => addr <= \""+bin(noim_address, ADDRBITS)+
"\";\t\t--\t"+hex(noim_address,4)+"\tsys_noim\n";
line += "\tend case;\n";
line += "end process;\n";
line += "\n";
line += "process(int_pend, exc_pend, addr) begin\n";
line += "\n";
line += "\tq <= addr;\n";
line += "\tif exc_pend='1' then\n";
line += "\t\tq <= \""+bin(exc_address, ADDRBITS)+
"\";\t\t--\t"+hex(exc_address,4)+"\tsys_exc\n";
line += "\telsif int_pend='1' then\n";
line += "\t\tq <= \""+bin(int_address, ADDRBITS)+
"\";\t\t--\t"+hex(int_address,4)+"\tsys_int\n";
line += "\tend if;\n";
line += "end process;\n";
line += "\n";
line += "end rtl;\n";
jtbl.write(line);
jtbl.close();
//
// print ROM as generic VHDL file
//
FileWriter romvhd = new FileWriter(dstDir + "rom.vhd");
line = "--\n";
line += "--\trom.vhd\n";
line += "--\n";
line += "--\tgeneric VHDL version of ROM\n";
line += "--\n";
line += "--\t\tDONT edit this file!\n";
line += "--\t\tgenerated by Jopa.java\n";
line += "--\n";
line += "\n";
line += "library ieee;\n";
line += "use ieee.std_logic_1164.all;\n";
line += "use ieee.std_logic_arith.all;\n";
line += "use ieee.std_logic_unsigned.all;\n";
line += "\n";
line += "entity rom is\n";
line += "generic (width : integer; addr_width : integer);\t-- for compatibility\n";
line += "port (\n";
line += "\tclk\t\t\t: in std_logic;\n";
line += "\taddress\t\t: in std_logic_vector("+(ADDRBITS-1)+" downto 0);\n";
line += "\tq\t\t\t: out std_logic_vector("+(DATABITS-1)+" downto 0)\n";
line += ");\n";
line += "end rom;\n";
line += "\n";
line += "architecture rtl of rom is\n";
line += "\n";
line += "\tsignal areg\t\t: std_logic_vector("+(ADDRBITS-1)+" downto 0);\n";
line += "\tsignal data\t\t: std_logic_vector("+(DATABITS-1)+" downto 0);\n";
line += "\n";
line += "begin\n";
line += "\n";
line += "process(clk) begin\n";
line += "\n";
// line += "\tif falling_edge(clk) then\n";
// line += "\t\tareg <= address;\n";
// line += "\tend if;\n";
line += "\tif rising_edge(clk) then\n";
// line += "\t\tq <= data;\n";
line += "\t\tareg <= address;\n";
line += "\tend if;\n";
line += "\n";
line += "end process;\n";
line += "\n";
line += "\tq <= data;\n";
line += "\n";
line += "process(areg) begin\n";
line += "\n";
line += "\tcase areg is\n";
line += "\n";
romvhd.write(line);
for (int i=0; i<romLen; ++i) {
romvhd.write("\t\twhen \""+bin(i, ADDRBITS) +
"\" => data <= \""+bin(romData[i], DATABITS)+"\";");
romvhd.write("\t-- "+"TODO: comment"+"\n");
}
line = "\n";
line += "\t\twhen others => data <= \""+bin(0, DATABITS)+"\";\n";
line += "\tend case;\n";
line += "end process;\n";
line += "\n";
line += "end rtl;\n";
romvhd.write(line);
romvhd.close();
PrintStream rom_mem = new PrintStream(new FileOutputStream(dstDir + "mem_rom.dat"));
for (int i=0; i<ROM_LEN; ++i) {
rom_mem.println(romData[i]+" ");
}
rom_mem.close();
//
// Print symbol table as ram.mif and data for the simulation.
//
FileWriter ram = new FileWriter(dstDir + "ram.mif");
for (int i=0; i<RAM_LEN; ++i) {
ramData[i] = 0x12345678;
}
line = "--\n";
line += "--\tram.mif\n";
line += "--\n";
line += "depth = "+RAM_LEN+";\n";
line += "width = 32;\n";
line += "\n";
line += "content\n";
line += "\n";
line += "begin\n";
// line += "\t[0..ff] : 00000000;\n";
line += "\t[0..ff] : 12345678;\n";
line += "\n";
ram.write( line );
line = "--\n";
line += "-- "+memcnt+" vars\n";
line += "--\n\n";
ram.write( line );
//
// Variables
//
for (int i=0; i<varList.size(); ++i) {
String s = varList.get(i);
ramData[i] = 0;
line = "\t";
line += hex(i, 4) + " : " ;
line += hex(0, 8) + ";\t--\t";
line += s + "\n";
ram.write( line );
}
line = "--\n";
line += "-- "+constMap.size()+" consts\n";
line += "--\n\n";
ram.write( line );
if (constMap.size()>VER_ADDR-CONST_ADDR) {
System.out.println("error: too many constants");
System.exit(-1);
}
//
// Constants
//
for (int i=0; i<constList.size(); ++i) {
Integer val = constList.get(i);
ramData[CONST_ADDR+i] = val.intValue();
line = "\t";
line += hex(CONST_ADDR+i, 4) + " : " ;
line += hex(val.intValue(), 8) + ";\t--\t";
line += val + "\n";
ram.write( line );
}
// check if version is set
Integer ver = symMap.get("version");
if (ver==null) {
error(in, "version not set, setting to -1");
} else {
version = ver.intValue();
}
ramData[VER_ADDR] = version;
ramData[VER_ADDR+1] = 0;
ram.write("\n\n--\tVersion now in the constant area\n");
line = "\t";
line += hex(VER_ADDR, 4) + " : " ;
line += hex(version, 8) + ";\t--\t";
line += version + "\n";
ram.write(line);
line = "\t";
line = "\t"+hex(VER_ADDR+1, 4) + " : " ;
line += hex(0, 8) + ";\t--\tfor future use - FPGA type?\n";
ram.write(line);
ram.write( "\nend;\n" );
ram.close();
PrintStream ram_mem = new PrintStream(new FileOutputStream(dstDir + "mem_ram.dat"));
for (int i=0; i<RAM_LEN; ++i) {
ram_mem.println(ramData[i]+" ");
}
ram_mem.close();
System.out.println(ji_cnt+" Instructions implemented");
} catch (IOException e) {
System.out.println(e.getMessage());
System.exit(-1);
}
}
private boolean processOptions(String clist[]) {
boolean success = true;
for (int i = 0; i < clist.length; i++) {
if (clist[i].equals("-s")) {
srcDir = clist[++i];
} else if (clist[i].equals("-d")) {
dstDir = clist[++i];
} else {
fname = clist[i];
}
}
return success;
}
/**
* Main for Jop assembler.
*/
public static void main(String args[]) {
if (args.length < 1) {
System.out.println(
"usage: java Jopa [-s srcDir] [-d dstDir] filename");
System.exit(-1);
}
Jopa j = new Jopa(args);
j.pass1();
j.pass2();
if (j.error) {
throw new Error("Errors in assembler file!");
}
}
}