Computer Laboratory

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