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::*;

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


module mkTinyComp(InstructionROM_T irom, TinyCompIfc ifc);
   Reg#(PCT) pc <- mkReg(0);
   Reg#(WordT) alu <- mkReg(0);
   Reg#(UInt#(3)) phase <- mkReg(7);
   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;
   Reg#(InstructionT) inst <- 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==7);
      WordT data = unpack(pack(irom[addr]));
      im.portA.request.put(BRAMRequest{write: True, responseOnWrite: False,
					address: addr, datain: data});
      
      InstructionT inst = unpack(pack(data));
      if(inst.Normal.op!=OpReserved)
	 $display("%05t: init pm[%1d] = ",$time,addr,disassemble(inst));
      let next_addr = addr+1;
      addr <= next_addr;
      if(next_addr==0)
	 phase <= 0;
   endrule
   
   rule fetch(phase==0);
      im.portA.request.put(BRAMRequest{write: False, responseOnWrite: False, address: pc, datain: 0});
      $write("%05t: fetch",$time);
      phase <= 1;
   endrule

   rule register_fetch(phase==1);
      let read <- im.portA.response.get();
      let i = word2instruction(read);
      let ib = pack(i);
      inst <= i;
      $display(" pm[%2d] : ",pc,disassemble(i));
      // read registers
      valRa <= rf[ib[23:17]]; // hacky - fetch registers regardless of whether an immediate is going to be used
      valRb <= rf[ib[16:10]];
      phase <= 2;
   endrule
   
   rule execute(phase==2);
      WordT alu_result;
      case (inst) matches
	 tagged Normal {func:.func, shift:.shift, op:.op, skip:.skip}:
	    begin
	       WordT alu_calc;
	       doSkip <= ((skip==SkipNeg) && (alu<0)) ||
	                 ((skip==SkipZero) && (alu==0)) ||
 			 ((skip==SkipInRdy) && inQ.notEmpty);
	       // initiate loads
	       if(op==OpLoadDM)
		  dm.portA.request.put(BRAMRequest{write: False, responseOnWrite: False, address: unpack(truncate(pack(valRb))), datain: 0});
	       // first do the ALU operation
	       case(func)
		  FaADDb: alu_calc = valRa+valRb;
		  FaSUBb: alu_calc = valRa-valRb;
		  FINCb:  alu_calc = valRb+1;
		  FDECb:  alu_calc = valRb-1;
		  FaANDb: alu_calc = valRa & valRb;
		  FaORb:  alu_calc = valRa | valRb;
		  FaXORb: alu_calc = valRa ^ valRb;
		  default: begin alu_calc = 0; $finish; end
	       endcase
	       // then do the shifter (rotates)
	       Bit#(32) alub = pack(alu_calc); // ALU result in bits
	       case(shift)
		  ShiftNone:  alu_result = unpack(alub);
		  ShiftRCY1:  alu_result = unpack({alub[30:0],alub[31]});
		  ShiftRCY8:  alu_result = unpack({alub[23:0],alub[31:24]});
		  ShiftRCY16: alu_result = unpack({alub[15:0],alub[31:16]});
		  default:    alu_result = unpack(alub);
	       endcase
	    end
	 tagged Immediate {rw:.rw, imm:.imm}:
	    begin
	       doSkip <= False;
	       alu_result = zeroExtend(unpack(pack(imm)));
	    end
	 default: alu_result = 0;
      endcase
      alu <= alu_result;
      phase <= 3;
   endrule
   
   rule write_back(phase==3);
      $display("%05t: doSkip = %s",$time,doSkip ? "True" : "False");
      case (inst) matches
	 tagged Normal {op:.op, rw:.rw}:
	    begin
	       pc <= doSkip ? pc+2 : ((op==OpJump) ? unpack(truncate(pack(alu))) : pc+1);
	       Maybe#(WordT) wbval=tagged Invalid;
	       case (op)
		  OpNormal, OpStoreDM, OpStoreIM, OpOut: wbval = tagged Valid alu;
		  OpLoadDM: begin
			       let read <- dm.portA.response.get();
			       wbval = tagged Valid read;
			    end
		  OpIn:     begin
			       wbval = tagged Valid inQ.first;
			       inQ.deq;
			    end
	       endcase
	       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);
	       PCT addr = unpack(truncate(pack(valRb)));
	       case (op)
		  OpStoreDM: dm.portA.request.put(BRAMRequest{write: True, responseOnWrite: False, address: addr, datain: valRa});
		  OpStoreIM: im.portA.request.put(BRAMRequest{write: True, responseOnWrite: False, address: addr, datain: valRa});
		  OpOut:     outQ.enq(alu);
		  OpReserved: $finish;
	       endcase
	    end
	 tagged Immediate {rw:.rw, imm:.imm}:
	    begin
	       pc <= pc+1;
	       rf[rw] <= alu;
	       $display("%05t: r%1d = imm = %1d",$time,rw,alu);
	    end
      endcase
      phase <= 0;
   endrule
   
   interface in = toPut(inQ);
   interface out = toGet(outQ);

endmodule


endpackage

Link to the Tiny3.bsv source