Computer Laboratory

Bluepsec Examples

Thacker's Tiny 3 Computer

Overview

This package contains the processor. Please read Chuck Thacker's paper to better understand the design.

The code

package Tiny3;

import FIFO::*;
import FIFOF::*;
import GetPut::*;
import TinyTypes::*;
import TinyAsm::*;
import BRAM::*;
import Vector::*;

function WordT alu(FuncT func, WordT raVal, WordT rbVal);
  case (func)
    FaADDb:     return raVal + rbVal;
    FaSUBb:     return raVal - rbVal;
    FINCb:      return rbVal + 1;
    FDECb:      return rbVal - 1;
    FaANDb:     return raVal & rbVal;
    FaORb:      return raVal | rbVal;
    FaXORb:     return raVal ^ rbVal;
    default:    return 0;
  endcase
endfunction

    
function WordT shifter(WordT val, ShiftT shift);
  Bit#(32) valb = pack(val); // val in bits
  case (shift)
    ShiftNone:  return val;
    ShiftRCY1:  return unpack({valb[0], valb[31:1]});
    ShiftRCY8:  return unpack({valb[7:0], valb[31:8]});
    ShiftRCY16: return unpack({valb[15:0], valb[31:16]});
    default:    return val;
  endcase
endfunction


function PCT pc_mux(OpcodeT op, PCT prog_counter, WordT alu_ret, Bool skip);
  if (skip)               return prog_counter + 2;
  else if (op == OpJump)  return word2pc(alu_ret);
  else                    return prog_counter + 1;
endfunction


function Maybe#(WordT) result_mux(OpcodeT op, PCT prog_counter, WordT alu_ret, WordT dm_ret, WordT in_ret);
  case (op)
    OpNormal,
    OpStoreDM,
    OpStoreIM,
    OpOut:      return tagged Valid alu_ret;
    OpLoadDM:   return tagged Valid dm_ret;
    OpIn:       return tagged Valid in_ret;
    OpJump:     return tagged Valid pc2word(prog_counter + 1);
    default:    return tagged Invalid;
  endcase
endfunction


function Bool skip_detect(InstructionT instruction, WordT alu_ret, Bool inQready);
  case (instruction) matches
    tagged Normal {skip:.skip}:      
      case (skip)
	SkipNever:  return False;
	SkipNeg:    return (alu_ret < 0);
	SkipZero:   return (alu_ret == 0);
	SkipInRdy:  return (inQready);
	default:    return False;
      endcase
    tagged Immediate {}:
      return False;
  endcase
endfunction


interface TinyCompIfc;
    interface Put#(WordT) in;
    interface Get#(WordT) out;
endinterface 


