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