-
Assembler Revision 2
04/17/2016 at 04:53 • 0 commentsIn this revision of the code editor, I added line numbers and syntax highlighting (sort of). The line numbers consist of text on a canvas that is refreshed every time a key is pressed, a left click occurs, or a click and drag occurs. Syntax highlighting utilizes the Text.search() function supported by Tkinter. I also decided to use two files for this project to keep the main file (slightly) less cluttered. Code (with very few comments) below:
Main file (assemblerv2.py):
from Tkinter import * from tkFileDialog import * from tkFont import * import os.path import sys import re from assemblerlib import * basefilename = "Untitled" filename = "" fileexists = False canvaswidthdefault = 20 canvaswidth = canvaswidthdefault currentlength = 1 mnemonicstring = "sll|add|sub|nand|nor|bez|bnez|bgez|blez|bgz|blz|lw|sw" registerstring = "r0|r1|r2|r3|r4|r5|r6|r7" def openFile(): global filename global basefilename openfilename = askopenfilename() if openfilename is not None: filename = openfilename basefilename = os.path.basename(filename) asmfile = open(filename, "r") asmfile.seek(0) asmdata = asmfile.read() textArea.delete("1.0", "end - 1c") textArea.insert("1.0", asmdata) asmfile.close() filemenu.entryconfig(filemenu.index("Save"), state = NORMAL) frame.title("muCPU Assembler [" + basefilename + "]") frame.focus() initonOpen() def saveFile(): global filename asmdata = textArea.get("1.0", "end - 1c") asmfile = open(filename, "w") asmfile.seek(0) asmfile.truncate() asmfile.write(asmdata) asmfile.close() def saveFileAs(): global filename global fileexists global basefilename saveasfilename = asksaveasfilename() if saveasfilename is not None: filename = saveasfilename basefilename = os.path.basename(filename) fileexists = True asmdata = textArea.get("1.0", "end - 1c") asmfile = open(filename, "w") asmfile.seek(0) asmfile.truncate() asmfile.write(asmdata) asmfile.close() filemenu.entryconfig(filemenu.index("Save"), state = NORMAL) frame.title("muCPU Assembler [" + basefilename + "]") frame.focus() def exitApp(): frame.destroy() sys.exit() def compileASM(): global filename cpu_out = "" asm_in = textArea.get("1.0", END) asmlines = re.split("\n", asm_in) for i in range (len(asmlines)): if (asmlines[i] != ""): cpu_out += str(i) + " => x\"" + decode(asmlines[i]) + "\",\n" name, ext = os.path.splitext(filename) hexfilename = name + ".hex" hexfile = open(hexfilename, "w") hexfile.seek(0) hexfile.truncate() hexfile.write(cpu_out) hexfile.close() def updateLinesEvent(event): drawLinenums() def updateHighlightEvent(event): highlightSyntax((re.split("\.", textArea.index(INSERT))[0] + ".0"), INSERT) def initonOpen(): highlightSyntax("1.0", END) drawLinenums() def drawLinenums(): global canvaswidth global canvaswidthdefault linenumbers.delete("all") i = textArea.index("@0,0") while True: dline = textArea.dlineinfo(i) if dline is None: break y = dline[1] linenum = str(i).split(".")[0] linenumbers.create_text(canvaswidth, y, anchor=NE,text=linenum) i = textArea.index("%s+1line" % i) linenumbers.config(width = canvaswidth) def highlightSyntax(start, end): global mnemonicstring global registerstring mnemoniclen = StringVar() registerlen = StringVar() numberlen = StringVar() pos = start while True: pos = textArea.search(mnemonicstring, pos, end, regexp = True, count = mnemoniclen) #print pos if not pos: break textArea.tag_add("mnemonic", pos, pos + " + " + str(mnemoniclen.get()) + "c") posarry = re.split("\.", pos) posarry[1] = str(int(posarry[1]) + 1) pos = posarry[0] + "." + posarry[1] + ("0" * (len(posarry[1]) - 2)) pos = start while True: pos = textArea.search("-?\\d", pos, end, regexp = True, count = numberlen) #print pos if not pos: break textArea.tag_add("number", pos, pos + " + " + str(numberlen.get()) + "c") posarry = re.split("\.", pos) posarry[1] = str(int(posarry[1]) + 1) pos = posarry[0] + "." + posarry[1] + ("0" * (len(posarry[1]) - 2)) pos = start while True: pos = textArea.search(registerstring, pos, end, regexp = True, count = registerlen) #print pos if not pos: break textArea.tag_add("register", pos, pos + " + " + str(registerlen.get()) + "c") posarry = re.split("\.", pos) posarry[1] = str(int(posarry[1]) + 1) pos = posarry[0] + "." + posarry[1] + ("0" * (len(posarry[1]) - 2)) Tk().withdraw() frame = Toplevel(bg="#D8D8D8") frame.bind("<Key>", updateLinesEvent) frame.bind("<Button-1>", updateLinesEvent) frame.bind("<B1-Motion>", updateLinesEvent) frame.bind("<ButtonRelease-1>", updateLinesEvent) frame.bind("<Key>", updateHighlightEvent) scrollbar = Scrollbar(frame) scrollbar.pack(side = RIGHT, fill = Y) frame.title("muCPU Assembler [" + basefilename + "]") textArea = Text(frame, height = 30, width = 100, padx = 3, pady = 3, yscrollcommand = scrollbar.set) textArea.pack(side=RIGHT) scrollbar.config(command=textArea.yview) mnemonicfont = Font(frame, family = "Courier", size = 10, weight = "bold") textArea.tag_config("mnemonic", foreground = "blue", font = mnemonicfont) numberfont = Font(frame, family = "Courier", size = 10) textArea.tag_config("number", foreground = "#585858", font = numberfont) registerfont = Font(frame, family = "Courier", size = 10)#, slant = "italic") textArea.tag_config("register", foreground = "red", font = registerfont) linenumbers = Canvas(frame, width = canvaswidthdefault, height = 487, bg = "#D8D8D8", highlightbackground = "#D8D8D8") linenumbers.pack() menubar = Menu(frame) filemenu = Menu(menubar, tearoff=0) filemenu.add_command(label="Open", command=openFile) filemenu.add_command(label="Save", command=saveFile, state = DISABLED) filemenu.add_command(label="Save as...", command=saveFileAs) filemenu.add_command(label="Exit", command=exitApp) menubar.add_cascade(label="File", menu=filemenu) runmenu = Menu(menubar, tearoff=0) runmenu.add_command(label="Compile", command=compileASM) menubar.add_cascade(label="Run", menu=runmenu) frame.config(menu=menubar) initonOpen() frame.resizable(0,0) frame.mainloop() #Current code """ lw r4, 176(r0) lw r3, 177(r0) sub r2, r4, r1 bez r2, 8 sw r1, 252(r0) bez r0, -8 add r1, r1, r3 sll r0, r0, r0 bez r0, -2 """
Library file (assemblerlib.py):
import re def asmtoint(asm): asm_split = re.split(" |, |\(|\)", asm) args = [] for i in range (len(asm_split)): if (asm_split[i] != ""): args.append(asm_split[i]) #print args opcode = 0 func = 0 rd = 0 rs = 0 rt = 0 imm = 0 if (args[0] == "sll"): if (len(args) != 4): return 0,0,0,0,0,0 opcode = 0 func = 0 rd = int(args[1][1:]) rs = int(args[2][1:]) rt = int(args[3][1:]) elif (args[0] == "add"): if (len(args) != 4): return 0,0,0,0,0,0 opcode = 0 func = 1 rd = int(args[1][1:]) rs = int(args[2][1:]) rt = int(args[3][1:]) elif (args[0] == "sub"): if (len(args) != 4): return 0,0,0,0,0,0 opcode = 0 func = 2 rd = int(args[1][1:]) rs = int(args[2][1:]) rt = int(args[3][1:]) elif (args[0] == "nand"): if (len(args) != 4): return 0,0,0,0,0,0 opcode = 0 func = 3 rd = int(args[1][1:]) rs = int(args[2][1:]) rt = int(args[3][1:]) elif (args[0] == "nor"): if (len(args) != 4): return 0,0,0,0,0,0 opcode = 0 func = 4 rd = int(args[1][1:]) rs = int(args[2][1:]) rt = int(args[3][1:]) elif (args[0] == "bez"): if (len(args) != 3): return 0,0,0,0,0,0 opcode = 1 rt = 0 rs = int(args[1][1:]) imm = int(args[2]) elif (args[0] == "bnez"): if (len(args) != 3): return 0,0,0,0,0,0 opcode = 1 rt = 1 rs = int(args[1][1:]) imm = int(args[2]) elif (args[0] == "bgez"): if (len(args) != 3): return 0,0,0,0,0,0 opcode = 1 rt = 2 rs = int(args[1][1:]) imm = int(args[2]) elif (args[0] == "blez"): if (len(args) != 3): return 0,0,0,0,0,0 opcode = 1 rt = 3 rs = int(args[1][1:]) imm = int(args[2]) elif (args[0] == "bgz"): if (len(args) != 3): return 0,0,0,0,0,0 opcode = 1 rt = 4 rs = int(args[1][1:]) imm = int(args[2]) elif (args[0] == "blz"): if (len(args) != 3): return 0 opcode = 1 rt = 5 rs = int(args[1][1:]) imm = int(args[2]) elif (args[0] == "lw"): if (args[-1] == ''): args = args[0:-1] if (len(args) != 3 and len(args) != 4): return 0,0,0,0,0,0 opcode = 2 rt = int(args[1][1:]) if (len(args) == 3): imm = 0 rs = int(args[2][1:]) else: imm = int(args[2]) rs = int(args[3][1:]) elif (args[0] == "sw"): if (args[-1] == ''): args = args[0:-1] if (len(args) != 3 and len(args) != 4): return 0,0,0,0,0,0 opcode = 3 rt = int(args[1][1:]) if (len(args) == 3): imm = 0 rs = int(args[2][1:]) else: imm = int(args[2]) rs = int(args[3][1:]) else: return 0,0,0,0,0,0 return opcode, rs, rt, rd, func, imm def inttohex(opcode, rs, rt, rd, func, imm): if (opcode == 0): opstr = format(opcode, '02b') rsstr = format(rs, '03b') rtstr = format(rt, '03b') rdstr = format(rd, '03b') fnstr = format(func, '05b') #print opstr, rsstr, rtstr, rdstr, fnstr instruction = opstr + rsstr + rtstr + rdstr + fnstr else : opstr = format(opcode, '02b') rtstr = format(rt, '03b') rsstr = format(rs, '03b') if (imm < 0): imm2s = ((-imm) ^ 255) + 1 immstr = format(imm2s, '08b') else : immstr = format(imm, '08b') #print opstr, rtstr, rsstr, immstr instruction = opstr + rsstr + rtstr + immstr return format(int(instruction, 2), '04x') def decode(asm): opcode, rs, rt, rd, func, imm = asmtoint(asm) instruction = inttohex(opcode, rs, rt, rd, func, imm) return instruction
-
First Program Runs Smoothly
04/16/2016 at 20:01 • 3 commentsUsing the assembler I wrote in Python, I generated some hex code to store in the synthesized ROM on the FPGA. I also added a clock divider to slow the system clock from 50MHz down to 25Hz. This results in about a half second period between increments of the output vector.
Assembler Code:
lw r4, 176(r0) //value to count to should be stored at this address lw r3, 177(r0) //increment value should be stored at this address sub r2, r4, r1 bez r2, 8 sw r1, 252(r0) bez r0, -8 add r1, r1, r3 sll r0, r0, r0 bez r0, -2
Hex Code:
0 => x"84b0", 1 => x"83b1", 2 => x"2142", 3 => x"5008", 4 => x"c1fc", 5 => x"40f8", 6 => x"0b21", 7 => x"0000", 8 => x"40fe"
-
Writing an Assembler in Python
04/16/2016 at 03:37 • 0 commentsAs I was writing some simple test code to run on my processor testbench (in iSim), I found that it was tedious and slow to write hex instructions from assembly mnemonics. What better solution to a problem that is tedious than a program that does the hex encoding over and over. I wrote a simple application in Python (because of its simple, powerful string manipulation functions) to "assemble" very rudimentary assembly language instructions. Screenshots and code below.
Input:
Output:
0 => x"84b0", 1 => x"83b0", 2 => x"2142", 3 => x"5008", 4 => x"c1fc", 5 => x"40f8", 6 => x"0b21", 7 => x"0000", 8 => x"40fe",
Code:from Tkinter import * import re from tkFileDialog import * import os.path import sys filename = "Untitled" fileexists = False def asmtoint(asm): asm_split = re.split(" |, |\(|\)", asm) args = [] for i in range (len(asm_split)): if (asm_split[i] != ""): args.append(asm_split[i]) #print args opcode = 0 func = 0 rd = 0 rs = 0 rt = 0 imm = 0 if (args[0] == "sll"): if (len(args) != 4): return 0,0,0,0,0,0 opcode = 0 func = 0 rd = int(args[1][1:]) rs = int(args[2][1:]) rt = int(args[3][1:]) elif (args[0] == "add"): if (len(args) != 4): return 0,0,0,0,0,0 opcode = 0 func = 1 rd = int(args[1][1:]) rs = int(args[2][1:]) rt = int(args[3][1:]) elif (args[0] == "sub"): if (len(args) != 4): return 0,0,0,0,0,0 opcode = 0 func = 2 rd = int(args[1][1:]) rs = int(args[2][1:]) rt = int(args[3][1:]) elif (args[0] == "nand"): if (len(args) != 4): return 0,0,0,0,0,0 opcode = 0 func = 3 rd = int(args[1][1:]) rs = int(args[2][1:]) rt = int(args[3][1:]) elif (args[0] == "nor"): if (len(args) != 4): return 0,0,0,0,0,0 opcode = 0 func = 4 rd = int(args[1][1:]) rs = int(args[2][1:]) rt = int(args[3][1:]) elif (args[0] == "bez"): if (len(args) != 3): return 0,0,0,0,0,0 opcode = 1 rt = 0 rs = int(args[1][1:]) imm = int(args[2]) elif (args[0] == "bnez"): if (len(args) != 3): return 0,0,0,0,0,0 opcode = 1 rt = 1 rs = int(args[1][1:]) imm = int(args[2]) elif (args[0] == "bgez"): if (len(args) != 3): return 0,0,0,0,0,0 opcode = 1 rt = 2 rs = int(args[1][1:]) imm = int(args[2]) elif (args[0] == "blez"): if (len(args) != 3): return 0,0,0,0,0,0 opcode = 1 rt = 3 rs = int(args[1][1:]) imm = int(args[2]) elif (args[0] == "bgz"): if (len(args) != 3): return 0,0,0,0,0,0 opcode = 1 rt = 4 rs = int(args[1][1:]) imm = int(args[2]) elif (args[0] == "blz"): if (len(args) != 3): return 0 opcode = 1 rt = 5 rs = int(args[1][1:]) imm = int(args[2]) elif (args[0] == "lw"): if (args[-1] == ''): args = args[0:-1] if (len(args) != 3 and len(args) != 4): return 0,0,0,0,0,0 opcode = 2 rt = int(args[1][1:]) if (len(args) == 3): imm = 0 rs = int(args[2][1:]) else: imm = int(args[2]) rs = int(args[3][1:]) elif (args[0] == "sw"): if (args[-1] == ''): args = args[0:-1] if (len(args) != 3 and len(args) != 4): return 0,0,0,0,0,0 opcode = 3 rt = int(args[1][1:]) if (len(args) == 3): imm = 0 rs = int(args[2][1:]) else: imm = int(args[2]) rs = int(args[3][1:]) else: return 0,0,0,0,0,0 return opcode, rs, rt, rd, func, imm def inttohex(opcode, rs, rt, rd, func, imm): if (opcode == 0): opstr = format(opcode, '02b') rsstr = format(rs, '03b') rtstr = format(rt, '03b') rdstr = format(rd, '03b') fnstr = format(func, '05b') #print opstr, rsstr, rtstr, rdstr, fnstr instruction = opstr + rsstr + rtstr + rdstr + fnstr else : opstr = format(opcode, '02b') rtstr = format(rt, '03b') rsstr = format(rs, '03b') if (imm < 0): imm2s = ((-imm) ^ 255) + 1 immstr = format(imm2s, '08b') else : immstr = format(imm, '08b') #print opstr, rtstr, rsstr, immstr instruction = opstr + rsstr + rtstr + immstr return format(int(instruction, 2), '04x') def decode(asm): opcode, rs, rt, rd, func, imm = asmtoint(asm) instruction = inttohex(opcode, rs, rt, rd, func, imm) return instruction def openFile(): global filename openfilename = askopenfilename() if openfilename is not None: filename = openfilename asmfile = open(filename, "r") asmfile.seek(0) asmdata = asmfile.read() textArea.delete("1.0", "end - 1c") textArea.insert("1.0", asmdata) asmfile.close() filemenu.entryconfig(filemenu.index("Save"), state = NORMAL) frame.title("muCPU Assembler [" + filename + "]") frame.focus() def saveFile(): global filename asmdata = textArea.get("1.0", "end - 1c") asmfile = open(filename, "w") asmfile.seek(0) asmfile.truncate() asmfile.write(asmdata) asmfile.close() def saveFileAs(): global filename global fileexists saveasfilename = asksaveasfilename() if saveasfilename is not None: filename = saveasfilename fileexists = True asmdata = textArea.get("1.0", "end - 1c") asmfile = open(filename, "w") asmfile.seek(0) asmfile.truncate() asmfile.write(asmdata) asmfile.close() filemenu.entryconfig(filemenu.index("Save"), state = NORMAL) frame.title("muCPU Assembler [" + filename + "]") frame.focus() def exitApp(): frame.destroy() sys.exit() def compileASM(): global filename cpu_out = "" asm_in = textArea.get("1.0", END) asmlines = re.split("\n", asm_in) for i in range (len(asmlines)): if (asmlines[i] != ""): #print asmlines[i] cpu_out += str(i) + " => x\"" + decode(asmlines[i]) + "\",\n" #print cpu_out name, ext = os.path.splitext(filename) hexfilename = name + ".hex" hexfile = open(hexfilename, "w") hexfile.seek(0) hexfile.truncate() hexfile.write(cpu_out) hexfile.close() Tk().withdraw() frame = Toplevel() scrollbar = Scrollbar(frame) scrollbar.pack(side = RIGHT, fill = Y) frame.title("muCPU Assembler [" + filename + "]") textArea = Text(frame, height = 30, width = 100, padx = 3, pady = 3, yscrollcommand = scrollbar.set) textArea.pack(side=RIGHT) scrollbar.config(command=textArea.yview) menubar = Menu(frame) filemenu = Menu(menubar, tearoff=0) filemenu.add_command(label="Open", command=openFile) filemenu.add_command(label="Save", command=saveFile, state = DISABLED) filemenu.add_command(label="Save as...", command=saveFileAs) filemenu.add_command(label="Exit", command=exitApp) menubar.add_cascade(label="File", menu=filemenu) runmenu = Menu(menubar, tearoff=0) runmenu.add_command(label="Compile", command=compileASM) menubar.add_cascade(label="Run", menu=runmenu) frame.config(menu=menubar) frame.minsize(750, 450) frame.maxsize(750, 450) frame.mainloop() #Sample counting loop code """ lw r4, 176(r0) lw r3, 177(r0) sub r2, r4, r1 bez r2, 8 sw r1, 252(r0) bez r0, -8 add r1, r1, r3 sll r0, r0, r0 bez r0, -2 """
-
VHDL is Fun (when it's not a pain)
04/16/2016 at 01:56 • 1 commentGoing into this project, my vhdl experience consisted of code that made an led blink on my minispartan6+ dev board. With several youtube videos and a very useful book (Free Range VHDL), my skills quickly improved. The thing that frustrated me the most was the occasionally cryptic error messages that the Xilinx ISE Design Suite spat out. Oftentimes a mistake in one source file would lead to an error in another source file that used the original file, or an error in a file that used the file that used the original file. Anyway, I started the build with an ALU and instruction decoder; pretty much everything else stays similar even when the instruction set changes (I changed mine several times to allow for more functionality and an increased amount of registers). For the coding style of the project I used a mixture of behavioral (mostly for memory/registers), dataflow (for subcomponents), and structural (for the larger components). My intention of this project was more to learn about VHDL, and less to build a fast processor (duh...why would you build a slow, wheezing machine on a board that costs 5x as much as an MCU that can run circles around a soft processor), so the combination of styles is not likely very efficient. It was really cool to synthesize my code and view the RTL schematic that the synthesizer generated.