module mkTinyComp(InstructionROM_T irom, TinyCompIfc ifc);
    
  Reg#(PhaseT)        phase <- mkReg(INIT);
  Reg#(PCT)              pc <- mkReg(0);
  Reg#(InstructionT)   inst <- mkRegU;
  Reg#(WordT)    alu_result <- mkReg(0);
  Reg#(Bool)         doSkip <- mkRegU;
  
  FIFOF#(WordT)         inQ <- mkFIFOF;
  FIFO#(WordT)         outQ <- mkFIFO;
    
  Vector#(TExp#(SizeOf#(PCT)),Reg#(WordT))
                         rf <- replicateM(mkRegU);
  Reg#(WordT)         valRa <- mkRegU;
  Reg#(WordT)         valRb <- mkRegU;
    
  BRAM1Port#(PCT, WordT) im <- mkBRAM1Server(defaultValue);
  BRAM1Port#(PCT, WordT) dm <- mkBRAM1Server(defaultValue);
  Reg#(PCT)            addr <- mkReg(0); 

  // initialise instruction memory (im) contents from ROM
  rule do_init (phase == INIT);
    WordT data = int2word(irom[addr]);
    im.portA.request.put(BRAMRequest{write: True, responseOnWrite: False,
				     address: addr, datain: data});      
    InstructionT inst = word2instruction(data);
    if (inst.Normal.op != OpReserved)
      begin
	$write("%05t: init pm[%1d] = ", $time, addr);
	$display(fshow(inst));
      end
    let next_addr = addr + 1;
    addr <= next_addr;
    if (next_addr == 0)
      phase <= IF;
  endrule
   
  rule fetch (phase == IF);
    im.portA.request.put(
       BRAMRequest{
	  write: False,
	  responseOnWrite: False,
	  address: pc,
	  datain: 0});
    $write("%05t: fetch", $time);
    phase <= DC;
  endrule
  
  rule register_fetch (phase == DC);
    let read <- im.portA.response.get();
    let i = word2instruction(read);
    inst <= i;
    $display(" pm[%2d] : ", pc, fshow(i));
    // read registers
    if (i matches tagged Normal .normalInst)
      begin
	valRa <= rf[normalInst.ra];
	valRb <= rf[normalInst.rb];
      end
    phase <= EX;
  endrule
  
  rule execute (phase == EX);
    case (inst) matches
      tagged Normal {func:.func, shift:.shift, op:.op, skip:.skip}:
        begin // memory accesses
	  let addr=word2pc(valRb);
	  case(op) 
	    OpLoadDM:
	    begin
	      dm.portA.request.put(BRAMRequest{write: False,
		 responseOnWrite: False, address: addr, datain: 0});
	      $display("%05t: load dm[%1d] initiated", $time, addr);
	    end
	    OpStoreDM:
	    begin
	      dm.portA.request.put(BRAMRequest{write: True,
		 responseOnWrite: False, address: addr, datain: valRa});
	      $display("%05t: dm[%1d] = %1d", $time, addr, valRa);
	    end
	    OpStoreIM:
	    begin
	      im.portA.request.put(BRAMRequest{write: True,
		 responseOnWrite: False, address: addr, datain: valRa});
	      $display("%05t: pm[%1d] = %1d", $time, addr, valRa);
	    end
	  endcase
	  alu_result <= shifter(alu(func, valRa, valRb), shift); // ALU+shift
	end
      tagged Immediate {rw:.rw, imm:.imm}:
	  alu_result <= imm2word(imm);
      default: alu_result <= 0;
    endcase
    phase <= WB;
  endrule
   
  rule write_back (phase == WB);
    Bool doSkip = skip_detect(inst, alu_result, inQ.notEmpty);
    if(doSkip)
      $display("%05t: doSkip", $time);
    case (inst) matches
      tagged Normal {op:.op, rw:.rw}: begin
	pc <= pc_mux(op, pc, alu_result, doSkip);
	WordT dm_result = 0, in_result = 0;
	PCT addr = word2pc(valRb);
	case (op)
	  OpLoadDM:
	    begin
	      dm_result <- dm.portA.response.get();
	      $display("%05t: dm returned %1d", $time, dm_result);
	    end
	  OpIn:
	    begin
	      in_result = inQ.first;
	      inQ.deq;
	    end
	  OpOut:
	    outQ.enq(alu_result);
	  OpReserved: $finish;
	endcase
        Maybe#(WordT) wbval = result_mux(op, pc, alu_result, dm_result, in_result);
        if(isValid(wbval))
          begin
            rf[rw] <= fromMaybe(?, wbval);
            $display("%05t: r%1d <- %1d", $time, rw, fromMaybe(?, wbval));
          end
        else
          $display("%05t: alu = %1d", $time, alu_result); 
	if(op==OpJump) // mark jumps
	  $display("%05t: -----------------------------------------------------------------",$time);
      end
      tagged Immediate {rw:.rw, imm:.imm}:
        begin
          pc <= pc + 1;
          rf[rw] <= alu_result;
          $display("%05t: r%1d = imm = %1d", $time, rw, alu_result);
        end
    endcase
    phase <= IF;
  endrule
   
  interface in = toPut(inQ);
  interface out = toGet(outQ);

endmodule

endpackage: Tiny3

Link to the Tiny3.bsv source