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