Bluepsec Examples
Cyan Assembler
Overview
This package contains an embedded assembler for the Cyan processor. The code is passed in as a function containing a list of instructions and calls to define and resolve labels. This code function is executed twice to form a two-pass assembler: the first pass determines the addresses of the labels and the second pass fills in the label details. It is a testimant to the power of Bluespec's static elaboration system that the assembler is so simple and yet quite powerful.
The code
/***************************************************************************** CyanAsm ======= Simon Moore, January 2010 Assembler for CyanProc processor embedded by Bluespec *****************************************************************************/ package CyanAsm; import List::*; import FIFO::*; import GetPut::*; import ClientServer::*; import CyanTypes::*; typedef Tuple2#(String,InstructionT) AsmLineT; // type of a line of assembler typedef List#(String) LabelTblT; // type of assembler label table typedef List#(WordT) InstructionROM_T; /********** functions to create (label,instruction) pairs **********/ function AsmLineT asm(String label, OpcodeT op, RegT d, RegT a, RegT b, ImmT i); return tuple2(label,InstructionT{opcode: op, rd: d, ra: a, rb: b, imm: i}); endfunction // returns the position of string "tofind" in "list" function Maybe#(Integer) find_pos_string (String tofind, List#(String) list); Maybe#(Integer) rtn = tagged Invalid; for(Integer j=0; (j<length(list)) && !isValid(rtn); j=j+1) if(list[j]==tofind) rtn=tagged Valid fromInteger(j); return rtn; endfunction // returns True if "tofind" is in "list", otherwise False function Bool find_str(String tofind, List#(String) list) = isValid(find_pos_string(tofind,list)); function InstructionROM_T assembler(function List#(AsmLineT) program_source(function ImmT findaddr(String label))); /********** phase 0 of the assembly process **********/ function ImmT no_label(String label) = -1; List#(AsmLineT) prog_phase_0 = program_source(no_label); let label_tbl = tpl_1(unzip(prog_phase_0)); /********** phase 1 of the assembly process **********/ function ImmT label_lookup(String label) = fromInteger(fromMaybe(-1,find_pos_string(label,label_tbl))); List#(AsmLineT) prog_phase_1 = program_source(label_lookup); Integer prog_len = length(prog_phase_1); List#(WordT) rom = map(instruction2word, tpl_2(unzip(prog_phase_1))); return rom; endfunction function Tuple2#(Bool,Fmt) find_duplicate_labels(function List#(AsmLineT) program_source(function ImmT findaddr(String label))); function Tuple2#(Bool,Fmt) report_duplicates(List#(String) labels); Fmt msg = $format(""); Bool error = False; if(labels!=Nil) begin if((head(labels)!="") && find_str(head(labels),tail(labels))) begin msg = $format("Assembler error: label \"%s\" defined more than once\n",head(labels)); error = True; end let rd = report_duplicates(tail(labels)); error = error || tpl_1(rd); msg = msg + tpl_2(rd); end return tuple2(error,msg); endfunction function ImmT no_label(String label) = -1; List#(AsmLineT) labelled_prog = program_source(no_label); Tuple2#(List#(String), List#(InstructionT)) lab_prog = unzip(labelled_prog); let labels = tpl_1(lab_prog); return report_duplicates(labels); endfunction function Fmt disassemble(InstructionROM_T rom); Fmt msg = $format(""); for(Integer a=0; a<length(rom); a=a+1) begin let i = word2instruction(rom[a]); msg = msg + $format("prog[%2d] : %s",a,opcode2string(i.opcode)) + + $format(" rd=") + reg2fmt(i.rd) + $format(" ra=") + reg2fmt(i.ra) + $format(" rb=") + reg2fmt(i.rb) + $format(" imm=%5d\n", i.imm ); end return msg; endfunction // assemble "the_program" (function with list of assembler) and create a ROM module mkInstructionROM#(function List#(AsmLineT) the_program(function ImmT findaddr(String label))) (Server#(ContextT, IFtokenT)); InstructionROM_T rom = assembler(the_program); FIFO#(Tuple2#(ContextT,InstructionT)) out_fifo <- mkLFIFO; interface Put request; method Action put(contx); out_fifo.enq(tuple2(contx, word2instruction(rom[contx.pc]))); endmethod endinterface interface response = toGet(out_fifo); endmodule // simple test module mkTestAssembler(Empty); /********** the program we wish to assemble **********/ function List#(AsmLineT) my_program(function ImmT findaddr(String label)) = cons(asm("", OpADD, Rg5, Rg6, Rg7, 0), cons(asm("loop", OpADD, Rg3, Rg2, Rg1, 0), cons(asm("", OpADD, Rg5, Rg6, Rg7, 0), cons(asm("loop", OpADD, Rg3, Rg2, Rg1, 0), cons(asm("", OpBEQ, RgZero, Rg1, Rg2, findaddr("loop")), cons(asm("", OpSUB, Rg3, Rg2, Rg1, 0), tagged Nil)))))); InstructionROM_T rom = assembler(my_program); // check assembly worked Reg#(Bool) report_rom_state <- mkReg(True); rule disassemble (report_rom_state); report_rom_state <= False; // check for duplicate labels let fd = find_duplicate_labels(my_program); if(tpl_1(fd)) begin $display("Assembler error - duplicate labels found:"); $write(tpl_2(fd)); // report any errors end // produce disassembly of the program $write(disassemble(rom)); endrule endmodule endpackage: CyanAsm
Link to the CyanAsm.bsv source